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