freebsd-dev/gnu/usr.bin/rcs/lib/rcslex.c

1569 lines
33 KiB
C
Raw Normal View History

/* lexical analysis of RCS files */
/******************************************************************************
1993-06-18 04:22:21 +00:00
* Lexical Analysis.
* hashtable, Lexinit, nextlex, getlex, getkey,
* getid, getnum, readstring, printstring, savestring,
* checkid, fatserror, error, faterror, warn, diagnose
* Testprogram: define LEXDB
******************************************************************************
1993-06-18 04:22:21 +00:00
*/
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
1993-06-18 04:22:21 +00:00
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1993-06-18 04:22:21 +00:00
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.19 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.18 1995/06/01 16:23:43 eggert
* (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate):
* New functions.
* (Iclose): If large_memory and maps_memory, use them to deallocate mapping.
* (fd2RILE): Use map_fd if available.
* If one mapping method fails, try the next instead of giving up;
* if they all fail, fall back on ordinary read.
* Work around bug: root mmap over NFS succeeds, but accessing dumps core.
* Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t.
* (advise_access): Use madvise only if this instance used mmap.
* (Iopen): Use fdSafer to get safer file descriptor.
* (aflush): Moved here from rcsedit.c.
*
* Revision 5.17 1994/03/20 04:52:58 eggert
* Don't worry if madvise fails. Add Orewind. Remove lint.
*
* Revision 5.16 1993/11/09 17:55:29 eggert
* Fix `label: }' typo.
1995-05-30 05:05:38 +00:00
*
* Revision 5.15 1993/11/03 17:42:27 eggert
* Improve quality of diagnostics by putting file names in them more often.
* Don't discard ignored phrases.
*
* Revision 5.14 1992/07/28 16:12:44 eggert
* Identifiers may now start with a digit and (unless they are symbolic names)
* may contain `.'. Avoid `unsigned'. Statement macro names now end in _.
*
* Revision 5.13 1992/02/17 23:02:27 eggert
* Work around NFS mmap SIGBUS problem.
*
* Revision 5.12 1992/01/06 02:42:34 eggert
* Use OPEN_O_BINARY if mode contains 'b'.
*
1993-06-18 04:22:21 +00:00
* Revision 5.11 1991/11/03 03:30:44 eggert
* Fix porting bug to ancient hosts lacking vfprintf.
*
* Revision 5.10 1991/10/07 17:32:46 eggert
* Support piece tables even if !has_mmap.
*
* Revision 5.9 1991/09/24 00:28:42 eggert
* Don't export errsay().
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Add eoflex(), mmap support. Tune.
*
* Revision 5.7 1991/04/21 11:58:26 eggert
* Add MS-DOS support.
*
* Revision 5.6 1991/02/25 07:12:42 eggert
* Work around fputs bug. strsave -> str_save (DG/UX name clash)
*
* Revision 5.5 1990/12/04 05:18:47 eggert
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.4 1990/11/19 20:05:28 hammer
* no longer gives warning about unknown keywords if -q is specified
*
* Revision 5.3 1990/11/01 05:03:48 eggert
* When ignoring unknown phrases, copy them to the output RCS file.
*
* Revision 5.2 1990/09/04 08:02:27 eggert
* Count RCS lines better.
*
* Revision 5.1 1990/08/29 07:14:03 eggert
* Work around buggy compilers with defective argument promotion.
*
* Revision 5.0 1990/08/22 08:12:55 eggert
* Remove compile-time limits; use malloc instead.
* Report errno-related errors with perror().
* Ansify and Posixate. Add support for ISO 8859.
* Use better hash function.
*
* Revision 4.6 89/05/01 15:13:07 narten
* changed copyright header to reflect current distribution rules
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.5 88/08/28 15:01:12 eggert
* Don't loop when writing error messages to a full filesystem.
* Flush stderr/stdout when mixing output.
* Yield exit status compatible with diff(1).
* Shrink stdio code size; allow cc -R; remove lint.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.4 87/12/18 11:44:47 narten
* fixed to use "varargs" in "fprintf"; this is required if it is to
* work on a SPARC machine such as a Sun-4
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.3 87/10/18 10:37:18 narten
* Updating version numbers. Changes relative to 1.1 actually relative
* to version 4.1
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 1.3 87/09/24 14:00:17 narten
1995-05-30 05:05:38 +00:00
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
1993-06-18 04:22:21 +00:00
* warnings)
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 1.2 87/03/27 14:22:33 jenkins
* Port to suns
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.1 83/03/25 18:12:51 wft
* Only changed $Header to $Id.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 3.3 82/12/10 16:22:37 wft
* Improved error messages, changed exit status on error to 1.
*
* Revision 3.2 82/11/28 21:27:10 wft
* Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
* Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
* properly in case there is an IO-error (e.g., file system full).
*
* Revision 3.1 82/10/11 19:43:56 wft
* removed unused label out:;
* made sure all calls to getc() return into an integer, not a char.
*/
/*
#define LEXDB
*/
/* version LEXDB is for testing the lexical analyzer. The testprogram
* reads a stream of lexemes, enters the revision numbers into the
* hashtable, and prints the recognized tokens. Keywords are recognized
* as identifiers.
*/
#include "rcsbase.h"
1997-02-22 15:48:31 +00:00
libId(lexId, "$Id$")
static char *checkidentifier P((char*,int,int));
static void errsay P((char const*));
static void fatsay P((char const*));
static void lookup P((char const*));
static void startsay P((const char*,const char*));
static void warnsay P((char const*));
1993-06-18 04:22:21 +00:00
static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/
enum tokens nexttok; /*next token, set by nextlex */
int hshenter; /*if true, next suitable lexeme will be entered */
/*into the symbol table. Handle with care. */
int nextc; /*next input character, initialized by Lexinit */
long rcsline; /*current line-number of input */
1993-06-18 04:22:21 +00:00
int nerror; /*counter for errors */
int quietflag; /*indicates quiet mode */
RILE * finptr; /*input file descriptor */
FILE * frewrite; /*file descriptor for echoing input */
FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */
static struct buf tokbuf; /* token buffer */
char const * NextString; /* next token */
/*
* Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
* so hshsize should be odd.
* See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
* Software--practice & experience 20, 2 (Feb 1990), 209-224.
*/
#ifndef hshsize
# define hshsize 511
#endif
static struct hshentry *hshtab[hshsize]; /*hashtable */
static int ignored_phrases; /* have we ignored phrases in this RCS file? */
void
warnignore()
{
if (!ignored_phrases) {
1993-06-18 04:22:21 +00:00
ignored_phrases = true;
rcswarn("Unknown phrases like `%s ...;' are present.", NextString);
1993-06-18 04:22:21 +00:00
}
}
static void
lookup(str)
char const *str;
/* Function: Looks up the character string pointed to by str in the
* hashtable. If the string is not present, a new entry for it is created.
* In any case, the address of the corresponding hashtable entry is placed
* into nexthsh.
*/
{
register unsigned ihash; /* index into hashtable */
register char const *sp;
register struct hshentry *n, **p;
/* calculate hash code */
sp = str;
ihash = 0;
while (*sp)
ihash = (ihash<<2) + *sp++;
ihash %= hshsize;
for (p = &hshtab[ihash]; ; p = &n->nexthsh)
if (!(n = *p)) {
/* empty slot found */
*p = n = ftalloc(struct hshentry);
n->num = fstr_save(str);
n->nexthsh = 0;
1993-06-18 04:22:21 +00:00
# ifdef LEXDB
VOID printf("\nEntered: %s at %u ", str, ihash);
# endif
break;
} else if (strcmp(str, n->num) == 0)
/* match found */
break;
nexthsh = n;
NextString = n->num;
}
void
Lexinit()
/* Function: Initialization of lexical analyzer:
* initializes the hashtable,
* initializes nextc, nexttok if finptr != 0
*/
{ register int c;
for (c = hshsize; 0 <= --c; ) {
hshtab[c] = 0;
1993-06-18 04:22:21 +00:00
}
nerror = 0;
if (finptr) {
foutptr = 0;
hshenter = true;
ignored_phrases = false;
rcsline = 1;
bufrealloc(&tokbuf, 2);
Iget_(finptr, nextc)
1993-06-18 04:22:21 +00:00
nextlex(); /*initial token*/
}
}
void
nextlex()
/* Function: Reads the next token and sets nexttok to the next token code.
* Only if hshenter is set, a revision number is entered into the
* hashtable and a pointer to it is placed into nexthsh.
* This is useful for avoiding that dates are placed into the hashtable.
* For ID's and NUM's, NextString is set to the character string.
* Assumption: nextc contains the next character.
*/
{ register c;
declarecache;
register FILE *frew;
register char * sp;
char const *limit;
register enum tokens d;
register RILE *fin;
fin=finptr; frew=foutptr;
setupcache(fin); cache(fin);
c = nextc;
for (;;) { switch ((d = ctab[c])) {
default:
fatserror("unknown character `%c'", c);
/*NOTREACHED*/
case NEWLN:
++rcsline;
# ifdef LEXDB
afputc('\n',stdout);
# endif
/* Note: falls into next case */
case SPACE:
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
continue;
case IDCHAR:
case LETTER:
1993-06-18 04:22:21 +00:00
case Letter:
d = ID;
/* fall into */
case DIGIT:
case PERIOD:
1993-06-18 04:22:21 +00:00
sp = tokbuf.string;
limit = sp + tokbuf.size;
*sp++ = c;
for (;;) {
GETC_(frew, c)
switch (ctab[c]) {
case IDCHAR:
case LETTER:
case Letter:
d = ID;
/* fall into */
case DIGIT:
case PERIOD:
*sp++ = c;
if (limit <= sp)
sp = bufenlarge(&tokbuf, &limit);
continue;
default:
1993-06-18 04:22:21 +00:00
break;
}
break;
1993-06-18 04:22:21 +00:00
}
*sp = 0;
if (d == DIGIT || d == PERIOD) {
d = NUM;
if (hshenter) {
lookup(tokbuf.string);
break;
}
}
1993-06-18 04:22:21 +00:00
NextString = fstr_save(tokbuf.string);
break;
case SBEGIN: /* long string */
d = STRING;
/* note: only the initial SBEGIN has been read*/
/* read the string, and reset nextc afterwards*/
break;
case COLON:
case SEMI:
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
break;
} break; }
nextc = c;
nexttok = d;
uncache(fin);
}
int
eoflex()
/*
* Yield true if we look ahead to the end of the input, false otherwise.
* nextc becomes undefined at end of file.
*/
{
register int c;
declarecache;
register FILE *fout;
register RILE *fin;
c = nextc;
fin = finptr;
fout = foutptr;
setupcache(fin); cache(fin);
for (;;) {
switch (ctab[c]) {
default:
nextc = c;
uncache(fin);
return false;
case NEWLN:
++rcsline;
/* fall into */
case SPACE:
cachegeteof_(c, {uncache(fin);return true;})
1993-06-18 04:22:21 +00:00
break;
}
if (fout)
aputc_(c, fout)
1993-06-18 04:22:21 +00:00
}
}
int getlex(token)
enum tokens token;
/* Function: Checks if nexttok is the same as token. If so,
* advances the input by calling nextlex and returns true.
* otherwise returns false.
* Doesn't work for strings and keywords; loses the character string for ids.
*/
{
if (nexttok==token) {
nextlex();
return(true);
} else return(false);
}
int
getkeyopt(key)
char const *key;
/* Function: If the current token is a keyword identical to key,
* advances the input by calling nextlex and returns true;
* otherwise returns false.
*/
{
if (nexttok==ID && strcmp(key,NextString) == 0) {
/* match found */
ffree1(NextString);
nextlex();
return(true);
}
return(false);
}
void
getkey(key)
char const *key;
/* Check that the current input token is a keyword identical to key,
* and advance the input by calling nextlex.
*/
{
if (!getkeyopt(key))
fatserror("missing '%s' keyword", key);
}
void
getkeystring(key)
char const *key;
/* Check that the current input token is a keyword identical to key,
* and advance the input by calling nextlex; then look ahead for a string.
*/
{
getkey(key);
if (nexttok != STRING)
fatserror("missing string after '%s' keyword", key);
}
char const *
getid()
/* Function: Checks if nexttok is an identifier. If so,
* advances the input by calling nextlex and returns a pointer
* to the identifier; otherwise returns 0.
1993-06-18 04:22:21 +00:00
* Treats keywords as identifiers.
*/
{
register char const *name;
if (nexttok==ID) {
name = NextString;
nextlex();
return name;
} else
return 0;
1993-06-18 04:22:21 +00:00
}
struct hshentry * getnum()
/* Function: Checks if nexttok is a number. If so,
* advances the input by calling nextlex and returns a pointer
* to the hashtable entry. Otherwise returns 0.
1993-06-18 04:22:21 +00:00
* Doesn't work if hshenter is false.
*/
{
register struct hshentry * num;
if (nexttok==NUM) {
num=nexthsh;
nextlex();
return num;
} else
return 0;
1993-06-18 04:22:21 +00:00
}
struct cbuf
getphrases(key)
char const *key;
/*
* Get a series of phrases that do not start with KEY. Yield resulting buffer.
* Stop when the next phrase starts with a token that is not an identifier,
* or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(),
* this routine assumes nextlex() has already been invoked before we start.
*/
1993-06-18 04:22:21 +00:00
{
declarecache;
register int c;
register char const *kn;
1993-06-18 04:22:21 +00:00
struct cbuf r;
register RILE *fin;
register FILE *frew;
# if large_memory
# define savech_(c) ;
# else
register char *p;
char const *limit;
struct buf b;
# define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
# endif
1993-06-18 04:22:21 +00:00
if (nexttok!=ID || strcmp(NextString,key) == 0)
clear_buf(&r);
else {
1993-06-18 04:22:21 +00:00
warnignore();
fin = finptr;
frew = foutptr;
1993-06-18 04:22:21 +00:00
setupcache(fin); cache(fin);
# if large_memory
r.string = (char const*)cacheptr() - strlen(NextString) - 1;
# else
bufautobegin(&b);
bufscpy(&b, NextString);
p = b.string + strlen(b.string);
limit = b.string + b.size;
# endif
1993-06-18 04:22:21 +00:00
ffree1(NextString);
c = nextc;
for (;;) {
for (;;) {
savech_(c)
1993-06-18 04:22:21 +00:00
switch (ctab[c]) {
default:
fatserror("unknown character `%c'", c);
/*NOTREACHED*/
case NEWLN:
++rcsline;
/* fall into */
case COLON: case DIGIT: case LETTER: case Letter:
case PERIOD: case SPACE:
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
continue;
case SBEGIN: /* long string */
for (;;) {
for (;;) {
GETC_(frew, c)
savech_(c)
1993-06-18 04:22:21 +00:00
switch (c) {
case '\n':
++rcsline;
/* fall into */
default:
continue;
case SDELIM:
break;
}
break;
}
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
if (c != SDELIM)
break;
savech_(c)
1993-06-18 04:22:21 +00:00
}
continue;
case SEMI:
cacheget_(c)
1993-06-18 04:22:21 +00:00
if (ctab[c] == NEWLN) {
if (frew)
aputc_(c, frew)
1993-06-18 04:22:21 +00:00
++rcsline;
savech_(c)
cacheget_(c)
1993-06-18 04:22:21 +00:00
}
# if large_memory
r.size = (char const*)cacheptr() - 1 - r.string;
# endif
1993-06-18 04:22:21 +00:00
for (;;) {
switch (ctab[c]) {
case NEWLN:
++rcsline;
/* fall into */
case SPACE:
cacheget_(c)
1993-06-18 04:22:21 +00:00
continue;
default: break;
}
break;
}
if (frew)
aputc_(c, frew)
1993-06-18 04:22:21 +00:00
break;
}
break;
}
if (ctab[c] == Letter) {
1993-06-18 04:22:21 +00:00
for (kn = key; c && *kn==c; kn++)
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
if (!*kn)
switch (ctab[c]) {
case DIGIT: case LETTER: case Letter:
case IDCHAR: case PERIOD:
1993-06-18 04:22:21 +00:00
break;
default:
nextc = c;
NextString = fstr_save(key);
nexttok = ID;
uncache(fin);
goto returnit;
}
# if !large_memory
{
register char const *ki;
for (ki=key; ki<kn; )
savech_(*ki++)
}
# endif
} else {
1993-06-18 04:22:21 +00:00
nextc = c;
uncache(fin);
nextlex();
break;
1993-06-18 04:22:21 +00:00
}
}
returnit:;
# if !large_memory
return bufremember(&b, (size_t)(p - b.string));
# endif
1993-06-18 04:22:21 +00:00
}
return r;
1993-06-18 04:22:21 +00:00
}
void
readstring()
/* skip over characters until terminating single SDELIM */
/* If foutptr is set, copy every character read to foutptr. */
/* Does not advance nextlex at the end. */
{ register c;
declarecache;
register FILE *frew;
register RILE *fin;
fin=finptr; frew=foutptr;
setupcache(fin); cache(fin);
for (;;) {
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
switch (c) {
case '\n':
++rcsline;
break;
case SDELIM:
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
if (c != SDELIM) {
/* end of string */
nextc = c;
uncache(fin);
return;
}
break;
}
}
}
void
printstring()
/* Function: copy a string to stdout, until terminated with a single SDELIM.
* Does not advance nextlex at the end.
*/
{
register c;
declarecache;
register FILE *fout;
register RILE *fin;
fin=finptr;
fout = stdout;
setupcache(fin); cache(fin);
for (;;) {
cacheget_(c)
1993-06-18 04:22:21 +00:00
switch (c) {
case '\n':
++rcsline;
break;
case SDELIM:
cacheget_(c)
1993-06-18 04:22:21 +00:00
if (c != SDELIM) {
nextc=c;
uncache(fin);
return;
}
break;
}
aputc_(c,fout)
1993-06-18 04:22:21 +00:00
}
}
struct cbuf
savestring(target)
struct buf *target;
/* Copies a string terminated with SDELIM from file finptr to buffer target.
* Double SDELIM is replaced with SDELIM.
* If foutptr is set, the string is also copied unchanged to foutptr.
* Does not advance nextlex at the end.
* Yield a copy of *TARGET, except with exact length.
*/
{
register c;
declarecache;
register FILE *frew;
register char *tp;
register RILE *fin;
char const *limit;
struct cbuf r;
fin=finptr; frew=foutptr;
setupcache(fin); cache(fin);
tp = target->string; limit = tp + target->size;
for (;;) {
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
switch (c) {
case '\n':
++rcsline;
break;
case SDELIM:
GETC_(frew, c)
1993-06-18 04:22:21 +00:00
if (c != SDELIM) {
/* end of string */
nextc=c;
r.string = target->string;
r.size = tp - r.string;
uncache(fin);
return r;
}
break;
}
if (tp == limit)
tp = bufenlarge(target, &limit);
*tp++ = c;
}
}
static char *
checkidentifier(id, delimiter, dotok)
1993-06-18 04:22:21 +00:00
register char *id;
int delimiter;
register int dotok;
1993-06-18 04:22:21 +00:00
/* Function: check whether the string starting at id is an */
/* identifier and return a pointer to the delimiter*/
/* after the identifier. White space, delim and 0 */
/* are legal delimiters. Aborts the program if not*/
/* a legal identifier. Useful for checking commands*/
/* If !delim, the only delimiter is 0. */
/* Allow '.' in identifier only if DOTOK is set. */
1993-06-18 04:22:21 +00:00
{
register char *temp;
register char c;
1993-06-18 04:22:21 +00:00
register char delim = delimiter;
int isid = false;
1993-06-18 04:22:21 +00:00
temp = id;
for (;; id++) {
switch (ctab[(unsigned char)(c = *id)]) {
case IDCHAR:
case LETTER:
case Letter:
isid = true;
continue;
case DIGIT:
continue;
case PERIOD:
if (dotok)
continue;
break;
default:
break;
}
break;
}
if ( ! isid
|| (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
) {
1993-06-18 04:22:21 +00:00
/* append \0 to end of id before error message */
while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
id++;
1993-06-18 04:22:21 +00:00
*id = '\0';
faterror("invalid %s `%s'",
dotok ? "identifier" : "symbol", temp
);
}
1993-06-18 04:22:21 +00:00
return id;
}
char *
checkid(id, delimiter)
char *id;
int delimiter;
{
return checkidentifier(id, delimiter, true);
}
char *
checksym(sym, delimiter)
char *sym;
int delimiter;
{
return checkidentifier(sym, delimiter, false);
}
1993-06-18 04:22:21 +00:00
void
checksid(id)
char *id;
/* Check whether the string ID is an identifier. */
{
VOID checkid(id, 0);
}
void
checkssym(sym)
char *sym;
{
VOID checksym(sym, 0);
}
#if !large_memory
# define Iclose(f) fclose(f)
#else
# if !maps_memory
static int Iclose P((RILE *));
static int
Iclose(f)
register RILE *f;
{
tfree(f->base);
f->base = 0;
return fclose(f->stream);
}
# else
static int Iclose P((RILE *));
static int
Iclose(f)
register RILE *f;
{
(* f->deallocate) (f);
f->base = 0;
return close(f->fd);
}
# if has_map_fd
static void map_fd_deallocate P((RILE *));
static void
map_fd_deallocate(f)
register RILE *f;
{
if (vm_deallocate(
task_self(),
(vm_address_t) f->base,
(vm_size_t) (f->lim - f->base)
) != KERN_SUCCESS)
efaterror("vm_deallocate");
}
# endif
# if has_mmap
static void mmap_deallocate P((RILE *));
static void
mmap_deallocate(f)
register RILE *f;
{
if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
efaterror("munmap");
}
# endif
static void read_deallocate P((RILE *));
static void
read_deallocate(f)
RILE *f;
{
tfree(f->base);
}
static void nothing_to_deallocate P((RILE *));
static void
nothing_to_deallocate(f)
RILE *f;
{
}
# endif
#endif
1993-06-18 04:22:21 +00:00
#if large_memory && maps_memory
static RILE *fd2_RILE P((int,char const*,struct stat*));
1993-06-18 04:22:21 +00:00
static RILE *
fd2_RILE(fd, name, status)
1993-06-18 04:22:21 +00:00
#else
static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
static RILE *
fd2RILE(fd, name, type, status)
char const *type;
1993-06-18 04:22:21 +00:00
#endif
int fd;
char const *name;
1993-06-18 04:22:21 +00:00
register struct stat *status;
{
struct stat st;
if (!status)
status = &st;
if (fstat(fd, status) != 0)
efaterror(name);
1993-06-18 04:22:21 +00:00
if (!S_ISREG(status->st_mode)) {
error("`%s' is not a regular file", name);
1993-06-18 04:22:21 +00:00
VOID close(fd);
errno = EINVAL;
return 0;
} else {
# if !(large_memory && maps_memory)
1993-06-18 04:22:21 +00:00
FILE *stream;
if (!(stream = fdopen(fd, type)))
efaterror(name);
1993-06-18 04:22:21 +00:00
# endif
# if !large_memory
return stream;
# else
# define RILES 3
{
static RILE rilebuf[RILES];
register RILE *f;
size_t s = status->st_size;
if (s != status->st_size)
faterror("%s: too large", name);
for (f = rilebuf; f->base; f++)
if (f == rilebuf+RILES)
faterror("too many RILEs");
# if maps_memory
f->deallocate = nothing_to_deallocate;
# endif
if (!s) {
static unsigned char nothing;
f->base = &nothing; /* Any nonzero address will do. */
} else {
f->base = 0;
# if has_map_fd
map_fd(
fd, (vm_offset_t)0, (vm_address_t*) &f->base,
TRUE, (vm_size_t)s
);
f->deallocate = map_fd_deallocate;
# endif
# if has_mmap
if (!f->base) {
catchmmapints();
f->base = (unsigned char *) mmap(
(char *)0, s, PROT_READ, MAP_SHARED,
fd, (off_t)0
);
# ifndef MAP_FAILED
# define MAP_FAILED (-1)
1993-06-18 04:22:21 +00:00
# endif
if (f->base == (unsigned char *) MAP_FAILED)
f->base = 0;
else {
# if has_NFS && mmap_signal
/*
* On many hosts, the superuser
* can mmap an NFS file it can't read.
* So access the first page now, and print
* a nice message if a bus error occurs.
*/
readAccessFilenameBuffer(name, f->base);
# endif
}
f->deallocate = mmap_deallocate;
}
# endif
if (!f->base) {
f->base = tnalloc(unsigned char, s);
# if maps_memory
{
/*
* We can't map the file into memory for some reason.
* Read it into main memory all at once; this is
* the simplest substitute for memory mapping.
*/
char *bufptr = (char *) f->base;
size_t bufsiz = s;
do {
ssize_t r = read(fd, bufptr, bufsiz);
switch (r) {
case -1:
efaterror(name);
case 0:
/* The file must have shrunk! */
status->st_size = s -= bufsiz;
bufsiz = 0;
break;
default:
bufptr += r;
bufsiz -= r;
break;
}
} while (bufsiz);
if (lseek(fd, (off_t)0, SEEK_SET) == -1)
efaterror(name);
f->deallocate = read_deallocate;
1993-06-18 04:22:21 +00:00
}
# endif
}
1993-06-18 04:22:21 +00:00
}
f->ptr = f->base;
f->lim = f->base + s;
f->fd = fd;
# if !maps_memory
f->readlim = f->base;
f->stream = stream;
# endif
if_advise_access(s, f, MADV_SEQUENTIAL);
return f;
}
1993-06-18 04:22:21 +00:00
# endif
}
}
#if !maps_memory && large_memory
1993-06-18 04:22:21 +00:00
int
Igetmore(f)
register RILE *f;
{
register fread_type r;
register size_t s = f->lim - f->readlim;
if (BUFSIZ < s)
s = BUFSIZ;
if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
testIerror(f->stream);
f->lim = f->readlim; /* The file might have shrunk! */
return 0;
}
f->readlim += r;
return 1;
}
#endif
#if has_madvise && has_mmap && large_memory
void
advise_access(f, advice)
register RILE *f;
int advice;
{
if (f->deallocate == mmap_deallocate)
VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
/* Don't worry if madvise fails; it's only advisory. */
1993-06-18 04:22:21 +00:00
}
#endif
RILE *
#if large_memory && maps_memory
I_open(name, status)
1993-06-18 04:22:21 +00:00
#else
Iopen(name, type, status)
char const *type;
1993-06-18 04:22:21 +00:00
#endif
char const *name;
1993-06-18 04:22:21 +00:00
struct stat *status;
/* Open NAME for reading, yield its descriptor, and set *STATUS. */
1993-06-18 04:22:21 +00:00
{
int fd = fdSafer(open(name, O_RDONLY
# if OPEN_O_BINARY
| (strchr(type,'b') ? OPEN_O_BINARY : 0)
# endif
));
1993-06-18 04:22:21 +00:00
if (fd < 0)
1993-06-18 04:22:21 +00:00
return 0;
# if large_memory && maps_memory
return fd2_RILE(fd, name, status);
1993-06-18 04:22:21 +00:00
# else
return fd2RILE(fd, name, type, status);
1993-06-18 04:22:21 +00:00
# endif
}
static int Oerrloop;
void
1993-06-18 04:22:21 +00:00
Oerror()
{
if (Oerrloop)
exiterr();
Oerrloop = true;
efaterror("output error");
}
void Ieof() { fatserror("unexpected end of file"); }
void Ierror() { efaterror("input error"); }
1993-06-18 04:22:21 +00:00
void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
#if !large_memory
void
testIeof(f)
FILE *f;
{
testIerror(f);
if (feof(f))
Ieof();
}
void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
#endif
void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1993-06-18 04:22:21 +00:00
void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
1993-06-18 04:22:21 +00:00
void oflush()
{
if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop)
Oerror();
}
void
1993-06-18 04:22:21 +00:00
fatcleanup(already_newline)
int already_newline;
{
VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
exiterr();
}
static void
startsay(s, t)
const char *s, *t;
{
oflush();
if (s)
aprintf(stderr, "%s: %s: %s", cmdid, s, t);
else
aprintf(stderr, "%s: %s", cmdid, t);
}
static void
fatsay(s)
char const *s;
{
startsay(s, "");
}
static void
errsay(s)
char const *s;
{
fatsay(s);
nerror++;
}
static void
warnsay(s)
char const *s;
{
startsay(s, "warning: ");
}
1993-06-18 04:22:21 +00:00
void eerror(s) char const *s; { enerror(errno,s); }
void
enerror(e,s)
int e;
char const *s;
{
errsay((char const*)0);
1993-06-18 04:22:21 +00:00
errno = e;
perror(s);
eflush();
}
void efaterror(s) char const *s; { enfaterror(errno,s); }
1993-06-18 04:22:21 +00:00
void
1993-06-18 04:22:21 +00:00
enfaterror(e,s)
int e;
char const *s;
{
fatsay((char const*)0);
1993-06-18 04:22:21 +00:00
errno = e;
perror(s);
fatcleanup(true);
}
#if has_prototypes
void
error(char const *format,...)
#else
/*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
#endif
/* non-fatal error */
{
va_list args;
errsay((char const*)0);
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
afputc('\n',stderr);
eflush();
}
#if has_prototypes
void
rcserror(char const *format,...)
#else
/*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
#endif
/* non-fatal RCS file error */
{
va_list args;
errsay(RCSname);
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
afputc('\n',stderr);
eflush();
}
#if has_prototypes
void
workerror(char const *format,...)
#else
/*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
#endif
/* non-fatal working file error */
{
va_list args;
errsay(workname);
1993-06-18 04:22:21 +00:00
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
afputc('\n',stderr);
eflush();
}
#if has_prototypes
void
1993-06-18 04:22:21 +00:00
fatserror(char const *format,...)
#else
/*VARARGS1*/ void
1993-06-18 04:22:21 +00:00
fatserror(format, va_alist) char const *format; va_dcl
#endif
/* fatal RCS file syntax error */
1993-06-18 04:22:21 +00:00
{
va_list args;
oflush();
VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
1993-06-18 04:22:21 +00:00
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
fatcleanup(false);
}
#if has_prototypes
void
1993-06-18 04:22:21 +00:00
faterror(char const *format,...)
#else
/*VARARGS1*/ void faterror(format, va_alist)
1993-06-18 04:22:21 +00:00
char const *format; va_dcl
#endif
/* fatal error, terminates program after cleanup */
{
va_list args;
fatsay((char const*)0);
1993-06-18 04:22:21 +00:00
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
fatcleanup(false);
}
#if has_prototypes
void
rcsfaterror(char const *format,...)
1993-06-18 04:22:21 +00:00
#else
/*VARARGS1*/ void rcsfaterror(format, va_alist)
char const *format; va_dcl
1993-06-18 04:22:21 +00:00
#endif
/* fatal RCS file error, terminates program after cleanup */
1993-06-18 04:22:21 +00:00
{
va_list args;
fatsay(RCSname);
1993-06-18 04:22:21 +00:00
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
fatcleanup(false);
}
#if has_prototypes
void
warn(char const *format,...)
#else
/*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
#endif
/* warning */
{
va_list args;
if (!quietflag) {
warnsay((char *)0);
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
afputc('\n', stderr);
eflush();
}
}
#if has_prototypes
void
rcswarn(char const *format,...)
#else
/*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
#endif
/* RCS file warning */
{
va_list args;
if (!quietflag) {
warnsay(RCSname);
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
afputc('\n', stderr);
eflush();
}
}
#if has_prototypes
void
workwarn(char const *format,...)
#else
/*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
#endif
/* working file warning */
{
va_list args;
if (!quietflag) {
warnsay(workname);
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
afputc('\n', stderr);
eflush();
}
1993-06-18 04:22:21 +00:00
}
void
redefined(c)
int c;
{
warn("redefinition of -%c option", c);
}
#if has_prototypes
void
diagnose(char const *format,...)
#else
/*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
#endif
/* prints a diagnostic message */
/* Unlike the other routines, it does not append a newline. */
/* This lets some callers suppress the newline, and is faster */
/* in implementations that flush stderr just at the end of each printf. */
{
va_list args;
if (!quietflag) {
oflush();
vararg_start(args, format);
fvfprintf(stderr, format, args);
va_end(args);
eflush();
}
}
void
afputc(c, f)
/* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */
1993-06-18 04:22:21 +00:00
int c;
register FILE *f;
{
aputc_(c,f)
1993-06-18 04:22:21 +00:00
}
void
aputs(s, iop)
char const *s;
FILE *iop;
/* Function: Put string s on file iop, abort on error.
*/
{
#if has_fputs
if (fputs(s, iop) < 0)
Oerror();
#else
awrite(s, strlen(s), iop);
#endif
}
void
#if has_prototypes
fvfprintf(FILE *stream, char const *format, va_list args)
#else
fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
#endif
/* like vfprintf, except abort program on error */
{
#if has_vfprintf
if (vfprintf(stream, format, args) < 0)
Oerror();
1993-06-18 04:22:21 +00:00
#else
# if has__doprintf
_doprintf(stream, format, args);
# else
# if has__doprnt
_doprnt(format, args, stream);
# else
int *a = (int *)args;
VOID fprintf(stream, format,
a[0], a[1], a[2], a[3], a[4],
a[5], a[6], a[7], a[8], a[9]
);
# endif
# endif
if (ferror(stream))
Oerror();
#endif
1993-06-18 04:22:21 +00:00
}
#if has_prototypes
void
aprintf(FILE *iop, char const *fmt, ...)
#else
/*VARARGS2*/ void
aprintf(iop, fmt, va_alist)
FILE *iop;
char const *fmt;
va_dcl
#endif
/* Function: formatted output. Same as fprintf in stdio,
* but aborts program on error
*/
{
va_list ap;
vararg_start(ap, fmt);
fvfprintf(iop, fmt, ap);
va_end(ap);
}
#ifdef LEXDB
/* test program reading a stream of lexemes and printing the tokens.
*/
int
main(argc,argv)
int argc; char * argv[];
{
cmdid="lextest";
if (argc<2) {
aputs("No input file\n",stderr);
exitmain(EXIT_FAILURE);
}
if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
faterror("can't open input file %s",argv[1]);
}
Lexinit();
while (!eoflex()) {
switch (nexttok) {
case ID:
VOID printf("ID: %s",NextString);
break;
case NUM:
if (hshenter)
VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
else
VOID printf("NUM, unentered: %s",NextString);
hshenter = !hshenter; /*alternate between dates and numbers*/
break;
case COLON:
VOID printf("COLON"); break;
case SEMI:
VOID printf("SEMI"); break;
case STRING:
readstring();
VOID printf("STRING"); break;
case UNKN:
VOID printf("UNKN"); break;
default:
VOID printf("DEFAULT"); break;
}
VOID printf(" | ");
nextlex();
}
exitmain(EXIT_SUCCESS);
}
void exiterr() { _exit(EXIT_FAILURE); }
1993-06-18 04:22:21 +00:00
#endif