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:" 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)); 54 static void save_d PROTO((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) 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) 75 + sizeof (CVSROOTADM_MODULES) + 3); 76 (void) sprintf (mfile, "%s/%s/%s", current_parsed_root->directory, 77 CVSROOTADM, CVSROOTADM_MODULES); 78 retval = dbm_open (mfile, O_RDONLY, 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) 91 dbm_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; 115 char *checkout_prog = NULL; 116 char *export_prog = NULL; 117 char *tag_prog = NULL; 118 char *update_prog = NULL; 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; 126 /* Found entry from modules file, including options and such. */ 127 char *value = NULL; 128 char *mwhere = NULL; 129 char *mfile = NULL; 130 char *spec_opt = NULL; 131 char *xvalue = NULL; 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_SUPPORT 139 int restore_server_dir = 0; 140 char *server_dir_to_restore = NULL; 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, 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 /* if this is a directory to ignore, add it to that list */ 174 if (mname[0] == '!' && mname[1] != '\0') 175 { 176 ign_dir_add (mname+1); 177 goto do_module_return; 178 } 179 180 /* strip extra stuff from the module name */ 181 strip_trailing_slashes (mname); 182 183 /* 184 * Look up the module using the following scheme: 185 * 1) look for mname as a module name 186 * 2) look for mname as a directory 187 * 3) look for mname as a file 188 * 4) take mname up to the first slash and look it up as a module name 189 * (this is for checking out only part of a module) 190 */ 191 192 /* look it up as a module name */ 193 key.dptr = mname; 194 key.dsize = strlen (key.dptr); 195 if (db != NULL) 196 val = dbm_fetch (db, key); 197 else 198 val.dptr = NULL; 199 if (val.dptr != NULL) 200 { 201 /* copy and null terminate the value */ 202 value = xmalloc (val.dsize + 1); 203 memcpy (value, val.dptr, val.dsize); 204 value[val.dsize] = '\0'; 205 206 /* If the line ends in a comment, strip it off */ 207 if ((cp = strchr (value, '#')) != NULL) 208 *cp = '\0'; 209 else 210 cp = value + val.dsize; 211 212 /* Always strip trailing spaces */ 213 while (cp > value && isspace ((unsigned char) *--cp)) 214 *cp = '\0'; 215 216 mwhere = xstrdup (mname); 217 goto found; 218 } 219 else 220 { 221 char *file; 222 char *attic_file; 223 char *acp; 224 int is_found = 0; 225 226 /* check to see if mname is a directory or file */ 227 file = xmalloc (strlen (current_parsed_root->directory) 228 + strlen (mname) + sizeof(RCSEXT) + 2); 229 (void) sprintf (file, "%s/%s", current_parsed_root->directory, mname); 230 attic_file = xmalloc (strlen (current_parsed_root->directory) 231 + strlen (mname) 232 + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3); 233 if ((acp = strrchr (mname, '/')) != NULL) 234 { 235 *acp = '\0'; 236 (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory, 237 mname, CVSATTIC, acp + 1, RCSEXT); 238 *acp = '/'; 239 } 240 else 241 (void) sprintf (attic_file, "%s/%s/%s%s", current_parsed_root->directory, 242 CVSATTIC, mname, RCSEXT); 243 244 if (isdir (file)) 245 { 246 modargv = xmalloc (sizeof (*modargv)); 247 modargv[0] = xstrdup (mname); 248 modargc = 1; 249 is_found = 1; 250 } 251 else 252 { 253 (void) strcat (file, RCSEXT); 254 if (isfile (file) || isfile (attic_file)) 255 { 256 /* if mname was a file, we have to split it into "dir file" */ 257 if ((cp = strrchr (mname, '/')) != NULL && cp != mname) 258 { 259 modargv = xmalloc (2 * sizeof (*modargv)); 260 modargv[0] = xmalloc (strlen (mname) + 2); 261 strncpy (modargv[0], mname, cp - mname); 262 modargv[0][cp - mname] = '\0'; 263 modargv[1] = xstrdup (cp + 1); 264 modargc = 2; 265 } 266 else 267 { 268 /* 269 * the only '/' at the beginning or no '/' at all 270 * means the file we are interested in is in CVSROOT 271 * itself so the directory should be '.' 272 */ 273 if (cp == mname) 274 { 275 /* drop the leading / if specified */ 276 modargv = xmalloc (2 * sizeof (*modargv)); 277 modargv[0] = xstrdup ("."); 278 modargv[1] = xstrdup (mname + 1); 279 modargc = 2; 280 } 281 else 282 { 283 /* otherwise just copy it */ 284 modargv = xmalloc (2 * sizeof (*modargv)); 285 modargv[0] = xstrdup ("."); 286 modargv[1] = xstrdup (mname); 287 modargc = 2; 288 } 289 } 290 is_found = 1; 291 } 292 } 293 free (attic_file); 294 free (file); 295 296 if (is_found) 297 { 298 assert (value == NULL); 299 300 /* OK, we have now set up modargv with the actual 301 file/directory we want to work on. We duplicate a 302 small amount of code here because the vast majority of 303 the code after the "found" label does not pertain to 304 the case where we found a file/directory rather than 305 finding an entry in the modules file. */ 306 if (save_cwd (&cwd)) 307 error_exit (); 308 cwd_saved = 1; 309 310 err += callback_proc (modargc, modargv, where, mwhere, mfile, 311 shorten, 312 local_specified, mname, msg); 313 314 free_names (&modargc, modargv); 315 316 /* cd back to where we started. */ 317 if (restore_cwd (&cwd, NULL)) 318 error_exit (); 319 free_cwd (&cwd); 320 cwd_saved = 0; 321 322 goto do_module_return; 323 } 324 } 325 326 /* look up everything to the first / as a module */ 327 if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL) 328 { 329 /* Make the slash the new end of the string temporarily */ 330 *cp = '\0'; 331 key.dptr = mname; 332 key.dsize = strlen (key.dptr); 333 334 /* do the lookup */ 335 if (db != NULL) 336 val = dbm_fetch (db, key); 337 else 338 val.dptr = NULL; 339 340 /* if we found it, clean up the value and life is good */ 341 if (val.dptr != NULL) 342 { 343 char *cp2; 344 345 /* copy and null terminate the value */ 346 value = xmalloc (val.dsize + 1); 347 memcpy (value, val.dptr, val.dsize); 348 value[val.dsize] = '\0'; 349 350 /* If the line ends in a comment, strip it off */ 351 if ((cp2 = strchr (value, '#')) != NULL) 352 *cp2 = '\0'; 353 else 354 cp2 = value + val.dsize; 355 356 /* Always strip trailing spaces */ 357 while (cp2 > value && isspace ((unsigned char) *--cp2)) 358 *cp2 = '\0'; 359 360 /* mwhere gets just the module name */ 361 mwhere = xstrdup (mname); 362 mfile = cp + 1; 363 364 /* put the / back in mname */ 365 *cp = '/'; 366 367 goto found; 368 } 369 370 /* put the / back in mname */ 371 *cp = '/'; 372 } 373 374 /* if we got here, we couldn't find it using our search, so give up */ 375 error (0, 0, "cannot find module `%s' - ignored", mname); 376 err++; 377 goto do_module_return; 378 379 380 /* 381 * At this point, we found what we were looking for in one 382 * of the many different forms. 383 */ 384 found: 385 386 /* remember where we start */ 387 if (save_cwd (&cwd)) 388 error_exit (); 389 cwd_saved = 1; 390 391 assert (value != NULL); 392 393 /* search the value for the special delimiter and save for later */ 394 if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL) 395 { 396 *cp = '\0'; /* null out the special char */ 397 spec_opt = cp + 1; /* save the options for later */ 398 399 /* strip whitespace if necessary */ 400 while (cp > value && isspace ((unsigned char) *--cp)) 401 *cp = '\0'; 402 } 403 404 /* don't do special options only part of a module was specified */ 405 if (mfile != NULL) 406 spec_opt = NULL; 407 408 /* 409 * value now contains one of the following: 410 * 1) dir 411 * 2) dir file 412 * 3) the value from modules without any special args 413 * [ args ] dir [file] [file] ... 414 * or -a module [ module ] ... 415 */ 416 417 /* Put the value on a line with XXX prepended for getopt to eat */ 418 line = xmalloc (strlen (value) + 5); 419 strcpy(line, "XXX "); 420 strcpy(line + 4, value); 421 422 /* turn the line into an argv[] array */ 423 line2argv (&xmodargc, &xmodargv, line, " \t"); 424 free (line); 425 modargc = xmodargc; 426 modargv = xmodargv; 427 428 /* parse the args */ 429 optind = 0; 430 while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1) 431 { 432 switch (c) 433 { 434 case 'a': 435 alias = 1; 436 break; 437 case 'd': 438 if (mwhere) 439 free (mwhere); 440 mwhere = xstrdup (optarg); 441 nonalias_opt = 1; 442 break; 443 case 'i': 444 if (checkin_prog) 445 free (checkin_prog); 446 checkin_prog = xstrdup (optarg); 447 nonalias_opt = 1; 448 break; 449 case 'l': 450 local_specified = 1; 451 nonalias_opt = 1; 452 break; 453 case 'o': 454 if (checkout_prog) 455 free (checkout_prog); 456 checkout_prog = xstrdup (optarg); 457 nonalias_opt = 1; 458 break; 459 case 'e': 460 if (export_prog) 461 free (export_prog); 462 export_prog = xstrdup (optarg); 463 nonalias_opt = 1; 464 break; 465 case 't': 466 if (tag_prog) 467 free (tag_prog); 468 tag_prog = xstrdup (optarg); 469 nonalias_opt = 1; 470 break; 471 case 'u': 472 if (update_prog) 473 free (update_prog); 474 update_prog = xstrdup (optarg); 475 nonalias_opt = 1; 476 break; 477 case '?': 478 error (0, 0, 479 "modules file has invalid option for key %s value %s", 480 key.dptr, value); 481 err++; 482 goto do_module_return; 483 } 484 } 485 modargc -= optind; 486 modargv += optind; 487 if (modargc == 0 && spec_opt == NULL) 488 { 489 error (0, 0, "modules file missing directory for module %s", mname); 490 ++err; 491 goto do_module_return; 492 } 493 494 if (alias && nonalias_opt) 495 { 496 /* The documentation has never said it is legal to specify 497 -a along with another option. And I believe that in the past 498 CVS has ignored the options other than -a, more or less, in this 499 situation. */ 500 error (0, 0, "\ 501 -a cannot be specified in the modules file along with other options"); 502 ++err; 503 goto do_module_return; 504 } 505 506 /* if this was an alias, call ourselves recursively for each module */ 507 if (alias) 508 { 509 int i; 510 511 for (i = 0; i < modargc; i++) 512 { 513 if (strcmp (mname, modargv[i]) == 0) 514 error (0, 0, 515 "module `%s' in modules file contains infinite loop", 516 mname); 517 else 518 err += do_module (db, modargv[i], m_type, msg, callback_proc, 519 where, shorten, local_specified, 520 run_module_prog, build_dirs, extra_arg); 521 } 522 goto do_module_return; 523 } 524 525 if (mfile != NULL && modargc > 1) 526 { 527 error (0, 0, "\ 528 module `%s' is a request for a file in a module which is not a directory", 529 mname); 530 ++err; 531 goto do_module_return; 532 } 533 534 /* otherwise, process this module */ 535 if (modargc > 0) 536 { 537 err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten, 538 local_specified, mname, msg); 539 } 540 else 541 { 542 /* 543 * we had nothing but special options, so we must 544 * make the appropriate directory and cd to it 545 */ 546 char *dir; 547 548 if (!build_dirs) 549 goto do_special; 550 551 dir = where ? where : (mwhere ? mwhere : mname); 552 /* XXX - think about making null repositories at each dir here 553 instead of just at the bottom */ 554 make_directories (dir); 555 if ( CVS_CHDIR (dir) < 0) 556 { 557 error (0, errno, "cannot chdir to %s", dir); 558 spec_opt = NULL; 559 err++; 560 goto do_special; 561 } 562 if (!isfile (CVSADM)) 563 { 564 char *nullrepos; 565 566 nullrepos = emptydir_name (); 567 568 Create_Admin (".", dir, 569 nullrepos, (char *) NULL, (char *) NULL, 0, 0, 1); 570 if (!noexec) 571 { 572 FILE *fp; 573 574 fp = open_file (CVSADM_ENTSTAT, "w+"); 575 if (fclose (fp) == EOF) 576 error (1, errno, "cannot close %s", CVSADM_ENTSTAT); 577 #ifdef SERVER_SUPPORT 578 if (server_active) 579 server_set_entstat (dir, nullrepos); 580 #endif 581 } 582 free (nullrepos); 583 } 584 } 585 586 /* if there were special include args, process them now */ 587 588 do_special: 589 590 free_names (&xmodargc, xmodargv); 591 xmodargv = NULL; 592 593 /* blow off special options if -l was specified */ 594 if (local_specified) 595 spec_opt = NULL; 596 597 #ifdef SERVER_SUPPORT 598 /* We want to check out into the directory named by the module. 599 So we set a global variable which tells the server to glom that 600 directory name onto the front. A cleaner approach would be some 601 way of passing it down to the recursive call, through the 602 callback_proc, to start_recursion, and then into the update_dir in 603 the struct file_info. That way the "Updating foo" message could 604 print the actual directory we are checking out into. 605 606 For local CVS, this is handled by the chdir call above 607 (directly or via the callback_proc). */ 608 if (server_active && spec_opt != NULL) 609 { 610 char *change_to; 611 612 change_to = where ? where : (mwhere ? mwhere : mname); 613 server_dir_to_restore = server_dir; 614 restore_server_dir = 1; 615 server_dir = 616 xmalloc ((server_dir_to_restore != NULL 617 ? strlen (server_dir_to_restore) 618 : 0) 619 + strlen (change_to) 620 + 5); 621 server_dir[0] = '\0'; 622 if (server_dir_to_restore != NULL) 623 { 624 strcat (server_dir, server_dir_to_restore); 625 strcat (server_dir, "/"); 626 } 627 strcat (server_dir, change_to); 628 } 629 #endif 630 631 while (spec_opt != NULL) 632 { 633 char *next_opt; 634 635 cp = strchr (spec_opt, CVSMODULE_SPEC); 636 if (cp != NULL) 637 { 638 /* save the beginning of the next arg */ 639 next_opt = cp + 1; 640 641 /* strip whitespace off the end */ 642 do 643 *cp = '\0'; 644 while (cp > spec_opt && isspace ((unsigned char) *--cp)); 645 } 646 else 647 next_opt = NULL; 648 649 /* strip whitespace from front */ 650 while (isspace ((unsigned char) *spec_opt)) 651 spec_opt++; 652 653 if (*spec_opt == '\0') 654 error (0, 0, "Mal-formed %c option for module %s - ignored", 655 CVSMODULE_SPEC, mname); 656 else 657 err += do_module (db, spec_opt, m_type, msg, callback_proc, 658 (char *) NULL, 0, local_specified, 659 run_module_prog, build_dirs, extra_arg); 660 spec_opt = next_opt; 661 } 662 663 #ifdef SERVER_SUPPORT 664 if (server_active && restore_server_dir) 665 { 666 free (server_dir); 667 server_dir = server_dir_to_restore; 668 } 669 #endif 670 671 /* write out the checkin/update prog files if necessary */ 672 #ifdef SERVER_SUPPORT 673 if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding) 674 { 675 if (checkin_prog != NULL) 676 server_prog (where ? where : mname, checkin_prog, PROG_CHECKIN); 677 if (update_prog != NULL) 678 server_prog (where ? where : mname, update_prog, PROG_UPDATE); 679 } 680 else 681 #endif 682 if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog) 683 { 684 FILE *fp; 685 686 if (checkin_prog != NULL) 687 { 688 fp = open_file (CVSADM_CIPROG, "w+"); 689 (void) fprintf (fp, "%s\n", checkin_prog); 690 if (fclose (fp) == EOF) 691 error (1, errno, "cannot close %s", CVSADM_CIPROG); 692 } 693 if (update_prog != NULL) 694 { 695 fp = open_file (CVSADM_UPROG, "w+"); 696 (void) fprintf (fp, "%s\n", update_prog); 697 if (fclose (fp) == EOF) 698 error (1, errno, "cannot close %s", CVSADM_UPROG); 699 } 700 } 701 702 /* cd back to where we started */ 703 if (restore_cwd (&cwd, NULL)) 704 error_exit (); 705 free_cwd (&cwd); 706 cwd_saved = 0; 707 708 /* run checkout or tag prog if appropriate */ 709 if (err == 0 && run_module_prog) 710 { 711 if ((m_type == TAG && tag_prog != NULL) || 712 (m_type == CHECKOUT && checkout_prog != NULL) || 713 (m_type == EXPORT && export_prog != NULL)) 714 { 715 /* 716 * If a relative pathname is specified as the checkout, tag 717 * or export proc, try to tack on the current "where" value. 718 * if we can't find a matching program, just punt and use 719 * whatever is specified in the modules file. 720 */ 721 char *real_prog = NULL; 722 char *prog = (m_type == TAG ? tag_prog : 723 (m_type == CHECKOUT ? checkout_prog : export_prog)); 724 char *real_where = (where != NULL ? where : mwhere); 725 char *expanded_path; 726 727 if ((*prog != '/') && (*prog != '.')) 728 { 729 real_prog = xmalloc (strlen (real_where) + strlen (prog) 730 + 10); 731 (void) sprintf (real_prog, "%s/%s", real_where, prog); 732 if (isfile (real_prog)) 733 prog = real_prog; 734 } 735 736 /* XXX can we determine the line number for this entry??? */ 737 expanded_path = expand_path (prog, "modules", 0); 738 if (expanded_path != NULL) 739 { 740 run_setup (expanded_path); 741 run_arg (real_where); 742 743 if (extra_arg) 744 run_arg (extra_arg); 745 746 if (!quiet) 747 { 748 cvs_output (program_name, 0); 749 cvs_output (" ", 1); 750 cvs_output (command_name, 0); 751 cvs_output (": Executing '", 0); 752 run_print (stdout); 753 cvs_output ("'\n", 0); 754 cvs_flushout (); 755 } 756 err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); 757 free (expanded_path); 758 } 759 free (real_prog); 760 } 761 } 762 763 do_module_return: 764 /* clean up */ 765 if (xmodargv != NULL) 766 free_names (&xmodargc, xmodargv); 767 if (mwhere) 768 free (mwhere); 769 if (checkin_prog) 770 free (checkin_prog); 771 if (checkout_prog) 772 free (checkout_prog); 773 if (export_prog) 774 free (export_prog); 775 if (tag_prog) 776 free (tag_prog); 777 if (update_prog) 778 free (update_prog); 779 if (cwd_saved) 780 free_cwd (&cwd); 781 if (value != NULL) 782 free (value); 783 784 if (xvalue != NULL) 785 free (xvalue); 786 return (err); 787 } 788 789 /* - Read all the records from the modules database into an array. 790 - Sort the array depending on what format is desired. 791 - Print the array in the format desired. 792 793 Currently, there are only two "desires": 794 795 1. Sort by module name and format the whole entry including switches, 796 files and the comment field: (Including aliases) 797 798 modulename -s switches, one per line, even if 799 -i it has many switches. 800 Directories and files involved, formatted 801 to cover multiple lines if necessary. 802 # Comment, also formatted to cover multiple 803 # lines if necessary. 804 805 2. Sort by status field string and print: (*not* including aliases) 806 807 modulename STATUS 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 813 static struct sortrec *s_head; 814 815 static int s_max = 0; /* Number of elements allocated */ 816 static int s_count = 0; /* Number of elements used */ 817 818 static int Status; /* Nonzero if the user is 819 interested in status 820 information as well as 821 module name */ 822 static char def_status[] = "NONE"; 823 824 /* Sort routine for qsort: 825 - If we want the "Status" field to be sorted, check it first. 826 - Then compare the "module name" fields. Since they are unique, we don't 827 have to look further. 828 */ 829 static int 830 sort_order (l, r) 831 const PTR l; 832 const PTR r; 833 { 834 int i; 835 const struct sortrec *left = (const struct sortrec *) l; 836 const struct sortrec *right = (const struct sortrec *) r; 837 838 if (Status) 839 { 840 /* If Sort by status field, compare them. */ 841 if ((i = strcmp (left->status, right->status)) != 0) 842 return (i); 843 } 844 return (strcmp (left->modname, right->modname)); 845 } 846 847 static void 848 save_d (k, ks, d, ds) 849 char *k; 850 int ks; 851 char *d; 852 int ds; 853 { 854 char *cp, *cp2; 855 struct sortrec *s_rec; 856 857 if (Status && *d == '-' && *(d + 1) == 'a') 858 return; /* We want "cvs co -s" and it is an alias! */ 859 860 if (s_count == s_max) 861 { 862 s_max += 64; 863 s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head)); 864 } 865 s_rec = &s_head[s_count]; 866 s_rec->modname = cp = xmalloc (ks + 1); 867 (void) strncpy (cp, k, ks); 868 *(cp + ks) = '\0'; 869 870 s_rec->rest = cp2 = xmalloc (ds + 1); 871 cp = d; 872 *(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */ 873 874 while (isspace ((unsigned char) *cp)) 875 cp++; 876 /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */ 877 while (*cp) 878 { 879 if (isspace ((unsigned char) *cp)) 880 { 881 *cp2++ = ' '; 882 while (isspace ((unsigned char) *cp)) 883 cp++; 884 } 885 else 886 *cp2++ = *cp++; 887 } 888 *cp2 = '\0'; 889 890 /* Look for the "-s statusvalue" text */ 891 if (Status) 892 { 893 s_rec->status = def_status; 894 895 for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2) 896 { 897 if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ') 898 { 899 char *status_start; 900 901 cp2 += 3; 902 status_start = cp2; 903 while (*cp2 != ' ' && *cp2 != '\0') 904 cp2++; 905 s_rec->status = xmalloc (cp2 - status_start + 1); 906 strncpy (s_rec->status, status_start, cp2 - status_start); 907 s_rec->status[cp2 - status_start] = '\0'; 908 cp = cp2; 909 break; 910 } 911 } 912 } 913 else 914 cp = s_rec->rest; 915 916 /* Find comment field, clean up on all three sides & compress blanks */ 917 if ((cp2 = cp = strchr (cp, '#')) != NULL) 918 { 919 if (*--cp2 == ' ') 920 *cp2 = '\0'; 921 if (*++cp == ' ') 922 cp++; 923 s_rec->comment = cp; 924 } 925 else 926 s_rec->comment = ""; 927 928 s_count++; 929 } 930 931 /* Print out the module database as we know it. If STATUS is 932 non-zero, print out status information for each module. */ 933 934 void 935 cat_module (status) 936 int status; 937 { 938 DBM *db; 939 datum key, val; 940 int i, c, wid, argc, cols = 80, indent, fill; 941 int moduleargc; 942 struct sortrec *s_h; 943 char *cp, *cp2, **argv; 944 char **moduleargv; 945 946 Status = status; 947 948 /* Read the whole modules file into allocated records */ 949 if (!(db = open_module ())) 950 error (1, 0, "failed to open the modules file"); 951 952 for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db)) 953 { 954 val = dbm_fetch (db, key); 955 if (val.dptr != NULL) 956 save_d (key.dptr, key.dsize, val.dptr, val.dsize); 957 } 958 959 close_module (db); 960 961 /* Sort the list as requested */ 962 qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order); 963 964 /* 965 * Run through the sorted array and format the entries 966 * indent = space for modulename + space for status field 967 */ 968 indent = 12 + (status * 12); 969 fill = cols - (indent + 2); 970 for (s_h = s_head, i = 0; i < s_count; i++, s_h++) 971 { 972 char *line; 973 974 /* Print module name (and status, if wanted) */ 975 line = xmalloc (strlen (s_h->modname) + 15); 976 sprintf (line, "%-12s", s_h->modname); 977 cvs_output (line, 0); 978 free (line); 979 if (status) 980 { 981 line = xmalloc (strlen (s_h->status) + 15); 982 sprintf (line, " %-11s", s_h->status); 983 cvs_output (line, 0); 984 free (line); 985 } 986 987 line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 15); 988 /* Parse module file entry as command line and print options */ 989 (void) sprintf (line, "%s %s", s_h->modname, s_h->rest); 990 line2argv (&moduleargc, &moduleargv, line, " \t"); 991 free (line); 992 argc = moduleargc; 993 argv = moduleargv; 994 995 optind = 0; 996 wid = 0; 997 while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1) 998 { 999 if (!status) 1000 { 1001 if (c == 'a' || c == 'l') 1002 { 1003 char buf[5]; 1004 1005 sprintf (buf, " -%c", c); 1006 cvs_output (buf, 0); 1007 wid += 3; /* Could just set it to 3 */ 1008 } 1009 else 1010 { 1011 char buf[10]; 1012 1013 if (strlen (optarg) + 4 + wid > (unsigned) fill) 1014 { 1015 int j; 1016 1017 cvs_output ("\n", 1); 1018 for (j = 0; j < indent; ++j) 1019 cvs_output (" ", 1); 1020 wid = 0; 1021 } 1022 sprintf (buf, " -%c ", c); 1023 cvs_output (buf, 0); 1024 cvs_output (optarg, 0); 1025 wid += strlen (optarg) + 4; 1026 } 1027 } 1028 } 1029 argc -= optind; 1030 argv += optind; 1031 1032 /* Format and Print all the files and directories */ 1033 for (; argc--; argv++) 1034 { 1035 if (strlen (*argv) + wid > (unsigned) fill) 1036 { 1037 int j; 1038 1039 cvs_output ("\n", 1); 1040 for (j = 0; j < indent; ++j) 1041 cvs_output (" ", 1); 1042 wid = 0; 1043 } 1044 cvs_output (" ", 1); 1045 cvs_output (*argv, 0); 1046 wid += strlen (*argv) + 1; 1047 } 1048 cvs_output ("\n", 1); 1049 1050 /* Format the comment field -- save_d (), compressed spaces */ 1051 for (cp2 = cp = s_h->comment; *cp; cp2 = cp) 1052 { 1053 int j; 1054 1055 for (j = 0; j < indent; ++j) 1056 cvs_output (" ", 1); 1057 cvs_output (" # ", 0); 1058 if (strlen (cp2) < (unsigned) (fill - 2)) 1059 { 1060 cvs_output (cp2, 0); 1061 cvs_output ("\n", 1); 1062 break; 1063 } 1064 cp += fill - 2; 1065 while (*cp != ' ' && cp > cp2) 1066 cp--; 1067 if (cp == cp2) 1068 { 1069 cvs_output (cp2, 0); 1070 cvs_output ("\n", 1); 1071 break; 1072 } 1073 1074 *cp++ = '\0'; 1075 cvs_output (cp2, 0); 1076 cvs_output ("\n", 1); 1077 } 1078 1079 free_names(&moduleargc, moduleargv); 1080 /* FIXME-leak: here is where we would free s_h->modname, s_h->rest, 1081 and if applicable, s_h->status. Not exactly a memory leak, 1082 in the sense that we are about to exit(), but may be worth 1083 noting if we ever do a multithreaded server or something of 1084 the sort. */ 1085 } 1086 /* FIXME-leak: as above, here is where we would free s_head. */ 1087 } 1088