|
|
1.1 root 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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.