home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- *** ch.c (JJB TEMPLAR) ***
- *** Date modifications begun: 7/8/89. ***
- *** Last modified: 13/8/89. ***
- *************************************************************************/
- /*** Low level character input from the input file. We use these ***
- *** special purpose routines which optimize moving both forward and ***
- *** backward from the current read pointer. ***
- *************************************************************************/
-
- #include "less.h"
- #include <exec/memory.h>
- #include <libraries/dos.h>
-
- extern int fast_line;
-
- BPTR file = NULL; /* File descriptor of the input file */
-
- /* Pool of buffers holding the most recently used blocks of the input file. */
- #define BUFSIZ 1024
- struct buf {
- struct buf *next, *prev;
- long block;
- char data[BUFSIZ];
- };
- static struct buf *bufs = NULL;
- int nbufs;
-
- /* The buffer pool is kept as a doubly-linked circular list,
- in order from most- to least-recently used.
- The circular list is anchored by buf_anchor. */
- static struct {
- struct buf *next, *prev;
- } buf_anchor;
- #define END_OF_CHAIN ((struct buf *)&buf_anchor)
- #define buf_head buf_anchor.next
- #define buf_tail buf_anchor.prev
-
- /* If we fail to allocate enough memory for buffers, we try to limp
- along with a minimum number of buffers. */
- #define DEF_NBUFS 2 /* Minimum number of buffers */
-
- /* Current position in file. Stored as block num. and offset into the block. */
- static long ch_block;
- static int ch_offset;
-
- /* Length of file, needed if input is a pipe. */
- static LONG ch_fsize;
-
- /* Largest block number read if input is standard input (a pipe). */
- static long last_piped_block;
-
-
- /*=============================fch_get===================================*/
- /* Get the character pointed to by the read pointer. ch_get() is a macro
- which is more efficient to call than fch_get (the function), in the
- usual case that the block desired is at the head of the chain. */
- #define ch_get() ((buf_head->block == ch_block) ? \
- buf_head->data[ch_offset] : fch_get())
-
- static int fch_get() /*=================================================*/
- {
- register struct buf *bp;
- register char *cp;
- register int n;
- register int end;
- LONG pos;
-
- /* Look for a buffer holding the desired block. */
- for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- if (bp->block == ch_block) goto found;
-
- /* Block is not in a buffer. Take the least recently used buffer
- and read the desired block into it. */
- bp = buf_tail;
- bp->block = ch_block;
- pos = ch_block * BUFSIZ;
- Seek(file, pos, OFFSET_BEGINNING);
-
- end = Read(file, &bp->data[0], BUFSIZ); /* Read block */
- if (end < 0) { /* Check if bombed out */
- error("read error",1);
- cleanup(NULL,50);
- }
- /* Zap through and convert zeros to asterisks */
- if (!fast_line) {
- cp = &bp->data[0];
- for (n = 0; n < end; n++,cp++) if (!(*cp)) *cp = '@';
- }
-
- /* Set an EOF marker in the buffered data itself.
- Then ensure the data is "clean": there are no
- extra EOF chars in the data and that the "meta"
- bit (the 0200 bit) is reset in each char. */
- if (end < BUFSIZ) {
- ch_fsize = pos + end;
- bp->data[end] = EOF;
- }
-
- found:
- /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
- /* Move the buffer to the head of the buffer chain.
- This orders the buffer chain, most- to least-recently used. */
- bp->next->prev = bp->prev;
- bp->prev->next = bp->next;
-
- bp->next = buf_head;
- bp->prev = END_OF_CHAIN;
- buf_head->prev = bp;
- buf_head = bp;
- /* } */
- return((int) bp->data[ch_offset]);
- }
-
- static int buffered(block) /*===========================================*/
- long block; /* Is block in one of the buffers? */
- {
- register struct buf *bp;
-
- for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- if (bp->block == block) return (1);
- return(0);
- }
-
- int ch_seek(pos) /*==================================================*/
- register LONG pos; /* Seek to a specific pos in file. */
- { /* Zero if successful, non-zero if fails. */
- long new_block;
- new_block = pos / BUFSIZ;
- /* Set read pointer. */
- ch_block = new_block;
- ch_offset = pos % BUFSIZ;
- return(0);
- }
-
- void end_seek() /*====================================================*/
- { /* Seek to end of file. */
- Seek(file, 0, OFFSET_END);
- ch_seek(Seek(file, 0, OFFSET_END)); /* Second Seek returns final pos */
- }
-
- LONG ch_length() /*===================================================*/
- {
- Seek(file, 0, OFFSET_END);
- return(Seek(file, 0, OFFSET_END));
- }
-
- LONG ch_tell() /*=====================================================*/
- { /* Return cur position in file. */
- return(ch_block * BUFSIZ + ch_offset);
- }
-
- int ch_forw_get() /*=================================================*/
- { /* Get char, post-int read pointer. */
- register int c;
-
- c = ch_get();
- if ((c != EOF) && (++ch_offset >= BUFSIZ)) {
- ch_offset = 0;
- ch_block ++;
- }
- return(c);
- }
-
- int ch_back_get() /*=================================================*/
- { /* Pre-dec read pointer, get char. */
- register int c;
-
- if (--ch_offset < 0) {
- if (ch_block <= 0) {
- ch_offset = 0;
- return (EOF);
- }
- ch_offset = BUFSIZ - 1;
- ch_block--;
- }
- c = ch_get();
- return(c);
- }
-
- void ch_init(want_nbufs) /*===========================================*/
- int want_nbufs; /* Init buffer pool to all empty. */
- {
- register struct buf *bp;
- char message[80];
-
- if (nbufs < want_nbufs) {
- /* We don't have enough buffers.
- Free what we have (if any) and allocate some new ones. */
- if (bufs) FreeMem((char *)bufs,sizeof(struct buf) * nbufs);
- bufs = (struct buf *)AllocMem(want_nbufs * sizeof(struct buf),MEMF_CLEAR);
- nbufs = want_nbufs;
- if (bufs == NULL) { /* Try for less buffers */
- sprintf(message,"Cannot allocate %d buffers. Using %d buffers.",nbufs,DEF_NBUFS);
- error(message,1);
-
- bufs = (struct buf *)AllocMem(DEF_NBUFS * sizeof(struct buf),MEMF_CLEAR);
- nbufs = DEF_NBUFS;
- if (bufs == NULL) { /* Dammit! No RAM for just small buffers */
- sprintf(message,"Cannot even allocate %d buffers! Quitting.\n",DEF_NBUFS);
- error(message,1);
- cleanup(NULL,50);
- }
- }
- }
-
- /* Initialize the buffers to empty. Set up the circular list. */
- for (bp = &bufs[0]; bp < &bufs[nbufs]; bp++) {
- bp->next = bp + 1;
- bp->prev = bp - 1;
- bp->block = -1L;
- }
- bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
- buf_head = &bufs[0];
- buf_tail = &bufs[nbufs-1];
- last_piped_block = -1;
- ch_fsize = NULL_POSITION;
- ch_seek(0);
- }
-
- void ch_memdump() /*==================================================*/
- { /* So cleanup() can catch memory to free, no check. */
- if (bufs) FreeMem((char *)bufs,sizeof(struct buf) * nbufs);
- }
-