1 /*****
2  ** ** Module Header ******************************************************* **
3  ** 									     **
4  **   Modules Revision 3.0						     **
5  **   Providing a flexible user environment				     **
6  ** 									     **
7  **   File:		Modulate_Avail.c				     **
8  **   First Edition:	1991/10/23					     **
9  ** 									     **
10  **   Authors:	John Furlan, jlf@behere.com				     **
11  **		Jens Hamisch, jens@Strawberry.COM			     **
12  ** 									     **
13  **   Description: 	This module command prints out the modulefiles that  **
14  **			are available in the directories listed in the	     **
15  **			MODULEPATH environment variable.		     **
16  ** 									     **
17  **   Exports:		ModuleCmd_Avail					     **
18  **			print_aligned_files				     **
19  **			check_dir					     **
20  **			get_dir						     **
21  **			dirlst_to_list					     **
22  **			delete_dirlst					     **
23  **			delete_cache_list				     **
24  ** 									     **
25  **   Notes:								     **
26  ** 									     **
27  ** ************************************************************************ **
28  ****/
29 
30 /** ** Copyright *********************************************************** **
31  ** 									     **
32  ** Copyright 1991-1994 by John L. Furlan.                      	     **
33  ** see LICENSE.GPL, which must be provided, for details		     **
34  ** 									     **
35  ** ************************************************************************ **/
36 
37 static char Id[] = "@(#)$Id: ceb8e5266126d923395a62c39c6e5d839bca96d8 $";
38 static void *UseId[] = { &UseId, Id };
39 
40 /** ************************************************************************ **/
41 /** 				      HEADERS				     **/
42 /** ************************************************************************ **/
43 
44 #include <time.h>
45 #include "modules_def.h"
46 #if defined HAVE_STRCOLL && defined HAVE_LOCALE_H && defined HAVE_SETLOCALE
47 #  include <locale.h>
48 #endif
49 
50 /** ************************************************************************ **/
51 /** 				  LOCAL DATATYPES			     **/
52 /** ************************************************************************ **/
53 
54 /**
55  **  Structure for a linked list that stores directories to be listed.
56  **/
57 
58 typedef struct _subdir_node {
59   fi_ent*              sd_dir;
60   struct _subdir_node* sd_next;
61 } sd_node;
62 
63 /** ************************************************************************ **/
64 /** 				     CONSTANTS				     **/
65 /** ************************************************************************ **/
66 
67 /**
68  **  I tried having a test for isgraph() in the configuration file,
69  **  but it fails on AIX. This is the best I could come up with...
70  **/
71 
72 #if !defined(isgraph) && defined(_P) && defined(_N)
73 #define isgraph(c)	((_ctype_+1)[c]&(_P|_U|_L|_N))
74 #endif
75 
76 #define DIREST  50
77 #define  CACHE_VERSION  "v3.0.0"
78 
79 #if !defined(CACHE_UMASK)
80 #define CACHE_UMASK	0
81 #endif
82 
83 /** ************************************************************************ **/
84 /**				      MACROS				     **/
85 /** ************************************************************************ **/
86 
87 /** not applicable **/
88 
89 /** ************************************************************************ **/
90 /** 				    LOCAL DATA				     **/
91 /** ************************************************************************ **/
92 
93 #ifdef CACHE_AVAIL
94 static	char	*namebuf = NULL;
95 #endif
96 static	char	 buffer[MOD_BUFSIZE];
97 static	char	 buf[ LINELENGTH];
98 static	char	 module_name[] = "ModuleCmd_Avail.c";	/** File name of this module **/
99 
100 #if WITH_DEBUGGING_MODULECMD
101 static	char	_proc_ModuleCmd_Avail[] = "ModuleCmd_Avail";
102 #endif
103 #if WITH_DEBUGGING_UTIL_1
104 static	char	_proc_print_dir[] = "print_dir";
105 static	char	_proc_print_aligned_files[] = "print_aligned_files";
106 #endif
107 #if WITH_DEBUGGING_UTIL_2
108 static	char	_proc_check_dir[] = "check_dir";
109 static	char	_proc_get_dir[] = "get_dir";
110 static	char	_proc_dirlst_to_list[] = "dirlst_to_list";
111 static	char	_proc_delete_dirlst[] = "delete_dirlst";
112 static	char	_proc_store_files[] = "store_files";
113 static	char	_proc_store_dirlst[] = "store_dirlst";
114 static	char	_proc_store_file[] = "store_file";
115 #ifdef CACHE_AVAIL
116 static	char	_proc_create_cache_list[] = "create_cache_list";
117 #endif
118 static	char	_proc_delete_cache_list[] = "delete_cache_list";
119 static	char	_proc_print_spaced_file[] = "print_spaced_file";
120 static	char	_proc_mkdirnm[] = "mkdirnm";
121 #endif
122 
123 #if	WITH_DEBUGGING
124 static	char	buffer1[ 80], buffer2[ 80];
125 #endif
126 
127 static	char	short_format[] = "%s";
128 static	char	short_format_part[] = "%s/%s";
129 static	char	short_format_full[] = "%s/%s(%s)";
130 static	char	long_format[] = "%-39.39s  %-10.10s  %17s\n";
131  	char	long_header[] = "\
132 - Package -----------------------------+- Versions -+- Last mod. ------\n";
133 
134 /**
135  **  Terse file list buffer
136  **/
137 
138 #define	FILE_LIST_SEGM_SIZE 100
139 static	char	  _file_list_buffer[ 200];
140 static	char	**_file_list_ptr = (char **) NULL;
141 static	int	  _file_list_cnt = 0;
142 static	int	  _file_list_wr_ndx = 0;
143 static	int	  _file_list_rd_ndx = 0;
144 
145 /** ************************************************************************ **/
146 /**				    PROTOTYPES				     **/
147 /** ************************************************************************ **/
148 
149 static	int	  print_dir( Tcl_Interp*, char*, char*);
150 #ifdef CACHE_AVAIL
151 static	void	  store_dirlst( FILE*, FILE*, fi_ent*, int, char*);
152 static	void	  store_files( fi_ent*, int, int, char*);
153 static	void	  store_file( FILE*, char*, fi_ent*);
154 #endif
155 static	void	  print_spaced_file( char*, int, int, int);
156 static	char	 *mkdirnm( char*, char*);
157 static	int	  fi_ent_cmp( const void*, const void*);
158 #ifdef CACHE_AVAIL
159 static	int	  check_cache( char *dir);
160 #endif
161 static	void	  _init_file_list(void);
162 static	void	  _add_file_list( char *name);
163 static	char	 *_get_file_list(void);
164 static	char	 *_pick_file_list( int ndx);
165 static	void	  print_terse_files( int terminal_width, int len, char *header,
166 			int numbered);
167 #ifdef CACHE_AVAIL
168 static	char	**create_cache_list( FILE*, int*, char* );
169 #endif
170 
171 /*++++
172  ** ** Function-Header ***************************************************** **
173  ** 									     **
174  **   Function:		ModuleCmd_Avail					     **
175  ** 									     **
176  **   Description:	Execution of the 'module avail' command		     **
177  ** 									     **
178  **   First Edition:	1991/10/23					     **
179  ** 									     **
180  **   Parameters:	Tcl_Interp	*interp		Current Tcl Interpr. **
181  **			char		*argv[]		Arguments to the     **
182  **							command		     **
183  ** 									     **
184  **   Result:		int	TCL_OK		Successful operation	     **
185  **				TCL_ERROR	Any failure		     **
186  ** 									     **
187  **   Attached Globals:	g_specified_module	The module name from the     **
188  **						command line.		     **
189  ** 									     **
190  ** ************************************************************************ **
191  ++++*/
192 
ModuleCmd_Avail(Tcl_Interp * interp,int argc,char * argv[])193 int ModuleCmd_Avail(	Tcl_Interp	*interp,
194 			int		 argc,
195                 	char		*argv[])
196 {
197     char	*dirname;
198     char	*modpath;
199     int		 Result = -TCL_ERROR;
200 
201 #if WITH_DEBUGGING_MODULECMD
202     ErrorLogger( NO_ERR_START, LOC, _proc_ModuleCmd_Avail, NULL);
203 #endif
204 
205     /**
206      **  If there's no MODULEPATH defined, we cannot output anything
207      **  We perform 1 level of env.var. expansion
208      **/
209 
210     if( !(modpath = (char *) xgetenv( "MODULEPATH")))
211 	if( OK != ErrorLogger( ERR_MODULE_PATH, LOC, NULL))
212 	    goto unwind0;
213 
214 #ifdef CACHE_AVAIL
215     if( (char *) NULL == (namebuf = stringer(NULL, MOD_BUFSIZE, NULL)))
216 	if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
217 	    goto unwind1;
218 #endif
219 
220 #if defined HAVE_STRCOLL && defined HAVE_SETLOCALE
221     /**
222      ** define the collation order using the locale
223      **/
224     (void) setlocale(LC_COLLATE,"");
225 #endif
226 
227     /**
228      **  If we're given a full-path, then we'll just check that directory.
229      **  Otherwise, we'll check every directory in MODULESPATH.
230      **/
231 
232     if( argc > 0 && **argv == '/') {
233 
234 	while( argc--) {
235 
236 	    /**
237 	     ** Set the name of the module specified on the command line
238 	     **/
239 
240 	    g_specified_module = *argv;
241 
242 	    if( !check_dir( *argv))
243 		if( OK != ErrorLogger( ERR_PARAM, LOC, NULL)) {
244 		    Result = TCL_ERROR;	/** --- EXIT PROCEDURE (FAILURE) --> **/
245 		}
246 	    else
247 		print_dir( interp, *argv, NULL);
248 
249 	    argv++;
250 	}
251 
252     } else {
253 
254 	/**
255 	 **  We're not given a full path. Tokenize the module path string and
256 	 **  print the contents of each directory specified (if it exists ;-)
257 	 **/
258 
259 	if( sw_format & SW_LONG)
260 	    fprintf( stderr, "%s", long_header);
261 
262 	/**
263 	 **  If a module category is specified check whether it is part
264 	 **  of the directory we're scanning at the moment.
265 	 **/
266 
267 	if( argc > 0) {  /* show sub directory */
268 	    while( argc--) {
269 		/**
270 		 ** Set the name of the module specified on the command line
271 		 **/
272 
273 		g_specified_module = *argv;
274 
275 		dirname = modpath;
276 		while( dirname && *dirname) {
277 
278 		    /**
279 		     **  We cannot use strtok here, because it interfers with
280 		     **  subsequent calls while printing the list
281 		     **/
282 
283 		    char *s;
284 
285 		    if( s = strchr( dirname, ':'))
286 			*s++ = '\0';
287 
288 		    /**
289 		     **  Print the cathegory
290 		     **/
291 
292 		    if( check_dir( dirname))
293 			print_dir( interp, dirname, *argv);
294 		    dirname = s;
295 		}
296 
297 		argv++;
298 	    }
299 
300 	/**
301 	 **  Otherwise, if there's no category given, descend the current
302 	 **  directory and print its contents.
303 	 **/
304 
305 	} else {
306 
307 	    dirname = modpath;
308 	    while( dirname && *dirname) {
309 
310 		/**
311 		 **  We cannot use strtok here, because it interfers with
312 		 **  subsequent calls while printing the list
313 		 **/
314 
315 		char *s;
316 
317 		if( s = strchr( dirname, ':'))
318 		    *s++ = '\0';
319 
320 		/**
321 		 **  Second part of tokenization
322 		 **/
323 
324 		if( check_dir( dirname))
325 		    print_dir( interp, dirname, NULL);
326 		dirname = s;
327 	    }
328 
329         } /** for **/
330     } /** if( no full path name given) **/
331 
332     /**
333      **  Free up what has been allocated and exit from this procedure
334      **/
335     /* if got here via this path ... it must have been OK */
336     if(Result < 0) Result = TCL_OK;
337 
338 unwind2:
339 #ifdef CACHE_AVAIL
340     null_free((void *) &namebuf);
341 #endif
342 unwind1:
343     null_free((void *) &modpath);
344 
345 unwind0:
346 #if WITH_DEBUGGING_MODULECMD
347     ErrorLogger( NO_ERR_END, LOC, _proc_ModuleCmd_Avail, NULL);
348 #endif
349 
350     /* if Result is negative here ... must have been an unwind */
351     if (Result < 0) Result = -Result;
352 
353     return( Result);		/** --- EXIT PROCEDURE (FAILURE/SUCCESS) --> **/
354 
355 } /** End of 'ModuleCmd_Avail' **/
356 
357 /*++++
358  ** ** Function-Header ***************************************************** **
359  ** 									     **
360  **   Function:		check_dir					     **
361  ** 									     **
362  **   Description:	Open and close the passed directory in order to check**
363  **			if it does exist and is readable		     **
364  ** 									     **
365  **   First Edition:	1991/10/23					     **
366  ** 									     **
367  **   Parameters:	char	*dirname	Name of the directory to be  **
368  **						checked			     **
369  ** 									     **
370  **   Result:		int	0	Not a directory or unreadable	     **
371  **				1	OK				     **
372  ** 									     **
373  **   Attached Globals:	-						     **
374  ** 									     **
375  ** ************************************************************************ **
376  ++++*/
377 
check_dir(char * dirname)378 int check_dir(	char	*dirname)
379 {
380     DIR*   dirp;
381 
382 #if WITH_DEBUGGING_UTIL_2
383     ErrorLogger( NO_ERR_START, LOC, _proc_check_dir, NULL);
384 #endif
385 
386     if( !(dirp = opendir( dirname)))
387 	return( 0);
388 
389     if( -1 == closedir( dirp))
390 	if( OK != ErrorLogger( ERR_CLOSEDIR, LOC, dirname, NULL))
391 	    return( 0);
392 
393     return( 1);
394 
395 } /** End of 'check_dir' **/
396 
397 /*++++
398  ** ** Function-Header ***************************************************** **
399  ** 									     **
400  **   Function:		print_dir					     **
401  ** 									     **
402  **   Description:	Print all files beyond the passed directory	     **
403  ** 									     **
404  **   First Edition:	1991/10/23					     **
405  ** 									     **
406  **   Parameters:	char	*dir		Directory to be scanned	     **
407  **			char	*module		A selcted module name or NULL**
408  ** 									     **
409  **   Result:		int	TCL_OK		Successful operation	     **
410  ** 									     **
411  **   Attached Globals:	-						     **
412  ** 									     **
413  ** ************************************************************************ **
414  ++++*/
415 
print_dir(Tcl_Interp * interp,char * dir,char * module)416 static	int	print_dir(	Tcl_Interp	*interp,
417 				char		*dir,
418 				char		*module)
419 {
420     fi_ent	 *dirlst_head = NULL;	/** Directory list base pointer	     **/
421     int		  count = 0;		/** Number of elements in the top    **/
422 					/** level directory list	     **/
423     int		  tcount = 0;		/** Total number of files to print   **/
424     int	 	  start = 0;
425     int		  dirlen;
426     char	**cache_list = NULL;
427     char	 *selection, *s;
428 
429 #ifdef CACHE_AVAIL
430     int		  usecache;
431     FILE	 *fi;
432 #endif
433 
434 #if WITH_DEBUGGING_UTIL_1
435     ErrorLogger( NO_ERR_START, LOC, _proc_print_dir, "dir='", dir, NULL);
436 #endif
437 
438     /**
439      **  Print the directory name
440      **/
441 
442     if( (sw_format & (SW_PARSE | SW_TERSE | SW_LONG))
443     && !(sw_format & (SW_HUMAN | SW_LIST)) ) {
444 	/* char *base = strrchr( dir, '/');
445 	fprintf( stderr, "%s:\n", base ? ++base : dir);
446 	*/
447 	fprintf( stderr, "%s:\n", dir);
448     }
449 
450     if( dir)
451 	dirlen = strlen( dir) + 1;
452     else
453 	dirlen = 0;
454 
455     /**
456      **  If the is a module selection given, build the whole selected path
457      **/
458 
459     if( module) {
460 
461 	if( dir) {
462 	    if((char *) NULL == (selection = stringer(NULL, 0,
463 		dir,"/",module, NULL))) {
464 		ErrorLogger( ERR_STRING, LOC, NULL);
465 		return( TCL_ERROR);     /** --- EXIT (FAILURE) --------> **/
466 	    }
467 
468 	} else
469 	    selection = module;
470 
471     } else
472 	selection = (char *) NULL;
473 
474 #ifdef CACHE_AVAIL
475 
476     /**
477      **  Ensure any files I create can be read and written by everyone
478      **/
479 
480     umask( CACHE_UMASK);
481 
482     /**
483      **  OK, if cache is to be used, go on reading the entire cache.
484      **  In case of success print the files. Otherwise read the files
485      **  from the file system and rebuild the cache.
486      **/
487 
488     if( usecache = check_cache( dir)) {
489 
490 	if( !sw_create) {
491 
492 	    if( (char *) NULL == stringer(namebuf, MOD_BUFSIZE,
493 		dir, "/.moduleavailcache", NULL))
494 		if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
495 		    goto unwind1;
496 
497 	    if( NULL == (fi = fopen( namebuf, "r"))) {
498 		if( OK != ErrorLogger( ERR_OPEN, LOC, namebuf, "reading", NULL))
499 		    goto unwind1;
500 
501 	    } else {
502 
503 		cache_list = create_cache_list( fi, &tcount, selection);
504 
505 		/**
506 		 **  Close the cache file
507 		 **/
508 
509 		if( EOF == fclose( fi))
510 		    if( OK != ErrorLogger( ERR_CLOSE, LOC, namebuf, NULL))
511 			goto unwind1;
512 	    }
513 
514 	} /** if( !create) **/
515 
516     }
517 #endif
518 
519     if( !cache_list) {
520 #ifdef CACHE_AVAIL
521 	usecache = 0;
522 #endif
523 
524 	/**
525 	 **  Normal reading of the files
526 	 **/
527 
528 	if( NULL == (dirlst_head = get_dir( dir, NULL, &count, &tcount)))
529 	    if( OK != ErrorLogger( ERR_READDIR, LOC, dir, NULL))
530 		goto unwind1;
531 
532 	if( NULL ==
533 		(cache_list = (char**) module_malloc(tcount * sizeof(char**))))
534 	    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
535 		goto unwind1;
536 	(void) memset(cache_list, 0, tcount * sizeof( char **));
537 
538 	start=0;
539 	dirlst_to_list( cache_list, dirlst_head, count, &start, dir, selection);
540 
541     }
542 #ifdef CACHE_AVAIL
543 
544     /**
545      **  Now open the files used for caching
546      **  In case of any error flush out the read files and exit
547      **/
548 
549     if( !usecache && sw_create)
550 	store_files( dirlst_head, count, tcount, dir);
551 #endif
552 
553     /**
554      **  In case of any selection, we have to force all .modulrc's and .versions
555      **  on the path
556      **/
557 
558     if( dir) {
559 
560 	s = dir;
561 	while( s) {
562 	    if( s = strchr( s, '/'))
563 	    	*s = '\0';
564 	    else
565 		break;
566 
567 	    SourceRC( interp, dir, modulerc_file);
568 	    SourceVers( interp, dir, module);
569 
570 	    if( s)
571 		*s++ = '/';
572 	}
573 
574 	/**
575 	 **  Finally source the rc files in the directory itself
576 	 **/
577 
578 	SourceRC( interp, dir, modulerc_file);
579 	SourceVers( interp, dir, module);
580     }
581 
582     if( dir && selection)
583 	null_free((void *) &selection);
584 
585     /**
586      **  Print and remove the cache list
587      **/
588 
589     delete_dirlst( dirlst_head, count);
590     print_aligned_files( interp, dir, dir, cache_list, tcount,
591 	(sw_format & SW_LIST ? 1 : -1));
592     delete_cache_list( cache_list, start);
593 
594     if( sw_format & SW_LONG)
595 	fprintf( stderr, "\n");
596     return( TCL_OK);     		/** ------- EXIT (SUCCESS) --------> **/
597 
598 unwind1:
599     if( dir && selection)
600 	null_free((void *) &selection);
601 
602 unwind0:
603     return( TCL_ERROR);     		/** ------- EXIT (FAILURE) --------> **/
604 
605 } /** End of 'print_dir' **/
606 
607 /*++++
608  ** ** Function-Header ***************************************************** **
609  ** 									     **
610  **   Function:		check_cache					     **
611  ** 									     **
612  **   Description:	Check whether an avail cache exists and is not out   **
613  **			of date						     **
614  ** 									     **
615  **   First Edition:	1996/01/03					     **
616  ** 									     **
617  **   Parameters:	char	*dir		Directory to be checked	     **
618  ** 									     **
619  **   Result:		int	0	Do not use the cache		     **
620  **				1	Use the cache			     **
621  ** 									     **
622  **   Attached Globals:	-						     **
623  ** 									     **
624  ** ************************************************************************ **
625  ++++*/
626 
627 #ifdef CACHE_AVAIL
628 
check_cache(char * dir)629 static	int	check_cache( char *dir)
630 {
631     time_t       dir_time=0, cache_time=0, info_time=0;
632     struct stat  dir_stats, cache_stats;
633     FILE	*cdir;
634 
635     /**
636      **  Check if a cache file exists. And if it does, check if it is
637      **  younger than the related directory.
638      **/
639 
640     if( (char *) NULL == stringer(namebuf, MOD_BUFSIZE,
641 	dir, "/.moduleavailcachedir", NULL))
642             return( 0);
643 
644     if( NULL != (cdir = fopen( namebuf, "r"))) {
645         if( stat( dir, &dir_stats) != -1) {
646             if( stat( namebuf, &cache_stats) != -1) {
647                 dir_time = dir_stats.st_mtime;
648                 cache_time = cache_stats.st_mtime;
649             }
650             if( dir_time > cache_time) {
651                 return( 0);
652             }
653         }
654 
655 	/**
656 	 **  Also check if the modification date of the cached files isn't
657 	 **  younger than the one derivered from the cache
658 	 **/
659 
660         while( !feof(cdir)) {
661 
662 	    if( fscanf( cdir, "%s %d\n", buf, (int *)&info_time) != 2)
663 	        continue;
664 
665             if( stat( buf, &dir_stats) != -1) {
666                 if( dir_stats.st_mtime <= info_time)
667                     continue;
668             }
669 
670             return( 0);
671         }
672 
673 	/**
674 	 **  Close the cache file
675 	 **/
676 
677         if( EOF == fclose( cdir))
678 	    if( OK != ErrorLogger( ERR_CLOSE, LOC, namebuf, NULL))
679 		return( 0);		/** ------- EXIT (FAILURE) --------> **/
680 
681 	return( 1);
682     }
683 
684     return( 0);
685 
686 } /** End of 'check_cache' **/
687 
688 #endif
689 
690 
test_version_dir(struct dirent * dp)691 static int	test_version_dir(	struct dirent	*dp)
692 {
693 }
694 /*++++
695  ** ** Function-Header ***************************************************** **
696  ** 									     **
697  **   Function:		get_dir						     **
698  ** 									     **
699  **   Description:	Read in the passed directory and save every interes- **
700  **			ting item in the directory list			     **
701  **			skipping known version control directories:	     **
702  **				CVS RCS .svn				     **
703  **			unless they contain .version files		     **
704  ** 									     **
705  **   First Edition:	1991/10/23					     **
706  ** 									     **
707  **   Parameters:	char	*dir		Directory to be read	     **
708  **			char	*prefix		Directory prefix (path)	     **
709  **			int	*listcount	Buffer to store the number of**
710  **						elements in the current      **
711  **						directory list		     **
712  **			int 	*total_count	Buffer for the total number  **
713  **						of files read		     **
714  ** 									     **
715  **   Result:		fi_ent*		NULL	Failure			     **
716  **					else	Directory list base pointer  **
717  **			*listcount		Number of elements in the    **
718  **						top level directory list     **
719  **			*total_count		Total number of files read   **
720  ** 									     **
721  **   Attached Globals:	-						     **
722  ** 									     **
723  ** ************************************************************************ **
724  ++++*/
725 
get_dir(char * dir,char * prefix,int * listcount,int * total_count)726 fi_ent	*get_dir(	char	*dir,
727 			char	*prefix,
728 			int	*listcount,
729 			int	*total_count)
730 {
731     struct dirent	*dp;		/** Directory read pointer	     **/
732     DIR			*dirptr;	/** Directory handle		     **/
733     fi_ent		*dirlst_head,	/** Directory list pointers. Head,   **/
734     			*dirlst_cur,	/** current			     **/
735     			*dirlst_last;	/** and last			     **/
736     char		*dirname;	/** Expanded directory path name     **/
737     char		*tmp;
738     int			 count = 0;
739 
740 #if WITH_DEBUGGING_UTIL_2
741     ErrorLogger( NO_ERR_START, LOC, _proc_get_dir, NULL);
742 #endif
743 
744     /**
745      **  Open the desired directiory
746      **/
747 
748     if( !(dirptr = opendir( dir))) {
749 #if 0
750 	/* if you can't open a directory ... is that really an error? */
751 	if( OK != ErrorLogger( ERR_OPENDIR, LOC, dir, NULL))
752 #endif
753 	    return( NULL);	/** ----------- EXIT (FAILURE) ------------> **/
754     }
755 
756     /**
757      **  Allocate memory for reading in the directory
758      **/
759 
760     if(!(dirlst_cur = dirlst_head = (fi_ent*) module_malloc( DIREST *
761 	sizeof( fi_ent)))) {
762 	if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL)) {
763 	    if( -1 == closedir( dirptr))
764 		ErrorLogger( ERR_CLOSEDIR, LOC, dir, NULL);
765 	    goto unwind0;
766 	}
767     }
768     dirlst_last = dirlst_head + DIREST;
769 
770     /**
771      **  Read in the  contents of the directory. Ignore dotfiles
772      **		and version directories.
773      **/
774 
775     for( count = 0,  dp = readdir( dirptr); dp != NULL; dp = readdir( dirptr)) {
776         if( *dp->d_name == '.') continue;
777 
778 	/**
779 	 **  Conditionally double up the space allocated for reading the direc-
780 	 **  tory
781 	 **/
782 
783         if(dirlst_cur == dirlst_last) {
784             if(!(dirlst_head = (fi_ent*) module_realloc( (char*) dirlst_head,
785 		(count<<1) * sizeof( fi_ent))))
786 		if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
787 		    goto unwind0;
788             dirlst_cur = dirlst_head + count;
789             dirlst_last = dirlst_head + (count<<1);
790 	}
791 
792 	/**
793 	 **  Build the complete path name and get information about the file
794 	 **/
795 
796         if( !( dirname = mkdirnm( dir, dp->d_name)))
797 	    if( OK != ErrorLogger( ERR_DIRNAME, LOC, NULL)) {
798 		if( -1 == closedir( dirptr))
799 		    ErrorLogger( ERR_CLOSEDIR, LOC, dir, NULL);
800 		goto unwind1;
801 	    }
802 
803         if( stat( dirname, &(dirlst_cur->fi_stats)) < 0)
804 	    if( OK != ErrorLogger( ERR_DIRNOTFOUND, LOC, dirname, NULL)) {
805 		if( -1 == closedir( dirptr))
806 		    ErrorLogger( ERR_CLOSEDIR, LOC, dir, NULL);
807 		goto unwind1;
808 	    }
809 
810 	/**
811 	 **  If it is a directory, recursively delve into it ..
812 	 **/
813 
814         if(dirlst_cur->fi_stats.st_mode & S_IFDIR) {
815             char* np;
816             char* ndir;
817             int   tmpcount = 0;
818 
819 	    /**
820 	     **  Build the new base points for the recursion
821 	     **/
822 
823             if( !( tmp = mkdirnm( prefix, dp->d_name))) {
824 		if( OK != ErrorLogger( ERR_DIRNAME, LOC, NULL)) {
825 		    if( -1 == closedir( dirptr))
826 			ErrorLogger( ERR_CLOSEDIR, LOC, dir, NULL);
827 		    goto unwind1;
828 		}
829 	    } else {
830 		if( NULL == (np = strdup( tmp)))
831 		    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
832 			goto unwind1;
833 	    }
834 
835             if( !( tmp = mkdirnm( dir, dp->d_name))) {
836 		if( OK != ErrorLogger( ERR_DIRNAME, LOC, NULL)) {
837 		    if( -1 == closedir( dirptr))
838 			ErrorLogger( ERR_CLOSEDIR, LOC, dir, NULL);
839 		    goto unwind1;
840 		}
841 	    } else {
842 		if( NULL == (ndir = strdup( tmp)))
843 		    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
844 			goto unwind1;
845 	    }
846 
847 	    /**
848 	     **  What if it's a known version control directory
849 	     **  check if it has a .version file
850 	     **/
851 	    if (!strcmp("CVS",dp->d_name)
852 	    ||  !strcmp("RCS",dp->d_name)
853 	    ||  !strcmp(".svn",dp->d_name)) {
854     		FILE	*fi;
855 		if( (char *) NULL == stringer(buffer, MOD_BUFSIZE,
856 		    tmp, "/.version", NULL))
857 		    if( OK != ErrorLogger( ERR_STRING, LOC, NULL))
858 			goto unwind1;
859 		if( NULL == (fi = fopen( buffer, "r"))) {
860 			/* does not have a .version file */
861 			continue;
862 		} else {
863 			/* has a .version file ... assume to be module dir */
864 			fclose(fi);
865 		}
866 	    }
867 
868 	    /**
869 	     **  The recursion itself ...
870 	     **/
871 
872             dirlst_cur->fi_subdir = get_dir( ndir,np,&dirlst_cur->fi_listcount,
873 		&tmpcount);
874 
875             /**
876              **  Add the number of real modulefiles (i.e. not subdirs and
877              **  not non-modulefiles) to our total number of modulefiles
878              **  contained in the structure.
879              **/
880 
881             *total_count += tmpcount;
882 
883             /**
884              **  This means that it's an empty directory so the prefix is
885 	     **  never used
886              **/
887 
888             if( !dirlst_cur->fi_listcount)
889 		null_free((void *) &np);
890             null_free((void *) &ndir);
891 
892 	/**
893 	 **  if it is not a directory check the magic cookie of the file. Only
894 	 **  files with the modules magic cookie will be accepted. Also tem-
895 	 **  porary files are to be ignored.
896 	 **/
897 
898         } else if( dp->d_name[NLENGTH(dp)-1] == '~' ||
899 	    !check_magic( dirname, MODULES_MAGIC_COOKIE,
900 		MODULES_MAGIC_COOKIE_LENGTH)) {
901 	    continue;
902 	} else {
903 	    dirlst_cur->fi_subdir = NULL;
904 	}
905 
906 	/**
907 	 **  Put the name of the file on the directory list
908 	 **/
909 
910 	dirlst_cur->fi_prefix = prefix;
911 	if( NULL == (dirlst_cur->fi_name = strdup( dp->d_name)))
912 	    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
913 		goto unwind1;
914 
915 	/**
916 	 **  Count even the number of elements in the current list as the
917 	 **  total number of elements read in so far.
918 	 **  Increment the list index to be prepared for the next entry.
919 	 **/
920 
921 	count++;
922 	(*total_count)++;
923 	dirlst_cur++;
924 
925     } /** for **/
926 
927     /**
928      **  Now sort alphabetically what has been read
929      **/
930 
931     if( count > 1)
932 	qsort( dirlst_head, count, sizeof(fi_ent), fi_ent_cmp);
933 
934     /**
935      **  Close the directory, set up return values
936      **/
937 
938     if( -1 == closedir( dirptr))
939 	if( OK != ErrorLogger( ERR_CLOSEDIR, LOC, dir, NULL))
940 	    goto unwind1;
941 
942     *listcount = count;
943     return( dirlst_head); 	/** ----------- EXIT (SUCCESS) ------------> **/
944 
945 unwind1:
946     null_free((void *) &dirlst_cur);
947 
948 unwind0:
949     return( NULL);	 	/** ----------- EXIT (FAILURE) ------------> **/
950 } /** End of 'get_dir' **/
951 
952 /*++++
953  ** ** Function-Header ***************************************************** **
954  ** 									     **
955  **   Function:		dirlst_to_list					     **
956  ** 									     **
957  **   Description:	Transform the passed nested directory list into a    **
958  **			flat list of strings				     **
959  ** 									     **
960  **   First Edition:	1991/10/23					     **
961  ** 									     **
962  **   Parameters:	char	**list		List to be created	     **
963  **			fi_ent	 *dirlst_head	Head of the directory list   **
964  **						to be transformed	     **
965  **			int	  count		Number of elements in the    **
966  **						directory list		     **
967  **			int	 *beginning	Index of the element in List **
968  **						to start appending the file- **
969  **						names at.		     **
970  **			char	 *path		prepend pathname to list     **
971  **			char	 *module	A search pattern	     **
972  **									     **
973  **   Result:		-						     **
974  ** 									     **
975  **   Attached Globals:	-						     **
976  ** 									     **
977  ** ************************************************************************ **
978  ++++*/
979 
dirlst_to_list(char ** list,fi_ent * dirlst_head,int count,int * beginning,char * path,char * module)980 void	dirlst_to_list(	char	**list,
981 			fi_ent	 *dirlst_head,
982 			int	  count,
983 			int	 *beginning,
984 			char	 *path,
985 			char	 *module)
986 {
987     fi_ent	*dirlst_cur;
988     int 	 i;
989     char	*ptr;
990     int		 mlen;
991 
992     /**
993      **  If there's any selection given, figure out its length
994      **/
995 
996     if( module)
997 	mlen = strlen( module);
998 
999     /**
1000      **  Put all files in the directory list at the end of the passed list
1001      **  of character arrays
1002      **/
1003 
1004 #if WITH_DEBUGGING_UTIL_2
1005     ErrorLogger( NO_ERR_START, LOC, _proc_dirlst_to_list, NULL);
1006 #endif
1007 
1008     for( i=0, dirlst_cur=dirlst_head;
1009 	 i<count && dirlst_cur;
1010          i++, dirlst_cur++) {
1011 
1012         if( dirlst_cur->fi_prefix) {
1013 
1014 	    if( path) {
1015 		if( (char *) NULL == stringer(buf, MOD_BUFSIZE,
1016 		    path,"/", dirlst_cur->fi_prefix,"/", dirlst_cur->fi_name,
1017 		    NULL))
1018 			return;
1019 	    } else {
1020 		if( (char *) NULL == stringer(buf, MOD_BUFSIZE,
1021 		    dirlst_cur->fi_prefix,"/", dirlst_cur->fi_name, NULL))
1022 			return;
1023 	    }
1024 
1025 	    ptr = buf;
1026         } else {
1027 	    if( path) {
1028 		if( (char *) NULL == stringer(buf, MOD_BUFSIZE,
1029 		    path,"/", dirlst_cur->fi_name, NULL))
1030 			return;
1031 		ptr = buf;
1032 
1033 	    } else
1034 		ptr = dirlst_cur->fi_name;
1035         }
1036 
1037 	/**
1038 	 **  Check whether this is part of the selected modules ...
1039 	 **/
1040 
1041 	if( !module || !strncmp( module, buf, mlen)) {
1042 
1043 	    /**
1044 	     **  Put this guy on the list
1045 	     **/
1046 
1047 	    if( NULL == (list[(*beginning)++] = strdup( ptr))) {
1048 		if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL)) {
1049 		    while( i--)
1050 			null_free((void *) list + (--(*beginning)));
1051 		    return;		/** ------- EXIT (FAILURE) --------> **/
1052 		}
1053 	    }
1054 	}
1055 
1056 	/**
1057 	 **  recursively descend to subdirectories
1058 	 **/
1059 
1060         if( dirlst_cur->fi_subdir)
1061             dirlst_to_list( list, dirlst_cur->fi_subdir,
1062 		dirlst_cur->fi_listcount, beginning, path, module);
1063     } /** for **/
1064 
1065 } /** end of 'dirlst_to_list' **/
1066 
1067 /*++++
1068  ** ** Function-Header ***************************************************** **
1069  ** 									     **
1070  **   Function:		delete_dirlst					     **
1071  ** 									     **
1072  **   Description:	Delete an entire directory list including all sub-   **
1073  **			directory lists					     **
1074  ** 									     **
1075  **   First Edition:	1991/10/23					     **
1076  ** 									     **
1077  **   Parameters:	fi_ent	*dirlst_head	Head of the list to be re-   **
1078  **						moved			     **
1079  ** 									     **
1080  **   Result:		-						     **
1081  ** 									     **
1082  **   Attached Globals:	-						     **
1083  ** 									     **
1084  ** ************************************************************************ **
1085  ++++*/
1086 
delete_dirlst(fi_ent * dirlst_head,int count)1087 void	delete_dirlst(	fi_ent	*dirlst_head,
1088 			int	 count)
1089 {
1090     fi_ent	*dirlst_cur;
1091     int 	 i;
1092 
1093 #if WITH_DEBUGGING_UTIL_2
1094     ErrorLogger( NO_ERR_START, LOC, _proc_delete_dirlst, NULL);
1095 #endif
1096 
1097     if( !dirlst_head)
1098 	return;
1099 
1100     /**
1101      **  Free all filenames stored in the list
1102      **/
1103 
1104     for( i=0, dirlst_cur=dirlst_head;
1105 	 i<count && dirlst_cur;
1106          i++, dirlst_cur++) {
1107 
1108         null_free((void *) &(dirlst_cur->fi_name));
1109 
1110 	/**
1111 	 **  Recursivle decend to subdirectories
1112 	 **/
1113 
1114         if( dirlst_cur->fi_subdir)
1115             delete_dirlst( dirlst_cur->fi_subdir, dirlst_cur->fi_listcount);
1116     } /** for **/
1117 
1118     /**
1119      **  Remove the entire list
1120      **/
1121 
1122     if( dirlst_head->fi_prefix)
1123 	null_free((void *) &(dirlst_head->fi_prefix));
1124     null_free((void *) &dirlst_head);
1125 
1126 } /** End of 'delete_dirlst' **/
1127 
1128 /*++++
1129  ** ** Function-Header ***************************************************** **
1130  ** 									     **
1131  **   Function:		store_files					     **
1132  ** 									     **
1133  **   Description:	Write a cache file using the given directory list    **
1134  ** 									     **
1135  **   First Edition:	1991/10/23					     **
1136  ** 									     **
1137  **   Parameters: 	fi_ent	*dirlist_head	List of files to be printed  **
1138  **			int	 count		Number of entries in the pas-**
1139  **						sed 'dirlist'		     **
1140  **			int	 tcount		Number of entries to write to**
1141  **						the cache file. This id the  **
1142  **						total number of files stores **
1143  **						in the nested directory lists**
1144  **			char	*dir		The current directory	     **
1145  **									     **
1146  **   Result:		-						     **
1147  ** 									     **
1148  **   Attached Globals:	-						     **
1149  ** 									     **
1150  ** ************************************************************************ **
1151  ++++*/
1152 
1153 #ifdef CACHE_AVAIL
1154 
store_files(fi_ent * dirlst_head,int count,int tcount,char * dir)1155 static	void	store_files(	fi_ent	*dirlst_head,
1156             			int	 count,
1157 				int	 tcount,
1158 				char	*dir)
1159 {
1160     FILE	*fi, *cdir;
1161 
1162 #if WITH_DEBUGGING_UTIL_2
1163     ErrorLogger( NO_ERR_START, LOC, _proc_store_files, NULL);
1164 #endif
1165 
1166     /**
1167      **  Open both, the cache info and the cache dir file
1168      **/
1169 
1170     /** CACHEINFO   **/
1171     if( (char *) NULL == stringer(namebuf, MOD_BUFSIZE,
1172 	dir, "/.moduleavailcache", NULL))
1173             return;
1174     if( NULL == (fi = fopen( namebuf, "w+")))
1175 	return;
1176 
1177     /** CACHEOUTPUT **/
1178     if( (char *) NULL == stringer(namebuf, MOD_BUFSIZE,
1179 	dir, "/.moduleavailcachedir", NULL))
1180             return;
1181     if( NULL == (cdir = fopen( namebuf, "w+"))) {
1182 	if( EOF == fclose( fi))
1183 	    ErrorLogger( ERR_CLOSE, LOC, NULL);
1184 	return;
1185     }
1186 
1187     /**
1188      **  Write the cache id
1189      **  Write the cache itsself
1190      **/
1191 
1192     fprintf( fi, "%s\n", CACHE_VERSION);
1193     fprintf( fi, "%d\n", tcount);
1194     store_dirlst( cdir, fi, dirlst_head, count, dir);
1195 
1196     /**
1197      **  Close the cache files again
1198      **/
1199 
1200     if( EOF == fclose( fi))
1201 	ErrorLogger( ERR_CLOSE, LOC, NULL);
1202 
1203     if( EOF == fclose( cdir))
1204 	ErrorLogger( ERR_CLOSE, LOC, namebuf, NULL);
1205 
1206 } /** End of 'store_files' **/
1207 
1208 /*++++
1209  ** ** Function-Header ***************************************************** **
1210  ** 									     **
1211  **   Function:		store_dirlst					     **
1212  ** 									     **
1213  **   Description:	Write the contents of a cache file		     **
1214  ** 									     **
1215  **   First Edition:	1991/10/23					     **
1216  ** 									     **
1217  **   Parameters:	FILE	*cacheoutput	Output stream to be used     **
1218  **			FILE	*cacheinfo	Cache log file to be written **
1219  **			fi_ent	*dirlist_head	List of files to be printed  **
1220  **			int	 count		Number of entries in the pas-**
1221  **						sed 'dirlist'		     **
1222  **   Result:		-						     **
1223  ** 									     **
1224  **   Attached Globals:	-						     **
1225  ** 									     **
1226  ** ************************************************************************ **
1227  ++++*/
1228 
store_dirlst(FILE * cacheinfo,FILE * cacheoutput,fi_ent * dirlst_head,int count,char * dir)1229 static	void	store_dirlst(	FILE	*cacheinfo,
1230 				FILE	*cacheoutput,
1231 				fi_ent	*dirlst_head,
1232              			int	 count,
1233 				char	*dir)
1234 {
1235     fi_ent* dirlst_cur;
1236     int i;
1237 
1238 #if WITH_DEBUGGING_UTIL_2
1239     ErrorLogger( NO_ERR_START, LOC, _proc_store_dirlst, NULL);
1240 #endif
1241 
1242     for( i=0, dirlst_cur=dirlst_head;
1243 	 i<count && dirlst_cur;
1244          i++, dirlst_cur++) {
1245 
1246 	/**
1247 	 **  Flush the filename to the cache if it is a directory
1248 	 **  Only directories reside in the cache log ....
1249 	 **/
1250 
1251         if( dirlst_cur->fi_stats.st_mode & S_IFDIR) {
1252             if( dirlst_cur->fi_prefix) {
1253                 fprintf( cacheinfo, "%s/%s/%s %d\n", dir, dirlst_cur->fi_prefix,
1254                          dirlst_cur->fi_name, (int)dirlst_cur->fi_stats.st_mtime);
1255             } else {
1256                 fprintf( cacheinfo, "%s/%s %d\n", dir, dirlst_cur->fi_name,
1257                          (int)dirlst_cur->fi_stats.st_mtime);
1258             }
1259         }
1260 
1261 	/**
1262 	 **  Flush out the filename
1263 	 **/
1264 
1265         store_file( cacheoutput, dir, dirlst_cur);
1266 
1267 	/**
1268 	 **  Recursively descent into directories
1269 	 **/
1270 
1271         if( dirlst_cur->fi_subdir)
1272             store_dirlst( cacheinfo, cacheoutput, dirlst_cur->fi_subdir,
1273                           dirlst_cur->fi_listcount, dir);
1274     } /** for **/
1275 
1276     /**
1277      **  Free up everything that has been allocated for the directory
1278      **  list
1279      **/
1280 
1281     if( dirlst_head->fi_prefix)
1282 	null_free((void *) &(dirlst_head->fi_prefix));
1283     null_free((void *) &dirlst_head);
1284 
1285 } /** End of 'store_dirlst' **/
1286 
1287 /*++++
1288  ** ** Function-Header ***************************************************** **
1289  ** 									     **
1290  **   Function:		store_file					     **
1291  ** 									     **
1292  **   Description:	Store the name of the file passed as 'file entry' to **
1293  **			the specified output stream if it isn't a temp file  **
1294  ** 									     **
1295  **   First Edition:	1991/10/23					     **
1296  ** 									     **
1297  **   Parameters:	FILE	*cacheoutput	Output stream to be used     **
1298  **			char	*dir		The current directory	     **
1299  **			fi_ent	*file		According file		     **
1300  ** 									     **
1301  **   Result:		-						     **
1302  ** 									     **
1303  **   Attached Globals:	-						     **
1304  ** 									     **
1305  ** ************************************************************************ **
1306  ++++*/
1307 
store_file(FILE * cacheoutput,char * dir,fi_ent * file)1308 static	void	store_file(	FILE	*cacheoutput,
1309 				char	*dir,
1310 				fi_ent	*file)
1311 {
1312     int filelen;		/** Length of the filename		     **/
1313 
1314 #if WITH_DEBUGGING_UTIL_2
1315     ErrorLogger( NO_ERR_START, LOC, _proc_store_file, NULL);
1316 #endif
1317 
1318     /**
1319      **  Turn any weird characters into ? marks and calculate the length
1320      **  of the filename
1321      **/
1322 
1323     chk4spch( file->fi_name);
1324     filelen = strlen( file->fi_name);
1325 
1326     /**
1327      **  Don't print termporary files which are supposed to end on '~'
1328      **/
1329 
1330     if( file->fi_name[ filelen-1] != '~') {
1331 	fprintf( cacheoutput, "%s/", dir);
1332 	if( file->fi_prefix)
1333 	    fprintf( cacheoutput, "%s/", file->fi_prefix);
1334 	fprintf( cacheoutput, "%s\n", file->fi_name);
1335     }
1336 
1337     /**
1338      **  Finally free up the memory that has been used to store the filename
1339      **/
1340 
1341     null_free((void *) &(file->fi_name));
1342 
1343 } /** End of 'store_file' **/
1344 
1345 
1346 /*++++
1347  ** ** Function-Header ***************************************************** **
1348  ** 									     **
1349  **   Function:		create_cache_list				     **
1350  ** 									     **
1351  **   Description:	Read the passed cache-file and create a list of file-**
1352  **			names out of it					     **
1353  ** 									     **
1354  **   First Edition:	1991/10/23					     **
1355  ** 									     **
1356  **   Parameters:	FILE	*cacheinput	Opened cache file	     **
1357  **			int	*count		Buffer to save the number of **
1358  **						filename to		     **
1359  **			char	*module		A module pattern ...	     **
1360  ** 									     **
1361  **   Result:		char**	NULL		Abort on failure	     **
1362  **				else		Pointer to the just created  **
1363  **						list			     **
1364  **			*count			Number of elements in the    **
1365  **						list			     **
1366  ** 									     **
1367  **   Attached Globals:	-						     **
1368  ** 									     **
1369  ** ************************************************************************ **
1370  ++++*/
1371 
create_cache_list(FILE * cacheinput,int * count,char * module)1372 static	char	**create_cache_list(	FILE	*cacheinput,
1373 					int	*count,
1374 					char	*module)
1375 {
1376     char	**list;		/** Resulting list 			     **/
1377     int      	  i;		/** Loop counter			     **/
1378     int		  mlen;
1379 
1380 #if WITH_DEBUGGING_UTIL_2
1381     ErrorLogger( NO_ERR_START, LOC, _proc_create_cache_list, NULL);
1382 #endif
1383 
1384     /**
1385      **  Check the version of the passed cache file at first
1386      **/
1387 
1388     if( 1 != fscanf( cacheinput, "%s", buf))
1389 	if( OK != ErrorLogger( ERR_READ, LOC, "cache", NULL))
1390 	    return( NULL);      /** ----------- EXIT (I/O error) ----------> **/
1391 
1392     if( strcmp( buf, CACHE_VERSION))
1393 	if( OK != ErrorLogger( ERR_CACHE_INVAL, LOC, buf, NULL))
1394 	    return( NULL);	/** -- EXIT (invalid cache file version) --> **/
1395 
1396     /**
1397      **  Read the number of entries in the cache file and allocate enough
1398      **  memory for the resulting list.
1399      **/
1400 
1401     if( 1 != fscanf( cacheinput, "%d", count))
1402         if( OK != ErrorLogger( ERR_READ, LOC, "cache", NULL))
1403 	    return( NULL);	/** ----------- EXIT (I/O error) ----------> **/
1404 
1405     if( NULL == (list = (char**) module_malloc( *count * sizeof(char**))))
1406         if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
1407 	    return( NULL);	/** ------------ EXIT (FAILURE) -----------> **/
1408 
1409     /**
1410      **  zero the cache
1411      **/
1412 	(void) memset((void *) list, 0, *count * sizeof(char**));
1413 
1414     /**
1415      **  Some selection given?
1416      **/
1417 
1418     if( module)
1419 	mlen = strlen( module);
1420 
1421     /**
1422      **  Now read the cache ...
1423      **/
1424 
1425 #if WITH_DEBUGGING_UTIL_2
1426     ErrorLogger( NO_ERR_DEBUG, LOC, "Read the cache", NULL);
1427 #endif
1428 
1429     for( i=0; i<*count; i++) {
1430 	if( 1 != fscanf( cacheinput, "%s", buf)) {
1431 	    if( OK != ErrorLogger( ERR_READ, LOC, "cache", NULL)) {
1432 		while( --i)
1433 		    null_free((void *) list + i);
1434 		null_free((void *) &list);
1435 		return( NULL);	/** ----------- EXIT (I/O error) ----------> **/
1436 	    }
1437 	}
1438 
1439 	/**
1440 	 **  Check whether this is part of the selected modules ...
1441 	 **/
1442 
1443 	if( module && strncmp( module, buf, mlen)) {
1444 	    --i; --*count;
1445 	    continue;
1446 	}
1447 
1448 	/**
1449 	 **  Save this guy
1450 	 **/
1451 
1452         if( NULL == (list[ i] = strdup( buf))) {
1453 	    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL)) {
1454 		while( --i)
1455 		    null_free((void *) list + i);
1456 		return( NULL);  /** ------------ EXIT (FAILURE) -----------> **/
1457 	    }
1458 	}
1459     }
1460 
1461     /**
1462      **  Success. Return the list created before
1463      **/
1464 
1465     return( list);
1466 
1467 } /** End of 'create_cache_list' **/
1468 
1469 #endif  /** CACHE_AVAIL **/
1470 
1471 /*++++
1472  ** ** Function-Header ***************************************************** **
1473  ** 									     **
1474  **   Function:		delete_cache_list				     **
1475  ** 									     **
1476  **   Description:	Remove an entire list of allocated strings and free  **
1477  **			up the used memory				     **
1478  ** 									     **
1479  **   First Edition:	1991/10/23					     **
1480  ** 									     **
1481  **   Parameters:	char	**list		List of filenames to be print**
1482  **			int	  tcount	Size ofd the list in elements**
1483  ** 									     **
1484  **   Result:		-						     **
1485  ** 									     **
1486  **   Attached Globals:	-						     **
1487  ** 									     **
1488  ** ************************************************************************ **
1489  ++++*/
1490 
delete_cache_list(char ** list,int tcount)1491 void delete_cache_list(	char	**list,
1492 			    int	  tcount)
1493 {
1494     int i;
1495 
1496 #if WITH_DEBUGGING_UTIL_2
1497     ErrorLogger( NO_ERR_START, LOC, _proc_delete_cache_list, NULL);
1498 #endif
1499 
1500     for( i=0; i<tcount; i++)
1501         null_free((void *) (list + i));
1502 
1503     null_free((void *)&list);
1504 
1505 } /** End of 'delete_cache_list' **/
1506 
1507 /*++++
1508  ** ** Function-Header ***************************************************** **
1509  ** 									     **
1510  **   Function:		print_aligned_files				     **
1511  ** 									     **
1512  **   Description:	Print out the filenames passed in a sorted array     **
1513  **			column by column taking care of the order being re-  **
1514  **			flected to the single columns			     **
1515  ** 									     **
1516  **   First Edition:	1991/10/23					     **
1517  ** 									     **
1518  **   Parameters:	char	**list		List of filenames to print   **
1519  **			char	 *path		common path		     **
1520  **			char	 *header	List header		     **
1521  **			int	  tcount	Size of the list in elements **
1522  **			int	  numbered	Controls printing of numbers **
1523  ** 						set to -1 for none	     **
1524  ** 									     **
1525  **   Result:		-						     **
1526  ** 									     **
1527  **   Attached Globals:	g_current_module	The module which is handled  **
1528  **						by the current command	     **
1529  ** 									     **
1530  ** ************************************************************************ **
1531  ++++*/
1532 
print_aligned_files(Tcl_Interp * interp,char * path,char * header,char ** list,int tcount,int numbered)1533 void print_aligned_files(	Tcl_Interp	 *interp,
1534 				char		 *path,
1535 				char		 *header,
1536 				char		**list,
1537 				int		  tcount,
1538 				int		  numbered)
1539 {
1540     struct stat	 stats;
1541     struct tm	*tm;
1542     char 	*symbols, *module, *release;
1543     char	 buffer[ 20];
1544     char	 modulefile[ MOD_BUFSIZE];
1545     char	 modulename[ MOD_BUFSIZE];
1546     char	*timestr;
1547     char	*s;
1548     int		 t;
1549     int		 terminal_width = 80;
1550     int 	 maxlen = 0;
1551     char	*modpath = NULL;
1552 
1553 #if WITH_DEBUGGING_UTIL_1
1554     ErrorLogger( NO_ERR_START, LOC, _proc_print_aligned_files, NULL);
1555 #endif
1556 
1557     /**
1558      **  In case of terse, human output we need to obtain the size of
1559      **  the tty
1560      **/
1561 
1562     if( sw_format & (SW_HUMAN | SW_LONG) ) {
1563 	struct winsize window_size;
1564 	int fd_err = fileno( stderr);
1565 
1566 	if( isatty( fd_err))
1567 	    if( ioctl( fd_err, TIOCGWINSZ, &window_size) != -1)
1568 		terminal_width = (window_size.ws_col == 0) ?
1569 		    80 : window_size.ws_col;
1570     }
1571 
1572     if (! path) {
1573 	modpath = (char *) xgetenv( "MODULEPATH");
1574 	if (! modpath) {
1575 	    if( OK != ErrorLogger( ERR_MODULE_PATH, LOC, NULL)) {
1576 		return;
1577 	    }
1578 	}
1579     }
1580 
1581     /**
1582      **  Scan all entries of the passed list
1583      **/
1584 
1585     _init_file_list();
1586     while( list && tcount-- && *list) {
1587 	/**
1588 	 ** find module[/version] in filename
1589 	 **/
1590 	if( g_current_module = s = strrchr( *list, '/')) {
1591 		*s = 0;
1592 		g_current_module++;
1593 		if (TCL_ERROR == Locate_ModuleFile(interp, g_current_module,
1594 			modulename, modulefile)) {
1595 			g_current_module = strrchr(*list, '/');
1596 			g_current_module++;
1597 		}
1598 		*s = '/';
1599 	}
1600 	if( !stat( *list, &stats)) {
1601 
1602 	    /**
1603 	     **  If the file is a directory, try to source the .modulerc
1604 	     **  file and skip to the next file
1605 	     **/
1606 
1607 	    if( S_ISDIR( stats.st_mode)) {
1608 		SourceRC( interp, *list, modulerc_file);
1609 		SourceVers( interp, *list, g_current_module);
1610 		g_current_module = (char *) NULL;
1611 		list++;
1612 		continue;
1613 	    }
1614 
1615 	    /**
1616 	     **  get a copy of the current item to print in order not to
1617 	     **  change the function input
1618 	     **/
1619 
1620 	    if(path == (char *)NULL) {
1621 		int maxPrefixLength = 0;
1622 
1623 		if (modpath) {
1624 		    /**
1625 		     ** try to find the longest prefix from the module path
1626 		     ** Huge hack!
1627 		     **/
1628 
1629 		    int prefixLength;
1630 		    char *colon;
1631 		    char *prefix = modpath;
1632 
1633 		    while (prefix != (char *) NULL && *prefix != '\0') {
1634 			colon = strchr(prefix, ':');
1635 			prefixLength = colon == NULL ? strlen(prefix) :
1636 						       colon - prefix;
1637 			while (prefix[prefixLength - 1] == '/') {
1638 			    prefixLength -= 1;
1639 			}
1640 			if (prefixLength > maxPrefixLength &&
1641 			    ! strncmp(*list, prefix, prefixLength) &&
1642 			    (*list)[prefixLength] == '/')
1643 			{
1644 			    maxPrefixLength = prefixLength;
1645 			}
1646 
1647 			if (colon != NULL) {
1648 			    colon += 1;
1649 			}
1650 		        prefix = colon;
1651 		    }
1652 
1653 		    /**
1654 		     ** Skip over '/'
1655 		     **/
1656 		    if (maxPrefixLength > 0) {
1657 			maxPrefixLength += 1;
1658 		    }
1659 		}
1660 
1661 		module = strdup(*list + maxPrefixLength);
1662 	    } else {
1663 		t = strlen(path);
1664 		if (*(*list + t) == '/') t++;
1665 		module = strdup(*list + t);
1666 	    }
1667 	    if((char *) NULL == module) {
1668 		if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
1669 		    break;
1670 		else
1671 		    continue;
1672 	    }
1673 
1674 	    /**
1675 	     **  Expand the symbols and get the version of the module
1676 	     **/
1677 
1678 	    if((char *) NULL == (symbols = ExpandVersions( module)))
1679 		symbols = "";
1680 
1681 	    if((sw_format & SW_LONG)
1682 		|| (char *) NULL == (release = strchr( module, '/')))
1683 		release = "";		/* no release info */
1684 	    else
1685 		*release++ = '\0';
1686 
1687 	    /**
1688 	     **  Long or short format
1689 	     **/
1690 
1691 	    if(sw_format & (SW_TERSE|SW_PARSE|SW_HUMAN)) {/** short format **/
1692 		int tmp_len;
1693 
1694 		if( sw_format & SW_PARSE ) {
1695 			sprintf( _file_list_buffer, short_format_full,
1696 				module, release, symbols);
1697 		} else {	/* assume a human readable format */
1698 			if (*symbols) {
1699 				sprintf( _file_list_buffer, short_format_full,
1700 					module, release, symbols);
1701 			} else {
1702 				if (*release) {
1703 					sprintf( _file_list_buffer,
1704 						short_format_part,
1705 						module, release);
1706 				} else {
1707 					sprintf( _file_list_buffer,
1708 						short_format, module);
1709 				}
1710 			}
1711 		}
1712 		_add_file_list( _file_list_buffer);
1713 
1714 		tmp_len = strlen( _file_list_buffer);
1715 		if( tmp_len > maxlen)
1716 		    maxlen = tmp_len;
1717 
1718 	    } else if ( sw_format & SW_LONG) {		/** long format **/
1719 
1720 		/**
1721 		 **  Get the time of last modification
1722 		 **/
1723 
1724 		if((struct tm *) NULL != (tm = gmtime( &stats.st_mtime))) {
1725 		    sprintf( buffer, "%04d/%02d/%02d %2d:%02d:%02d",
1726 			1900+tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
1727 			tm->tm_hour, tm->tm_min, tm->tm_sec);
1728 		    timestr = buffer;
1729 
1730 		} else
1731 		    timestr = "";
1732 
1733 		/**
1734 		 **  Now print and free what we've allocated
1735 		 **/
1736 
1737 		fprintf( stderr, long_format, module, symbols,
1738 		    timestr);
1739 
1740 	    }
1741 
1742 	    null_free((void *) &module);
1743 
1744 	} /** if( !stat) **/
1745 
1746 	list++;
1747 
1748     } /** while **/
1749 
1750     /**
1751      **  In case of terse output we have to flush our buffer
1752      **/
1753 
1754     if( !(sw_format & SW_LONG) ) {
1755 	if( _file_list_wr_ndx > 0)
1756 	    print_terse_files( terminal_width, maxlen, header, numbered);
1757     }
1758 
1759     if (! modpath) {
1760 	null_free((void *)&modpath);
1761     }
1762 
1763 #if WITH_DEBUGGING_UTIL_1
1764     ErrorLogger( NO_ERR_END, LOC, _proc_print_aligned_files, NULL);
1765 #endif
1766 
1767 } /** End of 'print_aligned_files' **/
1768 
1769 /*++++
1770  ** ** Function-Header ***************************************************** **
1771  ** 									     **
1772  **   Function:		print_terse_files				     **
1773  ** 									     **
1774  **   Description:	Print out the filenames in the _file_list array in   **
1775  **			case of terse output 				     **
1776  ** 									     **
1777  **   First Edition:	1991/10/23					     **
1778  ** 									     **
1779  **   Parameters:	int	  terminal_width	Terminal size	     **
1780  **			int	  len			max. filename length **
1781  **			char	 *header		header to print	     **
1782  **			int	  number		value to start number**
1783  ** 							use -1 for none	     **
1784  ** 									     **
1785  **   Result:		-						     **
1786  ** 									     **
1787  **   Attached Globals:	-						     **
1788  ** 									     **
1789  ** ************************************************************************ **
1790  ++++*/
1791 
print_terse_files(int terminal_width,int len,char * header,int numbered)1792 static	void	print_terse_files(  int terminal_width,
1793 				    int len,
1794 				    char *header,
1795 				    int numbered)
1796 {
1797     char	*module;
1798     char	*moduleright;
1799 
1800     /**
1801      **  Print human readable lists
1802      **/
1803 
1804     len += (numbered != -1 ? 6 : 1);
1805     if( sw_format & SW_HUMAN ) {
1806 	int columns = (terminal_width - 1) / len;
1807 	int col_ndx, row_ndx;
1808 	int rows;
1809         int mod_ndx;
1810 
1811 	/**
1812 	 **  Print the header line
1813 	 **/
1814 
1815 	if( header) {
1816 	    int lin_len = terminal_width - strlen( header) - 2;
1817 	    int i;
1818 
1819 	    fprintf( stderr, "\n");
1820 
1821 	    if( lin_len >= 2)
1822 		for( i = 0; i < lin_len / 2; i++)
1823 		    fprintf( stderr, "-");
1824 
1825 	    fprintf( stderr, " %s ", header);
1826 
1827 	    if( lin_len >= 2)
1828 		for( i = 0; i < (lin_len+1) / 2; i++)
1829 		    fprintf( stderr, "-");
1830 
1831 	    fprintf( stderr, "\n");
1832 	}
1833 
1834 	/**
1835 	 **  Print the columns
1836 	 **/
1837 
1838 
1839 	if( !columns)
1840 	    columns = 1;
1841 	rows = (_file_list_wr_ndx + columns - 1) / columns;
1842 
1843         for( row_ndx = 0; row_ndx < rows; row_ndx++) {
1844           for( col_ndx = 0; col_ndx < columns; col_ndx++) {
1845 
1846             mod_ndx = row_ndx + col_ndx * rows;
1847 
1848             if( module = _pick_file_list( mod_ndx)) {
1849               moduleright = _pick_file_list( row_ndx + (col_ndx+1)* rows);
1850 
1851               print_spaced_file( module, len,
1852                                  ( (col_ndx == columns - 1)
1853                                    || (moduleright == (char *) NULL)
1854                                    ? 0 : 1 ),
1855                                   ( (numbered == -1) ? numbered : ++mod_ndx) );
1856 		}
1857 	    }
1858 	    fprintf( stderr, "\n");
1859 	}
1860     }
1861 
1862     /**
1863      **  Print parseable lists
1864      **/
1865 
1866     else {
1867 	while( module = _get_file_list()) {
1868 	    fprintf( stderr, "%s\n", module);
1869 	}
1870     }
1871 }
1872 
1873 /*++++
1874  ** ** Function-Header ***************************************************** **
1875  ** 									     **
1876  **   Function:		_add_file_list   				     **
1877  **			_init_file_list					     **
1878  **			_get_file_list					     **
1879  **			_pick_file_list					     **
1880  ** 									     **
1881  **   Description:	File list functions for terse module display mode    **
1882  ** 									     **
1883  **   First Edition:	1991/10/23					     **
1884  ** 									     **
1885  **   Parameters:	char	*name		Name to be stored	     **
1886  ** 									     **
1887  **   Result:		-						     **
1888  ** 									     **
1889  **   Attached Globals:	-						     **
1890  ** 									     **
1891  ** ************************************************************************ **
1892  ++++*/
1893 
_init_file_list()1894 static	void _init_file_list()
1895 {
1896     if( _file_list_ptr && !_file_list_cnt) {
1897 	null_free((void *) &_file_list_ptr);
1898 	_file_list_cnt = 0;
1899     }
1900 
1901     _file_list_wr_ndx = 0;
1902     _file_list_rd_ndx = 0;
1903 }
1904 
_add_file_list(char * name)1905 static	void _add_file_list( char *name)
1906 {
1907     /**
1908      **  Parameter check
1909      **/
1910 
1911     if( !name || !*name)
1912 	return;
1913 
1914     /**
1915      **  Reallocate if the current array is to small
1916      **/
1917 
1918     if( _file_list_cnt <= _file_list_wr_ndx) {
1919 	_file_list_cnt += FILE_LIST_SEGM_SIZE;
1920 
1921 	if( !_file_list_ptr)
1922 	    _file_list_ptr =
1923 		(char **) module_malloc(_file_list_cnt * sizeof(char *));
1924 	else
1925 	    _file_list_ptr = (char **) module_realloc( _file_list_ptr,
1926 		_file_list_cnt * sizeof(char *));
1927 
1928     }
1929 
1930     /**
1931      **  Save the passed name, if the allocation succeeded
1932      **/
1933 
1934     if( !_file_list_ptr) {
1935 	ErrorLogger( ERR_ALLOC, LOC, NULL);
1936 	_file_list_cnt = 0;
1937 	_file_list_wr_ndx = 0;
1938 	_file_list_rd_ndx = 0;
1939 
1940     } else {
1941 	_file_list_ptr[ _file_list_wr_ndx++] = strdup( name);
1942     }
1943 }
1944 
_get_file_list()1945 static	char *_get_file_list()
1946 {
1947     return((_file_list_rd_ndx < _file_list_wr_ndx) ?
1948 	 _file_list_ptr[ _file_list_rd_ndx++] : (char *) NULL);
1949 }
1950 
_pick_file_list(int ndx)1951 static	char *_pick_file_list( int ndx)
1952 {
1953     return((ndx < _file_list_wr_ndx) ? _file_list_ptr[ ndx] : (char *) NULL);
1954 }
1955 
1956 /*++++
1957  ** ** Function-Header ***************************************************** **
1958  ** 									     **
1959  **   Function:		print_spaced_file				     **
1960  ** 									     **
1961  **   Description:	Print out the passed filename and fill the output    **
1962  **			area up to the passed number of characters	     **
1963  ** 									     **
1964  **   First Edition:	1991/10/23					     **
1965  ** 									     **
1966  **   Parameters:	char	*name		Name to be printed	     **
1967  **			int	 maxwidth	With of the output field to  **
1968  **						be filled up		     **
1969  **			int	 space		Boolean value controlling if **
1970  **						the output area should be    **
1971  **						filled up with spaces or not **
1972  **			int	 number		value to start number list   **
1973  ** 						use -1 for none		     **
1974  ** 									     **
1975  **   Result:		-						     **
1976  ** 									     **
1977  **   Attached Globals:	-						     **
1978  ** 									     **
1979  ** ************************************************************************ **
1980  ++++*/
1981 
print_spaced_file(char * name,int maxwidth,int space,int number)1982 static	void print_spaced_file(	char	*name,
1983 				int	 maxwidth,
1984 				int	 space,
1985 				int	 number)
1986 {
1987     int filelen;		/** Length of the filename to print	     **/
1988 
1989 #if WITH_DEBUGGING_UTIL_2
1990     ErrorLogger( NO_ERR_START, LOC, _proc_print_spaced_file, NULL);
1991 #endif
1992 
1993     chk4spch( name);		/** turn any weird characters into ? marks   **/
1994 
1995     /**
1996      **  Print the name and calculate its length
1997      **/
1998 
1999     filelen = strlen( name);
2000     if( -1 != number) {
2001 	fprintf( stderr, "%3d) ", number);
2002 	filelen += 5;
2003     }
2004 
2005     fprintf(stderr, "%s",  name);
2006 
2007     /**
2008      **  Conditionally fill the output area with spaces
2009      **/
2010 
2011     if( space) {
2012 	putc(' ', stderr);
2013 	while( ++filelen < maxwidth)
2014 	    putc(' ', stderr);
2015     }
2016 
2017 } /** end of 'print_spaced_file' **/
2018 
2019 /*++++
2020  ** ** Function-Header ***************************************************** **
2021  ** 									     **
2022  **   Function:		mkdirnm						     **
2023  ** 									     **
2024  **   Description:	Build a full pathname out of the passed directory    **
2025  **			and file					     **
2026  ** 									     **
2027  **   First Edition:	1991/10/23					     **
2028  ** 									     **
2029  **   Parameters:	char	*dir		The directory to be used     **
2030  **			char	*file		The filename w/o path	     **
2031  ** 									     **
2032  **   Result:		char*	NULL		Compound filename to long    **
2033  **				else		Pointer to the full path     **
2034  ** 									     **
2035  **   Attached Globals:	-						     **
2036  ** 									     **
2037  ** ************************************************************************ **
2038  ++++*/
2039 
mkdirnm(char * dir,char * file)2040 static	char *mkdirnm(	char	*dir,
2041 			char	*file)
2042 {
2043     static char  dirbuf[ MOD_BUFSIZE];	/** Buffer for path creation	     **/
2044 
2045 #if WITH_DEBUGGING_UTIL_2
2046     ErrorLogger( NO_ERR_START, LOC, _proc_mkdirnm, ", dir='", dir, ", file='",
2047 	file, "'", NULL);
2048 #endif
2049 
2050     /**
2051      **  If only a file is given, or the file is in the current directory
2052      **  return just the file.
2053      **/
2054 
2055     if( dir == NULL || *dir == '\0' || !strcmp(dir,"."))
2056 	return( strcpy( dirbuf, file));
2057 
2058     /**
2059      **  Check whether the full path fits into the buffer
2060      **/
2061 
2062     if( (int) ( strlen( dir) + 1 + strlen( file) + 1 ) > MOD_BUFSIZE) {
2063 	if( OK != ErrorLogger( ERR_NAMETOLONG, LOC, dir, file, NULL))
2064 	    return( NULL);
2065     }
2066 
2067     /**
2068      **  Copy directory and file into the buffer taking care that there will
2069      **  be no double slash ...
2070      **/
2071 
2072     strcpy( dirbuf, dir);
2073     if( dir[ strlen( dir) - 1] != '/' && file[0] != '/')
2074 	strcat( dirbuf, "/");
2075     return( strcat( dirbuf, file));
2076 
2077 } /** End of 'mkdirnm' **/
2078 
2079 /*++++
2080  ** ** Function-Header ***************************************************** **
2081  ** 									     **
2082  **   Function:		fi_ent_cmp					     **
2083  ** 									     **
2084  **   Description:	compares two file entry structures		     **
2085  **			Different cmdline arguments (i.e. -u, -c, -t, -z)    **
2086  **			will change what value is compared. As a default,    **
2087  **			the name is used.				     **
2088  **									     **
2089  **   Notes:		This procedure is used as comparison function for    **
2090  **			qsort()					 	     **
2091  ** 									     **
2092  **   First Edition:	1991/10/23					     **
2093  ** 									     **
2094  **   Parameters:	const void *fi1		First file entry	     **
2095  **			const void *fi2		Second one to compare	     **
2096  ** 									     **
2097  **   Result:		int	1 	fi2 > fi1			     **
2098  **    				-1	fi2 < fi1			     **
2099  **	  			0	fi2 == fi1			     **
2100  ** 									     **
2101  **   Attached Globals:							     **
2102  ** 									     **
2103  ** ************************************************************************ **
2104  ++++*/
2105 
fi_ent_cmp(const void * fi1,const void * fi2)2106 static	int fi_ent_cmp(	const void	*fi1,
2107 			const void	*fi2)
2108 {
2109 
2110 #ifdef DEF_COLLATE_BY_NUMBER
2111 	return colcomp( ((fi_ent*)fi1)->fi_name, ((fi_ent*)fi2)->fi_name);
2112 #else
2113 #  ifdef HAVE_STRCOLL
2114 	return strcoll( ((fi_ent*)fi1)->fi_name, ((fi_ent*)fi2)->fi_name);
2115 #  else
2116 	return strcmp( ((fi_ent*)fi1)->fi_name, ((fi_ent*)fi2)->fi_name);
2117 #  endif
2118 #endif
2119 }
2120