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