xref: /dragonfly/gnu/usr.bin/rcs/lib/rcslex.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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
warnignore()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
lookup(str)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
Lexinit()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
nextlex()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 int 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
eoflex()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 
getlex(token)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
getkeyopt(key)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
getkey(key)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
getkeystring(key)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 *
getid()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 
getnum()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
getphrases(key)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
readstring()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 int 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
printstring()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 int 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
savestring(target)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 int 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 *
checkidentifier(id,delimiter,dotok)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 *
checkid(id,delimiter)838 checkid(id, delimiter)
839           char *id;
840           int delimiter;
841 {
842           return checkidentifier(id, delimiter, true);
843 }
844 
845           char *
checksym(sym,delimiter)846 checksym(sym, delimiter)
847           char *sym;
848           int delimiter;
849 {
850           return checkidentifier(sym, delimiter, false);
851 }
852 
853           void
checksid(id)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
checkssym(sym)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
Iclose(f)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
Iclose(f)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
map_fd_deallocate(f)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
mmap_deallocate(f)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
read_deallocate(f)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
nothing_to_deallocate(f)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 *
fd2_RILE(fd,name,status)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
Igetmore(f)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
advise_access(f,advice)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
I_open(name,status)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
Oerror()1136 Oerror()
1137 {
1138           if (Oerrloop)
1139                     exiterr();
1140           Oerrloop = true;
1141           efaterror("output error");
1142 }
1143 
Ieof()1144 void Ieof() { fatserror("unexpected end of file"); }
Ierror()1145 void Ierror() { efaterror("input error"); }
testIerror(f)1146 void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
testOerror(o)1147 void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
1148 
Ifclose(f)1149 void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
Ofclose(f)1150 void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
Izclose(p)1151 void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
Ozclose(p)1152 void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
1153 
1154 #if !large_memory
1155           void
testIeof(f)1156 testIeof(f)
1157           FILE *f;
1158 {
1159           testIerror(f);
1160           if (feof(f))
1161                     Ieof();
1162 }
Irewind(f)1163 void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
1164 #endif
1165 
Orewind(f)1166 void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1167 
aflush(f)1168 void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
eflush()1169 void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
oflush()1170 void oflush()
1171 {
1172           if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
1173                     Oerror();
1174 }
1175 
1176           void
fatcleanup(already_newline)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
startsay(s,t)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
fatsay(s)1196 fatsay(s)
1197           char const *s;
1198 {
1199           startsay(s, "");
1200 }
1201 
1202           static void
errsay(s)1203 errsay(s)
1204           char const *s;
1205 {
1206           fatsay(s);
1207           nerror++;
1208 }
1209 
1210           static void
warnsay(s)1211 warnsay(s)
1212           char const *s;
1213 {
1214           startsay(s, "warning: ");
1215 }
1216 
eerror(s)1217 void eerror(s) char const *s; { enerror(errno,s); }
1218 
1219           void
enerror(e,s)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 
efaterror(s)1230 void efaterror(s) char const *s; { enfaterror(errno,s); }
1231 
1232           void
enfaterror(e,s)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
error(char const * format,...)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
rcserror(char const * format,...)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
workerror(char const * format,...)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
fatserror(char const * format,...)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
faterror(char const * format,...)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
rcsfaterror(char const * format,...)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
warn(char const * format,...)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
rcswarn(char const * format,...)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
workwarn(char const * format,...)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
redefined(c)1404 redefined(c)
1405           int c;
1406 {
1407           warn("redefinition of -%c option", c);
1408 }
1409 
1410 #if has_prototypes
1411           void
diagnose(char const * format,...)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
afputc(c,f)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
aputs(s,iop)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
fvfprintf(FILE * stream,char const * format,va_list args)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
aprintf(FILE * iop,char const * fmt,...)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
main(argc,argv)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 
exiterr()1568 void exiterr() { _exit(EXIT_FAILURE); }
1569 
1570 
1571 #endif
1572