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