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