Bug Summary

File:src/gnu/usr.bin/cvs/src/modules.c
Warning:line 315, column 6
Value stored to 'cwd_saved' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name modules.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/gnu/usr.bin/cvs/obj/src -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -I . -I /usr/src/gnu/usr.bin/cvs/src -I .. -I . -I /usr/src/gnu/usr.bin/cvs/lib -I /usr/src/gnu/usr.bin/cvs/diff -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/usr.bin/cvs/obj/src -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/gnu/usr.bin/cvs/src/modules.c
1/*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 *
5 * You may distribute under the terms of the GNU General Public License
6 * as specified in the README file that comes with the CVS source distribution.
7 *
8 * Modules
9 *
10 * Functions for accessing the modules file.
11 *
12 * The modules file supports basically three formats of lines:
13 * key [options] directory files... [ -x directory [files] ] ...
14 * key [options] directory [ -x directory [files] ] ...
15 * key -a aliases...
16 *
17 * The -a option allows an aliasing step in the parsing of the modules
18 * file. The "aliases" listed on a line following the -a are
19 * processed one-by-one, as if they were specified as arguments on the
20 * command line.
21 */
22
23#include <assert.h>
24#include "cvs.h"
25#include "savecwd.h"
26
27
28/* Defines related to the syntax of the modules file. */
29
30/* Options in modules file. Note that it is OK to use GNU getopt features;
31 we already are arranging to make sure we are using the getopt distributed
32 with CVS. */
33#define CVSMODULE_OPTS"+ad:i:lo:e:s:t:u:" "+ad:i:lo:e:s:t:u:"
34
35/* Special delimiter. */
36#define CVSMODULE_SPEC'&' '&'
37
38struct sortrec
39{
40 /* Name of the module, malloc'd. */
41 char *modname;
42 /* If Status variable is set, this is either def_status or the malloc'd
43 name of the status. If Status is not set, the field is left
44 uninitialized. */
45 char *status;
46 /* Pointer to a malloc'd array which contains (1) the raw contents
47 of the options and arguments, excluding comments, (2) a '\0',
48 and (3) the storage for the "comment" field. */
49 char *rest;
50 char *comment;
51};
52
53static int sort_order PROTO((const PTR l, const PTR r))(const void * l, const void * r);
54static void save_d PROTO((char *k, int ks, char *d, int ds))(char *k, int ks, char *d, int ds);
55
56
57/*
58 * Open the modules file, and die if the CVSROOT environment variable
59 * was not set. If the modules file does not exist, that's fine, and
60 * a warning message is displayed and a NULL is returned.
61 */
62DBM *
63open_module ()
64{
65 char *mfile;
66 DBM *retval;
67
68 if (current_parsed_root == NULL((void*)0))
69 {
70 error (0, 0, "must set the CVSROOT environment variable");
71 error (1, 0, "or specify the '-d' global option");
72 }
73 mfile = xmalloc (strlen (current_parsed_root->directory)
74 + sizeof (CVSROOTADM"CVSROOT")
75 + sizeof (CVSROOTADM_MODULES"modules") + 3);
76 (void) sprintf (mfile, "%s/%s/%s", current_parsed_root->directory,
77 CVSROOTADM"CVSROOT", CVSROOTADM_MODULES"modules");
78 retval = dbm_openmydbm_open (mfile, O_RDONLY0x0000, 0666);
79 free (mfile);
80 return retval;
81}
82
83/*
84 * Close the modules file, if the open succeeded, that is
85 */
86void
87close_module (db)
88 DBM *db;
89{
90 if (db != NULL((void*)0))
91 dbm_closemydbm_close (db);
92}
93
94/*
95 * This is the recursive function that processes a module name.
96 * It calls back the passed routine for each directory of a module
97 * It runs the post checkout or post tag proc from the modules file
98 */
99int
100do_module (db, mname, m_type, msg, callback_proc, where, shorten,
101 local_specified, run_module_prog, build_dirs, extra_arg)
102 DBM *db;
103 char *mname;
104 enum mtype m_type;
105 char *msg;
106 CALLBACKPROC callback_proc;
107 char *where;
108 int shorten;
109 int local_specified;
110 int run_module_prog;
111 int build_dirs;
112 char *extra_arg;
113{
114 char *checkin_prog = NULL((void*)0);
115 char *checkout_prog = NULL((void*)0);
116 char *export_prog = NULL((void*)0);
117 char *tag_prog = NULL((void*)0);
118 char *update_prog = NULL((void*)0);
119 struct saved_cwd cwd;
120 int cwd_saved = 0;
121 char *line;
122 int modargc;
123 int xmodargc;
124 char **modargv;
125 char **xmodargv = NULL((void*)0);
126 /* Found entry from modules file, including options and such. */
127 char *value = NULL((void*)0);
128 char *mwhere = NULL((void*)0);
129 char *mfile = NULL((void*)0);
130 char *spec_opt = NULL((void*)0);
131 char *xvalue = NULL((void*)0);
132 int alias = 0;
133 datum key, val;
134 char *cp;
135 int c, err = 0;
136 int nonalias_opt = 0;
137
138#ifdef SERVER_SUPPORT1
139 int restore_server_dir = 0;
140 char *server_dir_to_restore = NULL((void*)0);
141 if (trace)
142 {
143 char *buf;
144
145 /* We use cvs_outerr, rather than fprintf to stderr, because
146 this may be called by server code with error_use_protocol
147 set. */
148 buf = xmalloc (100
149 + strlen (mname)
150 + strlen (msg)
151 + (where ? strlen (where) : 0)
152 + (extra_arg ? strlen (extra_arg) : 0));
153 sprintf (buf, "%s-> do_module (%s, %s, %s, %s)\n",
154 CLIENT_SERVER_STR((server_active) ? "S" : " "),
155 mname, msg, where ? where : "",
156 extra_arg ? extra_arg : "");
157 cvs_outerr (buf, 0);
158 free (buf);
159 }
160#endif
161
162 /* Don't process absolute directories. Anything else could be a security
163 * problem. Before this check was put in place:
164 *
165 * $ cvs -d:fork:/cvsroot co /foo
166 * cvs server: warning: cannot make directory CVS in /: Permission denied
167 * cvs [server aborted]: cannot make directory /foo: Permission denied
168 * $
169 */
170 if (isabsolute (mname))
171 error (1, 0, "Absolute module reference invalid: `%s'", mname);
172
173 /* Similarly for directories that attempt to step above the root of the
174 * repository.
175 */
176 if (pathname_levels (mname) > 0)
177 error (1, 0, "up-level in module reference (`..') invalid: `%s'.",
178 mname);
179
180 /* if this is a directory to ignore, add it to that list */
181 if (mname[0] == '!' && mname[1] != '\0')
182 {
183 ign_dir_add (mname+1);
184 goto do_module_return;
185 }
186
187 /* strip extra stuff from the module name */
188 strip_trailing_slashes (mname);
189
190 /*
191 * Look up the module using the following scheme:
192 * 1) look for mname as a module name
193 * 2) look for mname as a directory
194 * 3) look for mname as a file
195 * 4) take mname up to the first slash and look it up as a module name
196 * (this is for checking out only part of a module)
197 */
198
199 /* look it up as a module name */
200 key.dptr = mname;
201 key.dsize = strlen (key.dptr);
202 if (db != NULL((void*)0))
203 val = dbm_fetchmydbm_fetch (db, key);
204 else
205 val.dptr = NULL((void*)0);
206 if (val.dptr != NULL((void*)0))
207 {
208 /* copy and null terminate the value */
209 value = xmalloc (val.dsize + 1);
210 memcpy (value, val.dptr, val.dsize);
211 value[val.dsize] = '\0';
212
213 /* If the line ends in a comment, strip it off */
214 if ((cp = strchr (value, '#')) != NULL((void*)0))
215 *cp = '\0';
216 else
217 cp = value + val.dsize;
218
219 /* Always strip trailing spaces */
220 while (cp > value && isspace ((unsigned char) *--cp))
221 *cp = '\0';
222
223 mwhere = xstrdup (mname);
224 goto found;
225 }
226 else
227 {
228 char *file;
229 char *attic_file;
230 char *acp;
231 int is_found = 0;
232
233 /* check to see if mname is a directory or file */
234 file = xmalloc (strlen (current_parsed_root->directory)
235 + strlen (mname) + sizeof(RCSEXT",v") + 2);
236 (void) sprintf (file, "%s/%s", current_parsed_root->directory, mname);
237 attic_file = xmalloc (strlen (current_parsed_root->directory)
238 + strlen (mname)
239 + sizeof (CVSATTIC"Attic") + sizeof (RCSEXT",v") + 3);
240 if ((acp = strrchr (mname, '/')) != NULL((void*)0))
241 {
242 *acp = '\0';
243 (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory,
244 mname, CVSATTIC"Attic", acp + 1, RCSEXT",v");
245 *acp = '/';
246 }
247 else
248 (void) sprintf (attic_file, "%s/%s/%s%s", current_parsed_root->directory,
249 CVSATTIC"Attic", mname, RCSEXT",v");
250
251 if (isdir (file))
252 {
253 modargv = xmalloc (sizeof (*modargv));
254 modargv[0] = xstrdup (mname);
255 modargc = 1;
256 is_found = 1;
257 }
258 else
259 {
260 (void) strcat (file, RCSEXT",v");
261 if (isfile (file) || isfile (attic_file))
262 {
263 /* if mname was a file, we have to split it into "dir file" */
264 if ((cp = strrchr (mname, '/')) != NULL((void*)0) && cp != mname)
265 {
266 modargv = xmalloc (2 * sizeof (*modargv));
267 modargv[0] = xmalloc (strlen (mname) + 2);
268 strncpy (modargv[0], mname, cp - mname);
269 modargv[0][cp - mname] = '\0';
270 modargv[1] = xstrdup (cp + 1);
271 modargc = 2;
272 }
273 else
274 {
275 /*
276 * the only '/' at the beginning or no '/' at all
277 * means the file we are interested in is in CVSROOT
278 * itself so the directory should be '.'
279 */
280 if (cp == mname)
281 {
282 /* drop the leading / if specified */
283 modargv = xmalloc (2 * sizeof (*modargv));
284 modargv[0] = xstrdup (".");
285 modargv[1] = xstrdup (mname + 1);
286 modargc = 2;
287 }
288 else
289 {
290 /* otherwise just copy it */
291 modargv = xmalloc (2 * sizeof (*modargv));
292 modargv[0] = xstrdup (".");
293 modargv[1] = xstrdup (mname);
294 modargc = 2;
295 }
296 }
297 is_found = 1;
298 }
299 }
300 free (attic_file);
301 free (file);
302
303 if (is_found)
304 {
305 assert (value == NULL)((value == ((void*)0)) ? (void)0 : __assert2("/usr/src/gnu/usr.bin/cvs/src/modules.c"
, 305, __func__, "value == NULL"))
;
306
307 /* OK, we have now set up modargv with the actual
308 file/directory we want to work on. We duplicate a
309 small amount of code here because the vast majority of
310 the code after the "found" label does not pertain to
311 the case where we found a file/directory rather than
312 finding an entry in the modules file. */
313 if (save_cwd (&cwd))
314 error_exit ();
315 cwd_saved = 1;
Value stored to 'cwd_saved' is never read
316
317 err += callback_proc (modargc, modargv, where, mwhere, mfile,
318 shorten,
319 local_specified, mname, msg);
320
321 free_names (&modargc, modargv);
322
323 /* cd back to where we started. */
324 if (restore_cwd (&cwd, NULL((void*)0)))
325 error_exit ();
326 free_cwd (&cwd);
327 cwd_saved = 0;
328
329 goto do_module_return;
330 }
331 }
332
333 /* look up everything to the first / as a module */
334 if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL((void*)0))
335 {
336 /* Make the slash the new end of the string temporarily */
337 *cp = '\0';
338 key.dptr = mname;
339 key.dsize = strlen (key.dptr);
340
341 /* do the lookup */
342 if (db != NULL((void*)0))
343 val = dbm_fetchmydbm_fetch (db, key);
344 else
345 val.dptr = NULL((void*)0);
346
347 /* if we found it, clean up the value and life is good */
348 if (val.dptr != NULL((void*)0))
349 {
350 char *cp2;
351
352 /* copy and null terminate the value */
353 value = xmalloc (val.dsize + 1);
354 memcpy (value, val.dptr, val.dsize);
355 value[val.dsize] = '\0';
356
357 /* If the line ends in a comment, strip it off */
358 if ((cp2 = strchr (value, '#')) != NULL((void*)0))
359 *cp2 = '\0';
360 else
361 cp2 = value + val.dsize;
362
363 /* Always strip trailing spaces */
364 while (cp2 > value && isspace ((unsigned char) *--cp2))
365 *cp2 = '\0';
366
367 /* mwhere gets just the module name */
368 mwhere = xstrdup (mname);
369 mfile = cp + 1;
370
371 /* put the / back in mname */
372 *cp = '/';
373
374 goto found;
375 }
376
377 /* put the / back in mname */
378 *cp = '/';
379 }
380
381 /* if we got here, we couldn't find it using our search, so give up */
382 error (0, 0, "cannot find module `%s' - ignored", mname);
383 err++;
384 goto do_module_return;
385
386
387 /*
388 * At this point, we found what we were looking for in one
389 * of the many different forms.
390 */
391 found:
392
393 /* remember where we start */
394 if (save_cwd (&cwd))
395 error_exit ();
396 cwd_saved = 1;
397
398 assert (value != NULL)((value != ((void*)0)) ? (void)0 : __assert2("/usr/src/gnu/usr.bin/cvs/src/modules.c"
, 398, __func__, "value != NULL"))
;
399
400 /* search the value for the special delimiter and save for later */
401 if ((cp = strchr (value, CVSMODULE_SPEC'&')) != NULL((void*)0))
402 {
403 *cp = '\0'; /* null out the special char */
404 spec_opt = cp + 1; /* save the options for later */
405
406 /* strip whitespace if necessary */
407 while (cp > value && isspace ((unsigned char) *--cp))
408 *cp = '\0';
409 }
410
411 /* don't do special options only part of a module was specified */
412 if (mfile != NULL((void*)0))
413 spec_opt = NULL((void*)0);
414
415 /*
416 * value now contains one of the following:
417 * 1) dir
418 * 2) dir file
419 * 3) the value from modules without any special args
420 * [ args ] dir [file] [file] ...
421 * or -a module [ module ] ...
422 */
423
424 /* Put the value on a line with XXX prepended for getopt to eat */
425 line = xmalloc (strlen (value) + 5);
426 strcpy(line, "XXX ");
427 strcpy(line + 4, value);
428
429 /* turn the line into an argv[] array */
430 line2argv (&xmodargc, &xmodargv, line, " \t");
431 free (line);
432 modargc = xmodargc;
433 modargv = xmodargv;
434
435 /* parse the args */
436 optind = 0;
437 while ((c = getopt (modargc, modargv, CVSMODULE_OPTS"+ad:i:lo:e:s:t:u:")) != -1)
438 {
439 switch (c)
440 {
441 case 'a':
442 alias = 1;
443 break;
444 case 'd':
445 if (mwhere)
446 free (mwhere);
447 mwhere = xstrdup (optarg);
448 nonalias_opt = 1;
449 break;
450 case 'i':
451 if (checkin_prog)
452 free (checkin_prog);
453 checkin_prog = xstrdup (optarg);
454 nonalias_opt = 1;
455 break;
456 case 'l':
457 local_specified = 1;
458 nonalias_opt = 1;
459 break;
460 case 'o':
461 if (checkout_prog)
462 free (checkout_prog);
463 checkout_prog = xstrdup (optarg);
464 nonalias_opt = 1;
465 break;
466 case 'e':
467 if (export_prog)
468 free (export_prog);
469 export_prog = xstrdup (optarg);
470 nonalias_opt = 1;
471 break;
472 case 't':
473 if (tag_prog)
474 free (tag_prog);
475 tag_prog = xstrdup (optarg);
476 nonalias_opt = 1;
477 break;
478 case 'u':
479 if (update_prog)
480 free (update_prog);
481 update_prog = xstrdup (optarg);
482 nonalias_opt = 1;
483 break;
484 case '?':
485 error (0, 0,
486 "modules file has invalid option for key %s value %s",
487 key.dptr, value);
488 err++;
489 goto do_module_return;
490 }
491 }
492 modargc -= optind;
493 modargv += optind;
494 if (modargc == 0 && spec_opt == NULL((void*)0))
495 {
496 error (0, 0, "modules file missing directory for module %s", mname);
497 ++err;
498 goto do_module_return;
499 }
500
501 if (alias && nonalias_opt)
502 {
503 /* The documentation has never said it is legal to specify
504 -a along with another option. And I believe that in the past
505 CVS has ignored the options other than -a, more or less, in this
506 situation. */
507 error (0, 0, "\
508-a cannot be specified in the modules file along with other options");
509 ++err;
510 goto do_module_return;
511 }
512
513 /* if this was an alias, call ourselves recursively for each module */
514 if (alias)
515 {
516 int i;
517
518 for (i = 0; i < modargc; i++)
519 {
520 if (strcmp (mname, modargv[i]) == 0)
521 error (0, 0,
522 "module `%s' in modules file contains infinite loop",
523 mname);
524 else
525 err += do_module (db, modargv[i], m_type, msg, callback_proc,
526 where, shorten, local_specified,
527 run_module_prog, build_dirs, extra_arg);
528 }
529 goto do_module_return;
530 }
531
532 if (mfile != NULL((void*)0) && modargc > 1)
533 {
534 error (0, 0, "\
535module `%s' is a request for a file in a module which is not a directory",
536 mname);
537 ++err;
538 goto do_module_return;
539 }
540
541 /* otherwise, process this module */
542 if (modargc > 0)
543 {
544 err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten,
545 local_specified, mname, msg);
546 }
547 else
548 {
549 /*
550 * we had nothing but special options, so we must
551 * make the appropriate directory and cd to it
552 */
553 char *dir;
554
555 if (!build_dirs)
556 goto do_special;
557
558 dir = where ? where : (mwhere ? mwhere : mname);
559 /* XXX - think about making null repositories at each dir here
560 instead of just at the bottom */
561 make_directories (dir);
562 if ( CVS_CHDIRchdir (dir) < 0)
563 {
564 error (0, errno(*__errno()), "cannot chdir to %s", dir);
565 spec_opt = NULL((void*)0);
566 err++;
567 goto do_special;
568 }
569 if (!isfile (CVSADM"CVS"))
570 {
571 char *nullrepos;
572
573 nullrepos = emptydir_name ();
574
575 Create_Admin (".", dir,
576 nullrepos, (char *) NULL((void*)0), (char *) NULL((void*)0), 0, 0, 1);
577 if (!noexec)
578 {
579 FILE *fp;
580
581 fp = open_file (CVSADM_ENTSTAT"CVS/Entries.Static", "w+");
582 if (fclose (fp) == EOF(-1))
583 error (1, errno(*__errno()), "cannot close %s", CVSADM_ENTSTAT"CVS/Entries.Static");
584#ifdef SERVER_SUPPORT1
585 if (server_active)
586 server_set_entstat (dir, nullrepos);
587#endif
588 }
589 free (nullrepos);
590 }
591 }
592
593 /* if there were special include args, process them now */
594
595 do_special:
596
597 free_names (&xmodargc, xmodargv);
598 xmodargv = NULL((void*)0);
599
600 /* blow off special options if -l was specified */
601 if (local_specified)
602 spec_opt = NULL((void*)0);
603
604#ifdef SERVER_SUPPORT1
605 /* We want to check out into the directory named by the module.
606 So we set a global variable which tells the server to glom that
607 directory name onto the front. A cleaner approach would be some
608 way of passing it down to the recursive call, through the
609 callback_proc, to start_recursion, and then into the update_dir in
610 the struct file_info. That way the "Updating foo" message could
611 print the actual directory we are checking out into.
612
613 For local CVS, this is handled by the chdir call above
614 (directly or via the callback_proc). */
615 if (server_active && spec_opt != NULL((void*)0))
616 {
617 char *change_to;
618
619 change_to = where ? where : (mwhere ? mwhere : mname);
620 server_dir_to_restore = server_dir;
621 restore_server_dir = 1;
622 server_dir =
623 xmalloc ((server_dir_to_restore != NULL((void*)0)
624 ? strlen (server_dir_to_restore)
625 : 0)
626 + strlen (change_to)
627 + 5);
628 server_dir[0] = '\0';
629 if (server_dir_to_restore != NULL((void*)0))
630 {
631 strcat (server_dir, server_dir_to_restore);
632 strcat (server_dir, "/");
633 }
634 strcat (server_dir, change_to);
635 }
636#endif
637
638 while (spec_opt != NULL((void*)0))
639 {
640 char *next_opt;
641
642 cp = strchr (spec_opt, CVSMODULE_SPEC'&');
643 if (cp != NULL((void*)0))
644 {
645 /* save the beginning of the next arg */
646 next_opt = cp + 1;
647
648 /* strip whitespace off the end */
649 do
650 *cp = '\0';
651 while (cp > spec_opt && isspace ((unsigned char) *--cp));
652 }
653 else
654 next_opt = NULL((void*)0);
655
656 /* strip whitespace from front */
657 while (isspace ((unsigned char) *spec_opt))
658 spec_opt++;
659
660 if (*spec_opt == '\0')
661 error (0, 0, "Mal-formed %c option for module %s - ignored",
662 CVSMODULE_SPEC'&', mname);
663 else
664 err += do_module (db, spec_opt, m_type, msg, callback_proc,
665 (char *) NULL((void*)0), 0, local_specified,
666 run_module_prog, build_dirs, extra_arg);
667 spec_opt = next_opt;
668 }
669
670#ifdef SERVER_SUPPORT1
671 if (server_active && restore_server_dir)
672 {
673 free (server_dir);
674 server_dir = server_dir_to_restore;
675 }
676#endif
677
678 /* write out the checkin/update prog files if necessary */
679#ifdef SERVER_SUPPORT1
680 if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding)
681 {
682 if (checkin_prog != NULL((void*)0))
683 server_prog (where ? where : mname, checkin_prog, PROG_CHECKIN);
684 if (update_prog != NULL((void*)0))
685 server_prog (where ? where : mname, update_prog, PROG_UPDATE);
686 }
687 else
688#endif
689 if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog)
690 {
691 FILE *fp;
692
693 if (checkin_prog != NULL((void*)0))
694 {
695 fp = open_file (CVSADM_CIPROG"CVS/Checkin.prog", "w+");
696 (void) fprintf (fp, "%s\n", checkin_prog);
697 if (fclose (fp) == EOF(-1))
698 error (1, errno(*__errno()), "cannot close %s", CVSADM_CIPROG"CVS/Checkin.prog");
699 }
700 if (update_prog != NULL((void*)0))
701 {
702 fp = open_file (CVSADM_UPROG"CVS/Update.prog", "w+");
703 (void) fprintf (fp, "%s\n", update_prog);
704 if (fclose (fp) == EOF(-1))
705 error (1, errno(*__errno()), "cannot close %s", CVSADM_UPROG"CVS/Update.prog");
706 }
707 }
708
709 /* cd back to where we started */
710 if (restore_cwd (&cwd, NULL((void*)0)))
711 error_exit ();
712 free_cwd (&cwd);
713 cwd_saved = 0;
714
715 /* run checkout or tag prog if appropriate */
716 if (err == 0 && run_module_prog)
717 {
718 if ((m_type == TAG && tag_prog != NULL((void*)0)) ||
719 (m_type == CHECKOUT && checkout_prog != NULL((void*)0)) ||
720 (m_type == EXPORT && export_prog != NULL((void*)0)))
721 {
722 /*
723 * If a relative pathname is specified as the checkout, tag
724 * or export proc, try to tack on the current "where" value.
725 * if we can't find a matching program, just punt and use
726 * whatever is specified in the modules file.
727 */
728 char *real_prog = NULL((void*)0);
729 char *prog = (m_type == TAG ? tag_prog :
730 (m_type == CHECKOUT ? checkout_prog : export_prog));
731 char *real_where = (where != NULL((void*)0) ? where : mwhere);
732 char *expanded_path;
733
734 if ((*prog != '/') && (*prog != '.'))
735 {
736 real_prog = xmalloc (strlen (real_where) + strlen (prog)
737 + 10);
738 (void) sprintf (real_prog, "%s/%s", real_where, prog);
739 if (isfile (real_prog))
740 prog = real_prog;
741 }
742
743 /* XXX can we determine the line number for this entry??? */
744 expanded_path = expand_path (prog, "modules", 0);
745 if (expanded_path != NULL((void*)0))
746 {
747 run_setup (expanded_path);
748 run_arg (real_where);
749
750 if (extra_arg)
751 run_arg (extra_arg);
752
753 if (!quiet)
754 {
755 cvs_output (program_name, 0);
756 cvs_output (" ", 1);
757 cvs_output (command_name, 0);
758 cvs_output (": Executing '", 0);
759 run_print (stdout(&__sF[1]));
760 cvs_output ("'\n", 0);
761 cvs_flushout ();
762 }
763 err += run_exec (RUN_TTY(char *)0, RUN_TTY(char *)0, RUN_TTY(char *)0, RUN_NORMAL0x0000);
764 free (expanded_path);
765 }
766 free (real_prog);
767 }
768 }
769
770 do_module_return:
771 /* clean up */
772 if (xmodargv != NULL((void*)0))
773 free_names (&xmodargc, xmodargv);
774 if (mwhere)
775 free (mwhere);
776 if (checkin_prog)
777 free (checkin_prog);
778 if (checkout_prog)
779 free (checkout_prog);
780 if (export_prog)
781 free (export_prog);
782 if (tag_prog)
783 free (tag_prog);
784 if (update_prog)
785 free (update_prog);
786 if (cwd_saved)
787 free_cwd (&cwd);
788 if (value != NULL((void*)0))
789 free (value);
790
791 if (xvalue != NULL((void*)0))
792 free (xvalue);
793 return (err);
794}
795
796/* - Read all the records from the modules database into an array.
797 - Sort the array depending on what format is desired.
798 - Print the array in the format desired.
799
800 Currently, there are only two "desires":
801
802 1. Sort by module name and format the whole entry including switches,
803 files and the comment field: (Including aliases)
804
805 modulename -s switches, one per line, even if
806 -i it has many switches.
807 Directories and files involved, formatted
808 to cover multiple lines if necessary.
809 # Comment, also formatted to cover multiple
810 # lines if necessary.
811
812 2. Sort by status field string and print: (*not* including aliases)
813
814 modulename STATUS Directories and files involved, formatted
815 to cover multiple lines if necessary.
816 # Comment, also formatted to cover multiple
817 # lines if necessary.
818*/
819
820static struct sortrec *s_head;
821
822static int s_max = 0; /* Number of elements allocated */
823static int s_count = 0; /* Number of elements used */
824
825static int Status; /* Nonzero if the user is
826 interested in status
827 information as well as
828 module name */
829static char def_status[] = "NONE";
830
831/* Sort routine for qsort:
832 - If we want the "Status" field to be sorted, check it first.
833 - Then compare the "module name" fields. Since they are unique, we don't
834 have to look further.
835*/
836static int
837sort_order (l, r)
838 const PTRvoid * l;
839 const PTRvoid * r;
840{
841 int i;
842 const struct sortrec *left = (const struct sortrec *) l;
843 const struct sortrec *right = (const struct sortrec *) r;
844
845 if (Status)
846 {
847 /* If Sort by status field, compare them. */
848 if ((i = strcmp (left->status, right->status)) != 0)
849 return (i);
850 }
851 return (strcmp (left->modname, right->modname));
852}
853
854static void
855save_d (k, ks, d, ds)
856 char *k;
857 int ks;
858 char *d;
859 int ds;
860{
861 char *cp, *cp2;
862 struct sortrec *s_rec;
863
864 if (Status && *d == '-' && *(d + 1) == 'a')
865 return; /* We want "cvs co -s" and it is an alias! */
866
867 if (s_count == s_max)
868 {
869 s_max += 64;
870 s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head));
871 }
872 s_rec = &s_head[s_count];
873 s_rec->modname = cp = xmalloc (ks + 1);
874 (void) strncpy (cp, k, ks);
875 *(cp + ks) = '\0';
876
877 s_rec->rest = cp2 = xmalloc (ds + 1);
878 cp = d;
879 *(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */
880
881 while (isspace ((unsigned char) *cp))
882 cp++;
883 /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
884 while (*cp)
885 {
886 if (isspace ((unsigned char) *cp))
887 {
888 *cp2++ = ' ';
889 while (isspace ((unsigned char) *cp))
890 cp++;
891 }
892 else
893 *cp2++ = *cp++;
894 }
895 *cp2 = '\0';
896
897 /* Look for the "-s statusvalue" text */
898 if (Status)
899 {
900 s_rec->status = def_status;
901
902 for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL((void*)0); cp = ++cp2)
903 {
904 if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
905 {
906 char *status_start;
907
908 cp2 += 3;
909 status_start = cp2;
910 while (*cp2 != ' ' && *cp2 != '\0')
911 cp2++;
912 s_rec->status = xmalloc (cp2 - status_start + 1);
913 strncpy (s_rec->status, status_start, cp2 - status_start);
914 s_rec->status[cp2 - status_start] = '\0';
915 cp = cp2;
916 break;
917 }
918 }
919 }
920 else
921 cp = s_rec->rest;
922
923 /* Find comment field, clean up on all three sides & compress blanks */
924 if ((cp2 = cp = strchr (cp, '#')) != NULL((void*)0))
925 {
926 if (*--cp2 == ' ')
927 *cp2 = '\0';
928 if (*++cp == ' ')
929 cp++;
930 s_rec->comment = cp;
931 }
932 else
933 s_rec->comment = "";
934
935 s_count++;
936}
937
938/* Print out the module database as we know it. If STATUS is
939 non-zero, print out status information for each module. */
940
941void
942cat_module (status)
943 int status;
944{
945 DBM *db;
946 datum key, val;
947 int i, c, wid, argc, cols = 80, indent, fill;
948 int moduleargc;
949 struct sortrec *s_h;
950 char *cp, *cp2, **argv;
951 char **moduleargv;
952
953 Status = status;
954
955 /* Read the whole modules file into allocated records */
956 if (!(db = open_module ()))
957 error (1, 0, "failed to open the modules file");
958
959 for (key = dbm_firstkeymydbm_firstkey (db); key.dptr != NULL((void*)0); key = dbm_nextkeymydbm_nextkey (db))
960 {
961 val = dbm_fetchmydbm_fetch (db, key);
962 if (val.dptr != NULL((void*)0))
963 save_d (key.dptr, key.dsize, val.dptr, val.dsize);
964 }
965
966 close_module (db);
967
968 /* Sort the list as requested */
969 qsort ((PTRvoid *) s_head, s_count, sizeof (struct sortrec), sort_order);
970
971 /*
972 * Run through the sorted array and format the entries
973 * indent = space for modulename + space for status field
974 */
975 indent = 12 + (status * 12);
976 fill = cols - (indent + 2);
977 for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
978 {
979 char *line;
980
981 /* Print module name (and status, if wanted) */
982 line = xmalloc (strlen (s_h->modname) + 15);
983 sprintf (line, "%-12s", s_h->modname);
984 cvs_output (line, 0);
985 free (line);
986 if (status)
987 {
988 line = xmalloc (strlen (s_h->status) + 15);
989 sprintf (line, " %-11s", s_h->status);
990 cvs_output (line, 0);
991 free (line);
992 }
993
994 line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 15);
995 /* Parse module file entry as command line and print options */
996 (void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
997 line2argv (&moduleargc, &moduleargv, line, " \t");
998 free (line);
999 argc = moduleargc;
1000 argv = moduleargv;
1001
1002 optind = 0;
1003 wid = 0;
1004 while ((c = getopt (argc, argv, CVSMODULE_OPTS"+ad:i:lo:e:s:t:u:")) != -1)
1005 {
1006 if (c == '?') {
1007 error (0, 0, "invalid module line");
1008 return;
1009 }
1010
1011 if (!status)
1012 {
1013 if (c == 'a' || c == 'l')
1014 {
1015 char buf[5];
1016
1017 sprintf (buf, " -%c", c);
1018 cvs_output (buf, 0);
1019 wid += 3; /* Could just set it to 3 */
1020 }
1021 else
1022 {
1023 char buf[10];
1024
1025 if (strlen (optarg) + 4 + wid > (unsigned) fill)
1026 {
1027 int j;
1028
1029 cvs_output ("\n", 1);
1030 for (j = 0; j < indent; ++j)
1031 cvs_output (" ", 1);
1032 wid = 0;
1033 }
1034 sprintf (buf, " -%c ", c);
1035 cvs_output (buf, 0);
1036 cvs_output (optarg, 0);
1037 wid += strlen (optarg) + 4;
1038 }
1039 }
1040 }
1041 argc -= optind;
1042 argv += optind;
1043
1044 /* Format and Print all the files and directories */
1045 for (; argc--; argv++)
1046 {
1047 if (strlen (*argv) + wid > (unsigned) fill)
1048 {
1049 int j;
1050
1051 cvs_output ("\n", 1);
1052 for (j = 0; j < indent; ++j)
1053 cvs_output (" ", 1);
1054 wid = 0;
1055 }
1056 cvs_output (" ", 1);
1057 cvs_output (*argv, 0);
1058 wid += strlen (*argv) + 1;
1059 }
1060 cvs_output ("\n", 1);
1061
1062 /* Format the comment field -- save_d (), compressed spaces */
1063 for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
1064 {
1065 int j;
1066
1067 for (j = 0; j < indent; ++j)
1068 cvs_output (" ", 1);
1069 cvs_output (" # ", 0);
1070 if (strlen (cp2) < (unsigned) (fill - 2))
1071 {
1072 cvs_output (cp2, 0);
1073 cvs_output ("\n", 1);
1074 break;
1075 }
1076 cp += fill - 2;
1077 while (*cp != ' ' && cp > cp2)
1078 cp--;
1079 if (cp == cp2)
1080 {
1081 cvs_output (cp2, 0);
1082 cvs_output ("\n", 1);
1083 break;
1084 }
1085
1086 *cp++ = '\0';
1087 cvs_output (cp2, 0);
1088 cvs_output ("\n", 1);
1089 }
1090
1091 free_names(&moduleargc, moduleargv);
1092 /* FIXME-leak: here is where we would free s_h->modname, s_h->rest,
1093 and if applicable, s_h->status. Not exactly a memory leak,
1094 in the sense that we are about to exit(), but may be worth
1095 noting if we ever do a multithreaded server or something of
1096 the sort. */
1097 }
1098 /* FIXME-leak: as above, here is where we would free s_head. */
1099}