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 if ( node->commitid ) 612 aprintf(out, "%s commitid: %s", (editscript) ? ";" : "", 613 node->commitid); 614 615 newbranch = node->branches; 616 if ( newbranch ) { 617 bufautobegin(&branchnum); 618 aputs("\nbranches:", out); 619 while( newbranch ) { 620 getbranchno(newbranch->hsh->num, &branchnum); 621 aprintf(out, " %s;", branchnum.string); 622 newbranch = newbranch->nextbranch; 623 } 624 bufautoend(&branchnum); 625 } 626 627 afputc('\n', out); 628 s = node->log.string; 629 if (!(n = node->log.size)) { 630 s = emptych; 631 n = sizeof(emptych)-1; 632 } 633 awrite(s, n, out); 634 if (s[n-1] != '\n') 635 afputc('\n', out); 636 } 637 638 639 static struct hshentry const * 640 readdeltalog() 641 /* Function : get the log message and skip the text of a deltatext node. 642 * Return the delta found. 643 * Assumes the current lexeme is not yet in nexttok; does not 644 * advance nexttok. 645 */ 646 { 647 register struct hshentry * Delta; 648 struct buf logbuf; 649 struct cbuf cb; 650 651 if (eoflex()) 652 fatserror("missing delta log"); 653 nextlex(); 654 if (!(Delta = getnum())) 655 fatserror("delta number corrupted"); 656 getkeystring(Klog); 657 if (Delta->log.string) 658 fatserror("duplicate delta log"); 659 bufautobegin(&logbuf); 660 cb = savestring(&logbuf); 661 Delta->log = bufremember(&logbuf, cb.size); 662 663 ignorephrases(Ktext); 664 getkeystring(Ktext); 665 Delta->insertlns = Delta->deletelns = 0; 666 if ( Delta != Head) 667 getscript(Delta); 668 else 669 readstring(); 670 return Delta; 671 } 672 673 674 static void 675 getscript(Delta) 676 struct hshentry * Delta; 677 /* function: read edit script of Delta and count how many lines added */ 678 /* and deleted in the script */ 679 680 { 681 int ed; /* editor command */ 682 declarecache; 683 register RILE *fin; 684 register int c; 685 register long i; 686 struct diffcmd dc; 687 688 fin = finptr; 689 setupcache(fin); 690 initdiffcmd(&dc); 691 while (0 <= (ed = getdiffcmd(fin,true,(FILE *)0,&dc))) 692 if (!ed) 693 Delta->deletelns += dc.nlines; 694 else { 695 /* skip scripted lines */ 696 i = dc.nlines; 697 Delta->insertlns += i; 698 cache(fin); 699 do { 700 for (;;) { 701 cacheget_(c) 702 switch (c) { 703 default: 704 continue; 705 case SDELIM: 706 cacheget_(c) 707 if (c == SDELIM) 708 continue; 709 if (--i) 710 unexpected_EOF(); 711 nextc = c; 712 uncache(fin); 713 return; 714 case '\n': 715 break; 716 } 717 break; 718 } 719 ++rcsline; 720 } while (--i); 721 uncache(fin); 722 } 723 } 724 725 726 727 728 729 730 731 static void 732 exttree(root) 733 struct hshentry *root; 734 /* function: select revisions , starting with root */ 735 736 { 737 struct branchhead const *newbranch; 738 739 if (!root) return; 740 741 root->selector = extractdelta(root); 742 root->log.string = 0; 743 exttree(root->next); 744 745 newbranch = root->branches; 746 while( newbranch ) { 747 exttree(newbranch->hsh); 748 newbranch = newbranch->nextbranch; 749 } 750 } 751 752 753 754 755 static void 756 getlocker(argv) 757 char * argv; 758 /* function : get the login names of lockers from command line */ 759 /* and store in lockerlist. */ 760 761 { 762 register char c; 763 struct rcslockers *newlocker; 764 argv--; 765 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 766 continue; 767 if ( c == '\0') { 768 lockerlist = 0; 769 return; 770 } 771 772 while( c != '\0' ) { 773 newlocker = talloc(struct rcslockers); 774 newlocker->lockerlink = lockerlist; 775 newlocker->login = argv; 776 lockerlist = newlocker; 777 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') 778 continue; 779 *argv = '\0'; 780 if ( c == '\0' ) return; 781 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 782 continue; 783 } 784 } 785 786 787 788 static void 789 getauthor(argv) 790 char *argv; 791 /* function: get the author's name from command line */ 792 /* and store in authorlist */ 793 794 { 795 register c; 796 struct authors * newauthor; 797 798 argv--; 799 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 800 continue; 801 if ( c == '\0' ) { 802 authorlist = talloc(struct authors); 803 authorlist->login = getusername(false); 804 authorlist->nextauthor = 0; 805 return; 806 } 807 808 while( c != '\0' ) { 809 newauthor = talloc(struct authors); 810 newauthor->nextauthor = authorlist; 811 newauthor->login = argv; 812 authorlist = newauthor; 813 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') 814 continue; 815 * argv = '\0'; 816 if ( c == '\0') return; 817 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 818 continue; 819 } 820 } 821 822 823 824 825 static void 826 getstate(argv) 827 char * argv; 828 /* function : get the states of revisions from command line */ 829 /* and store in statelist */ 830 831 { 832 register char c; 833 struct stateattri *newstate; 834 835 argv--; 836 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 837 continue; 838 if ( c == '\0'){ 839 error("missing state attributes after -s options"); 840 return; 841 } 842 843 while( c != '\0' ) { 844 newstate = talloc(struct stateattri); 845 newstate->nextstate = statelist; 846 newstate->status = argv; 847 statelist = newstate; 848 while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') 849 continue; 850 *argv = '\0'; 851 if ( c == '\0' ) return; 852 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 853 continue; 854 } 855 } 856 857 858 859 static void 860 trunclocks() 861 /* Function: Truncate the list of locks to those that are held by the */ 862 /* id's on lockerlist. Do not truncate if lockerlist empty. */ 863 864 { 865 struct rcslockers const *plocker; 866 struct rcslock *p, **pp; 867 868 if (!lockerlist) return; 869 870 /* shorten Locks to those contained in lockerlist */ 871 for (pp = &Locks; (p = *pp); ) 872 for (plocker = lockerlist; ; ) 873 if (strcmp(plocker->login, p->login) == 0) { 874 pp = &p->nextlock; 875 break; 876 } else if (!(plocker = plocker->lockerlink)) { 877 *pp = p->nextlock; 878 break; 879 } 880 } 881 882 883 884 static void 885 recentdate(root, pd) 886 struct hshentry const *root; 887 struct Datepairs *pd; 888 /* function: Finds the delta that is closest to the cutoff date given by */ 889 /* pd among the revisions selected by exttree. */ 890 /* Successively narrows down the interval given by pd, */ 891 /* and sets the strtdate of pd to the date of the selected delta */ 892 { 893 struct branchhead const *newbranch; 894 895 if (!root) return; 896 if (root->selector) { 897 if ( cmpdate(root->date, pd->strtdate) >= 0 && 898 cmpdate(root->date, pd->enddate) <= 0) 899 VOID strcpy(pd->strtdate, root->date); 900 } 901 902 recentdate(root->next, pd); 903 newbranch = root->branches; 904 while( newbranch) { 905 recentdate(newbranch->hsh, pd); 906 newbranch = newbranch->nextbranch; 907 } 908 } 909 910 911 912 913 914 915 static int 916 extdate(root) 917 struct hshentry * root; 918 /* function: select revisions which are in the date range specified */ 919 /* in duelst and datelist, start at root */ 920 /* Yield number of revisions selected, including those already selected. */ 921 { 922 struct branchhead const *newbranch; 923 struct Datepairs const *pdate; 924 int revno, ne; 925 926 if (!root) 927 return 0; 928 929 if ( datelist || duelst) { 930 pdate = datelist; 931 while( pdate ) { 932 ne = pdate->ne_date; 933 if ( 934 (!pdate->strtdate[0] 935 || ne <= cmpdate(root->date, pdate->strtdate)) 936 && 937 (!pdate->enddate[0] 938 || ne <= cmpdate(pdate->enddate, root->date)) 939 ) 940 break; 941 pdate = pdate->dnext; 942 } 943 if (!pdate) { 944 pdate = duelst; 945 for (;;) { 946 if (!pdate) { 947 root->selector = false; 948 break; 949 } 950 if (cmpdate(root->date, pdate->strtdate) == 0) 951 break; 952 pdate = pdate->dnext; 953 } 954 } 955 } 956 revno = root->selector + extdate(root->next); 957 958 newbranch = root->branches; 959 while( newbranch ) { 960 revno += extdate(newbranch->hsh); 961 newbranch = newbranch->nextbranch; 962 } 963 return revno; 964 } 965 966 967 968 static char 969 extractdelta(pdelta) 970 struct hshentry const *pdelta; 971 /* function: compare information of pdelta to the authorlist, lockerlist,*/ 972 /* statelist, revlist and yield true if pdelta is selected. */ 973 974 { 975 struct rcslock const *plock; 976 struct stateattri const *pstate; 977 struct authors const *pauthor; 978 struct Revpairs const *prevision; 979 int length; 980 981 if ((pauthor = authorlist)) /* only certain authors wanted */ 982 while (strcmp(pauthor->login, pdelta->author) != 0) 983 if (!(pauthor = pauthor->nextauthor)) 984 return false; 985 if ((pstate = statelist)) /* only certain states wanted */ 986 while (strcmp(pstate->status, pdelta->state) != 0) 987 if (!(pstate = pstate->nextstate)) 988 return false; 989 if (lockflag) /* only locked revisions wanted */ 990 for (plock = Locks; ; plock = plock->nextlock) 991 if (!plock) 992 return false; 993 else if (plock->delta == pdelta) 994 break; 995 if ((prevision = Revlst)) /* only certain revs or branches wanted */ 996 for (;;) { 997 length = prevision->numfld; 998 if ( 999 countnumflds(pdelta->num) == length+(length&1) && 1000 0 <= compartial(pdelta->num, prevision->strtrev, length) && 1001 0 <= compartial(prevision->endrev, pdelta->num, length) 1002 ) 1003 break; 1004 if (!(prevision = prevision->rnext)) 1005 return false; 1006 } 1007 return true; 1008 } 1009 1010 1011 1012 static void 1013 getdatepair(argv) 1014 char * argv; 1015 /* function: get time range from command line and store in datelist if */ 1016 /* a time range specified or in duelst if a time spot specified */ 1017 1018 { 1019 register char c; 1020 struct Datepairs * nextdate; 1021 char const * rawdate; 1022 int switchflag; 1023 1024 argv--; 1025 while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') 1026 continue; 1027 if ( c == '\0' ) { 1028 error("missing date/time after -d"); 1029 return; 1030 } 1031 1032 while( c != '\0' ) { 1033 switchflag = false; 1034 nextdate = talloc(struct Datepairs); 1035 if ( c == '<' ) { /* case: -d <date */ 1036 c = *++argv; 1037 if (!(nextdate->ne_date = c!='=')) 1038 c = *++argv; 1039 (nextdate->strtdate)[0] = '\0'; 1040 } else if (c == '>') { /* case: -d'>date' */ 1041 c = *++argv; 1042 if (!(nextdate->ne_date = c!='=')) 1043 c = *++argv; 1044 (nextdate->enddate)[0] = '\0'; 1045 switchflag = true; 1046 } else { 1047 rawdate = argv; 1048 while( c != '<' && c != '>' && c != ';' && c != '\0') 1049 c = *++argv; 1050 *argv = '\0'; 1051 if ( c == '>' ) switchflag=true; 1052 str2date(rawdate, 1053 switchflag ? nextdate->enddate : nextdate->strtdate); 1054 if ( c == ';' || c == '\0') { /* case: -d date */ 1055 VOID strcpy(nextdate->enddate,nextdate->strtdate); 1056 nextdate->dnext = duelst; 1057 duelst = nextdate; 1058 goto end; 1059 } else { 1060 /* case: -d date< or -d date>; see switchflag */ 1061 int eq = argv[1]=='='; 1062 nextdate->ne_date = !eq; 1063 argv += eq; 1064 while ((c = *++argv) == ' ' || c=='\t' || c=='\n') 1065 continue; 1066 if ( c == ';' || c == '\0') { 1067 /* second date missing */ 1068 if (switchflag) 1069 *nextdate->strtdate= '\0'; 1070 else 1071 *nextdate->enddate= '\0'; 1072 nextdate->dnext = datelist; 1073 datelist = nextdate; 1074 goto end; 1075 } 1076 } 1077 } 1078 rawdate = argv; 1079 while( c != '>' && c != '<' && c != ';' && c != '\0') 1080 c = *++argv; 1081 *argv = '\0'; 1082 str2date(rawdate, 1083 switchflag ? nextdate->strtdate : nextdate->enddate); 1084 nextdate->dnext = datelist; 1085 datelist = nextdate; 1086 end: 1087 if (RCSversion < VERSION(5)) 1088 nextdate->ne_date = 0; 1089 if ( c == '\0') return; 1090 while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n') 1091 continue; 1092 } 1093 } 1094 1095 1096 1097 static int 1098 getnumericrev() 1099 /* function: get the numeric name of revisions which stored in revlist */ 1100 /* and then stored the numeric names in Revlst */ 1101 /* if branchflag, also add default branch */ 1102 1103 { 1104 struct Revpairs * ptr, *pt; 1105 int n; 1106 struct buf s, e; 1107 char const *lrev; 1108 struct buf const *rstart, *rend; 1109 1110 Revlst = 0; 1111 ptr = revlist; 1112 bufautobegin(&s); 1113 bufautobegin(&e); 1114 while( ptr ) { 1115 n = 0; 1116 rstart = &s; 1117 rend = &e; 1118 1119 switch (ptr->numfld) { 1120 1121 case 1: /* -rREV */ 1122 if (!expandsym(ptr->strtrev, &s)) 1123 goto freebufs; 1124 rend = &s; 1125 n = countnumflds(s.string); 1126 if (!n && (lrev = tiprev())) { 1127 bufscpy(&s, lrev); 1128 n = countnumflds(lrev); 1129 } 1130 break; 1131 1132 case 2: /* -rREV: */ 1133 if (!expandsym(ptr->strtrev, &s)) 1134 goto freebufs; 1135 bufscpy(&e, s.string); 1136 n = countnumflds(s.string); 1137 (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0; 1138 break; 1139 1140 case 3: /* -r:REV */ 1141 if (!expandsym(ptr->endrev, &e)) 1142 goto freebufs; 1143 if ((n = countnumflds(e.string)) < 2) 1144 bufscpy(&s, ".0"); 1145 else { 1146 bufscpy(&s, e.string); 1147 VOID strcpy(strrchr(s.string,'.'), ".0"); 1148 } 1149 break; 1150 1151 default: /* -rREV1:REV2 */ 1152 if (!( 1153 expandsym(ptr->strtrev, &s) 1154 && expandsym(ptr->endrev, &e) 1155 && checkrevpair(s.string, e.string) 1156 )) 1157 goto freebufs; 1158 n = countnumflds(s.string); 1159 /* Swap if out of order. */ 1160 if (compartial(s.string,e.string,n) > 0) { 1161 rstart = &e; 1162 rend = &s; 1163 } 1164 break; 1165 } 1166 1167 if (n) { 1168 pt = ftalloc(struct Revpairs); 1169 pt->numfld = n; 1170 pt->strtrev = fstr_save(rstart->string); 1171 pt->endrev = fstr_save(rend->string); 1172 pt->rnext = Revlst; 1173 Revlst = pt; 1174 } 1175 ptr = ptr->rnext; 1176 } 1177 /* Now take care of branchflag */ 1178 if (branchflag && (Dbranch||Head)) { 1179 pt = ftalloc(struct Revpairs); 1180 pt->strtrev = pt->endrev = 1181 Dbranch ? Dbranch : fstr_save(partialno(&s,Head->num,1)); 1182 pt->rnext=Revlst; Revlst=pt; 1183 pt->numfld = countnumflds(pt->strtrev); 1184 } 1185 1186 freebufs: 1187 bufautoend(&s); 1188 bufautoend(&e); 1189 return !ptr; 1190 } 1191 1192 1193 1194 static int 1195 checkrevpair(num1,num2) 1196 char const *num1, *num2; 1197 /* function: check whether num1, num2 are legal pair,i.e. 1198 only the last field are different and have same number of 1199 fields( if length <= 2, may be different if first field) */ 1200 1201 { 1202 int length = countnumflds(num1); 1203 1204 if ( 1205 countnumflds(num2) != length 1206 || (2 < length && compartial(num1, num2, length-1) != 0) 1207 ) { 1208 rcserror("invalid branch or revision pair %s : %s", num1, num2); 1209 return false; 1210 } 1211 1212 return true; 1213 } 1214 1215 1216 1217 static void 1218 getrevpairs(argv) 1219 register char * argv; 1220 /* function: get revision or branch range from command line, and */ 1221 /* store in revlist */ 1222 1223 { 1224 register char c; 1225 struct Revpairs * nextrevpair; 1226 int separator; 1227 1228 c = *argv; 1229 1230 /* Support old ambiguous '-' syntax; this will go away. */ 1231 if (strchr(argv,':')) 1232 separator = ':'; 1233 else { 1234 if (strchr(argv,'-') && VERSION(5) <= RCSversion) 1235 warn("`-' is obsolete in `-r%s'; use `:' instead", argv); 1236 separator = '-'; 1237 } 1238 1239 for (;;) { 1240 while (c==' ' || c=='\t' || c=='\n') 1241 c = *++argv; 1242 nextrevpair = talloc(struct Revpairs); 1243 nextrevpair->rnext = revlist; 1244 revlist = nextrevpair; 1245 nextrevpair->numfld = 1; 1246 nextrevpair->strtrev = argv; 1247 for (;; c = *++argv) { 1248 switch (c) { 1249 default: 1250 continue; 1251 case '\0': case ' ': case '\t': case '\n': 1252 case ',': case ';': 1253 break; 1254 case ':': case '-': 1255 if (c == separator) 1256 break; 1257 continue; 1258 } 1259 break; 1260 } 1261 *argv = '\0'; 1262 while (c==' ' || c=='\t' || c=='\n') 1263 c = *++argv; 1264 if (c == separator) { 1265 while ((c = *++argv) == ' ' || c == '\t' || c =='\n') 1266 continue; 1267 nextrevpair->endrev = argv; 1268 for (;; c = *++argv) { 1269 switch (c) { 1270 default: 1271 continue; 1272 case '\0': case ' ': case '\t': case '\n': 1273 case ',': case ';': 1274 break; 1275 case ':': case '-': 1276 if (c == separator) 1277 break; 1278 continue; 1279 } 1280 break; 1281 } 1282 *argv = '\0'; 1283 while (c==' ' || c=='\t' || c =='\n') 1284 c = *++argv; 1285 nextrevpair->numfld = 1286 !nextrevpair->endrev[0] ? 2 /* -rREV: */ : 1287 !nextrevpair->strtrev[0] ? 3 /* -r:REV */ : 1288 4 /* -rREV1:REV2 */; 1289 } 1290 if (!c) 1291 break; 1292 else if (c==',' || c==';') 1293 c = *++argv; 1294 else 1295 error("missing `,' near `%c%s'", c, argv+1); 1296 } 1297 } 1298