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