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