rcslex.c revision 10
1/*
2 *                     RCS file input
3 */
4/*********************************************************************************
5 *                     Lexical Analysis.
6 *                     hashtable, Lexinit, nextlex, getlex, getkey,
7 *                     getid, getnum, readstring, printstring, savestring,
8 *                     checkid, fatserror, error, faterror, warn, diagnose
9 *                     Testprogram: define LEXDB
10 *********************************************************************************
11 */
12
13/* Copyright (C) 1982, 1988, 1989 Walter Tichy
14   Copyright 1990, 1991 by Paul Eggert
15   Distributed under license by the Free Software Foundation, Inc.
16
17This file is part of RCS.
18
19RCS is free software; you can redistribute it and/or modify
20it under the terms of the GNU General Public License as published by
21the Free Software Foundation; either version 2, or (at your option)
22any later version.
23
24RCS is distributed in the hope that it will be useful,
25but WITHOUT ANY WARRANTY; without even the implied warranty of
26MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27GNU General Public License for more details.
28
29You should have received a copy of the GNU General Public License
30along with RCS; see the file COPYING.  If not, write to
31the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
32
33Report problems and direct all questions to:
34
35    rcs-bugs@cs.purdue.edu
36
37*/
38
39
40
41/* $Log: rcslex.c,v $
42 * Revision 5.11  1991/11/03  03:30:44  eggert
43 * Fix porting bug to ancient hosts lacking vfprintf.
44 *
45 * Revision 5.10  1991/10/07  17:32:46  eggert
46 * Support piece tables even if !has_mmap.
47 *
48 * Revision 5.9  1991/09/24  00:28:42  eggert
49 * Don't export errsay().
50 *
51 * Revision 5.8  1991/08/19  03:13:55  eggert
52 * Add eoflex(), mmap support.  Tune.
53 *
54 * Revision 5.7  1991/04/21  11:58:26  eggert
55 * Add MS-DOS support.
56 *
57 * Revision 5.6  1991/02/25  07:12:42  eggert
58 * Work around fputs bug.  strsave -> str_save (DG/UX name clash)
59 *
60 * Revision 5.5  1990/12/04  05:18:47  eggert
61 * Use -I for prompts and -q for diagnostics.
62 *
63 * Revision 5.4  1990/11/19  20:05:28  hammer
64 * no longer gives warning about unknown keywords if -q is specified
65 *
66 * Revision 5.3  1990/11/01  05:03:48  eggert
67 * When ignoring unknown phrases, copy them to the output RCS file.
68 *
69 * Revision 5.2  1990/09/04  08:02:27  eggert
70 * Count RCS lines better.
71 *
72 * Revision 5.1  1990/08/29  07:14:03  eggert
73 * Work around buggy compilers with defective argument promotion.
74 *
75 * Revision 5.0  1990/08/22  08:12:55  eggert
76 * Remove compile-time limits; use malloc instead.
77 * Report errno-related errors with perror().
78 * Ansify and Posixate.  Add support for ISO 8859.
79 * Use better hash function.
80 *
81 * Revision 4.6  89/05/01  15:13:07  narten
82 * changed copyright header to reflect current distribution rules
83 *
84 * Revision 4.5  88/08/28  15:01:12  eggert
85 * Don't loop when writing error messages to a full filesystem.
86 * Flush stderr/stdout when mixing output.
87 * Yield exit status compatible with diff(1).
88 * Shrink stdio code size; allow cc -R; remove lint.
89 *
90 * Revision 4.4  87/12/18  11:44:47  narten
91 * fixed to use "varargs" in "fprintf"; this is required if it is to
92 * work on a SPARC machine such as a Sun-4
93 *
94 * Revision 4.3  87/10/18  10:37:18  narten
95 * Updating version numbers. Changes relative to 1.1 actually relative
96 * to version 4.1
97 *
98 * Revision 1.3  87/09/24  14:00:17  narten
99 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
100 * warnings)
101 *
102 * Revision 1.2  87/03/27  14:22:33  jenkins
103 * Port to suns
104 *
105 * Revision 4.1  83/03/25  18:12:51  wft
106 * Only changed $Header to $Id.
107 *
108 * Revision 3.3  82/12/10  16:22:37  wft
109 * Improved error messages, changed exit status on error to 1.
110 *
111 * Revision 3.2  82/11/28  21:27:10  wft
112 * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
113 * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
114 * properly in case there is an IO-error (e.g., file system full).
115 *
116 * Revision 3.1  82/10/11  19:43:56  wft
117 * removed unused label out:;
118 * made sure all calls to getc() return into an integer, not a char.
119 */
120
121
122/*
123#define LEXDB
124*/
125/* version LEXDB is for testing the lexical analyzer. The testprogram
126 * reads a stream of lexemes, enters the revision numbers into the
127 * hashtable, and prints the recognized tokens. Keywords are recognized
128 * as identifiers.
129 */
130
131
132
133#include "rcsbase.h"
134
135libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $")
136
137static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
138
139enum tokens     nexttok;    /*next token, set by nextlex                    */
140
141int             hshenter;   /*if true, next suitable lexeme will be entered */
142                            /*into the symbol table. Handle with care.      */
143int             nextc;      /*next input character, initialized by Lexinit  */
144
145unsigned long	rcsline;    /*current line-number of input		    */
146int             nerror;     /*counter for errors                            */
147int             quietflag;  /*indicates quiet mode                          */
148RILE *		finptr;	    /*input file descriptor			    */
149
150FILE *          frewrite;   /*file descriptor for echoing input             */
151
152FILE *		foutptr;    /* copy of frewrite, but 0 to suppress echo  */
153
154static struct buf tokbuf;   /* token buffer				    */
155
156char const *    NextString; /* next token				    */
157
158/*
159 * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
160 * so hshsize should be odd.
161 * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
162 * Software--practice & experience 20, 2 (Feb 1990), 209-224.
163 */
164#ifndef hshsize
165#	define hshsize 511
166#endif
167
168static struct hshentry *hshtab[hshsize]; /*hashtable			    */
169
170static int ignored_phrases; /* have we ignored phrases in this RCS file? */
171
172    void
173warnignore()
174{
175    if (! (ignored_phrases|quietflag)) {
176	ignored_phrases = true;
177	warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString);
178    }
179}
180
181
182
183	static void
184lookup(str)
185	char const *str;
186/* Function: Looks up the character string pointed to by str in the
187 * hashtable. If the string is not present, a new entry for it is created.
188 * In any case, the address of the corresponding hashtable entry is placed
189 * into nexthsh.
190 */
191{
192	register unsigned ihash;  /* index into hashtable */
193	register char const *sp;
194	register struct hshentry *n, **p;
195
196        /* calculate hash code */
197	sp = str;
198        ihash = 0;
199	while (*sp)
200		ihash  =  (ihash<<2) + *sp++;
201	ihash %= hshsize;
202
203	for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
204		if (!(n = *p)) {
205			/* empty slot found */
206			*p = n = ftalloc(struct hshentry);
207			n->num = fstr_save(str);
208			n->nexthsh = nil;
209#			ifdef LEXDB
210				VOID printf("\nEntered: %s at %u ", str, ihash);
211#			endif
212			break;
213		} else if (strcmp(str, n->num) == 0)
214			/* match found */
215			break;
216	nexthsh = n;
217	NextString = n->num;
218}
219
220
221
222
223
224
225	void
226Lexinit()
227/* Function: Initialization of lexical analyzer:
228 * initializes the hashtable,
229 * initializes nextc, nexttok if finptr != 0
230 */
231{       register int            c;
232
233	for (c = hshsize;  0 <= --c;  ) {
234		hshtab[c] = nil;
235        }
236
237	nerror = 0;
238	if (finptr) {
239		foutptr = 0;
240		hshenter = true;
241		ignored_phrases = false;
242		rcsline = 1;
243		bufrealloc(&tokbuf, 2);
244		Iget(finptr, nextc);
245                nextlex();            /*initial token*/
246        }
247}
248
249
250
251
252
253
254
255	void
256nextlex()
257
258/* Function: Reads the next token and sets nexttok to the next token code.
259 * Only if hshenter is set, a revision number is entered into the
260 * hashtable and a pointer to it is placed into nexthsh.
261 * This is useful for avoiding that dates are placed into the hashtable.
262 * For ID's and NUM's, NextString is set to the character string.
263 * Assumption: nextc contains the next character.
264 */
265{       register c;
266	declarecache;
267	register FILE *frew;
268        register char * sp;
269	char const *limit;
270        register enum tokens d;
271	register RILE *fin;
272
273	fin=finptr; frew=foutptr;
274	setupcache(fin); cache(fin);
275	c = nextc;
276
277	for (;;) { switch ((d = ctab[c])) {
278
279	default:
280		fatserror("unknown character `%c'", c);
281		/*NOTREACHED*/
282
283        case NEWLN:
284		++rcsline;
285#               ifdef LEXDB
286		afputc('\n',stdout);
287#               endif
288                /* Note: falls into next case */
289
290        case SPACE:
291		GETC(frew, c);
292		continue;
293
294        case DIGIT:
295		sp = tokbuf.string;
296		limit = sp + tokbuf.size;
297		*sp++ = c;
298		for (;;) {
299			GETC(frew, c);
300			if ((d=ctab[c])!=DIGIT && d!=PERIOD)
301				break;
302                        *sp++ = c;         /* 1.2. and 1.2 are different */
303			if (limit <= sp)
304				sp = bufenlarge(&tokbuf, &limit);
305                }
306		*sp = 0;
307		if (hshenter)
308			lookup(tokbuf.string);
309		else
310			NextString = fstr_save(tokbuf.string);
311		d = NUM;
312		break;
313
314
315        case LETTER:
316	case Letter:
317		sp = tokbuf.string;
318		limit = sp + tokbuf.size;
319		*sp++ = c;
320		for (;;) {
321			GETC(frew, c);
322			if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR)
323				break;
324                        *sp++ = c;
325			if (limit <= sp)
326				sp = bufenlarge(&tokbuf, &limit);
327                }
328		*sp = 0;
329		NextString = fstr_save(tokbuf.string);
330		d = ID;  /* may be ID or keyword */
331		break;
332
333        case SBEGIN: /* long string */
334		d = STRING;
335                /* note: only the initial SBEGIN has been read*/
336                /* read the string, and reset nextc afterwards*/
337		break;
338
339	case COLON:
340	case SEMI:
341		GETC(frew, c);
342		break;
343	} break; }
344	nextc = c;
345	nexttok = d;
346	uncache(fin);
347}
348
349	int
350eoflex()
351/*
352 * Yield true if we look ahead to the end of the input, false otherwise.
353 * nextc becomes undefined at end of file.
354 */
355{
356	register int c;
357	declarecache;
358	register FILE *fout;
359	register RILE *fin;
360
361	c = nextc;
362	fin = finptr;
363	fout = foutptr;
364	setupcache(fin); cache(fin);
365
366	for (;;) {
367		switch (ctab[c]) {
368			default:
369				nextc = c;
370				uncache(fin);
371				return false;
372
373			case NEWLN:
374				++rcsline;
375				/* fall into */
376			case SPACE:
377				cachegeteof(c, {uncache(fin);return true;});
378				break;
379		}
380		if (fout)
381			aputc(c, fout);
382	}
383}
384
385
386int getlex(token)
387enum tokens token;
388/* Function: Checks if nexttok is the same as token. If so,
389 * advances the input by calling nextlex and returns true.
390 * otherwise returns false.
391 * Doesn't work for strings and keywords; loses the character string for ids.
392 */
393{
394        if (nexttok==token) {
395                nextlex();
396                return(true);
397        } else  return(false);
398}
399
400	int
401getkeyopt(key)
402	char const *key;
403/* Function: If the current token is a keyword identical to key,
404 * advances the input by calling nextlex and returns true;
405 * otherwise returns false.
406 */
407{
408	if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
409		 /* match found */
410		 ffree1(NextString);
411		 nextlex();
412		 return(true);
413        }
414        return(false);
415}
416
417	void
418getkey(key)
419	char const *key;
420/* Check that the current input token is a keyword identical to key,
421 * and advance the input by calling nextlex.
422 */
423{
424	if (!getkeyopt(key))
425		fatserror("missing '%s' keyword", key);
426}
427
428	void
429getkeystring(key)
430	char const *key;
431/* Check that the current input token is a keyword identical to key,
432 * and advance the input by calling nextlex; then look ahead for a string.
433 */
434{
435	getkey(key);
436	if (nexttok != STRING)
437		fatserror("missing string after '%s' keyword", key);
438}
439
440
441	char const *
442getid()
443/* Function: Checks if nexttok is an identifier. If so,
444 * advances the input by calling nextlex and returns a pointer
445 * to the identifier; otherwise returns nil.
446 * Treats keywords as identifiers.
447 */
448{
449	register char const *name;
450        if (nexttok==ID) {
451                name = NextString;
452                nextlex();
453                return name;
454        } else  return nil;
455}
456
457
458struct hshentry * getnum()
459/* Function: Checks if nexttok is a number. If so,
460 * advances the input by calling nextlex and returns a pointer
461 * to the hashtable entry. Otherwise returns nil.
462 * Doesn't work if hshenter is false.
463 */
464{
465        register struct hshentry * num;
466        if (nexttok==NUM) {
467                num=nexthsh;
468                nextlex();
469                return num;
470        } else  return nil;
471}
472
473	struct cbuf
474getphrases(key)
475	char const *key;
476/* Get a series of phrases that do not start with KEY, yield resulting buffer.
477 * Stop when the next phrase starts with a token that is not an identifier,
478 * or is KEY.
479 * Assume !foutptr.
480 */
481{
482    declarecache;
483    register int c;
484    register char *p;
485    char const *limit;
486    register char const *ki, *kn;
487    struct cbuf r;
488    struct buf b;
489    register RILE *fin;
490
491    if (nexttok!=ID  ||  strcmp(NextString,key) == 0) {
492	r.string = 0;
493	r.size = 0;
494	return r;
495    } else {
496	warnignore();
497	fin = finptr;
498	setupcache(fin); cache(fin);
499	bufautobegin(&b);
500	bufscpy(&b, NextString);
501	ffree1(NextString);
502	p = b.string + strlen(b.string);
503	limit = b.string + b.size;
504	c = nextc;
505	for (;;) {
506	    for (;;) {
507		if (limit <= p)
508		    p = bufenlarge(&b, &limit);
509		*p++ = c;
510		switch (ctab[c]) {
511		    default:
512			fatserror("unknown character `%c'", c);
513			/*NOTREACHED*/
514		    case NEWLN:
515			++rcsline;
516			/* fall into */
517		    case COLON: case DIGIT: case LETTER: case Letter:
518		    case PERIOD: case SPACE:
519			cacheget(c);
520			continue;
521		    case SBEGIN: /* long string */
522			for (;;) {
523			    for (;;) {
524				if (limit <= p)
525				    p = bufenlarge(&b, &limit);
526				cacheget(c);
527				*p++ = c;
528				switch (c) {
529				    case '\n':
530					++rcsline;
531					/* fall into */
532				    default:
533					continue;
534
535				    case SDELIM:
536					break;
537				}
538				break;
539			    }
540			    cacheget(c);
541			    if (c != SDELIM)
542				break;
543			    if (limit <= p)
544				p = bufenlarge(&b, &limit);
545			    *p++ = c;
546			}
547			continue;
548		    case SEMI:
549			cacheget(c);
550			if (ctab[c] == NEWLN) {
551			    ++rcsline;
552			    if (limit <= p)
553				p = bufenlarge(&b, &limit);
554			    *p++ = c;
555			    cacheget(c);
556			}
557			for (;;) {
558			    switch (ctab[c]) {
559				case NEWLN:
560					++rcsline;
561					/* fall into */
562				case SPACE:
563					cacheget(c);
564					continue;
565
566				default: break;
567			    }
568			    break;
569			}
570			break;
571		}
572		break;
573	    }
574	    switch (ctab[c]) {
575		case LETTER:
576		case Letter:
577		    for (kn = key;  c && *kn==c;  kn++)
578			cacheget(c);
579		    if (!*kn)
580			switch (ctab[c]) {
581			    case DIGIT: case LETTER: case Letter:
582				break;
583			    default:
584				nextc = c;
585				NextString = fstr_save(key);
586				nexttok = ID;
587				uncache(fin);
588				goto returnit;
589			}
590		    for (ki=key; ki<kn; ) {
591			if (limit <= p)
592			    p = bufenlarge(&b, &limit);
593			*p++ = *ki++;
594		    }
595		    break;
596
597		default:
598		    nextc = c;
599		    uncache(fin);
600		    nextlex();
601		    goto returnit;
602	    }
603	}
604    returnit:
605	return bufremember(&b, (size_t)(p - b.string));
606    }
607}
608
609
610	void
611readstring()
612/* skip over characters until terminating single SDELIM        */
613/* If foutptr is set, copy every character read to foutptr.    */
614/* Does not advance nextlex at the end.                        */
615{       register c;
616	declarecache;
617	register FILE *frew;
618	register RILE *fin;
619	fin=finptr; frew=foutptr;
620	setupcache(fin); cache(fin);
621	for (;;) {
622		GETC(frew, c);
623		switch (c) {
624		    case '\n':
625			++rcsline;
626			break;
627
628		    case SDELIM:
629			GETC(frew, c);
630			if (c != SDELIM) {
631				/* end of string */
632				nextc = c;
633				uncache(fin);
634				return;
635			}
636			break;
637		}
638	}
639}
640
641
642	void
643printstring()
644/* Function: copy a string to stdout, until terminated with a single SDELIM.
645 * Does not advance nextlex at the end.
646 */
647{
648        register c;
649	declarecache;
650	register FILE *fout;
651	register RILE *fin;
652	fin=finptr;
653	fout = stdout;
654	setupcache(fin); cache(fin);
655	for (;;) {
656		cacheget(c);
657		switch (c) {
658		    case '\n':
659			++rcsline;
660			break;
661		    case SDELIM:
662			cacheget(c);
663			if (c != SDELIM) {
664                                nextc=c;
665				uncache(fin);
666                                return;
667                        }
668			break;
669                }
670		aputc(c,fout);
671        }
672}
673
674
675
676	struct cbuf
677savestring(target)
678	struct buf *target;
679/* Copies a string terminated with SDELIM from file finptr to buffer target.
680 * Double SDELIM is replaced with SDELIM.
681 * If foutptr is set, the string is also copied unchanged to foutptr.
682 * Does not advance nextlex at the end.
683 * Yield a copy of *TARGET, except with exact length.
684 */
685{
686        register c;
687	declarecache;
688	register FILE *frew;
689	register char *tp;
690	register RILE *fin;
691	char const *limit;
692	struct cbuf r;
693
694	fin=finptr; frew=foutptr;
695	setupcache(fin); cache(fin);
696	tp = target->string;  limit = tp + target->size;
697	for (;;) {
698		GETC(frew, c);
699		switch (c) {
700		    case '\n':
701			++rcsline;
702			break;
703		    case SDELIM:
704			GETC(frew, c);
705			if (c != SDELIM) {
706                                /* end of string */
707                                nextc=c;
708				r.string = target->string;
709				r.size = tp - r.string;
710				uncache(fin);
711				return r;
712                        }
713			break;
714                }
715		if (tp == limit)
716			tp = bufenlarge(target, &limit);
717		*tp++ = c;
718        }
719}
720
721
722	char *
723checkid(id, delimiter)
724	register char *id;
725	int delimiter;
726/*   Function:  check whether the string starting at id is an   */
727/*		identifier and return a pointer to the delimiter*/
728/*		after the identifier.  White space, delim and 0 */
729/*              are legal delimiters.  Aborts the program if not*/
730/*              a legal identifier. Useful for checking commands*/
731/*		If !delim, the only delimiter is 0.		*/
732{
733        register enum  tokens  d;
734        register char    *temp;
735        register char    c,tc;
736	register char delim = delimiter;
737
738	temp = id;
739	if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) {
740	    while ((d = ctab[(unsigned char)(c = *++id)])==LETTER
741		|| d==Letter || d==DIGIT || d==IDCHAR
742	    )
743		;
744	    if (c  &&  (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) {
745                /* append \0 to end of id before error message */
746                tc = c;
747                while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
748                *id = '\0';
749		faterror("invalid character %c in identifier `%s'",tc,temp);
750	    }
751        } else {
752            /* append \0 to end of id before error message */
753            while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ;
754            *id = '\0';
755	    faterror("identifier `%s' doesn't start with letter", temp);
756        }
757	return id;
758}
759
760	void
761checksid(id)
762	char *id;
763/* Check whether the string ID is an identifier.  */
764{
765	VOID checkid(id, 0);
766}
767
768
769	static RILE *
770#if has_mmap && large_memory
771fd2_RILE(fd, filename, status)
772#else
773fd2RILE(fd, filename, mode, status)
774	char const *mode;
775#endif
776	int fd;
777	char const *filename;
778	register struct stat *status;
779{
780	struct stat st;
781
782	if (!status)
783		status = &st;
784	if (fstat(fd, status) != 0)
785		efaterror(filename);
786	if (!S_ISREG(status->st_mode)) {
787		error("`%s' is not a regular file", filename);
788		VOID close(fd);
789		errno = EINVAL;
790		return 0;
791	} else {
792
793#	    if ! (has_mmap && large_memory)
794		FILE *stream;
795		if (!(stream = fdopen(fd, mode)))
796			efaterror(filename);
797#	    endif
798
799#	    if !large_memory
800		return stream;
801#	    else
802#		define RILES 3
803		{
804			static RILE rilebuf[RILES];
805
806			register RILE *f;
807			size_t s = status->st_size;
808
809			if (s != status->st_size)
810				faterror("`%s' is enormous", filename);
811			for (f = rilebuf;  f->base;  f++)
812				if (f == rilebuf+RILES)
813					faterror("too many RILEs");
814			if (!s) {
815				static unsigned char dummy;
816				f->base = &dummy;
817			} else {
818#			    if has_mmap
819				if (
820				    (f->base = (unsigned char *)mmap(
821					(caddr_t)0, s, PROT_READ, MAP_SHARED,
822					fd, (off_t)0
823				    )) == (unsigned char *)-1
824				)
825					efaterror("mmap");
826#			    else
827			    	f->base = tnalloc(unsigned char, s);
828#			    endif
829			}
830			f->ptr = f->base;
831			f->lim = f->base + s;
832#			if has_mmap
833			    f->fd = fd;
834#			else
835			    f->readlim = f->base;
836			    f->stream = stream;
837#			endif
838			if_advise_access(s, f, MADV_SEQUENTIAL);
839			return f;
840		}
841#	    endif
842	}
843}
844
845#if !has_mmap && large_memory
846	int
847Igetmore(f)
848	register RILE *f;
849{
850	register fread_type r;
851	register size_t s = f->lim - f->readlim;
852
853	if (BUFSIZ < s)
854		s = BUFSIZ;
855	if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
856		testIerror(f->stream);
857		f->lim = f->readlim;  /* The file might have shrunk!  */
858		return 0;
859	}
860	f->readlim += r;
861	return 1;
862}
863#endif
864
865#if has_madvise && has_mmap && large_memory
866	void
867advise_access(f, advice)
868	register RILE *f;
869	int advice;
870{
871	if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0)
872		efaterror("madvise");
873}
874#endif
875
876	RILE *
877#if has_mmap && large_memory
878I_open(filename, status)
879#else
880Iopen(filename, mode, status)
881	char const *mode;
882#endif
883	char const *filename;
884	struct stat *status;
885/* Open FILENAME for reading, yield its descriptor, and set *STATUS.  */
886{
887	int fd;
888
889	if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0)
890		return 0;
891#	if has_mmap && large_memory
892		return fd2_RILE(fd, filename, status);
893#	else
894		return fd2RILE(fd, filename, mode, status);
895#	endif
896}
897
898
899#if !large_memory
900#	define Iclose(f) fclose(f)
901#else
902		static int
903	Iclose(f)
904		register RILE *f;
905	{
906#	    if has_mmap
907		size_t s = f->lim - f->base;
908		if (s  &&  munmap((caddr_t)f->base, s) != 0)
909			return -1;
910		f->base = 0;
911		return close(f->fd);
912#	    else
913		tfree(f->base);
914		f->base = 0;
915		return fclose(f->stream);
916#	    endif
917	}
918#endif
919
920
921static int Oerrloop;
922
923	exiting void
924Oerror()
925{
926	if (Oerrloop)
927		exiterr();
928	Oerrloop = true;
929	efaterror("output error");
930}
931
932exiting void Ieof() { fatserror("unexpected end of file"); }
933exiting void Ierror() { efaterror("input error"); }
934void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
935void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
936
937void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
938void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
939void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
940void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
941
942#if !large_memory
943	void
944testIeof(f)
945	FILE *f;
946{
947	testIerror(f);
948	if (feof(f))
949		Ieof();
950}
951void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
952#endif
953
954void eflush()
955{
956	if (fflush(stderr) != 0  &&  !Oerrloop)
957		Oerror();
958}
959
960void oflush()
961{
962	if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
963		Oerror();
964}
965
966	static exiting void
967fatcleanup(already_newline)
968	int already_newline;
969{
970	VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
971	exiterr();
972}
973
974static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; }
975static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); }
976
977void eerror(s) char const *s; { enerror(errno,s); }
978
979	void
980enerror(e,s)
981	int e;
982	char const *s;
983{
984	errsay();
985	errno = e;
986	perror(s);
987	eflush();
988}
989
990exiting void efaterror(s) char const *s; { enfaterror(errno,s); }
991
992	exiting void
993enfaterror(e,s)
994	int e;
995	char const *s;
996{
997	fatsay();
998	errno = e;
999	perror(s);
1000	fatcleanup(true);
1001}
1002
1003#if has_prototypes
1004	void
1005error(char const *format,...)
1006#else
1007	/*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
1008#endif
1009/* non-fatal error */
1010{
1011	va_list args;
1012	errsay();
1013	vararg_start(args, format);
1014	fvfprintf(stderr, format, args);
1015	va_end(args);
1016	afputc('\n',stderr);
1017	eflush();
1018}
1019
1020#if has_prototypes
1021	exiting void
1022fatserror(char const *format,...)
1023#else
1024	/*VARARGS1*/ exiting void
1025	fatserror(format, va_alist) char const *format; va_dcl
1026#endif
1027/* fatal syntax error */
1028{
1029	va_list args;
1030	oflush();
1031	VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline);
1032	vararg_start(args, format);
1033	fvfprintf(stderr, format, args);
1034	va_end(args);
1035	fatcleanup(false);
1036}
1037
1038#if has_prototypes
1039	exiting void
1040faterror(char const *format,...)
1041#else
1042	/*VARARGS1*/ exiting void faterror(format, va_alist)
1043	char const *format; va_dcl
1044#endif
1045/* fatal error, terminates program after cleanup */
1046{
1047	va_list args;
1048	fatsay();
1049	vararg_start(args, format);
1050	fvfprintf(stderr, format, args);
1051	va_end(args);
1052	fatcleanup(false);
1053}
1054
1055#if has_prototypes
1056	void
1057warn(char const *format,...)
1058#else
1059	/*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
1060#endif
1061/* prints a warning message */
1062{
1063	va_list args;
1064	oflush();
1065	aprintf(stderr,"%s warning: ",cmdid);
1066	vararg_start(args, format);
1067	fvfprintf(stderr, format, args);
1068	va_end(args);
1069	afputc('\n',stderr);
1070	eflush();
1071}
1072
1073	void
1074redefined(c)
1075	int c;
1076{
1077	warn("redefinition of -%c option", c);
1078}
1079
1080#if has_prototypes
1081	void
1082diagnose(char const *format,...)
1083#else
1084	/*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
1085#endif
1086/* prints a diagnostic message */
1087/* Unlike the other routines, it does not append a newline. */
1088/* This lets some callers suppress the newline, and is faster */
1089/* in implementations that flush stderr just at the end of each printf. */
1090{
1091	va_list args;
1092        if (!quietflag) {
1093		oflush();
1094		vararg_start(args, format);
1095		fvfprintf(stderr, format, args);
1096		va_end(args);
1097		eflush();
1098        }
1099}
1100
1101
1102
1103	void
1104afputc(c, f)
1105/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower.
1106 */
1107	int c;
1108	register FILE *f;
1109{
1110	aputc(c,f);
1111}
1112
1113
1114	void
1115aputs(s, iop)
1116	char const *s;
1117	FILE *iop;
1118/* Function: Put string s on file iop, abort on error.
1119 */
1120{
1121#if has_fputs
1122	if (fputs(s, iop) < 0)
1123		Oerror();
1124#else
1125	awrite(s, strlen(s), iop);
1126#endif
1127}
1128
1129
1130
1131	void
1132#if has_prototypes
1133fvfprintf(FILE *stream, char const *format, va_list args)
1134#else
1135	fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
1136#endif
1137/* like vfprintf, except abort program on error */
1138{
1139#if has_vfprintf
1140	if (vfprintf(stream, format, args) < 0)
1141#else
1142#	if has__doprintf
1143		_doprintf(stream, format, args);
1144#	else
1145#	if has__doprnt
1146		_doprnt(format, args, stream);
1147#	else
1148		int *a = (int *)args;
1149		VOID fprintf(stream, format,
1150			a[0], a[1], a[2], a[3], a[4],
1151			a[5], a[6], a[7], a[8], a[9]
1152		);
1153#	endif
1154#	endif
1155	if (ferror(stream))
1156#endif
1157		Oerror();
1158}
1159
1160#if has_prototypes
1161	void
1162aprintf(FILE *iop, char const *fmt, ...)
1163#else
1164	/*VARARGS2*/ void
1165aprintf(iop, fmt, va_alist)
1166FILE *iop;
1167char const *fmt;
1168va_dcl
1169#endif
1170/* Function: formatted output. Same as fprintf in stdio,
1171 * but aborts program on error
1172 */
1173{
1174	va_list ap;
1175	vararg_start(ap, fmt);
1176	fvfprintf(iop, fmt, ap);
1177	va_end(ap);
1178}
1179
1180
1181
1182#ifdef LEXDB
1183/* test program reading a stream of lexemes and printing the tokens.
1184 */
1185
1186
1187
1188	int
1189main(argc,argv)
1190int argc; char * argv[];
1191{
1192        cmdid="lextest";
1193        if (argc<2) {
1194		aputs("No input file\n",stderr);
1195		exitmain(EXIT_FAILURE);
1196        }
1197	if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
1198		faterror("can't open input file %s",argv[1]);
1199        }
1200        Lexinit();
1201	while (!eoflex()) {
1202        switch (nexttok) {
1203
1204        case ID:
1205                VOID printf("ID: %s",NextString);
1206                break;
1207
1208        case NUM:
1209		if (hshenter)
1210                   VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
1211                else
1212                   VOID printf("NUM, unentered: %s",NextString);
1213                hshenter = !hshenter; /*alternate between dates and numbers*/
1214                break;
1215
1216        case COLON:
1217                VOID printf("COLON"); break;
1218
1219        case SEMI:
1220                VOID printf("SEMI"); break;
1221
1222        case STRING:
1223                readstring();
1224                VOID printf("STRING"); break;
1225
1226        case UNKN:
1227                VOID printf("UNKN"); break;
1228
1229        default:
1230                VOID printf("DEFAULT"); break;
1231        }
1232        VOID printf(" | ");
1233        nextlex();
1234        }
1235	exitmain(EXIT_SUCCESS);
1236}
1237
1238exiting void exiterr() { _exit(EXIT_FAILURE); }
1239
1240
1241#endif
1242