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