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