1 /* $NetBSD: rcssyn.c,v 1.1.1.2 1996/10/13 21:56:52 veego Exp $ */
3 /* RCS file syntactic analysis */
5 /******************************************************************************
8 * Testprogram: define SYNTEST
9 * Compatibility with Release 2: define COMPAT2=1
10 ******************************************************************************
13 /* Copyright 1982, 1988, 1989 Walter Tichy
14 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
15 Distributed under license by the Free Software Foundation, Inc.
17 This file is part of RCS.
19 RCS is free software; you can redistribute it and/or modify
20 it under the terms of the GNU General Public License as published by
21 the Free Software Foundation; either version 2, or (at your option)
24 RCS is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with RCS; see the file COPYING.
31 If not, write to the Free Software Foundation,
32 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 Report problems and direct all questions to:
36 rcs-bugs@cs.purdue.edu
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
, "Id: rcssyn.c,v 5.15 1995/06/16 06:19:24 eggert 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",
178 Kexpand
[] = "expand",
184 Kstrict
[] = "strict",
185 Ksymbols
[] = "symbols",
190 Ksuffix
[] = "suffix",
192 K_branches
[]= "branches";
194 static struct buf Commleader
;
197 struct access
* AccessList
;
198 struct assoc
* Symbols
;
199 struct rcslock
*Locks
;
202 struct hshentry
* Head
;
203 char const * Dbranch
;
210 /* Get a semicolon to finish off a phrase started by KEY. */
213 fatserror("missing ';' after '%s'", key
);
216 static struct hshentry
*
218 /* Get a delta number. */
220 register struct hshentry
*delta
= getnum();
221 if (delta
&& countnumflds(delta
->num
)&1)
222 fatserror("%s isn't a delta number", delta
->num
);
229 /* Read an <admin> and initialize the appropriate global variables. */
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
;
249 if (getkeyopt(Kbranch
)) {
250 if ((delta
= getnum()))
251 Dbranch
= delta
->num
;
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
) {
269 LastAccess
= &AccessList
;
270 while ((id
= getid())) {
271 newaccess
= ftalloc(struct access
);
272 newaccess
->login
= id
;
273 *LastAccess
= newaccess
;
274 LastAccess
= &newaccess
->nextaccess
;
280 LastSymbol
= &Symbols
;
281 while ((id
= getid())) {
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
);
289 newassoc
->num
= delta
->num
;
290 *LastSymbol
= newassoc
;
291 LastSymbol
= &newassoc
->nextassoc
;
299 while ((id
= getid())) {
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
);
307 newlock
->delta
=delta
;
309 LastLock
= &newlock
->nextlock
;
315 if ((StrictLocks
= getkeyopt(Kstrict
)))
319 if (getkeyopt(Kcomment
)) {
320 if (nexttok
==STRING
) {
321 Comment
= savestring(&Commleader
);
327 Expand
= KEYVAL_EXPAND
;
328 if (getkeyopt(Kexpand
)) {
329 if (nexttok
==STRING
) {
332 if ((Expand
= strn2expmode(cb
.string
,cb
.size
)) < 0)
333 fatserror("unknown expand mode %.*s",
334 (int)cb
.size
, cb
.string
341 Ignored
= getphrases(Kdesc
);
344 char const *const expand_names
[] = {
345 /* These must agree with *_EXPAND in rcsbase.h. */
346 "kv", "kvl", "k", "v", "o", "b",
353 /* Yield expand mode corresponding to S, or -1 if bad. */
355 return strn2expmode(s
, strlen(s
));
363 char const *const *p
;
365 for (p
= expand_names
; *p
; ++p
)
366 if (memcmp(*p
,s
,n
) == 0 && !(*p
)[n
])
367 return p
- expand_names
;
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,
383 if (nexttok
!= ID
|| strcmp(NextString
,key
) == 0)
389 case SEMI
: hshenter
=true; break;
391 case NUM
: ffree1(NextString
); continue;
392 case STRING
: readstring(); continue;
403 /* Function: reads a delta block.
404 * returns false if the current block does not start with a number.
407 register struct hshentry
* Delta
, * num
;
408 struct branchhead
**LastBranch
, *NewBranch
;
410 if (!(Delta
= getdnum()))
413 hshenter
= false; /*Don't enter dates into hashtable*/
414 Delta
->date
= getkeyval(Kdate
, NUM
, false);
415 hshenter
=true; /*reset hshenter for revision numbers.*/
417 Delta
->author
= getkeyval(Kauthor
, ID
, false);
419 Delta
->state
= getkeyval(Kstate
, ID
, true);
422 LastBranch
= &Delta
->branches
;
423 while ((num
= getdnum())) {
424 NewBranch
= ftalloc(struct branchhead
);
425 NewBranch
->hsh
= num
;
426 *LastBranch
= NewBranch
;
427 LastBranch
= &NewBranch
->nextbranch
;
433 Delta
->next
= num
= getdnum();
436 Delta
->log
.string
= 0;
437 Delta
->selector
= true;
438 Delta
->ig
= getphrases(Kdesc
);
446 /* Function: Reads in the delta tree with getdelta(), then
447 * updates the lockedby fields.
450 struct rcslock
const *currlock
;
456 currlock
->delta
->lockedby
= currlock
->login
;
457 currlock
= currlock
->nextlock
;
465 /* Function: read in descriptive text
466 * nexttok is not advanced afterwards.
467 * If prdesc is set, the text is printed to stdout.
473 printstring(); /*echo string*/
474 else readstring(); /*skip string*/
483 getkeyval(keyword
, token
, 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.
494 register char const *val
= 0;
497 if (nexttok
==token
) {
502 fatserror("missing %s", keyword
);
512 rcsfaterror("unexpected EOF in diff output");
517 register struct diffcmd
*dc
;
518 /* Initialize *dc suitably for getdiffcmd(). */
528 rcsfaterror("bad diff output line: %s", buf
);
532 diffLineNumberTooLarge(buf
)
535 rcsfaterror("diff line number too large: %s", buf
);
539 getdiffcmd(finfile
, delimiter
, foutfile
, 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.
557 long line1
, nlines
, t
;
562 setupcache(fin
); cache(fin
);
563 cachegeteof_(c
, { if (delimiter
) unexpected_EOF(); return -1; } )
575 aprintf(fout
, "%c%c", SDELIM
, c
);
581 if (buf
+BUFSIZ
-2 <= p
) {
582 rcsfaterror("diff output command line too long");
585 cachegeteof_(c
, unexpected_EOF();)
591 for (p
= buf
+1; (c
= *p
++) == ' '; )
596 LONG_MAX
/10 < line1
||
597 (t
= line1
* 10, (line1
= t
+ (c
- '0')) < t
)
599 diffLineNumberTooLarge(buf
);
607 LONG_MAX
/10 < nlines
||
608 (t
= nlines
* 10, (nlines
= t
+ (c
- '0')) < t
)
610 diffLineNumberTooLarge(buf
);
618 if (line1
+nlines
< line1
)
619 diffLineNumberTooLarge(buf
);
622 if (line1
< dc
->adprev
) {
623 rcsfaterror("backward insertion in diff output: %s", buf
);
625 dc
->adprev
= line1
+ 1;
628 if (line1
< dc
->adprev
|| line1
< dc
->dafter
) {
629 rcsfaterror("backward deletion in diff output: %s", buf
);
632 dc
->dafter
= line1
+ nlines
;
638 aprintf(fout
, "%s\n", buf
);
642 return buf
[0] == 'a';
649 /* Input an RCS file and print its internal data structures. */
651 char const cmdid
[] = "syntest";
655 int argc
; char * argv
[];
659 aputs("No input file\n",stderr
);
660 exitmain(EXIT_FAILURE
);
662 if (!(finptr
= Iopen(argv
[1], FOPEN_R
, (struct stat
*)0))) {
663 faterror("can't open input file %s", argv
[1]);
667 fdlock
= STDOUT_FILENO
;
677 fatserror("expecting EOF");
679 exitmain(EXIT_SUCCESS
);
682 void exiterr() { _exit(EXIT_FAILURE
); }