If there is no commitid, we need to initialize Delta->commitid to NULL to
[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.4 2007/01/21 17:58:42 pavalos 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.4 2007/01/21 17:58:42 pavalos 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         Kcommitid[] = "commitid",
177         Kdate[]     = "date",
178         Kdesc[]     = "desc",
179         Kexpand[]   = "expand",
180         Khead[]     = "head",
181         Klocks[]    = "locks",
182         Klog[]      = "log",
183         Knext[]     = "next",
184         Kstate[]    = "state",
185         Kstrict[]   = "strict",
186         Ksymbols[]  = "symbols",
187         Ktext[]     = "text";
188
189 static char const
190 #if COMPAT2
191         Ksuffix[]   = "suffix",
192 #endif
193         K_branches[]= "branches";
194
195 static struct buf Commleader;
196 struct cbuf Comment;
197 struct cbuf Ignored;
198 struct access   * AccessList;
199 struct assoc    * Symbols;
200 struct rcslock *Locks;
201 int               Expand;
202 int               StrictLocks;
203 struct hshentry * Head;
204 char const      * Dbranch;
205 int TotalDeltas;
206
207
208         static void
209 getsemi(key)
210         char const *key;
211 /* Get a semicolon to finish off a phrase started by KEY.  */
212 {
213         if (!getlex(SEMI))
214                 fatserror("missing ';' after '%s'", key);
215 }
216
217         static struct hshentry *
218 getdnum()
219 /* Get a delta number.  */
220 {
221         register struct hshentry *delta = getnum();
222         if (delta && countnumflds(delta->num)&1)
223                 fatserror("%s isn't a delta number", delta->num);
224         return delta;
225 }
226
227
228         void
229 getadmin()
230 /* Read an <admin> and initialize the appropriate global variables.  */
231 {
232         register char const *id;
233         struct access   * newaccess;
234         struct assoc    * newassoc;
235         struct rcslock *newlock;
236         struct hshentry * delta;
237         struct access **LastAccess;
238         struct assoc **LastSymbol;
239         struct rcslock **LastLock;
240         struct buf b;
241         struct cbuf cb;
242
243         TotalDeltas=0;
244
245         getkey(Khead);
246         Head = getdnum();
247         getsemi(Khead);
248
249         Dbranch = 0;
250         if (getkeyopt(Kbranch)) {
251                 if ((delta = getnum()))
252                         Dbranch = delta->num;
253                 getsemi(Kbranch);
254         }
255
256
257 #if COMPAT2
258         /* read suffix. Only in release 2 format */
259         if (getkeyopt(Ksuffix)) {
260                 if (nexttok==STRING) {
261                         readstring(); nextlex(); /* Throw away the suffix.  */
262                 } else if (nexttok==ID) {
263                         nextlex();
264                 }
265                 getsemi(Ksuffix);
266         }
267 #endif
268
269         getkey(Kaccess);
270         LastAccess = &AccessList;
271         while ((id = getid())) {
272                 newaccess = ftalloc(struct access);
273                 newaccess->login = id;
274                 *LastAccess = newaccess;
275                 LastAccess = &newaccess->nextaccess;
276         }
277         *LastAccess = 0;
278         getsemi(Kaccess);
279
280         getkey(Ksymbols);
281         LastSymbol = &Symbols;
282         while ((id = getid())) {
283                 if (!getlex(COLON))
284                         fatserror("missing ':' in symbolic name definition");
285                 if (!(delta=getnum())) {
286                         fatserror("missing number in symbolic name definition");
287                 } else { /*add new pair to association list*/
288                         newassoc = ftalloc(struct assoc);
289                         newassoc->symbol=id;
290                         newassoc->num = delta->num;
291                         *LastSymbol = newassoc;
292                         LastSymbol = &newassoc->nextassoc;
293                 }
294         }
295         *LastSymbol = 0;
296         getsemi(Ksymbols);
297
298         getkey(Klocks);
299         LastLock = &Locks;
300         while ((id = getid())) {
301                 if (!getlex(COLON))
302                         fatserror("missing ':' in lock");
303                 if (!(delta=getdnum())) {
304                         fatserror("missing number in lock");
305                 } else { /*add new pair to lock list*/
306                         newlock = ftalloc(struct rcslock);
307                         newlock->login=id;
308                         newlock->delta=delta;
309                         *LastLock = newlock;
310                         LastLock = &newlock->nextlock;
311                 }
312         }
313         *LastLock = 0;
314         getsemi(Klocks);
315
316         if ((StrictLocks = getkeyopt(Kstrict)))
317                 getsemi(Kstrict);
318
319         clear_buf(&Comment);
320         if (getkeyopt(Kcomment)) {
321                 if (nexttok==STRING) {
322                         Comment = savestring(&Commleader);
323                         nextlex();
324                 }
325                 getsemi(Kcomment);
326         }
327
328         Expand = KEYVAL_EXPAND;
329         if (getkeyopt(Kexpand)) {
330                 if (nexttok==STRING) {
331                         bufautobegin(&b);
332                         cb = savestring(&b);
333                         if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
334                             fatserror("unknown expand mode %.*s",
335                                 (int)cb.size, cb.string
336                             );
337                         bufautoend(&b);
338                         nextlex();
339                 }
340                 getsemi(Kexpand);
341         }
342         Ignored = getphrases(Kdesc);
343 }
344
345 char const *const expand_names[] = {
346         /* These must agree with *_EXPAND in rcsbase.h.  */
347         "kv", "kvl", "k", "v", "o", "b",
348         0
349 };
350
351         int
352 str2expmode(s)
353         char const *s;
354 /* Yield expand mode corresponding to S, or -1 if bad.  */
355 {
356         return strn2expmode(s, strlen(s));
357 }
358
359         static int
360 strn2expmode(s, n)
361         char const *s;
362         size_t n;
363 {
364         char const *const *p;
365
366         for (p = expand_names;  *p;  ++p)
367                 if (memcmp(*p,s,n) == 0  &&  !(*p)[n])
368                         return p - expand_names;
369         return -1;
370 }
371
372
373         void
374 ignorephrases(key)
375         const char *key;
376 /*
377 * Ignore a series of phrases that do not start with KEY.
378 * Stop when the next phrase starts with a token that is not an identifier,
379 * or is KEY.
380 */
381 {
382         for (;;) {
383                 nextlex();
384                 if (nexttok != ID  ||  strcmp(NextString,key) == 0)
385                         break;
386                 warnignore();
387                 hshenter=false;
388                 for (;; nextlex()) {
389                         switch (nexttok) {
390                                 case SEMI: hshenter=true; break;
391                                 case ID:
392                                 case NUM: ffree1(NextString); continue;
393                                 case STRING: readstring(); continue;
394                                 default: continue;
395                         }
396                         break;
397                 }
398         }
399 }
400
401
402         static int
403 getdelta()
404 /* Function: reads a delta block.
405  * returns false if the current block does not start with a number.
406  */
407 {
408         register struct hshentry * Delta, * num;
409         struct branchhead **LastBranch, *NewBranch;
410
411         if (!(Delta = getdnum()))
412                 return false;
413
414         hshenter = false; /*Don't enter dates into hashtable*/
415         Delta->date = getkeyval(Kdate, NUM, false);
416         hshenter=true;    /*reset hshenter for revision numbers.*/
417
418         Delta->author = getkeyval(Kauthor, ID, false);
419
420         Delta->state = getkeyval(Kstate, ID, true);
421
422         getkey(K_branches);
423         LastBranch = &Delta->branches;
424         while ((num = getdnum())) {
425                 NewBranch = ftalloc(struct branchhead);
426                 NewBranch->hsh = num;
427                 *LastBranch = NewBranch;
428                 LastBranch = &NewBranch->nextbranch;
429         }
430         *LastBranch = 0;
431         getsemi(K_branches);
432
433         getkey(Knext);
434         Delta->next = num = getdnum();
435         getsemi(Knext);
436         Delta->lockedby = 0;
437         Delta->log.string = 0;
438         Delta->selector = true;
439
440         if (getkeyopt(Kcommitid)) {
441                 Delta->commitid = NextString;
442                 nextlex();
443                 getsemi(Kcommitid);
444         } else {
445                 Delta->commitid = NULL;
446         }
447
448         Delta->ig = getphrases(Kdesc);
449         TotalDeltas++;
450         return (true);
451 }
452
453
454         void
455 gettree()
456 /* Function: Reads in the delta tree with getdelta(), then
457  * updates the lockedby fields.
458  */
459 {
460         struct rcslock const *currlock;
461
462         while (getdelta())
463                 continue;
464         currlock=Locks;
465         while (currlock) {
466                 currlock->delta->lockedby = currlock->login;
467                 currlock = currlock->nextlock;
468         }
469 }
470
471
472         void
473 getdesc(prdesc)
474 int  prdesc;
475 /* Function: read in descriptive text
476  * nexttok is not advanced afterwards.
477  * If prdesc is set, the text is printed to stdout.
478  */
479 {
480
481         getkeystring(Kdesc);
482         if (prdesc)
483                 printstring();  /*echo string*/
484         else    readstring();   /*skip string*/
485 }
486
487
488
489
490
491
492         static char const *
493 getkeyval(keyword, token, optional)
494         char const *keyword;
495         enum tokens token;
496         int optional;
497 /* reads a pair of the form
498  * <keyword> <token> ;
499  * where token is one of <id> or <num>. optional indicates whether
500  * <token> is optional. A pointer to
501  * the actual character string of <id> or <num> is returned.
502  */
503 {
504         register char const *val = 0;
505
506         getkey(keyword);
507         if (nexttok==token) {
508                 val = NextString;
509                 nextlex();
510         } else {
511                 if (!optional)
512                         fatserror("missing %s", keyword);
513         }
514         getsemi(keyword);
515         return(val);
516 }
517
518
519         void
520 unexpected_EOF()
521 {
522         rcsfaterror("unexpected EOF in diff output");
523 }
524
525         void
526 initdiffcmd(dc)
527         register struct diffcmd *dc;
528 /* Initialize *dc suitably for getdiffcmd(). */
529 {
530         dc->adprev = 0;
531         dc->dafter = 0;
532 }
533
534         static void
535 badDiffOutput(buf)
536         char const *buf;
537 {
538         rcsfaterror("bad diff output line: %s", buf);
539 }
540
541         static void
542 diffLineNumberTooLarge(buf)
543         char const *buf;
544 {
545         rcsfaterror("diff line number too large: %s", buf);
546 }
547
548         int
549 getdiffcmd(finfile, delimiter, foutfile, dc)
550         RILE *finfile;
551         FILE *foutfile;
552         int delimiter;
553         struct diffcmd *dc;
554 /* Get a editing command output by 'diff -n' from fin.
555  * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
556  * Copy a clean version of the command to fout (if nonnull).
557  * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
558  * Store the command's line number and length into dc->line1 and dc->nlines.
559  * Keep dc->adprev and dc->dafter up to date.
560  */
561 {
562         register int c;
563         declarecache;
564         register FILE *fout;
565         register char *p;
566         register RILE *fin;
567         long line1, nlines, t;
568         char buf[BUFSIZ];
569
570         fin = finfile;
571         fout = foutfile;
572         setupcache(fin); cache(fin);
573         cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
574         if (delimiter) {
575                 if (c==SDELIM) {
576                         cacheget_(c)
577                         if (c==SDELIM) {
578                                 buf[0] = c;
579                                 buf[1] = 0;
580                                 badDiffOutput(buf);
581                         }
582                         uncache(fin);
583                         nextc = c;
584                         if (fout)
585                                 aprintf(fout, "%c%c", SDELIM, c);
586                         return -1;
587                 }
588         }
589         p = buf;
590         do {
591                 if (buf+BUFSIZ-2 <= p) {
592                         rcsfaterror("diff output command line too long");
593                 }
594                 *p++ = c;
595                 cachegeteof_(c, unexpected_EOF();)
596         } while (c != '\n');
597         uncache(fin);
598         if (delimiter)
599                 ++rcsline;
600         *p = '\0';
601         for (p = buf+1;  (c = *p++) == ' ';  )
602                 continue;
603         line1 = 0;
604         while (isdigit(c)) {
605                 if (
606                         LONG_MAX/10 < line1  ||
607                         (t = line1 * 10,   (line1 = t + (c - '0'))  <  t)
608                 )
609                         diffLineNumberTooLarge(buf);
610                 c = *p++;
611         }
612         while (c == ' ')
613                 c = *p++;
614         nlines = 0;
615         while (isdigit(c)) {
616                 if (
617                         LONG_MAX/10 < nlines  ||
618                         (t = nlines * 10,   (nlines = t + (c - '0'))  <  t)
619                 )
620                         diffLineNumberTooLarge(buf);
621                 c = *p++;
622         }
623         if (c == '\r')
624                 c = *p++;
625         if (c || !nlines) {
626                 badDiffOutput(buf);
627         }
628         if (line1+nlines < line1)
629                 diffLineNumberTooLarge(buf);
630         switch (buf[0]) {
631             case 'a':
632                 if (line1 < dc->adprev) {
633                     rcsfaterror("backward insertion in diff output: %s", buf);
634                 }
635                 dc->adprev = line1 + 1;
636                 break;
637             case 'd':
638                 if (line1 < dc->adprev  ||  line1 < dc->dafter) {
639                     rcsfaterror("backward deletion in diff output: %s", buf);
640                 }
641                 dc->adprev = line1;
642                 dc->dafter = line1 + nlines;
643                 break;
644             default:
645                 badDiffOutput(buf);
646         }
647         if (fout) {
648                 aprintf(fout, "%s\n", buf);
649         }
650         dc->line1 = line1;
651         dc->nlines = nlines;
652         return buf[0] == 'a';
653 }
654
655
656
657 #ifdef SYNTEST
658
659 /* Input an RCS file and print its internal data structures.  */
660
661 char const cmdid[] = "syntest";
662
663         int
664 main(argc,argv)
665 int argc; char * argv[];
666 {
667
668         if (argc<2) {
669                 aputs("No input file\n",stderr);
670                 exitmain(EXIT_FAILURE);
671         }
672         if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
673                 faterror("can't open input file %s", argv[1]);
674         }
675         Lexinit();
676         getadmin();
677         fdlock = STDOUT_FILENO;
678         putadmin();
679
680         gettree();
681
682         getdesc(true);
683
684         nextlex();
685
686         if (!eoflex()) {
687                 fatserror("expecting EOF");
688         }
689         exitmain(EXIT_SUCCESS);
690 }
691
692 void exiterr() { _exit(EXIT_FAILURE); }
693
694 #endif