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