/* 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 #include #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; extern long sys_filesize(); char **pbuf; /* output parameter */ { 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) { struct buffer_header *push_bh(); 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; { struct buffer_header *push_bh(); 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; }