File: | src/gnu/usr.bin/cvs/src/modules.c |
Warning: | line 315, column 6 Value stored to 'cwd_saved' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
38 | struct 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 | |
53 | static int sort_order PROTO((const PTR l, const PTR r))(const void * l, const void * r); |
54 | static 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 | */ |
62 | DBM * |
63 | open_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 | */ |
86 | void |
87 | close_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 | */ |
99 | int |
100 | do_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, "\ |
535 | module `%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 | |
820 | static struct sortrec *s_head; |
821 | |
822 | static int s_max = 0; /* Number of elements allocated */ |
823 | static int s_count = 0; /* Number of elements used */ |
824 | |
825 | static int Status; /* Nonzero if the user is |
826 | interested in status |
827 | information as well as |
828 | module name */ |
829 | static 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 | */ |
836 | static int |
837 | sort_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 | |
854 | static void |
855 | save_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 | |
941 | void |
942 | cat_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 | } |