/* INPUT AND BUFFER HANDLING MODULE */ /* 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 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 */ #if __STDC__ #include #else extern char *malloc(); #endif #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 INP_PRIVATE #else #define INP_PRIVATE static #endif struct INP_buffer_header { struct INP_buffer_header *bh_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 INP_i_buf { struct INP_i_buf *ib_next; char ib_text[INP_BUFSIZE+INP_NPUSHBACK]; }; #endif /* not INP_READ_IN_ONE */ char *_ipp; INP_PRIVATE struct INP_buffer_header *INP_head, *INP_free; #ifdef INP_READ_IN_ONE /* INP_rdfile() 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. */ _PROTOTYPE(INP_PRIVATE int INP_rdfile, (File *, char *, long *, char **)); INP_PRIVATE int INP_rdfile(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)))) { return 0; } if ( !sys_read(fd, *pbuf, (int) *size, &rsize) || *size != rsize ) { free(*pbuf); return 0; } (*pbuf)[rsize] = '\0'; /* invoke loadbuf() at end */ return 1; } #endif /* INP_READ_IN_ONE */ #ifndef INP_READ_IN_ONE /* Input buffer supplying routines: INP_pbuf() */ INP_PRIVATE struct INP_i_buf *i_ptr; _PROTOTYPE(INP_PRIVATE char * INP_pbuf, (void)); INP_PRIVATE char * INP_pbuf() { register struct INP_i_buf *ib = (struct INP_i_buf *) malloc(sizeof(struct INP_i_buf)); if (!ib) return 0; ib->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: INP_push_bh() and INP_pop_bh() */ _PROTOTYPE(INP_PRIVATE struct INP_buffer_header *INP_push_bh, (void)); _PROTOTYPE(INP_PRIVATE int INP_pop_bh, (void)); INP_PRIVATE struct INP_buffer_header * INP_push_bh() { register struct INP_buffer_header *bh; if (bh = INP_head) { bh->bh_ipp = _ipp; #ifdef INP_TYPE bh->bh_i = INP_VAR; #endif /* INP_TYPE */ } bh = INP_free; if (bh) INP_free = bh->bh_next; else if (!(bh = (struct INP_buffer_header *)malloc(sizeof(struct INP_buffer_header)))) return 0; bh->bh_next = INP_head; bh->bh_eofreturned = 0; INP_head = bh; return bh; } /* INP_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. */ INP_PRIVATE int INP_pop_bh() { register struct INP_buffer_header *bh = INP_head; bh = bh->bh_next; INP_head->bh_next = INP_free; INP_free = INP_head; INP_head = bh; if (!bh) { /* no more entries */ INP_head = (struct INP_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 : INP_rdblock */ _PROTOTYPE(INP_PRIVATE int INP_rdblock, (File *, char *, int *)); INP_PRIVATE int INP_rdblock(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 : INP_mk_filename() */ _PROTOTYPE(INP_PRIVATE int INP_mk_filename, (char *, char *, char **)); /* INP_mk_filename() concatenates a dir and filename. */ INP_PRIVATE int INP_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; if (*dir) { while (*dst++ = *dir++) ; *(dst-1) = '/'; } 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 (!INP_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 INP_buffer_header *bh = INP_push_bh(); if (!bh) { if (fd != STDIN) sys_close(fd); return 0; } #ifdef INP_READ_IN_ONE if (fd == STDIN) return 0; /* illegal */ if (!INP_rdfile(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 = INP_pbuf()) || !INP_rdblock(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 */ if (result) *result = filnam; return 1; } return 0; } int InsertText(text, length) char *text; { register struct INP_buffer_header *bh = INP_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 INP_buffer_header *bh = INP_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'; } if (!bh->bh_eofreturned) { FromFile = (bh->bh_fd != 0); #ifndef INP_READ_IN_ONE if (FromFile && bh->bh_size > 0) { #if INP_NPUSHBACK > 1 register char *so = &(bh->bh_text[bh->bh_size]); register char *de = bh->bh_text; register int i = INP_NPUSHBACK - 1; if (i >= bh->bh_size) i = bh->bh_size - 1; while (i-- > 0) { /* make sure PushBack will work */ *--de = *--so; } #endif if ( INP_rdblock(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 (FromFile && bh->bh_fd != STDIN) sys_close(bh->bh_fd); #if INP_NPUSHBACK > 1 { register char *so = &(bh->bh_text[bh->bh_size]); register char *de = &buf[INP_NPUSHBACK - 1]; register int i = INP_NPUSHBACK - 1; if (i >= bh->bh_size) i = bh->bh_size - 1; for (;i > 0; i--) { /* make sure PushBack will work */ *--de = *--so; } } #endif buf[INP_NPUSHBACK-1] = 0; /* make PushBack work on EOI */ _ipp = &buf[INP_NPUSHBACK]; if (bh->bh_fd) { /* unstack a file */ #ifndef INP_READ_IN_ONE struct INP_i_buf *ib; ib = i_ptr->ib_next; free((char *) i_ptr); i_ptr = ib; #else /* INP_READ_IN_ONE */ free(bh->bh_text); #endif /* INP_READ_IN_ONE */ } bh->bh_text = buf; bh->bh_size = INP_NPUSHBACK - 1; if (FromFile) { if (AtEoIF()) { bh->bh_eofreturned = 1; return EOI; } } else { if (AtEoIT()) { bh->bh_eofreturned = 1; return EOI; } } } if (bh->bh_eofreturned && _ipp == &buf[INP_NPUSHBACK]) { return EOI; } if (INP_pop_bh()) { int c; LoadChar(c); return c; } _ipp = &buf[INP_NPUSHBACK]; return EOI; }