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