BSD 4_3_Tahoe development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Thu, 18 Feb 1988 08:16:15 +0000 (00:16 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Thu, 18 Feb 1988 08:16:15 +0000 (00:16 -0800)
Work on file usr/src/new/rcs/src/rcsrev.c

Synthesized-from: CSRG/cd2/4.3tahoe

usr/src/new/rcs/src/rcsrev.c [new file with mode: 0644]

diff --git a/usr/src/new/rcs/src/rcsrev.c b/usr/src/new/rcs/src/rcsrev.c
new file mode 100644 (file)
index 0000000..2ba3206
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ *                     RCS revision number handling
+ */
+#ifndef lint
+static char rcsid[]= "$Id: rcsrev.c,v 4.3 87/10/18 10:38:42 narten Exp $ Purdue CS";
+#endif
+/*********************************************************************************
+ *********************************************************************************
+ *
+ * Copyright (C) 1982 by Walter F. Tichy
+ *                       Purdue University
+ *                       Computer Science Department
+ *                       West Lafayette, IN 47907
+ *
+ * All rights reserved. No part of this software may be sold or distributed
+ * in any form or by any means without the prior written permission of the
+ * author.
+ * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
+ */
+
+
+/* $Log:       rcsrev.c,v $
+ * 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();
+extern int delta(), deltatext();
+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;
+       fprintf(stderr, "RCS Internal error, routine: compartial\n");
+       return(0);
+/*NOTREACHED*/
+}
+
+
+
+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