Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / gnu / usr.bin / rcs / lib / rcslex.c
1 /* lexical analysis of RCS files */
2
3 /******************************************************************************
4  *                     Lexical Analysis.
5  *                     hashtable, Lexinit, nextlex, getlex, getkey,
6  *                     getid, getnum, readstring, printstring, savestring,
7  *                     checkid, fatserror, error, faterror, warn, diagnose
8  *                     Testprogram: define LEXDB
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
41 /*
42  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcslex.c,v 1.8 1999/08/27 23:36:47 peter Exp $
43  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcslex.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
44  *
45  * Revision 5.19  1995/06/16 06:19:24  eggert
46  * Update FSF address.
47  *
48  * Revision 5.18  1995/06/01 16:23:43  eggert
49  * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate):
50  * New functions.
51  * (Iclose): If large_memory and maps_memory, use them to deallocate mapping.
52  * (fd2RILE): Use map_fd if available.
53  * If one mapping method fails, try the next instead of giving up;
54  * if they all fail, fall back on ordinary read.
55  * Work around bug: root mmap over NFS succeeds, but accessing dumps core.
56  * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t.
57  * (advise_access): Use madvise only if this instance used mmap.
58  * (Iopen): Use fdSafer to get safer file descriptor.
59  * (aflush): Moved here from rcsedit.c.
60  *
61  * Revision 5.17  1994/03/20 04:52:58  eggert
62  * Don't worry if madvise fails.  Add Orewind.  Remove lint.
63  *
64  * Revision 5.16  1993/11/09 17:55:29  eggert
65  * Fix `label: }' typo.
66  *
67  * Revision 5.15  1993/11/03 17:42:27  eggert
68  * Improve quality of diagnostics by putting file names in them more often.
69  * Don't discard ignored phrases.
70  *
71  * Revision 5.14  1992/07/28  16:12:44  eggert
72  * Identifiers may now start with a digit and (unless they are symbolic names)
73  * may contain `.'.  Avoid `unsigned'.  Statement macro names now end in _.
74  *
75  * Revision 5.13  1992/02/17  23:02:27  eggert
76  * Work around NFS mmap SIGBUS problem.
77  *
78  * Revision 5.12  1992/01/06  02:42:34  eggert
79  * Use OPEN_O_BINARY if mode contains 'b'.
80  *
81  * Revision 5.11  1991/11/03  03:30:44  eggert
82  * Fix porting bug to ancient hosts lacking vfprintf.
83  *
84  * Revision 5.10  1991/10/07  17:32:46  eggert
85  * Support piece tables even if !has_mmap.
86  *
87  * Revision 5.9  1991/09/24  00:28:42  eggert
88  * Don't export errsay().
89  *
90  * Revision 5.8  1991/08/19  03:13:55  eggert
91  * Add eoflex(), mmap support.  Tune.
92  *
93  * Revision 5.7  1991/04/21  11:58:26  eggert
94  * Add MS-DOS support.
95  *
96  * Revision 5.6  1991/02/25  07:12:42  eggert
97  * Work around fputs bug.  strsave -> str_save (DG/UX name clash)
98  *
99  * Revision 5.5  1990/12/04  05:18:47  eggert
100  * Use -I for prompts and -q for diagnostics.
101  *
102  * Revision 5.4  1990/11/19  20:05:28  hammer
103  * no longer gives warning about unknown keywords if -q is specified
104  *
105  * Revision 5.3  1990/11/01  05:03:48  eggert
106  * When ignoring unknown phrases, copy them to the output RCS file.
107  *
108  * Revision 5.2  1990/09/04  08:02:27  eggert
109  * Count RCS lines better.
110  *
111  * Revision 5.1  1990/08/29  07:14:03  eggert
112  * Work around buggy compilers with defective argument promotion.
113  *
114  * Revision 5.0  1990/08/22  08:12:55  eggert
115  * Remove compile-time limits; use malloc instead.
116  * Report errno-related errors with perror().
117  * Ansify and Posixate.  Add support for ISO 8859.
118  * Use better hash function.
119  *
120  * Revision 4.6  89/05/01  15:13:07  narten
121  * changed copyright header to reflect current distribution rules
122  *
123  * Revision 4.5  88/08/28  15:01:12  eggert
124  * Don't loop when writing error messages to a full filesystem.
125  * Flush stderr/stdout when mixing output.
126  * Yield exit status compatible with diff(1).
127  * Shrink stdio code size; allow cc -R; remove lint.
128  *
129  * Revision 4.4  87/12/18  11:44:47  narten
130  * fixed to use "varargs" in "fprintf"; this is required if it is to
131  * work on a SPARC machine such as a Sun-4
132  *
133  * Revision 4.3  87/10/18  10:37:18  narten
134  * Updating version numbers. Changes relative to 1.1 actually relative
135  * to version 4.1
136  *
137  * Revision 1.3  87/09/24  14:00:17  narten
138  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
139  * warnings)
140  *
141  * Revision 1.2  87/03/27  14:22:33  jenkins
142  * Port to suns
143  *
144  * Revision 4.1  83/03/25  18:12:51  wft
145  * Only changed $Header to $Id.
146  *
147  * Revision 3.3  82/12/10  16:22:37  wft
148  * Improved error messages, changed exit status on error to 1.
149  *
150  * Revision 3.2  82/11/28  21:27:10  wft
151  * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
152  * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
153  * properly in case there is an IO-error (e.g., file system full).
154  *
155  * Revision 3.1  82/10/11  19:43:56  wft
156  * removed unused label out:;
157  * made sure all calls to getc() return into an integer, not a char.
158  */
159
160
161 /*
162 #define LEXDB
163 */
164 /* version LEXDB is for testing the lexical analyzer. The testprogram
165  * reads a stream of lexemes, enters the revision numbers into the
166  * hashtable, and prints the recognized tokens. Keywords are recognized
167  * as identifiers.
168  */
169
170
171
172 #include "rcsbase.h"
173
174 libId(lexId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcslex.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
175
176 static char *checkidentifier P((char*,int,int));
177 static void errsay P((char const*));
178 static void fatsay P((char const*));
179 static void lookup P((char const*));
180 static void startsay P((const char*,const char*));
181 static void warnsay P((char const*));
182
183 static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
184
185 enum tokens     nexttok;    /*next token, set by nextlex                    */
186
187 int             hshenter;   /*if true, next suitable lexeme will be entered */
188                             /*into the symbol table. Handle with care.      */
189 int             nextc;      /*next input character, initialized by Lexinit  */
190
191 long            rcsline;    /*current line-number of input                  */
192 int             nerror;     /*counter for errors                            */
193 int             quietflag;  /*indicates quiet mode                          */
194 RILE *          finptr;     /*input file descriptor                         */
195
196 FILE *          frewrite;   /*file descriptor for echoing input             */
197
198 FILE *          foutptr;    /* copy of frewrite, but 0 to suppress echo  */
199
200 static struct buf tokbuf;   /* token buffer                                 */
201
202 char const *    NextString; /* next token                                   */
203
204 /*
205  * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
206  * so hshsize should be odd.
207  * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
208  * Software--practice & experience 20, 2 (Feb 1990), 209-224.
209  */
210 #ifndef hshsize
211 #       define hshsize 511
212 #endif
213
214 static struct hshentry *hshtab[hshsize]; /*hashtable                        */
215
216 static int ignored_phrases; /* have we ignored phrases in this RCS file? */
217
218     void
219 warnignore()
220 {
221     if (!ignored_phrases) {
222         ignored_phrases = true;
223         rcswarn("Unknown phrases like `%s ...;' are present.", NextString);
224     }
225 }
226
227
228
229         static void
230 lookup(str)
231         char const *str;
232 /* Function: Looks up the character string pointed to by str in the
233  * hashtable. If the string is not present, a new entry for it is created.
234  * In any case, the address of the corresponding hashtable entry is placed
235  * into nexthsh.
236  */
237 {
238         register unsigned ihash;  /* index into hashtable */
239         register char const *sp;
240         register struct hshentry *n, **p;
241
242         /* calculate hash code */
243         sp = str;
244         ihash = 0;
245         while (*sp)
246                 ihash  =  (ihash<<2) + *sp++;
247         ihash %= hshsize;
248
249         for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
250                 if (!(n = *p)) {
251                         /* empty slot found */
252                         *p = n = ftalloc(struct hshentry);
253                         n->num = fstr_save(str);
254                         n->nexthsh = 0;
255 #                       ifdef LEXDB
256                                 VOID printf("\nEntered: %s at %u ", str, ihash);
257 #                       endif
258                         break;
259                 } else if (strcmp(str, n->num) == 0)
260                         /* match found */
261                         break;
262         nexthsh = n;
263         NextString = n->num;
264 }
265
266
267
268
269
270
271         void
272 Lexinit()
273 /* Function: Initialization of lexical analyzer:
274  * initializes the hashtable,
275  * initializes nextc, nexttok if finptr != 0
276  */
277 {       register int            c;
278
279         for (c = hshsize;  0 <= --c;  ) {
280                 hshtab[c] = 0;
281         }
282
283         nerror = 0;
284         if (finptr) {
285                 foutptr = 0;
286                 hshenter = true;
287                 ignored_phrases = false;
288                 rcsline = 1;
289                 bufrealloc(&tokbuf, 2);
290                 Iget_(finptr, nextc)
291                 nextlex();            /*initial token*/
292         }
293 }
294
295
296
297
298
299
300
301         void
302 nextlex()
303
304 /* Function: Reads the next token and sets nexttok to the next token code.
305  * Only if hshenter is set, a revision number is entered into the
306  * hashtable and a pointer to it is placed into nexthsh.
307  * This is useful for avoiding that dates are placed into the hashtable.
308  * For ID's and NUM's, NextString is set to the character string.
309  * Assumption: nextc contains the next character.
310  */
311 {       register c;
312         declarecache;
313         register FILE *frew;
314         register char * sp;
315         char const *limit;
316         register enum tokens d;
317         register RILE *fin;
318
319         fin=finptr; frew=foutptr;
320         setupcache(fin); cache(fin);
321         c = nextc;
322
323         for (;;) { switch ((d = ctab[c])) {
324
325         default:
326                 fatserror("unknown character `%c'", c);
327                 /*NOTREACHED*/
328
329         case NEWLN:
330                 ++rcsline;
331 #               ifdef LEXDB
332                 afputc('\n',stdout);
333 #               endif
334                 /* Note: falls into next case */
335
336         case SPACE:
337                 GETC_(frew, c)
338                 continue;
339
340         case IDCHAR:
341         case LETTER:
342         case Letter:
343                 d = ID;
344                 /* fall into */
345         case DIGIT:
346         case PERIOD:
347                 sp = tokbuf.string;
348                 limit = sp + tokbuf.size;
349                 *sp++ = c;
350                 for (;;) {
351                         GETC_(frew, c)
352                         switch (ctab[c]) {
353                             case IDCHAR:
354                             case LETTER:
355                             case Letter:
356                                 d = ID;
357                                 /* fall into */
358                             case DIGIT:
359                             case PERIOD:
360                                 *sp++ = c;
361                                 if (limit <= sp)
362                                         sp = bufenlarge(&tokbuf, &limit);
363                                 continue;
364
365                             default:
366                                 break;
367                         }
368                         break;
369                 }
370                 *sp = 0;
371                 if (d == DIGIT  ||  d == PERIOD) {
372                         d = NUM;
373                         if (hshenter) {
374                                 lookup(tokbuf.string);
375                                 break;
376                         }
377                 }
378                 NextString = fstr_save(tokbuf.string);
379                 break;
380
381         case SBEGIN: /* long string */
382                 d = STRING;
383                 /* note: only the initial SBEGIN has been read*/
384                 /* read the string, and reset nextc afterwards*/
385                 break;
386
387         case COLON:
388         case SEMI:
389                 GETC_(frew, c)
390                 break;
391         } break; }
392         nextc = c;
393         nexttok = d;
394         uncache(fin);
395 }
396
397         int
398 eoflex()
399 /*
400  * Yield true if we look ahead to the end of the input, false otherwise.
401  * nextc becomes undefined at end of file.
402  */
403 {
404         register int c;
405         declarecache;
406         register FILE *fout;
407         register RILE *fin;
408
409         c = nextc;
410         fin = finptr;
411         fout = foutptr;
412         setupcache(fin); cache(fin);
413
414         for (;;) {
415                 switch (ctab[c]) {
416                         default:
417                                 nextc = c;
418                                 uncache(fin);
419                                 return false;
420
421                         case NEWLN:
422                                 ++rcsline;
423                                 /* fall into */
424                         case SPACE:
425                                 cachegeteof_(c, {uncache(fin);return true;})
426                                 break;
427                 }
428                 if (fout)
429                         aputc_(c, fout)
430         }
431 }
432
433
434 int getlex(token)
435 enum tokens token;
436 /* Function: Checks if nexttok is the same as token. If so,
437  * advances the input by calling nextlex and returns true.
438  * otherwise returns false.
439  * Doesn't work for strings and keywords; loses the character string for ids.
440  */
441 {
442         if (nexttok==token) {
443                 nextlex();
444                 return(true);
445         } else  return(false);
446 }
447
448         int
449 getkeyopt(key)
450         char const *key;
451 /* Function: If the current token is a keyword identical to key,
452  * advances the input by calling nextlex and returns true;
453  * otherwise returns false.
454  */
455 {
456         if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
457                  /* match found */
458                  ffree1(NextString);
459                  nextlex();
460                  return(true);
461         }
462         return(false);
463 }
464
465         void
466 getkey(key)
467         char const *key;
468 /* Check that the current input token is a keyword identical to key,
469  * and advance the input by calling nextlex.
470  */
471 {
472         if (!getkeyopt(key))
473                 fatserror("missing '%s' keyword", key);
474 }
475
476         void
477 getkeystring(key)
478         char const *key;
479 /* Check that the current input token is a keyword identical to key,
480  * and advance the input by calling nextlex; then look ahead for a string.
481  */
482 {
483         getkey(key);
484         if (nexttok != STRING)
485                 fatserror("missing string after '%s' keyword", key);
486 }
487
488
489         char const *
490 getid()
491 /* Function: Checks if nexttok is an identifier. If so,
492  * advances the input by calling nextlex and returns a pointer
493  * to the identifier; otherwise returns 0.
494  * Treats keywords as identifiers.
495  */
496 {
497         register char const *name;
498         if (nexttok==ID) {
499                 name = NextString;
500                 nextlex();
501                 return name;
502         } else
503                 return 0;
504 }
505
506
507 struct hshentry * getnum()
508 /* Function: Checks if nexttok is a number. If so,
509  * advances the input by calling nextlex and returns a pointer
510  * to the hashtable entry.  Otherwise returns 0.
511  * Doesn't work if hshenter is false.
512  */
513 {
514         register struct hshentry * num;
515         if (nexttok==NUM) {
516                 num=nexthsh;
517                 nextlex();
518                 return num;
519         } else
520                 return 0;
521 }
522
523         struct cbuf
524 getphrases(key)
525         char const *key;
526 /*
527 * Get a series of phrases that do not start with KEY.  Yield resulting buffer.
528 * Stop when the next phrase starts with a token that is not an identifier,
529 * or is KEY.  Copy input to foutptr if it is set.  Unlike ignorephrases(),
530 * this routine assumes nextlex() has already been invoked before we start.
531 */
532 {
533     declarecache;
534     register int c;
535     register char const *kn;
536     struct cbuf r;
537     register RILE *fin;
538     register FILE *frew;
539 #   if large_memory
540 #       define savech_(c) ;
541 #   else
542         register char *p;
543         char const *limit;
544         struct buf b;
545 #       define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
546 #   endif
547
548     if (nexttok!=ID  ||  strcmp(NextString,key) == 0)
549         clear_buf(&r);
550     else {
551         warnignore();
552         fin = finptr;
553         frew = foutptr;
554         setupcache(fin); cache(fin);
555 #       if large_memory
556             r.string = (char const*)cacheptr() - strlen(NextString) - 1;
557 #       else
558             bufautobegin(&b);
559             bufscpy(&b, NextString);
560             p = b.string + strlen(b.string);
561             limit = b.string + b.size;
562 #       endif
563         ffree1(NextString);
564         c = nextc;
565         for (;;) {
566             for (;;) {
567                 savech_(c)
568                 switch (ctab[c]) {
569                     default:
570                         fatserror("unknown character `%c'", c);
571                         /*NOTREACHED*/
572                     case NEWLN:
573                         ++rcsline;
574                         /* fall into */
575                     case COLON: case DIGIT: case LETTER: case Letter:
576                     case PERIOD: case SPACE:
577                         GETC_(frew, c)
578                         continue;
579                     case SBEGIN: /* long string */
580                         for (;;) {
581                             for (;;) {
582                                 GETC_(frew, c)
583                                 savech_(c)
584                                 switch (c) {
585                                     case '\n':
586                                         ++rcsline;
587                                         /* fall into */
588                                     default:
589                                         continue;
590
591                                     case SDELIM:
592                                         break;
593                                 }
594                                 break;
595                             }
596                             GETC_(frew, c)
597                             if (c != SDELIM)
598                                 break;
599                             savech_(c)
600                         }
601                         continue;
602                     case SEMI:
603                         cacheget_(c)
604                         if (ctab[c] == NEWLN) {
605                             if (frew)
606                                 aputc_(c, frew)
607                             ++rcsline;
608                             savech_(c)
609                             cacheget_(c)
610                         }
611 #                       if large_memory
612                             r.size = (char const*)cacheptr() - 1 - r.string;
613 #                       endif
614                         for (;;) {
615                             switch (ctab[c]) {
616                                 case NEWLN:
617                                         ++rcsline;
618                                         /* fall into */
619                                 case SPACE:
620                                         cacheget_(c)
621                                         continue;
622
623                                 default: break;
624                             }
625                             break;
626                         }
627                         if (frew)
628                             aputc_(c, frew)
629                         break;
630                 }
631                 break;
632             }
633             if (ctab[c] == Letter) {
634                     for (kn = key;  c && *kn==c;  kn++)
635                         GETC_(frew, c)
636                     if (!*kn)
637                         switch (ctab[c]) {
638                             case DIGIT: case LETTER: case Letter:
639                             case IDCHAR: case PERIOD:
640                                 break;
641                             default:
642                                 nextc = c;
643                                 NextString = fstr_save(key);
644                                 nexttok = ID;
645                                 uncache(fin);
646                                 goto returnit;
647                         }
648 #                   if !large_memory
649                         {
650                             register char const *ki;
651                             for (ki=key; ki<kn; )
652                                 savech_(*ki++)
653                         }
654 #                   endif
655             } else {
656                     nextc = c;
657                     uncache(fin);
658                     nextlex();
659                     break;
660             }
661         }
662     returnit:;
663 #       if !large_memory
664             return bufremember(&b, (size_t)(p - b.string));
665 #       endif
666     }
667     return r;
668 }
669
670
671         void
672 readstring()
673 /* skip over characters until terminating single SDELIM        */
674 /* If foutptr is set, copy every character read to foutptr.    */
675 /* Does not advance nextlex at the end.                        */
676 {       register c;
677         declarecache;
678         register FILE *frew;
679         register RILE *fin;
680         fin=finptr; frew=foutptr;
681         setupcache(fin); cache(fin);
682         for (;;) {
683                 GETC_(frew, c)
684                 switch (c) {
685                     case '\n':
686                         ++rcsline;
687                         break;
688
689                     case SDELIM:
690                         GETC_(frew, c)
691                         if (c != SDELIM) {
692                                 /* end of string */
693                                 nextc = c;
694                                 uncache(fin);
695                                 return;
696                         }
697                         break;
698                 }
699         }
700 }
701
702
703         void
704 printstring()
705 /* Function: copy a string to stdout, until terminated with a single SDELIM.
706  * Does not advance nextlex at the end.
707  */
708 {
709         register c;
710         declarecache;
711         register FILE *fout;
712         register RILE *fin;
713         fin=finptr;
714         fout = stdout;
715         setupcache(fin); cache(fin);
716         for (;;) {
717                 cacheget_(c)
718                 switch (c) {
719                     case '\n':
720                         ++rcsline;
721                         break;
722                     case SDELIM:
723                         cacheget_(c)
724                         if (c != SDELIM) {
725                                 nextc=c;
726                                 uncache(fin);
727                                 return;
728                         }
729                         break;
730                 }
731                 aputc_(c,fout)
732         }
733 }
734
735
736
737         struct cbuf
738 savestring(target)
739         struct buf *target;
740 /* Copies a string terminated with SDELIM from file finptr to buffer target.
741  * Double SDELIM is replaced with SDELIM.
742  * If foutptr is set, the string is also copied unchanged to foutptr.
743  * Does not advance nextlex at the end.
744  * Yield a copy of *TARGET, except with exact length.
745  */
746 {
747         register c;
748         declarecache;
749         register FILE *frew;
750         register char *tp;
751         register RILE *fin;
752         char const *limit;
753         struct cbuf r;
754
755         fin=finptr; frew=foutptr;
756         setupcache(fin); cache(fin);
757         tp = target->string;  limit = tp + target->size;
758         for (;;) {
759                 GETC_(frew, c)
760                 switch (c) {
761                     case '\n':
762                         ++rcsline;
763                         break;
764                     case SDELIM:
765                         GETC_(frew, c)
766                         if (c != SDELIM) {
767                                 /* end of string */
768                                 nextc=c;
769                                 r.string = target->string;
770                                 r.size = tp - r.string;
771                                 uncache(fin);
772                                 return r;
773                         }
774                         break;
775                 }
776                 if (tp == limit)
777                         tp = bufenlarge(target, &limit);
778                 *tp++ = c;
779         }
780 }
781
782
783         static char *
784 checkidentifier(id, delimiter, dotok)
785         register char *id;
786         int delimiter;
787         register int dotok;
788 /*   Function:  check whether the string starting at id is an   */
789 /*              identifier and return a pointer to the delimiter*/
790 /*              after the identifier.  White space, delim and 0 */
791 /*              are legal delimiters.  Aborts the program if not*/
792 /*              a legal identifier. Useful for checking commands*/
793 /*              If !delim, the only delimiter is 0.             */
794 /*              Allow '.' in identifier only if DOTOK is set.   */
795 {
796         register char    *temp;
797         register char c;
798         register char delim = delimiter;
799         int isid = false;
800
801         temp = id;
802         for (;;  id++) {
803                 switch (ctab[(unsigned char)(c = *id)]) {
804                         case IDCHAR:
805                         case LETTER:
806                         case Letter:
807                                 isid = true;
808                                 continue;
809
810                         case DIGIT:
811                                 continue;
812
813                         case PERIOD:
814                                 if (dotok)
815                                         continue;
816                                 break;
817
818                         default:
819                                 break;
820                 }
821                 break;
822         }
823         if (     ! isid
824             ||   (c  &&  (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
825         ) {
826                 /* append \0 to end of id before error message */
827                 while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
828                     id++;
829                 *id = '\0';
830                 faterror("invalid %s `%s'",
831                         dotok ? "identifier" : "symbol", temp
832                 );
833         }
834         return id;
835 }
836
837         char *
838 checkid(id, delimiter)
839         char *id;
840         int delimiter;
841 {
842         return checkidentifier(id, delimiter, true);
843 }
844
845         char *
846 checksym(sym, delimiter)
847         char *sym;
848         int delimiter;
849 {
850         return checkidentifier(sym, delimiter, false);
851 }
852
853         void
854 checksid(id)
855         char *id;
856 /* Check whether the string ID is an identifier.  */
857 {
858         VOID checkid(id, 0);
859 }
860
861         void
862 checkssym(sym)
863         char *sym;
864 {
865         VOID checksym(sym, 0);
866 }
867
868
869 #if !large_memory
870 #   define Iclose(f) fclose(f)
871 #else
872 # if !maps_memory
873     static int Iclose P((RILE *));
874         static int
875     Iclose(f)
876         register RILE *f;
877     {
878         tfree(f->base);
879         f->base = 0;
880         return fclose(f->stream);
881     }
882 # else
883     static int Iclose P((RILE *));
884         static int
885     Iclose(f)
886         register RILE *f;
887     {
888         (* f->deallocate) (f);
889         f->base = 0;
890         return close(f->fd);
891     }
892
893 #   if has_map_fd
894         static void map_fd_deallocate P((RILE *));
895             static void
896         map_fd_deallocate(f)
897             register RILE *f;
898         {
899             if (vm_deallocate(
900                 task_self(),
901                 (vm_address_t) f->base,
902                 (vm_size_t) (f->lim - f->base)
903             ) != KERN_SUCCESS)
904                 efaterror("vm_deallocate");
905         }
906 #   endif
907 #   if has_mmap
908         static void mmap_deallocate P((RILE *));
909             static void
910         mmap_deallocate(f)
911             register RILE *f;
912         {
913             if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
914                 efaterror("munmap");
915         }
916 #   endif
917     static void read_deallocate P((RILE *));
918         static void
919     read_deallocate(f)
920         RILE *f;
921     {
922         tfree(f->base);
923     }
924
925     static void nothing_to_deallocate P((RILE *));
926         static void
927     nothing_to_deallocate(f)
928         RILE *f;
929     {
930     }
931 # endif
932 #endif
933
934
935 #if large_memory && maps_memory
936         static RILE *fd2_RILE P((int,char const*,struct stat*));
937         static RILE *
938 fd2_RILE(fd, name, status)
939 #else
940         static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
941         static RILE *
942 fd2RILE(fd, name, type, status)
943         char const *type;
944 #endif
945         int fd;
946         char const *name;
947         register struct stat *status;
948 {
949         struct stat st;
950
951         if (!status)
952                 status = &st;
953         if (fstat(fd, status) != 0)
954                 efaterror(name);
955         if (!S_ISREG(status->st_mode)) {
956                 error("`%s' is not a regular file", name);
957                 VOID close(fd);
958                 errno = EINVAL;
959                 return 0;
960         } else {
961
962 #           if !(large_memory && maps_memory)
963                 FILE *stream;
964                 if (!(stream = fdopen(fd, type)))
965                         efaterror(name);
966 #           endif
967
968 #           if !large_memory
969                 return stream;
970 #           else
971 #               define RILES 3
972               {
973                 static RILE rilebuf[RILES];
974
975                 register RILE *f;
976                 size_t s = status->st_size;
977
978                 if (s != status->st_size)
979                         faterror("%s: too large", name);
980                 for (f = rilebuf;  f->base;  f++)
981                         if (f == rilebuf+RILES)
982                                 faterror("too many RILEs");
983 #               if maps_memory
984                         f->deallocate = nothing_to_deallocate;
985 #               endif
986                 if (!s) {
987                     static unsigned char nothing;
988                     f->base = &nothing; /* Any nonzero address will do.  */
989                 } else {
990                     f->base = 0;
991 #                   if has_map_fd
992                         map_fd(
993                                 fd, (vm_offset_t)0, (vm_address_t*) &f->base,
994                                 TRUE, (vm_size_t)s
995                         );
996                         f->deallocate = map_fd_deallocate;
997 #                   endif
998 #                   if has_mmap
999                         if (!f->base) {
1000                             catchmmapints();
1001                             f->base = (unsigned char *) mmap(
1002                                 (char *)0, s, PROT_READ, MAP_SHARED,
1003                                 fd, (off_t)0
1004                             );
1005 #                           ifndef MAP_FAILED
1006 #                           define MAP_FAILED (-1)
1007 #                           endif
1008                             if (f->base == (unsigned char *) MAP_FAILED)
1009                                 f->base = 0;
1010                             else {
1011 #                               if has_NFS && mmap_signal
1012                                     /*
1013                                     * On many hosts, the superuser
1014                                     * can mmap an NFS file it can't read.
1015                                     * So access the first page now, and print
1016                                     * a nice message if a bus error occurs.
1017                                     */
1018                                     readAccessFilenameBuffer(name, f->base);
1019 #                               endif
1020                             }
1021                             f->deallocate = mmap_deallocate;
1022                         }
1023 #                   endif
1024                     if (!f->base) {
1025                         f->base = tnalloc(unsigned char, s);
1026 #                       if maps_memory
1027                         {
1028                             /*
1029                             * We can't map the file into memory for some reason.
1030                             * Read it into main memory all at once; this is
1031                             * the simplest substitute for memory mapping.
1032                             */
1033                             char *bufptr = (char *) f->base;
1034                             size_t bufsiz = s;
1035                             do {
1036                                 ssize_t r = read(fd, bufptr, bufsiz);
1037                                 switch (r) {
1038                                     case -1:
1039                                         efaterror(name);
1040
1041                                     case 0:
1042                                         /* The file must have shrunk!  */
1043                                         status->st_size = s -= bufsiz;
1044                                         bufsiz = 0;
1045                                         break;
1046
1047                                     default:
1048                                         bufptr += r;
1049                                         bufsiz -= r;
1050                                         break;
1051                                 }
1052                             } while (bufsiz);
1053                             if (lseek(fd, (off_t)0, SEEK_SET) == -1)
1054                                 efaterror(name);
1055                             f->deallocate = read_deallocate;
1056                         }
1057 #                       endif
1058                     }
1059                 }
1060                 f->ptr = f->base;
1061                 f->lim = f->base + s;
1062                 f->fd = fd;
1063 #               if !maps_memory
1064                     f->readlim = f->base;
1065                     f->stream = stream;
1066 #               endif
1067                 if_advise_access(s, f, MADV_SEQUENTIAL);
1068                 return f;
1069               }
1070 #           endif
1071         }
1072 }
1073
1074 #if !maps_memory && large_memory
1075         int
1076 Igetmore(f)
1077         register RILE *f;
1078 {
1079         register fread_type r;
1080         register size_t s = f->lim - f->readlim;
1081
1082         if (BUFSIZ < s)
1083                 s = BUFSIZ;
1084         if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
1085                 testIerror(f->stream);
1086                 f->lim = f->readlim;  /* The file might have shrunk!  */
1087                 return 0;
1088         }
1089         f->readlim += r;
1090         return 1;
1091 }
1092 #endif
1093
1094 #if has_madvise && has_mmap && large_memory
1095         void
1096 advise_access(f, advice)
1097         register RILE *f;
1098         int advice;
1099 {
1100     if (f->deallocate == mmap_deallocate)
1101         VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
1102         /* Don't worry if madvise fails; it's only advisory.  */
1103 }
1104 #endif
1105
1106         RILE *
1107 #if large_memory && maps_memory
1108 I_open(name, status)
1109 #else
1110 Iopen(name, type, status)
1111         char const *type;
1112 #endif
1113         char const *name;
1114         struct stat *status;
1115 /* Open NAME for reading, yield its descriptor, and set *STATUS.  */
1116 {
1117         int fd = fdSafer(open(name, O_RDONLY
1118 #               if OPEN_O_BINARY
1119                         |  (strchr(type,'b') ? OPEN_O_BINARY : 0)
1120 #               endif
1121         ));
1122
1123         if (fd < 0)
1124                 return 0;
1125 #       if large_memory && maps_memory
1126                 return fd2_RILE(fd, name, status);
1127 #       else
1128                 return fd2RILE(fd, name, type, status);
1129 #       endif
1130 }
1131
1132
1133 static int Oerrloop;
1134
1135         void
1136 Oerror()
1137 {
1138         if (Oerrloop)
1139                 exiterr();
1140         Oerrloop = true;
1141         efaterror("output error");
1142 }
1143
1144 void Ieof() { fatserror("unexpected end of file"); }
1145 void Ierror() { efaterror("input error"); }
1146 void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
1147 void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
1148
1149 void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
1150 void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
1151 void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
1152 void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
1153
1154 #if !large_memory
1155         void
1156 testIeof(f)
1157         FILE *f;
1158 {
1159         testIerror(f);
1160         if (feof(f))
1161                 Ieof();
1162 }
1163 void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
1164 #endif
1165
1166 void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1167
1168 void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
1169 void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
1170 void oflush()
1171 {
1172         if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
1173                 Oerror();
1174 }
1175
1176         void
1177 fatcleanup(already_newline)
1178         int already_newline;
1179 {
1180         VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
1181         exiterr();
1182 }
1183
1184         static void
1185 startsay(s, t)
1186         const char *s, *t;
1187 {
1188         oflush();
1189         if (s)
1190             aprintf(stderr, "%s: %s: %s", cmdid, s, t);
1191         else
1192             aprintf(stderr, "%s: %s", cmdid, t);
1193 }
1194
1195         static void
1196 fatsay(s)
1197         char const *s;
1198 {
1199         startsay(s, "");
1200 }
1201
1202         static void
1203 errsay(s)
1204         char const *s;
1205 {
1206         fatsay(s);
1207         nerror++;
1208 }
1209
1210         static void
1211 warnsay(s)
1212         char const *s;
1213 {
1214         startsay(s, "warning: ");
1215 }
1216
1217 void eerror(s) char const *s; { enerror(errno,s); }
1218
1219         void
1220 enerror(e,s)
1221         int e;
1222         char const *s;
1223 {
1224         errsay((char const*)0);
1225         errno = e;
1226         perror(s);
1227         eflush();
1228 }
1229
1230 void efaterror(s) char const *s; { enfaterror(errno,s); }
1231
1232         void
1233 enfaterror(e,s)
1234         int e;
1235         char const *s;
1236 {
1237         fatsay((char const*)0);
1238         errno = e;
1239         perror(s);
1240         fatcleanup(true);
1241 }
1242
1243 #if has_prototypes
1244         void
1245 error(char const *format,...)
1246 #else
1247         /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
1248 #endif
1249 /* non-fatal error */
1250 {
1251         va_list args;
1252         errsay((char const*)0);
1253         vararg_start(args, format);
1254         fvfprintf(stderr, format, args);
1255         va_end(args);
1256         afputc('\n',stderr);
1257         eflush();
1258 }
1259
1260 #if has_prototypes
1261         void
1262 rcserror(char const *format,...)
1263 #else
1264         /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
1265 #endif
1266 /* non-fatal RCS file error */
1267 {
1268         va_list args;
1269         errsay(RCSname);
1270         vararg_start(args, format);
1271         fvfprintf(stderr, format, args);
1272         va_end(args);
1273         afputc('\n',stderr);
1274         eflush();
1275 }
1276
1277 #if has_prototypes
1278         void
1279 workerror(char const *format,...)
1280 #else
1281         /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
1282 #endif
1283 /* non-fatal working file error */
1284 {
1285         va_list args;
1286         errsay(workname);
1287         vararg_start(args, format);
1288         fvfprintf(stderr, format, args);
1289         va_end(args);
1290         afputc('\n',stderr);
1291         eflush();
1292 }
1293
1294 #if has_prototypes
1295         void
1296 fatserror(char const *format,...)
1297 #else
1298         /*VARARGS1*/ void
1299         fatserror(format, va_alist) char const *format; va_dcl
1300 #endif
1301 /* fatal RCS file syntax error */
1302 {
1303         va_list args;
1304         oflush();
1305         VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
1306         vararg_start(args, format);
1307         fvfprintf(stderr, format, args);
1308         va_end(args);
1309         fatcleanup(false);
1310 }
1311
1312 #if has_prototypes
1313         void
1314 faterror(char const *format,...)
1315 #else
1316         /*VARARGS1*/ void faterror(format, va_alist)
1317         char const *format; va_dcl
1318 #endif
1319 /* fatal error, terminates program after cleanup */
1320 {
1321         va_list args;
1322         fatsay((char const*)0);
1323         vararg_start(args, format);
1324         fvfprintf(stderr, format, args);
1325         va_end(args);
1326         fatcleanup(false);
1327 }
1328
1329 #if has_prototypes
1330         void
1331 rcsfaterror(char const *format,...)
1332 #else
1333         /*VARARGS1*/ void rcsfaterror(format, va_alist)
1334         char const *format; va_dcl
1335 #endif
1336 /* fatal RCS file error, terminates program after cleanup */
1337 {
1338         va_list args;
1339         fatsay(RCSname);
1340         vararg_start(args, format);
1341         fvfprintf(stderr, format, args);
1342         va_end(args);
1343         fatcleanup(false);
1344 }
1345
1346 #if has_prototypes
1347         void
1348 warn(char const *format,...)
1349 #else
1350         /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
1351 #endif
1352 /* warning */
1353 {
1354         va_list args;
1355         if (!quietflag) {
1356                 warnsay((char *)0);
1357                 vararg_start(args, format);
1358                 fvfprintf(stderr, format, args);
1359                 va_end(args);
1360                 afputc('\n', stderr);
1361                 eflush();
1362         }
1363 }
1364
1365 #if has_prototypes
1366         void
1367 rcswarn(char const *format,...)
1368 #else
1369         /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
1370 #endif
1371 /* RCS file warning */
1372 {
1373         va_list args;
1374         if (!quietflag) {
1375                 warnsay(RCSname);
1376                 vararg_start(args, format);
1377                 fvfprintf(stderr, format, args);
1378                 va_end(args);
1379                 afputc('\n', stderr);
1380                 eflush();
1381         }
1382 }
1383
1384 #if has_prototypes
1385         void
1386 workwarn(char const *format,...)
1387 #else
1388         /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
1389 #endif
1390 /* working file warning */
1391 {
1392         va_list args;
1393         if (!quietflag) {
1394                 warnsay(workname);
1395                 vararg_start(args, format);
1396                 fvfprintf(stderr, format, args);
1397                 va_end(args);
1398                 afputc('\n', stderr);
1399                 eflush();
1400         }
1401 }
1402
1403         void
1404 redefined(c)
1405         int c;
1406 {
1407         warn("redefinition of -%c option", c);
1408 }
1409
1410 #if has_prototypes
1411         void
1412 diagnose(char const *format,...)
1413 #else
1414         /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
1415 #endif
1416 /* prints a diagnostic message */
1417 /* Unlike the other routines, it does not append a newline. */
1418 /* This lets some callers suppress the newline, and is faster */
1419 /* in implementations that flush stderr just at the end of each printf. */
1420 {
1421         va_list args;
1422         if (!quietflag) {
1423                 oflush();
1424                 vararg_start(args, format);
1425                 fvfprintf(stderr, format, args);
1426                 va_end(args);
1427                 eflush();
1428         }
1429 }
1430
1431
1432
1433         void
1434 afputc(c, f)
1435 /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower.  */
1436         int c;
1437         register FILE *f;
1438 {
1439         aputc_(c,f)
1440 }
1441
1442
1443         void
1444 aputs(s, iop)
1445         char const *s;
1446         FILE *iop;
1447 /* Function: Put string s on file iop, abort on error.
1448  */
1449 {
1450 #if has_fputs
1451         if (fputs(s, iop) < 0)
1452                 Oerror();
1453 #else
1454         awrite(s, strlen(s), iop);
1455 #endif
1456 }
1457
1458
1459
1460         void
1461 #if has_prototypes
1462 fvfprintf(FILE *stream, char const *format, va_list args)
1463 #else
1464         fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
1465 #endif
1466 /* like vfprintf, except abort program on error */
1467 {
1468 #if has_vfprintf
1469         if (vfprintf(stream, format, args) < 0)
1470                 Oerror();
1471 #else
1472 #       if has__doprintf
1473                 _doprintf(stream, format, args);
1474 #       else
1475 #       if has__doprnt
1476                 _doprnt(format, args, stream);
1477 #       else
1478                 int *a = (int *)args;
1479                 VOID fprintf(stream, format,
1480                         a[0], a[1], a[2], a[3], a[4],
1481                         a[5], a[6], a[7], a[8], a[9]
1482                 );
1483 #       endif
1484 #       endif
1485         if (ferror(stream))
1486                 Oerror();
1487 #endif
1488 }
1489
1490 #if has_prototypes
1491         void
1492 aprintf(FILE *iop, char const *fmt, ...)
1493 #else
1494         /*VARARGS2*/ void
1495 aprintf(iop, fmt, va_alist)
1496 FILE *iop;
1497 char const *fmt;
1498 va_dcl
1499 #endif
1500 /* Function: formatted output. Same as fprintf in stdio,
1501  * but aborts program on error
1502  */
1503 {
1504         va_list ap;
1505         vararg_start(ap, fmt);
1506         fvfprintf(iop, fmt, ap);
1507         va_end(ap);
1508 }
1509
1510
1511
1512 #ifdef LEXDB
1513 /* test program reading a stream of lexemes and printing the tokens.
1514  */
1515
1516
1517
1518         int
1519 main(argc,argv)
1520 int argc; char * argv[];
1521 {
1522         cmdid="lextest";
1523         if (argc<2) {
1524                 aputs("No input file\n",stderr);
1525                 exitmain(EXIT_FAILURE);
1526         }
1527         if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
1528                 faterror("can't open input file %s",argv[1]);
1529         }
1530         Lexinit();
1531         while (!eoflex()) {
1532         switch (nexttok) {
1533
1534         case ID:
1535                 VOID printf("ID: %s",NextString);
1536                 break;
1537
1538         case NUM:
1539                 if (hshenter)
1540                    VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
1541                 else
1542                    VOID printf("NUM, unentered: %s",NextString);
1543                 hshenter = !hshenter; /*alternate between dates and numbers*/
1544                 break;
1545
1546         case COLON:
1547                 VOID printf("COLON"); break;
1548
1549         case SEMI:
1550                 VOID printf("SEMI"); break;
1551
1552         case STRING:
1553                 readstring();
1554                 VOID printf("STRING"); break;
1555
1556         case UNKN:
1557                 VOID printf("UNKN"); break;
1558
1559         default:
1560                 VOID printf("DEFAULT"); break;
1561         }
1562         VOID printf(" | ");
1563         nextlex();
1564         }
1565         exitmain(EXIT_SUCCESS);
1566 }
1567
1568 void exiterr() { _exit(EXIT_FAILURE); }
1569
1570
1571 #endif