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