| 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 | } |