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 * Revision 5.15 1995/06/16 06:19:24 eggert
42 * Revision 5.14 1995/06/01 16:23:43 eggert
43 * (expand_names): Add "b" for -kb.
44 * (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate.
46 * Revision 5.13 1994/03/20 04:52:58 eggert
49 * Revision 5.12 1993/11/03 17:42:27 eggert
50 * Parse MKS RCS dates; ignore \r in diff control lines.
51 * Don't discard ignored phrases. Improve quality of diagnostics.
53 * Revision 5.11 1992/07/28 16:12:44 eggert
54 * Avoid `unsigned'. Statement macro names now end in _.
56 * Revision 5.10 1992/01/24 18:44:19 eggert
57 * Move put routines to rcsgen.c.
59 * Revision 5.9 1992/01/06 02:42:34 eggert
60 * ULONG_MAX/10 -> ULONG_MAX_OVER_10
61 * while (E) ; -> while (E) continue;
63 * Revision 5.8 1991/08/19 03:13:55 eggert
66 * Revision 5.7 1991/04/21 11:58:29 eggert
67 * Disambiguate names on shortname hosts.
68 * Fix errno bug. Add MS-DOS support.
70 * Revision 5.6 1991/02/28 19:18:51 eggert
71 * Fix null termination bug in reporting keyword expansion.
73 * Revision 5.5 1991/02/25 07:12:44 eggert
74 * Check diff output more carefully; avoid overflow.
76 * Revision 5.4 1990/11/01 05:28:48 eggert
77 * When ignoring unknown phrases, copy them to the output RCS file.
78 * Permit arbitrary data in logs and comment leaders.
79 * Don't check for nontext on initial checkin.
81 * Revision 5.3 1990/09/20 07:58:32 eggert
82 * Remove the test for non-text bytes; it caused more pain than it cured.
84 * Revision 5.2 1990/09/04 08:02:30 eggert
85 * Parse RCS files with no revisions.
86 * Don't strip leading white space from diff commands. Count RCS lines better.
88 * Revision 5.1 1990/08/29 07:14:06 eggert
89 * Add -kkvl. Clean old log messages too.
91 * Revision 5.0 1990/08/22 08:13:44 eggert
92 * Try to parse future RCS formats without barfing.
93 * Add -k. Don't require final newline.
94 * Remove compile-time limits; use malloc instead.
95 * Don't output branch keyword if there's no default branch,
96 * because RCS version 3 doesn't understand it.
98 * Add support for ISO 8859. Ansify and Posixate.
99 * Check that a newly checked-in file is acceptable as input to 'diff'.
100 * Check diff's output.
102 * Revision 4.6 89/05/01 15:13:32 narten
103 * changed copyright header to reflect current distribution rules
105 * Revision 4.5 88/08/09 19:13:21 eggert
106 * Allow cc -R; remove lint.
108 * Revision 4.4 87/12/18 11:46:16 narten
109 * more lint cleanups (Guy Harris)
111 * Revision 4.3 87/10/18 10:39:36 narten
112 * Updating version numbers. Changes relative to 1.1 actually relative to
115 * Revision 1.3 87/09/24 14:00:49 narten
116 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
119 * Revision 1.2 87/03/27 14:22:40 jenkins
122 * Revision 4.1 83/03/28 11:38:49 wft
123 * Added parsing and printing of default branch.
125 * Revision 3.6 83/01/15 17:46:50 wft
126 * Changed readdelta() to initialize selector and log-pointer.
127 * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
129 * Revision 3.5 82/12/08 21:58:58 wft
130 * renamed Commentleader to Commleader.
132 * Revision 3.4 82/12/04 13:24:40 wft
133 * Added routine gettree(), which updates keeplock after reading the
136 * Revision 3.3 82/11/28 21:30:11 wft
137 * Reading and printing of Suffix removed; version COMPAT2 skips the
138 * Suffix for files of release 2 format. Fixed problems with printing nil.
140 * Revision 3.2 82/10/18 21:18:25 wft
141 * renamed putdeltatext to putdtext.
143 * Revision 3.1 82/10/11 19:45:11 wft
144 * made sure getc() returns into an integer.
149 /* version COMPAT2 reads files of the format of release 2 and 3, but
150 * generates files of release 3 format. Need not be defined if no
151 * old RCS files generated with release 2 exist.
156 libId(synId, "$FreeBSD$")
158 static char const *getkeyval P((char const*,enum tokens,int));
159 static int getdelta P((void));
160 static int strn2expmode P((char const*,size_t));
161 static struct hshentry *getdnum P((void));
162 static void badDiffOutput P((char const*)) exiting;
163 static void diffLineNumberTooLarge P((char const*)) exiting;
164 static void getsemi P((char const*));
169 Kaccess[] = "access",
170 Kauthor[] = "author",
171 Kbranch[] = "branch",
172 Kcomment[] = "comment",
175 Kexpand[] = "expand",
181 Kstrict[] = "strict",
182 Ksymbols[] = "symbols",
187 Ksuffix[] = "suffix",
189 K_branches[]= "branches";
191 static struct buf Commleader;
194 struct access * AccessList;
195 struct assoc * Symbols;
196 struct rcslock *Locks;
199 struct hshentry * Head;
200 char const * Dbranch;
207 /* Get a semicolon to finish off a phrase started by KEY. */
210 fatserror("missing ';' after '%s'", key);
213 static struct hshentry *
215 /* Get a delta number. */
217 register struct hshentry *delta = getnum();
218 if (delta && countnumflds(delta->num)&1)
219 fatserror("%s isn't a delta number", delta->num);
226 /* Read an <admin> and initialize the appropriate global variables. */
228 register char const *id;
229 struct access * newaccess;
230 struct assoc * newassoc;
231 struct rcslock *newlock;
232 struct hshentry * delta;
233 struct access **LastAccess;
234 struct assoc **LastSymbol;
235 struct rcslock **LastLock;
246 if (getkeyopt(Kbranch)) {
247 if ((delta = getnum()))
248 Dbranch = delta->num;
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) {
266 LastAccess = &AccessList;
267 while ((id = getid())) {
268 newaccess = ftalloc(struct access);
269 newaccess->login = id;
270 *LastAccess = newaccess;
271 LastAccess = &newaccess->nextaccess;
277 LastSymbol = &Symbols;
278 while ((id = getid())) {
280 fatserror("missing ':' in symbolic name definition");
281 if (!(delta=getnum())) {
282 fatserror("missing number in symbolic name definition");
283 } else { /*add new pair to association list*/
284 newassoc = ftalloc(struct assoc);
286 newassoc->num = delta->num;
287 *LastSymbol = newassoc;
288 LastSymbol = &newassoc->nextassoc;
296 while ((id = getid())) {
298 fatserror("missing ':' in lock");
299 if (!(delta=getdnum())) {
300 fatserror("missing number in lock");
301 } else { /*add new pair to lock list*/
302 newlock = ftalloc(struct rcslock);
304 newlock->delta=delta;
306 LastLock = &newlock->nextlock;
312 if ((StrictLocks = getkeyopt(Kstrict)))
316 if (getkeyopt(Kcomment)) {
317 if (nexttok==STRING) {
318 Comment = savestring(&Commleader);
324 Expand = KEYVAL_EXPAND;
325 if (getkeyopt(Kexpand)) {
326 if (nexttok==STRING) {
329 if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
330 fatserror("unknown expand mode %.*s",
331 (int)cb.size, cb.string
338 Ignored = getphrases(Kdesc);
341 char const *const expand_names[] = {
342 /* These must agree with *_EXPAND in rcsbase.h. */
343 "kv", "kvl", "k", "v", "o", "b",
350 /* Yield expand mode corresponding to S, or -1 if bad. */
352 return strn2expmode(s, strlen(s));
360 char const *const *p;
362 for (p = expand_names; *p; ++p)
363 if (memcmp(*p,s,n) == 0 && !(*p)[n])
364 return p - expand_names;
373 * Ignore a series of phrases that do not start with KEY.
374 * Stop when the next phrase starts with a token that is not an identifier,
380 if (nexttok != ID || strcmp(NextString,key) == 0)
386 case SEMI: hshenter=true; break;
388 case NUM: ffree1(NextString); continue;
389 case STRING: readstring(); continue;
400 /* Function: reads a delta block.
401 * returns false if the current block does not start with a number.
404 register struct hshentry * Delta, * num;
405 struct branchhead **LastBranch, *NewBranch;
407 if (!(Delta = getdnum()))
410 hshenter = false; /*Don't enter dates into hashtable*/
411 Delta->date = getkeyval(Kdate, NUM, false);
412 hshenter=true; /*reset hshenter for revision numbers.*/
414 Delta->author = getkeyval(Kauthor, ID, false);
416 Delta->state = getkeyval(Kstate, ID, true);
419 LastBranch = &Delta->branches;
420 while ((num = getdnum())) {
421 NewBranch = ftalloc(struct branchhead);
422 NewBranch->hsh = num;
423 *LastBranch = NewBranch;
424 LastBranch = &NewBranch->nextbranch;
430 Delta->next = num = getdnum();
433 Delta->log.string = 0;
434 Delta->selector = true;
435 Delta->ig = getphrases(Kdesc);
443 /* Function: Reads in the delta tree with getdelta(), then
444 * updates the lockedby fields.
447 struct rcslock const *currlock;
453 currlock->delta->lockedby = currlock->login;
454 currlock = currlock->nextlock;
462 /* Function: read in descriptive text
463 * nexttok is not advanced afterwards.
464 * If prdesc is set, the text is printed to stdout.
470 printstring(); /*echo string*/
471 else readstring(); /*skip string*/
480 getkeyval(keyword, token, optional)
484 /* reads a pair of the form
485 * <keyword> <token> ;
486 * where token is one of <id> or <num>. optional indicates whether
487 * <token> is optional. A pointer to
488 * the actual character string of <id> or <num> is returned.
491 register char const *val = 0;
494 if (nexttok==token) {
499 fatserror("missing %s", keyword);
509 rcsfaterror("unexpected EOF in diff output");
514 register struct diffcmd *dc;
515 /* Initialize *dc suitably for getdiffcmd(). */
525 rcsfaterror("bad diff output line: %s", buf);
529 diffLineNumberTooLarge(buf)
532 rcsfaterror("diff line number too large: %s", buf);
536 getdiffcmd(finfile, delimiter, foutfile, dc)
541 /* Get a editing command output by 'diff -n' from fin.
542 * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
543 * Copy a clean version of the command to fout (if nonnull).
544 * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
545 * Store the command's line number and length into dc->line1 and dc->nlines.
546 * Keep dc->adprev and dc->dafter up to date.
554 long line1, nlines, t;
559 setupcache(fin); cache(fin);
560 cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
572 aprintf(fout, "%c%c", SDELIM, c);
578 if (buf+BUFSIZ-2 <= p) {
579 rcsfaterror("diff output command line too long");
582 cachegeteof_(c, unexpected_EOF();)
588 for (p = buf+1; (c = *p++) == ' '; )
593 LONG_MAX/10 < line1 ||
594 (t = line1 * 10, (line1 = t + (c - '0')) < t)
596 diffLineNumberTooLarge(buf);
604 LONG_MAX/10 < nlines ||
605 (t = nlines * 10, (nlines = t + (c - '0')) < t)
607 diffLineNumberTooLarge(buf);
615 if (line1+nlines < line1)
616 diffLineNumberTooLarge(buf);
619 if (line1 < dc->adprev) {
620 rcsfaterror("backward insertion in diff output: %s", buf);
622 dc->adprev = line1 + 1;
625 if (line1 < dc->adprev || line1 < dc->dafter) {
626 rcsfaterror("backward deletion in diff output: %s", buf);
629 dc->dafter = line1 + nlines;
635 aprintf(fout, "%s\n", buf);
639 return buf[0] == 'a';
646 /* Input an RCS file and print its internal data structures. */
648 char const cmdid[] = "syntest";
652 int argc; char * argv[];
656 aputs("No input file\n",stderr);
657 exitmain(EXIT_FAILURE);
659 if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
660 faterror("can't open input file %s", argv[1]);
664 fdlock = STDOUT_FILENO;
674 fatserror("expecting EOF");
676 exitmain(EXIT_SUCCESS);
679 void exiterr() { _exit(EXIT_FAILURE); }