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