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