1 /* Handle RCS revision numbers. */ 2 3 /* Copyright 1982, 1988, 1989 Walter Tichy 4 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 5 Distributed under license by the Free Software Foundation, Inc. 6 7 This file is part of RCS. 8 9 RCS is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 RCS is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with RCS; see the file COPYING. 21 If not, write to the Free Software Foundation, 22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 24 Report problems and direct all questions to: 25 26 rcs-bugs@cs.purdue.edu 27 28 */ 29 30 /* 31 * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsrev.c,v 1.8 1999/08/27 23:36:48 peter Exp $ 32 * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsrev.c,v 1.2 2003/06/17 04:25:47 dillon Exp $ 33 * 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, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsrev.c,v 1.2 2003/06/17 04:25:47 dillon 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 && strcmp(state,trail->state)!=0) { 636 rcserror("Revision %s has state %s.", 637 trail->num, 638 trail->state ? trail->state : "<empty>" 639 ); 640 return 0; 641 } 642 } 643 bhead = trail->branches; 644 645 } while ((field+=2) <= length); 646 *store = 0; 647 return trail; 648 } 649 650 651 static char const * 652 lookupsym(id) 653 char const *id; 654 /* Function: looks up id in the list of symbolic names starting 655 * with pointer SYMBOLS, and returns a pointer to the corresponding 656 * revision number. Return 0 if not present. 657 */ 658 { 659 register struct assoc const *next; 660 for (next = Symbols; next; next = next->nextassoc) 661 if (strcmp(id, next->symbol)==0) 662 return next->num; 663 return 0; 664 } 665 666 int expandsym(source, target) 667 char const *source; 668 struct buf *target; 669 /* Function: Source points to a revision number. Expandsym copies 670 * the number to target, but replaces all symbolic fields in the 671 * source number with their numeric values. 672 * Expand a branch followed by `.' to the latest revision on that branch. 673 * Ignore `.' after a revision. Remove leading zeros. 674 * returns false on error; 675 */ 676 { 677 return fexpandsym(source, target, (RILE*)0); 678 } 679 680 int 681 fexpandsym(source, target, fp) 682 char const *source; 683 struct buf *target; 684 RILE *fp; 685 /* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */ 686 { 687 register char const *sp, *bp; 688 register char *tp; 689 char const *tlim; 690 int dots; 691 692 sp = source; 693 bufalloc(target, 1); 694 tp = target->string; 695 if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */ 696 *tp='\0'; 697 return true; 698 } 699 if (sp[0] == KDELIM && !sp[1]) { 700 if (!getoldkeys(fp)) 701 return false; 702 if (!*prevrev.string) { 703 workerror("working file lacks revision number"); 704 return false; 705 } 706 bufscpy(target, prevrev.string); 707 return true; 708 } 709 tlim = tp + target->size; 710 dots = 0; 711 712 for (;;) { 713 register char *p = tp; 714 size_t s = tp - target->string; 715 int id = false; 716 for (;;) { 717 switch (ctab[(unsigned char)*sp]) { 718 case IDCHAR: 719 case LETTER: 720 case Letter: 721 id = true; 722 /* fall into */ 723 case DIGIT: 724 if (tlim <= p) 725 p = bufenlarge(target, &tlim); 726 *p++ = *sp++; 727 continue; 728 729 default: 730 break; 731 } 732 break; 733 } 734 if (tlim <= p) 735 p = bufenlarge(target, &tlim); 736 *p = 0; 737 tp = target->string + s; 738 739 if (id) { 740 bp = lookupsym(tp); 741 if (!bp) { 742 rcserror("Symbolic name `%s' is undefined.",tp); 743 return false; 744 } 745 } else { 746 /* skip leading zeros */ 747 for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++) 748 continue; 749 750 if (!*bp) { 751 if (s || *sp!='.') 752 break; 753 else { 754 /* Insert default branch before initial `.'. */ 755 char const *b; 756 if (Dbranch) 757 b = Dbranch; 758 else if (Head) 759 b = Head->num; 760 else 761 break; 762 getbranchno(b, target); 763 bp = tp = target->string; 764 tlim = tp + target->size; 765 } 766 } 767 } 768 769 while ((*tp++ = *bp++)) 770 if (tlim <= tp) 771 tp = bufenlarge(target, &tlim); 772 773 switch (*sp++) { 774 case '\0': 775 return true; 776 777 case '.': 778 if (!*sp) { 779 if (dots & 1) 780 break; 781 if (!(bp = branchtip(target->string))) 782 return false; 783 bufscpy(target, bp); 784 return true; 785 } 786 ++dots; 787 tp[-1] = '.'; 788 continue; 789 } 790 break; 791 } 792 793 rcserror("improper revision number: %s", source); 794 return false; 795 } 796 797 char const * 798 namedrev(name, delta) 799 char const *name; 800 struct hshentry *delta; 801 /* Yield NAME if it names DELTA, 0 otherwise. */ 802 { 803 if (name) { 804 char const *id = 0, *p, *val; 805 for (p = name; ; p++) 806 switch (ctab[(unsigned char)*p]) { 807 case IDCHAR: 808 case LETTER: 809 case Letter: 810 id = name; 811 break; 812 813 case DIGIT: 814 break; 815 816 case UNKN: 817 if (!*p && id && 818 (val = lookupsym(id)) && 819 strcmp(val, delta->num) == 0 820 ) 821 return id; 822 /* fall into */ 823 default: 824 return 0; 825 } 826 } 827 return 0; 828 } 829 830 static char const * 831 branchtip(branch) 832 char const *branch; 833 { 834 struct hshentry *h; 835 struct hshentries *hs; 836 837 h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs); 838 return h ? h->num : (char const*)0; 839 } 840 841 char const * 842 tiprev() 843 { 844 return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0; 845 } 846 847 848 849 #ifdef REVTEST 850 851 /* 852 * Test the routines that generate a sequence of delta numbers 853 * needed to regenerate a given delta. 854 */ 855 856 char const cmdid[] = "revtest"; 857 858 int 859 main(argc,argv) 860 int argc; char * argv[]; 861 { 862 static struct buf numricrevno; 863 char symrevno[100]; /* used for input of revision numbers */ 864 char author[20]; 865 char state[20]; 866 char date[20]; 867 struct hshentries *gendeltas; 868 struct hshentry * target; 869 int i; 870 871 if (argc<2) { 872 aputs("No input file\n",stderr); 873 exitmain(EXIT_FAILURE); 874 } 875 if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { 876 faterror("can't open input file %s", argv[1]); 877 } 878 Lexinit(); 879 getadmin(); 880 881 gettree(); 882 883 getdesc(false); 884 885 do { 886 /* all output goes to stderr, to have diagnostics and */ 887 /* errors in sequence. */ 888 aputs("\nEnter revision number or <return> or '.': ",stderr); 889 if (!fgets(symrevno, 100, stdin)) break; 890 if (*symrevno == '.') break; 891 aprintf(stderr,"%s;\n",symrevno); 892 expandsym(symrevno,&numricrevno); 893 aprintf(stderr,"expanded number: %s; ",numricrevno.string); 894 aprintf(stderr,"Date: "); 895 fgets(date, 20, stdin); aprintf(stderr,"%s; ",date); 896 aprintf(stderr,"Author: "); 897 fgets(author, 20, stdin); aprintf(stderr,"%s; ",author); 898 aprintf(stderr,"State: "); 899 fgets(state, 20, stdin); aprintf(stderr, "%s;\n", state); 900 target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0, 901 *state?state:(char*)0, &gendeltas); 902 if (target) { 903 while (gendeltas) { 904 aprintf(stderr,"%s\n",gendeltas->first->num); 905 gendeltas = gendeltas->next; 906 } 907 } 908 } while (true); 909 aprintf(stderr,"done\n"); 910 exitmain(EXIT_SUCCESS); 911 } 912 913 void exiterr() { _exit(EXIT_FAILURE); } 914 915 #endif 916