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