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