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