xref: /dragonfly/gnu/usr.bin/rcs/lib/rcssyn.c (revision cd1c6085)
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.4 2007/01/21 17:58:42 pavalos 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.4 2007/01/21 17:58:42 pavalos 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
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 *
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
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
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
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
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
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 	Delta->lockedby = 0;
437 	Delta->log.string = 0;
438 	Delta->selector = true;
439 
440 	if (getkeyopt(Kcommitid)) {
441 		Delta->commitid = NextString;
442 		nextlex();
443 		getsemi(Kcommitid);
444         } else {
445 		Delta->commitid = NULL;
446 	}
447 
448 	Delta->ig = getphrases(Kdesc);
449         TotalDeltas++;
450         return (true);
451 }
452 
453 
454 	void
455 gettree()
456 /* Function: Reads in the delta tree with getdelta(), then
457  * updates the lockedby fields.
458  */
459 {
460 	struct rcslock const *currlock;
461 
462 	while (getdelta())
463 		continue;
464         currlock=Locks;
465         while (currlock) {
466                 currlock->delta->lockedby = currlock->login;
467                 currlock = currlock->nextlock;
468         }
469 }
470 
471 
472 	void
473 getdesc(prdesc)
474 int  prdesc;
475 /* Function: read in descriptive text
476  * nexttok is not advanced afterwards.
477  * If prdesc is set, the text is printed to stdout.
478  */
479 {
480 
481 	getkeystring(Kdesc);
482         if (prdesc)
483                 printstring();  /*echo string*/
484         else    readstring();   /*skip string*/
485 }
486 
487 
488 
489 
490 
491 
492 	static char const *
493 getkeyval(keyword, token, optional)
494 	char const *keyword;
495 	enum tokens token;
496 	int optional;
497 /* reads a pair of the form
498  * <keyword> <token> ;
499  * where token is one of <id> or <num>. optional indicates whether
500  * <token> is optional. A pointer to
501  * the actual character string of <id> or <num> is returned.
502  */
503 {
504 	register char const *val = 0;
505 
506 	getkey(keyword);
507         if (nexttok==token) {
508                 val = NextString;
509                 nextlex();
510         } else {
511 		if (!optional)
512 			fatserror("missing %s", keyword);
513         }
514 	getsemi(keyword);
515         return(val);
516 }
517 
518 
519 	void
520 unexpected_EOF()
521 {
522 	rcsfaterror("unexpected EOF in diff output");
523 }
524 
525 	void
526 initdiffcmd(dc)
527 	register struct diffcmd *dc;
528 /* Initialize *dc suitably for getdiffcmd(). */
529 {
530 	dc->adprev = 0;
531 	dc->dafter = 0;
532 }
533 
534 	static void
535 badDiffOutput(buf)
536 	char const *buf;
537 {
538 	rcsfaterror("bad diff output line: %s", buf);
539 }
540 
541 	static void
542 diffLineNumberTooLarge(buf)
543 	char const *buf;
544 {
545 	rcsfaterror("diff line number too large: %s", buf);
546 }
547 
548 	int
549 getdiffcmd(finfile, delimiter, foutfile, dc)
550 	RILE *finfile;
551 	FILE *foutfile;
552 	int delimiter;
553 	struct diffcmd *dc;
554 /* Get a editing command output by 'diff -n' from fin.
555  * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
556  * Copy a clean version of the command to fout (if nonnull).
557  * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
558  * Store the command's line number and length into dc->line1 and dc->nlines.
559  * Keep dc->adprev and dc->dafter up to date.
560  */
561 {
562 	register int c;
563 	declarecache;
564 	register FILE *fout;
565 	register char *p;
566 	register RILE *fin;
567 	long line1, nlines, t;
568 	char buf[BUFSIZ];
569 
570 	fin = finfile;
571 	fout = foutfile;
572 	setupcache(fin); cache(fin);
573 	cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
574 	if (delimiter) {
575 		if (c==SDELIM) {
576 			cacheget_(c)
577 			if (c==SDELIM) {
578 				buf[0] = c;
579 				buf[1] = 0;
580 				badDiffOutput(buf);
581 			}
582 			uncache(fin);
583 			nextc = c;
584 			if (fout)
585 				aprintf(fout, "%c%c", SDELIM, c);
586 			return -1;
587 		}
588 	}
589 	p = buf;
590 	do {
591 		if (buf+BUFSIZ-2 <= p) {
592 			rcsfaterror("diff output command line too long");
593 		}
594 		*p++ = c;
595 		cachegeteof_(c, unexpected_EOF();)
596 	} while (c != '\n');
597 	uncache(fin);
598 	if (delimiter)
599 		++rcsline;
600 	*p = '\0';
601 	for (p = buf+1;  (c = *p++) == ' ';  )
602 		continue;
603 	line1 = 0;
604 	while (isdigit(c)) {
605 		if (
606 			LONG_MAX/10 < line1  ||
607 			(t = line1 * 10,   (line1 = t + (c - '0'))  <  t)
608 		)
609 			diffLineNumberTooLarge(buf);
610 		c = *p++;
611 	}
612 	while (c == ' ')
613 		c = *p++;
614 	nlines = 0;
615 	while (isdigit(c)) {
616 		if (
617 			LONG_MAX/10 < nlines  ||
618 			(t = nlines * 10,   (nlines = t + (c - '0'))  <  t)
619 		)
620 			diffLineNumberTooLarge(buf);
621 		c = *p++;
622 	}
623 	if (c == '\r')
624 		c = *p++;
625 	if (c || !nlines) {
626 		badDiffOutput(buf);
627 	}
628 	if (line1+nlines < line1)
629 		diffLineNumberTooLarge(buf);
630 	switch (buf[0]) {
631 	    case 'a':
632 		if (line1 < dc->adprev) {
633 		    rcsfaterror("backward insertion in diff output: %s", buf);
634 		}
635 		dc->adprev = line1 + 1;
636 		break;
637 	    case 'd':
638 		if (line1 < dc->adprev  ||  line1 < dc->dafter) {
639 		    rcsfaterror("backward deletion in diff output: %s", buf);
640 		}
641 		dc->adprev = line1;
642 		dc->dafter = line1 + nlines;
643 		break;
644 	    default:
645 		badDiffOutput(buf);
646 	}
647 	if (fout) {
648 		aprintf(fout, "%s\n", buf);
649 	}
650 	dc->line1 = line1;
651 	dc->nlines = nlines;
652 	return buf[0] == 'a';
653 }
654 
655 
656 
657 #ifdef SYNTEST
658 
659 /* Input an RCS file and print its internal data structures.  */
660 
661 char const cmdid[] = "syntest";
662 
663 	int
664 main(argc,argv)
665 int argc; char * argv[];
666 {
667 
668         if (argc<2) {
669 		aputs("No input file\n",stderr);
670 		exitmain(EXIT_FAILURE);
671         }
672 	if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
673 		faterror("can't open input file %s", argv[1]);
674         }
675         Lexinit();
676         getadmin();
677 	fdlock = STDOUT_FILENO;
678 	putadmin();
679 
680         gettree();
681 
682         getdesc(true);
683 
684 	nextlex();
685 
686 	if (!eoflex()) {
687 		fatserror("expecting EOF");
688         }
689 	exitmain(EXIT_SUCCESS);
690 }
691 
692 void exiterr() { _exit(EXIT_FAILURE); }
693 
694 #endif
695