xref: /dragonfly/gnu/usr.bin/rcs/lib/rcssyn.c (revision b40e316c)
1 /* RCS file syntactic analysis */
2 
3 /******************************************************************************
4  *                       Syntax Analysis.
5  *                       Keyword table
6  *                       Testprogram: define SYNTEST
7  *                       Compatibility with Release 2: define COMPAT2=1
8  ******************************************************************************
9  */
10 
11 /* Copyright 1982, 1988, 1989 Walter Tichy
12    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
13    Distributed under license by the Free Software Foundation, Inc.
14 
15 This file is part of RCS.
16 
17 RCS is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation; either version 2, or (at your option)
20 any later version.
21 
22 RCS is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 GNU General Public License for more details.
26 
27 You should have received a copy of the GNU General Public License
28 along with RCS; see the file COPYING.
29 If not, write to the Free Software Foundation,
30 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 
32 Report problems and direct all questions to:
33 
34     rcs-bugs@cs.purdue.edu
35 
36 */
37 
38 /*
39  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.7 1999/08/27 23:36:48 peter Exp $
40  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
41  *
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, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.2 2003/06/17 04:25:47 dillon 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 	Kdate[]     = "date",
177 	Kdesc[]     = "desc",
178 	Kexpand[]   = "expand",
179 	Khead[]     = "head",
180 	Klocks[]    = "locks",
181 	Klog[]      = "log",
182 	Knext[]     = "next",
183 	Kstate[]    = "state",
184 	Kstrict[]   = "strict",
185 	Ksymbols[]  = "symbols",
186 	Ktext[]     = "text";
187 
188 static char const
189 #if COMPAT2
190 	Ksuffix[]   = "suffix",
191 #endif
192 	K_branches[]= "branches";
193 
194 static struct buf Commleader;
195 struct cbuf Comment;
196 struct cbuf Ignored;
197 struct access   * AccessList;
198 struct assoc    * Symbols;
199 struct rcslock *Locks;
200 int		  Expand;
201 int               StrictLocks;
202 struct hshentry * Head;
203 char const      * Dbranch;
204 int TotalDeltas;
205 
206 
207 	static void
208 getsemi(key)
209 	char const *key;
210 /* Get a semicolon to finish off a phrase started by KEY.  */
211 {
212 	if (!getlex(SEMI))
213 		fatserror("missing ';' after '%s'", key);
214 }
215 
216 	static struct hshentry *
217 getdnum()
218 /* Get a delta number.  */
219 {
220 	register struct hshentry *delta = getnum();
221 	if (delta && countnumflds(delta->num)&1)
222 		fatserror("%s isn't a delta number", delta->num);
223 	return delta;
224 }
225 
226 
227 	void
228 getadmin()
229 /* Read an <admin> and initialize the appropriate global variables.  */
230 {
231 	register char const *id;
232         struct access   * newaccess;
233         struct assoc    * newassoc;
234 	struct rcslock *newlock;
235         struct hshentry * delta;
236 	struct access **LastAccess;
237 	struct assoc **LastSymbol;
238 	struct rcslock **LastLock;
239 	struct buf b;
240 	struct cbuf cb;
241 
242         TotalDeltas=0;
243 
244 	getkey(Khead);
245 	Head = getdnum();
246 	getsemi(Khead);
247 
248 	Dbranch = 0;
249 	if (getkeyopt(Kbranch)) {
250 		if ((delta = getnum()))
251 			Dbranch = delta->num;
252 		getsemi(Kbranch);
253         }
254 
255 
256 #if COMPAT2
257         /* read suffix. Only in release 2 format */
258 	if (getkeyopt(Ksuffix)) {
259                 if (nexttok==STRING) {
260 			readstring(); nextlex(); /* Throw away the suffix.  */
261 		} else if (nexttok==ID) {
262                         nextlex();
263                 }
264 		getsemi(Ksuffix);
265         }
266 #endif
267 
268 	getkey(Kaccess);
269 	LastAccess = &AccessList;
270 	while ((id = getid())) {
271 		newaccess = ftalloc(struct access);
272                 newaccess->login = id;
273 		*LastAccess = newaccess;
274 		LastAccess = &newaccess->nextaccess;
275         }
276 	*LastAccess = 0;
277 	getsemi(Kaccess);
278 
279 	getkey(Ksymbols);
280 	LastSymbol = &Symbols;
281         while ((id = getid())) {
282                 if (!getlex(COLON))
283 			fatserror("missing ':' in symbolic name definition");
284                 if (!(delta=getnum())) {
285 			fatserror("missing number in symbolic name definition");
286                 } else { /*add new pair to association list*/
287 			newassoc = ftalloc(struct assoc);
288                         newassoc->symbol=id;
289 			newassoc->num = delta->num;
290 			*LastSymbol = newassoc;
291 			LastSymbol = &newassoc->nextassoc;
292                 }
293         }
294 	*LastSymbol = 0;
295 	getsemi(Ksymbols);
296 
297 	getkey(Klocks);
298 	LastLock = &Locks;
299         while ((id = getid())) {
300                 if (!getlex(COLON))
301 			fatserror("missing ':' in lock");
302 		if (!(delta=getdnum())) {
303 			fatserror("missing number in lock");
304                 } else { /*add new pair to lock list*/
305 			newlock = ftalloc(struct rcslock);
306                         newlock->login=id;
307                         newlock->delta=delta;
308 			*LastLock = newlock;
309 			LastLock = &newlock->nextlock;
310                 }
311         }
312 	*LastLock = 0;
313 	getsemi(Klocks);
314 
315 	if ((StrictLocks = getkeyopt(Kstrict)))
316 		getsemi(Kstrict);
317 
318 	clear_buf(&Comment);
319 	if (getkeyopt(Kcomment)) {
320 		if (nexttok==STRING) {
321 			Comment = savestring(&Commleader);
322 			nextlex();
323 		}
324 		getsemi(Kcomment);
325         }
326 
327 	Expand = KEYVAL_EXPAND;
328 	if (getkeyopt(Kexpand)) {
329 		if (nexttok==STRING) {
330 			bufautobegin(&b);
331 			cb = savestring(&b);
332 			if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
333 			    fatserror("unknown expand mode %.*s",
334 				(int)cb.size, cb.string
335 			    );
336 			bufautoend(&b);
337 			nextlex();
338 		}
339 		getsemi(Kexpand);
340         }
341 	Ignored = getphrases(Kdesc);
342 }
343 
344 char const *const expand_names[] = {
345 	/* These must agree with *_EXPAND in rcsbase.h.  */
346 	"kv", "kvl", "k", "v", "o", "b",
347 	0
348 };
349 
350 	int
351 str2expmode(s)
352 	char const *s;
353 /* Yield expand mode corresponding to S, or -1 if bad.  */
354 {
355 	return strn2expmode(s, strlen(s));
356 }
357 
358 	static int
359 strn2expmode(s, n)
360 	char const *s;
361 	size_t n;
362 {
363 	char const *const *p;
364 
365 	for (p = expand_names;  *p;  ++p)
366 		if (memcmp(*p,s,n) == 0  &&  !(*p)[n])
367 			return p - expand_names;
368 	return -1;
369 }
370 
371 
372 	void
373 ignorephrases(key)
374 	const char *key;
375 /*
376 * Ignore a series of phrases that do not start with KEY.
377 * Stop when the next phrase starts with a token that is not an identifier,
378 * or is KEY.
379 */
380 {
381 	for (;;) {
382 		nextlex();
383 		if (nexttok != ID  ||  strcmp(NextString,key) == 0)
384 			break;
385 		warnignore();
386 		hshenter=false;
387 		for (;; nextlex()) {
388 			switch (nexttok) {
389 				case SEMI: hshenter=true; break;
390 				case ID:
391 				case NUM: ffree1(NextString); continue;
392 				case STRING: readstring(); continue;
393 				default: continue;
394 			}
395 			break;
396 		}
397 	}
398 }
399 
400 
401 	static int
402 getdelta()
403 /* Function: reads a delta block.
404  * returns false if the current block does not start with a number.
405  */
406 {
407         register struct hshentry * Delta, * num;
408 	struct branchhead **LastBranch, *NewBranch;
409 
410 	if (!(Delta = getdnum()))
411 		return false;
412 
413         hshenter = false; /*Don't enter dates into hashtable*/
414 	Delta->date = getkeyval(Kdate, NUM, false);
415         hshenter=true;    /*reset hshenter for revision numbers.*/
416 
417         Delta->author = getkeyval(Kauthor, ID, false);
418 
419         Delta->state = getkeyval(Kstate, ID, true);
420 
421 	getkey(K_branches);
422 	LastBranch = &Delta->branches;
423 	while ((num = getdnum())) {
424 		NewBranch = ftalloc(struct branchhead);
425                 NewBranch->hsh = num;
426 		*LastBranch = NewBranch;
427 		LastBranch = &NewBranch->nextbranch;
428         }
429 	*LastBranch = 0;
430 	getsemi(K_branches);
431 
432 	getkey(Knext);
433 	Delta->next = num = getdnum();
434 	getsemi(Knext);
435 	Delta->lockedby = 0;
436 	Delta->log.string = 0;
437 	Delta->selector = true;
438 	Delta->ig = getphrases(Kdesc);
439         TotalDeltas++;
440         return (true);
441 }
442 
443 
444 	void
445 gettree()
446 /* Function: Reads in the delta tree with getdelta(), then
447  * updates the lockedby fields.
448  */
449 {
450 	struct rcslock const *currlock;
451 
452 	while (getdelta())
453 		continue;
454         currlock=Locks;
455         while (currlock) {
456                 currlock->delta->lockedby = currlock->login;
457                 currlock = currlock->nextlock;
458         }
459 }
460 
461 
462 	void
463 getdesc(prdesc)
464 int  prdesc;
465 /* Function: read in descriptive text
466  * nexttok is not advanced afterwards.
467  * If prdesc is set, the text is printed to stdout.
468  */
469 {
470 
471 	getkeystring(Kdesc);
472         if (prdesc)
473                 printstring();  /*echo string*/
474         else    readstring();   /*skip string*/
475 }
476 
477 
478 
479 
480 
481 
482 	static char const *
483 getkeyval(keyword, token, optional)
484 	char const *keyword;
485 	enum tokens token;
486 	int optional;
487 /* reads a pair of the form
488  * <keyword> <token> ;
489  * where token is one of <id> or <num>. optional indicates whether
490  * <token> is optional. A pointer to
491  * the actual character string of <id> or <num> is returned.
492  */
493 {
494 	register char const *val = 0;
495 
496 	getkey(keyword);
497         if (nexttok==token) {
498                 val = NextString;
499                 nextlex();
500         } else {
501 		if (!optional)
502 			fatserror("missing %s", keyword);
503         }
504 	getsemi(keyword);
505         return(val);
506 }
507 
508 
509 	void
510 unexpected_EOF()
511 {
512 	rcsfaterror("unexpected EOF in diff output");
513 }
514 
515 	void
516 initdiffcmd(dc)
517 	register struct diffcmd *dc;
518 /* Initialize *dc suitably for getdiffcmd(). */
519 {
520 	dc->adprev = 0;
521 	dc->dafter = 0;
522 }
523 
524 	static void
525 badDiffOutput(buf)
526 	char const *buf;
527 {
528 	rcsfaterror("bad diff output line: %s", buf);
529 }
530 
531 	static void
532 diffLineNumberTooLarge(buf)
533 	char const *buf;
534 {
535 	rcsfaterror("diff line number too large: %s", buf);
536 }
537 
538 	int
539 getdiffcmd(finfile, delimiter, foutfile, dc)
540 	RILE *finfile;
541 	FILE *foutfile;
542 	int delimiter;
543 	struct diffcmd *dc;
544 /* Get a editing command output by 'diff -n' from fin.
545  * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
546  * Copy a clean version of the command to fout (if nonnull).
547  * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
548  * Store the command's line number and length into dc->line1 and dc->nlines.
549  * Keep dc->adprev and dc->dafter up to date.
550  */
551 {
552 	register int c;
553 	declarecache;
554 	register FILE *fout;
555 	register char *p;
556 	register RILE *fin;
557 	long line1, nlines, t;
558 	char buf[BUFSIZ];
559 
560 	fin = finfile;
561 	fout = foutfile;
562 	setupcache(fin); cache(fin);
563 	cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
564 	if (delimiter) {
565 		if (c==SDELIM) {
566 			cacheget_(c)
567 			if (c==SDELIM) {
568 				buf[0] = c;
569 				buf[1] = 0;
570 				badDiffOutput(buf);
571 			}
572 			uncache(fin);
573 			nextc = c;
574 			if (fout)
575 				aprintf(fout, "%c%c", SDELIM, c);
576 			return -1;
577 		}
578 	}
579 	p = buf;
580 	do {
581 		if (buf+BUFSIZ-2 <= p) {
582 			rcsfaterror("diff output command line too long");
583 		}
584 		*p++ = c;
585 		cachegeteof_(c, unexpected_EOF();)
586 	} while (c != '\n');
587 	uncache(fin);
588 	if (delimiter)
589 		++rcsline;
590 	*p = '\0';
591 	for (p = buf+1;  (c = *p++) == ' ';  )
592 		continue;
593 	line1 = 0;
594 	while (isdigit(c)) {
595 		if (
596 			LONG_MAX/10 < line1  ||
597 			(t = line1 * 10,   (line1 = t + (c - '0'))  <  t)
598 		)
599 			diffLineNumberTooLarge(buf);
600 		c = *p++;
601 	}
602 	while (c == ' ')
603 		c = *p++;
604 	nlines = 0;
605 	while (isdigit(c)) {
606 		if (
607 			LONG_MAX/10 < nlines  ||
608 			(t = nlines * 10,   (nlines = t + (c - '0'))  <  t)
609 		)
610 			diffLineNumberTooLarge(buf);
611 		c = *p++;
612 	}
613 	if (c == '\r')
614 		c = *p++;
615 	if (c || !nlines) {
616 		badDiffOutput(buf);
617 	}
618 	if (line1+nlines < line1)
619 		diffLineNumberTooLarge(buf);
620 	switch (buf[0]) {
621 	    case 'a':
622 		if (line1 < dc->adprev) {
623 		    rcsfaterror("backward insertion in diff output: %s", buf);
624 		}
625 		dc->adprev = line1 + 1;
626 		break;
627 	    case 'd':
628 		if (line1 < dc->adprev  ||  line1 < dc->dafter) {
629 		    rcsfaterror("backward deletion in diff output: %s", buf);
630 		}
631 		dc->adprev = line1;
632 		dc->dafter = line1 + nlines;
633 		break;
634 	    default:
635 		badDiffOutput(buf);
636 	}
637 	if (fout) {
638 		aprintf(fout, "%s\n", buf);
639 	}
640 	dc->line1 = line1;
641 	dc->nlines = nlines;
642 	return buf[0] == 'a';
643 }
644 
645 
646 
647 #ifdef SYNTEST
648 
649 /* Input an RCS file and print its internal data structures.  */
650 
651 char const cmdid[] = "syntest";
652 
653 	int
654 main(argc,argv)
655 int argc; char * argv[];
656 {
657 
658         if (argc<2) {
659 		aputs("No input file\n",stderr);
660 		exitmain(EXIT_FAILURE);
661         }
662 	if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
663 		faterror("can't open input file %s", argv[1]);
664         }
665         Lexinit();
666         getadmin();
667 	fdlock = STDOUT_FILENO;
668 	putadmin();
669 
670         gettree();
671 
672         getdesc(true);
673 
674 	nextlex();
675 
676 	if (!eoflex()) {
677 		fatserror("expecting EOF");
678         }
679 	exitmain(EXIT_SUCCESS);
680 }
681 
682 void exiterr() { _exit(EXIT_FAILURE); }
683 
684 #endif
685