Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / gnu / usr.bin / rcs / lib / rcssyn.c
1 /* RCS file syntactic analysis */
2
3 /******************************************************************************
4  *                       Syntax Analysis.
5  *                       Keyword table
6  *                       Testprogram: define SYNTEST
7  *                       Compatibility with Release 2: define COMPAT2=1
8  ******************************************************************************
9  */
10
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.
14
15 This file is part of RCS.
16
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)
20 any later version.
21
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.
26
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.
31
32 Report problems and direct all questions to:
33
34     rcs-bugs@cs.purdue.edu
35
36 */
37
38 /*
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.2 2003/06/17 04:25:47 dillon Exp $
41  *
42  * Revision 5.15  1995/06/16 06:19:24  eggert
43  * Update FSF address.
44  *
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.
48  *
49  * Revision 5.13  1994/03/20 04:52:58  eggert
50  * Remove lint.
51  *
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.
55  *
56  * Revision 5.11  1992/07/28  16:12:44  eggert
57  * Avoid `unsigned'.  Statement macro names now end in _.
58  *
59  * Revision 5.10  1992/01/24  18:44:19  eggert
60  * Move put routines to rcsgen.c.
61  *
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;
65  *
66  * Revision 5.8  1991/08/19  03:13:55  eggert
67  * Tune.
68  *
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.
72  *
73  * Revision 5.6  1991/02/28  19:18:51  eggert
74  * Fix null termination bug in reporting keyword expansion.
75  *
76  * Revision 5.5  1991/02/25  07:12:44  eggert
77  * Check diff output more carefully; avoid overflow.
78  *
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.
83  *
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.
86  *
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.
90  *
91  * Revision 5.1  1990/08/29  07:14:06  eggert
92  * Add -kkvl.  Clean old log messages too.
93  *
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.
104  *
105  * Revision 4.6  89/05/01  15:13:32  narten
106  * changed copyright header to reflect current distribution rules
107  *
108  * Revision 4.5  88/08/09  19:13:21  eggert
109  * Allow cc -R; remove lint.
110  *
111  * Revision 4.4  87/12/18  11:46:16  narten
112  * more lint cleanups (Guy Harris)
113  *
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
117  *
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)
121  *
122  * Revision 1.2  87/03/27  14:22:40  jenkins
123  * Port to suns
124  *
125  * Revision 4.1  83/03/28  11:38:49  wft
126  * Added parsing and printing of default branch.
127  *
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.
131  *
132  * Revision 3.5  82/12/08  21:58:58  wft
133  * renamed Commentleader to Commleader.
134  *
135  * Revision 3.4  82/12/04  13:24:40  wft
136  * Added routine gettree(), which updates keeplock after reading the
137  * delta tree.
138  *
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.
142  *
143  * Revision 3.2  82/10/18  21:18:25  wft
144  * renamed putdeltatext to putdtext.
145  *
146  * Revision 3.1  82/10/11  19:45:11  wft
147  * made sure getc() returns into an integer.
148  */
149
150
151
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.
155  */
156
157 #include "rcsbase.h"
158
159 libId(synId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcssyn.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
160
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*));
168
169 /* keyword table */
170
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";
187
188 static char const
189 #if COMPAT2
190         Ksuffix[]   = "suffix",
191 #endif
192         K_branches[]= "branches";
193
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;
205
206
207         static void
208 getsemi(key)
209         char const *key;
210 /* Get a semicolon to finish off a phrase started by KEY.  */
211 {
212         if (!getlex(SEMI))
213                 fatserror("missing ';' after '%s'", key);
214 }
215
216         static struct hshentry *
217 getdnum()
218 /* Get a delta number.  */
219 {
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;
224 }
225
226
227         void
228 getadmin()
229 /* Read an <admin> and initialize the appropriate global variables.  */
230 {
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;
241
242         TotalDeltas=0;
243
244         getkey(Khead);
245         Head = getdnum();
246         getsemi(Khead);
247
248         Dbranch = 0;
249         if (getkeyopt(Kbranch)) {
250                 if ((delta = getnum()))
251                         Dbranch = delta->num;
252                 getsemi(Kbranch);
253         }
254
255
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();
263                 }
264                 getsemi(Ksuffix);
265         }
266 #endif
267
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;
275         }
276         *LastAccess = 0;
277         getsemi(Kaccess);
278
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;
292                 }
293         }
294         *LastSymbol = 0;
295         getsemi(Ksymbols);
296
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;
310                 }
311         }
312         *LastLock = 0;
313         getsemi(Klocks);
314
315         if ((StrictLocks = getkeyopt(Kstrict)))
316                 getsemi(Kstrict);
317
318         clear_buf(&Comment);
319         if (getkeyopt(Kcomment)) {
320                 if (nexttok==STRING) {
321                         Comment = savestring(&Commleader);
322                         nextlex();
323                 }
324                 getsemi(Kcomment);
325         }
326
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
335                             );
336                         bufautoend(&b);
337                         nextlex();
338                 }
339                 getsemi(Kexpand);
340         }
341         Ignored = getphrases(Kdesc);
342 }
343
344 char const *const expand_names[] = {
345         /* These must agree with *_EXPAND in rcsbase.h.  */
346         "kv", "kvl", "k", "v", "o", "b",
347         0
348 };
349
350         int
351 str2expmode(s)
352         char const *s;
353 /* Yield expand mode corresponding to S, or -1 if bad.  */
354 {
355         return strn2expmode(s, strlen(s));
356 }
357
358         static int
359 strn2expmode(s, n)
360         char const *s;
361         size_t n;
362 {
363         char const *const *p;
364
365         for (p = expand_names;  *p;  ++p)
366                 if (memcmp(*p,s,n) == 0  &&  !(*p)[n])
367                         return p - expand_names;
368         return -1;
369 }
370
371
372         void
373 ignorephrases(key)
374         const char *key;
375 /*
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.
379 */
380 {
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;
394                         }
395                         break;
396                 }
397         }
398 }
399
400
401         static int
402 getdelta()
403 /* Function: reads a delta block.
404  * returns false if the current block does not start with a number.
405  */
406 {
407         register struct hshentry * Delta, * num;
408         struct branchhead **LastBranch, *NewBranch;
409
410         if (!(Delta = getdnum()))
411                 return false;
412
413         hshenter = false; /*Don't enter dates into hashtable*/
414         Delta->date = getkeyval(Kdate, NUM, false);
415         hshenter=true;    /*reset hshenter for revision numbers.*/
416
417         Delta->author = getkeyval(Kauthor, ID, false);
418
419         Delta->state = getkeyval(Kstate, ID, true);
420
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;
428         }
429         *LastBranch = 0;
430         getsemi(K_branches);
431
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);
441 }
442
443
444         void
445 gettree()
446 /* Function: Reads in the delta tree with getdelta(), then
447  * updates the lockedby fields.
448  */
449 {
450         struct rcslock const *currlock;
451
452         while (getdelta())
453                 continue;
454         currlock=Locks;
455         while (currlock) {
456                 currlock->delta->lockedby = currlock->login;
457                 currlock = currlock->nextlock;
458         }
459 }
460
461
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.
468  */
469 {
470
471         getkeystring(Kdesc);
472         if (prdesc)
473                 printstring();  /*echo string*/
474         else    readstring();   /*skip string*/
475 }
476
477
478
479
480
481
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.
492  */
493 {
494         register char const *val = 0;
495
496         getkey(keyword);
497         if (nexttok==token) {
498                 val = NextString;
499                 nextlex();
500         } else {
501                 if (!optional)
502                         fatserror("missing %s", keyword);
503         }
504         getsemi(keyword);
505         return(val);
506 }
507
508
509         void
510 unexpected_EOF()
511 {
512         rcsfaterror("unexpected EOF in diff output");
513 }
514
515         void
516 initdiffcmd(dc)
517         register struct diffcmd *dc;
518 /* Initialize *dc suitably for getdiffcmd(). */
519 {
520         dc->adprev = 0;
521         dc->dafter = 0;
522 }
523
524         static void
525 badDiffOutput(buf)
526         char const *buf;
527 {
528         rcsfaterror("bad diff output line: %s", buf);
529 }
530
531         static void
532 diffLineNumberTooLarge(buf)
533         char const *buf;
534 {
535         rcsfaterror("diff line number too large: %s", buf);
536 }
537
538         int
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.
550  */
551 {
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];
559
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);
571                         }
572                         uncache(fin);
573                         nextc = c;
574                         if (fout)
575                                 aprintf(fout, "%c%c", SDELIM, c);
576                         return -1;
577                 }
578         }
579         p = buf;
580         do {
581                 if (buf+BUFSIZ-2 <= p) {
582                         rcsfaterror("diff output command line too long");
583                 }
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)
598                 )
599                         diffLineNumberTooLarge(buf);
600                 c = *p++;
601         }
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)
609                 )
610                         diffLineNumberTooLarge(buf);
611                 c = *p++;
612         }
613         if (c == '\r')
614                 c = *p++;
615         if (c || !nlines) {
616                 badDiffOutput(buf);
617         }
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);
624                 }
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);
630                 }
631                 dc->adprev = line1;
632                 dc->dafter = line1 + nlines;
633                 break;
634             default:
635                 badDiffOutput(buf);
636         }
637         if (fout) {
638                 aprintf(fout, "%s\n", buf);
639         }
640         dc->line1 = line1;
641         dc->nlines = nlines;
642         return buf[0] == 'a';
643 }
644
645
646
647 #ifdef SYNTEST
648
649 /* Input an RCS file and print its internal data structures.  */
650
651 char const cmdid[] = "syntest";
652
653         int
654 main(argc,argv)
655 int argc; char * argv[];
656 {
657
658         if (argc<2) {
659                 aputs("No input file\n",stderr);
660                 exitmain(EXIT_FAILURE);
661         }
662         if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
663                 faterror("can't open input file %s", argv[1]);
664         }
665         Lexinit();
666         getadmin();
667         fdlock = STDOUT_FILENO;
668         putadmin();
669
670         gettree();
671
672         getdesc(true);
673
674         nextlex();
675
676         if (!eoflex()) {
677                 fatserror("expecting EOF");
678         }
679         exitmain(EXIT_SUCCESS);
680 }
681
682 void exiterr() { _exit(EXIT_FAILURE); }
683
684 #endif