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