1 /* $NetBSD: rcsrev.c,v 1.2 2016/01/14 04:22:39 christos Exp $ */ 2 3 /* Handle RCS revision numbers. */ 4 5 /* Copyright 1982, 1988, 1989 Walter Tichy 6 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 7 Distributed under license by the Free Software Foundation, Inc. 8 9 This file is part of RCS. 10 11 RCS is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 2, or (at your option) 14 any later version. 15 16 RCS is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20 21 You should have received a copy of the GNU General Public License 22 along with RCS; see the file COPYING. 23 If not, write to the Free Software Foundation, 24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 25 26 Report problems and direct all questions to: 27 28 rcs-bugs@cs.purdue.edu 29 30 */ 31 32 /* 33 * Log: rcsrev.c,v 34 * Revision 5.10 1995/06/16 06:19:24 eggert 35 * Update FSF address. 36 * 37 * Revision 5.9 1995/06/01 16:23:43 eggert 38 * (cmpdate, normalizeyear): New functions work around MKS RCS incompatibility. 39 * (cmpnum, compartial): s[d] -> *(s+d) to work around Cray compiler bug. 40 * (genrevs, genbranch): cmpnum -> cmpdate 41 * 42 * Revision 5.8 1994/03/17 14:05:48 eggert 43 * Remove lint. 44 * 45 * Revision 5.7 1993/11/09 17:40:15 eggert 46 * Fix format string typos. 47 * 48 * Revision 5.6 1993/11/03 17:42:27 eggert 49 * Revision number `.N' now stands for `D.N', where D is the default branch. 50 * Add -z. Improve quality of diagnostics. Add `namedrev' for Name support. 51 * 52 * Revision 5.5 1992/07/28 16:12:44 eggert 53 * Identifiers may now start with a digit. Avoid `unsigned'. 54 * 55 * Revision 5.4 1992/01/06 02:42:34 eggert 56 * while (E) ; -> while (E) continue; 57 * 58 * Revision 5.3 1991/08/19 03:13:55 eggert 59 * Add `-r$', `-rB.'. Remove botches like `<now>' from messages. Tune. 60 * 61 * Revision 5.2 1991/04/21 11:58:28 eggert 62 * Add tiprev(). 63 * 64 * Revision 5.1 1991/02/25 07:12:43 eggert 65 * Avoid overflow when comparing revision numbers. 66 * 67 * Revision 5.0 1990/08/22 08:13:43 eggert 68 * Remove compile-time limits; use malloc instead. 69 * Ansify and Posixate. Tune. 70 * Remove possibility of an internal error. Remove lint. 71 * 72 * Revision 4.5 89/05/01 15:13:22 narten 73 * changed copyright header to reflect current distribution rules 74 * 75 * Revision 4.4 87/12/18 11:45:22 narten 76 * more lint cleanups. Also, the NOTREACHED comment is no longer necessary, 77 * since there's now a return value there with a value. (Guy Harris) 78 * 79 * Revision 4.3 87/10/18 10:38:42 narten 80 * Updating version numbers. Changes relative to version 1.1 actually 81 * relative to 4.1 82 * 83 * Revision 1.3 87/09/24 14:00:37 narten 84 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 85 * warnings) 86 * 87 * Revision 1.2 87/03/27 14:22:37 jenkins 88 * Port to suns 89 * 90 * Revision 4.1 83/03/25 21:10:45 wft 91 * Only changed Header to Id. 92 * 93 * Revision 3.4 82/12/04 13:24:08 wft 94 * Replaced getdelta() with gettree(). 95 * 96 * Revision 3.3 82/11/28 21:33:15 wft 97 * fixed compartial() and compnum() for nil-parameters; fixed nils 98 * in error messages. Testprogram output shortenend. 99 * 100 * Revision 3.2 82/10/18 21:19:47 wft 101 * renamed compnum->cmpnum, compnumfld->cmpnumfld, 102 * numericrevno->numricrevno. 103 * 104 * Revision 3.1 82/10/11 19:46:09 wft 105 * changed expandsym() to check for source==nil; returns zero length string 106 * in that case. 107 */ 108 109 #include "rcsbase.h" 110 111 libId(revId, "Id: rcsrev.c,v 5.10 1995/06/16 06:19:24 eggert Exp ") 112 113 static char const *branchtip P((char const*)); 114 static char const *lookupsym P((char const*)); 115 static char const *normalizeyear P((char const*,char[5])); 116 static struct hshentry *genbranch P((struct hshentry const*,char const*,int,char const*,char const*,char const*,struct hshentries**)); 117 static void absent P((char const*,int)); 118 static void cantfindbranch P((char const*,char const[datesize],char const*,char const*)); 119 static void store1 P((struct hshentries***,struct hshentry*)); 120 121 122 123 int 124 countnumflds(s) 125 char const *s; 126 /* Given a pointer s to a dotted number (date or revision number), 127 * countnumflds returns the number of digitfields in s. 128 */ 129 { 130 register char const *sp; 131 register int count; 132 if (!(sp=s) || !*sp) 133 return 0; 134 count = 1; 135 do { 136 if (*sp++ == '.') count++; 137 } while (*sp); 138 return(count); 139 } 140 141 void 142 getbranchno(revno,branchno) 143 char const *revno; 144 struct buf *branchno; 145 /* Given a revision number revno, getbranchno copies the number of the branch 146 * on which revno is into branchno. If revno itself is a branch number, 147 * it is copied unchanged. 148 */ 149 { 150 register int numflds; 151 register char *tp; 152 153 bufscpy(branchno, revno); 154 numflds=countnumflds(revno); 155 if (!(numflds & 1)) { 156 tp = branchno->string; 157 while (--numflds) 158 while (*tp++ != '.') 159 continue; 160 *(tp-1)='\0'; 161 } 162 } 163 164 165 166 int cmpnum(num1, num2) 167 char const *num1, *num2; 168 /* compares the two dotted numbers num1 and num2 lexicographically 169 * by field. Individual fields are compared numerically. 170 * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp. 171 * omitted fields are assumed to be higher than the existing ones. 172 */ 173 { 174 register char const *s1, *s2; 175 register size_t d1, d2; 176 register int r; 177 178 s1 = num1 ? num1 : ""; 179 s2 = num2 ? num2 : ""; 180 181 for (;;) { 182 /* Give precedence to shorter one. */ 183 if (!*s1) 184 return (unsigned char)*s2; 185 if (!*s2) 186 return -1; 187 188 /* Strip leading zeros, then find number of digits. */ 189 while (*s1=='0') ++s1; 190 while (*s2=='0') ++s2; 191 for (d1=0; isdigit(*(s1+d1)); d1++) continue; 192 for (d2=0; isdigit(*(s2+d2)); d2++) continue; 193 194 /* Do not convert to integer; it might overflow! */ 195 if (d1 != d2) 196 return d1<d2 ? -1 : 1; 197 if ((r = memcmp(s1, s2, d1))) 198 return r; 199 s1 += d1; 200 s2 += d1; 201 202 /* skip '.' */ 203 if (*s1) s1++; 204 if (*s2) s2++; 205 } 206 } 207 208 209 210 int cmpnumfld(num1, num2, fld) 211 char const *num1, *num2; 212 int fld; 213 /* Compare the two dotted numbers at field fld. 214 * num1 and num2 must have at least fld fields. 215 * fld must be positive. 216 */ 217 { 218 register char const *s1, *s2; 219 register size_t d1, d2; 220 221 s1 = num1; 222 s2 = num2; 223 /* skip fld-1 fields */ 224 while (--fld) { 225 while (*s1++ != '.') 226 continue; 227 while (*s2++ != '.') 228 continue; 229 } 230 /* Now s1 and s2 point to the beginning of the respective fields */ 231 while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue; 232 while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue; 233 234 return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1; 235 } 236 237 238 int 239 cmpdate(d1, d2) 240 char const *d1, *d2; 241 /* 242 * Compare the two dates. This is just like cmpnum, 243 * except that for compatibility with old versions of RCS, 244 * 1900 is added to dates with two-digit years. 245 */ 246 { 247 char year1[5], year2[5]; 248 int r = cmpnumfld(normalizeyear(d1,year1), normalizeyear(d2,year2), 1); 249 250 if (r) 251 return r; 252 else { 253 while (isdigit(*d1)) d1++; d1 += *d1=='.'; 254 while (isdigit(*d2)) d2++; d2 += *d2=='.'; 255 return cmpnum(d1, d2); 256 } 257 } 258 259 static char const * 260 normalizeyear(date, year) 261 char const *date; 262 char year[5]; 263 { 264 if (isdigit(date[0]) && isdigit(date[1]) && !isdigit(date[2])) { 265 year[0] = '1'; 266 year[1] = '9'; 267 year[2] = date[0]; 268 year[3] = date[1]; 269 year[4] = 0; 270 return year; 271 } else 272 return date; 273 } 274 275 276 static void 277 cantfindbranch(revno, date, author, state) 278 char const *revno, date[datesize], *author, *state; 279 { 280 char datebuf[datesize + zonelenmax]; 281 282 rcserror("No revision on branch %s has%s%s%s%s%s%s.", 283 revno, 284 date ? " a date before " : "", 285 date ? date2str(date,datebuf) : "", 286 author ? " and author "+(date?0:4) : "", 287 author ? author : "", 288 state ? " and state "+(date||author?0:4) : "", 289 state ? state : "" 290 ); 291 } 292 293 static void 294 absent(revno, field) 295 char const *revno; 296 int field; 297 { 298 struct buf t; 299 bufautobegin(&t); 300 rcserror("%s %s absent", field&1?"revision":"branch", 301 partialno(&t,revno,field) 302 ); 303 bufautoend(&t); 304 } 305 306 307 int 308 compartial(num1, num2, length) 309 char const *num1, *num2; 310 int length; 311 312 /* compare the first "length" fields of two dot numbers; 313 the omitted field is considered to be larger than any number */ 314 /* restriction: at least one number has length or more fields */ 315 316 { 317 register char const *s1, *s2; 318 register size_t d1, d2; 319 register int r; 320 321 s1 = num1; s2 = num2; 322 if (!s1) return 1; 323 if (!s2) return -1; 324 325 for (;;) { 326 if (!*s1) return 1; 327 if (!*s2) return -1; 328 329 while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue; 330 while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue; 331 332 if (d1 != d2) 333 return d1<d2 ? -1 : 1; 334 if ((r = memcmp(s1, s2, d1))) 335 return r; 336 if (!--length) 337 return 0; 338 339 s1 += d1; 340 s2 += d1; 341 342 if (*s1 == '.') s1++; 343 if (*s2 == '.') s2++; 344 } 345 } 346 347 348 char * partialno(rev1,rev2,length) 349 struct buf *rev1; 350 char const *rev2; 351 register int length; 352 /* Function: Copies length fields of revision number rev2 into rev1. 353 * Return rev1's string. 354 */ 355 { 356 register char *r1; 357 358 bufscpy(rev1, rev2); 359 r1 = rev1->string; 360 while (length) { 361 while (*r1!='.' && *r1) 362 ++r1; 363 ++r1; 364 length--; 365 } 366 /* eliminate last '.'*/ 367 *(r1-1)='\0'; 368 return rev1->string; 369 } 370 371 372 373 374 static void 375 store1(store, next) 376 struct hshentries ***store; 377 struct hshentry *next; 378 /* 379 * Allocate a new list node that addresses NEXT. 380 * Append it to the list that **STORE is the end pointer of. 381 */ 382 { 383 register struct hshentries *p; 384 385 p = ftalloc(struct hshentries); 386 p->first = next; 387 **store = p; 388 *store = &p->rest; 389 } 390 391 struct hshentry * genrevs(revno,date,author,state,store) 392 char const *revno, *date, *author, *state; 393 struct hshentries **store; 394 /* Function: finds the deltas needed for reconstructing the 395 * revision given by revno, date, author, and state, and stores pointers 396 * to these deltas into a list whose starting address is given by store. 397 * The last delta (target delta) is returned. 398 * If the proper delta could not be found, 0 is returned. 399 */ 400 { 401 int length; 402 register struct hshentry * next; 403 int result; 404 char const *branchnum; 405 struct buf t; 406 char datebuf[datesize + zonelenmax]; 407 408 bufautobegin(&t); 409 410 if (!(next = Head)) { 411 rcserror("RCS file empty"); 412 goto norev; 413 } 414 415 length = countnumflds(revno); 416 417 if (length >= 1) { 418 /* at least one field; find branch exactly */ 419 while ((result=cmpnumfld(revno,next->num,1)) < 0) { 420 store1(&store, next); 421 next = next->next; 422 if (!next) { 423 rcserror("branch number %s too low", partialno(&t,revno,1)); 424 goto norev; 425 } 426 } 427 428 if (result>0) { 429 absent(revno, 1); 430 goto norev; 431 } 432 } 433 if (length<=1){ 434 /* pick latest one on given branch */ 435 branchnum = next->num; /* works even for empty revno*/ 436 while (next && 437 cmpnumfld(branchnum,next->num,1) == 0 && 438 ( 439 (date && cmpdate(date,next->date) < 0) || 440 (author && strcmp(author,next->author) != 0) || 441 (state && strcmp(state,next->state) != 0) 442 ) 443 ) 444 { 445 store1(&store, next); 446 next=next->next; 447 } 448 if (!next || 449 (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { 450 cantfindbranch( 451 length ? revno : partialno(&t,branchnum,1), 452 date, author, state 453 ); 454 goto norev; 455 } else { 456 store1(&store, next); 457 } 458 *store = 0; 459 return next; 460 } 461 462 /* length >=2 */ 463 /* find revision; may go low if length==2*/ 464 while ((result=cmpnumfld(revno,next->num,2)) < 0 && 465 (cmpnumfld(revno,next->num,1)==0) ) { 466 store1(&store, next); 467 next = next->next; 468 if (!next) 469 break; 470 } 471 472 if (!next || cmpnumfld(revno,next->num,1) != 0) { 473 rcserror("revision number %s too low", partialno(&t,revno,2)); 474 goto norev; 475 } 476 if ((length>2) && (result!=0)) { 477 absent(revno, 2); 478 goto norev; 479 } 480 481 /* print last one */ 482 store1(&store, next); 483 484 if (length>2) 485 return genbranch(next,revno,length,date,author,state,store); 486 else { /* length == 2*/ 487 if (date && cmpdate(date,next->date)<0) { 488 rcserror("Revision %s has date %s.", 489 next->num, 490 date2str(next->date, datebuf) 491 ); 492 return 0; 493 } 494 if (author && strcmp(author,next->author)!=0) { 495 rcserror("Revision %s has author %s.", 496 next->num, next->author 497 ); 498 return 0; 499 } 500 if (state && strcmp(state,next->state)!=0) { 501 rcserror("Revision %s has state %s.", 502 next->num, 503 next->state ? next->state : "<empty>" 504 ); 505 return 0; 506 } 507 *store = 0; 508 return next; 509 } 510 511 norev: 512 bufautoend(&t); 513 return 0; 514 } 515 516 517 518 519 static struct hshentry * 520 genbranch(bpoint, revno, length, date, author, state, store) 521 struct hshentry const *bpoint; 522 char const *revno; 523 int length; 524 char const *date, *author, *state; 525 struct hshentries **store; 526 /* Function: given a branchpoint, a revision number, date, author, and state, 527 * genbranch finds the deltas necessary to reconstruct the given revision 528 * from the branch point on. 529 * Pointers to the found deltas are stored in a list beginning with store. 530 * revno must be on a side branch. 531 * Return 0 on error. 532 */ 533 { 534 int field; 535 register struct hshentry * next, * trail; 536 register struct branchhead const *bhead; 537 int result; 538 struct buf t; 539 char datebuf[datesize + zonelenmax]; 540 541 field = 3; 542 bhead = bpoint->branches; 543 544 do { 545 if (!bhead) { 546 bufautobegin(&t); 547 rcserror("no side branches present for %s", 548 partialno(&t,revno,field-1) 549 ); 550 bufautoend(&t); 551 return 0; 552 } 553 554 /*find branch head*/ 555 /*branches are arranged in increasing order*/ 556 while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) { 557 bhead = bhead->nextbranch; 558 if (!bhead) { 559 bufautobegin(&t); 560 rcserror("branch number %s too high", 561 partialno(&t,revno,field) 562 ); 563 bufautoend(&t); 564 return 0; 565 } 566 } 567 568 if (result<0) { 569 absent(revno, field); 570 return 0; 571 } 572 573 next = bhead->hsh; 574 if (length==field) { 575 /* pick latest one on that branch */ 576 trail = 0; 577 do { if ((!date || cmpdate(date,next->date)>=0) && 578 (!author || strcmp(author,next->author)==0) && 579 (!state || strcmp(state,next->state)==0) 580 ) trail = next; 581 next=next->next; 582 } while (next); 583 584 if (!trail) { 585 cantfindbranch(revno, date, author, state); 586 return 0; 587 } else { /* print up to last one suitable */ 588 next = bhead->hsh; 589 while (next!=trail) { 590 store1(&store, next); 591 next=next->next; 592 } 593 store1(&store, next); 594 } 595 *store = 0; 596 return next; 597 } 598 599 /* length > field */ 600 /* find revision */ 601 /* check low */ 602 if (cmpnumfld(revno,next->num,field+1)<0) { 603 bufautobegin(&t); 604 rcserror("revision number %s too low", 605 partialno(&t,revno,field+1) 606 ); 607 bufautoend(&t); 608 return 0; 609 } 610 do { 611 store1(&store, next); 612 trail = next; 613 next = next->next; 614 } while (next && cmpnumfld(revno,next->num,field+1)>=0); 615 616 if ((length>field+1) && /*need exact hit */ 617 (cmpnumfld(revno,trail->num,field+1) !=0)){ 618 absent(revno, field+1); 619 return 0; 620 } 621 if (length == field+1) { 622 if (date && cmpdate(date,trail->date)<0) { 623 rcserror("Revision %s has date %s.", 624 trail->num, 625 date2str(trail->date, datebuf) 626 ); 627 return 0; 628 } 629 if (author && strcmp(author,trail->author)!=0) { 630 rcserror("Revision %s has author %s.", 631 trail->num, trail->author 632 ); 633 return 0; 634 } 635 if (state) { 636 const char *st; 637 638 if (trail->state == NULL) 639 st = "<empty>"; 640 else if (strcmp(trail->state, state) != 0) 641 st = trail->state; 642 else 643 st = NULL; 644 645 if (st) 646 rcserror("Revision %s has state %s.", 647 trail->num, st); 648 return 0; 649 } 650 } 651 bhead = trail->branches; 652 653 } while ((field+=2) <= length); 654 *store = 0; 655 return trail; 656 } 657 658 659 static char const * 660 lookupsym(id) 661 char const *id; 662 /* Function: looks up id in the list of symbolic names starting 663 * with pointer SYMBOLS, and returns a pointer to the corresponding 664 * revision number. Return 0 if not present. 665 */ 666 { 667 register struct assoc const *next; 668 for (next = Symbols; next; next = next->nextassoc) 669 if (strcmp(id, next->symbol)==0) 670 return next->num; 671 return 0; 672 } 673 674 int expandsym(source, target) 675 char const *source; 676 struct buf *target; 677 /* Function: Source points to a revision number. Expandsym copies 678 * the number to target, but replaces all symbolic fields in the 679 * source number with their numeric values. 680 * Expand a branch followed by `.' to the latest revision on that branch. 681 * Ignore `.' after a revision. Remove leading zeros. 682 * returns false on error; 683 */ 684 { 685 return fexpandsym(source, target, (RILE*)0); 686 } 687 688 int 689 fexpandsym(source, target, fp) 690 char const *source; 691 struct buf *target; 692 RILE *fp; 693 /* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */ 694 { 695 register char const *sp, *bp; 696 register char *tp; 697 char const *tlim; 698 int dots; 699 700 sp = source; 701 bufalloc(target, 1); 702 tp = target->string; 703 if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */ 704 *tp='\0'; 705 return true; 706 } 707 if (sp[0] == KDELIM && !sp[1]) { 708 if (!getoldkeys(fp)) 709 return false; 710 if (!*prevrev.string) { 711 workerror("working file lacks revision number"); 712 return false; 713 } 714 bufscpy(target, prevrev.string); 715 return true; 716 } 717 tlim = tp + target->size; 718 dots = 0; 719 720 for (;;) { 721 register char *p = tp; 722 size_t s = tp - target->string; 723 int id = false; 724 for (;;) { 725 switch (ctab[(unsigned char)*sp]) { 726 case IDCHAR: 727 case LETTER: 728 case Letter: 729 id = true; 730 /* fall into */ 731 case DIGIT: 732 if (tlim <= p) 733 p = bufenlarge(target, &tlim); 734 *p++ = *sp++; 735 continue; 736 737 default: 738 break; 739 } 740 break; 741 } 742 if (tlim <= p) 743 p = bufenlarge(target, &tlim); 744 *p = 0; 745 tp = target->string + s; 746 747 if (id) { 748 bp = lookupsym(tp); 749 if (!bp) { 750 rcserror("Symbolic name `%s' is undefined.",tp); 751 return false; 752 } 753 } else { 754 /* skip leading zeros */ 755 for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++) 756 continue; 757 758 if (!*bp) { 759 if (s || *sp!='.') 760 break; 761 else { 762 /* Insert default branch before initial `.'. */ 763 char const *b; 764 if (Dbranch) 765 b = Dbranch; 766 else if (Head) 767 b = Head->num; 768 else 769 break; 770 getbranchno(b, target); 771 bp = tp = target->string; 772 tlim = tp + target->size; 773 } 774 } 775 } 776 777 while ((*tp++ = *bp++)) 778 if (tlim <= tp) 779 tp = bufenlarge(target, &tlim); 780 781 switch (*sp++) { 782 case '\0': 783 return true; 784 785 case '.': 786 if (!*sp) { 787 if (dots & 1) 788 break; 789 if (!(bp = branchtip(target->string))) 790 return false; 791 bufscpy(target, bp); 792 return true; 793 } 794 ++dots; 795 tp[-1] = '.'; 796 continue; 797 } 798 break; 799 } 800 801 rcserror("improper revision number: %s", source); 802 return false; 803 } 804 805 char const * 806 namedrev(name, delta) 807 char const *name; 808 struct hshentry *delta; 809 /* Yield NAME if it names DELTA, 0 otherwise. */ 810 { 811 if (name) { 812 char const *id = 0, *p, *val; 813 for (p = name; ; p++) 814 switch (ctab[(unsigned char)*p]) { 815 case IDCHAR: 816 case LETTER: 817 case Letter: 818 id = name; 819 break; 820 821 case DIGIT: 822 break; 823 824 case UNKN: 825 if (!*p && id && 826 (val = lookupsym(id)) && 827 strcmp(val, delta->num) == 0 828 ) 829 return id; 830 /* fall into */ 831 default: 832 return 0; 833 } 834 } 835 return 0; 836 } 837 838 static char const * 839 branchtip(branch) 840 char const *branch; 841 { 842 struct hshentry *h; 843 struct hshentries *hs; 844 845 h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs); 846 return h ? h->num : (char const*)0; 847 } 848 849 char const * 850 tiprev() 851 { 852 return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0; 853 } 854 855 856 857 #ifdef REVTEST 858 859 /* 860 * Test the routines that generate a sequence of delta numbers 861 * needed to regenerate a given delta. 862 */ 863 864 char const cmdid[] = "revtest"; 865 866 int 867 main(argc,argv) 868 int argc; char * argv[]; 869 { 870 static struct buf numricrevno; 871 char symrevno[100]; /* used for input of revision numbers */ 872 char author[20]; 873 char state[20]; 874 char date[20]; 875 struct hshentries *gendeltas; 876 struct hshentry * target; 877 int i; 878 879 if (argc<2) { 880 aputs("No input file\n",stderr); 881 exitmain(EXIT_FAILURE); 882 } 883 if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { 884 faterror("can't open input file %s", argv[1]); 885 } 886 Lexinit(); 887 getadmin(); 888 889 gettree(); 890 891 getdesc(false); 892 893 do { 894 /* all output goes to stderr, to have diagnostics and */ 895 /* errors in sequence. */ 896 aputs("\nEnter revision number or <return> or '.': ",stderr); 897 if (!gets(symrevno)) break; 898 if (*symrevno == '.') break; 899 aprintf(stderr,"%s;\n",symrevno); 900 expandsym(symrevno,&numricrevno); 901 aprintf(stderr,"expanded number: %s; ",numricrevno.string); 902 aprintf(stderr,"Date: "); 903 gets(date); aprintf(stderr,"%s; ",date); 904 aprintf(stderr,"Author: "); 905 gets(author); aprintf(stderr,"%s; ",author); 906 aprintf(stderr,"State: "); 907 gets(state); aprintf(stderr, "%s;\n", state); 908 target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0, 909 *state?state:(char*)0, &gendeltas); 910 if (target) { 911 while (gendeltas) { 912 aprintf(stderr,"%s\n",gendeltas->first->num); 913 gendeltas = gendeltas->next; 914 } 915 } 916 } while (true); 917 aprintf(stderr,"done\n"); 918 exitmain(EXIT_SUCCESS); 919 } 920 921 void exiterr() { _exit(EXIT_FAILURE); } 922 923 #endif 924