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