xref: /dragonfly/gnu/usr.bin/rcs/lib/rcslex.c (revision 984263bc)
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: src/gnu/usr.bin/rcs/lib/rcslex.c,v 1.8 1999/08/27 23:36:47 peter Exp $")
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
1569