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