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