459 lines
9.9 KiB
C
459 lines
9.9 KiB
C
/* $Header$ */
|
|
/* INPUT AND BUFFER HANDLING MODULE */
|
|
|
|
/*
|
|
[input.c input.h]
|
|
Input buffering module: this module contains the routines that
|
|
offers an input buffering mechanism to the user.
|
|
|
|
This module exports the following objects:
|
|
InsertFile() : suspend input from current buffer and obtain the
|
|
next input characters from the specified file
|
|
InsertText() : suspend input from current buffer and take the
|
|
specified text as stream of input characters
|
|
LoadChar() : (defined in input.h) read next character from
|
|
the input ; LoadChar() invokes loadbuf() on
|
|
encounting a ASCII NUL character
|
|
NoUnstack : if set to non-zero:
|
|
loadbuf() reports "unexpected EOF" on encounting
|
|
the end-of-file or end-of-stacked-text.
|
|
|
|
Imported objects are:
|
|
IDEPTH, DEBUG, READ_IN_ONE, PATHLENGTH: compile-time parameters
|
|
Malloc(), Salloc(): memory allocation routines
|
|
fatal(), lexerror(): exception handling
|
|
FileName, LineNumber, WorkingDir: input trace for lexical analyser
|
|
|
|
READ_IN_ONE DEFINED: every input file is read into memory completely
|
|
and made an input buffer
|
|
READ_IN_ONE NOT DEFINED: the input from files is buffered in
|
|
a fixed length input buffer
|
|
*/
|
|
|
|
#include "nopp.h"
|
|
#include "inputtype.h" /* UF */
|
|
#include "interface.h"
|
|
#include "arith.h"
|
|
#include "LLlex.h"
|
|
#include "input.h"
|
|
#include "alloc.h"
|
|
#include "system.h"
|
|
#include "bufsiz.h"
|
|
|
|
#ifndef NOPP
|
|
#include "idepth.h" /* UF */
|
|
#include "debug.h" /* UF */
|
|
#include "pathlength.h" /* UF */
|
|
#include "assert.h"
|
|
#endif NOPP
|
|
|
|
EXPORT char *ipp = 0; /* input pointer */
|
|
EXPORT int NoUnstack = 0; /* if 1: report EOF */
|
|
|
|
#ifndef READ_IN_ONE
|
|
PRIVATE int FilDes = -1; /* current input medium */
|
|
#endif READ_IN_ONE
|
|
|
|
#ifndef NOPP
|
|
struct buffer_header {
|
|
char *bh_name; /* file name where the text comes from */
|
|
unsigned int bh_lineno;
|
|
/* current lineno in file */
|
|
long bh_size; /* = strlen (text), should be unsigned */
|
|
char *bh_text; /* pointer to buffer containing text */
|
|
char *bh_ipp; /* current read pointer (= stacked ipp) */
|
|
char *bh_wdir; /* directory of current file */
|
|
int bh_fd; /* >= 0 (fd if !READ_IN_ONE) in case of file */
|
|
};
|
|
|
|
PRIVATE struct buffer_header instack[IDEPTH]; /* stack of input media */
|
|
PRIVATE struct buffer_header *head = 0; /* current input buffer */
|
|
|
|
IMPORT char **WorkingDir; /* name of current working directory */
|
|
#else NOPP
|
|
long isize;
|
|
char ibuf[BUFSIZ];
|
|
#endif NOPP
|
|
|
|
#ifdef READ_IN_ONE
|
|
/* readfile() creates a buffer in which the text of the file
|
|
is situated. A pointer to the start of this text is
|
|
returned. *size is initialized with the buffer length.
|
|
Note that the file input buffer is prepared for the
|
|
preprocessor by inserting a '\n' in the beginning of the
|
|
text and appending a '\n' at the end of the text. The
|
|
file text start at position 1 of the input buffer. This is
|
|
done to allow pushback.
|
|
*/
|
|
|
|
PRIVATE char *
|
|
readfile(filename, size)
|
|
char *filename;
|
|
long *size;
|
|
{
|
|
int fd; /* filedescriptor for `filename' */
|
|
char *cbuf; /* pointer to buffer to be returned */
|
|
register tmp;
|
|
|
|
if ((fd = sys_open(filename, OP_RDONLY)) < 0) /* can't open this file */
|
|
return (char *) 0;
|
|
|
|
if ((*size = sys_fsize(fd)) < 0)
|
|
fatal("(readfile) cannot get size of file");
|
|
|
|
/* allocate enough space to store contents of the file */
|
|
cbuf = Malloc(*size + 2);
|
|
|
|
tmp = sys_read(fd, cbuf + 1, (int) *size); /* read the file */
|
|
if (tmp != *size)
|
|
fatal("(readfile) bad read count");
|
|
|
|
(*size)++; /* keep book of the size! */
|
|
sys_close(fd); /* filedes no longer needed */
|
|
cbuf[0] = '\0'; /* allow pushback of first char */
|
|
cbuf[*size] = '\0'; /* invoke loadbuf() at end */
|
|
return cbuf;
|
|
}
|
|
#endif READ_IN_ONE
|
|
|
|
#ifndef NOPP
|
|
#ifndef READ_IN_ONE
|
|
/* Input buffer supplying routines: pushbuf() and popbuf()
|
|
*/
|
|
PRIVATE char *bufstack[IDEPTH] = 0;
|
|
PRIVATE bufstptr = 0;
|
|
|
|
PRIVATE char *
|
|
pushbuf()
|
|
{
|
|
if (bufstptr >= IDEPTH)
|
|
fatal("ran out of input buffers");
|
|
if (bufstack[bufstptr] == 0) {
|
|
bufstack[bufstptr] = Malloc(BUFSIZ + 4);
|
|
}
|
|
return bufstack[bufstptr++];
|
|
}
|
|
|
|
PRIVATE
|
|
popbuf()
|
|
{
|
|
bufstptr--;
|
|
ASSERT(bufstptr >= 0);
|
|
}
|
|
#endif READ_IN_ONE
|
|
#endif NOPP
|
|
|
|
#ifndef NOPP
|
|
/* Input buffer administration: push_bh() and pop_bh()
|
|
*/
|
|
PRIVATE struct buffer_header *
|
|
push_bh()
|
|
{
|
|
if (head) {
|
|
if (head >= &instack[IDEPTH - 1])
|
|
fatal("too many nested input texts");
|
|
head->bh_ipp = ipp;
|
|
head->bh_lineno = LineNumber;
|
|
head++;
|
|
}
|
|
else
|
|
head = &instack[0];
|
|
|
|
return head;
|
|
}
|
|
#endif NOPP
|
|
|
|
#ifndef NOPP
|
|
/* pop_bh() uncovers the previous inputbuffer on the stack
|
|
of headers. 0 is returned if there are no more
|
|
inputbuffers on the stack, 1 is returned in the other case.
|
|
*/
|
|
PRIVATE int
|
|
pop_bh()
|
|
{
|
|
int pfd = head->bh_fd;
|
|
|
|
if (NoUnstack) {
|
|
lexerror("unexpected EOF");
|
|
}
|
|
|
|
if (head <= &instack[0]) { /* no more entries */
|
|
head = (struct buffer_header *) 0;
|
|
return 0;
|
|
}
|
|
|
|
ipp = (--head)->bh_ipp; /* restore the previous input pointer */
|
|
|
|
if (pfd >= 0) { /* unstack a file */
|
|
#ifndef READ_IN_ONE
|
|
closefile(pfd);
|
|
popbuf(); /* free last buffer */
|
|
#endif READ_IN_ONE
|
|
LineNumber = head->bh_lineno;
|
|
FileName = head->bh_name;
|
|
*WorkingDir = head->bh_wdir;
|
|
}
|
|
|
|
#ifndef READ_IN_ONE
|
|
FilDes = head->bh_fd;
|
|
#endif READ_IN_ONE
|
|
|
|
return 1;
|
|
}
|
|
#endif NOPP
|
|
|
|
#ifndef READ_IN_ONE
|
|
/* low level IO routines: openfile(), readblock() and closefile()
|
|
*/
|
|
|
|
PRIVATE int
|
|
openfile(filename)
|
|
char *filename;
|
|
{
|
|
int fd; /* filedescriptor for `filename' */
|
|
|
|
if ((fd = sys_open(filename, OP_RDONLY)) < 0 && sys_errno == EMFILE)
|
|
fatal("too many files open");
|
|
return fd;
|
|
}
|
|
|
|
PRIVATE
|
|
closefile(fd)
|
|
{
|
|
sys_close(fd);
|
|
}
|
|
|
|
PRIVATE int
|
|
readblock(fd, buf)
|
|
char buf[];
|
|
{
|
|
register n;
|
|
|
|
if ((n = sys_read(fd, &buf[1], BUFSIZ)) < 0) {
|
|
fatal("(readblock) bad read from file");
|
|
}
|
|
buf[0] = buf[n + 1] = '\0';
|
|
return n;
|
|
}
|
|
#endif READ_IN_ONE
|
|
|
|
/* Interface routines : InsertFile(), InsertText() and loadbuf()
|
|
*/
|
|
|
|
EXPORT int
|
|
InsertFile(filnam, table)
|
|
char *filnam;
|
|
char *table[];
|
|
{
|
|
char *mk_filename(), *newfn;
|
|
char *strcpy();
|
|
|
|
#ifdef READ_IN_ONE
|
|
char *readfile(), *text;
|
|
long size;
|
|
#else READ_IN_ONE
|
|
int fd = -1;
|
|
#endif READ_IN_ONE
|
|
|
|
if (!filnam)
|
|
return 0;
|
|
|
|
#ifndef NOPP
|
|
if (table == 0 || filnam[0] == '/') { /* don't look in the table! */
|
|
#endif NOPP
|
|
#ifdef READ_IN_ONE
|
|
text = readfile(filnam, &size);
|
|
#else READ_IN_ONE
|
|
fd = openfile(filnam);
|
|
#endif READ_IN_ONE
|
|
#ifndef NOPP
|
|
}
|
|
else {
|
|
while (*table) { /* look in the directory table */
|
|
newfn = mk_filename(*table++, filnam);
|
|
#ifdef READ_IN_ONE
|
|
if (text = readfile(newfn, &size))
|
|
#else READ_IN_ONE
|
|
if ((fd = openfile(newfn)) >= 0)
|
|
#endif READ_IN_ONE
|
|
{
|
|
/* free filnam ??? */
|
|
filnam = Salloc(newfn, strlen(newfn) + 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif NOPP
|
|
|
|
#ifdef READ_IN_ONE
|
|
if (text)
|
|
#else READ_IN_ONE
|
|
if (fd >= 0)
|
|
#endif READ_IN_ONE
|
|
#ifndef NOPP
|
|
{
|
|
struct buffer_header *push_bh();
|
|
register struct buffer_header *bh = push_bh();
|
|
|
|
setwdir(WorkingDir, filnam);
|
|
bh->bh_lineno = LineNumber = 0;
|
|
bh->bh_name = FileName = filnam;
|
|
bh->bh_wdir = *WorkingDir;
|
|
#ifdef READ_IN_ONE
|
|
bh->bh_size = size;
|
|
bh->bh_fd = 0; /* this is a file */
|
|
ipp = bh->bh_text = text;
|
|
#else READ_IN_ONE
|
|
bh->bh_size = readblock(fd, ipp = bh->bh_text = pushbuf()) + 1;
|
|
FilDes = bh->bh_fd = fd;
|
|
#endif READ_IN_ONE
|
|
bh->bh_text[0] = '\n'; /* wake up pp if '#' comes first */
|
|
return 1;
|
|
}
|
|
#else NOPP
|
|
{
|
|
#ifdef READ_IN_ONE
|
|
isize = size;
|
|
ipp = text;
|
|
#else READ_IN_ONE
|
|
isize = readblock(FilDes = fd, ipp = &ibuf[0]) + 1;
|
|
#endif READ_IN_ONE
|
|
ibuf[0] = '\n';
|
|
return 1;
|
|
}
|
|
#endif NOPP
|
|
return 0;
|
|
}
|
|
|
|
#ifndef NOPP
|
|
EXPORT
|
|
InsertText(text, length)
|
|
char *text;
|
|
{
|
|
struct buffer_header *push_bh();
|
|
register struct buffer_header *bh = push_bh();
|
|
|
|
bh->bh_name = FileName;
|
|
bh->bh_lineno = LineNumber;
|
|
bh->bh_size = (long) length;
|
|
bh->bh_text = text;
|
|
bh->bh_wdir = *WorkingDir;
|
|
bh->bh_fd = -1; /* this is no file ! */
|
|
ipp = text + 1;
|
|
#ifndef READ_IN_ONE
|
|
FilDes = -1;
|
|
#endif READ_IN_ONE
|
|
}
|
|
#endif NOPP
|
|
|
|
/* loadbuf() is called if LoadChar meets a '\0' character
|
|
which may be the end-of-buffer mark of the current input
|
|
buffer. The '\0' could be genuine although not likely.
|
|
Note: this routine is exported due to its occurence in the definition
|
|
of LoadChar [input.h], that is defined as a macro.
|
|
*/
|
|
EXPORT int
|
|
loadbuf()
|
|
{
|
|
#ifndef NOPP
|
|
if (!head) {
|
|
/* stack exhausted, EOF on sourcefile */
|
|
return EOI;
|
|
}
|
|
#endif NOPP
|
|
|
|
#ifndef NOPP
|
|
if (ipp < &(head->bh_text[head->bh_size]))
|
|
#else NOPP
|
|
if (ipp < &ibuf[isize])
|
|
#endif NOPP
|
|
{
|
|
/* a genuine '\0' character has been seen */
|
|
return '\0';
|
|
}
|
|
|
|
#ifndef READ_IN_ONE
|
|
#ifndef NOPP
|
|
if (FilDes >= 0 && (head->bh_size = readblock(FilDes, head->bh_text)) > 0)
|
|
return ipp = &(head->bh_text[1]), *ipp++;
|
|
#else NOPP
|
|
if (FilDes >= 0 && (isize = readblock(FilDes, &ibuf[0])) > 0)
|
|
return ipp = &ibuf[1], *ipp++;
|
|
#endif NOPP
|
|
|
|
#endif READ_IN_ONE
|
|
|
|
#ifdef NOPP
|
|
if (NoUnstack)
|
|
lexerror("unexpected EOF");
|
|
#ifndef READ_IN_ONE
|
|
closefile(FilDes);
|
|
#endif READ_IN_ONE
|
|
#endif NOPP
|
|
|
|
return
|
|
#ifndef NOPP
|
|
pop_bh() ? (*ipp ? *ipp++ : loadbuf()) :
|
|
#endif NOPP
|
|
(ipp = &"\0\0"[1], EOI);
|
|
}
|
|
|
|
/* Some miscellaneous routines : setwdir() and mk_filename()
|
|
*/
|
|
|
|
#ifndef NOPP
|
|
/* setwdir() updates *wdir according to the old working
|
|
directory (*wdir) and the filename fn, which may contain
|
|
some path name. The algorithm used here is:
|
|
setwdir(DIR, FILE):
|
|
if (FILE == "/***")
|
|
*DIR = "/"
|
|
else
|
|
if (contains(FILE, '/'))
|
|
*DIR = directory(FILE)
|
|
else
|
|
*DIR remains unchanged
|
|
*/
|
|
PRIVATE
|
|
setwdir(wdir, fn)
|
|
char *fn, **wdir;
|
|
{
|
|
register char *p;
|
|
char *rindex();
|
|
|
|
p = rindex(fn, '/');
|
|
while (p && *(p + 1) == '\0') { /* remove trailing /'s */
|
|
*p = '\0';
|
|
p = rindex(fn, '/');
|
|
}
|
|
|
|
if (fn[0] == '\0' || (fn[0] == '/' && p == &fn[0])) /* absolute path */
|
|
*wdir = "/";
|
|
else
|
|
if (p) {
|
|
*p = '\0';
|
|
*wdir = Salloc(fn, p - &fn[0] + 1);
|
|
*p = '/';
|
|
}
|
|
}
|
|
#endif NOPP
|
|
|
|
#ifndef NOPP
|
|
/* mk_filename() concatenates a dir and filename.
|
|
*/
|
|
PRIVATE char *
|
|
mk_filename(dir, file)
|
|
register char *dir, *file;
|
|
{
|
|
static char newfn[PATHLENGTH];
|
|
register char *dst = &newfn[0];
|
|
|
|
if (!(dir[0] == '.' && dir[1] == '\0')) {
|
|
while (*dst++ = *dir++);
|
|
*(dst - 1) = '/';
|
|
}
|
|
while (*dst++ = *file++);
|
|
return &newfn[0];
|
|
}
|
|
#endif NOPP
|