1/*	$NetBSD: rcslex.c,v 1.2 2016/01/14 04:22:39 christos 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
18This file is part of RCS.
19
20RCS is free software; you can redistribute it and/or modify
21it under the terms of the GNU General Public License as published by
22the Free Software Foundation; either version 2, or (at your option)
23any later version.
24
25RCS is distributed in the hope that it will be useful,
26but WITHOUT ANY WARRANTY; without even the implied warranty of
27MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28GNU General Public License for more details.
29
30You should have received a copy of the GNU General Public License
31along with RCS; see the file COPYING.
32If not, write to the Free Software Foundation,
3359 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34
35Report 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
174libId(lexId, "Id: rcslex.c,v 5.19 1995/06/16 06:19:24 eggert Exp ")
175
176static char *checkidentifier P((char*,int,int));
177static void errsay P((char const*));
178static void fatsay P((char const*));
179static void lookup P((char const*));
180static void startsay P((const char*,const char*));
181static void warnsay P((char const*));
182
183static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
184
185enum tokens     nexttok;    /*next token, set by nextlex                    */
186
187int             hshenter;   /*if true, next suitable lexeme will be entered */
188                            /*into the symbol table. Handle with care.      */
189int             nextc;      /*next input character, initialized by Lexinit  */
190
191long		rcsline;    /*current line-number of input		    */
192int             nerror;     /*counter for errors                            */
193int             quietflag;  /*indicates quiet mode                          */
194RILE *		finptr;	    /*input file descriptor			    */
195
196FILE *          frewrite;   /*file descriptor for echoing input             */
197
198FILE *		foutptr;    /* copy of frewrite, but 0 to suppress echo  */
199
200static struct buf tokbuf;   /* token buffer				    */
201
202char 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
214static struct hshentry *hshtab[hshsize]; /*hashtable			    */
215
216static int ignored_phrases; /* have we ignored phrases in this RCS file? */
217
218    void
219warnignore()
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
230lookup(str)
231	char const *str;
232/* Function: Looks up the character string pointed to by str in the
233 * hashtable. If the string is not present, a new entry for it is created.
234 * In any case, the address of the corresponding hashtable entry is placed
235 * into nexthsh.
236 */
237{
238	register unsigned ihash;  /* index into hashtable */
239	register char const *sp;
240	register struct hshentry *n, **p;
241
242        /* calculate hash code */
243	sp = str;
244        ihash = 0;
245	while (*sp)
246		ihash  =  (ihash<<2) + *sp++;
247	ihash %= hshsize;
248
249	for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
250		if (!(n = *p)) {
251			/* empty slot found */
252			*p = n = ftalloc(struct hshentry);
253			n->num = fstr_save(str);
254			n->nexthsh = 0;
255#			ifdef LEXDB
256				VOID printf("\nEntered: %s at %u ", str, ihash);
257#			endif
258			break;
259		} else if (strcmp(str, n->num) == 0)
260			/* match found */
261			break;
262	nexthsh = n;
263	NextString = n->num;
264}
265
266
267
268
269
270
271	void
272Lexinit()
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
302nextlex()
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{       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
398eoflex()
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
434int getlex(token)
435enum 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
449getkeyopt(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
466getkey(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
477getkeystring(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 *
490getid()
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
507struct 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
524getphrases(key)
525	char const *key;
526/*
527* Get a series of phrases that do not start with KEY.  Yield resulting buffer.
528* Stop when the next phrase starts with a token that is not an identifier,
529* or is KEY.  Copy input to foutptr if it is set.  Unlike ignorephrases(),
530* this routine assumes nextlex() has already been invoked before we start.
531*/
532{
533    declarecache;
534    register int c;
535    register char const *kn;
536    struct cbuf r;
537    register RILE *fin;
538    register FILE *frew;
539#   if large_memory
540#	define savech_(c) ;
541#   else
542	register char *p;
543	char const *limit;
544	struct buf b;
545#	define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
546#   endif
547
548    if (nexttok!=ID  ||  strcmp(NextString,key) == 0)
549	clear_buf(&r);
550    else {
551	warnignore();
552	fin = finptr;
553	frew = foutptr;
554	setupcache(fin); cache(fin);
555#	if large_memory
556	    r.string = (char const*)cacheptr() - strlen(NextString) - 1;
557#	else
558	    bufautobegin(&b);
559	    bufscpy(&b, NextString);
560	    p = b.string + strlen(b.string);
561	    limit = b.string + b.size;
562#	endif
563	ffree1(NextString);
564	c = nextc;
565	for (;;) {
566	    for (;;) {
567		savech_(c)
568		switch (ctab[c]) {
569		    default:
570			fatserror("unknown character `%c'", c);
571			/*NOTREACHED*/
572		    case NEWLN:
573			++rcsline;
574			/* fall into */
575		    case COLON: case DIGIT: case LETTER: case Letter:
576		    case PERIOD: case SPACE:
577			GETC_(frew, c)
578			continue;
579		    case SBEGIN: /* long string */
580			for (;;) {
581			    for (;;) {
582				GETC_(frew, c)
583				savech_(c)
584				switch (c) {
585				    case '\n':
586					++rcsline;
587					/* fall into */
588				    default:
589					continue;
590
591				    case SDELIM:
592					break;
593				}
594				break;
595			    }
596			    GETC_(frew, c)
597			    if (c != SDELIM)
598				break;
599			    savech_(c)
600			}
601			continue;
602		    case SEMI:
603			cacheget_(c)
604			if (ctab[c] == NEWLN) {
605			    if (frew)
606				aputc_(c, frew)
607			    ++rcsline;
608			    savech_(c)
609			    cacheget_(c)
610			}
611#			if large_memory
612			    r.size = (char const*)cacheptr() - 1 - r.string;
613#			endif
614			for (;;) {
615			    switch (ctab[c]) {
616				case NEWLN:
617					++rcsline;
618					/* fall into */
619				case SPACE:
620					cacheget_(c)
621					continue;
622
623				default: break;
624			    }
625			    break;
626			}
627			if (frew)
628			    aputc_(c, frew)
629			break;
630		}
631		break;
632	    }
633	    if (ctab[c] == Letter) {
634		    for (kn = key;  c && *kn==c;  kn++)
635			GETC_(frew, c)
636		    if (!*kn)
637			switch (ctab[c]) {
638			    case DIGIT: case LETTER: case Letter:
639			    case IDCHAR: case PERIOD:
640				break;
641			    default:
642				nextc = c;
643				NextString = fstr_save(key);
644				nexttok = ID;
645				uncache(fin);
646				goto returnit;
647			}
648#		    if !large_memory
649			{
650			    register char const *ki;
651			    for (ki=key; ki<kn; )
652				savech_(*ki++)
653			}
654#		    endif
655	    } else {
656		    nextc = c;
657		    uncache(fin);
658		    nextlex();
659		    break;
660	    }
661	}
662    returnit:;
663#	if !large_memory
664	    return bufremember(&b, (size_t)(p - b.string));
665#	endif
666    }
667    return r;
668}
669
670
671	void
672readstring()
673/* skip over characters until terminating single SDELIM        */
674/* If foutptr is set, copy every character read to foutptr.    */
675/* Does not advance nextlex at the end.                        */
676{       int c;
677	declarecache;
678	register FILE *frew;
679	register RILE *fin;
680	fin=finptr; frew=foutptr;
681	setupcache(fin); cache(fin);
682	for (;;) {
683		GETC_(frew, c)
684		switch (c) {
685		    case '\n':
686			++rcsline;
687			break;
688
689		    case SDELIM:
690			GETC_(frew, c)
691			if (c != SDELIM) {
692				/* end of string */
693				nextc = c;
694				uncache(fin);
695				return;
696			}
697			break;
698		}
699	}
700}
701
702
703	void
704printstring()
705/* Function: copy a string to stdout, until terminated with a single SDELIM.
706 * Does not advance nextlex at the end.
707 */
708{
709        int c;
710	declarecache;
711	register FILE *fout;
712	register RILE *fin;
713	fin=finptr;
714	fout = stdout;
715	setupcache(fin); cache(fin);
716	for (;;) {
717		cacheget_(c)
718		switch (c) {
719		    case '\n':
720			++rcsline;
721			break;
722		    case SDELIM:
723			cacheget_(c)
724			if (c != SDELIM) {
725                                nextc=c;
726				uncache(fin);
727                                return;
728                        }
729			break;
730                }
731		aputc_(c,fout)
732        }
733}
734
735
736
737	struct cbuf
738savestring(target)
739	struct buf *target;
740/* Copies a string terminated with SDELIM from file finptr to buffer target.
741 * Double SDELIM is replaced with SDELIM.
742 * If foutptr is set, the string is also copied unchanged to foutptr.
743 * Does not advance nextlex at the end.
744 * Yield a copy of *TARGET, except with exact length.
745 */
746{
747        int c;
748	declarecache;
749	register FILE *frew;
750	register char *tp;
751	register RILE *fin;
752	char const *limit;
753	struct cbuf r;
754
755	fin=finptr; frew=foutptr;
756	setupcache(fin); cache(fin);
757	tp = target->string;  limit = tp + target->size;
758	for (;;) {
759		GETC_(frew, c)
760		switch (c) {
761		    case '\n':
762			++rcsline;
763			break;
764		    case SDELIM:
765			GETC_(frew, c)
766			if (c != SDELIM) {
767                                /* end of string */
768                                nextc=c;
769				r.string = target->string;
770				r.size = tp - r.string;
771				uncache(fin);
772				return r;
773                        }
774			break;
775                }
776		if (tp == limit)
777			tp = bufenlarge(target, &limit);
778		*tp++ = c;
779        }
780}
781
782
783	static char *
784checkidentifier(id, delimiter, dotok)
785	register char *id;
786	int delimiter;
787	register int dotok;
788/*   Function:  check whether the string starting at id is an   */
789/*		identifier and return a pointer to the delimiter*/
790/*		after the identifier.  White space, delim and 0 */
791/*              are legal delimiters.  Aborts the program if not*/
792/*              a legal identifier. Useful for checking commands*/
793/*		If !delim, the only delimiter is 0.		*/
794/*		Allow '.' in identifier only if DOTOK is set.   */
795{
796        register char    *temp;
797	register char c;
798	register char delim = delimiter;
799	int isid = false;
800
801	temp = id;
802	for (;;  id++) {
803		switch (ctab[(unsigned char)(c = *id)]) {
804			case IDCHAR:
805			case LETTER:
806			case Letter:
807				isid = true;
808				continue;
809
810			case DIGIT:
811				continue;
812
813			case PERIOD:
814				if (dotok)
815					continue;
816				break;
817
818			default:
819				break;
820		}
821		break;
822	}
823	if (	 ! isid
824	    ||	 (c  &&  (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
825	) {
826                /* append \0 to end of id before error message */
827		while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
828		    id++;
829                *id = '\0';
830		faterror("invalid %s `%s'",
831			dotok ? "identifier" : "symbol", temp
832		);
833	}
834	return id;
835}
836
837	char *
838checkid(id, delimiter)
839	char *id;
840	int delimiter;
841{
842	return checkidentifier(id, delimiter, true);
843}
844
845	char *
846checksym(sym, delimiter)
847	char *sym;
848	int delimiter;
849{
850	return checkidentifier(sym, delimiter, false);
851}
852
853	void
854checksid(id)
855	char *id;
856/* Check whether the string ID is an identifier.  */
857{
858	VOID checkid(id, 0);
859}
860
861	void
862checkssym(sym)
863	char *sym;
864{
865	VOID checksym(sym, 0);
866}
867
868
869#if !large_memory
870#   define Iclose(f) fclose(f)
871#else
872# if !maps_memory
873    static int Iclose P((RILE *));
874	static int
875    Iclose(f)
876	register RILE *f;
877    {
878	tfree(f->base);
879	f->base = 0;
880	return fclose(f->stream);
881    }
882# else
883    static int Iclose P((RILE *));
884	static int
885    Iclose(f)
886	register RILE *f;
887    {
888	(* f->deallocate) (f);
889	f->base = 0;
890	return close(f->fd);
891    }
892
893#   if has_map_fd
894	static void map_fd_deallocate P((RILE *));
895	    static void
896	map_fd_deallocate(f)
897	    register RILE *f;
898	{
899	    if (vm_deallocate(
900		task_self(),
901		(vm_address_t) f->base,
902		(vm_size_t) (f->lim - f->base)
903	    ) != KERN_SUCCESS)
904		efaterror("vm_deallocate");
905	}
906#   endif
907#   if has_mmap
908	static void mmap_deallocate P((RILE *));
909	    static void
910	mmap_deallocate(f)
911	    register RILE *f;
912	{
913	    if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
914		efaterror("munmap");
915	}
916#   endif
917    static void read_deallocate P((RILE *));
918	static void
919    read_deallocate(f)
920	RILE *f;
921    {
922	tfree(f->base);
923    }
924
925    static void nothing_to_deallocate P((RILE *));
926	static void
927    nothing_to_deallocate(f)
928	RILE *f;
929    {
930    }
931# endif
932#endif
933
934
935#if large_memory && maps_memory
936	static RILE *fd2_RILE P((int,char const*,struct stat*));
937	static RILE *
938fd2_RILE(fd, name, status)
939#else
940	static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
941	static RILE *
942fd2RILE(fd, name, type, status)
943	char const *type;
944#endif
945	int fd;
946	char const *name;
947	register struct stat *status;
948{
949	struct stat st;
950
951	if (!status)
952		status = &st;
953	if (fstat(fd, status) != 0)
954		efaterror(name);
955	if (!S_ISREG(status->st_mode)) {
956		error("`%s' is not a regular file", name);
957		VOID close(fd);
958		errno = EINVAL;
959		return 0;
960	} else {
961
962#	    if !(large_memory && maps_memory)
963		FILE *stream;
964		if (!(stream = fdopen(fd, type)))
965			efaterror(name);
966#	    endif
967
968#	    if !large_memory
969		return stream;
970#	    else
971#		define RILES 3
972	      {
973		static RILE rilebuf[RILES];
974
975		register RILE *f;
976		size_t s = status->st_size;
977
978		if (s != status->st_size)
979			faterror("%s: too large", name);
980		for (f = rilebuf;  f->base;  f++)
981			if (f == rilebuf+RILES)
982				faterror("too many RILEs");
983#		if maps_memory
984			f->deallocate = nothing_to_deallocate;
985#		endif
986		if (!s) {
987		    static unsigned char nothing;
988		    f->base = &nothing; /* Any nonzero address will do.  */
989		} else {
990		    f->base = 0;
991#		    if has_map_fd
992			map_fd(
993				fd, (vm_offset_t)0, (vm_address_t*) &f->base,
994				TRUE, (vm_size_t)s
995			);
996			f->deallocate = map_fd_deallocate;
997#		    endif
998#		    if has_mmap
999			if (!f->base) {
1000			    catchmmapints();
1001			    f->base = (unsigned char *) mmap(
1002				(char *)0, s, PROT_READ, MAP_FILE|MAP_SHARED,
1003				fd, (off_t)0
1004			    );
1005#			    ifndef MAP_FAILED
1006#			    define MAP_FAILED (-1)
1007#			    endif
1008			    if (f->base == (unsigned char *) MAP_FAILED)
1009				f->base = 0;
1010			    else {
1011#				if has_NFS && mmap_signal
1012				    /*
1013				    * On many hosts, the superuser
1014				    * can mmap an NFS file it can't read.
1015				    * So access the first page now, and print
1016				    * a nice message if a bus error occurs.
1017				    */
1018				    readAccessFilenameBuffer(name, f->base);
1019#				endif
1020			    }
1021			    f->deallocate = mmap_deallocate;
1022			}
1023#		    endif
1024		    if (!f->base) {
1025			f->base = tnalloc(unsigned char, s);
1026#			if maps_memory
1027			{
1028			    /*
1029			    * We can't map the file into memory for some reason.
1030			    * Read it into main memory all at once; this is
1031			    * the simplest substitute for memory mapping.
1032			    */
1033			    char *bufptr = (char *) f->base;
1034			    size_t bufsiz = s;
1035			    do {
1036				ssize_t r = read(fd, bufptr, bufsiz);
1037				switch (r) {
1038				    case -1:
1039					efaterror(name);
1040
1041				    case 0:
1042					/* The file must have shrunk!  */
1043					status->st_size = s -= bufsiz;
1044					bufsiz = 0;
1045					break;
1046
1047				    default:
1048					bufptr += r;
1049					bufsiz -= r;
1050					break;
1051				}
1052			    } while (bufsiz);
1053			    if (lseek(fd, (off_t)0, SEEK_SET) == -1)
1054				efaterror(name);
1055			    f->deallocate = read_deallocate;
1056			}
1057#			endif
1058		    }
1059		}
1060		f->ptr = f->base;
1061		f->lim = f->base + s;
1062		f->fd = fd;
1063#		if !maps_memory
1064		    f->readlim = f->base;
1065		    f->stream = stream;
1066#		endif
1067		if_advise_access(s, f, MADV_SEQUENTIAL);
1068		return f;
1069	      }
1070#	    endif
1071	}
1072}
1073
1074#if !maps_memory && large_memory
1075	int
1076Igetmore(f)
1077	register RILE *f;
1078{
1079	register fread_type r;
1080	register size_t s = f->lim - f->readlim;
1081
1082	if (BUFSIZ < s)
1083		s = BUFSIZ;
1084	if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
1085		testIerror(f->stream);
1086		f->lim = f->readlim;  /* The file might have shrunk!  */
1087		return 0;
1088	}
1089	f->readlim += r;
1090	return 1;
1091}
1092#endif
1093
1094#if has_madvise && has_mmap && large_memory
1095	void
1096advise_access(f, advice)
1097	register RILE *f;
1098	int advice;
1099{
1100    if (f->deallocate == mmap_deallocate)
1101	VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
1102	/* Don't worry if madvise fails; it's only advisory.  */
1103}
1104#endif
1105
1106	RILE *
1107#if large_memory && maps_memory
1108I_open(name, status)
1109#else
1110Iopen(name, type, status)
1111	char const *type;
1112#endif
1113	char const *name;
1114	struct stat *status;
1115/* Open NAME for reading, yield its descriptor, and set *STATUS.  */
1116{
1117	int fd = fdSafer(open(name, O_RDONLY
1118#		if OPEN_O_BINARY
1119			|  (strchr(type,'b') ? OPEN_O_BINARY : 0)
1120#		endif
1121	));
1122
1123	if (fd < 0)
1124		return 0;
1125#	if large_memory && maps_memory
1126		return fd2_RILE(fd, name, status);
1127#	else
1128		return fd2RILE(fd, name, type, status);
1129#	endif
1130}
1131
1132
1133static int Oerrloop;
1134
1135	void
1136Oerror()
1137{
1138	if (Oerrloop)
1139		exiterr();
1140	Oerrloop = true;
1141	efaterror("output error");
1142}
1143
1144void Ieof() { fatserror("unexpected end of file"); }
1145void Ierror() { efaterror("input error"); }
1146void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
1147void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
1148
1149void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
1150void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
1151void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
1152void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
1153
1154#if !large_memory
1155	void
1156testIeof(f)
1157	FILE *f;
1158{
1159	testIerror(f);
1160	if (feof(f))
1161		Ieof();
1162}
1163void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
1164#endif
1165
1166void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1167
1168void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
1169void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
1170void oflush()
1171{
1172	if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
1173		Oerror();
1174}
1175
1176	void
1177fatcleanup(already_newline)
1178	int already_newline;
1179{
1180	VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
1181	exiterr();
1182}
1183
1184	static void
1185startsay(s, t)
1186	const char *s, *t;
1187{
1188	oflush();
1189	if (s)
1190	    aprintf(stderr, "%s: %s: %s", cmdid, s, t);
1191	else
1192	    aprintf(stderr, "%s: %s", cmdid, t);
1193}
1194
1195	static void
1196fatsay(s)
1197	char const *s;
1198{
1199	startsay(s, "");
1200}
1201
1202	static void
1203errsay(s)
1204	char const *s;
1205{
1206	fatsay(s);
1207	nerror++;
1208}
1209
1210	static void
1211warnsay(s)
1212	char const *s;
1213{
1214	startsay(s, "warning: ");
1215}
1216
1217void eerror(s) char const *s; { enerror(errno,s); }
1218
1219	void
1220enerror(e,s)
1221	int e;
1222	char const *s;
1223{
1224	errsay((char const*)0);
1225	errno = e;
1226	perror(s);
1227	eflush();
1228}
1229
1230void efaterror(s) char const *s; { enfaterror(errno,s); }
1231
1232	void
1233enfaterror(e,s)
1234	int e;
1235	char const *s;
1236{
1237	fatsay((char const*)0);
1238	errno = e;
1239	perror(s);
1240	fatcleanup(true);
1241}
1242
1243#if has_prototypes
1244	void
1245error(char const *format,...)
1246#else
1247	/*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
1248#endif
1249/* non-fatal error */
1250{
1251	va_list args;
1252	errsay((char const*)0);
1253	vararg_start(args, format);
1254	fvfprintf(stderr, format, args);
1255	va_end(args);
1256	afputc('\n',stderr);
1257	eflush();
1258}
1259
1260#if has_prototypes
1261	void
1262rcserror(char const *format,...)
1263#else
1264	/*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
1265#endif
1266/* non-fatal RCS file error */
1267{
1268	va_list args;
1269	errsay(RCSname);
1270	vararg_start(args, format);
1271	fvfprintf(stderr, format, args);
1272	va_end(args);
1273	afputc('\n',stderr);
1274	eflush();
1275}
1276
1277#if has_prototypes
1278	void
1279workerror(char const *format,...)
1280#else
1281	/*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
1282#endif
1283/* non-fatal working file error */
1284{
1285	va_list args;
1286	errsay(workname);
1287	vararg_start(args, format);
1288	fvfprintf(stderr, format, args);
1289	va_end(args);
1290	afputc('\n',stderr);
1291	eflush();
1292}
1293
1294#if has_prototypes
1295	void
1296fatserror(char const *format,...)
1297#else
1298	/*VARARGS1*/ void
1299	fatserror(format, va_alist) char const *format; va_dcl
1300#endif
1301/* fatal RCS file syntax error */
1302{
1303	va_list args;
1304	oflush();
1305	VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
1306	vararg_start(args, format);
1307	fvfprintf(stderr, format, args);
1308	va_end(args);
1309	fatcleanup(false);
1310}
1311
1312#if has_prototypes
1313	void
1314faterror(char const *format,...)
1315#else
1316	/*VARARGS1*/ void faterror(format, va_alist)
1317	char const *format; va_dcl
1318#endif
1319/* fatal error, terminates program after cleanup */
1320{
1321	va_list args;
1322	fatsay((char const*)0);
1323	vararg_start(args, format);
1324	fvfprintf(stderr, format, args);
1325	va_end(args);
1326	fatcleanup(false);
1327}
1328
1329#if has_prototypes
1330	void
1331rcsfaterror(char const *format,...)
1332#else
1333	/*VARARGS1*/ void rcsfaterror(format, va_alist)
1334	char const *format; va_dcl
1335#endif
1336/* fatal RCS file error, terminates program after cleanup */
1337{
1338	va_list args;
1339	fatsay(RCSname);
1340	vararg_start(args, format);
1341	fvfprintf(stderr, format, args);
1342	va_end(args);
1343	fatcleanup(false);
1344}
1345
1346#if has_prototypes
1347	void
1348warn(char const *format,...)
1349#else
1350	/*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
1351#endif
1352/* warning */
1353{
1354	va_list args;
1355	if (!quietflag) {
1356		warnsay((char *)0);
1357		vararg_start(args, format);
1358		fvfprintf(stderr, format, args);
1359		va_end(args);
1360		afputc('\n', stderr);
1361		eflush();
1362	}
1363}
1364
1365#if has_prototypes
1366	void
1367rcswarn(char const *format,...)
1368#else
1369	/*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
1370#endif
1371/* RCS file warning */
1372{
1373	va_list args;
1374	if (!quietflag) {
1375		warnsay(RCSname);
1376		vararg_start(args, format);
1377		fvfprintf(stderr, format, args);
1378		va_end(args);
1379		afputc('\n', stderr);
1380		eflush();
1381	}
1382}
1383
1384#if has_prototypes
1385	void
1386workwarn(char const *format,...)
1387#else
1388	/*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
1389#endif
1390/* working file warning */
1391{
1392	va_list args;
1393	if (!quietflag) {
1394		warnsay(workname);
1395		vararg_start(args, format);
1396		fvfprintf(stderr, format, args);
1397		va_end(args);
1398		afputc('\n', stderr);
1399		eflush();
1400	}
1401}
1402
1403	void
1404redefined(c)
1405	int c;
1406{
1407	warn("redefinition of -%c option", c);
1408}
1409
1410#if has_prototypes
1411	void
1412diagnose(char const *format,...)
1413#else
1414	/*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
1415#endif
1416/* prints a diagnostic message */
1417/* Unlike the other routines, it does not append a newline. */
1418/* This lets some callers suppress the newline, and is faster */
1419/* in implementations that flush stderr just at the end of each printf. */
1420{
1421	va_list args;
1422        if (!quietflag) {
1423		oflush();
1424		vararg_start(args, format);
1425		fvfprintf(stderr, format, args);
1426		va_end(args);
1427		eflush();
1428        }
1429}
1430
1431
1432
1433	void
1434afputc(c, f)
1435/* afputc(c,f); acts like aputc_(c,f) but is smaller and slower.  */
1436	int c;
1437	register FILE *f;
1438{
1439	aputc_(c,f)
1440}
1441
1442
1443	void
1444aputs(s, iop)
1445	char const *s;
1446	FILE *iop;
1447/* Function: Put string s on file iop, abort on error.
1448 */
1449{
1450#if has_fputs
1451	if (fputs(s, iop) < 0)
1452		Oerror();
1453#else
1454	awrite(s, strlen(s), iop);
1455#endif
1456}
1457
1458
1459
1460	void
1461#if has_prototypes
1462fvfprintf(FILE *stream, char const *format, va_list args)
1463#else
1464	fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
1465#endif
1466/* like vfprintf, except abort program on error */
1467{
1468#if has_vfprintf
1469	if (vfprintf(stream, format, args) < 0)
1470		Oerror();
1471#else
1472#	if has__doprintf
1473		_doprintf(stream, format, args);
1474#	else
1475#	if has__doprnt
1476		_doprnt(format, args, stream);
1477#	else
1478		int *a = (int *)args;
1479		VOID fprintf(stream, format,
1480			a[0], a[1], a[2], a[3], a[4],
1481			a[5], a[6], a[7], a[8], a[9]
1482		);
1483#	endif
1484#	endif
1485	if (ferror(stream))
1486		Oerror();
1487#endif
1488}
1489
1490#if has_prototypes
1491	void
1492aprintf(FILE *iop, char const *fmt, ...)
1493#else
1494	/*VARARGS2*/ void
1495aprintf(iop, fmt, va_alist)
1496FILE *iop;
1497char const *fmt;
1498va_dcl
1499#endif
1500/* Function: formatted output. Same as fprintf in stdio,
1501 * but aborts program on error
1502 */
1503{
1504	va_list ap;
1505	vararg_start(ap, fmt);
1506	fvfprintf(iop, fmt, ap);
1507	va_end(ap);
1508}
1509
1510
1511
1512#ifdef LEXDB
1513/* test program reading a stream of lexemes and printing the tokens.
1514 */
1515
1516
1517
1518	int
1519main(argc,argv)
1520int argc; char * argv[];
1521{
1522        cmdid="lextest";
1523        if (argc<2) {
1524		aputs("No input file\n",stderr);
1525		exitmain(EXIT_FAILURE);
1526        }
1527	if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
1528		faterror("can't open input file %s",argv[1]);
1529        }
1530        Lexinit();
1531	while (!eoflex()) {
1532        switch (nexttok) {
1533
1534        case ID:
1535                VOID printf("ID: %s",NextString);
1536                break;
1537
1538        case NUM:
1539		if (hshenter)
1540                   VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
1541                else
1542                   VOID printf("NUM, unentered: %s",NextString);
1543                hshenter = !hshenter; /*alternate between dates and numbers*/
1544                break;
1545
1546        case COLON:
1547                VOID printf("COLON"); break;
1548
1549        case SEMI:
1550                VOID printf("SEMI"); break;
1551
1552        case STRING:
1553                readstring();
1554                VOID printf("STRING"); break;
1555
1556        case UNKN:
1557                VOID printf("UNKN"); break;
1558
1559        default:
1560                VOID printf("DEFAULT"); break;
1561        }
1562        VOID printf(" | ");
1563        nextlex();
1564        }
1565	exitmain(EXIT_SUCCESS);
1566}
1567
1568void exiterr() { _exit(EXIT_FAILURE); }
1569
1570
1571#endif
1572