xref: /dflybsd-src/gnu/usr.bin/rcs/lib/rcssyn.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /* RCS file syntactic analysis */
2*86d7f5d3SJohn Marino 
3*86d7f5d3SJohn Marino /******************************************************************************
4*86d7f5d3SJohn Marino  *                       Syntax Analysis.
5*86d7f5d3SJohn Marino  *                       Keyword table
6*86d7f5d3SJohn Marino  *                       Testprogram: define SYNTEST
7*86d7f5d3SJohn Marino  *                       Compatibility with Release 2: define COMPAT2=1
8*86d7f5d3SJohn Marino  ******************************************************************************
9*86d7f5d3SJohn Marino  */
10*86d7f5d3SJohn Marino 
11*86d7f5d3SJohn Marino /* Copyright 1982, 1988, 1989 Walter Tichy
12*86d7f5d3SJohn Marino    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
13*86d7f5d3SJohn Marino    Distributed under license by the Free Software Foundation, Inc.
14*86d7f5d3SJohn Marino 
15*86d7f5d3SJohn Marino This file is part of RCS.
16*86d7f5d3SJohn Marino 
17*86d7f5d3SJohn Marino RCS is free software; you can redistribute it and/or modify
18*86d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
19*86d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
20*86d7f5d3SJohn Marino any later version.
21*86d7f5d3SJohn Marino 
22*86d7f5d3SJohn Marino RCS is distributed in the hope that it will be useful,
23*86d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
24*86d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25*86d7f5d3SJohn Marino GNU General Public License for more details.
26*86d7f5d3SJohn Marino 
27*86d7f5d3SJohn Marino You should have received a copy of the GNU General Public License
28*86d7f5d3SJohn Marino along with RCS; see the file COPYING.
29*86d7f5d3SJohn Marino If not, write to the Free Software Foundation,
30*86d7f5d3SJohn Marino 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31*86d7f5d3SJohn Marino 
32*86d7f5d3SJohn Marino Report problems and direct all questions to:
33*86d7f5d3SJohn Marino 
34*86d7f5d3SJohn Marino     rcs-bugs@cs.purdue.edu
35*86d7f5d3SJohn Marino 
36*86d7f5d3SJohn Marino */
37*86d7f5d3SJohn Marino 
38*86d7f5d3SJohn Marino /*
39*86d7f5d3SJohn Marino  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.7 1999/08/27 23:36:48 peter Exp $
40*86d7f5d3SJohn Marino  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.4 2007/01/21 17:58:42 pavalos Exp $
41*86d7f5d3SJohn Marino  *
42*86d7f5d3SJohn Marino  * Revision 5.15  1995/06/16 06:19:24  eggert
43*86d7f5d3SJohn Marino  * Update FSF address.
44*86d7f5d3SJohn Marino  *
45*86d7f5d3SJohn Marino  * Revision 5.14  1995/06/01 16:23:43  eggert
46*86d7f5d3SJohn Marino  * (expand_names): Add "b" for -kb.
47*86d7f5d3SJohn Marino  * (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate.
48*86d7f5d3SJohn Marino  *
49*86d7f5d3SJohn Marino  * Revision 5.13  1994/03/20 04:52:58  eggert
50*86d7f5d3SJohn Marino  * Remove lint.
51*86d7f5d3SJohn Marino  *
52*86d7f5d3SJohn Marino  * Revision 5.12  1993/11/03 17:42:27  eggert
53*86d7f5d3SJohn Marino  * Parse MKS RCS dates; ignore \r in diff control lines.
54*86d7f5d3SJohn Marino  * Don't discard ignored phrases.  Improve quality of diagnostics.
55*86d7f5d3SJohn Marino  *
56*86d7f5d3SJohn Marino  * Revision 5.11  1992/07/28  16:12:44  eggert
57*86d7f5d3SJohn Marino  * Avoid `unsigned'.  Statement macro names now end in _.
58*86d7f5d3SJohn Marino  *
59*86d7f5d3SJohn Marino  * Revision 5.10  1992/01/24  18:44:19  eggert
60*86d7f5d3SJohn Marino  * Move put routines to rcsgen.c.
61*86d7f5d3SJohn Marino  *
62*86d7f5d3SJohn Marino  * Revision 5.9  1992/01/06  02:42:34  eggert
63*86d7f5d3SJohn Marino  * ULONG_MAX/10 -> ULONG_MAX_OVER_10
64*86d7f5d3SJohn Marino  * while (E) ; -> while (E) continue;
65*86d7f5d3SJohn Marino  *
66*86d7f5d3SJohn Marino  * Revision 5.8  1991/08/19  03:13:55  eggert
67*86d7f5d3SJohn Marino  * Tune.
68*86d7f5d3SJohn Marino  *
69*86d7f5d3SJohn Marino  * Revision 5.7  1991/04/21  11:58:29  eggert
70*86d7f5d3SJohn Marino  * Disambiguate names on shortname hosts.
71*86d7f5d3SJohn Marino  * Fix errno bug.  Add MS-DOS support.
72*86d7f5d3SJohn Marino  *
73*86d7f5d3SJohn Marino  * Revision 5.6  1991/02/28  19:18:51  eggert
74*86d7f5d3SJohn Marino  * Fix null termination bug in reporting keyword expansion.
75*86d7f5d3SJohn Marino  *
76*86d7f5d3SJohn Marino  * Revision 5.5  1991/02/25  07:12:44  eggert
77*86d7f5d3SJohn Marino  * Check diff output more carefully; avoid overflow.
78*86d7f5d3SJohn Marino  *
79*86d7f5d3SJohn Marino  * Revision 5.4  1990/11/01  05:28:48  eggert
80*86d7f5d3SJohn Marino  * When ignoring unknown phrases, copy them to the output RCS file.
81*86d7f5d3SJohn Marino  * Permit arbitrary data in logs and comment leaders.
82*86d7f5d3SJohn Marino  * Don't check for nontext on initial checkin.
83*86d7f5d3SJohn Marino  *
84*86d7f5d3SJohn Marino  * Revision 5.3  1990/09/20  07:58:32  eggert
85*86d7f5d3SJohn Marino  * Remove the test for non-text bytes; it caused more pain than it cured.
86*86d7f5d3SJohn Marino  *
87*86d7f5d3SJohn Marino  * Revision 5.2  1990/09/04  08:02:30  eggert
88*86d7f5d3SJohn Marino  * Parse RCS files with no revisions.
89*86d7f5d3SJohn Marino  * Don't strip leading white space from diff commands.  Count RCS lines better.
90*86d7f5d3SJohn Marino  *
91*86d7f5d3SJohn Marino  * Revision 5.1  1990/08/29  07:14:06  eggert
92*86d7f5d3SJohn Marino  * Add -kkvl.  Clean old log messages too.
93*86d7f5d3SJohn Marino  *
94*86d7f5d3SJohn Marino  * Revision 5.0  1990/08/22  08:13:44  eggert
95*86d7f5d3SJohn Marino  * Try to parse future RCS formats without barfing.
96*86d7f5d3SJohn Marino  * Add -k.  Don't require final newline.
97*86d7f5d3SJohn Marino  * Remove compile-time limits; use malloc instead.
98*86d7f5d3SJohn Marino  * Don't output branch keyword if there's no default branch,
99*86d7f5d3SJohn Marino  * because RCS version 3 doesn't understand it.
100*86d7f5d3SJohn Marino  * Tune.  Remove lint.
101*86d7f5d3SJohn Marino  * Add support for ISO 8859.  Ansify and Posixate.
102*86d7f5d3SJohn Marino  * Check that a newly checked-in file is acceptable as input to 'diff'.
103*86d7f5d3SJohn Marino  * Check diff's output.
104*86d7f5d3SJohn Marino  *
105*86d7f5d3SJohn Marino  * Revision 4.6  89/05/01  15:13:32  narten
106*86d7f5d3SJohn Marino  * changed copyright header to reflect current distribution rules
107*86d7f5d3SJohn Marino  *
108*86d7f5d3SJohn Marino  * Revision 4.5  88/08/09  19:13:21  eggert
109*86d7f5d3SJohn Marino  * Allow cc -R; remove lint.
110*86d7f5d3SJohn Marino  *
111*86d7f5d3SJohn Marino  * Revision 4.4  87/12/18  11:46:16  narten
112*86d7f5d3SJohn Marino  * more lint cleanups (Guy Harris)
113*86d7f5d3SJohn Marino  *
114*86d7f5d3SJohn Marino  * Revision 4.3  87/10/18  10:39:36  narten
115*86d7f5d3SJohn Marino  * Updating version numbers. Changes relative to 1.1 actually relative to
116*86d7f5d3SJohn Marino  * 4.1
117*86d7f5d3SJohn Marino  *
118*86d7f5d3SJohn Marino  * Revision 1.3  87/09/24  14:00:49  narten
119*86d7f5d3SJohn Marino  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
120*86d7f5d3SJohn Marino  * warnings)
121*86d7f5d3SJohn Marino  *
122*86d7f5d3SJohn Marino  * Revision 1.2  87/03/27  14:22:40  jenkins
123*86d7f5d3SJohn Marino  * Port to suns
124*86d7f5d3SJohn Marino  *
125*86d7f5d3SJohn Marino  * Revision 4.1  83/03/28  11:38:49  wft
126*86d7f5d3SJohn Marino  * Added parsing and printing of default branch.
127*86d7f5d3SJohn Marino  *
128*86d7f5d3SJohn Marino  * Revision 3.6  83/01/15  17:46:50  wft
129*86d7f5d3SJohn Marino  * Changed readdelta() to initialize selector and log-pointer.
130*86d7f5d3SJohn Marino  * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
131*86d7f5d3SJohn Marino  *
132*86d7f5d3SJohn Marino  * Revision 3.5  82/12/08  21:58:58  wft
133*86d7f5d3SJohn Marino  * renamed Commentleader to Commleader.
134*86d7f5d3SJohn Marino  *
135*86d7f5d3SJohn Marino  * Revision 3.4  82/12/04  13:24:40  wft
136*86d7f5d3SJohn Marino  * Added routine gettree(), which updates keeplock after reading the
137*86d7f5d3SJohn Marino  * delta tree.
138*86d7f5d3SJohn Marino  *
139*86d7f5d3SJohn Marino  * Revision 3.3  82/11/28  21:30:11  wft
140*86d7f5d3SJohn Marino  * Reading and printing of Suffix removed; version COMPAT2 skips the
141*86d7f5d3SJohn Marino  * Suffix for files of release 2 format. Fixed problems with printing nil.
142*86d7f5d3SJohn Marino  *
143*86d7f5d3SJohn Marino  * Revision 3.2  82/10/18  21:18:25  wft
144*86d7f5d3SJohn Marino  * renamed putdeltatext to putdtext.
145*86d7f5d3SJohn Marino  *
146*86d7f5d3SJohn Marino  * Revision 3.1  82/10/11  19:45:11  wft
147*86d7f5d3SJohn Marino  * made sure getc() returns into an integer.
148*86d7f5d3SJohn Marino  */
149*86d7f5d3SJohn Marino 
150*86d7f5d3SJohn Marino 
151*86d7f5d3SJohn Marino 
152*86d7f5d3SJohn Marino /* version COMPAT2 reads files of the format of release 2 and 3, but
153*86d7f5d3SJohn Marino  * generates files of release 3 format. Need not be defined if no
154*86d7f5d3SJohn Marino  * old RCS files generated with release 2 exist.
155*86d7f5d3SJohn Marino  */
156*86d7f5d3SJohn Marino 
157*86d7f5d3SJohn Marino #include "rcsbase.h"
158*86d7f5d3SJohn Marino 
159*86d7f5d3SJohn Marino libId(synId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.4 2007/01/21 17:58:42 pavalos Exp $")
160*86d7f5d3SJohn Marino 
161*86d7f5d3SJohn Marino static char const *getkeyval P((char const*,enum tokens,int));
162*86d7f5d3SJohn Marino static int getdelta P((void));
163*86d7f5d3SJohn Marino static int strn2expmode P((char const*,size_t));
164*86d7f5d3SJohn Marino static struct hshentry *getdnum P((void));
165*86d7f5d3SJohn Marino static void badDiffOutput P((char const*)) exiting;
166*86d7f5d3SJohn Marino static void diffLineNumberTooLarge P((char const*)) exiting;
167*86d7f5d3SJohn Marino static void getsemi P((char const*));
168*86d7f5d3SJohn Marino 
169*86d7f5d3SJohn Marino /* keyword table */
170*86d7f5d3SJohn Marino 
171*86d7f5d3SJohn Marino char const
172*86d7f5d3SJohn Marino 	Kaccess[]   = "access",
173*86d7f5d3SJohn Marino 	Kauthor[]   = "author",
174*86d7f5d3SJohn Marino 	Kbranch[]   = "branch",
175*86d7f5d3SJohn Marino 	Kcomment[]  = "comment",
176*86d7f5d3SJohn Marino 	Kcommitid[] = "commitid",
177*86d7f5d3SJohn Marino 	Kdate[]     = "date",
178*86d7f5d3SJohn Marino 	Kdesc[]     = "desc",
179*86d7f5d3SJohn Marino 	Kexpand[]   = "expand",
180*86d7f5d3SJohn Marino 	Khead[]     = "head",
181*86d7f5d3SJohn Marino 	Klocks[]    = "locks",
182*86d7f5d3SJohn Marino 	Klog[]      = "log",
183*86d7f5d3SJohn Marino 	Knext[]     = "next",
184*86d7f5d3SJohn Marino 	Kstate[]    = "state",
185*86d7f5d3SJohn Marino 	Kstrict[]   = "strict",
186*86d7f5d3SJohn Marino 	Ksymbols[]  = "symbols",
187*86d7f5d3SJohn Marino 	Ktext[]     = "text";
188*86d7f5d3SJohn Marino 
189*86d7f5d3SJohn Marino static char const
190*86d7f5d3SJohn Marino #if COMPAT2
191*86d7f5d3SJohn Marino 	Ksuffix[]   = "suffix",
192*86d7f5d3SJohn Marino #endif
193*86d7f5d3SJohn Marino 	K_branches[]= "branches";
194*86d7f5d3SJohn Marino 
195*86d7f5d3SJohn Marino static struct buf Commleader;
196*86d7f5d3SJohn Marino struct cbuf Comment;
197*86d7f5d3SJohn Marino struct cbuf Ignored;
198*86d7f5d3SJohn Marino struct access   * AccessList;
199*86d7f5d3SJohn Marino struct assoc    * Symbols;
200*86d7f5d3SJohn Marino struct rcslock *Locks;
201*86d7f5d3SJohn Marino int		  Expand;
202*86d7f5d3SJohn Marino int               StrictLocks;
203*86d7f5d3SJohn Marino struct hshentry * Head;
204*86d7f5d3SJohn Marino char const      * Dbranch;
205*86d7f5d3SJohn Marino int TotalDeltas;
206*86d7f5d3SJohn Marino 
207*86d7f5d3SJohn Marino 
208*86d7f5d3SJohn Marino 	static void
getsemi(key)209*86d7f5d3SJohn Marino getsemi(key)
210*86d7f5d3SJohn Marino 	char const *key;
211*86d7f5d3SJohn Marino /* Get a semicolon to finish off a phrase started by KEY.  */
212*86d7f5d3SJohn Marino {
213*86d7f5d3SJohn Marino 	if (!getlex(SEMI))
214*86d7f5d3SJohn Marino 		fatserror("missing ';' after '%s'", key);
215*86d7f5d3SJohn Marino }
216*86d7f5d3SJohn Marino 
217*86d7f5d3SJohn Marino 	static struct hshentry *
getdnum()218*86d7f5d3SJohn Marino getdnum()
219*86d7f5d3SJohn Marino /* Get a delta number.  */
220*86d7f5d3SJohn Marino {
221*86d7f5d3SJohn Marino 	register struct hshentry *delta = getnum();
222*86d7f5d3SJohn Marino 	if (delta && countnumflds(delta->num)&1)
223*86d7f5d3SJohn Marino 		fatserror("%s isn't a delta number", delta->num);
224*86d7f5d3SJohn Marino 	return delta;
225*86d7f5d3SJohn Marino }
226*86d7f5d3SJohn Marino 
227*86d7f5d3SJohn Marino 
228*86d7f5d3SJohn Marino 	void
getadmin()229*86d7f5d3SJohn Marino getadmin()
230*86d7f5d3SJohn Marino /* Read an <admin> and initialize the appropriate global variables.  */
231*86d7f5d3SJohn Marino {
232*86d7f5d3SJohn Marino 	register char const *id;
233*86d7f5d3SJohn Marino         struct access   * newaccess;
234*86d7f5d3SJohn Marino         struct assoc    * newassoc;
235*86d7f5d3SJohn Marino 	struct rcslock *newlock;
236*86d7f5d3SJohn Marino         struct hshentry * delta;
237*86d7f5d3SJohn Marino 	struct access **LastAccess;
238*86d7f5d3SJohn Marino 	struct assoc **LastSymbol;
239*86d7f5d3SJohn Marino 	struct rcslock **LastLock;
240*86d7f5d3SJohn Marino 	struct buf b;
241*86d7f5d3SJohn Marino 	struct cbuf cb;
242*86d7f5d3SJohn Marino 
243*86d7f5d3SJohn Marino         TotalDeltas=0;
244*86d7f5d3SJohn Marino 
245*86d7f5d3SJohn Marino 	getkey(Khead);
246*86d7f5d3SJohn Marino 	Head = getdnum();
247*86d7f5d3SJohn Marino 	getsemi(Khead);
248*86d7f5d3SJohn Marino 
249*86d7f5d3SJohn Marino 	Dbranch = 0;
250*86d7f5d3SJohn Marino 	if (getkeyopt(Kbranch)) {
251*86d7f5d3SJohn Marino 		if ((delta = getnum()))
252*86d7f5d3SJohn Marino 			Dbranch = delta->num;
253*86d7f5d3SJohn Marino 		getsemi(Kbranch);
254*86d7f5d3SJohn Marino         }
255*86d7f5d3SJohn Marino 
256*86d7f5d3SJohn Marino 
257*86d7f5d3SJohn Marino #if COMPAT2
258*86d7f5d3SJohn Marino         /* read suffix. Only in release 2 format */
259*86d7f5d3SJohn Marino 	if (getkeyopt(Ksuffix)) {
260*86d7f5d3SJohn Marino                 if (nexttok==STRING) {
261*86d7f5d3SJohn Marino 			readstring(); nextlex(); /* Throw away the suffix.  */
262*86d7f5d3SJohn Marino 		} else if (nexttok==ID) {
263*86d7f5d3SJohn Marino                         nextlex();
264*86d7f5d3SJohn Marino                 }
265*86d7f5d3SJohn Marino 		getsemi(Ksuffix);
266*86d7f5d3SJohn Marino         }
267*86d7f5d3SJohn Marino #endif
268*86d7f5d3SJohn Marino 
269*86d7f5d3SJohn Marino 	getkey(Kaccess);
270*86d7f5d3SJohn Marino 	LastAccess = &AccessList;
271*86d7f5d3SJohn Marino 	while ((id = getid())) {
272*86d7f5d3SJohn Marino 		newaccess = ftalloc(struct access);
273*86d7f5d3SJohn Marino                 newaccess->login = id;
274*86d7f5d3SJohn Marino 		*LastAccess = newaccess;
275*86d7f5d3SJohn Marino 		LastAccess = &newaccess->nextaccess;
276*86d7f5d3SJohn Marino         }
277*86d7f5d3SJohn Marino 	*LastAccess = 0;
278*86d7f5d3SJohn Marino 	getsemi(Kaccess);
279*86d7f5d3SJohn Marino 
280*86d7f5d3SJohn Marino 	getkey(Ksymbols);
281*86d7f5d3SJohn Marino 	LastSymbol = &Symbols;
282*86d7f5d3SJohn Marino         while ((id = getid())) {
283*86d7f5d3SJohn Marino                 if (!getlex(COLON))
284*86d7f5d3SJohn Marino 			fatserror("missing ':' in symbolic name definition");
285*86d7f5d3SJohn Marino                 if (!(delta=getnum())) {
286*86d7f5d3SJohn Marino 			fatserror("missing number in symbolic name definition");
287*86d7f5d3SJohn Marino                 } else { /*add new pair to association list*/
288*86d7f5d3SJohn Marino 			newassoc = ftalloc(struct assoc);
289*86d7f5d3SJohn Marino                         newassoc->symbol=id;
290*86d7f5d3SJohn Marino 			newassoc->num = delta->num;
291*86d7f5d3SJohn Marino 			*LastSymbol = newassoc;
292*86d7f5d3SJohn Marino 			LastSymbol = &newassoc->nextassoc;
293*86d7f5d3SJohn Marino                 }
294*86d7f5d3SJohn Marino         }
295*86d7f5d3SJohn Marino 	*LastSymbol = 0;
296*86d7f5d3SJohn Marino 	getsemi(Ksymbols);
297*86d7f5d3SJohn Marino 
298*86d7f5d3SJohn Marino 	getkey(Klocks);
299*86d7f5d3SJohn Marino 	LastLock = &Locks;
300*86d7f5d3SJohn Marino         while ((id = getid())) {
301*86d7f5d3SJohn Marino                 if (!getlex(COLON))
302*86d7f5d3SJohn Marino 			fatserror("missing ':' in lock");
303*86d7f5d3SJohn Marino 		if (!(delta=getdnum())) {
304*86d7f5d3SJohn Marino 			fatserror("missing number in lock");
305*86d7f5d3SJohn Marino                 } else { /*add new pair to lock list*/
306*86d7f5d3SJohn Marino 			newlock = ftalloc(struct rcslock);
307*86d7f5d3SJohn Marino                         newlock->login=id;
308*86d7f5d3SJohn Marino                         newlock->delta=delta;
309*86d7f5d3SJohn Marino 			*LastLock = newlock;
310*86d7f5d3SJohn Marino 			LastLock = &newlock->nextlock;
311*86d7f5d3SJohn Marino                 }
312*86d7f5d3SJohn Marino         }
313*86d7f5d3SJohn Marino 	*LastLock = 0;
314*86d7f5d3SJohn Marino 	getsemi(Klocks);
315*86d7f5d3SJohn Marino 
316*86d7f5d3SJohn Marino 	if ((StrictLocks = getkeyopt(Kstrict)))
317*86d7f5d3SJohn Marino 		getsemi(Kstrict);
318*86d7f5d3SJohn Marino 
319*86d7f5d3SJohn Marino 	clear_buf(&Comment);
320*86d7f5d3SJohn Marino 	if (getkeyopt(Kcomment)) {
321*86d7f5d3SJohn Marino 		if (nexttok==STRING) {
322*86d7f5d3SJohn Marino 			Comment = savestring(&Commleader);
323*86d7f5d3SJohn Marino 			nextlex();
324*86d7f5d3SJohn Marino 		}
325*86d7f5d3SJohn Marino 		getsemi(Kcomment);
326*86d7f5d3SJohn Marino         }
327*86d7f5d3SJohn Marino 
328*86d7f5d3SJohn Marino 	Expand = KEYVAL_EXPAND;
329*86d7f5d3SJohn Marino 	if (getkeyopt(Kexpand)) {
330*86d7f5d3SJohn Marino 		if (nexttok==STRING) {
331*86d7f5d3SJohn Marino 			bufautobegin(&b);
332*86d7f5d3SJohn Marino 			cb = savestring(&b);
333*86d7f5d3SJohn Marino 			if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
334*86d7f5d3SJohn Marino 			    fatserror("unknown expand mode %.*s",
335*86d7f5d3SJohn Marino 				(int)cb.size, cb.string
336*86d7f5d3SJohn Marino 			    );
337*86d7f5d3SJohn Marino 			bufautoend(&b);
338*86d7f5d3SJohn Marino 			nextlex();
339*86d7f5d3SJohn Marino 		}
340*86d7f5d3SJohn Marino 		getsemi(Kexpand);
341*86d7f5d3SJohn Marino         }
342*86d7f5d3SJohn Marino 	Ignored = getphrases(Kdesc);
343*86d7f5d3SJohn Marino }
344*86d7f5d3SJohn Marino 
345*86d7f5d3SJohn Marino char const *const expand_names[] = {
346*86d7f5d3SJohn Marino 	/* These must agree with *_EXPAND in rcsbase.h.  */
347*86d7f5d3SJohn Marino 	"kv", "kvl", "k", "v", "o", "b",
348*86d7f5d3SJohn Marino 	0
349*86d7f5d3SJohn Marino };
350*86d7f5d3SJohn Marino 
351*86d7f5d3SJohn Marino 	int
str2expmode(s)352*86d7f5d3SJohn Marino str2expmode(s)
353*86d7f5d3SJohn Marino 	char const *s;
354*86d7f5d3SJohn Marino /* Yield expand mode corresponding to S, or -1 if bad.  */
355*86d7f5d3SJohn Marino {
356*86d7f5d3SJohn Marino 	return strn2expmode(s, strlen(s));
357*86d7f5d3SJohn Marino }
358*86d7f5d3SJohn Marino 
359*86d7f5d3SJohn Marino 	static int
strn2expmode(s,n)360*86d7f5d3SJohn Marino strn2expmode(s, n)
361*86d7f5d3SJohn Marino 	char const *s;
362*86d7f5d3SJohn Marino 	size_t n;
363*86d7f5d3SJohn Marino {
364*86d7f5d3SJohn Marino 	char const *const *p;
365*86d7f5d3SJohn Marino 
366*86d7f5d3SJohn Marino 	for (p = expand_names;  *p;  ++p)
367*86d7f5d3SJohn Marino 		if (memcmp(*p,s,n) == 0  &&  !(*p)[n])
368*86d7f5d3SJohn Marino 			return p - expand_names;
369*86d7f5d3SJohn Marino 	return -1;
370*86d7f5d3SJohn Marino }
371*86d7f5d3SJohn Marino 
372*86d7f5d3SJohn Marino 
373*86d7f5d3SJohn Marino 	void
ignorephrases(key)374*86d7f5d3SJohn Marino ignorephrases(key)
375*86d7f5d3SJohn Marino 	const char *key;
376*86d7f5d3SJohn Marino /*
377*86d7f5d3SJohn Marino * Ignore a series of phrases that do not start with KEY.
378*86d7f5d3SJohn Marino * Stop when the next phrase starts with a token that is not an identifier,
379*86d7f5d3SJohn Marino * or is KEY.
380*86d7f5d3SJohn Marino */
381*86d7f5d3SJohn Marino {
382*86d7f5d3SJohn Marino 	for (;;) {
383*86d7f5d3SJohn Marino 		nextlex();
384*86d7f5d3SJohn Marino 		if (nexttok != ID  ||  strcmp(NextString,key) == 0)
385*86d7f5d3SJohn Marino 			break;
386*86d7f5d3SJohn Marino 		warnignore();
387*86d7f5d3SJohn Marino 		hshenter=false;
388*86d7f5d3SJohn Marino 		for (;; nextlex()) {
389*86d7f5d3SJohn Marino 			switch (nexttok) {
390*86d7f5d3SJohn Marino 				case SEMI: hshenter=true; break;
391*86d7f5d3SJohn Marino 				case ID:
392*86d7f5d3SJohn Marino 				case NUM: ffree1(NextString); continue;
393*86d7f5d3SJohn Marino 				case STRING: readstring(); continue;
394*86d7f5d3SJohn Marino 				default: continue;
395*86d7f5d3SJohn Marino 			}
396*86d7f5d3SJohn Marino 			break;
397*86d7f5d3SJohn Marino 		}
398*86d7f5d3SJohn Marino 	}
399*86d7f5d3SJohn Marino }
400*86d7f5d3SJohn Marino 
401*86d7f5d3SJohn Marino 
402*86d7f5d3SJohn Marino 	static int
getdelta()403*86d7f5d3SJohn Marino getdelta()
404*86d7f5d3SJohn Marino /* Function: reads a delta block.
405*86d7f5d3SJohn Marino  * returns false if the current block does not start with a number.
406*86d7f5d3SJohn Marino  */
407*86d7f5d3SJohn Marino {
408*86d7f5d3SJohn Marino         register struct hshentry * Delta, * num;
409*86d7f5d3SJohn Marino 	struct branchhead **LastBranch, *NewBranch;
410*86d7f5d3SJohn Marino 
411*86d7f5d3SJohn Marino 	if (!(Delta = getdnum()))
412*86d7f5d3SJohn Marino 		return false;
413*86d7f5d3SJohn Marino 
414*86d7f5d3SJohn Marino         hshenter = false; /*Don't enter dates into hashtable*/
415*86d7f5d3SJohn Marino 	Delta->date = getkeyval(Kdate, NUM, false);
416*86d7f5d3SJohn Marino         hshenter=true;    /*reset hshenter for revision numbers.*/
417*86d7f5d3SJohn Marino 
418*86d7f5d3SJohn Marino         Delta->author = getkeyval(Kauthor, ID, false);
419*86d7f5d3SJohn Marino 
420*86d7f5d3SJohn Marino         Delta->state = getkeyval(Kstate, ID, true);
421*86d7f5d3SJohn Marino 
422*86d7f5d3SJohn Marino 	getkey(K_branches);
423*86d7f5d3SJohn Marino 	LastBranch = &Delta->branches;
424*86d7f5d3SJohn Marino 	while ((num = getdnum())) {
425*86d7f5d3SJohn Marino 		NewBranch = ftalloc(struct branchhead);
426*86d7f5d3SJohn Marino                 NewBranch->hsh = num;
427*86d7f5d3SJohn Marino 		*LastBranch = NewBranch;
428*86d7f5d3SJohn Marino 		LastBranch = &NewBranch->nextbranch;
429*86d7f5d3SJohn Marino         }
430*86d7f5d3SJohn Marino 	*LastBranch = 0;
431*86d7f5d3SJohn Marino 	getsemi(K_branches);
432*86d7f5d3SJohn Marino 
433*86d7f5d3SJohn Marino 	getkey(Knext);
434*86d7f5d3SJohn Marino 	Delta->next = num = getdnum();
435*86d7f5d3SJohn Marino 	getsemi(Knext);
436*86d7f5d3SJohn Marino 	Delta->lockedby = 0;
437*86d7f5d3SJohn Marino 	Delta->log.string = 0;
438*86d7f5d3SJohn Marino 	Delta->selector = true;
439*86d7f5d3SJohn Marino 
440*86d7f5d3SJohn Marino 	if (getkeyopt(Kcommitid)) {
441*86d7f5d3SJohn Marino 		Delta->commitid = NextString;
442*86d7f5d3SJohn Marino 		nextlex();
443*86d7f5d3SJohn Marino 		getsemi(Kcommitid);
444*86d7f5d3SJohn Marino         } else {
445*86d7f5d3SJohn Marino 		Delta->commitid = NULL;
446*86d7f5d3SJohn Marino 	}
447*86d7f5d3SJohn Marino 
448*86d7f5d3SJohn Marino 	Delta->ig = getphrases(Kdesc);
449*86d7f5d3SJohn Marino         TotalDeltas++;
450*86d7f5d3SJohn Marino         return (true);
451*86d7f5d3SJohn Marino }
452*86d7f5d3SJohn Marino 
453*86d7f5d3SJohn Marino 
454*86d7f5d3SJohn Marino 	void
gettree()455*86d7f5d3SJohn Marino gettree()
456*86d7f5d3SJohn Marino /* Function: Reads in the delta tree with getdelta(), then
457*86d7f5d3SJohn Marino  * updates the lockedby fields.
458*86d7f5d3SJohn Marino  */
459*86d7f5d3SJohn Marino {
460*86d7f5d3SJohn Marino 	struct rcslock const *currlock;
461*86d7f5d3SJohn Marino 
462*86d7f5d3SJohn Marino 	while (getdelta())
463*86d7f5d3SJohn Marino 		continue;
464*86d7f5d3SJohn Marino         currlock=Locks;
465*86d7f5d3SJohn Marino         while (currlock) {
466*86d7f5d3SJohn Marino                 currlock->delta->lockedby = currlock->login;
467*86d7f5d3SJohn Marino                 currlock = currlock->nextlock;
468*86d7f5d3SJohn Marino         }
469*86d7f5d3SJohn Marino }
470*86d7f5d3SJohn Marino 
471*86d7f5d3SJohn Marino 
472*86d7f5d3SJohn Marino 	void
getdesc(prdesc)473*86d7f5d3SJohn Marino getdesc(prdesc)
474*86d7f5d3SJohn Marino int  prdesc;
475*86d7f5d3SJohn Marino /* Function: read in descriptive text
476*86d7f5d3SJohn Marino  * nexttok is not advanced afterwards.
477*86d7f5d3SJohn Marino  * If prdesc is set, the text is printed to stdout.
478*86d7f5d3SJohn Marino  */
479*86d7f5d3SJohn Marino {
480*86d7f5d3SJohn Marino 
481*86d7f5d3SJohn Marino 	getkeystring(Kdesc);
482*86d7f5d3SJohn Marino         if (prdesc)
483*86d7f5d3SJohn Marino                 printstring();  /*echo string*/
484*86d7f5d3SJohn Marino         else    readstring();   /*skip string*/
485*86d7f5d3SJohn Marino }
486*86d7f5d3SJohn Marino 
487*86d7f5d3SJohn Marino 
488*86d7f5d3SJohn Marino 
489*86d7f5d3SJohn Marino 
490*86d7f5d3SJohn Marino 
491*86d7f5d3SJohn Marino 
492*86d7f5d3SJohn Marino 	static char const *
getkeyval(keyword,token,optional)493*86d7f5d3SJohn Marino getkeyval(keyword, token, optional)
494*86d7f5d3SJohn Marino 	char const *keyword;
495*86d7f5d3SJohn Marino 	enum tokens token;
496*86d7f5d3SJohn Marino 	int optional;
497*86d7f5d3SJohn Marino /* reads a pair of the form
498*86d7f5d3SJohn Marino  * <keyword> <token> ;
499*86d7f5d3SJohn Marino  * where token is one of <id> or <num>. optional indicates whether
500*86d7f5d3SJohn Marino  * <token> is optional. A pointer to
501*86d7f5d3SJohn Marino  * the actual character string of <id> or <num> is returned.
502*86d7f5d3SJohn Marino  */
503*86d7f5d3SJohn Marino {
504*86d7f5d3SJohn Marino 	register char const *val = 0;
505*86d7f5d3SJohn Marino 
506*86d7f5d3SJohn Marino 	getkey(keyword);
507*86d7f5d3SJohn Marino         if (nexttok==token) {
508*86d7f5d3SJohn Marino                 val = NextString;
509*86d7f5d3SJohn Marino                 nextlex();
510*86d7f5d3SJohn Marino         } else {
511*86d7f5d3SJohn Marino 		if (!optional)
512*86d7f5d3SJohn Marino 			fatserror("missing %s", keyword);
513*86d7f5d3SJohn Marino         }
514*86d7f5d3SJohn Marino 	getsemi(keyword);
515*86d7f5d3SJohn Marino         return(val);
516*86d7f5d3SJohn Marino }
517*86d7f5d3SJohn Marino 
518*86d7f5d3SJohn Marino 
519*86d7f5d3SJohn Marino 	void
unexpected_EOF()520*86d7f5d3SJohn Marino unexpected_EOF()
521*86d7f5d3SJohn Marino {
522*86d7f5d3SJohn Marino 	rcsfaterror("unexpected EOF in diff output");
523*86d7f5d3SJohn Marino }
524*86d7f5d3SJohn Marino 
525*86d7f5d3SJohn Marino 	void
initdiffcmd(dc)526*86d7f5d3SJohn Marino initdiffcmd(dc)
527*86d7f5d3SJohn Marino 	register struct diffcmd *dc;
528*86d7f5d3SJohn Marino /* Initialize *dc suitably for getdiffcmd(). */
529*86d7f5d3SJohn Marino {
530*86d7f5d3SJohn Marino 	dc->adprev = 0;
531*86d7f5d3SJohn Marino 	dc->dafter = 0;
532*86d7f5d3SJohn Marino }
533*86d7f5d3SJohn Marino 
534*86d7f5d3SJohn Marino 	static void
badDiffOutput(buf)535*86d7f5d3SJohn Marino badDiffOutput(buf)
536*86d7f5d3SJohn Marino 	char const *buf;
537*86d7f5d3SJohn Marino {
538*86d7f5d3SJohn Marino 	rcsfaterror("bad diff output line: %s", buf);
539*86d7f5d3SJohn Marino }
540*86d7f5d3SJohn Marino 
541*86d7f5d3SJohn Marino 	static void
diffLineNumberTooLarge(buf)542*86d7f5d3SJohn Marino diffLineNumberTooLarge(buf)
543*86d7f5d3SJohn Marino 	char const *buf;
544*86d7f5d3SJohn Marino {
545*86d7f5d3SJohn Marino 	rcsfaterror("diff line number too large: %s", buf);
546*86d7f5d3SJohn Marino }
547*86d7f5d3SJohn Marino 
548*86d7f5d3SJohn Marino 	int
getdiffcmd(finfile,delimiter,foutfile,dc)549*86d7f5d3SJohn Marino getdiffcmd(finfile, delimiter, foutfile, dc)
550*86d7f5d3SJohn Marino 	RILE *finfile;
551*86d7f5d3SJohn Marino 	FILE *foutfile;
552*86d7f5d3SJohn Marino 	int delimiter;
553*86d7f5d3SJohn Marino 	struct diffcmd *dc;
554*86d7f5d3SJohn Marino /* Get a editing command output by 'diff -n' from fin.
555*86d7f5d3SJohn Marino  * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
556*86d7f5d3SJohn Marino  * Copy a clean version of the command to fout (if nonnull).
557*86d7f5d3SJohn Marino  * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
558*86d7f5d3SJohn Marino  * Store the command's line number and length into dc->line1 and dc->nlines.
559*86d7f5d3SJohn Marino  * Keep dc->adprev and dc->dafter up to date.
560*86d7f5d3SJohn Marino  */
561*86d7f5d3SJohn Marino {
562*86d7f5d3SJohn Marino 	register int c;
563*86d7f5d3SJohn Marino 	declarecache;
564*86d7f5d3SJohn Marino 	register FILE *fout;
565*86d7f5d3SJohn Marino 	register char *p;
566*86d7f5d3SJohn Marino 	register RILE *fin;
567*86d7f5d3SJohn Marino 	long line1, nlines, t;
568*86d7f5d3SJohn Marino 	char buf[BUFSIZ];
569*86d7f5d3SJohn Marino 
570*86d7f5d3SJohn Marino 	fin = finfile;
571*86d7f5d3SJohn Marino 	fout = foutfile;
572*86d7f5d3SJohn Marino 	setupcache(fin); cache(fin);
573*86d7f5d3SJohn Marino 	cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
574*86d7f5d3SJohn Marino 	if (delimiter) {
575*86d7f5d3SJohn Marino 		if (c==SDELIM) {
576*86d7f5d3SJohn Marino 			cacheget_(c)
577*86d7f5d3SJohn Marino 			if (c==SDELIM) {
578*86d7f5d3SJohn Marino 				buf[0] = c;
579*86d7f5d3SJohn Marino 				buf[1] = 0;
580*86d7f5d3SJohn Marino 				badDiffOutput(buf);
581*86d7f5d3SJohn Marino 			}
582*86d7f5d3SJohn Marino 			uncache(fin);
583*86d7f5d3SJohn Marino 			nextc = c;
584*86d7f5d3SJohn Marino 			if (fout)
585*86d7f5d3SJohn Marino 				aprintf(fout, "%c%c", SDELIM, c);
586*86d7f5d3SJohn Marino 			return -1;
587*86d7f5d3SJohn Marino 		}
588*86d7f5d3SJohn Marino 	}
589*86d7f5d3SJohn Marino 	p = buf;
590*86d7f5d3SJohn Marino 	do {
591*86d7f5d3SJohn Marino 		if (buf+BUFSIZ-2 <= p) {
592*86d7f5d3SJohn Marino 			rcsfaterror("diff output command line too long");
593*86d7f5d3SJohn Marino 		}
594*86d7f5d3SJohn Marino 		*p++ = c;
595*86d7f5d3SJohn Marino 		cachegeteof_(c, unexpected_EOF();)
596*86d7f5d3SJohn Marino 	} while (c != '\n');
597*86d7f5d3SJohn Marino 	uncache(fin);
598*86d7f5d3SJohn Marino 	if (delimiter)
599*86d7f5d3SJohn Marino 		++rcsline;
600*86d7f5d3SJohn Marino 	*p = '\0';
601*86d7f5d3SJohn Marino 	for (p = buf+1;  (c = *p++) == ' ';  )
602*86d7f5d3SJohn Marino 		continue;
603*86d7f5d3SJohn Marino 	line1 = 0;
604*86d7f5d3SJohn Marino 	while (isdigit(c)) {
605*86d7f5d3SJohn Marino 		if (
606*86d7f5d3SJohn Marino 			LONG_MAX/10 < line1  ||
607*86d7f5d3SJohn Marino 			(t = line1 * 10,   (line1 = t + (c - '0'))  <  t)
608*86d7f5d3SJohn Marino 		)
609*86d7f5d3SJohn Marino 			diffLineNumberTooLarge(buf);
610*86d7f5d3SJohn Marino 		c = *p++;
611*86d7f5d3SJohn Marino 	}
612*86d7f5d3SJohn Marino 	while (c == ' ')
613*86d7f5d3SJohn Marino 		c = *p++;
614*86d7f5d3SJohn Marino 	nlines = 0;
615*86d7f5d3SJohn Marino 	while (isdigit(c)) {
616*86d7f5d3SJohn Marino 		if (
617*86d7f5d3SJohn Marino 			LONG_MAX/10 < nlines  ||
618*86d7f5d3SJohn Marino 			(t = nlines * 10,   (nlines = t + (c - '0'))  <  t)
619*86d7f5d3SJohn Marino 		)
620*86d7f5d3SJohn Marino 			diffLineNumberTooLarge(buf);
621*86d7f5d3SJohn Marino 		c = *p++;
622*86d7f5d3SJohn Marino 	}
623*86d7f5d3SJohn Marino 	if (c == '\r')
624*86d7f5d3SJohn Marino 		c = *p++;
625*86d7f5d3SJohn Marino 	if (c || !nlines) {
626*86d7f5d3SJohn Marino 		badDiffOutput(buf);
627*86d7f5d3SJohn Marino 	}
628*86d7f5d3SJohn Marino 	if (line1+nlines < line1)
629*86d7f5d3SJohn Marino 		diffLineNumberTooLarge(buf);
630*86d7f5d3SJohn Marino 	switch (buf[0]) {
631*86d7f5d3SJohn Marino 	    case 'a':
632*86d7f5d3SJohn Marino 		if (line1 < dc->adprev) {
633*86d7f5d3SJohn Marino 		    rcsfaterror("backward insertion in diff output: %s", buf);
634*86d7f5d3SJohn Marino 		}
635*86d7f5d3SJohn Marino 		dc->adprev = line1 + 1;
636*86d7f5d3SJohn Marino 		break;
637*86d7f5d3SJohn Marino 	    case 'd':
638*86d7f5d3SJohn Marino 		if (line1 < dc->adprev  ||  line1 < dc->dafter) {
639*86d7f5d3SJohn Marino 		    rcsfaterror("backward deletion in diff output: %s", buf);
640*86d7f5d3SJohn Marino 		}
641*86d7f5d3SJohn Marino 		dc->adprev = line1;
642*86d7f5d3SJohn Marino 		dc->dafter = line1 + nlines;
643*86d7f5d3SJohn Marino 		break;
644*86d7f5d3SJohn Marino 	    default:
645*86d7f5d3SJohn Marino 		badDiffOutput(buf);
646*86d7f5d3SJohn Marino 	}
647*86d7f5d3SJohn Marino 	if (fout) {
648*86d7f5d3SJohn Marino 		aprintf(fout, "%s\n", buf);
649*86d7f5d3SJohn Marino 	}
650*86d7f5d3SJohn Marino 	dc->line1 = line1;
651*86d7f5d3SJohn Marino 	dc->nlines = nlines;
652*86d7f5d3SJohn Marino 	return buf[0] == 'a';
653*86d7f5d3SJohn Marino }
654*86d7f5d3SJohn Marino 
655*86d7f5d3SJohn Marino 
656*86d7f5d3SJohn Marino 
657*86d7f5d3SJohn Marino #ifdef SYNTEST
658*86d7f5d3SJohn Marino 
659*86d7f5d3SJohn Marino /* Input an RCS file and print its internal data structures.  */
660*86d7f5d3SJohn Marino 
661*86d7f5d3SJohn Marino char const cmdid[] = "syntest";
662*86d7f5d3SJohn Marino 
663*86d7f5d3SJohn Marino 	int
main(argc,argv)664*86d7f5d3SJohn Marino main(argc,argv)
665*86d7f5d3SJohn Marino int argc; char * argv[];
666*86d7f5d3SJohn Marino {
667*86d7f5d3SJohn Marino 
668*86d7f5d3SJohn Marino         if (argc<2) {
669*86d7f5d3SJohn Marino 		aputs("No input file\n",stderr);
670*86d7f5d3SJohn Marino 		exitmain(EXIT_FAILURE);
671*86d7f5d3SJohn Marino         }
672*86d7f5d3SJohn Marino 	if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
673*86d7f5d3SJohn Marino 		faterror("can't open input file %s", argv[1]);
674*86d7f5d3SJohn Marino         }
675*86d7f5d3SJohn Marino         Lexinit();
676*86d7f5d3SJohn Marino         getadmin();
677*86d7f5d3SJohn Marino 	fdlock = STDOUT_FILENO;
678*86d7f5d3SJohn Marino 	putadmin();
679*86d7f5d3SJohn Marino 
680*86d7f5d3SJohn Marino         gettree();
681*86d7f5d3SJohn Marino 
682*86d7f5d3SJohn Marino         getdesc(true);
683*86d7f5d3SJohn Marino 
684*86d7f5d3SJohn Marino 	nextlex();
685*86d7f5d3SJohn Marino 
686*86d7f5d3SJohn Marino 	if (!eoflex()) {
687*86d7f5d3SJohn Marino 		fatserror("expecting EOF");
688*86d7f5d3SJohn Marino         }
689*86d7f5d3SJohn Marino 	exitmain(EXIT_SUCCESS);
690*86d7f5d3SJohn Marino }
691*86d7f5d3SJohn Marino 
exiterr()692*86d7f5d3SJohn Marino void exiterr() { _exit(EXIT_FAILURE); }
693*86d7f5d3SJohn Marino 
694*86d7f5d3SJohn Marino #endif
695