1 /* $MirOS: src/gnu/usr.bin/rcs/src/rcslex.c,v 1.3 2005/03/15 20:15:15 tg Exp $ */
2 
3 /* lexical analysis of RCS files */
4 
5 /******************************************************************************
6  *                     Lexical Analysis.
7  *                     hashtable, Lexinit, nextlex, getlex, getkey,
8  *                     getid, getnum, readstring, printstring, savestring,
9  *                     checkid, fatserror, error, faterror, warn, diagnose
10  *                     Testprogram: define LEXDB
11  ******************************************************************************
12  */
13 
14 /* Copyright 1982, 1988, 1989 Walter Tichy
15    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
16    Distributed under license by the Free Software Foundation, Inc.
17 
18 This file is part of RCS.
19 
20 RCS is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2, or (at your option)
23 any later version.
24 
25 RCS is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28 GNU General Public License for more details.
29 
30 You should have received a copy of the GNU General Public License
31 along with RCS; see the file COPYING.
32 If not, write to the Free Software Foundation,
33 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 
35 Report problems and direct all questions to:
36 
37     rcs-bugs@cs.purdue.edu
38 
39 */
40 
41 
42 
43 /*
44  * $Log: rcslex.c,v $
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 __RCSID("$MirOS: src/gnu/usr.bin/rcs/src/rcslex.c,v 1.3 2005/03/15 20:15:15 tg Exp $");
175 
176 static char *checkidentifier(char*,int,int);
177 static void errsay(char const*);
178 static void fatsay(char const*);
179 static void lookup(char const*);
180 static void startsay(const char*,const char*);
181 static void warnsay(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 				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 #	define savech_(c) ;
540 
541     if (nexttok!=ID  ||  strcmp(NextString,key) == 0)
542 	clear_buf(&r);
543     else {
544 	warnignore();
545 	fin = finptr;
546 	frew = foutptr;
547 	setupcache(fin); cache(fin);
548 	    r.string = (char const*)cacheptr() - strlen(NextString) - 1;
549 	ffree1(NextString);
550 	c = nextc;
551 	for (;;) {
552 	    for (;;) {
553 		savech_(c)
554 		switch (ctab[c]) {
555 		    default:
556 			fatserror("unknown character `%c'", c);
557 			/*NOTREACHED*/
558 		    case NEWLN:
559 			++rcsline;
560 			/* fall into */
561 		    case COLON: case DIGIT: case LETTER: case Letter:
562 		    case PERIOD: case SPACE:
563 			GETC_(frew, c)
564 			continue;
565 		    case SBEGIN: /* long string */
566 			for (;;) {
567 			    for (;;) {
568 				GETC_(frew, c)
569 				savech_(c)
570 				switch (c) {
571 				    case '\n':
572 					++rcsline;
573 					/* fall into */
574 				    default:
575 					continue;
576 
577 				    case SDELIM:
578 					break;
579 				}
580 				break;
581 			    }
582 			    GETC_(frew, c)
583 			    if (c != SDELIM)
584 				break;
585 			    savech_(c)
586 			}
587 			continue;
588 		    case SEMI:
589 			cacheget_(c)
590 			if (ctab[c] == NEWLN) {
591 			    if (frew)
592 				aputc_(c, frew)
593 			    ++rcsline;
594 			    savech_(c)
595 			    cacheget_(c)
596 			}
597 			    r.size = (char const*)cacheptr() - 1 - r.string;
598 			for (;;) {
599 			    switch (ctab[c]) {
600 				case NEWLN:
601 					++rcsline;
602 					/* fall into */
603 				case SPACE:
604 					cacheget_(c)
605 					continue;
606 
607 				default: break;
608 			    }
609 			    break;
610 			}
611 			if (frew)
612 			    aputc_(c, frew)
613 			break;
614 		}
615 		break;
616 	    }
617 	    if (ctab[c] == Letter) {
618 		    for (kn = key;  c && *kn==c;  kn++)
619 			GETC_(frew, c)
620 		    if (!*kn)
621 			switch (ctab[c]) {
622 			    case DIGIT: case LETTER: case Letter:
623 			    case IDCHAR: case PERIOD:
624 				break;
625 			    default:
626 				nextc = c;
627 				NextString = fstr_save(key);
628 				nexttok = ID;
629 				uncache(fin);
630 				goto returnit;
631 			}
632 	    } else {
633 		    nextc = c;
634 		    uncache(fin);
635 		    nextlex();
636 		    break;
637 	    }
638 	}
639     returnit:;
640     }
641     return r;
642 }
643 
644 
645 	void
readstring()646 readstring()
647 /* skip over characters until terminating single SDELIM        */
648 /* If foutptr is set, copy every character read to foutptr.    */
649 /* Does not advance nextlex at the end.                        */
650 {       register int c;
651 	declarecache;
652 	register FILE *frew;
653 	register RILE *fin;
654 	fin=finptr; frew=foutptr;
655 	setupcache(fin); cache(fin);
656 	for (;;) {
657 		GETC_(frew, c)
658 		switch (c) {
659 		    case '\n':
660 			++rcsline;
661 			break;
662 
663 		    case SDELIM:
664 			GETC_(frew, c)
665 			if (c != SDELIM) {
666 				/* end of string */
667 				nextc = c;
668 				uncache(fin);
669 				return;
670 			}
671 			break;
672 		}
673 	}
674 }
675 
676 
677 	void
printstring()678 printstring()
679 /* Function: copy a string to stdout, until terminated with a single SDELIM.
680  * Does not advance nextlex at the end.
681  */
682 {
683         register int c;
684 	declarecache;
685 	register FILE *fout;
686 	register RILE *fin;
687 	fin=finptr;
688 	fout = stdout;
689 	setupcache(fin); cache(fin);
690 	for (;;) {
691 		cacheget_(c)
692 		switch (c) {
693 		    case '\n':
694 			++rcsline;
695 			break;
696 		    case SDELIM:
697 			cacheget_(c)
698 			if (c != SDELIM) {
699                                 nextc=c;
700 				uncache(fin);
701                                 return;
702                         }
703 			break;
704                 }
705 		aputc_(c,fout)
706         }
707 }
708 
709 
710 
711 	struct cbuf
savestring(target)712 savestring(target)
713 	struct buf *target;
714 /* Copies a string terminated with SDELIM from file finptr to buffer target.
715  * Double SDELIM is replaced with SDELIM.
716  * If foutptr is set, the string is also copied unchanged to foutptr.
717  * Does not advance nextlex at the end.
718  * Yield a copy of *TARGET, except with exact length.
719  */
720 {
721         register int c;
722 	declarecache;
723 	register FILE *frew;
724 	register char *tp;
725 	register RILE *fin;
726 	char const *limit;
727 	struct cbuf r;
728 
729 	fin=finptr; frew=foutptr;
730 	setupcache(fin); cache(fin);
731 	tp = target->string;  limit = tp + target->size;
732 	for (;;) {
733 		GETC_(frew, c)
734 		switch (c) {
735 		    case '\n':
736 			++rcsline;
737 			break;
738 		    case SDELIM:
739 			GETC_(frew, c)
740 			if (c != SDELIM) {
741                                 /* end of string */
742                                 nextc=c;
743 				r.string = target->string;
744 				r.size = tp - r.string;
745 				uncache(fin);
746 				return r;
747                         }
748 			break;
749                 }
750 		if (tp == limit)
751 			tp = bufenlarge(target, &limit);
752 		*tp++ = c;
753         }
754 }
755 
756 
757 	static char *
checkidentifier(id,delimiter,dotok)758 checkidentifier(id, delimiter, dotok)
759 	register char *id;
760 	int delimiter;
761 	register int dotok;
762 /*   Function:  check whether the string starting at id is an   */
763 /*		identifier and return a pointer to the delimiter*/
764 /*		after the identifier.  White space, delim and 0 */
765 /*              are legal delimiters.  Aborts the program if not*/
766 /*              a legal identifier. Useful for checking commands*/
767 /*		If !delim, the only delimiter is 0.		*/
768 /*		Allow '.' in identifier only if DOTOK is set.   */
769 {
770         register char    *temp;
771 	register char c;
772 	register char delim = delimiter;
773 	int isid = false;
774 
775 	temp = id;
776 	for (;;  id++) {
777 		switch (ctab[(unsigned char)(c = *id)]) {
778 			case IDCHAR:
779 			case LETTER:
780 			case Letter:
781 				isid = true;
782 				continue;
783 
784 			case DIGIT:
785 				continue;
786 
787 			case PERIOD:
788 				if (dotok)
789 					continue;
790 				break;
791 
792 			default:
793 				break;
794 		}
795 		break;
796 	}
797 	if (	 ! isid
798 	    ||	 (c  &&  (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
799 	) {
800                 /* append \0 to end of id before error message */
801 		while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
802 		    id++;
803                 *id = '\0';
804 		faterror("invalid %s `%s'",
805 			dotok ? "identifier" : "symbol", temp
806 		);
807 	}
808 	return id;
809 }
810 
811 	char *
checkid(id,delimiter)812 checkid(id, delimiter)
813 	char *id;
814 	int delimiter;
815 {
816 	return checkidentifier(id, delimiter, true);
817 }
818 
819 	char *
checksym(sym,delimiter)820 checksym(sym, delimiter)
821 	char *sym;
822 	int delimiter;
823 {
824 	return checkidentifier(sym, delimiter, false);
825 }
826 
827 	void
checksid(id)828 checksid(id)
829 	char *id;
830 /* Check whether the string ID is an identifier.  */
831 {
832 	checkid(id, 0);
833 }
834 
835 	void
checkssym(sym)836 checkssym(sym)
837 	char *sym;
838 {
839 	checksym(sym, 0);
840 }
841 
842 
843     static int Iclose(RILE *);
844 	static int
Iclose(f)845     Iclose(f)
846 	register RILE *f;
847     {
848 	(* f->deallocate) (f);
849 	f->base = 0;
850 	return close(f->fd);
851     }
852 
853 #   if has_map_fd
854 	static void map_fd_deallocate(RILE *);
855 	    static void
map_fd_deallocate(f)856 	map_fd_deallocate(f)
857 	    register RILE *f;
858 	{
859 	    if (vm_deallocate(
860 		task_self(),
861 		(vm_address_t) f->base,
862 		(vm_size_t) (f->lim - f->base)
863 	    ) != KERN_SUCCESS)
864 		efaterror("vm_deallocate");
865 	}
866 #   endif
867 	static void mmap_deallocate(RILE *);
868 	    static void
mmap_deallocate(f)869 	mmap_deallocate(f)
870 	    register RILE *f;
871 	{
872 	    if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
873 		efaterror("munmap");
874 	}
875     static void read_deallocate(RILE *);
876 	static void
read_deallocate(f)877     read_deallocate(f)
878 	RILE *f;
879     {
880 	tfree(f->base);
881     }
882 
883     static void nothing_to_deallocate(RILE *);
884 	static void
nothing_to_deallocate(f)885     nothing_to_deallocate(f)
886 	RILE *f __attribute__((unused));
887     {
888     }
889 
890 
891 	static RILE *fd2_RILE(int,char const*,struct stat*);
892 	static RILE *
fd2_RILE(fd,name,status)893 fd2_RILE(fd, name, status)
894 	int fd;
895 	char const *name;
896 	register struct stat *status;
897 {
898 	struct stat st;
899 
900 	if (!status)
901 		status = &st;
902 	if (fstat(fd, status) != 0)
903 		efaterror(name);
904 	if (!S_ISREG(status->st_mode)) {
905 		error("`%s' is not a regular file", name);
906 		close(fd);
907 		errno = EINVAL;
908 		return 0;
909 	} else {
910 
911 #	    if !(large_memory && maps_memory)
912 		FILE *stream;
913 		if (!(stream = fdopen(fd, type)))
914 			efaterror(name);
915 #	    endif
916 
917 #	    if !large_memory
918 		return stream;
919 #	    else
920 #		define RILES 3
921 	      {
922 		static RILE rilebuf[RILES];
923 
924 		register RILE *f;
925 		size_t s = status->st_size;
926 
927 		if (s != status->st_size)
928 			faterror("%s: too large", name);
929 		for (f = rilebuf;  f->base;  f++)
930 			if (f == rilebuf+RILES)
931 				faterror("too many RILEs");
932 #		if maps_memory
933 			f->deallocate = nothing_to_deallocate;
934 #		endif
935 		if (!s) {
936 		    static unsigned char nothing;
937 		    f->base = &nothing; /* Any nonzero address will do.  */
938 		} else {
939 		    f->base = 0;
940 #		    if has_map_fd
941 			map_fd(
942 				fd, (vm_offset_t)0, (vm_address_t*) &f->base,
943 				TRUE, (vm_size_t)s
944 			);
945 			f->deallocate = map_fd_deallocate;
946 #		    endif
947 #		    if has_mmap
948 			if (!f->base) {
949 			    catchmmapints();
950 			    f->base = (unsigned char *) mmap(
951 				(char *)0, s, PROT_READ, MAP_SHARED,
952 				fd, (off_t)0
953 			    );
954 #			    ifndef MAP_FAILED
955 #			    define MAP_FAILED (-1)
956 #			    endif
957 			    if (f->base == (unsigned char *) MAP_FAILED)
958 				f->base = 0;
959 			    else {
960 #				if has_NFS && mmap_signal
961 				    /*
962 				    * On many hosts, the superuser
963 				    * can mmap an NFS file it can't read.
964 				    * So access the first page now, and print
965 				    * a nice message if a bus error occurs.
966 				    */
967 				    readAccessFilenameBuffer(name, f->base);
968 #				endif
969 			    }
970 			    f->deallocate = mmap_deallocate;
971 			}
972 #		    endif
973 		    if (!f->base) {
974 			f->base = tnalloc(unsigned char, s);
975 #			if maps_memory
976 			{
977 			    /*
978 			    * We can't map the file into memory for some reason.
979 			    * Read it into main memory all at once; this is
980 			    * the simplest substitute for memory mapping.
981 			    */
982 			    char *bufptr = (char *) f->base;
983 			    size_t bufsiz = s;
984 			    do {
985 				ssize_t r = read(fd, bufptr, bufsiz);
986 				switch (r) {
987 				    case -1:
988 					efaterror(name);
989 
990 				    case 0:
991 					/* The file must have shrunk!  */
992 					status->st_size = s -= bufsiz;
993 					bufsiz = 0;
994 					break;
995 
996 				    default:
997 					bufptr += r;
998 					bufsiz -= r;
999 					break;
1000 				}
1001 			    } while (bufsiz);
1002 			    if (lseek(fd, (off_t)0, SEEK_SET) == -1)
1003 				efaterror(name);
1004 			    f->deallocate = read_deallocate;
1005 			}
1006 #			endif
1007 		    }
1008 		}
1009 		f->ptr = f->base;
1010 		f->lim = f->base + s;
1011 		f->fd = fd;
1012 #		if !maps_memory
1013 		    f->readlim = f->base;
1014 		    f->stream = stream;
1015 #		endif
1016 		if_advise_access(s, f, MADV_SEQUENTIAL);
1017 		return f;
1018 	      }
1019 #	    endif
1020 	}
1021 }
1022 
1023 	void
advise_access(f,advice)1024 advise_access(f, advice)
1025 	register RILE *f;
1026 	int advice;
1027 {
1028     if (f->deallocate == mmap_deallocate)
1029 	madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
1030 	/* Don't worry if madvise fails; it's only advisory.  */
1031 }
1032 
1033 	RILE *
I_open(name,status)1034 I_open(name, status)
1035 	char const *name;
1036 	struct stat *status;
1037 /* Open NAME for reading, yield its descriptor, and set *STATUS.  */
1038 {
1039 	int fd = fdSafer(open(name, O_RDONLY));
1040 
1041 	if (fd < 0)
1042 		return 0;
1043 		return fd2_RILE(fd, name, status);
1044 }
1045 
1046 
1047 static int Oerrloop;
1048 
1049 	void
Oerror()1050 Oerror()
1051 {
1052 	if (Oerrloop)
1053 		exiterr();
1054 	Oerrloop = true;
1055 	efaterror("output error");
1056 }
1057 
Ieof()1058 void Ieof() { fatserror("unexpected end of file"); }
Ierror()1059 void Ierror() { efaterror("input error"); }
testIerror(f)1060 void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
testOerror(o)1061 void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
1062 
Ifclose(f)1063 void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
Ofclose(f)1064 void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
Izclose(p)1065 void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
Ozclose(p)1066 void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
1067 
Orewind(f)1068 void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1069 
aflush(f)1070 void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
eflush()1071 void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
oflush()1072 void oflush()
1073 {
1074 	if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
1075 		Oerror();
1076 }
1077 
1078 	void
fatcleanup(already_newline)1079 fatcleanup(already_newline)
1080 	int already_newline;
1081 {
1082 	fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
1083 	exiterr();
1084 }
1085 
1086 	static void
startsay(s,t)1087 startsay(s, t)
1088 	const char *s, *t;
1089 {
1090 	oflush();
1091 	if (s)
1092 	    aprintf(stderr, "%s: %s: %s", cmdid, s, t);
1093 	else
1094 	    aprintf(stderr, "%s: %s", cmdid, t);
1095 }
1096 
1097 	static void
fatsay(s)1098 fatsay(s)
1099 	char const *s;
1100 {
1101 	startsay(s, "");
1102 }
1103 
1104 	static void
errsay(s)1105 errsay(s)
1106 	char const *s;
1107 {
1108 	fatsay(s);
1109 	nerror++;
1110 }
1111 
1112 	static void
warnsay(s)1113 warnsay(s)
1114 	char const *s;
1115 {
1116 	startsay(s, "warning: ");
1117 }
1118 
eerror(s)1119 void eerror(s) char const *s; { enerror(errno,s); }
1120 
1121 	void
enerror(e,s)1122 enerror(e,s)
1123 	int e;
1124 	char const *s;
1125 {
1126 	errsay((char const*)0);
1127 	errno = e;
1128 	perror(s);
1129 	eflush();
1130 }
1131 
efaterror(s)1132 void efaterror(s) char const *s; { enfaterror(errno,s); }
1133 
1134 	void
enfaterror(e,s)1135 enfaterror(e,s)
1136 	int e;
1137 	char const *s;
1138 {
1139 	fatsay((char const*)0);
1140 	errno = e;
1141 	perror(s);
1142 	fatcleanup(true);
1143 }
1144 
1145 	void
error(char const * format,...)1146 error(char const *format,...)
1147 /* non-fatal error */
1148 {
1149 	va_list args;
1150 	errsay((char const*)0);
1151 	va_start(args, format);
1152 	fvfprintf(stderr, format, args);
1153 	va_end(args);
1154 	afputc('\n',stderr);
1155 	eflush();
1156 }
1157 
1158 	void
rcserror(char const * format,...)1159 rcserror(char const *format,...)
1160 /* non-fatal RCS file error */
1161 {
1162 	va_list args;
1163 	errsay(RCSname);
1164 	va_start(args, format);
1165 	fvfprintf(stderr, format, args);
1166 	va_end(args);
1167 	afputc('\n',stderr);
1168 	eflush();
1169 }
1170 
1171 	void
workerror(char const * format,...)1172 workerror(char const *format,...)
1173 /* non-fatal working file error */
1174 {
1175 	va_list args;
1176 	errsay(workname);
1177 	va_start(args, format);
1178 	fvfprintf(stderr, format, args);
1179 	va_end(args);
1180 	afputc('\n',stderr);
1181 	eflush();
1182 }
1183 
1184 	void
fatserror(char const * format,...)1185 fatserror(char const *format,...)
1186 /* fatal RCS file syntax error */
1187 {
1188 	va_list args;
1189 	oflush();
1190 	fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
1191 	va_start(args, format);
1192 	fvfprintf(stderr, format, args);
1193 	va_end(args);
1194 	fatcleanup(false);
1195 }
1196 
1197 	void
faterror(char const * format,...)1198 faterror(char const *format,...)
1199 /* fatal error, terminates program after cleanup */
1200 {
1201 	va_list args;
1202 	fatsay((char const*)0);
1203 	va_start(args, format);
1204 	fvfprintf(stderr, format, args);
1205 	va_end(args);
1206 	fatcleanup(false);
1207 }
1208 
1209 	void
rcsfaterror(char const * format,...)1210 rcsfaterror(char const *format,...)
1211 /* fatal RCS file error, terminates program after cleanup */
1212 {
1213 	va_list args;
1214 	fatsay(RCSname);
1215 	va_start(args, format);
1216 	fvfprintf(stderr, format, args);
1217 	va_end(args);
1218 	fatcleanup(false);
1219 }
1220 
1221 	void
warn(char const * format,...)1222 warn(char const *format,...)
1223 /* warning */
1224 {
1225 	va_list args;
1226 	if (!quietflag) {
1227 		warnsay((char *)0);
1228 		va_start(args, format);
1229 		fvfprintf(stderr, format, args);
1230 		va_end(args);
1231 		afputc('\n', stderr);
1232 		eflush();
1233 	}
1234 }
1235 
1236 	void
rcswarn(char const * format,...)1237 rcswarn(char const *format,...)
1238 /* RCS file warning */
1239 {
1240 	va_list args;
1241 	if (!quietflag) {
1242 		warnsay(RCSname);
1243 		va_start(args, format);
1244 		fvfprintf(stderr, format, args);
1245 		va_end(args);
1246 		afputc('\n', stderr);
1247 		eflush();
1248 	}
1249 }
1250 
1251 	void
workwarn(char const * format,...)1252 workwarn(char const *format,...)
1253 /* working file warning */
1254 {
1255 	va_list args;
1256 	if (!quietflag) {
1257 		warnsay(workname);
1258 		va_start(args, format);
1259 		fvfprintf(stderr, format, args);
1260 		va_end(args);
1261 		afputc('\n', stderr);
1262 		eflush();
1263 	}
1264 }
1265 
1266 	void
redefined(c)1267 redefined(c)
1268 	int c;
1269 {
1270 	warn("redefinition of -%c option", c);
1271 }
1272 
1273 	void
diagnose(char const * format,...)1274 diagnose(char const *format,...)
1275 /* prints a diagnostic message */
1276 /* Unlike the other routines, it does not append a newline. */
1277 /* This lets some callers suppress the newline, and is faster */
1278 /* in implementations that flush stderr just at the end of each printf. */
1279 {
1280 	va_list args;
1281         if (!quietflag) {
1282 		oflush();
1283 		va_start(args, format);
1284 		fvfprintf(stderr, format, args);
1285 		va_end(args);
1286 		eflush();
1287         }
1288 }
1289 
1290 
1291 
1292 	void
afputc(c,f)1293 afputc(c, f)
1294 /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower.  */
1295 	int c;
1296 	register FILE *f;
1297 {
1298 	aputc_(c,f)
1299 }
1300 
1301 
1302 	void
aputs(s,iop)1303 aputs(s, iop)
1304 	char const *s;
1305 	FILE *iop;
1306 /* Function: Put string s on file iop, abort on error.
1307  */
1308 {
1309 #if has_fputs
1310 	if (fputs(s, iop) < 0)
1311 		Oerror();
1312 #else
1313 	awrite(s, strlen(s), iop);
1314 #endif
1315 }
1316 
1317 
1318 
1319 	void
fvfprintf(FILE * stream,char const * format,va_list args)1320 fvfprintf(FILE *stream, char const *format, va_list args)
1321 /* like vfprintf, except abort program on error */
1322 {
1323 #if has_vfprintf
1324 	if (vfprintf(stream, format, args) < 0)
1325 		Oerror();
1326 #else
1327 #	if has__doprintf
1328 		_doprintf(stream, format, args);
1329 #	else
1330 #	if has__doprnt
1331 		_doprnt(format, args, stream);
1332 #	else
1333 		int *a = (int *)args;
1334 		fprintf(stream, format,
1335 			a[0], a[1], a[2], a[3], a[4],
1336 			a[5], a[6], a[7], a[8], a[9]
1337 		);
1338 #	endif
1339 #	endif
1340 	if (ferror(stream))
1341 		Oerror();
1342 #endif
1343 }
1344 
1345 	void
aprintf(FILE * iop,char const * fmt,...)1346 aprintf(FILE *iop, char const *fmt, ...)
1347 /* Function: formatted output. Same as fprintf in stdio,
1348  * but aborts program on error
1349  */
1350 {
1351 	va_list ap;
1352 	va_start(ap, fmt);
1353 	fvfprintf(iop, fmt, ap);
1354 	va_end(ap);
1355 }
1356 
1357 
1358 
1359 #ifdef LEXDB
1360 /* test program reading a stream of lexemes and printing the tokens.
1361  */
1362 
1363 
1364 
1365 	int
main(argc,argv)1366 main(argc,argv)
1367 int argc; char * argv[];
1368 {
1369         cmdid="lextest";
1370         if (argc<2) {
1371 		aputs("No input file\n",stderr);
1372 		return EXIT_FAILURE;
1373         }
1374 	if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
1375 		faterror("can't open input file %s",argv[1]);
1376         }
1377         Lexinit();
1378 	while (!eoflex()) {
1379         switch (nexttok) {
1380 
1381         case ID:
1382                 printf("ID: %s",NextString);
1383                 break;
1384 
1385         case NUM:
1386 		if (hshenter)
1387                    printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
1388                 else
1389                    printf("NUM, unentered: %s",NextString);
1390                 hshenter = !hshenter; /*alternate between dates and numbers*/
1391                 break;
1392 
1393         case COLON:
1394                 printf("COLON"); break;
1395 
1396         case SEMI:
1397                 printf("SEMI"); break;
1398 
1399         case STRING:
1400                 readstring();
1401                 printf("STRING"); break;
1402 
1403         case UNKN:
1404                 printf("UNKN"); break;
1405 
1406         default:
1407                 printf("DEFAULT"); break;
1408         }
1409         printf(" | ");
1410         nextlex();
1411         }
1412 	return EXIT_SUCCESS;
1413 }
1414 
exiterr()1415 void exiterr() { _exit(EXIT_FAILURE); }
1416 
1417 
1418 #endif
1419