Sync usage with man page.
[netbsd-mini2440.git] / usr.bin / rcs / src / rcsrev.c
blobf628f73b8f391bfd699d7246bde0d29ae6eea5ae
1 /*
2 * RCS revision number handling
3 */
4 #ifndef lint
5 static char rcsid[]= "$Id: rcsrev.c,v 1.1 1993/03/21 09:58:09 cgd Exp $ Purdue CS";
6 #endif
8 /* Copyright (C) 1982, 1988, 1989 Walter Tichy
9 * All rights reserved.
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.
21 * Report all problems and direct all questions to:
22 * rcs-bugs@cs.purdue.edu
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
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
46 * relative to 4.1
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)
52 * Revision 1.2 87/03/27 14:22:37 jenkins
53 * Port to suns
55 * Revision 1.1 84/01/23 14:50:37 kcs
56 * Initial revision
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
74 * in that case.
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.
85 #include "rcsbase.h"
87 extern FILE * finptr; /* RCS input file */
88 extern char * getid();
89 extern struct hshentry * getnum();
90 extern int getkey();
91 extern int getlex();
93 extern char * getkeyval();
94 struct hshentry * genbranch(); /* forward */
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.
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++;
111 if (*(--sp) == '.') count--; /*trailing periods don't count*/
112 return(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);
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++;
134 *(tp-1)='\0';
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.
148 register char * s1, *s2;
149 register int n1, n2;
151 s1=num1==nil?"":num1;
152 s2=num2==nil?"":num2;
154 do {
155 n1 = 0;
156 while (('0' <= *s1) && (*s1 <= '9')) {
157 n1 = n1*10 + (*s1 - '0');
158 s1++;
160 /* skip '.' */
161 if (*s1=='.') s1++;
163 n2 = 0;
164 while (('0' <= *s2) && (*s2 <= '9')) {
165 n2 = n2*10 + (*s2 - '0');
166 s2++;
168 /* skip '.' */
169 if (*s2=='.') s2++;
171 } while ((n1==n2) && (*s1!='\0') && (*s2!='\0'));
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;
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;
191 register int n1, n2;
193 s1=num1; n1=fld-1;
194 /* skip fld-1 fields */
195 while (n1) {
196 while(*s1 != '.') s1++;
197 n1--; s1++;
199 s2 = num2; n2=fld-1;
200 while (n2) {
201 while(*s2 != '.') s2++;
202 n2--; 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 */
207 n1 = 0;
208 while (('0' <= *s1) && (*s1 <= '9')) {
209 n1 = n1*10 + (*s1 - '0');
210 s1++;
212 n2 = 0;
213 while (('0' <= *s2) && (*s2 <= '9')) {
214 n2 = n2*10 + (*s2 - '0');
215 s2++;
217 return (n1 - n2);
221 int compartial(num1, num2, length)
222 char * num1;
223 char * num2;
224 int 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;
232 register int n1, n2;
235 s1 = num1; s2 = num2;
236 if ( s1==nil || *s1 == '\0' ) return 1;
237 if ( s2==nil || *s2 == '\0' ) return -1;
239 do {
240 n1 = 0;
241 while( ('0' <= *s1) && (*s1 <= '9') ) {
242 n1 = n1 * 10 + (*s1 - '0') ;
243 s1++;
245 if ( *s1 == '.' ) s1++; /* skip . */
247 n2 = 0;
248 while( ( '0' <= *s2) && ( *s2 <= '9' ) ) {
249 n2 = n2 * 10 + ( *s2 - '0' ) ;
250 s2++;
252 if (*s2 == '.') s2++;
253 } while( ( n1 == n2) && ((--length) != 0) &&
254 ( *s1 != '\0') && (*s2 != '\0') );
256 if ( (n1 != n2) || (length == 0) ){
257 return(n1-n2); }
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);
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
273 register char * sp, *tp;
274 register int i;
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.
288 * returns rev1.
290 { register char * r1,* r2;
292 r1=rev1; r2=rev2;
293 while (length) {
294 while(*r2 != '.' && *r2!='\0') *r1++ = *r2++;
295 *r1++ = *r2++;
296 length--;
298 /* eliminate last '.'*/
299 *(r1-1)='\0';
300 return rev1;
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.
312 { int l1, l2, l3;
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);
319 return false;
322 l3=0;
323 while ((cmpnumfld(r1, r2, l3+1)==0) && (cmpnumfld(r1, r2, l3+2)==0)){
324 l3=l3+2;
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;
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;
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.
360 int length;
361 register struct hshentry * next;
362 int result;
363 char * branchnum;
364 char t[revlength];
366 if (Head == nil) {
367 error("RCSfile empty.");
368 return nil;
371 length = countnumflds(revno);
372 next=Head;
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;
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;}
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) &&
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);*/
398 *store ++ = next;
399 next=next->next;
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;
411 *store = nil;
412 return next;
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;
425 if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) {
426 error("Revision number %s too low.",partialno(t,revno,2));
427 return nil;
429 if ((length>2) && (result!=0)) {
430 error("Revision %s not present.",partialno(t,revno,2));
431 return nil;
434 /* print last one */
435 /*puts(next->num);*/
436 *store++ = next;
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;
445 if ((author!=nil)&&(strcmp(author,next->author)!=0)) {
446 error("Revision %s has author %s.",next->num,next->author);
447 return nil;
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;
454 *store=nil;
455 return next;
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
475 int field;
476 register struct hshentry * next, * trail;
477 register struct branchhead * bhead;
478 int result;
479 char t[revlength];
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;}
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;
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;}
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);
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;
520 /*puts(next->num);*/
521 *store++ = next;
523 *store = nil;
524 return next;
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);
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));
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);
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;
551 if ((author!=nil)&&(strcmp(author,trail->author)!=0)) {
552 error("Revision %s has author %s.",trail->num,trail->author);
553 return nil;
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;
561 bhead = trail->branches;
564 * store = nil;
565 return trail;
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.
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;
583 return nil;
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;
595 char symbuf[30];
596 register enum tokens d;
598 sp = source; tp=target;
599 if (sp == nil) { /*accept nil pointer as a legal value*/
600 *tp='\0';
601 return true;
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*/
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++;
617 else {
618 error("Improper revision number: %s",source);
619 *tp = '\0';
620 return false;
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*/
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;
644 }else {
645 error("Improper revision number: %s", source);
646 *tp = '\0';
647 return false;
650 *tp = '\0';
651 return true;
656 #ifdef REVTEST
658 main(argc,argv)
659 int argc; char * argv[];
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;
670 cmdid = "revtest";
671 if (argc<2) {
672 VOID fputs("No input file\n",stderr);
673 exit(-1);
675 if ((finptr=fopen(argv[1], "r")) == NULL) {
676 faterror("Can't open input file %s\n",argv[1]);
678 Lexinit();
679 getadmin();
681 gettree();
683 getdesc(false);
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);
708 } while (true);
709 VOID fprintf(stderr,"done\n");
713 cleanup(){}
714 /*dummy*/
716 #endif REVTEST