Sync usage with man page.
[netbsd-mini2440.git] / gnu / usr.bin / rcs / lib / rcssyn.c
blob4260af4e212cf99de1f0fed07ec56af4a7ddbdfc
1 /* $NetBSD: rcssyn.c,v 1.1.1.2 1996/10/13 21:56:52 veego Exp $ */
3 /* RCS file syntactic analysis */
5 /******************************************************************************
6 * Syntax Analysis.
7 * Keyword table
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)
22 any later version.
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
41 * $Log: rcssyn.c,v $
42 * Revision 5.15 1995/06/16 06:19:24 eggert
43 * Update FSF address.
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
50 * Remove lint.
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
67 * Tune.
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.
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.
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
116 * 4.1
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)
122 * Revision 1.2 87/03/27 14:22:40 jenkins
123 * Port to suns
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
137 * delta tree.
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.
157 #include "rcsbase.h"
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*));
169 /* keyword table */
171 char const
172 Kaccess[] = "access",
173 Kauthor[] = "author",
174 Kbranch[] = "branch",
175 Kcomment[] = "comment",
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";
188 static char const
189 #if COMPAT2
190 Ksuffix[] = "suffix",
191 #endif
192 K_branches[]= "branches";
194 static struct buf Commleader;
195 struct cbuf Comment;
196 struct cbuf Ignored;
197 struct access * AccessList;
198 struct assoc * Symbols;
199 struct rcslock *Locks;
200 int Expand;
201 int StrictLocks;
202 struct hshentry * Head;
203 char const * Dbranch;
204 int TotalDeltas;
207 static void
208 getsemi(key)
209 char const *key;
210 /* Get a semicolon to finish off a phrase started by KEY. */
212 if (!getlex(SEMI))
213 fatserror("missing ';' after '%s'", key);
216 static struct hshentry *
217 getdnum()
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);
223 return delta;
227 void
228 getadmin()
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;
239 struct buf b;
240 struct cbuf cb;
242 TotalDeltas=0;
244 getkey(Khead);
245 Head = getdnum();
246 getsemi(Khead);
248 Dbranch = 0;
249 if (getkeyopt(Kbranch)) {
250 if ((delta = getnum()))
251 Dbranch = delta->num;
252 getsemi(Kbranch);
256 #if COMPAT2
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) {
262 nextlex();
264 getsemi(Ksuffix);
266 #endif
268 getkey(Kaccess);
269 LastAccess = &AccessList;
270 while ((id = getid())) {
271 newaccess = ftalloc(struct access);
272 newaccess->login = id;
273 *LastAccess = newaccess;
274 LastAccess = &newaccess->nextaccess;
276 *LastAccess = 0;
277 getsemi(Kaccess);
279 getkey(Ksymbols);
280 LastSymbol = &Symbols;
281 while ((id = getid())) {
282 if (!getlex(COLON))
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);
288 newassoc->symbol=id;
289 newassoc->num = delta->num;
290 *LastSymbol = newassoc;
291 LastSymbol = &newassoc->nextassoc;
294 *LastSymbol = 0;
295 getsemi(Ksymbols);
297 getkey(Klocks);
298 LastLock = &Locks;
299 while ((id = getid())) {
300 if (!getlex(COLON))
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);
306 newlock->login=id;
307 newlock->delta=delta;
308 *LastLock = newlock;
309 LastLock = &newlock->nextlock;
312 *LastLock = 0;
313 getsemi(Klocks);
315 if ((StrictLocks = getkeyopt(Kstrict)))
316 getsemi(Kstrict);
318 clear_buf(&Comment);
319 if (getkeyopt(Kcomment)) {
320 if (nexttok==STRING) {
321 Comment = savestring(&Commleader);
322 nextlex();
324 getsemi(Kcomment);
327 Expand = KEYVAL_EXPAND;
328 if (getkeyopt(Kexpand)) {
329 if (nexttok==STRING) {
330 bufautobegin(&b);
331 cb = savestring(&b);
332 if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
333 fatserror("unknown expand mode %.*s",
334 (int)cb.size, cb.string
336 bufautoend(&b);
337 nextlex();
339 getsemi(Kexpand);
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",
351 str2expmode(s)
352 char const *s;
353 /* Yield expand mode corresponding to S, or -1 if bad. */
355 return strn2expmode(s, strlen(s));
358 static int
359 strn2expmode(s, n)
360 char const *s;
361 size_t n;
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;
368 return -1;
372 void
373 ignorephrases(key)
374 const char *key;
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,
378 * or is KEY.
381 for (;;) {
382 nextlex();
383 if (nexttok != ID || strcmp(NextString,key) == 0)
384 break;
385 warnignore();
386 hshenter=false;
387 for (;; nextlex()) {
388 switch (nexttok) {
389 case SEMI: hshenter=true; break;
390 case ID:
391 case NUM: ffree1(NextString); continue;
392 case STRING: readstring(); continue;
393 default: continue;
395 break;
401 static int
402 getdelta()
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()))
411 return false;
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);
421 getkey(K_branches);
422 LastBranch = &Delta->branches;
423 while ((num = getdnum())) {
424 NewBranch = ftalloc(struct branchhead);
425 NewBranch->hsh = num;
426 *LastBranch = NewBranch;
427 LastBranch = &NewBranch->nextbranch;
429 *LastBranch = 0;
430 getsemi(K_branches);
432 getkey(Knext);
433 Delta->next = num = getdnum();
434 getsemi(Knext);
435 Delta->lockedby = 0;
436 Delta->log.string = 0;
437 Delta->selector = true;
438 Delta->ig = getphrases(Kdesc);
439 TotalDeltas++;
440 return (true);
444 void
445 gettree()
446 /* Function: Reads in the delta tree with getdelta(), then
447 * updates the lockedby fields.
450 struct rcslock const *currlock;
452 while (getdelta())
453 continue;
454 currlock=Locks;
455 while (currlock) {
456 currlock->delta->lockedby = currlock->login;
457 currlock = currlock->nextlock;
462 void
463 getdesc(prdesc)
464 int prdesc;
465 /* Function: read in descriptive text
466 * nexttok is not advanced afterwards.
467 * If prdesc is set, the text is printed to stdout.
471 getkeystring(Kdesc);
472 if (prdesc)
473 printstring(); /*echo string*/
474 else readstring(); /*skip string*/
482 static char const *
483 getkeyval(keyword, token, optional)
484 char const *keyword;
485 enum tokens token;
486 int 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;
496 getkey(keyword);
497 if (nexttok==token) {
498 val = NextString;
499 nextlex();
500 } else {
501 if (!optional)
502 fatserror("missing %s", keyword);
504 getsemi(keyword);
505 return(val);
509 void
510 unexpected_EOF()
512 rcsfaterror("unexpected EOF in diff output");
515 void
516 initdiffcmd(dc)
517 register struct diffcmd *dc;
518 /* Initialize *dc suitably for getdiffcmd(). */
520 dc->adprev = 0;
521 dc->dafter = 0;
524 static void
525 badDiffOutput(buf)
526 char const *buf;
528 rcsfaterror("bad diff output line: %s", buf);
531 static void
532 diffLineNumberTooLarge(buf)
533 char const *buf;
535 rcsfaterror("diff line number too large: %s", buf);
539 getdiffcmd(finfile, delimiter, foutfile, dc)
540 RILE *finfile;
541 FILE *foutfile;
542 int delimiter;
543 struct diffcmd *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.
552 register int c;
553 declarecache;
554 register FILE *fout;
555 register char *p;
556 register RILE *fin;
557 long line1, nlines, t;
558 char buf[BUFSIZ];
560 fin = finfile;
561 fout = foutfile;
562 setupcache(fin); cache(fin);
563 cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
564 if (delimiter) {
565 if (c==SDELIM) {
566 cacheget_(c)
567 if (c==SDELIM) {
568 buf[0] = c;
569 buf[1] = 0;
570 badDiffOutput(buf);
572 uncache(fin);
573 nextc = c;
574 if (fout)
575 aprintf(fout, "%c%c", SDELIM, c);
576 return -1;
579 p = buf;
580 do {
581 if (buf+BUFSIZ-2 <= p) {
582 rcsfaterror("diff output command line too long");
584 *p++ = c;
585 cachegeteof_(c, unexpected_EOF();)
586 } while (c != '\n');
587 uncache(fin);
588 if (delimiter)
589 ++rcsline;
590 *p = '\0';
591 for (p = buf+1; (c = *p++) == ' '; )
592 continue;
593 line1 = 0;
594 while (isdigit(c)) {
595 if (
596 LONG_MAX/10 < line1 ||
597 (t = line1 * 10, (line1 = t + (c - '0')) < t)
599 diffLineNumberTooLarge(buf);
600 c = *p++;
602 while (c == ' ')
603 c = *p++;
604 nlines = 0;
605 while (isdigit(c)) {
606 if (
607 LONG_MAX/10 < nlines ||
608 (t = nlines * 10, (nlines = t + (c - '0')) < t)
610 diffLineNumberTooLarge(buf);
611 c = *p++;
613 if (c == '\r')
614 c = *p++;
615 if (c || !nlines) {
616 badDiffOutput(buf);
618 if (line1+nlines < line1)
619 diffLineNumberTooLarge(buf);
620 switch (buf[0]) {
621 case 'a':
622 if (line1 < dc->adprev) {
623 rcsfaterror("backward insertion in diff output: %s", buf);
625 dc->adprev = line1 + 1;
626 break;
627 case 'd':
628 if (line1 < dc->adprev || line1 < dc->dafter) {
629 rcsfaterror("backward deletion in diff output: %s", buf);
631 dc->adprev = line1;
632 dc->dafter = line1 + nlines;
633 break;
634 default:
635 badDiffOutput(buf);
637 if (fout) {
638 aprintf(fout, "%s\n", buf);
640 dc->line1 = line1;
641 dc->nlines = nlines;
642 return buf[0] == 'a';
647 #ifdef SYNTEST
649 /* Input an RCS file and print its internal data structures. */
651 char const cmdid[] = "syntest";
654 main(argc,argv)
655 int argc; char * argv[];
658 if (argc<2) {
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]);
665 Lexinit();
666 getadmin();
667 fdlock = STDOUT_FILENO;
668 putadmin();
670 gettree();
672 getdesc(true);
674 nextlex();
676 if (!eoflex()) {
677 fatserror("expecting EOF");
679 exitmain(EXIT_SUCCESS);
682 void exiterr() { _exit(EXIT_FAILURE); }
684 #endif