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