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