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