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