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