1*fa28c6faSchristos /* $NetBSD: rcsrev.c,v 1.2 2016/01/14 04:22:39 christos Exp $ */
27bdc2678Schristos
37bdc2678Schristos /* Handle RCS revision numbers. */
47bdc2678Schristos
57bdc2678Schristos /* Copyright 1982, 1988, 1989 Walter Tichy
67bdc2678Schristos Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
77bdc2678Schristos Distributed under license by the Free Software Foundation, Inc.
87bdc2678Schristos
97bdc2678Schristos This file is part of RCS.
107bdc2678Schristos
117bdc2678Schristos RCS is free software; you can redistribute it and/or modify
127bdc2678Schristos it under the terms of the GNU General Public License as published by
137bdc2678Schristos the Free Software Foundation; either version 2, or (at your option)
147bdc2678Schristos any later version.
157bdc2678Schristos
167bdc2678Schristos RCS is distributed in the hope that it will be useful,
177bdc2678Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
187bdc2678Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
197bdc2678Schristos GNU General Public License for more details.
207bdc2678Schristos
217bdc2678Schristos You should have received a copy of the GNU General Public License
227bdc2678Schristos along with RCS; see the file COPYING.
237bdc2678Schristos If not, write to the Free Software Foundation,
247bdc2678Schristos 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
257bdc2678Schristos
267bdc2678Schristos Report problems and direct all questions to:
277bdc2678Schristos
287bdc2678Schristos rcs-bugs@cs.purdue.edu
297bdc2678Schristos
307bdc2678Schristos */
317bdc2678Schristos
327bdc2678Schristos /*
337bdc2678Schristos * Log: rcsrev.c,v
347bdc2678Schristos * Revision 5.10 1995/06/16 06:19:24 eggert
357bdc2678Schristos * Update FSF address.
367bdc2678Schristos *
377bdc2678Schristos * Revision 5.9 1995/06/01 16:23:43 eggert
387bdc2678Schristos * (cmpdate, normalizeyear): New functions work around MKS RCS incompatibility.
397bdc2678Schristos * (cmpnum, compartial): s[d] -> *(s+d) to work around Cray compiler bug.
407bdc2678Schristos * (genrevs, genbranch): cmpnum -> cmpdate
417bdc2678Schristos *
427bdc2678Schristos * Revision 5.8 1994/03/17 14:05:48 eggert
437bdc2678Schristos * Remove lint.
447bdc2678Schristos *
457bdc2678Schristos * Revision 5.7 1993/11/09 17:40:15 eggert
467bdc2678Schristos * Fix format string typos.
477bdc2678Schristos *
487bdc2678Schristos * Revision 5.6 1993/11/03 17:42:27 eggert
497bdc2678Schristos * Revision number `.N' now stands for `D.N', where D is the default branch.
507bdc2678Schristos * Add -z. Improve quality of diagnostics. Add `namedrev' for Name support.
517bdc2678Schristos *
527bdc2678Schristos * Revision 5.5 1992/07/28 16:12:44 eggert
537bdc2678Schristos * Identifiers may now start with a digit. Avoid `unsigned'.
547bdc2678Schristos *
557bdc2678Schristos * Revision 5.4 1992/01/06 02:42:34 eggert
567bdc2678Schristos * while (E) ; -> while (E) continue;
577bdc2678Schristos *
587bdc2678Schristos * Revision 5.3 1991/08/19 03:13:55 eggert
597bdc2678Schristos * Add `-r$', `-rB.'. Remove botches like `<now>' from messages. Tune.
607bdc2678Schristos *
617bdc2678Schristos * Revision 5.2 1991/04/21 11:58:28 eggert
627bdc2678Schristos * Add tiprev().
637bdc2678Schristos *
647bdc2678Schristos * Revision 5.1 1991/02/25 07:12:43 eggert
657bdc2678Schristos * Avoid overflow when comparing revision numbers.
667bdc2678Schristos *
677bdc2678Schristos * Revision 5.0 1990/08/22 08:13:43 eggert
687bdc2678Schristos * Remove compile-time limits; use malloc instead.
697bdc2678Schristos * Ansify and Posixate. Tune.
707bdc2678Schristos * Remove possibility of an internal error. Remove lint.
717bdc2678Schristos *
727bdc2678Schristos * Revision 4.5 89/05/01 15:13:22 narten
737bdc2678Schristos * changed copyright header to reflect current distribution rules
747bdc2678Schristos *
757bdc2678Schristos * Revision 4.4 87/12/18 11:45:22 narten
767bdc2678Schristos * more lint cleanups. Also, the NOTREACHED comment is no longer necessary,
777bdc2678Schristos * since there's now a return value there with a value. (Guy Harris)
787bdc2678Schristos *
797bdc2678Schristos * Revision 4.3 87/10/18 10:38:42 narten
807bdc2678Schristos * Updating version numbers. Changes relative to version 1.1 actually
817bdc2678Schristos * relative to 4.1
827bdc2678Schristos *
837bdc2678Schristos * Revision 1.3 87/09/24 14:00:37 narten
847bdc2678Schristos * Sources now pass through lint (if you ignore printf/sprintf/fprintf
857bdc2678Schristos * warnings)
867bdc2678Schristos *
877bdc2678Schristos * Revision 1.2 87/03/27 14:22:37 jenkins
887bdc2678Schristos * Port to suns
897bdc2678Schristos *
907bdc2678Schristos * Revision 4.1 83/03/25 21:10:45 wft
917bdc2678Schristos * Only changed Header to Id.
927bdc2678Schristos *
937bdc2678Schristos * Revision 3.4 82/12/04 13:24:08 wft
947bdc2678Schristos * Replaced getdelta() with gettree().
957bdc2678Schristos *
967bdc2678Schristos * Revision 3.3 82/11/28 21:33:15 wft
977bdc2678Schristos * fixed compartial() and compnum() for nil-parameters; fixed nils
987bdc2678Schristos * in error messages. Testprogram output shortenend.
997bdc2678Schristos *
1007bdc2678Schristos * Revision 3.2 82/10/18 21:19:47 wft
1017bdc2678Schristos * renamed compnum->cmpnum, compnumfld->cmpnumfld,
1027bdc2678Schristos * numericrevno->numricrevno.
1037bdc2678Schristos *
1047bdc2678Schristos * Revision 3.1 82/10/11 19:46:09 wft
1057bdc2678Schristos * changed expandsym() to check for source==nil; returns zero length string
1067bdc2678Schristos * in that case.
1077bdc2678Schristos */
1087bdc2678Schristos
1097bdc2678Schristos #include "rcsbase.h"
1107bdc2678Schristos
1117bdc2678Schristos libId(revId, "Id: rcsrev.c,v 5.10 1995/06/16 06:19:24 eggert Exp ")
1127bdc2678Schristos
1137bdc2678Schristos static char const *branchtip P((char const*));
1147bdc2678Schristos static char const *lookupsym P((char const*));
1157bdc2678Schristos static char const *normalizeyear P((char const*,char[5]));
1167bdc2678Schristos static struct hshentry *genbranch P((struct hshentry const*,char const*,int,char const*,char const*,char const*,struct hshentries**));
1177bdc2678Schristos static void absent P((char const*,int));
1187bdc2678Schristos static void cantfindbranch P((char const*,char const[datesize],char const*,char const*));
1197bdc2678Schristos static void store1 P((struct hshentries***,struct hshentry*));
1207bdc2678Schristos
1217bdc2678Schristos
1227bdc2678Schristos
1237bdc2678Schristos int
countnumflds(s)1247bdc2678Schristos countnumflds(s)
1257bdc2678Schristos char const *s;
1267bdc2678Schristos /* Given a pointer s to a dotted number (date or revision number),
1277bdc2678Schristos * countnumflds returns the number of digitfields in s.
1287bdc2678Schristos */
1297bdc2678Schristos {
1307bdc2678Schristos register char const *sp;
1317bdc2678Schristos register int count;
1327bdc2678Schristos if (!(sp=s) || !*sp)
1337bdc2678Schristos return 0;
1347bdc2678Schristos count = 1;
1357bdc2678Schristos do {
1367bdc2678Schristos if (*sp++ == '.') count++;
1377bdc2678Schristos } while (*sp);
1387bdc2678Schristos return(count);
1397bdc2678Schristos }
1407bdc2678Schristos
1417bdc2678Schristos void
getbranchno(revno,branchno)1427bdc2678Schristos getbranchno(revno,branchno)
1437bdc2678Schristos char const *revno;
1447bdc2678Schristos struct buf *branchno;
1457bdc2678Schristos /* Given a revision number revno, getbranchno copies the number of the branch
1467bdc2678Schristos * on which revno is into branchno. If revno itself is a branch number,
1477bdc2678Schristos * it is copied unchanged.
1487bdc2678Schristos */
1497bdc2678Schristos {
1507bdc2678Schristos register int numflds;
1517bdc2678Schristos register char *tp;
1527bdc2678Schristos
1537bdc2678Schristos bufscpy(branchno, revno);
1547bdc2678Schristos numflds=countnumflds(revno);
1557bdc2678Schristos if (!(numflds & 1)) {
1567bdc2678Schristos tp = branchno->string;
1577bdc2678Schristos while (--numflds)
1587bdc2678Schristos while (*tp++ != '.')
1597bdc2678Schristos continue;
1607bdc2678Schristos *(tp-1)='\0';
1617bdc2678Schristos }
1627bdc2678Schristos }
1637bdc2678Schristos
1647bdc2678Schristos
1657bdc2678Schristos
cmpnum(num1,num2)1667bdc2678Schristos int cmpnum(num1, num2)
1677bdc2678Schristos char const *num1, *num2;
1687bdc2678Schristos /* compares the two dotted numbers num1 and num2 lexicographically
1697bdc2678Schristos * by field. Individual fields are compared numerically.
1707bdc2678Schristos * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
1717bdc2678Schristos * omitted fields are assumed to be higher than the existing ones.
1727bdc2678Schristos */
1737bdc2678Schristos {
1747bdc2678Schristos register char const *s1, *s2;
1757bdc2678Schristos register size_t d1, d2;
1767bdc2678Schristos register int r;
1777bdc2678Schristos
1787bdc2678Schristos s1 = num1 ? num1 : "";
1797bdc2678Schristos s2 = num2 ? num2 : "";
1807bdc2678Schristos
1817bdc2678Schristos for (;;) {
1827bdc2678Schristos /* Give precedence to shorter one. */
1837bdc2678Schristos if (!*s1)
1847bdc2678Schristos return (unsigned char)*s2;
1857bdc2678Schristos if (!*s2)
1867bdc2678Schristos return -1;
1877bdc2678Schristos
1887bdc2678Schristos /* Strip leading zeros, then find number of digits. */
1897bdc2678Schristos while (*s1=='0') ++s1;
1907bdc2678Schristos while (*s2=='0') ++s2;
1917bdc2678Schristos for (d1=0; isdigit(*(s1+d1)); d1++) continue;
1927bdc2678Schristos for (d2=0; isdigit(*(s2+d2)); d2++) continue;
1937bdc2678Schristos
1947bdc2678Schristos /* Do not convert to integer; it might overflow! */
1957bdc2678Schristos if (d1 != d2)
1967bdc2678Schristos return d1<d2 ? -1 : 1;
1977bdc2678Schristos if ((r = memcmp(s1, s2, d1)))
1987bdc2678Schristos return r;
1997bdc2678Schristos s1 += d1;
2007bdc2678Schristos s2 += d1;
2017bdc2678Schristos
2027bdc2678Schristos /* skip '.' */
2037bdc2678Schristos if (*s1) s1++;
2047bdc2678Schristos if (*s2) s2++;
2057bdc2678Schristos }
2067bdc2678Schristos }
2077bdc2678Schristos
2087bdc2678Schristos
2097bdc2678Schristos
cmpnumfld(num1,num2,fld)2107bdc2678Schristos int cmpnumfld(num1, num2, fld)
2117bdc2678Schristos char const *num1, *num2;
2127bdc2678Schristos int fld;
2137bdc2678Schristos /* Compare the two dotted numbers at field fld.
2147bdc2678Schristos * num1 and num2 must have at least fld fields.
2157bdc2678Schristos * fld must be positive.
2167bdc2678Schristos */
2177bdc2678Schristos {
2187bdc2678Schristos register char const *s1, *s2;
2197bdc2678Schristos register size_t d1, d2;
2207bdc2678Schristos
2217bdc2678Schristos s1 = num1;
2227bdc2678Schristos s2 = num2;
2237bdc2678Schristos /* skip fld-1 fields */
2247bdc2678Schristos while (--fld) {
2257bdc2678Schristos while (*s1++ != '.')
2267bdc2678Schristos continue;
2277bdc2678Schristos while (*s2++ != '.')
2287bdc2678Schristos continue;
2297bdc2678Schristos }
2307bdc2678Schristos /* Now s1 and s2 point to the beginning of the respective fields */
2317bdc2678Schristos while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
2327bdc2678Schristos while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
2337bdc2678Schristos
2347bdc2678Schristos return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1;
2357bdc2678Schristos }
2367bdc2678Schristos
2377bdc2678Schristos
2387bdc2678Schristos int
cmpdate(d1,d2)2397bdc2678Schristos cmpdate(d1, d2)
2407bdc2678Schristos char const *d1, *d2;
2417bdc2678Schristos /*
2427bdc2678Schristos * Compare the two dates. This is just like cmpnum,
2437bdc2678Schristos * except that for compatibility with old versions of RCS,
2447bdc2678Schristos * 1900 is added to dates with two-digit years.
2457bdc2678Schristos */
2467bdc2678Schristos {
2477bdc2678Schristos char year1[5], year2[5];
2487bdc2678Schristos int r = cmpnumfld(normalizeyear(d1,year1), normalizeyear(d2,year2), 1);
2497bdc2678Schristos
2507bdc2678Schristos if (r)
2517bdc2678Schristos return r;
2527bdc2678Schristos else {
2537bdc2678Schristos while (isdigit(*d1)) d1++; d1 += *d1=='.';
2547bdc2678Schristos while (isdigit(*d2)) d2++; d2 += *d2=='.';
2557bdc2678Schristos return cmpnum(d1, d2);
2567bdc2678Schristos }
2577bdc2678Schristos }
2587bdc2678Schristos
2597bdc2678Schristos static char const *
normalizeyear(date,year)2607bdc2678Schristos normalizeyear(date, year)
2617bdc2678Schristos char const *date;
2627bdc2678Schristos char year[5];
2637bdc2678Schristos {
2647bdc2678Schristos if (isdigit(date[0]) && isdigit(date[1]) && !isdigit(date[2])) {
2657bdc2678Schristos year[0] = '1';
2667bdc2678Schristos year[1] = '9';
2677bdc2678Schristos year[2] = date[0];
2687bdc2678Schristos year[3] = date[1];
2697bdc2678Schristos year[4] = 0;
2707bdc2678Schristos return year;
2717bdc2678Schristos } else
2727bdc2678Schristos return date;
2737bdc2678Schristos }
2747bdc2678Schristos
2757bdc2678Schristos
2767bdc2678Schristos static void
cantfindbranch(revno,date,author,state)2777bdc2678Schristos cantfindbranch(revno, date, author, state)
2787bdc2678Schristos char const *revno, date[datesize], *author, *state;
2797bdc2678Schristos {
2807bdc2678Schristos char datebuf[datesize + zonelenmax];
2817bdc2678Schristos
2827bdc2678Schristos rcserror("No revision on branch %s has%s%s%s%s%s%s.",
2837bdc2678Schristos revno,
2847bdc2678Schristos date ? " a date before " : "",
2857bdc2678Schristos date ? date2str(date,datebuf) : "",
2867bdc2678Schristos author ? " and author "+(date?0:4) : "",
2877bdc2678Schristos author ? author : "",
2887bdc2678Schristos state ? " and state "+(date||author?0:4) : "",
2897bdc2678Schristos state ? state : ""
2907bdc2678Schristos );
2917bdc2678Schristos }
2927bdc2678Schristos
2937bdc2678Schristos static void
absent(revno,field)2947bdc2678Schristos absent(revno, field)
2957bdc2678Schristos char const *revno;
2967bdc2678Schristos int field;
2977bdc2678Schristos {
2987bdc2678Schristos struct buf t;
2997bdc2678Schristos bufautobegin(&t);
3007bdc2678Schristos rcserror("%s %s absent", field&1?"revision":"branch",
3017bdc2678Schristos partialno(&t,revno,field)
3027bdc2678Schristos );
3037bdc2678Schristos bufautoend(&t);
3047bdc2678Schristos }
3057bdc2678Schristos
3067bdc2678Schristos
3077bdc2678Schristos int
compartial(num1,num2,length)3087bdc2678Schristos compartial(num1, num2, length)
3097bdc2678Schristos char const *num1, *num2;
3107bdc2678Schristos int length;
3117bdc2678Schristos
3127bdc2678Schristos /* compare the first "length" fields of two dot numbers;
3137bdc2678Schristos the omitted field is considered to be larger than any number */
3147bdc2678Schristos /* restriction: at least one number has length or more fields */
3157bdc2678Schristos
3167bdc2678Schristos {
3177bdc2678Schristos register char const *s1, *s2;
3187bdc2678Schristos register size_t d1, d2;
3197bdc2678Schristos register int r;
3207bdc2678Schristos
3217bdc2678Schristos s1 = num1; s2 = num2;
3227bdc2678Schristos if (!s1) return 1;
3237bdc2678Schristos if (!s2) return -1;
3247bdc2678Schristos
3257bdc2678Schristos for (;;) {
3267bdc2678Schristos if (!*s1) return 1;
3277bdc2678Schristos if (!*s2) return -1;
3287bdc2678Schristos
3297bdc2678Schristos while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
3307bdc2678Schristos while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
3317bdc2678Schristos
3327bdc2678Schristos if (d1 != d2)
3337bdc2678Schristos return d1<d2 ? -1 : 1;
3347bdc2678Schristos if ((r = memcmp(s1, s2, d1)))
3357bdc2678Schristos return r;
3367bdc2678Schristos if (!--length)
3377bdc2678Schristos return 0;
3387bdc2678Schristos
3397bdc2678Schristos s1 += d1;
3407bdc2678Schristos s2 += d1;
3417bdc2678Schristos
3427bdc2678Schristos if (*s1 == '.') s1++;
3437bdc2678Schristos if (*s2 == '.') s2++;
3447bdc2678Schristos }
3457bdc2678Schristos }
3467bdc2678Schristos
3477bdc2678Schristos
partialno(rev1,rev2,length)3487bdc2678Schristos char * partialno(rev1,rev2,length)
3497bdc2678Schristos struct buf *rev1;
3507bdc2678Schristos char const *rev2;
3517bdc2678Schristos register int length;
3527bdc2678Schristos /* Function: Copies length fields of revision number rev2 into rev1.
3537bdc2678Schristos * Return rev1's string.
3547bdc2678Schristos */
3557bdc2678Schristos {
3567bdc2678Schristos register char *r1;
3577bdc2678Schristos
3587bdc2678Schristos bufscpy(rev1, rev2);
3597bdc2678Schristos r1 = rev1->string;
3607bdc2678Schristos while (length) {
3617bdc2678Schristos while (*r1!='.' && *r1)
3627bdc2678Schristos ++r1;
3637bdc2678Schristos ++r1;
3647bdc2678Schristos length--;
3657bdc2678Schristos }
3667bdc2678Schristos /* eliminate last '.'*/
3677bdc2678Schristos *(r1-1)='\0';
3687bdc2678Schristos return rev1->string;
3697bdc2678Schristos }
3707bdc2678Schristos
3717bdc2678Schristos
3727bdc2678Schristos
3737bdc2678Schristos
3747bdc2678Schristos static void
store1(store,next)3757bdc2678Schristos store1(store, next)
3767bdc2678Schristos struct hshentries ***store;
3777bdc2678Schristos struct hshentry *next;
3787bdc2678Schristos /*
3797bdc2678Schristos * Allocate a new list node that addresses NEXT.
3807bdc2678Schristos * Append it to the list that **STORE is the end pointer of.
3817bdc2678Schristos */
3827bdc2678Schristos {
3837bdc2678Schristos register struct hshentries *p;
3847bdc2678Schristos
3857bdc2678Schristos p = ftalloc(struct hshentries);
3867bdc2678Schristos p->first = next;
3877bdc2678Schristos **store = p;
3887bdc2678Schristos *store = &p->rest;
3897bdc2678Schristos }
3907bdc2678Schristos
genrevs(revno,date,author,state,store)3917bdc2678Schristos struct hshentry * genrevs(revno,date,author,state,store)
3927bdc2678Schristos char const *revno, *date, *author, *state;
3937bdc2678Schristos struct hshentries **store;
3947bdc2678Schristos /* Function: finds the deltas needed for reconstructing the
3957bdc2678Schristos * revision given by revno, date, author, and state, and stores pointers
3967bdc2678Schristos * to these deltas into a list whose starting address is given by store.
3977bdc2678Schristos * The last delta (target delta) is returned.
3987bdc2678Schristos * If the proper delta could not be found, 0 is returned.
3997bdc2678Schristos */
4007bdc2678Schristos {
4017bdc2678Schristos int length;
4027bdc2678Schristos register struct hshentry * next;
4037bdc2678Schristos int result;
4047bdc2678Schristos char const *branchnum;
4057bdc2678Schristos struct buf t;
4067bdc2678Schristos char datebuf[datesize + zonelenmax];
4077bdc2678Schristos
4087bdc2678Schristos bufautobegin(&t);
4097bdc2678Schristos
4107bdc2678Schristos if (!(next = Head)) {
4117bdc2678Schristos rcserror("RCS file empty");
4127bdc2678Schristos goto norev;
4137bdc2678Schristos }
4147bdc2678Schristos
4157bdc2678Schristos length = countnumflds(revno);
4167bdc2678Schristos
4177bdc2678Schristos if (length >= 1) {
4187bdc2678Schristos /* at least one field; find branch exactly */
4197bdc2678Schristos while ((result=cmpnumfld(revno,next->num,1)) < 0) {
4207bdc2678Schristos store1(&store, next);
4217bdc2678Schristos next = next->next;
4227bdc2678Schristos if (!next) {
4237bdc2678Schristos rcserror("branch number %s too low", partialno(&t,revno,1));
4247bdc2678Schristos goto norev;
4257bdc2678Schristos }
4267bdc2678Schristos }
4277bdc2678Schristos
4287bdc2678Schristos if (result>0) {
4297bdc2678Schristos absent(revno, 1);
4307bdc2678Schristos goto norev;
4317bdc2678Schristos }
4327bdc2678Schristos }
4337bdc2678Schristos if (length<=1){
4347bdc2678Schristos /* pick latest one on given branch */
4357bdc2678Schristos branchnum = next->num; /* works even for empty revno*/
4367bdc2678Schristos while (next &&
4377bdc2678Schristos cmpnumfld(branchnum,next->num,1) == 0 &&
4387bdc2678Schristos (
4397bdc2678Schristos (date && cmpdate(date,next->date) < 0) ||
4407bdc2678Schristos (author && strcmp(author,next->author) != 0) ||
4417bdc2678Schristos (state && strcmp(state,next->state) != 0)
4427bdc2678Schristos )
4437bdc2678Schristos )
4447bdc2678Schristos {
4457bdc2678Schristos store1(&store, next);
4467bdc2678Schristos next=next->next;
4477bdc2678Schristos }
4487bdc2678Schristos if (!next ||
4497bdc2678Schristos (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
4507bdc2678Schristos cantfindbranch(
4517bdc2678Schristos length ? revno : partialno(&t,branchnum,1),
4527bdc2678Schristos date, author, state
4537bdc2678Schristos );
4547bdc2678Schristos goto norev;
4557bdc2678Schristos } else {
4567bdc2678Schristos store1(&store, next);
4577bdc2678Schristos }
4587bdc2678Schristos *store = 0;
4597bdc2678Schristos return next;
4607bdc2678Schristos }
4617bdc2678Schristos
4627bdc2678Schristos /* length >=2 */
4637bdc2678Schristos /* find revision; may go low if length==2*/
4647bdc2678Schristos while ((result=cmpnumfld(revno,next->num,2)) < 0 &&
4657bdc2678Schristos (cmpnumfld(revno,next->num,1)==0) ) {
4667bdc2678Schristos store1(&store, next);
4677bdc2678Schristos next = next->next;
4687bdc2678Schristos if (!next)
4697bdc2678Schristos break;
4707bdc2678Schristos }
4717bdc2678Schristos
4727bdc2678Schristos if (!next || cmpnumfld(revno,next->num,1) != 0) {
4737bdc2678Schristos rcserror("revision number %s too low", partialno(&t,revno,2));
4747bdc2678Schristos goto norev;
4757bdc2678Schristos }
4767bdc2678Schristos if ((length>2) && (result!=0)) {
4777bdc2678Schristos absent(revno, 2);
4787bdc2678Schristos goto norev;
4797bdc2678Schristos }
4807bdc2678Schristos
4817bdc2678Schristos /* print last one */
4827bdc2678Schristos store1(&store, next);
4837bdc2678Schristos
4847bdc2678Schristos if (length>2)
4857bdc2678Schristos return genbranch(next,revno,length,date,author,state,store);
4867bdc2678Schristos else { /* length == 2*/
4877bdc2678Schristos if (date && cmpdate(date,next->date)<0) {
4887bdc2678Schristos rcserror("Revision %s has date %s.",
4897bdc2678Schristos next->num,
4907bdc2678Schristos date2str(next->date, datebuf)
4917bdc2678Schristos );
4927bdc2678Schristos return 0;
4937bdc2678Schristos }
4947bdc2678Schristos if (author && strcmp(author,next->author)!=0) {
4957bdc2678Schristos rcserror("Revision %s has author %s.",
4967bdc2678Schristos next->num, next->author
4977bdc2678Schristos );
4987bdc2678Schristos return 0;
4997bdc2678Schristos }
5007bdc2678Schristos if (state && strcmp(state,next->state)!=0) {
5017bdc2678Schristos rcserror("Revision %s has state %s.",
5027bdc2678Schristos next->num,
5037bdc2678Schristos next->state ? next->state : "<empty>"
5047bdc2678Schristos );
5057bdc2678Schristos return 0;
5067bdc2678Schristos }
5077bdc2678Schristos *store = 0;
5087bdc2678Schristos return next;
5097bdc2678Schristos }
5107bdc2678Schristos
5117bdc2678Schristos norev:
5127bdc2678Schristos bufautoend(&t);
5137bdc2678Schristos return 0;
5147bdc2678Schristos }
5157bdc2678Schristos
5167bdc2678Schristos
5177bdc2678Schristos
5187bdc2678Schristos
5197bdc2678Schristos static struct hshentry *
genbranch(bpoint,revno,length,date,author,state,store)5207bdc2678Schristos genbranch(bpoint, revno, length, date, author, state, store)
5217bdc2678Schristos struct hshentry const *bpoint;
5227bdc2678Schristos char const *revno;
5237bdc2678Schristos int length;
5247bdc2678Schristos char const *date, *author, *state;
5257bdc2678Schristos struct hshentries **store;
5267bdc2678Schristos /* Function: given a branchpoint, a revision number, date, author, and state,
5277bdc2678Schristos * genbranch finds the deltas necessary to reconstruct the given revision
5287bdc2678Schristos * from the branch point on.
5297bdc2678Schristos * Pointers to the found deltas are stored in a list beginning with store.
5307bdc2678Schristos * revno must be on a side branch.
5317bdc2678Schristos * Return 0 on error.
5327bdc2678Schristos */
5337bdc2678Schristos {
5347bdc2678Schristos int field;
5357bdc2678Schristos register struct hshentry * next, * trail;
5367bdc2678Schristos register struct branchhead const *bhead;
5377bdc2678Schristos int result;
5387bdc2678Schristos struct buf t;
5397bdc2678Schristos char datebuf[datesize + zonelenmax];
5407bdc2678Schristos
5417bdc2678Schristos field = 3;
5427bdc2678Schristos bhead = bpoint->branches;
5437bdc2678Schristos
5447bdc2678Schristos do {
5457bdc2678Schristos if (!bhead) {
5467bdc2678Schristos bufautobegin(&t);
5477bdc2678Schristos rcserror("no side branches present for %s",
5487bdc2678Schristos partialno(&t,revno,field-1)
5497bdc2678Schristos );
5507bdc2678Schristos bufautoend(&t);
5517bdc2678Schristos return 0;
5527bdc2678Schristos }
5537bdc2678Schristos
5547bdc2678Schristos /*find branch head*/
5557bdc2678Schristos /*branches are arranged in increasing order*/
5567bdc2678Schristos while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) {
5577bdc2678Schristos bhead = bhead->nextbranch;
5587bdc2678Schristos if (!bhead) {
5597bdc2678Schristos bufautobegin(&t);
5607bdc2678Schristos rcserror("branch number %s too high",
5617bdc2678Schristos partialno(&t,revno,field)
5627bdc2678Schristos );
5637bdc2678Schristos bufautoend(&t);
5647bdc2678Schristos return 0;
5657bdc2678Schristos }
5667bdc2678Schristos }
5677bdc2678Schristos
5687bdc2678Schristos if (result<0) {
5697bdc2678Schristos absent(revno, field);
5707bdc2678Schristos return 0;
5717bdc2678Schristos }
5727bdc2678Schristos
5737bdc2678Schristos next = bhead->hsh;
5747bdc2678Schristos if (length==field) {
5757bdc2678Schristos /* pick latest one on that branch */
5767bdc2678Schristos trail = 0;
5777bdc2678Schristos do { if ((!date || cmpdate(date,next->date)>=0) &&
5787bdc2678Schristos (!author || strcmp(author,next->author)==0) &&
5797bdc2678Schristos (!state || strcmp(state,next->state)==0)
5807bdc2678Schristos ) trail = next;
5817bdc2678Schristos next=next->next;
5827bdc2678Schristos } while (next);
5837bdc2678Schristos
5847bdc2678Schristos if (!trail) {
5857bdc2678Schristos cantfindbranch(revno, date, author, state);
5867bdc2678Schristos return 0;
5877bdc2678Schristos } else { /* print up to last one suitable */
5887bdc2678Schristos next = bhead->hsh;
5897bdc2678Schristos while (next!=trail) {
5907bdc2678Schristos store1(&store, next);
5917bdc2678Schristos next=next->next;
5927bdc2678Schristos }
5937bdc2678Schristos store1(&store, next);
5947bdc2678Schristos }
5957bdc2678Schristos *store = 0;
5967bdc2678Schristos return next;
5977bdc2678Schristos }
5987bdc2678Schristos
5997bdc2678Schristos /* length > field */
6007bdc2678Schristos /* find revision */
6017bdc2678Schristos /* check low */
6027bdc2678Schristos if (cmpnumfld(revno,next->num,field+1)<0) {
6037bdc2678Schristos bufautobegin(&t);
6047bdc2678Schristos rcserror("revision number %s too low",
6057bdc2678Schristos partialno(&t,revno,field+1)
6067bdc2678Schristos );
6077bdc2678Schristos bufautoend(&t);
6087bdc2678Schristos return 0;
6097bdc2678Schristos }
6107bdc2678Schristos do {
6117bdc2678Schristos store1(&store, next);
6127bdc2678Schristos trail = next;
6137bdc2678Schristos next = next->next;
6147bdc2678Schristos } while (next && cmpnumfld(revno,next->num,field+1)>=0);
6157bdc2678Schristos
6167bdc2678Schristos if ((length>field+1) && /*need exact hit */
6177bdc2678Schristos (cmpnumfld(revno,trail->num,field+1) !=0)){
6187bdc2678Schristos absent(revno, field+1);
6197bdc2678Schristos return 0;
6207bdc2678Schristos }
6217bdc2678Schristos if (length == field+1) {
6227bdc2678Schristos if (date && cmpdate(date,trail->date)<0) {
6237bdc2678Schristos rcserror("Revision %s has date %s.",
6247bdc2678Schristos trail->num,
6257bdc2678Schristos date2str(trail->date, datebuf)
6267bdc2678Schristos );
6277bdc2678Schristos return 0;
6287bdc2678Schristos }
6297bdc2678Schristos if (author && strcmp(author,trail->author)!=0) {
6307bdc2678Schristos rcserror("Revision %s has author %s.",
6317bdc2678Schristos trail->num, trail->author
6327bdc2678Schristos );
6337bdc2678Schristos return 0;
6347bdc2678Schristos }
635*fa28c6faSchristos if (state) {
636*fa28c6faSchristos const char *st;
637*fa28c6faSchristos
638*fa28c6faSchristos if (trail->state == NULL)
639*fa28c6faSchristos st = "<empty>";
640*fa28c6faSchristos else if (strcmp(trail->state, state) != 0)
641*fa28c6faSchristos st = trail->state;
642*fa28c6faSchristos else
643*fa28c6faSchristos st = NULL;
644*fa28c6faSchristos
645*fa28c6faSchristos if (st)
6467bdc2678Schristos rcserror("Revision %s has state %s.",
647*fa28c6faSchristos trail->num, st);
6487bdc2678Schristos return 0;
6497bdc2678Schristos }
6507bdc2678Schristos }
6517bdc2678Schristos bhead = trail->branches;
6527bdc2678Schristos
6537bdc2678Schristos } while ((field+=2) <= length);
6547bdc2678Schristos *store = 0;
6557bdc2678Schristos return trail;
6567bdc2678Schristos }
6577bdc2678Schristos
6587bdc2678Schristos
6597bdc2678Schristos static char const *
lookupsym(id)6607bdc2678Schristos lookupsym(id)
6617bdc2678Schristos char const *id;
6627bdc2678Schristos /* Function: looks up id in the list of symbolic names starting
6637bdc2678Schristos * with pointer SYMBOLS, and returns a pointer to the corresponding
6647bdc2678Schristos * revision number. Return 0 if not present.
6657bdc2678Schristos */
6667bdc2678Schristos {
6677bdc2678Schristos register struct assoc const *next;
6687bdc2678Schristos for (next = Symbols; next; next = next->nextassoc)
6697bdc2678Schristos if (strcmp(id, next->symbol)==0)
6707bdc2678Schristos return next->num;
6717bdc2678Schristos return 0;
6727bdc2678Schristos }
6737bdc2678Schristos
expandsym(source,target)6747bdc2678Schristos int expandsym(source, target)
6757bdc2678Schristos char const *source;
6767bdc2678Schristos struct buf *target;
6777bdc2678Schristos /* Function: Source points to a revision number. Expandsym copies
6787bdc2678Schristos * the number to target, but replaces all symbolic fields in the
6797bdc2678Schristos * source number with their numeric values.
6807bdc2678Schristos * Expand a branch followed by `.' to the latest revision on that branch.
6817bdc2678Schristos * Ignore `.' after a revision. Remove leading zeros.
6827bdc2678Schristos * returns false on error;
6837bdc2678Schristos */
6847bdc2678Schristos {
6857bdc2678Schristos return fexpandsym(source, target, (RILE*)0);
6867bdc2678Schristos }
6877bdc2678Schristos
6887bdc2678Schristos int
fexpandsym(source,target,fp)6897bdc2678Schristos fexpandsym(source, target, fp)
6907bdc2678Schristos char const *source;
6917bdc2678Schristos struct buf *target;
6927bdc2678Schristos RILE *fp;
6937bdc2678Schristos /* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */
6947bdc2678Schristos {
6957bdc2678Schristos register char const *sp, *bp;
6967bdc2678Schristos register char *tp;
6977bdc2678Schristos char const *tlim;
6987bdc2678Schristos int dots;
6997bdc2678Schristos
7007bdc2678Schristos sp = source;
7017bdc2678Schristos bufalloc(target, 1);
7027bdc2678Schristos tp = target->string;
7037bdc2678Schristos if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */
7047bdc2678Schristos *tp='\0';
7057bdc2678Schristos return true;
7067bdc2678Schristos }
7077bdc2678Schristos if (sp[0] == KDELIM && !sp[1]) {
7087bdc2678Schristos if (!getoldkeys(fp))
7097bdc2678Schristos return false;
7107bdc2678Schristos if (!*prevrev.string) {
7117bdc2678Schristos workerror("working file lacks revision number");
7127bdc2678Schristos return false;
7137bdc2678Schristos }
7147bdc2678Schristos bufscpy(target, prevrev.string);
7157bdc2678Schristos return true;
7167bdc2678Schristos }
7177bdc2678Schristos tlim = tp + target->size;
7187bdc2678Schristos dots = 0;
7197bdc2678Schristos
7207bdc2678Schristos for (;;) {
7217bdc2678Schristos register char *p = tp;
7227bdc2678Schristos size_t s = tp - target->string;
7237bdc2678Schristos int id = false;
7247bdc2678Schristos for (;;) {
7257bdc2678Schristos switch (ctab[(unsigned char)*sp]) {
7267bdc2678Schristos case IDCHAR:
7277bdc2678Schristos case LETTER:
7287bdc2678Schristos case Letter:
7297bdc2678Schristos id = true;
7307bdc2678Schristos /* fall into */
7317bdc2678Schristos case DIGIT:
7327bdc2678Schristos if (tlim <= p)
7337bdc2678Schristos p = bufenlarge(target, &tlim);
7347bdc2678Schristos *p++ = *sp++;
7357bdc2678Schristos continue;
7367bdc2678Schristos
7377bdc2678Schristos default:
7387bdc2678Schristos break;
7397bdc2678Schristos }
7407bdc2678Schristos break;
7417bdc2678Schristos }
7427bdc2678Schristos if (tlim <= p)
7437bdc2678Schristos p = bufenlarge(target, &tlim);
7447bdc2678Schristos *p = 0;
7457bdc2678Schristos tp = target->string + s;
7467bdc2678Schristos
7477bdc2678Schristos if (id) {
7487bdc2678Schristos bp = lookupsym(tp);
7497bdc2678Schristos if (!bp) {
7507bdc2678Schristos rcserror("Symbolic name `%s' is undefined.",tp);
7517bdc2678Schristos return false;
7527bdc2678Schristos }
7537bdc2678Schristos } else {
7547bdc2678Schristos /* skip leading zeros */
7557bdc2678Schristos for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++)
7567bdc2678Schristos continue;
7577bdc2678Schristos
758*fa28c6faSchristos if (!*bp) {
7597bdc2678Schristos if (s || *sp!='.')
7607bdc2678Schristos break;
7617bdc2678Schristos else {
7627bdc2678Schristos /* Insert default branch before initial `.'. */
7637bdc2678Schristos char const *b;
7647bdc2678Schristos if (Dbranch)
7657bdc2678Schristos b = Dbranch;
7667bdc2678Schristos else if (Head)
7677bdc2678Schristos b = Head->num;
7687bdc2678Schristos else
7697bdc2678Schristos break;
7707bdc2678Schristos getbranchno(b, target);
7717bdc2678Schristos bp = tp = target->string;
7727bdc2678Schristos tlim = tp + target->size;
7737bdc2678Schristos }
7747bdc2678Schristos }
775*fa28c6faSchristos }
7767bdc2678Schristos
7777bdc2678Schristos while ((*tp++ = *bp++))
7787bdc2678Schristos if (tlim <= tp)
7797bdc2678Schristos tp = bufenlarge(target, &tlim);
7807bdc2678Schristos
7817bdc2678Schristos switch (*sp++) {
7827bdc2678Schristos case '\0':
7837bdc2678Schristos return true;
7847bdc2678Schristos
7857bdc2678Schristos case '.':
7867bdc2678Schristos if (!*sp) {
7877bdc2678Schristos if (dots & 1)
7887bdc2678Schristos break;
7897bdc2678Schristos if (!(bp = branchtip(target->string)))
7907bdc2678Schristos return false;
7917bdc2678Schristos bufscpy(target, bp);
7927bdc2678Schristos return true;
7937bdc2678Schristos }
7947bdc2678Schristos ++dots;
7957bdc2678Schristos tp[-1] = '.';
7967bdc2678Schristos continue;
7977bdc2678Schristos }
7987bdc2678Schristos break;
7997bdc2678Schristos }
8007bdc2678Schristos
8017bdc2678Schristos rcserror("improper revision number: %s", source);
8027bdc2678Schristos return false;
8037bdc2678Schristos }
8047bdc2678Schristos
8057bdc2678Schristos char const *
namedrev(name,delta)8067bdc2678Schristos namedrev(name, delta)
8077bdc2678Schristos char const *name;
8087bdc2678Schristos struct hshentry *delta;
8097bdc2678Schristos /* Yield NAME if it names DELTA, 0 otherwise. */
8107bdc2678Schristos {
8117bdc2678Schristos if (name) {
8127bdc2678Schristos char const *id = 0, *p, *val;
8137bdc2678Schristos for (p = name; ; p++)
8147bdc2678Schristos switch (ctab[(unsigned char)*p]) {
8157bdc2678Schristos case IDCHAR:
8167bdc2678Schristos case LETTER:
8177bdc2678Schristos case Letter:
8187bdc2678Schristos id = name;
8197bdc2678Schristos break;
8207bdc2678Schristos
8217bdc2678Schristos case DIGIT:
8227bdc2678Schristos break;
8237bdc2678Schristos
8247bdc2678Schristos case UNKN:
8257bdc2678Schristos if (!*p && id &&
8267bdc2678Schristos (val = lookupsym(id)) &&
8277bdc2678Schristos strcmp(val, delta->num) == 0
8287bdc2678Schristos )
8297bdc2678Schristos return id;
8307bdc2678Schristos /* fall into */
8317bdc2678Schristos default:
8327bdc2678Schristos return 0;
8337bdc2678Schristos }
8347bdc2678Schristos }
8357bdc2678Schristos return 0;
8367bdc2678Schristos }
8377bdc2678Schristos
8387bdc2678Schristos static char const *
branchtip(branch)8397bdc2678Schristos branchtip(branch)
8407bdc2678Schristos char const *branch;
8417bdc2678Schristos {
8427bdc2678Schristos struct hshentry *h;
8437bdc2678Schristos struct hshentries *hs;
8447bdc2678Schristos
8457bdc2678Schristos h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs);
8467bdc2678Schristos return h ? h->num : (char const*)0;
8477bdc2678Schristos }
8487bdc2678Schristos
8497bdc2678Schristos char const *
tiprev()8507bdc2678Schristos tiprev()
8517bdc2678Schristos {
8527bdc2678Schristos return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0;
8537bdc2678Schristos }
8547bdc2678Schristos
8557bdc2678Schristos
8567bdc2678Schristos
8577bdc2678Schristos #ifdef REVTEST
8587bdc2678Schristos
8597bdc2678Schristos /*
8607bdc2678Schristos * Test the routines that generate a sequence of delta numbers
8617bdc2678Schristos * needed to regenerate a given delta.
8627bdc2678Schristos */
8637bdc2678Schristos
8647bdc2678Schristos char const cmdid[] = "revtest";
8657bdc2678Schristos
8667bdc2678Schristos int
main(argc,argv)8677bdc2678Schristos main(argc,argv)
8687bdc2678Schristos int argc; char * argv[];
8697bdc2678Schristos {
8707bdc2678Schristos static struct buf numricrevno;
8717bdc2678Schristos char symrevno[100]; /* used for input of revision numbers */
8727bdc2678Schristos char author[20];
8737bdc2678Schristos char state[20];
8747bdc2678Schristos char date[20];
8757bdc2678Schristos struct hshentries *gendeltas;
8767bdc2678Schristos struct hshentry * target;
8777bdc2678Schristos int i;
8787bdc2678Schristos
8797bdc2678Schristos if (argc<2) {
8807bdc2678Schristos aputs("No input file\n",stderr);
8817bdc2678Schristos exitmain(EXIT_FAILURE);
8827bdc2678Schristos }
8837bdc2678Schristos if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
8847bdc2678Schristos faterror("can't open input file %s", argv[1]);
8857bdc2678Schristos }
8867bdc2678Schristos Lexinit();
8877bdc2678Schristos getadmin();
8887bdc2678Schristos
8897bdc2678Schristos gettree();
8907bdc2678Schristos
8917bdc2678Schristos getdesc(false);
8927bdc2678Schristos
8937bdc2678Schristos do {
8947bdc2678Schristos /* all output goes to stderr, to have diagnostics and */
8957bdc2678Schristos /* errors in sequence. */
8967bdc2678Schristos aputs("\nEnter revision number or <return> or '.': ",stderr);
8977bdc2678Schristos if (!gets(symrevno)) break;
8987bdc2678Schristos if (*symrevno == '.') break;
8997bdc2678Schristos aprintf(stderr,"%s;\n",symrevno);
9007bdc2678Schristos expandsym(symrevno,&numricrevno);
9017bdc2678Schristos aprintf(stderr,"expanded number: %s; ",numricrevno.string);
9027bdc2678Schristos aprintf(stderr,"Date: ");
9037bdc2678Schristos gets(date); aprintf(stderr,"%s; ",date);
9047bdc2678Schristos aprintf(stderr,"Author: ");
9057bdc2678Schristos gets(author); aprintf(stderr,"%s; ",author);
9067bdc2678Schristos aprintf(stderr,"State: ");
9077bdc2678Schristos gets(state); aprintf(stderr, "%s;\n", state);
9087bdc2678Schristos target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0,
9097bdc2678Schristos *state?state:(char*)0, &gendeltas);
9107bdc2678Schristos if (target) {
9117bdc2678Schristos while (gendeltas) {
9127bdc2678Schristos aprintf(stderr,"%s\n",gendeltas->first->num);
9137bdc2678Schristos gendeltas = gendeltas->next;
9147bdc2678Schristos }
9157bdc2678Schristos }
9167bdc2678Schristos } while (true);
9177bdc2678Schristos aprintf(stderr,"done\n");
9187bdc2678Schristos exitmain(EXIT_SUCCESS);
9197bdc2678Schristos }
9207bdc2678Schristos
exiterr()9217bdc2678Schristos void exiterr() { _exit(EXIT_FAILURE); }
9227bdc2678Schristos
9237bdc2678Schristos #endif
924