xref: /netbsd-src/external/gpl2/rcs/dist/src/rcsrev.c (revision fa28c6faa16e0b00edee7acdcaf4899797043def)
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