1: /* 2: * RCS revision number handling 3: */ 4: #ifndef lint 5: static char rcsid[]= "$Id: rcsrev.c,v 4.3 87/10/18 10:38:42 narten Exp $ Purdue CS"; 6: #endif 7: /********************************************************************************* 8: ********************************************************************************* 9: * 10: * Copyright (C) 1982 by Walter F. Tichy 11: * Purdue University 12: * Computer Science Department 13: * West Lafayette, IN 47907 14: * 15: * All rights reserved. No part of this software may be sold or distributed 16: * in any form or by any means without the prior written permission of the 17: * author. 18: * Report problems and direct all inquiries to Tichy@purdue (ARPA net). 19: */ 20: 21: 22: /* $Log: rcsrev.c,v $ 23: * Revision 4.3 87/10/18 10:38:42 narten 24: * Updating version numbers. Changes relative to version 1.1 actually 25: * relative to 4.1 26: * 27: * Revision 1.3 87/09/24 14:00:37 narten 28: * Sources now pass through lint (if you ignore printf/sprintf/fprintf 29: * warnings) 30: * 31: * Revision 1.2 87/03/27 14:22:37 jenkins 32: * Port to suns 33: * 34: * Revision 1.1 84/01/23 14:50:37 kcs 35: * Initial revision 36: * 37: * Revision 4.1 83/03/25 21:10:45 wft 38: * Only changed $Header to $Id. 39: * 40: * Revision 3.4 82/12/04 13:24:08 wft 41: * Replaced getdelta() with gettree(). 42: * 43: * Revision 3.3 82/11/28 21:33:15 wft 44: * fixed compartial() and compnum() for nil-parameters; fixed nils 45: * in error messages. Testprogram output shortenend. 46: * 47: * Revision 3.2 82/10/18 21:19:47 wft 48: * renamed compnum->cmpnum, compnumfld->cmpnumfld, 49: * numericrevno->numricrevno. 50: * 51: * Revision 3.1 82/10/11 19:46:09 wft 52: * changed expandsym() to check for source==nil; returns zero length string 53: * in that case. 54: */ 55: 56: 57: 58: /* 59: #define REVTEST 60: /* version REVTEST is for testing the routines that generate a sequence 61: * of delta numbers needed to regenerate a given delta. 62: */ 63: 64: #include "rcsbase.h" 65: 66: extern FILE * finptr; /* RCS input file */ 67: extern char * getid(); 68: extern struct hshentry * getnum(); 69: extern int getkey(); 70: extern int getlex(); 71: 72: extern char * getkeyval(); 73: extern int delta(), deltatext(); 74: struct hshentry * genbranch(); /* forward */ 75: 76: 77: 78: int countnumflds(s) 79: char * s; 80: /* Given a pointer s to a dotted number (date or revision number), 81: * countnumflds returns the number of digitfields in s. 82: */ 83: { register char * sp; 84: register int count; 85: if ((sp=s)==nil) return(0); 86: if (*sp == '\0') return(0); 87: count = 1; 88: while (*sp) { 89: if (*sp++ == '.') count++; 90: } 91: if (*(--sp) == '.') count--; /*trailing periods don't count*/ 92: return(count); 93: } 94: 95: getbranchno(revno,branchno) 96: char * revno, * branchno; 97: /* Given a non-nil revision number revno, getbranchno copies the number of the branch 98: * on which revno is into branchnumber. If revno itself is a branch number, 99: * it is copied unchanged. 100: */ 101: { 102: register int i, numflds; 103: register char * tp, * sp; 104: 105: numflds=countnumflds(revno); 106: if (numflds%2 == 1) 107: VOID strcpy(branchno,revno); 108: else { 109: sp=revno; tp=branchno; 110: for (i=1;i<numflds;i++) { 111: while(*sp!='.') *tp++ = *sp++; 112: *tp++ = *sp++; 113: } 114: *(tp-1)='\0'; 115: } 116: } 117: 118: 119: 120: int cmpnum(num1, num2) 121: char * num1, * num2; 122: /* compares the two dotted numbers num1 and num2 lexicographically 123: * by field. Individual fields are compared numerically. 124: * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp. 125: * omitted fields are assumed to be higher than the existing ones. 126: */ 127: { 128: register char * s1, *s2; 129: register int n1, n2; 130: 131: s1=num1==nil?"":num1; 132: s2=num2==nil?"":num2; 133: 134: do { 135: n1 = 0; 136: while (('0' <= *s1) && (*s1 <= '9')) { 137: n1 = n1*10 + (*s1 - '0'); 138: s1++; 139: } 140: /* skip '.' */ 141: if (*s1=='.') s1++; 142: 143: n2 = 0; 144: while (('0' <= *s2) && (*s2 <= '9')) { 145: n2 = n2*10 + (*s2 - '0'); 146: s2++; 147: } 148: /* skip '.' */ 149: if (*s2=='.') s2++; 150: 151: } while ((n1==n2) && (*s1!='\0') && (*s2!='\0')); 152: 153: if (((*s1=='\0') && (*s2=='\0')) || (n1!=n2)) 154: return (n1 - n2); 155: /*now n1==n2 and one of s1 or s2 is shorter*/ 156: /*give precedence to shorter one*/ 157: if (*s1=='\0') return 1; 158: else return -1; 159: 160: } 161: 162: 163: 164: int cmpnumfld(num1, num2, fld) 165: char * num1, * num2; int fld; 166: /* compares the two dotted numbers at field fld and returns 167: * num1[fld]-num2[fld]. Assumes that num1 and num2 have at least fld fields. 168: */ 169: { 170: register char * s1, *s2; 171: register int n1, n2; 172: 173: s1=num1; n1=fld-1; 174: /* skip fld-1 fields */ 175: while (n1) { 176: while(*s1 != '.') s1++; 177: n1--; s1++; 178: } 179: s2 = num2; n2=fld-1; 180: while (n2) { 181: while(*s2 != '.') s2++; 182: n2--; s2++; 183: } 184: /* Don't put the above into a single loop! */ 185: /* Now s1 and s2 point to the beginning of the respective fields */ 186: /* compute numerical value and compare */ 187: n1 = 0; 188: while (('0' <= *s1) && (*s1 <= '9')) { 189: n1 = n1*10 + (*s1 - '0'); 190: s1++; 191: } 192: n2 = 0; 193: while (('0' <= *s2) && (*s2 <= '9')) { 194: n2 = n2*10 + (*s2 - '0'); 195: s2++; 196: } 197: return (n1 - n2); 198: } 199: 200: 201: int compartial(num1, num2, length) 202: char * num1; 203: char * num2; 204: int length; 205: 206: /* compare the first "length" fields of two dot numbers; 207: the omitted field is considered to be larger than any number */ 208: /* restriction: at least one number has length or more fields */ 209: 210: { 211: register char *s1, *s2; 212: register int n1, n2; 213: 214: 215: s1 = num1; s2 = num2; 216: if ( s1==nil || *s1 == '\0' ) return 1; 217: if ( s2==nil || *s2 == '\0' ) return -1; 218: 219: do { 220: n1 = 0; 221: while( ('0' <= *s1) && (*s1 <= '9') ) { 222: n1 = n1 * 10 + (*s1 - '0') ; 223: s1++; 224: } 225: if ( *s1 == '.' ) s1++; /* skip . */ 226: 227: n2 = 0; 228: while( ( '0' <= *s2) && ( *s2 <= '9' ) ) { 229: n2 = n2 * 10 + ( *s2 - '0' ) ; 230: s2++; 231: } 232: if (*s2 == '.') s2++; 233: } while( ( n1 == n2) && ((--length) != 0) && 234: ( *s1 != '\0') && (*s2 != '\0') ); 235: 236: if ( (n1 != n2) || (length == 0) ){ 237: return(n1-n2); } 238: 239: if ( *s1 == '\0' ) return 1; 240: if ( *s2 == '\0' ) return -1; 241: fprintf(stderr, "RCS Internal error, routine: compartial\n"); 242: return(0); 243: /*NOTREACHED*/ 244: } 245: 246: 247: 248: incnum(onum,nnum) 249: char * onum, *nnum; 250: /* increments the last field of revision number onum by one and 251: * places the result into nnum 252: */ 253: { 254: register char * sp, *tp; 255: register int i; 256: 257: sp = onum; tp = nnum; 258: for (i=countnumflds(onum)-1; i>0; i--) { 259: while (*sp != '.') *tp++ = *sp++; 260: *tp++ = *sp++; /* copy dot also */ 261: } 262: VOID sprintf(tp,"%d",atoi(sp)+1); 263: } 264: 265: 266: char * partialno(rev1,rev2,length) 267: char * rev1, * rev2; register int length; 268: /* Function: Copies length fields of revision number rev2 into rev1. 269: * returns rev1. 270: */ 271: { register char * r1,* r2; 272: 273: r1=rev1; r2=rev2; 274: while (length) { 275: while(*r2 != '.' && *r2!='\0') *r1++ = *r2++; 276: *r1++ = *r2++; 277: length--; 278: } 279: /* eliminate last '.'*/ 280: *(r1-1)='\0'; 281: return rev1; 282: } 283: 284: 285: 286: char * getancestor(r1, r2, r3) 287: char * r1, *r2, *r3; 288: /* function: finds the common ancestor of r1 and r2 and 289: * places it into r3. 290: * returns r3 if successful, false otherwise. 291: * works reliably only if r1 and r2 are not branch numbers. 292: */ 293: { int l1, l2, l3; 294: char t1[revlength], t2[revlength]; 295: 296: l1=countnumflds(r1); l2=countnumflds(r2); 297: if ((l1<=2 && l2<=2)||(cmpnum(r1,r2)==0)) { 298: /* on main trunk or identical */ 299: error("Common ancestor of %s and %s undefined.", r1, r2); 300: return false; 301: } 302: 303: l3=0; 304: while ((cmpnumfld(r1, r2, l3+1)==0) && (cmpnumfld(r1, r2, l3+2)==0)){ 305: l3=l3+2; 306: } 307: /* This will terminate since r1 and r2 are not the same; see above*/ 308: if (l3==0) { 309: /* no common prefix. Common ancestor on main trunk. */ 310: VOID partialno(t1,r1,l1>2?2:l1); VOID partialno(t2,r2,l2>2?2:l2); 311: if (cmpnum(t1,t2)<0) 312: VOID strcpy(r3,t1); 313: else VOID strcpy(r3,t2); 314: if ((cmpnum(r3,r1)==0)||(cmpnum(r3,r2)==0)) { 315: error("Ancestor for %s and %s undefined.",r1,r2); 316: return false; 317: } 318: return r3; 319: } else { 320: if (cmpnumfld(r1,r2,l3+1)==0) { 321: error("Ancestor for %s and %s undefined.",r1,r2); 322: return false; 323: } 324: return(partialno(r3,r1,l3)); 325: } 326: } 327: 328: 329: 330: 331: struct hshentry * genrevs(revno,date,author,state,store) 332: char * revno, * date, * author, * state; 333: struct hshentry * * store; 334: /* Function: finds the deltas needed for reconstructing the 335: * revision given by revno, date, author, and state, and stores pointers 336: * to these deltas into an array whose starting address is given by store. 337: * The last pointer stored is nil. The last delta (target delta) is returned. 338: * If the proper delta could not be found, nil is returned. 339: */ 340: { 341: int length; 342: register struct hshentry * next; 343: int result; 344: char * branchnum; 345: char t[revlength]; 346: 347: if (Head == nil) { 348: error("RCSfile empty."); 349: return nil; 350: } 351: 352: length = countnumflds(revno); 353: next=Head; 354: 355: if (length >= 1) { 356: /* at least one field; find branch exactly */ 357: while ((next!=nil) && 358: ((result=cmpnumfld(revno,next->num,1))<0)) { 359: /*puts(next->num);*/ 360: *store++ = next; 361: next = next->next; 362: } 363: 364: if (next==nil) {error("Branch number %s too low.",partialno(t,revno,1));return nil;} 365: if (result>0) {error("Branch number %s not present.",partialno(t,revno,1));return nil;} 366: } 367: if (length<=1){ 368: /* pick latest one on given branch */ 369: branchnum = next->num; /* works even for empty revno*/ 370: while ((next!=nil) && 371: (cmpnumfld(branchnum,next->num,1)==0) && 372: !( 373: (date==nil?1:(cmpnum(date,next->date)>=0)) && 374: (author==nil?1:(strcmp(author,next->author)==0)) && 375: (state ==nil?1:(strcmp(state, next->state) ==0)) 376: ) 377: ) 378: { /*puts(next->num);*/ 379: *store ++ = next; 380: next=next->next; 381: } 382: if ((next==nil) || 383: (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { 384: error("Cannot find revision on branch %s with a date before %s, author %s, and state %s.", 385: length==0?partialno(t,branchnum,1):revno,date==nil?"<now>":date, 386: author==nil?"<any>":author, state==nil?"<any>":state); 387: return nil; 388: } else { 389: /*puts(next->num);*/ 390: *store++ = next; 391: } 392: *store = nil; 393: return next; 394: } 395: 396: /* length >=2 */ 397: /* find revision; may go low if length==2*/ 398: while ((next!=nil) && 399: ((result =cmpnumfld(revno,next->num,2)) <0) && 400: (cmpnumfld(revno,next->num,1)==0) ) { 401: /*puts(next->num);*/ 402: *store++ = next; 403: next = next->next; 404: } 405: 406: if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) { 407: error("Revision number %s too low.",partialno(t,revno,2)); 408: return nil; 409: } 410: if ((length>2) && (result!=0)) { 411: error("Revision %s not present.",partialno(t,revno,2)); 412: return nil; 413: } 414: 415: /* print last one */ 416: /*puts(next->num);*/ 417: *store++ = next; 418: 419: if (length>2) 420: return genbranch(next,revno,length,date,author,state,store); 421: else { /* length == 2*/ 422: if ((date!=nil) && (cmpnum(date,next->date)<0)){ 423: error("Revision %s has date %s.",next->num, next->date); 424: return nil; 425: } 426: if ((author!=nil)&&(strcmp(author,next->author)!=0)) { 427: error("Revision %s has author %s.",next->num,next->author); 428: return nil; 429: } 430: if ((state!=nil)&&(strcmp(state,next->state)!=0)) { 431: error("Revision %s has state %s.",next->num, 432: next->state==nil?"<empty>":next->state); 433: return nil; 434: } 435: *store=nil; 436: return next; 437: } 438: } 439: 440: 441: 442: 443: struct hshentry * genbranch(bpoint, revno, length,date,author,state,store) 444: struct hshentry * bpoint; 445: char * revno; int length; 446: char * date, * author, * state; 447: struct hshentry ** store; 448: /* Function: given a branchpoint, a revision number, date, author, and state, 449: * genbranch finds the deltas necessary to reconstruct the given revision 450: * from the branch point on. 451: * Pointers to the found deltas are stored in an array beginning with store. 452: * revno must be on a side branch. 453: * return nil on error 454: */ 455: { 456: int field; 457: register struct hshentry * next, * trail; 458: register struct branchhead * bhead; 459: int result; 460: char t[revlength]; 461: 462: bhead = bpoint->branches; 463: 464: for (field=3; field<=length; field=field+2) { 465: 466: if (bhead==nil) {error("No side branches present for %s.",partialno(t,revno,field-1));return nil;} 467: 468: /*find branch head*/ 469: /*branches are arranged in increasing order*/ 470: while ((bhead!=nil) && 471: ((result=cmpnumfld(revno,bhead->hsh->num,field))>0)) { 472: bhead = bhead->nextbranch; 473: } 474: 475: if (bhead==nil) {error("Branch number %s too high.",partialno(t,revno,field));return nil;} 476: if (result<0) {error("Branch number %s not present.",partialno(t,revno,field));return nil;} 477: 478: next = bhead->hsh; 479: if (length==field) { 480: /* pick latest one on that branch */ 481: trail=nil; 482: do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) && 483: (author==nil?1:(strcmp(author,next->author)==0)) && 484: (state ==nil?1:(strcmp(state, next->state) ==0)) 485: ) trail = next; 486: next=next->next; 487: } while (next!=nil); 488: 489: if (trail==nil) { 490: error("Cannot find revision on branch %s with a date before %s, author %s, and state %s.", 491: revno, date==nil?"<now>":date, 492: author==nil?"<any>":author, state==nil?"<any>":state); 493: return nil; 494: } else { /* print up to last one suitable */ 495: next = bhead->hsh; 496: while (next!=trail) { 497: /*puts(next->num);*/ 498: *store++ = next; 499: next=next->next; 500: } 501: /*puts(next->num);*/ 502: *store++ = next; 503: } 504: *store = nil; 505: return next; 506: } 507: 508: /* length > field */ 509: /* find revision */ 510: /* check low */ 511: if (cmpnumfld(revno,next->num,field+1)<0) { 512: error("Revision number %s too low.",partialno(t,revno,field+1)); 513: return(nil); 514: } 515: do { /*puts(next->num);*/ 516: *store++ = next; 517: trail = next; 518: next = next->next; 519: } while ((next!=nil) && 520: (cmpnumfld(revno,next->num,field+1) >=0)); 521: 522: if ((length>field+1) && /*need exact hit */ 523: (cmpnumfld(revno,trail->num,field+1) !=0)){ 524: error("Revision %s not present.",partialno(t,revno,field+1)); 525: return(nil); 526: } 527: if (length == field+1) { 528: if ((date!=nil) && (cmpnum(date,trail->date)<0)){ 529: error("Revision %s has date %s.",trail->num, trail->date); 530: return nil; 531: } 532: if ((author!=nil)&&(strcmp(author,trail->author)!=0)) { 533: error("Revision %s has author %s.",trail->num,trail->author); 534: return nil; 535: } 536: if ((state!=nil)&&(strcmp(state,trail->state)!=0)) { 537: error("Revision %s has state %s.",trail->num, 538: trail->state==nil?"<empty>":trail->state); 539: return nil; 540: } 541: } 542: bhead = trail->branches; 543: 544: } 545: * store = nil; 546: return trail; 547: } 548: 549: 550: char * lookupsym(id) 551: char * id; 552: /* Function: looks up id in the list of symbolic names starting 553: * with pointer SYMBOLS, and returns a pointer to the corresponding 554: * revision number. Returns nil if not present. 555: */ 556: { 557: register struct assoc * next; 558: next = Symbols; 559: while (next!=nil) { 560: if (strcmp(id, next->symbol)==0) 561: return(next->delta->num); 562: else next=next->nextassoc; 563: } 564: return nil; 565: } 566: 567: int expandsym(source, target) 568: char * source, * target; 569: /* Function: Source points to a revision number. Expandsym copies 570: * the number to target, but replaces all symbolic fields in the 571: * source number with their numeric values. 572: * A trailing '.' is omitted; leading zeroes are compressed. 573: * returns false on error; 574: */ 575: { register char * sp, * tp, *bp; 576: char symbuf[30]; 577: register enum tokens d; 578: 579: sp = source; tp=target; 580: if (sp == nil) { /*accept nil pointer as a legal value*/ 581: *tp='\0'; 582: return true; 583: } 584: 585: while (*sp != '\0') { 586: if (ctab[*sp] == DIGIT) { 587: if (*sp=='0') { 588: /* skip leading zeroes */ 589: sp++; 590: while(*sp == '0') sp++; 591: if (*sp=='\0' || *sp=='.') *tp++ = '0'; /*single zero*/ 592: } 593: while(ctab[*sp] == DIGIT) *tp++ = *sp++; 594: if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) { 595: *tp='\0'; return true; 596: } 597: if (*sp == '.') *tp++ = *sp++; 598: else { 599: error("Improper revision number: %s",source); 600: *tp = '\0'; 601: return false; 602: } 603: } elsif (ctab[*sp] == LETTER) { 604: bp = symbuf; 605: do { *bp++ = *sp++; 606: } while(((d=ctab[*sp])==LETTER) || (d==DIGIT) || 607: (d==IDCHAR)); 608: *bp= '\0'; 609: bp=lookupsym(symbuf); 610: if (bp==nil) { 611: error("Symbolic number %s is undefined.",symbuf); 612: *tp='\0'; 613: return false; 614: } else { /* copy number */ 615: while (*tp++ = *bp++); /* copies the trailing \0*/ 616: } 617: if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) 618: return true; 619: if (*sp == '.') { 620: *(tp-1) = *sp++; 621: } else { 622: error("Improper revision number: %s",source); 623: return false; 624: } 625: }else { 626: error("Improper revision number: %s", source); 627: *tp = '\0'; 628: return false; 629: } 630: } 631: *tp = '\0'; 632: return true; 633: } 634: 635: 636: 637: #ifdef REVTEST 638: 639: main(argc,argv) 640: int argc; char * argv[]; 641: { 642: char symrevno[revlength]; /* used for input of revision numbers */ 643: char numricrevno[revlength]; 644: char author[20]; 645: char state[20]; 646: char date[20]; 647: struct hshentry * gendeltas[hshsize/2]; 648: struct hshentry * target; 649: int i; 650: 651: cmdid = "revtest"; 652: if (argc<2) { 653: VOID fputs("No input file\n",stderr); 654: exit(-1); 655: } 656: if ((finptr=fopen(argv[1], "r")) == NULL) { 657: faterror("Can't open input file %s\n",argv[1]); 658: } 659: Lexinit(); 660: getadmin(); 661: 662: gettree(); 663: 664: getdesc(false); 665: 666: do { 667: /* all output goes to stderr, to have diagnostics and */ 668: /* errors in sequence. */ 669: VOID fprintf(stderr,"\nEnter revision number or <return> or '.': "); 670: if(gets(symrevno)==NULL) break; 671: if (*symrevno == '.') break; 672: VOID fprintf(stderr,"%s;\n",symrevno); 673: expandsym(symrevno,numricrevno); 674: VOID fprintf(stderr,"expanded number: %s; ",numricrevno); 675: VOID fprintf(stderr,"Date: "); 676: gets(date); VOID fprintf(stderr,"%s; ",date); 677: VOID fprintf(stderr,"Author: "); 678: gets(author);VOID fprintf(stderr,"%s; ",author); 679: VOID fprintf(stderr,"State: "); 680: gets(state); VOID fprintf(stderr, "%s;\n", state); 681: target=genrevs(numricrevno,*date=='\0'?(char *)nil:date, *author=='\0'?(char *)nil:author, 682: *state=='\0'?(char *)nil:state,gendeltas); 683: if (target!=nil) { 684: i=0; 685: while (gendeltas[i]!=nil) { 686: VOID fprintf(stderr,"%s\n",gendeltas[i++]->num); 687: } 688: } 689: } while (true); 690: VOID fprintf(stderr,"done\n"); 691: 692: } 693: 694: cleanup(){} 695: /*dummy*/ 696: 697: #endif REVTEST