411 lines
8.6 KiB
Text
411 lines
8.6 KiB
Text
/* INPUT AND BUFFER HANDLING MODULE */
|
|
|
|
/*
|
|
[input.X inp_pkg.spec 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:
|
|
input.h : an include-file, which must be included when using
|
|
this module
|
|
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
|
|
PushBack() : (defined in input.h) push last character back onto
|
|
the input stream; NPUSHBACK characters of pushback
|
|
are guaranteed, provided that they have all been read
|
|
from the current input stream
|
|
AtEoIT() : this routine is called at the end of an inserted text.
|
|
A default one is provided, which does nothing.
|
|
AtEoIF() : this routine is called at the end of an inserted file.
|
|
A default one is provided, which does nothing.
|
|
|
|
Imported objects are:
|
|
INP_NPUSHBACK, INP_READ_IN_ONE, INP_TYPE, INP_VAR,
|
|
routines from the "alloc" package, routines from the "storage"
|
|
package, and routines from the "system" package.
|
|
|
|
INP_READ_IN_ONE defined: every input file is read into memory completely
|
|
and made an input buffer. Only use it if the size of a file
|
|
fits always fits in an integer and you have lots of memory.
|
|
INP_READ_IN_ONE not defined: the input from files is buffered in
|
|
a fixed length input buffer
|
|
INP_NPUSHBACK: the number of characters pushback
|
|
*/
|
|
|
|
#include <alloc.h>
|
|
#include <system.h>
|
|
|
|
#ifndef INP_NPUSHBACK
|
|
#define INP_NPUSHBACK 1
|
|
#endif
|
|
|
|
#if INP_NPUSHBACK < 1
|
|
#define INP_NPUSHBACK 1
|
|
#endif
|
|
|
|
#ifndef INP_BUFSIZE
|
|
#define INP_BUFSIZE BUFSIZ
|
|
#endif
|
|
|
|
#if INP_NPUSHBACK > INP_BUFSIZE/2
|
|
Now this is really ridiculous! You deserve what you get!!
|
|
#endif
|
|
|
|
#ifdef INP_TYPE
|
|
extern INP_TYPE INP_VAR;
|
|
#endif INP_TYPE
|
|
|
|
#ifdef DEBUG
|
|
#define PRIVATE
|
|
#else
|
|
#define PRIVATE static
|
|
#endif
|
|
|
|
struct buffer_header {
|
|
struct buffer_header *next;
|
|
int bh_size; /* = strlen (text), should be unsigned */
|
|
char *bh_text; /* pointer to buffer containing text */
|
|
char *bh_ipp; /* current read pointer (= stacked ipp) */
|
|
#ifdef INP_TYPE
|
|
INP_TYPE bh_i; /* user defined */
|
|
#endif INP_TYPE
|
|
File *bh_fd; /* A file descriptor in case of a file */
|
|
char bh_eofreturned; /* set if we returned eof for this buffer */
|
|
};
|
|
|
|
#ifndef INP_READ_IN_ONE
|
|
struct i_buf {
|
|
struct i_buf *next;
|
|
char ib_text[INP_BUFSIZE+INP_NPUSHBACK];
|
|
};
|
|
|
|
# endif not INP_READ_IN_ONE
|
|
|
|
char *_ipp;
|
|
PRIVATE struct buffer_header *head;
|
|
|
|
#ifdef INP_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.
|
|
*/
|
|
|
|
PRIVATE int
|
|
readfile(fd, fn, size, pbuf)
|
|
register File *fd;
|
|
char *fn; /* file name */
|
|
register long *size;
|
|
char **pbuf; /* output parameter */
|
|
{
|
|
extern long sys_filesize();
|
|
int rsize;
|
|
|
|
if (
|
|
(*size = sys_filesize(fn)) < 0
|
|
||
|
|
((unsigned) (*size + 1) != (*size + 1))
|
|
||
|
|
!(*pbuf = malloc((unsigned) (*size + 1)))) {
|
|
sys_close(fd);
|
|
return 0;
|
|
}
|
|
if (
|
|
!sys_read(fd, *pbuf, (int) *size, &rsize)
|
|
||
|
|
*size != rsize
|
|
) {
|
|
sys_close(fd);
|
|
free(*pbuf);
|
|
return 0;
|
|
}
|
|
sys_close(fd);
|
|
(*pbuf)[*size] = '\0'; /* invoke loadbuf() at end */
|
|
return 1;
|
|
}
|
|
#endif INP_READ_IN_ONE
|
|
|
|
#ifndef INP_READ_IN_ONE
|
|
/* Input buffer supplying routines: pushbuf()
|
|
*/
|
|
|
|
PRIVATE struct i_buf *i_ptr;
|
|
|
|
PRIVATE char *
|
|
pushbuf()
|
|
{
|
|
register struct i_buf *ib =
|
|
(struct i_buf *) malloc(sizeof(struct i_buf));
|
|
|
|
if (!ib) return 0;
|
|
ib->next = i_ptr;
|
|
i_ptr = ib;
|
|
|
|
/* Don't give him all of it, we need some to implement a good
|
|
PushBack
|
|
*/
|
|
return &(ib->ib_text[INP_NPUSHBACK-1]);
|
|
}
|
|
#endif not INP_READ_IN_ONE
|
|
|
|
/* Input buffer administration: push_bh() and pop_bh()
|
|
*/
|
|
PRIVATE struct buffer_header *
|
|
push_bh()
|
|
{
|
|
register struct buffer_header *bh;
|
|
|
|
if (bh = head) {
|
|
bh->bh_ipp = _ipp;
|
|
#ifdef INP_TYPE
|
|
bh->bh_i = INP_VAR;
|
|
#endif INP_TYPE
|
|
}
|
|
if (!(bh = (struct buffer_header *)malloc(sizeof(struct buffer_header)))) return 0;
|
|
bh->next = head;
|
|
bh->bh_eofreturned = 0;
|
|
head = bh;
|
|
return bh;
|
|
}
|
|
|
|
/* 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()
|
|
{
|
|
register struct buffer_header *bh = head;
|
|
|
|
if (bh->bh_fd) { /* unstack a file */
|
|
#ifndef INP_READ_IN_ONE
|
|
struct i_buf *ib;
|
|
|
|
ib = i_ptr->next;
|
|
free((char *) i_ptr);
|
|
i_ptr = ib;
|
|
#else INP_READ_IN_ONE
|
|
free(bh->bh_text);
|
|
#endif INP_READ_IN_ONE
|
|
}
|
|
|
|
bh = bh->next;
|
|
free((char *) head);
|
|
head = bh;
|
|
|
|
if (!bh) { /* no more entries */
|
|
head = (struct buffer_header *) 0;
|
|
return 0;
|
|
}
|
|
|
|
_ipp = bh->bh_ipp; /* restore previous input pointer */
|
|
#ifdef INP_TYPE
|
|
INP_VAR = bh->bh_i;
|
|
#endif INP_TYPE
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifndef INP_READ_IN_ONE
|
|
/* low level I/O routine : read one block from current input
|
|
stream : readblock
|
|
*/
|
|
|
|
PRIVATE int
|
|
readblock(fd, buf, n)
|
|
File *fd;
|
|
char buf[];
|
|
int *n;
|
|
{
|
|
|
|
if (!sys_read(fd, buf, INP_BUFSIZE, n)) {
|
|
return 0;
|
|
}
|
|
buf[*n] = '\0';
|
|
return 1;
|
|
}
|
|
#endif not INP_READ_IN_ONE
|
|
|
|
/* Miscellaneous routines :
|
|
mk_filename()
|
|
*/
|
|
|
|
/* mk_filename() concatenates a dir and filename.
|
|
*/
|
|
PRIVATE int
|
|
mk_filename(dir, file, newname)
|
|
register char *dir, *file;
|
|
char **newname;
|
|
{
|
|
|
|
register char *dst;
|
|
|
|
dst = malloc((unsigned) (strlen(dir) + strlen(file) + 2));
|
|
if (!dst) return 0;
|
|
*newname = dst;
|
|
while (*dst++ = *dir++);
|
|
*--dst = '/';
|
|
while (*++dst = *file++);
|
|
return 1;
|
|
}
|
|
|
|
/* Interface routines : InsertFile, InsertText, and loadbuf
|
|
*/
|
|
|
|
int
|
|
InsertFile(filnam, table, result)
|
|
char *filnam;
|
|
char *table[];
|
|
char **result;
|
|
{
|
|
char *newfn = 0;
|
|
|
|
#ifdef INP_READ_IN_ONE
|
|
char *text;
|
|
long size;
|
|
#endif INP_READ_IN_ONE
|
|
File *fd = 0;
|
|
|
|
if (!filnam) fd = STDIN;
|
|
else {
|
|
if (table == 0 || filnam[0] == '/') {
|
|
/* don't look in the table! */
|
|
if (!sys_open(filnam, OP_READ, &fd)) return 0;
|
|
}
|
|
else {
|
|
while (*table) {
|
|
/* look in the directory table */
|
|
if (!mk_filename(*table++, filnam, &newfn)) {
|
|
return 0;
|
|
}
|
|
if (sys_open(newfn, OP_READ, &fd)) {
|
|
/* free filnam ??? NO we don't know
|
|
where it comes from!
|
|
*/
|
|
filnam = newfn;
|
|
break;
|
|
}
|
|
free(newfn);
|
|
newfn = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fd) {
|
|
register struct buffer_header *bh = push_bh();
|
|
|
|
if (!bh) {
|
|
if (fd != STDIN) sys_close(fd);
|
|
return 0;
|
|
}
|
|
#ifdef INP_READ_IN_ONE
|
|
if (fd == STDIN) return 0; /* illegal */
|
|
if (!readfile(fd, filnam, &size, &text)) {
|
|
sys_close(fd);
|
|
return 0;
|
|
}
|
|
bh->bh_size = size;
|
|
_ipp = bh->bh_text = text;
|
|
#else not INP_READ_IN_ONE
|
|
if (
|
|
!(_ipp = bh->bh_text = pushbuf())
|
|
||
|
|
!readblock(fd,_ipp,&(bh->bh_size))) {
|
|
if (fd != STDIN) sys_close(fd);
|
|
return 0;
|
|
}
|
|
#endif INP_READ_IN_ONE
|
|
bh->bh_fd = fd; /* this is a file */
|
|
*result = filnam;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InsertText(text, length)
|
|
char *text;
|
|
{
|
|
register struct buffer_header *bh = push_bh();
|
|
|
|
if (!bh) return 0;
|
|
bh->bh_size = (long) length;
|
|
_ipp = bh->bh_text = text;
|
|
bh->bh_fd = 0; /* No file! */
|
|
return 1;
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
int
|
|
loadbuf()
|
|
{
|
|
register struct buffer_header *bh = head;
|
|
static char buf[INP_NPUSHBACK + 1];
|
|
int FromFile;
|
|
|
|
if (!bh) { /* stack exhausted, EOF on sourcefile */
|
|
return EOI;
|
|
}
|
|
|
|
if (_ipp < &(bh->bh_text[bh->bh_size])) {
|
|
/* a genuine '\0' character has been seen */
|
|
return '\0';
|
|
}
|
|
|
|
FromFile = (bh->bh_fd != 0);
|
|
|
|
#ifndef INP_READ_IN_ONE
|
|
if (FromFile) {
|
|
#if INP_PUSHBACK > 1
|
|
register char *so = &(bh->bh_text[bh->bh_size]);
|
|
register char *de = bh->bh_text;
|
|
register int i = INP_NPUSHBACK - 1;
|
|
|
|
while (i-- > 0) {
|
|
/* make sure PushBack will work */
|
|
*--de = *--so;
|
|
}
|
|
#endif
|
|
if (
|
|
readblock(bh->bh_fd, bh->bh_text, &(bh->bh_size))
|
|
&&
|
|
bh->bh_size > 0
|
|
) {
|
|
_ipp = bh->bh_text;
|
|
return *_ipp++;
|
|
}
|
|
}
|
|
|
|
#endif not INP_READ_IN_ONE
|
|
|
|
if (!bh->bh_eofreturned) {
|
|
bh->bh_eofreturned = 1;
|
|
_ipp--;
|
|
if (FromFile) {
|
|
if (AtEoIF()) return EOI;
|
|
}
|
|
else {
|
|
if (AtEoIT()) return EOI;
|
|
}
|
|
}
|
|
|
|
#ifndef INP_READ_IN_ONE
|
|
if (FromFile && bh->bh_fd != STDIN) sys_close(bh->bh_fd);
|
|
#endif not INP_READ_IN_ONE
|
|
|
|
if (pop_bh()) {
|
|
if (*_ipp) return *_ipp++;
|
|
return loadbuf();
|
|
}
|
|
_ipp = &buf[INP_NPUSHBACK];
|
|
return EOI;
|
|
}
|