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