2 * RCS revision number handling
5 static char rcsid
[]= "$Id: rcsrev.c,v 1.1 1993/03/21 09:58:09 cgd Exp $ Purdue CS";
8 /* Copyright (C) 1982, 1988, 1989 Walter Tichy
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
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.
21 * Report all problems and direct all questions to:
22 * rcs-bugs@cs.purdue.edu
37 * Revision 4.5 89/05/01 15:13:22 narten
38 * changed copyright header to reflect current distribution rules
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)
44 * Revision 4.3 87/10/18 10:38:42 narten
45 * Updating version numbers. Changes relative to version 1.1 actually
48 * Revision 1.3 87/09/24 14:00:37 narten
49 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
52 * Revision 1.2 87/03/27 14:22:37 jenkins
55 * Revision 1.1 84/01/23 14:50:37 kcs
58 * Revision 4.1 83/03/25 21:10:45 wft
59 * Only changed $Header to $Id.
61 * Revision 3.4 82/12/04 13:24:08 wft
62 * Replaced getdelta() with gettree().
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.
68 * Revision 3.2 82/10/18 21:19:47 wft
69 * renamed compnum->cmpnum, compnumfld->cmpnumfld,
70 * numericrevno->numricrevno.
72 * Revision 3.1 82/10/11 19:46:09 wft
73 * changed expandsym() to check for source==nil; returns zero length string
81 /* version REVTEST is for testing the routines that generate a sequence
82 * of delta numbers needed to regenerate a given delta.
87 extern FILE * finptr
; /* RCS input file */
88 extern char * getid();
89 extern struct hshentry
* getnum();
93 extern char * getkeyval();
94 struct hshentry
* genbranch(); /* forward */
100 /* Given a pointer s to a dotted number (date or revision number),
101 * countnumflds returns the number of digitfields in s.
103 { register char * sp
;
105 if ((sp
=s
)==nil
) return(0);
106 if (*sp
== '\0') return(0);
109 if (*sp
++ == '.') count
++;
111 if (*(--sp
) == '.') count
--; /*trailing periods don't count*/
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.
122 register int i
, numflds
;
123 register char * tp
, * sp
;
125 numflds
=countnumflds(revno
);
127 VOID
strcpy(branchno
,revno
);
129 sp
=revno
; tp
=branchno
;
130 for (i
=1;i
<numflds
;i
++) {
131 while(*sp
!='.') *tp
++ = *sp
++;
140 int cmpnum(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.
148 register char * s1
, *s2
;
151 s1
=num1
==nil
?"":num1
;
152 s2
=num2
==nil
?"":num2
;
156 while (('0' <= *s1
) && (*s1
<= '9')) {
157 n1
= n1
*10 + (*s1
- '0');
164 while (('0' <= *s2
) && (*s2
<= '9')) {
165 n2
= n2
*10 + (*s2
- '0');
171 } while ((n1
==n2
) && (*s1
!='\0') && (*s2
!='\0'));
173 if (((*s1
=='\0') && (*s2
=='\0')) || (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;
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.
190 register char * s1
, *s2
;
194 /* skip fld-1 fields */
196 while(*s1
!= '.') s1
++;
201 while(*s2
!= '.') s2
++;
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 */
208 while (('0' <= *s1
) && (*s1
<= '9')) {
209 n1
= n1
*10 + (*s1
- '0');
213 while (('0' <= *s2
) && (*s2
<= '9')) {
214 n2
= n2
*10 + (*s2
- '0');
221 int compartial(num1
, num2
, length
)
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 */
231 register char *s1
, *s2
;
235 s1
= num1
; s2
= num2
;
236 if ( s1
==nil
|| *s1
== '\0' ) return 1;
237 if ( s2
==nil
|| *s2
== '\0' ) return -1;
241 while( ('0' <= *s1
) && (*s1
<= '9') ) {
242 n1
= n1
* 10 + (*s1
- '0') ;
245 if ( *s1
== '.' ) s1
++; /* skip . */
248 while( ( '0' <= *s2
) && ( *s2
<= '9' ) ) {
249 n2
= n2
* 10 + ( *s2
- '0' ) ;
252 if (*s2
== '.') s2
++;
253 } while( ( n1
== n2
) && ((--length
) != 0) &&
254 ( *s1
!= '\0') && (*s2
!= '\0') );
256 if ( (n1
!= n2
) || (length
== 0) ){
259 if ( *s1
== '\0' ) return 1;
260 if ( *s2
== '\0' ) return -1;
261 VOID
fprintf(stderr
, "RCS Internal error, routine: compartial\n");
269 /* increments the last field of revision number onum by one and
270 * places the result into nnum
273 register char * sp
, *tp
;
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 */
281 VOID
sprintf(tp
,"%d",atoi(sp
)+1);
285 char * partialno(rev1
,rev2
,length
)
286 char * rev1
, * rev2
; register int length
;
287 /* Function: Copies length fields of revision number rev2 into rev1.
290 { register char * r1
,* r2
;
294 while(*r2
!= '.' && *r2
!='\0') *r1
++ = *r2
++;
298 /* eliminate last '.'*/
305 char * getancestor(r1
, r2
, r3
)
307 /* function: finds the common ancestor of r1 and r2 and
309 * returns r3 if successful, false otherwise.
310 * works reliably only if r1 and r2 are not branch numbers.
313 char t1
[revlength
], t2
[revlength
];
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
);
323 while ((cmpnumfld(r1
, r2
, l3
+1)==0) && (cmpnumfld(r1
, r2
, l3
+2)==0)){
326 /* This will terminate since r1 and r2 are not the same; see above*/
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
);
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
);
339 if (cmpnumfld(r1
,r2
,l3
+1)==0) {
340 error("Ancestor for %s and %s undefined.",r1
,r2
);
343 return(partialno(r3
,r1
,l3
));
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.
361 register struct hshentry
* next
;
367 error("RCSfile empty.");
371 length
= countnumflds(revno
);
375 /* at least one field; find branch exactly */
376 while ((next
!=nil
) &&
377 ((result
=cmpnumfld(revno
,next
->num
,1))<0)) {
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
;}
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) &&
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))
397 { /*puts(next->num);*/
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
);
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) ) {
425 if ((next
==nil
) || (cmpnumfld(revno
,next
->num
,1)!=0)) {
426 error("Revision number %s too low.",partialno(t
,revno
,2));
429 if ((length
>2) && (result
!=0)) {
430 error("Revision %s not present.",partialno(t
,revno
,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
);
445 if ((author
!=nil
)&&(strcmp(author
,next
->author
)!=0)) {
446 error("Revision %s has author %s.",next
->num
,next
->author
);
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
);
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
476 register struct hshentry
* next
, * trail
;
477 register struct branchhead
* bhead
;
481 bhead
= bpoint
->branches
;
483 for (field
=3; field
<=length
; field
=field
+2) {
485 if (bhead
==nil
) {error("No side branches present for %s.",partialno(t
,revno
,field
-1));return nil
;}
488 /*branches are arranged in increasing order*/
489 while ((bhead
!=nil
) &&
490 ((result
=cmpnumfld(revno
,bhead
->hsh
->num
,field
))>0)) {
491 bhead
= bhead
->nextbranch
;
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
;}
499 /* pick latest one on that branch */
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))
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
);
513 } else { /* print up to last one suitable */
515 while (next
!=trail
) {
530 if (cmpnumfld(revno
,next
->num
,field
+1)<0) {
531 error("Revision number %s too low.",partialno(t
,revno
,field
+1));
534 do { /*puts(next->num);*/
538 } while ((next
!=nil
) &&
539 (cmpnumfld(revno
,next
->num
,field
+1) >=0));
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));
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
);
551 if ((author
!=nil
)&&(strcmp(author
,trail
->author
)!=0)) {
552 error("Revision %s has author %s.",trail
->num
,trail
->author
);
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
);
561 bhead
= trail
->branches
;
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.
576 register struct assoc
* next
;
579 if (strcmp(id
, next
->symbol
)==0)
580 return(next
->delta
->num
);
581 else next
=next
->nextassoc
;
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;
594 { register char * sp
, * tp
, *bp
;
596 register enum tokens d
;
598 sp
= source
; tp
=target
;
599 if (sp
== nil
) { /*accept nil pointer as a legal value*/
604 while (*sp
!= '\0') {
605 if (ctab
[*sp
] == DIGIT
) {
607 /* skip leading zeroes */
609 while(*sp
== '0') sp
++;
610 if (*sp
=='\0' || *sp
=='.') *tp
++ = '0'; /*single zero*/
612 while(ctab
[*sp
] == DIGIT
) *tp
++ = *sp
++;
613 if ((*sp
== '\0') || ((*sp
=='.')&&(*(sp
+1)=='\0'))) {
614 *tp
='\0'; return true;
616 if (*sp
== '.') *tp
++ = *sp
++;
618 error("Improper revision number: %s",source
);
622 } elsif (ctab
[*sp
] == LETTER
) {
625 } while(((d
=ctab
[*sp
])==LETTER
) || (d
==DIGIT
) ||
628 bp
=lookupsym(symbuf
);
630 error("Symbolic number %s is undefined.",symbuf
);
633 } else { /* copy number */
634 while (*tp
++ = *bp
++); /* copies the trailing \0*/
636 if ((*sp
== '\0') || ((*sp
=='.')&&(*(sp
+1)=='\0')))
641 error("Improper revision number: %s",source
);
645 error("Improper revision number: %s", source
);
659 int argc
; char * argv
[];
661 char symrevno
[revlength
]; /* used for input of revision numbers */
662 char numricrevno
[revlength
];
666 struct hshentry
* gendeltas
[hshsize
/2];
667 struct hshentry
* target
;
672 VOID
fputs("No input file\n",stderr
);
675 if ((finptr
=fopen(argv
[1], "r")) == NULL
) {
676 faterror("Can't open input file %s\n",argv
[1]);
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
);
704 while (gendeltas
[i
]!=nil
) {
705 VOID
fprintf(stderr
,"%s\n",gendeltas
[i
++]->num
);
709 VOID
fprintf(stderr
,"done\n");