1 /* Print log messages and other information about RCS files. */ 2 3 /* Copyright 1982, 1988, 1989 Walter Tichy 4 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 5 Distributed under license by the Free Software Foundation, Inc. 6 7 This file is part of RCS. 8 9 RCS is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 RCS is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with RCS; see the file COPYING. 21 If not, write to the Free Software Foundation, 22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 24 Report problems and direct all questions to: 25 26 rcs-bugs@cs.purdue.edu 27 28 */ 29 30 /* 31 * $FreeBSD: src/gnu/usr.bin/rcs/rlog/rlog.c,v 1.13 1999/08/27 23:36:59 peter Exp $ 32 * $DragonFly: src/gnu/usr.bin/rcs/rlog/rlog.c,v 1.2 2003/06/17 04:25:48 dillon Exp $ 33 * 34 * Revision 5.18 1995/06/16 06:19:24 eggert 35 * Update FSF address. 36 * 37 * Revision 5.17 1995/06/01 16:23:43 eggert 38 * (struct rcslockers): Renamed from `struct lockers'. 39 * (getnumericrev): Return error indication instead of ignoring errors. 40 * (main): Check it. Don't use dateform. 41 * (recentdate, extdate): cmpnum -> cmpdate 42 * 43 * Revision 5.16 1994/04/13 16:30:34 eggert 44 * Fix bug; `rlog -lxxx' inverted the sense of -l. 45 * 46 * Revision 5.15 1994/03/17 14:05:48 eggert 47 * -d'<DATE' now excludes DATE; the new syntax -d'<=DATE' includes it. 48 * Emulate -V4's white space generation more precisely. 49 * Work around SVR4 stdio performance bug. Remove lint. 50 * 51 * Revision 5.14 1993/11/09 17:40:15 eggert 52 * -V now prints version on stdout and exits. 53 * 54 * Revision 5.13 1993/11/03 17:42:27 eggert 55 * Add -N, -z. Ignore -T. 56 * 57 * Revision 5.12 1992/07/28 16:12:44 eggert 58 * Don't miss B.0 when handling branch B. Diagnose missing `,' in -r. 59 * Add -V. Avoid `unsigned'. Statement macro names now end in _. 60 * 61 * Revision 5.11 1992/01/24 18:44:19 eggert 62 * Don't duplicate unexpected_EOF's function. lint -> RCS_lint 63 * 64 * Revision 5.10 1992/01/06 02:42:34 eggert 65 * Update usage string. 66 * while (E) ; -> while (E) continue; 67 * 68 * Revision 5.9 1991/09/17 19:07:40 eggert 69 * Getscript() didn't uncache partial lines. 70 * 71 * Revision 5.8 1991/08/19 03:13:55 eggert 72 * Revision separator is `:', not `-'. 73 * Check for missing and duplicate logs. Tune. 74 * Permit log messages that do not end in newline (including empty logs). 75 * 76 * Revision 5.7 1991/04/21 11:58:31 eggert 77 * Add -x, RCSINIT, MS-DOS support. 78 * 79 * Revision 5.6 1991/02/26 17:07:17 eggert 80 * Survive RCS files with missing logs. 81 * strsave -> str_save (DG/UX name clash) 82 * 83 * Revision 5.5 1990/11/01 05:03:55 eggert 84 * Permit arbitrary data in logs and comment leaders. 85 * 86 * Revision 5.4 1990/10/04 06:30:22 eggert 87 * Accumulate exit status across files. 88 * 89 * Revision 5.3 1990/09/11 02:41:16 eggert 90 * Plug memory leak. 91 * 92 * Revision 5.2 1990/09/04 08:02:33 eggert 93 * Count RCS lines better. 94 * 95 * Revision 5.0 1990/08/22 08:13:48 eggert 96 * Remove compile-time limits; use malloc instead. Add setuid support. 97 * Switch to GMT. 98 * Report dates in long form, to warn about dates past 1999/12/31. 99 * Change "added/del" message to make room for the longer dates. 100 * Don't generate trailing white space. Add -V. Ansify and Posixate. 101 * 102 * Revision 4.7 89/05/01 15:13:48 narten 103 * changed copyright header to reflect current distribution rules 104 * 105 * Revision 4.6 88/08/09 19:13:28 eggert 106 * Check for memory exhaustion; don't access freed storage. 107 * Shrink stdio code size; remove lint. 108 * 109 * Revision 4.5 87/12/18 11:46:38 narten 110 * more lint cleanups (Guy Harris) 111 * 112 * Revision 4.4 87/10/18 10:41:12 narten 113 * Updating version numbers 114 * Changes relative to 1.1 actually relative to 4.2 115 * 116 * Revision 1.3 87/09/24 14:01:10 narten 117 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 118 * warnings) 119 * 120 * Revision 1.2 87/03/27 14:22:45 jenkins 121 * Port to suns 122 * 123 * Revision 4.2 83/12/05 09:18:09 wft 124 * changed rewriteflag to external. 125 * 126 * Revision 4.1 83/05/11 16:16:55 wft 127 * Added -b, updated getnumericrev() accordingly. 128 * Replaced getpwuid() with getcaller(). 129 * 130 * Revision 3.7 83/05/11 14:24:13 wft 131 * Added options -L and -R; 132 * Fixed selection bug with -l on multiple files. 133 * Fixed error on dates of the form -d'>date' (rewrote getdatepair()). 134 * 135 * Revision 3.6 82/12/24 15:57:53 wft 136 * shortened output format. 137 * 138 * Revision 3.5 82/12/08 21:45:26 wft 139 * removed call to checkaccesslist(); used DATEFORM to format all dates; 140 * removed unused variables. 141 * 142 * Revision 3.4 82/12/04 13:26:25 wft 143 * Replaced getdelta() with gettree(); removed updating of field lockedby. 144 * 145 * Revision 3.3 82/12/03 14:08:20 wft 146 * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE. 147 * Fixed printing of nil, removed printing of Suffix, 148 * added shortcut if no revisions are printed, disambiguated struct members. 149 * 150 * Revision 3.2 82/10/18 21:09:06 wft 151 * call to curdir replaced with getfullRCSname(), 152 * fixed call to getlogin(), cosmetic changes on output, 153 * changed conflicting long identifiers. 154 * 155 * Revision 3.1 82/10/13 16:07:56 wft 156 * fixed type of variables receiving from getc() (char -> int). 157 */ 158 159 160 161 #include "rcsbase.h" 162 163 struct rcslockers { /* lockers in locker option; stored */ 164 char const * login; /* lockerlist */ 165 struct rcslockers * lockerlink; 166 } ; 167 168 struct stateattri { /* states in state option; stored in */ 169 char const * status; /* statelist */ 170 struct stateattri * nextstate; 171 } ; 172 173 struct authors { /* login names in author option; */ 174 char const * login; /* stored in authorlist */ 175 struct authors * nextauthor; 176 } ; 177 178 struct Revpairs{ /* revision or branch range in -r */ 179 int numfld; /* option; stored in revlist */ 180 char const * strtrev; 181 char const * endrev; 182 struct Revpairs * rnext; 183 } ; 184 185 struct Datepairs{ /* date range in -d option; stored in */ 186 struct Datepairs *dnext; 187 char strtdate[datesize]; /* duelst and datelist */ 188 char enddate[datesize]; 189 char ne_date; /* datelist only; distinguishes < from <= */ 190 }; 191 192 static char extractdelta P((struct hshentry const*)); 193 static int checkrevpair P((char const*,char const*)); 194 static int extdate P((struct hshentry*)); 195 static int getnumericrev P((void)); 196 static struct hshentry const *readdeltalog P((void)); 197 static void cleanup P((void)); 198 static void exttree P((struct hshentry*)); 199 static void getauthor P((char*)); 200 static void getdatepair P((char*)); 201 static void getlocker P((char*)); 202 static void getrevpairs P((char*)); 203 static void getscript P((struct hshentry*)); 204 static void getstate P((char*)); 205 static void putabranch P((struct hshentry const*)); 206 static void putadelta P((struct hshentry const*,struct hshentry const*,int)); 207 static void putforest P((struct branchhead const*)); 208 static void putree P((struct hshentry const*)); 209 static void putrunk P((void)); 210 static void recentdate P((struct hshentry const*,struct Datepairs*)); 211 static void trunclocks P((void)); 212 213 static char const *insDelFormat; 214 static int branchflag; /*set on -b */ 215 static int exitstatus; 216 static int lockflag; 217 static struct Datepairs *datelist, *duelst; 218 static struct Revpairs *revlist, *Revlst; 219 static struct authors *authorlist; 220 static struct rcslockers *lockerlist; 221 static struct stateattri *statelist; 222 223 224 mainProg(rlogId, "rlog", "$DragonFly: src/gnu/usr.bin/rcs/rlog/rlog.c,v 1.2 2003/06/17 04:25:48 dillon Exp $") 225 { 226 static char const cmdusage[] = 227 "\nrlog usage: rlog -{bhLNRt} -v[string] -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ..."; 228 229 register FILE *out; 230 char *a, **newargv; 231 struct Datepairs *currdate; 232 char const *accessListString, *accessFormat; 233 char const *headFormat, *symbolFormat; 234 struct access const *curaccess; 235 struct assoc const *curassoc; 236 struct hshentry const *delta; 237 struct rcslock const *currlock; 238 int descflag, selectflag; 239 int onlylockflag; /* print only files with locks */ 240 int onlyRCSflag; /* print only RCS pathname */ 241 int pre5; 242 int shownames; 243 int revno; 244 int versionlist; 245 char *vstring; 246 247 descflag = selectflag = shownames = true; 248 versionlist = onlylockflag = onlyRCSflag = false; 249 vstring=0; 250 out = stdout; 251 suffixes = X_DEFAULT; 252 253 argc = getRCSINIT(argc, argv, &newargv); 254 argv = newargv; 255 while (a = *++argv, 0<--argc && *a++=='-') { 256 switch (*a++) { 257 258 case 'L': 259 onlylockflag = true; 260 break; 261 262 case 'N': 263 shownames = false; 264 break; 265 266 case 'R': 267 onlyRCSflag =true; 268 break; 269 270 case 'l': 271 lockflag = true; 272 getlocker(a); 273 break; 274 275 case 'b': 276 branchflag = true; 277 break; 278 279 case 'r': 280 getrevpairs(a); 281 break; 282 283 case 'd': 284 getdatepair(a); 285 break; 286 287 case 's': 288 getstate(a); 289 break; 290 291 case 'w': 292 getauthor(a); 293 break; 294 295 case 'h': 296 descflag = false; 297 break; 298 299 case 't': 300 selectflag = false; 301 break; 302 303 case 'q': 304 /* This has no effect; it's here for consistency. */ 305 quietflag = true; 306 break; 307 308 case 'x': 309 suffixes = a; 310 break; 311 312 case 'z': 313 zone_set(a); 314 break; 315 316 case 'T': 317 /* Ignore -T, so that RCSINIT can contain -T. */ 318 if (*a) 319 goto unknown; 320 break; 321 322 case 'V': 323 setRCSversion(*argv); 324 break; 325 326 case 'v': 327 versionlist = true; 328 vstring = a; 329 break; 330 331 default: 332 unknown: 333 error("unknown option: %s%s", *argv, cmdusage); 334 335 }; 336 } /* end of option processing */ 337 338 if (! (descflag|selectflag)) { 339 warn("-t overrides -h."); 340 descflag = true; 341 } 342 343 pre5 = RCSversion < VERSION(5); 344 if (pre5) { 345 accessListString = "\naccess list: "; 346 accessFormat = " %s"; 347 headFormat = "RCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: "; 348 insDelFormat = " lines added/del: %ld/%ld"; 349 symbolFormat = " %s: %s;"; 350 } else { 351 accessListString = "\naccess list:"; 352 accessFormat = "\n\t%s"; 353 headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s"; 354 insDelFormat = " lines: +%ld -%ld"; 355 symbolFormat = "\n\t%s: %s"; 356 } 357 358 /* Now handle all pathnames. */ 359 if (nerror) 360 cleanup(); 361 else if (argc < 1) 362 faterror("no input file%s", cmdusage); 363 else 364 for (; 0 < argc; cleanup(), ++argv, --argc) { 365 ffree(); 366 367 if (pairnames(argc, argv, rcsreadopen, true, false) <= 0) 368 continue; 369 370 /* 371 * RCSname contains the name of the RCS file, 372 * and finptr the file descriptor; 373 * workname contains the name of the working file. 374 */ 375 376 /* Keep only those locks given by -l. */ 377 if (lockflag) 378 trunclocks(); 379 380 /* do nothing if -L is given and there are no locks*/ 381 if (onlylockflag && !Locks) 382 continue; 383 384 if ( versionlist ) { 385 gettree(); 386 aprintf(out, "%s%s %s\n", vstring, workname, tiprev()); 387 continue; 388 } 389 390 if ( onlyRCSflag ) { 391 aprintf(out, "%s\n", RCSname); 392 continue; 393 } 394 395 gettree(); 396 397 if (!getnumericrev()) 398 continue; 399 400 /* 401 * Output the first character with putc, not printf. 402 * Otherwise, an SVR4 stdio bug buffers output inefficiently. 403 */ 404 aputc_('\n', out) 405 406 /* print RCS pathname, working pathname and optional 407 administrative information */ 408 /* could use getfullRCSname() here, but that is very slow */ 409 aprintf(out, headFormat, RCSname, workname, 410 Head ? " " : "", Head ? Head->num : "", 411 Dbranch ? " " : "", Dbranch ? Dbranch : "", 412 StrictLocks ? " strict" : "" 413 ); 414 currlock = Locks; 415 while( currlock ) { 416 aprintf(out, symbolFormat, currlock->login, 417 currlock->delta->num); 418 currlock = currlock->nextlock; 419 } 420 if (StrictLocks && pre5) 421 aputs(" ; strict" + (Locks?3:0), out); 422 423 aputs(accessListString, out); /* print access list */ 424 curaccess = AccessList; 425 while(curaccess) { 426 aprintf(out, accessFormat, curaccess->login); 427 curaccess = curaccess->nextaccess; 428 } 429 430 if (shownames) { 431 aputs("\nsymbolic names:", out); /* print symbolic names */ 432 for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc) 433 aprintf(out, symbolFormat, curassoc->symbol, curassoc->num); 434 } 435 if (pre5) { 436 aputs("\ncomment leader: \"", out); 437 awrite(Comment.string, Comment.size, out); 438 afputc('\"', out); 439 } 440 if (!pre5 || Expand != KEYVAL_EXPAND) 441 aprintf(out, "\nkeyword substitution: %s", 442 expand_names[Expand] 443 ); 444 445 aprintf(out, "\ntotal revisions: %d", TotalDeltas); 446 447 revno = 0; 448 449 if (Head && selectflag & descflag) { 450 451 exttree(Head); 452 453 /* get most recently date of the dates pointed by duelst */ 454 currdate = duelst; 455 while( currdate) { 456 VOID strcpy(currdate->strtdate, "0.0.0.0.0.0"); 457 recentdate(Head, currdate); 458 currdate = currdate->dnext; 459 } 460 461 revno = extdate(Head); 462 463 aprintf(out, ";\tselected revisions: %d", revno); 464 } 465 466 afputc('\n',out); 467 if (descflag) { 468 aputs("description:\n", out); 469 getdesc(true); 470 } 471 if (revno) { 472 while (! (delta = readdeltalog())->selector || --revno) 473 continue; 474 if (delta->next && countnumflds(delta->num)==2) 475 /* Read through delta->next to get its insertlns. */ 476 while (readdeltalog() != delta->next) 477 continue; 478 putrunk(); 479 putree(Head); 480 } 481 aputs("----------------------------\n", out); 482 aputs("=============================================================================\n",out); 483 } 484 Ofclose(out); 485 exitmain(exitstatus); 486 } 487 488 static void 489 cleanup() 490 { 491 if (nerror) exitstatus = EXIT_FAILURE; 492 Izclose(&finptr); 493 } 494 495 #if RCS_lint 496 # define exiterr rlogExit 497 #endif 498 void 499 exiterr() 500 { 501 _exit(EXIT_FAILURE); 502 } 503 504 505 506 static void 507 putrunk() 508 /* function: print revisions chosen, which are in trunk */ 509 510 { 511 register struct hshentry const *ptr; 512 513 for (ptr = Head; ptr; ptr = ptr->next) 514 putadelta(ptr, ptr->next, true); 515 } 516 517 518 519 static void 520 putree(root) 521 struct hshentry const *root; 522 /* function: print delta tree (not including trunk) in reverse 523 order on each branch */ 524 525 { 526 if (!root) return; 527 528 putree(root->next); 529 530 putforest(root->branches); 531 } 532 533 534 535 536 static void 537 putforest(branchroot) 538 struct branchhead const *branchroot; 539 /* function: print branches that has the same direct ancestor */ 540 { 541 if (!branchroot) return; 542 543 putforest(branchroot->nextbranch); 544 545 putabranch(branchroot->hsh); 546 putree(branchroot->hsh); 547 } 548 549 550 551 552 static void 553 putabranch(root) 554 struct hshentry const *root; 555 /* function : print one branch */ 556 557 { 558 if (!root) return; 559 560 putabranch(root->next); 561 562 putadelta(root, root, false); 563 } 564 565 566 567 568 569 static void 570 putadelta(node,editscript,trunk) 571 register struct hshentry const *node, *editscript; 572 int trunk; 573 /* function: Print delta node if node->selector is set. */ 574 /* editscript indicates where the editscript is stored */ 575 /* trunk indicated whether this node is in trunk */ 576 { 577 static char emptych[] = EMPTYLOG; 578 579 register FILE *out; 580 char const *s; 581 size_t n; 582 struct branchhead const *newbranch; 583 struct buf branchnum; 584 char datebuf[datesize + zonelenmax]; 585 int pre5 = RCSversion < VERSION(5); 586 587 if (!node->selector) 588 return; 589 590 out = stdout; 591 aprintf(out, 592 "----------------------------\nrevision %s%s", 593 node->num, pre5 ? " " : "" 594 ); 595 if ( node->lockedby ) 596 aprintf(out, pre5+"\tlocked by: %s;", node->lockedby); 597 598 aprintf(out, "\ndate: %s; author: %s; state: %s;", 599 date2str(node->date, datebuf), 600 node->author, node->state 601 ); 602 603 if ( editscript ) 604 if(trunk) 605 aprintf(out, insDelFormat, 606 editscript->deletelns, editscript->insertlns); 607 else 608 aprintf(out, insDelFormat, 609 editscript->insertlns, editscript->deletelns); 610 611 newbranch = node->branches; 612 if ( newbranch ) { 613 bufautobegin(&branchnum); 614 aputs("\nbranches:", out); 615 while( newbranch ) { 616 getbranchno(newbranch->hsh->num, &branchnum); 617 aprintf(out, " %s;", branchnum.string); 618 newbranch = newbranch->nextbranch; 619 } 620 bufautoend(&branchnum); 621 } 622 623 afputc('\n', out); 624 s = node->log.string; 625 if (!(n = node->log.size)) { 626 s = emptych; 627 n = sizeof(emptych)-1; 628 } 629 awrite(s, n, out); 630 if (s[n-1] != '\n') 631 afputc('\n', out); 632 } 633 634 635 static struct hshentry const * 636 readdeltalog() 637 /* Function : get the log message and skip the text of a deltatext node. 638 * Return the delta found. 639 * Assumes the current lexeme is not yet in nexttok; does not 640 * advance nexttok. 641 */ 642 { 643 register struct hshentry * Delta; 644 struct buf logbuf; 645 struct cbuf cb; 646 647 if (eoflex()) 648 fatserror("missing delta log"); 649 nextlex(); 650 if (!(Delta = getnum())) 651 fatserror("delta number corrupted"); 652 getkeystring(Klog); 653 if (Delta->log.string) 654 fatserror("duplicate delta log"); 655 bufautobegin(&logbuf); 656 cb = savestring(&logbuf); 657 Delta->log = bufremember(&logbuf, cb.size); 658 659 ignorephrases(Ktext); 660 getkeystring(Ktext); 661 Delta->insertlns = Delta->deletelns = 0; 662 if ( Delta != Head) 663 getscript(Delta); 664 else 665 readstring(); 666 return Delta; 667 } 668 669 670 static void 671 getscript(Delta) 672 struct hshentry * Delta; 673 /* function: read edit script of Delta and count how many lines added */ 674 /* and deleted in the script */ 675 676 { 677 int ed; /* editor command */ 678 declarecache; 679 register RILE *fin; 680 register int c; 681 register long i; 682 struct diffcmd dc; 683 684 fin = finptr; 685 setupcache(fin); 686 initdiffcmd(&dc); 687 while (0 <= (ed = getdiffcmd(fin,true,(FILE *)0,&dc))) 688 if (!ed) 689 Delta->deletelns += dc.nlines; 690 else { 691 /* skip scripted lines */ 692 i = dc.nlines; 693 Delta->insertlns += i; 694 cache(fin); 695 do { 696 for (;;) { 697 cacheget_(c) 698 switch (c) { 699 default: 700 continue; 701 case SDELIM: 702 cacheget_(c) 703 if (c == SDELIM) 704 continue; 705 if (--i) 706 unexpected_EOF(); 707 nextc = c; 708 uncache(fin); 709 return; 710 case '\n': 711 break; 712 } 713 break; 714 } 715 ++rcsline; 716 } while (--i); 717 uncache(fin); 718 } 719 } 720 721 722 723 724 725 726 727 static void 728 exttree(root) 729 struct hshentry *root; 730 /* function: select revisions , starting with root */ 731 732 { 733 struct branchhead const *newbranch; 734 735 if (!root) return; 736 737 root->selector = extractdelta(root); 738 root->log.string = 0; 739 exttree(root->next); 740 741 newbranch = root->branches; 742 while( newbranch ) { 743 exttree(newbranch->hsh); 744 newbranch = newbranch->nextbranch; 745 } 746 } 747 748 749 750 751 static void 752 getlocker(argv) 753 char * argv; 754 /* function : get the login names of lockers from command line */ 755 /* and store in lockerlist. */ 756 757 { 758 register char c; 759 struct rcslockers *newlocker; 760 argv--; 761 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 762 continue; 763 if ( c == '\0') { 764 lockerlist = 0; 765 return; 766 } 767 768 while( c != '\0' ) { 769 newlocker = talloc(struct rcslockers); 770 newlocker->lockerlink = lockerlist; 771 newlocker->login = argv; 772 lockerlist = newlocker; 773 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') 774 continue; 775 *argv = '\0'; 776 if ( c == '\0' ) return; 777 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 778 continue; 779 } 780 } 781 782 783 784 static void 785 getauthor(argv) 786 char *argv; 787 /* function: get the author's name from command line */ 788 /* and store in authorlist */ 789 790 { 791 register c; 792 struct authors * newauthor; 793 794 argv--; 795 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 796 continue; 797 if ( c == '\0' ) { 798 authorlist = talloc(struct authors); 799 authorlist->login = getusername(false); 800 authorlist->nextauthor = 0; 801 return; 802 } 803 804 while( c != '\0' ) { 805 newauthor = talloc(struct authors); 806 newauthor->nextauthor = authorlist; 807 newauthor->login = argv; 808 authorlist = newauthor; 809 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') 810 continue; 811 * argv = '\0'; 812 if ( c == '\0') return; 813 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 814 continue; 815 } 816 } 817 818 819 820 821 static void 822 getstate(argv) 823 char * argv; 824 /* function : get the states of revisions from command line */ 825 /* and store in statelist */ 826 827 { 828 register char c; 829 struct stateattri *newstate; 830 831 argv--; 832 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 833 continue; 834 if ( c == '\0'){ 835 error("missing state attributes after -s options"); 836 return; 837 } 838 839 while( c != '\0' ) { 840 newstate = talloc(struct stateattri); 841 newstate->nextstate = statelist; 842 newstate->status = argv; 843 statelist = newstate; 844 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') 845 continue; 846 *argv = '\0'; 847 if ( c == '\0' ) return; 848 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 849 continue; 850 } 851 } 852 853 854 855 static void 856 trunclocks() 857 /* Function: Truncate the list of locks to those that are held by the */ 858 /* id's on lockerlist. Do not truncate if lockerlist empty. */ 859 860 { 861 struct rcslockers const *plocker; 862 struct rcslock *p, **pp; 863 864 if (!lockerlist) return; 865 866 /* shorten Locks to those contained in lockerlist */ 867 for (pp = &Locks; (p = *pp); ) 868 for (plocker = lockerlist; ; ) 869 if (strcmp(plocker->login, p->login) == 0) { 870 pp = &p->nextlock; 871 break; 872 } else if (!(plocker = plocker->lockerlink)) { 873 *pp = p->nextlock; 874 break; 875 } 876 } 877 878 879 880 static void 881 recentdate(root, pd) 882 struct hshentry const *root; 883 struct Datepairs *pd; 884 /* function: Finds the delta that is closest to the cutoff date given by */ 885 /* pd among the revisions selected by exttree. */ 886 /* Successively narrows down the interval given by pd, */ 887 /* and sets the strtdate of pd to the date of the selected delta */ 888 { 889 struct branchhead const *newbranch; 890 891 if (!root) return; 892 if (root->selector) { 893 if ( cmpdate(root->date, pd->strtdate) >= 0 && 894 cmpdate(root->date, pd->enddate) <= 0) 895 VOID strcpy(pd->strtdate, root->date); 896 } 897 898 recentdate(root->next, pd); 899 newbranch = root->branches; 900 while( newbranch) { 901 recentdate(newbranch->hsh, pd); 902 newbranch = newbranch->nextbranch; 903 } 904 } 905 906 907 908 909 910 911 static int 912 extdate(root) 913 struct hshentry * root; 914 /* function: select revisions which are in the date range specified */ 915 /* in duelst and datelist, start at root */ 916 /* Yield number of revisions selected, including those already selected. */ 917 { 918 struct branchhead const *newbranch; 919 struct Datepairs const *pdate; 920 int revno, ne; 921 922 if (!root) 923 return 0; 924 925 if ( datelist || duelst) { 926 pdate = datelist; 927 while( pdate ) { 928 ne = pdate->ne_date; 929 if ( 930 (!pdate->strtdate[0] 931 || ne <= cmpdate(root->date, pdate->strtdate)) 932 && 933 (!pdate->enddate[0] 934 || ne <= cmpdate(pdate->enddate, root->date)) 935 ) 936 break; 937 pdate = pdate->dnext; 938 } 939 if (!pdate) { 940 pdate = duelst; 941 for (;;) { 942 if (!pdate) { 943 root->selector = false; 944 break; 945 } 946 if (cmpdate(root->date, pdate->strtdate) == 0) 947 break; 948 pdate = pdate->dnext; 949 } 950 } 951 } 952 revno = root->selector + extdate(root->next); 953 954 newbranch = root->branches; 955 while( newbranch ) { 956 revno += extdate(newbranch->hsh); 957 newbranch = newbranch->nextbranch; 958 } 959 return revno; 960 } 961 962 963 964 static char 965 extractdelta(pdelta) 966 struct hshentry const *pdelta; 967 /* function: compare information of pdelta to the authorlist, lockerlist,*/ 968 /* statelist, revlist and yield true if pdelta is selected. */ 969 970 { 971 struct rcslock const *plock; 972 struct stateattri const *pstate; 973 struct authors const *pauthor; 974 struct Revpairs const *prevision; 975 int length; 976 977 if ((pauthor = authorlist)) /* only certain authors wanted */ 978 while (strcmp(pauthor->login, pdelta->author) != 0) 979 if (!(pauthor = pauthor->nextauthor)) 980 return false; 981 if ((pstate = statelist)) /* only certain states wanted */ 982 while (strcmp(pstate->status, pdelta->state) != 0) 983 if (!(pstate = pstate->nextstate)) 984 return false; 985 if (lockflag) /* only locked revisions wanted */ 986 for (plock = Locks; ; plock = plock->nextlock) 987 if (!plock) 988 return false; 989 else if (plock->delta == pdelta) 990 break; 991 if ((prevision = Revlst)) /* only certain revs or branches wanted */ 992 for (;;) { 993 length = prevision->numfld; 994 if ( 995 countnumflds(pdelta->num) == length+(length&1) && 996 0 <= compartial(pdelta->num, prevision->strtrev, length) && 997 0 <= compartial(prevision->endrev, pdelta->num, length) 998 ) 999 break; 1000 if (!(prevision = prevision->rnext)) 1001 return false; 1002 } 1003 return true; 1004 } 1005 1006 1007 1008 static void 1009 getdatepair(argv) 1010 char * argv; 1011 /* function: get time range from command line and store in datelist if */ 1012 /* a time range specified or in duelst if a time spot specified */ 1013 1014 { 1015 register char c; 1016 struct Datepairs * nextdate; 1017 char const * rawdate; 1018 int switchflag; 1019 1020 argv--; 1021 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 1022 continue; 1023 if ( c == '\0' ) { 1024 error("missing date/time after -d"); 1025 return; 1026 } 1027 1028 while( c != '\0' ) { 1029 switchflag = false; 1030 nextdate = talloc(struct Datepairs); 1031 if ( c == '<' ) { /* case: -d <date */ 1032 c = *++argv; 1033 if (!(nextdate->ne_date = c!='=')) 1034 c = *++argv; 1035 (nextdate->strtdate)[0] = '\0'; 1036 } else if (c == '>') { /* case: -d'>date' */ 1037 c = *++argv; 1038 if (!(nextdate->ne_date = c!='=')) 1039 c = *++argv; 1040 (nextdate->enddate)[0] = '\0'; 1041 switchflag = true; 1042 } else { 1043 rawdate = argv; 1044 while( c != '<' && c != '>' && c != ';' && c != '\0') 1045 c = *++argv; 1046 *argv = '\0'; 1047 if ( c == '>' ) switchflag=true; 1048 str2date(rawdate, 1049 switchflag ? nextdate->enddate : nextdate->strtdate); 1050 if ( c == ';' || c == '\0') { /* case: -d date */ 1051 VOID strcpy(nextdate->enddate,nextdate->strtdate); 1052 nextdate->dnext = duelst; 1053 duelst = nextdate; 1054 goto end; 1055 } else { 1056 /* case: -d date< or -d date>; see switchflag */ 1057 int eq = argv[1]=='='; 1058 nextdate->ne_date = !eq; 1059 argv += eq; 1060 while ((c = *++argv) == ' ' || c=='\t' || c=='\n') 1061 continue; 1062 if ( c == ';' || c == '\0') { 1063 /* second date missing */ 1064 if (switchflag) 1065 *nextdate->strtdate= '\0'; 1066 else 1067 *nextdate->enddate= '\0'; 1068 nextdate->dnext = datelist; 1069 datelist = nextdate; 1070 goto end; 1071 } 1072 } 1073 } 1074 rawdate = argv; 1075 while( c != '>' && c != '<' && c != ';' && c != '\0') 1076 c = *++argv; 1077 *argv = '\0'; 1078 str2date(rawdate, 1079 switchflag ? nextdate->strtdate : nextdate->enddate); 1080 nextdate->dnext = datelist; 1081 datelist = nextdate; 1082 end: 1083 if (RCSversion < VERSION(5)) 1084 nextdate->ne_date = 0; 1085 if ( c == '\0') return; 1086 while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n') 1087 continue; 1088 } 1089 } 1090 1091 1092 1093 static int 1094 getnumericrev() 1095 /* function: get the numeric name of revisions which stored in revlist */ 1096 /* and then stored the numeric names in Revlst */ 1097 /* if branchflag, also add default branch */ 1098 1099 { 1100 struct Revpairs * ptr, *pt; 1101 int n; 1102 struct buf s, e; 1103 char const *lrev; 1104 struct buf const *rstart, *rend; 1105 1106 Revlst = 0; 1107 ptr = revlist; 1108 bufautobegin(&s); 1109 bufautobegin(&e); 1110 while( ptr ) { 1111 n = 0; 1112 rstart = &s; 1113 rend = &e; 1114 1115 switch (ptr->numfld) { 1116 1117 case 1: /* -rREV */ 1118 if (!expandsym(ptr->strtrev, &s)) 1119 goto freebufs; 1120 rend = &s; 1121 n = countnumflds(s.string); 1122 if (!n && (lrev = tiprev())) { 1123 bufscpy(&s, lrev); 1124 n = countnumflds(lrev); 1125 } 1126 break; 1127 1128 case 2: /* -rREV: */ 1129 if (!expandsym(ptr->strtrev, &s)) 1130 goto freebufs; 1131 bufscpy(&e, s.string); 1132 n = countnumflds(s.string); 1133 (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0; 1134 break; 1135 1136 case 3: /* -r:REV */ 1137 if (!expandsym(ptr->endrev, &e)) 1138 goto freebufs; 1139 if ((n = countnumflds(e.string)) < 2) 1140 bufscpy(&s, ".0"); 1141 else { 1142 bufscpy(&s, e.string); 1143 VOID strcpy(strrchr(s.string,'.'), ".0"); 1144 } 1145 break; 1146 1147 default: /* -rREV1:REV2 */ 1148 if (!( 1149 expandsym(ptr->strtrev, &s) 1150 && expandsym(ptr->endrev, &e) 1151 && checkrevpair(s.string, e.string) 1152 )) 1153 goto freebufs; 1154 n = countnumflds(s.string); 1155 /* Swap if out of order. */ 1156 if (compartial(s.string,e.string,n) > 0) { 1157 rstart = &e; 1158 rend = &s; 1159 } 1160 break; 1161 } 1162 1163 if (n) { 1164 pt = ftalloc(struct Revpairs); 1165 pt->numfld = n; 1166 pt->strtrev = fstr_save(rstart->string); 1167 pt->endrev = fstr_save(rend->string); 1168 pt->rnext = Revlst; 1169 Revlst = pt; 1170 } 1171 ptr = ptr->rnext; 1172 } 1173 /* Now take care of branchflag */ 1174 if (branchflag && (Dbranch||Head)) { 1175 pt = ftalloc(struct Revpairs); 1176 pt->strtrev = pt->endrev = 1177 Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1)); 1178 pt->rnext=Revlst; Revlst=pt; 1179 pt->numfld = countnumflds(pt->strtrev); 1180 } 1181 1182 freebufs: 1183 bufautoend(&s); 1184 bufautoend(&e); 1185 return !ptr; 1186 } 1187 1188 1189 1190 static int 1191 checkrevpair(num1,num2) 1192 char const *num1, *num2; 1193 /* function: check whether num1, num2 are legal pair,i.e. 1194 only the last field are different and have same number of 1195 fields( if length <= 2, may be different if first field) */ 1196 1197 { 1198 int length = countnumflds(num1); 1199 1200 if ( 1201 countnumflds(num2) != length 1202 || (2 < length && compartial(num1, num2, length-1) != 0) 1203 ) { 1204 rcserror("invalid branch or revision pair %s : %s", num1, num2); 1205 return false; 1206 } 1207 1208 return true; 1209 } 1210 1211 1212 1213 static void 1214 getrevpairs(argv) 1215 register char * argv; 1216 /* function: get revision or branch range from command line, and */ 1217 /* store in revlist */ 1218 1219 { 1220 register char c; 1221 struct Revpairs * nextrevpair; 1222 int separator; 1223 1224 c = *argv; 1225 1226 /* Support old ambiguous '-' syntax; this will go away. */ 1227 if (strchr(argv,':')) 1228 separator = ':'; 1229 else { 1230 if (strchr(argv,'-') && VERSION(5) <= RCSversion) 1231 warn("`-' is obsolete in `-r%s'; use `:' instead", argv); 1232 separator = '-'; 1233 } 1234 1235 for (;;) { 1236 while (c==' ' || c=='\t' || c=='\n') 1237 c = *++argv; 1238 nextrevpair = talloc(struct Revpairs); 1239 nextrevpair->rnext = revlist; 1240 revlist = nextrevpair; 1241 nextrevpair->numfld = 1; 1242 nextrevpair->strtrev = argv; 1243 for (;; c = *++argv) { 1244 switch (c) { 1245 default: 1246 continue; 1247 case '\0': case ' ': case '\t': case '\n': 1248 case ',': case ';': 1249 break; 1250 case ':': case '-': 1251 if (c == separator) 1252 break; 1253 continue; 1254 } 1255 break; 1256 } 1257 *argv = '\0'; 1258 while (c==' ' || c=='\t' || c=='\n') 1259 c = *++argv; 1260 if (c == separator) { 1261 while ((c = *++argv) == ' ' || c == '\t' || c =='\n') 1262 continue; 1263 nextrevpair->endrev = argv; 1264 for (;; c = *++argv) { 1265 switch (c) { 1266 default: 1267 continue; 1268 case '\0': case ' ': case '\t': case '\n': 1269 case ',': case ';': 1270 break; 1271 case ':': case '-': 1272 if (c == separator) 1273 break; 1274 continue; 1275 } 1276 break; 1277 } 1278 *argv = '\0'; 1279 while (c==' ' || c=='\t' || c =='\n') 1280 c = *++argv; 1281 nextrevpair->numfld = 1282 !nextrevpair->endrev[0] ? 2 /* -rREV: */ : 1283 !nextrevpair->strtrev[0] ? 3 /* -r:REV */ : 1284 4 /* -rREV1:REV2 */; 1285 } 1286 if (!c) 1287 break; 1288 else if (c==',' || c==';') 1289 c = *++argv; 1290 else 1291 error("missing `,' near `%c%s'", c, argv+1); 1292 } 1293 } 1294