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