1 /* RCS file syntactic analysis */
3 /******************************************************************************
6 * Testprogram: define SYNTEST
7 * Compatibility with Release 2: define COMPAT2=1
8 ******************************************************************************
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.
15 This file is part of RCS.
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)
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.
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.
32 Report problems and direct all questions to:
34 rcs-bugs@cs.purdue.edu
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 $
42 * Revision 5.15 1995/06/16 06:19:24 eggert
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.
49 * Revision 5.13 1994/03/20 04:52:58 eggert
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.
56 * Revision 5.11 1992/07/28 16:12:44 eggert
57 * Avoid `unsigned'. Statement macro names now end in _.
59 * Revision 5.10 1992/01/24 18:44:19 eggert
60 * Move put routines to rcsgen.c.
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;
66 * Revision 5.8 1991/08/19 03:13:55 eggert
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.
73 * Revision 5.6 1991/02/28 19:18:51 eggert
74 * Fix null termination bug in reporting keyword expansion.
76 * Revision 5.5 1991/02/25 07:12:44 eggert
77 * Check diff output more carefully; avoid overflow.
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.
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.
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.
91 * Revision 5.1 1990/08/29 07:14:06 eggert
92 * Add -kkvl. Clean old log messages too.
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.
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.
105 * Revision 4.6 89/05/01 15:13:32 narten
106 * changed copyright header to reflect current distribution rules
108 * Revision 4.5 88/08/09 19:13:21 eggert
109 * Allow cc -R; remove lint.
111 * Revision 4.4 87/12/18 11:46:16 narten
112 * more lint cleanups (Guy Harris)
114 * Revision 4.3 87/10/18 10:39:36 narten
115 * Updating version numbers. Changes relative to 1.1 actually relative to
118 * Revision 1.3 87/09/24 14:00:49 narten
119 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
122 * Revision 1.2 87/03/27 14:22:40 jenkins
125 * Revision 4.1 83/03/28 11:38:49 wft
126 * Added parsing and printing of default branch.
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.
132 * Revision 3.5 82/12/08 21:58:58 wft
133 * renamed Commentleader to Commleader.
135 * Revision 3.4 82/12/04 13:24:40 wft
136 * Added routine gettree(), which updates keeplock after reading the
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.
143 * Revision 3.2 82/10/18 21:18:25 wft
144 * renamed putdeltatext to putdtext.
146 * Revision 3.1 82/10/11 19:45:11 wft
147 * made sure getc() returns into an integer.
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.
159 libId(synId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.4 2007/01/21 17:58:42 pavalos Exp $")
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*));
172 Kaccess[] = "access",
173 Kauthor[] = "author",
174 Kbranch[] = "branch",
175 Kcomment[] = "comment",
176 Kcommitid[] = "commitid",
179 Kexpand[] = "expand",
185 Kstrict[] = "strict",
186 Ksymbols[] = "symbols",
191 Ksuffix[] = "suffix",
193 K_branches[]= "branches";
195 static struct buf Commleader;
198 struct access * AccessList;
199 struct assoc * Symbols;
200 struct rcslock *Locks;
203 struct hshentry * Head;
204 char const * Dbranch;
211 /* Get a semicolon to finish off a phrase started by KEY. */
214 fatserror("missing ';' after '%s'", key);
217 static struct hshentry *
219 /* Get a delta number. */
221 register struct hshentry *delta = getnum();
222 if (delta && countnumflds(delta->num)&1)
223 fatserror("%s isn't a delta number", delta->num);
230 /* Read an <admin> and initialize the appropriate global variables. */
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;
250 if (getkeyopt(Kbranch)) {
251 if ((delta = getnum()))
252 Dbranch = delta->num;
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) {
270 LastAccess = &AccessList;
271 while ((id = getid())) {
272 newaccess = ftalloc(struct access);
273 newaccess->login = id;
274 *LastAccess = newaccess;
275 LastAccess = &newaccess->nextaccess;
281 LastSymbol = &Symbols;
282 while ((id = getid())) {
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);
290 newassoc->num = delta->num;
291 *LastSymbol = newassoc;
292 LastSymbol = &newassoc->nextassoc;
300 while ((id = getid())) {
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);
308 newlock->delta=delta;
310 LastLock = &newlock->nextlock;
316 if ((StrictLocks = getkeyopt(Kstrict)))
320 if (getkeyopt(Kcomment)) {
321 if (nexttok==STRING) {
322 Comment = savestring(&Commleader);
328 Expand = KEYVAL_EXPAND;
329 if (getkeyopt(Kexpand)) {
330 if (nexttok==STRING) {
333 if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
334 fatserror("unknown expand mode %.*s",
335 (int)cb.size, cb.string
342 Ignored = getphrases(Kdesc);
345 char const *const expand_names[] = {
346 /* These must agree with *_EXPAND in rcsbase.h. */
347 "kv", "kvl", "k", "v", "o", "b",
354 /* Yield expand mode corresponding to S, or -1 if bad. */
356 return strn2expmode(s, strlen(s));
364 char const *const *p;
366 for (p = expand_names; *p; ++p)
367 if (memcmp(*p,s,n) == 0 && !(*p)[n])
368 return p - expand_names;
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,
384 if (nexttok != ID || strcmp(NextString,key) == 0)
390 case SEMI: hshenter=true; break;
392 case NUM: ffree1(NextString); continue;
393 case STRING: readstring(); continue;
404 /* Function: reads a delta block.
405 * returns false if the current block does not start with a number.
408 register struct hshentry * Delta, * num;
409 struct branchhead **LastBranch, *NewBranch;
411 if (!(Delta = getdnum()))
414 hshenter = false; /*Don't enter dates into hashtable*/
415 Delta->date = getkeyval(Kdate, NUM, false);
416 hshenter=true; /*reset hshenter for revision numbers.*/
418 Delta->author = getkeyval(Kauthor, ID, false);
420 Delta->state = getkeyval(Kstate, ID, true);
423 LastBranch = &Delta->branches;
424 while ((num = getdnum())) {
425 NewBranch = ftalloc(struct branchhead);
426 NewBranch->hsh = num;
427 *LastBranch = NewBranch;
428 LastBranch = &NewBranch->nextbranch;
434 Delta->next = num = getdnum();
437 Delta->log.string = 0;
438 Delta->selector = true;
440 if (getkeyopt(Kcommitid)) {
441 Delta->commitid = NextString;
445 Delta->commitid = NULL;
448 Delta->ig = getphrases(Kdesc);
456 /* Function: Reads in the delta tree with getdelta(), then
457 * updates the lockedby fields.
460 struct rcslock const *currlock;
466 currlock->delta->lockedby = currlock->login;
467 currlock = currlock->nextlock;
475 /* Function: read in descriptive text
476 * nexttok is not advanced afterwards.
477 * If prdesc is set, the text is printed to stdout.
483 printstring(); /*echo string*/
484 else readstring(); /*skip string*/
493 getkeyval(keyword, token, 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.
504 register char const *val = 0;
507 if (nexttok==token) {
512 fatserror("missing %s", keyword);
522 rcsfaterror("unexpected EOF in diff output");
527 register struct diffcmd *dc;
528 /* Initialize *dc suitably for getdiffcmd(). */
538 rcsfaterror("bad diff output line: %s", buf);
542 diffLineNumberTooLarge(buf)
545 rcsfaterror("diff line number too large: %s", buf);
549 getdiffcmd(finfile, delimiter, foutfile, 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.
567 long line1, nlines, t;
572 setupcache(fin); cache(fin);
573 cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
585 aprintf(fout, "%c%c", SDELIM, c);
591 if (buf+BUFSIZ-2 <= p) {
592 rcsfaterror("diff output command line too long");
595 cachegeteof_(c, unexpected_EOF();)
601 for (p = buf+1; (c = *p++) == ' '; )
606 LONG_MAX/10 < line1 ||
607 (t = line1 * 10, (line1 = t + (c - '0')) < t)
609 diffLineNumberTooLarge(buf);
617 LONG_MAX/10 < nlines ||
618 (t = nlines * 10, (nlines = t + (c - '0')) < t)
620 diffLineNumberTooLarge(buf);
628 if (line1+nlines < line1)
629 diffLineNumberTooLarge(buf);
632 if (line1 < dc->adprev) {
633 rcsfaterror("backward insertion in diff output: %s", buf);
635 dc->adprev = line1 + 1;
638 if (line1 < dc->adprev || line1 < dc->dafter) {
639 rcsfaterror("backward deletion in diff output: %s", buf);
642 dc->dafter = line1 + nlines;
648 aprintf(fout, "%s\n", buf);
652 return buf[0] == 'a';
659 /* Input an RCS file and print its internal data structures. */
661 char const cmdid[] = "syntest";
665 int argc; char * argv[];
669 aputs("No input file\n",stderr);
670 exitmain(EXIT_FAILURE);
672 if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
673 faterror("can't open input file %s", argv[1]);
677 fdlock = STDOUT_FILENO;
687 fatserror("expecting EOF");
689 exitmain(EXIT_SUCCESS);
692 void exiterr() { _exit(EXIT_FAILURE); }