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