home *** CD-ROM | disk | FTP | other *** search
- /*
- * RCS revision number handling
- */
- #ifndef lint
- static char rcsid[]= "$Id: rcsrev.c,v 5.3 90/07/16 21:40:12 lfk Release $ Purdue CS";
- #endif
-
- /* Copyright (C) 1982, 1988, 1989 Walter Tichy
- Distributed under license by the Free Software Foundation, Inc.
-
- This file is part of RCS.
-
- RCS is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- RCS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with RCS; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Report problems and direct all questions to:
-
- rcs-bugs@cs.purdue.edu
-
- */
-
-
-
-
- /* $Log: rcsrev.c,v $
- * Revision 5.3 90/07/16 21:40:12 lfk
- * checkin for release compilation
- *
- * Revision 5.2 90/07/15 11:34:46 ROOT_DOS
- * DOS version of RCS 4.0 checked in for MODS
- * by lfk@athena.mit.edu
- * Also update to MSC 6.0
- *
- * Revision 4.5 89/05/01 15:13:22 narten
- * changed copyright header to reflect current distribution rules
- *
- * Revision 4.4 87/12/18 11:45:22 narten
- * more lint cleanups. Also, the NOTREACHED comment is no longer necessary,
- * since there's now a return value there with a value. (Guy Harris)
- *
- * Revision 4.3 87/10/18 10:38:42 narten
- * Updating version numbers. Changes relative to version 1.1 actually
- * relative to 4.1
- *
- * Revision 1.3 87/09/24 14:00:37 narten
- * Sources now pass through lint (if you ignore printf/sprintf/fprintf
- * warnings)
- *
- * Revision 1.2 87/03/27 14:22:37 jenkins
- * Port to suns
- *
- * Revision 1.1 84/01/23 14:50:37 kcs
- * Initial revision
- *
- * Revision 4.1 83/03/25 21:10:45 wft
- * Only changed $Header to $Id.
- *
- * Revision 3.4 82/12/04 13:24:08 wft
- * Replaced getdelta() with gettree().
- *
- * Revision 3.3 82/11/28 21:33:15 wft
- * fixed compartial() and compnum() for nil-parameters; fixed nils
- * in error messages. Testprogram output shortenend.
- *
- * Revision 3.2 82/10/18 21:19:47 wft
- * renamed compnum->cmpnum, compnumfld->cmpnumfld,
- * numericrevno->numricrevno.
- *
- * Revision 3.1 82/10/11 19:46:09 wft
- * changed expandsym() to check for source==nil; returns zero length string
- * in that case.
- */
-
-
-
- /*
- #define REVTEST
- /* version REVTEST is for testing the routines that generate a sequence
- * of delta numbers needed to regenerate a given delta.
- */
-
- #include "rcsbase.h"
-
- extern FILE * finptr; /* RCS input file */
- extern char * getid();
- extern struct hshentry * getnum();
- extern int getkey();
- extern int getlex();
-
- extern char * getkeyval();
- struct hshentry * genbranch(); /* forward */
-
-
-
- int countnumflds(s)
- char * s;
- /* Given a pointer s to a dotted number (date or revision number),
- * countnumflds returns the number of digitfields in s.
- */
- { register char * sp;
- register int count;
- if ((sp=s)==nil) return(0);
- if (*sp == '\0') return(0);
- count = 1;
- while (*sp) {
- if (*sp++ == '.') count++;
- }
- if (*(--sp) == '.') count--; /*trailing periods don't count*/
- return(count);
- }
-
- getbranchno(revno,branchno)
- char * revno, * branchno;
- /* Given a non-nil revision number revno, getbranchno copies the number of the branch
- * on which revno is into branchnumber. If revno itself is a branch number,
- * it is copied unchanged.
- */
- {
- register int i, numflds;
- register char * tp, * sp;
-
- numflds=countnumflds(revno);
- if (numflds%2 == 1)
- VOID strcpy(branchno,revno);
- else {
- sp=revno; tp=branchno;
- for (i=1;i<numflds;i++) {
- while(*sp!='.') *tp++ = *sp++;
- *tp++ = *sp++;
- }
- *(tp-1)='\0';
- }
- }
-
-
-
- int cmpnum(num1, num2)
- char * num1, * num2;
- /* compares the two dotted numbers num1 and num2 lexicographically
- * by field. Individual fields are compared numerically.
- * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
- * omitted fields are assumed to be higher than the existing ones.
- */
- {
- register char * s1, *s2;
- register int n1, n2;
-
- s1=num1==nil?"":num1;
- s2=num2==nil?"":num2;
-
- do {
- n1 = 0;
- while (('0' <= *s1) && (*s1 <= '9')) {
- n1 = n1*10 + (*s1 - '0');
- s1++;
- }
- /* skip '.' */
- if (*s1=='.') s1++;
-
- n2 = 0;
- while (('0' <= *s2) && (*s2 <= '9')) {
- n2 = n2*10 + (*s2 - '0');
- s2++;
- }
- /* skip '.' */
- if (*s2=='.') s2++;
-
- } while ((n1==n2) && (*s1!='\0') && (*s2!='\0'));
-
- if (((*s1=='\0') && (*s2=='\0')) || (n1!=n2))
- return (n1 - n2);
- /*now n1==n2 and one of s1 or s2 is shorter*/
- /*give precedence to shorter one*/
- if (*s1=='\0') return 1;
- else return -1;
-
- }
-
-
-
- int cmpnumfld(num1, num2, fld)
- char * num1, * num2; int fld;
- /* compares the two dotted numbers at field fld and returns
- * num1[fld]-num2[fld]. Assumes that num1 and num2 have at least fld fields.
- */
- {
- register char * s1, *s2;
- register int n1, n2;
-
- s1=num1; n1=fld-1;
- /* skip fld-1 fields */
- while (n1) {
- while(*s1 != '.') s1++;
- n1--; s1++;
- }
- s2 = num2; n2=fld-1;
- while (n2) {
- while(*s2 != '.') s2++;
- n2--; s2++;
- }
- /* Don't put the above into a single loop! */
- /* Now s1 and s2 point to the beginning of the respective fields */
- /* compute numerical value and compare */
- n1 = 0;
- while (('0' <= *s1) && (*s1 <= '9')) {
- n1 = n1*10 + (*s1 - '0');
- s1++;
- }
- n2 = 0;
- while (('0' <= *s2) && (*s2 <= '9')) {
- n2 = n2*10 + (*s2 - '0');
- s2++;
- }
- return (n1 - n2);
- }
-
-
- int compartial(num1, num2, length)
- char * num1;
- char * num2;
- int length;
-
- /* compare the first "length" fields of two dot numbers;
- the omitted field is considered to be larger than any number */
- /* restriction: at least one number has length or more fields */
-
- {
- register char *s1, *s2;
- register int n1, n2;
-
-
- s1 = num1; s2 = num2;
- if ( s1==nil || *s1 == '\0' ) return 1;
- if ( s2==nil || *s2 == '\0' ) return -1;
-
- do {
- n1 = 0;
- while( ('0' <= *s1) && (*s1 <= '9') ) {
- n1 = n1 * 10 + (*s1 - '0') ;
- s1++;
- }
- if ( *s1 == '.' ) s1++; /* skip . */
-
- n2 = 0;
- while( ( '0' <= *s2) && ( *s2 <= '9' ) ) {
- n2 = n2 * 10 + ( *s2 - '0' ) ;
- s2++;
- }
- if (*s2 == '.') s2++;
- } while( ( n1 == n2) && ((--length) != 0) &&
- ( *s1 != '\0') && (*s2 != '\0') );
-
- if ( (n1 != n2) || (length == 0) ){
- return(n1-n2); }
-
- if ( *s1 == '\0' ) return 1;
- if ( *s2 == '\0' ) return -1;
- VOID fprintf(stderr, "RCS Internal error, routine: compartial\n");
- return(0);
- }
-
-
-
- incnum(onum,nnum)
- char * onum, *nnum;
- /* increments the last field of revision number onum by one and
- * places the result into nnum
- */
- {
- register char * sp, *tp;
- register int i;
-
- sp = onum; tp = nnum;
- for (i=countnumflds(onum)-1; i>0; i--) {
- while (*sp != '.') *tp++ = *sp++;
- *tp++ = *sp++; /* copy dot also */
- }
- VOID sprintf(tp,"%d",atoi(sp)+1);
- }
-
-
- char * partialno(rev1,rev2,length)
- char * rev1, * rev2; register int length;
- /* Function: Copies length fields of revision number rev2 into rev1.
- * returns rev1.
- */
- { register char * r1,* r2;
-
- r1=rev1; r2=rev2;
- while (length) {
- while(*r2 != '.' && *r2!='\0') *r1++ = *r2++;
- *r1++ = *r2++;
- length--;
- }
- /* eliminate last '.'*/
- *(r1-1)='\0';
- return rev1;
- }
-
-
-
- char * getancestor(r1, r2, r3)
- char * r1, *r2, *r3;
- /* function: finds the common ancestor of r1 and r2 and
- * places it into r3.
- * returns r3 if successful, false otherwise.
- * works reliably only if r1 and r2 are not branch numbers.
- */
- { int l1, l2, l3;
- char t1[revlength], t2[revlength];
-
- l1=countnumflds(r1); l2=countnumflds(r2);
- if ((l1<=2 && l2<=2)||(cmpnum(r1,r2)==0)) {
- /* on main trunk or identical */
- error("Common ancestor of %s and %s undefined.", r1, r2);
- return false;
- }
-
- l3=0;
- while ((cmpnumfld(r1, r2, l3+1)==0) && (cmpnumfld(r1, r2, l3+2)==0)){
- l3=l3+2;
- }
- /* This will terminate since r1 and r2 are not the same; see above*/
- if (l3==0) {
- /* no common prefix. Common ancestor on main trunk. */
- VOID partialno(t1,r1,l1>2?2:l1); VOID partialno(t2,r2,l2>2?2:l2);
- if (cmpnum(t1,t2)<0)
- VOID strcpy(r3,t1);
- else VOID strcpy(r3,t2);
- if ((cmpnum(r3,r1)==0)||(cmpnum(r3,r2)==0)) {
- error("Ancestor for %s and %s undefined.",r1,r2);
- return false;
- }
- return r3;
- } else {
- if (cmpnumfld(r1,r2,l3+1)==0) {
- error("Ancestor for %s and %s undefined.",r1,r2);
- return false;
- }
- return(partialno(r3,r1,l3));
- }
- }
-
-
-
-
- struct hshentry * genrevs(revno,date,author,state,store)
- char * revno, * date, * author, * state;
- struct hshentry * * store;
- /* Function: finds the deltas needed for reconstructing the
- * revision given by revno, date, author, and state, and stores pointers
- * to these deltas into an array whose starting address is given by store.
- * The last pointer stored is nil. The last delta (target delta) is returned.
- * If the proper delta could not be found, nil is returned.
- */
- {
- int length;
- register struct hshentry * next;
- int result;
- char * branchnum;
- char t[revlength];
-
- if (Head == nil) {
- error("RCSfile empty.");
- return nil;
- }
-
- length = countnumflds(revno);
- next=Head;
-
- if (length >= 1) {
- /* at least one field; find branch exactly */
- while ((next!=nil) &&
- ((result=cmpnumfld(revno,next->num,1))<0)) {
- /*puts(next->num);*/
- *store++ = next;
- next = next->next;
- }
-
- if (next==nil) {error("Branch number %s too low.",partialno(t,revno,1));return nil;}
- if (result>0) {error("Branch number %s not present.",partialno(t,revno,1));return nil;}
- }
- if (length<=1){
- /* pick latest one on given branch */
- branchnum = next->num; /* works even for empty revno*/
- while ((next!=nil) &&
- (cmpnumfld(branchnum,next->num,1)==0) &&
- !(
- (date==nil?1:(cmpnum(date,next->date)>=0)) &&
- (author==nil?1:(strcmp(author,next->author)==0)) &&
- (state ==nil?1:(strcmp(state, next->state) ==0))
- )
- )
- { /*puts(next->num);*/
- *store ++ = next;
- next=next->next;
- }
- if ((next==nil) ||
- (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
- error("Cannot find revision on branch %s with a date before %s, author %s, and state %s.",
- length==0?partialno(t,branchnum,1):revno,date==nil?"<now>":date,
- author==nil?"<any>":author, state==nil?"<any>":state);
- return nil;
- } else {
- /*puts(next->num);*/
- *store++ = next;
- }
- *store = nil;
- return next;
- }
-
- /* length >=2 */
- /* find revision; may go low if length==2*/
- while ((next!=nil) &&
- ((result =cmpnumfld(revno,next->num,2)) <0) &&
- (cmpnumfld(revno,next->num,1)==0) ) {
- /*puts(next->num);*/
- *store++ = next;
- next = next->next;
- }
-
- if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) {
- error("Revision number %s too low.",partialno(t,revno,2));
- return nil;
- }
- if ((length>2) && (result!=0)) {
- error("Revision %s not present.",partialno(t,revno,2));
- return nil;
- }
-
- /* print last one */
- /*puts(next->num);*/
- *store++ = next;
-
- if (length>2)
- return genbranch(next,revno,length,date,author,state,store);
- else { /* length == 2*/
- if ((date!=nil) && (cmpnum(date,next->date)<0)){
- error("Revision %s has date %s.",next->num, next->date);
- return nil;
- }
- if ((author!=nil)&&(strcmp(author,next->author)!=0)) {
- error("Revision %s has author %s.",next->num,next->author);
- return nil;
- }
- if ((state!=nil)&&(strcmp(state,next->state)!=0)) {
- error("Revision %s has state %s.",next->num,
- next->state==nil?"<empty>":next->state);
- return nil;
- }
- *store=nil;
- return next;
- }
- }
-
-
-
-
- struct hshentry * genbranch(bpoint, revno, length,date,author,state,store)
- struct hshentry * bpoint;
- char * revno; int length;
- char * date, * author, * state;
- struct hshentry ** store;
- /* Function: given a branchpoint, a revision number, date, author, and state,
- * genbranch finds the deltas necessary to reconstruct the given revision
- * from the branch point on.
- * Pointers to the found deltas are stored in an array beginning with store.
- * revno must be on a side branch.
- * return nil on error
- */
- {
- int field;
- register struct hshentry * next, * trail;
- register struct branchhead * bhead;
- int result;
- char t[revlength];
-
- bhead = bpoint->branches;
-
- for (field=3; field<=length; field=field+2) {
-
- if (bhead==nil) {error("No side branches present for %s.",partialno(t,revno,field-1));return nil;}
-
- /*find branch head*/
- /*branches are arranged in increasing order*/
- while ((bhead!=nil) &&
- ((result=cmpnumfld(revno,bhead->hsh->num,field))>0)) {
- bhead = bhead->nextbranch;
- }
-
- if (bhead==nil) {error("Branch number %s too high.",partialno(t,revno,field));return nil;}
- if (result<0) {error("Branch number %s not present.",partialno(t,revno,field));return nil;}
-
- next = bhead->hsh;
- if (length==field) {
- /* pick latest one on that branch */
- trail=nil;
- do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) &&
- (author==nil?1:(strcmp(author,next->author)==0)) &&
- (state ==nil?1:(strcmp(state, next->state) ==0))
- ) trail = next;
- next=next->next;
- } while (next!=nil);
-
- if (trail==nil) {
- error("Cannot find revision on branch %s with a date before %s, author %s, and state %s.",
- revno, date==nil?"<now>":date,
- author==nil?"<any>":author, state==nil?"<any>":state);
- return nil;
- } else { /* print up to last one suitable */
- next = bhead->hsh;
- while (next!=trail) {
- /*puts(next->num);*/
- *store++ = next;
- next=next->next;
- }
- /*puts(next->num);*/
- *store++ = next;
- }
- *store = nil;
- return next;
- }
-
- /* length > field */
- /* find revision */
- /* check low */
- if (cmpnumfld(revno,next->num,field+1)<0) {
- error("Revision number %s too low.",partialno(t,revno,field+1));
- return(nil);
- }
- do { /*puts(next->num);*/
- *store++ = next;
- trail = next;
- next = next->next;
- } while ((next!=nil) &&
- (cmpnumfld(revno,next->num,field+1) >=0));
-
- if ((length>field+1) && /*need exact hit */
- (cmpnumfld(revno,trail->num,field+1) !=0)){
- error("Revision %s not present.",partialno(t,revno,field+1));
- return(nil);
- }
- if (length == field+1) {
- if ((date!=nil) && (cmpnum(date,trail->date)<0)){
- error("Revision %s has date %s.",trail->num, trail->date);
- return nil;
- }
- if ((author!=nil)&&(strcmp(author,trail->author)!=0)) {
- error("Revision %s has author %s.",trail->num,trail->author);
- return nil;
- }
- if ((state!=nil)&&(strcmp(state,trail->state)!=0)) {
- error("Revision %s has state %s.",trail->num,
- trail->state==nil?"<empty>":trail->state);
- return nil;
- }
- }
- bhead = trail->branches;
-
- }
- * store = nil;
- return trail;
- }
-
-
- char * lookupsym(id)
- char * id;
- /* Function: looks up id in the list of symbolic names starting
- * with pointer SYMBOLS, and returns a pointer to the corresponding
- * revision number. Returns nil if not present.
- */
- {
- register struct assoc * next;
- next = Symbols;
- while (next!=nil) {
- if (strcmp(id, next->symbol)==0)
- return(next->delta->num);
- else next=next->nextassoc;
- }
- return nil;
- }
-
- int expandsym(source, target)
- char * source, * target;
- /* Function: Source points to a revision number. Expandsym copies
- * the number to target, but replaces all symbolic fields in the
- * source number with their numeric values.
- * A trailing '.' is omitted; leading zeroes are compressed.
- * returns false on error;
- */
- { register char * sp, * tp, *bp;
- char symbuf[30];
- register enum tokens d;
-
- sp = source; tp=target;
- if (sp == nil) { /*accept nil pointer as a legal value*/
- *tp='\0';
- return true;
- }
-
- while (*sp != '\0') {
- if (ctab[*sp] == DIGIT) {
- if (*sp=='0') {
- /* skip leading zeroes */
- sp++;
- while(*sp == '0') sp++;
- if (*sp=='\0' || *sp=='.') *tp++ = '0'; /*single zero*/
- }
- while(ctab[*sp] == DIGIT) *tp++ = *sp++;
- if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) {
- *tp='\0'; return true;
- }
- if (*sp == '.') *tp++ = *sp++;
- else {
- error("Improper revision number: %s",source);
- *tp = '\0';
- return false;
- }
- } elsif (ctab[*sp] == LETTER) {
- bp = symbuf;
- do { *bp++ = *sp++;
- } while(((d=ctab[*sp])==LETTER) || (d==DIGIT) ||
- (d==IDCHAR));
- *bp= '\0';
- bp=lookupsym(symbuf);
- if (bp==nil) {
- error("Symbolic number %s is undefined.",symbuf);
- *tp='\0';
- return false;
- } else { /* copy number */
- while (*tp++ = *bp++); /* copies the trailing \0*/
- }
- if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0')))
- return true;
- if (*sp == '.') {
- *(tp-1) = *sp++;
- } else {
- error("Improper revision number: %s",source);
- return false;
- }
- }else {
- error("Improper revision number: %s", source);
- *tp = '\0';
- return false;
- }
- }
- *tp = '\0';
- return true;
- }
-
-
-
- #ifdef REVTEST
-
- main(argc,argv)
- int argc; char * argv[];
- {
- char symrevno[revlength]; /* used for input of revision numbers */
- char numricrevno[revlength];
- char author[20];
- char state[20];
- char date[20];
- struct hshentry * gendeltas[hshsize/2];
- struct hshentry * target;
- int i;
-
- cmdid = "revtest";
- if (argc<2) {
- VOID fputs("No input file\n",stderr);
- exit(-1);
- }
- if ((finptr=fopen(argv[1], "r")) == NULL) {
- faterror("Can't open input file %s\n",argv[1]);
- }
- Lexinit();
- getadmin();
-
- gettree();
-
- getdesc(false);
-
- do {
- /* all output goes to stderr, to have diagnostics and */
- /* errors in sequence. */
- VOID fprintf(stderr,"\nEnter revision number or <return> or '.': ");
- if(gets(symrevno)==NULL) break;
- if (*symrevno == '.') break;
- VOID fprintf(stderr,"%s;\n",symrevno);
- expandsym(symrevno,numricrevno);
- VOID fprintf(stderr,"expanded number: %s; ",numricrevno);
- VOID fprintf(stderr,"Date: ");
- gets(date); VOID fprintf(stderr,"%s; ",date);
- VOID fprintf(stderr,"Author: ");
- gets(author);VOID fprintf(stderr,"%s; ",author);
- VOID fprintf(stderr,"State: ");
- gets(state); VOID fprintf(stderr, "%s;\n", state);
- target=genrevs(numricrevno,*date=='\0'?(char *)nil:date, *author=='\0'?(char *)nil:author,
- *state=='\0'?(char *)nil:state,gendeltas);
- if (target!=nil) {
- i=0;
- while (gendeltas[i]!=nil) {
- VOID fprintf(stderr,"%s\n",gendeltas[i++]->num);
- }
- }
- } while (true);
- VOID fprintf(stderr,"done\n");
-
- }
-
- cleanup(){}
- /*dummy*/
-
- #endif /* REVTEST */
-