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