ch.c revision 89019
160786Sps/* 260786Sps * Copyright (C) 1984-2000 Mark Nudelman 360786Sps * 460786Sps * You may distribute under the terms of either the GNU General Public 560786Sps * License or the Less License, as specified in the README file. 660786Sps * 760786Sps * For more information about less, or for information on how to 860786Sps * contact the author, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps/* 1360786Sps * Low level character input from the input file. 1460786Sps * We use these special purpose routines which optimize moving 1560786Sps * both forward and backward from the current read pointer. 1660786Sps */ 1760786Sps 1860786Sps#include "less.h" 1960786Sps#if MSDOS_COMPILER==WIN32C 2060786Sps#include <errno.h> 2160786Sps#include <windows.h> 2260786Sps#endif 2360786Sps 2489019Spstypedef POSITION BLOCKNUM; 2589019Sps 2660786Spspublic int ignore_eoi; 2760786Sps 2860786Sps/* 2960786Sps * Pool of buffers holding the most recently used blocks of the input file. 3060786Sps * The buffer pool is kept as a doubly-linked circular list, 3160786Sps * in order from most- to least-recently used. 3260786Sps * The circular list is anchored by the file state "thisfile". 3360786Sps */ 3489019Sps#define LBUFSIZE 8192 3560786Spsstruct buf { 3689019Sps struct buf *next, *prev; 3789019Sps struct buf *hnext, *hprev; 3889019Sps BLOCKNUM block; 3960786Sps unsigned int datasize; 4060786Sps unsigned char data[LBUFSIZE]; 4160786Sps}; 4260786Sps 4389019Spsstruct buflist { 4489019Sps /* -- Following members must match struct buf */ 4589019Sps struct buf *buf_next, *buf_prev; 4689019Sps struct buf *buf_hnext, *buf_hprev; 4789019Sps}; 4889019Sps 4960786Sps/* 5060786Sps * The file state is maintained in a filestate structure. 5160786Sps * A pointer to the filestate is kept in the ifile structure. 5260786Sps */ 5389019Sps#define BUFHASH_SIZE 64 5460786Spsstruct filestate { 5560786Sps struct buf *buf_next, *buf_prev; 5689019Sps struct buflist hashtbl[BUFHASH_SIZE]; 5760786Sps int file; 5860786Sps int flags; 5960786Sps POSITION fpos; 6060786Sps int nbufs; 6189019Sps BLOCKNUM block; 6260786Sps unsigned int offset; 6360786Sps POSITION fsize; 6460786Sps}; 6560786Sps 6660786Sps#define ch_bufhead thisfile->buf_next 6760786Sps#define ch_buftail thisfile->buf_prev 6860786Sps#define ch_nbufs thisfile->nbufs 6960786Sps#define ch_block thisfile->block 7060786Sps#define ch_offset thisfile->offset 7160786Sps#define ch_fpos thisfile->fpos 7260786Sps#define ch_fsize thisfile->fsize 7360786Sps#define ch_flags thisfile->flags 7460786Sps#define ch_file thisfile->file 7560786Sps 7689019Sps#define END_OF_CHAIN ((struct buf *)&thisfile->buf_next) 7789019Sps#define END_OF_HCHAIN(h) ((struct buf *)&thisfile->hashtbl[h]) 7889019Sps#define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) 7989019Sps 8089019Sps#define FOR_BUFS_IN_CHAIN(h,bp) \ 8189019Sps for (bp = thisfile->hashtbl[h].buf_hnext; \ 8289019Sps bp != END_OF_HCHAIN(h); bp = bp->hnext) 8389019Sps 8489019Sps#define HASH_RM(bp) \ 8589019Sps (bp)->hnext->hprev = (bp)->hprev; \ 8689019Sps (bp)->hprev->hnext = (bp)->hnext; 8789019Sps 8889019Sps#define HASH_INS(bp,h) \ 8989019Sps (bp)->hnext = thisfile->hashtbl[h].buf_hnext; \ 9089019Sps (bp)->hprev = END_OF_HCHAIN(h); \ 9189019Sps thisfile->hashtbl[h].buf_hnext->hprev = (bp); \ 9289019Sps thisfile->hashtbl[h].buf_hnext = (bp); 9389019Sps 9460786Spsstatic struct filestate *thisfile; 9560786Spsstatic int ch_ungotchar = -1; 9660786Sps 9760786Spsextern int autobuf; 9860786Spsextern int sigs; 9960786Spsextern int cbufs; 10060786Spsextern int secure; 10160786Spsextern constant char helpdata[]; 10260786Spsextern constant int size_helpdata; 10360786Spsextern IFILE curr_ifile; 10460786Sps#if LOGFILE 10560786Spsextern int logfile; 10660786Spsextern char *namelogfile; 10760786Sps#endif 10860786Sps 10960786Spsstatic int ch_addbuf(); 11060786Sps 11160786Sps 11260786Sps/* 11360786Sps * Get the character pointed to by the read pointer. 11460786Sps * ch_get() is a macro which is more efficient to call 11560786Sps * than fch_get (the function), in the usual case 11660786Sps * that the block desired is at the head of the chain. 11760786Sps */ 11860786Sps#define ch_get() ((ch_block == ch_bufhead->block && \ 11960786Sps ch_offset < ch_bufhead->datasize) ? \ 12060786Sps ch_bufhead->data[ch_offset] : fch_get()) 12160786Sps int 12260786Spsfch_get() 12360786Sps{ 12460786Sps register struct buf *bp; 12560786Sps register int n; 12660786Sps register int slept; 12789019Sps register int h; 12860786Sps POSITION pos; 12960786Sps POSITION len; 13060786Sps 13160786Sps slept = FALSE; 13260786Sps 13360786Sps /* 13460786Sps * Look for a buffer holding the desired block. 13560786Sps */ 13689019Sps h = BUFHASH(ch_block); 13789019Sps FOR_BUFS_IN_CHAIN(h, bp) 13889019Sps { 13960786Sps if (bp->block == ch_block) 14060786Sps { 14160786Sps if (ch_offset >= bp->datasize) 14260786Sps /* 14360786Sps * Need more data in this buffer. 14460786Sps */ 14560786Sps goto read_more; 14660786Sps goto found; 14760786Sps } 14889019Sps } 14960786Sps /* 15060786Sps * Block is not in a buffer. 15160786Sps * Take the least recently used buffer 15260786Sps * and read the desired block into it. 15360786Sps * If the LRU buffer has data in it, 15460786Sps * then maybe allocate a new buffer. 15560786Sps */ 15689019Sps if (ch_buftail == END_OF_CHAIN || ch_buftail->block != -1) 15760786Sps { 15860786Sps /* 15960786Sps * There is no empty buffer to use. 16060786Sps * Allocate a new buffer if: 16160786Sps * 1. We can't seek on this file and -b is not in effect; or 16260786Sps * 2. We haven't allocated the max buffers for this file yet. 16360786Sps */ 16460786Sps if ((autobuf && !(ch_flags & CH_CANSEEK)) || 16560786Sps (cbufs == -1 || ch_nbufs < cbufs)) 16660786Sps if (ch_addbuf()) 16760786Sps /* 16860786Sps * Allocation failed: turn off autobuf. 16960786Sps */ 17060786Sps autobuf = OPT_OFF; 17160786Sps } 17260786Sps bp = ch_buftail; 17389019Sps HASH_RM(bp); /* Remove from old hash chain. */ 17460786Sps bp->block = ch_block; 17560786Sps bp->datasize = 0; 17689019Sps HASH_INS(bp, h); /* Insert into new hash chain. */ 17760786Sps 17860786Sps read_more: 17960786Sps pos = (ch_block * LBUFSIZE) + bp->datasize; 18060786Sps if ((len = ch_length()) != NULL_POSITION && pos >= len) 18160786Sps /* 18260786Sps * At end of file. 18360786Sps */ 18460786Sps return (EOI); 18560786Sps 18660786Sps if (pos != ch_fpos) 18760786Sps { 18860786Sps /* 18960786Sps * Not at the correct position: must seek. 19060786Sps * If input is a pipe, we're in trouble (can't seek on a pipe). 19160786Sps * Some data has been lost: just return "?". 19260786Sps */ 19360786Sps if (!(ch_flags & CH_CANSEEK)) 19460786Sps return ('?'); 19560786Sps if (lseek(ch_file, (off_t)pos, 0) == BAD_LSEEK) 19660786Sps { 19760786Sps error("seek error", NULL_PARG); 19860786Sps clear_eol(); 19960786Sps return (EOI); 20060786Sps } 20160786Sps ch_fpos = pos; 20260786Sps } 20360786Sps 20460786Sps /* 20560786Sps * Read the block. 20660786Sps * If we read less than a full block, that's ok. 20760786Sps * We use partial block and pick up the rest next time. 20860786Sps */ 20960786Sps if (ch_ungotchar != -1) 21060786Sps { 21160786Sps bp->data[bp->datasize] = ch_ungotchar; 21260786Sps n = 1; 21360786Sps ch_ungotchar = -1; 21460786Sps } else if (ch_flags & CH_HELPFILE) 21560786Sps { 21660786Sps bp->data[bp->datasize] = helpdata[ch_fpos]; 21760786Sps n = 1; 21860786Sps } else 21960786Sps { 22060786Sps n = iread(ch_file, &bp->data[bp->datasize], 22160786Sps (unsigned int)(LBUFSIZE - bp->datasize)); 22260786Sps } 22360786Sps 22460786Sps if (n == READ_INTR) 22560786Sps return (EOI); 22660786Sps if (n < 0) 22760786Sps { 22860786Sps#if MSDOS_COMPILER==WIN32C 22960786Sps if (errno != EPIPE) 23060786Sps#endif 23160786Sps { 23260786Sps error("read error", NULL_PARG); 23360786Sps clear_eol(); 23460786Sps } 23560786Sps n = 0; 23660786Sps } 23760786Sps 23860786Sps#if LOGFILE 23960786Sps /* 24060786Sps * If we have a log file, write the new data to it. 24160786Sps */ 24260786Sps if (!secure && logfile >= 0 && n > 0) 24360786Sps write(logfile, (char *) &bp->data[bp->datasize], n); 24460786Sps#endif 24560786Sps 24660786Sps ch_fpos += n; 24760786Sps bp->datasize += n; 24860786Sps 24960786Sps /* 25060786Sps * If we have read to end of file, set ch_fsize to indicate 25160786Sps * the position of the end of file. 25260786Sps */ 25360786Sps if (n == 0) 25460786Sps { 25560786Sps ch_fsize = pos; 25660786Sps if (ignore_eoi) 25760786Sps { 25860786Sps /* 25960786Sps * We are ignoring EOF. 26060786Sps * Wait a while, then try again. 26160786Sps */ 26260786Sps if (!slept) 26389019Sps { 26489019Sps PARG parg; 26589019Sps parg.p_string = wait_message(); 26689019Sps ierror("%s", &parg); 26789019Sps } 26860786Sps#if !MSDOS_COMPILER 26960786Sps sleep(1); 27060786Sps#else 27160786Sps#if MSDOS_COMPILER==WIN32C 27260786Sps Sleep(1000); 27360786Sps#endif 27460786Sps#endif 27560786Sps slept = TRUE; 27660786Sps } 27760786Sps if (sigs) 27860786Sps return (EOI); 27960786Sps } 28060786Sps 28160786Sps found: 28260786Sps if (ch_bufhead != bp) 28360786Sps { 28460786Sps /* 28560786Sps * Move the buffer to the head of the buffer chain. 28660786Sps * This orders the buffer chain, most- to least-recently used. 28760786Sps */ 28860786Sps bp->next->prev = bp->prev; 28960786Sps bp->prev->next = bp->next; 29060786Sps bp->next = ch_bufhead; 29160786Sps bp->prev = END_OF_CHAIN; 29260786Sps ch_bufhead->prev = bp; 29360786Sps ch_bufhead = bp; 29489019Sps 29589019Sps /* 29689019Sps * Move to head of hash chain too. 29789019Sps */ 29889019Sps HASH_RM(bp); 29989019Sps HASH_INS(bp, h); 30060786Sps } 30160786Sps 30260786Sps if (ch_offset >= bp->datasize) 30360786Sps /* 30460786Sps * After all that, we still don't have enough data. 30560786Sps * Go back and try again. 30660786Sps */ 30760786Sps goto read_more; 30860786Sps 30960786Sps return (bp->data[ch_offset]); 31060786Sps} 31160786Sps 31260786Sps/* 31360786Sps * ch_ungetchar is a rather kludgy and limited way to push 31460786Sps * a single char onto an input file descriptor. 31560786Sps */ 31660786Sps public void 31760786Spsch_ungetchar(c) 31860786Sps int c; 31960786Sps{ 32060786Sps if (c != -1 && ch_ungotchar != -1) 32160786Sps error("ch_ungetchar overrun", NULL_PARG); 32260786Sps ch_ungotchar = c; 32360786Sps} 32460786Sps 32560786Sps#if LOGFILE 32660786Sps/* 32760786Sps * Close the logfile. 32860786Sps * If we haven't read all of standard input into it, do that now. 32960786Sps */ 33060786Sps public void 33160786Spsend_logfile() 33260786Sps{ 33360786Sps static int tried = FALSE; 33460786Sps 33560786Sps if (logfile < 0) 33660786Sps return; 33760786Sps if (!tried && ch_fsize == NULL_POSITION) 33860786Sps { 33960786Sps tried = TRUE; 34060786Sps ierror("Finishing logfile", NULL_PARG); 34160786Sps while (ch_forw_get() != EOI) 34260786Sps if (ABORT_SIGS()) 34360786Sps break; 34460786Sps } 34560786Sps close(logfile); 34660786Sps logfile = -1; 34760786Sps namelogfile = NULL; 34860786Sps} 34960786Sps 35060786Sps/* 35160786Sps * Start a log file AFTER less has already been running. 35260786Sps * Invoked from the - command; see toggle_option(). 35360786Sps * Write all the existing buffered data to the log file. 35460786Sps */ 35560786Sps public void 35660786Spssync_logfile() 35760786Sps{ 35860786Sps register struct buf *bp; 35960786Sps int warned = FALSE; 36089019Sps BLOCKNUM block; 36189019Sps BLOCKNUM nblocks; 36260786Sps 36360786Sps nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; 36460786Sps for (block = 0; block < nblocks; block++) 36560786Sps { 36660786Sps for (bp = ch_bufhead; ; bp = bp->next) 36760786Sps { 36860786Sps if (bp == END_OF_CHAIN) 36960786Sps { 37060786Sps if (!warned) 37160786Sps { 37260786Sps error("Warning: log file is incomplete", 37360786Sps NULL_PARG); 37460786Sps warned = TRUE; 37560786Sps } 37660786Sps break; 37760786Sps } 37860786Sps if (bp->block == block) 37960786Sps { 38060786Sps write(logfile, (char *) bp->data, bp->datasize); 38160786Sps break; 38260786Sps } 38360786Sps } 38460786Sps } 38560786Sps} 38660786Sps 38760786Sps#endif 38860786Sps 38960786Sps/* 39060786Sps * Determine if a specific block is currently in one of the buffers. 39160786Sps */ 39260786Sps static int 39360786Spsbuffered(block) 39489019Sps BLOCKNUM block; 39560786Sps{ 39660786Sps register struct buf *bp; 39789019Sps register int h; 39860786Sps 39989019Sps h = BUFHASH(block); 40089019Sps FOR_BUFS_IN_CHAIN(h, bp) 40189019Sps { 40260786Sps if (bp->block == block) 40360786Sps return (TRUE); 40489019Sps } 40560786Sps return (FALSE); 40660786Sps} 40760786Sps 40860786Sps/* 40960786Sps * Seek to a specified position in the file. 41060786Sps * Return 0 if successful, non-zero if can't seek there. 41160786Sps */ 41260786Sps public int 41360786Spsch_seek(pos) 41460786Sps register POSITION pos; 41560786Sps{ 41689019Sps BLOCKNUM new_block; 41760786Sps POSITION len; 41860786Sps 41960786Sps len = ch_length(); 42060786Sps if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) 42160786Sps return (1); 42260786Sps 42360786Sps new_block = pos / LBUFSIZE; 42460786Sps if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) 42560786Sps { 42660786Sps if (ch_fpos > pos) 42760786Sps return (1); 42860786Sps while (ch_fpos < pos) 42960786Sps { 43060786Sps if (ch_forw_get() == EOI) 43160786Sps return (1); 43260786Sps if (ABORT_SIGS()) 43360786Sps return (1); 43460786Sps } 43560786Sps return (0); 43660786Sps } 43760786Sps /* 43860786Sps * Set read pointer. 43960786Sps */ 44060786Sps ch_block = new_block; 44160786Sps ch_offset = pos % LBUFSIZE; 44260786Sps return (0); 44360786Sps} 44460786Sps 44560786Sps/* 44660786Sps * Seek to the end of the file. 44760786Sps */ 44860786Sps public int 44960786Spsch_end_seek() 45060786Sps{ 45160786Sps POSITION len; 45260786Sps 45360786Sps if (ch_flags & CH_CANSEEK) 45460786Sps ch_fsize = filesize(ch_file); 45560786Sps 45660786Sps len = ch_length(); 45760786Sps if (len != NULL_POSITION) 45860786Sps return (ch_seek(len)); 45960786Sps 46060786Sps /* 46160786Sps * Do it the slow way: read till end of data. 46260786Sps */ 46360786Sps while (ch_forw_get() != EOI) 46460786Sps if (ABORT_SIGS()) 46560786Sps return (1); 46660786Sps return (0); 46760786Sps} 46860786Sps 46960786Sps/* 47060786Sps * Seek to the beginning of the file, or as close to it as we can get. 47160786Sps * We may not be able to seek there if input is a pipe and the 47260786Sps * beginning of the pipe is no longer buffered. 47360786Sps */ 47460786Sps public int 47560786Spsch_beg_seek() 47660786Sps{ 47760786Sps register struct buf *bp, *firstbp; 47860786Sps 47960786Sps /* 48060786Sps * Try a plain ch_seek first. 48160786Sps */ 48260786Sps if (ch_seek(ch_zero()) == 0) 48360786Sps return (0); 48460786Sps 48560786Sps /* 48660786Sps * Can't get to position 0. 48760786Sps * Look thru the buffers for the one closest to position 0. 48860786Sps */ 48960786Sps firstbp = bp = ch_bufhead; 49060786Sps if (bp == END_OF_CHAIN) 49160786Sps return (1); 49260786Sps while ((bp = bp->next) != END_OF_CHAIN) 49360786Sps if (bp->block < firstbp->block) 49460786Sps firstbp = bp; 49560786Sps ch_block = firstbp->block; 49660786Sps ch_offset = 0; 49760786Sps return (0); 49860786Sps} 49960786Sps 50060786Sps/* 50160786Sps * Return the length of the file, if known. 50260786Sps */ 50360786Sps public POSITION 50460786Spsch_length() 50560786Sps{ 50660786Sps if (ignore_eoi) 50760786Sps return (NULL_POSITION); 50860786Sps if (ch_flags & CH_HELPFILE) 50960786Sps return (size_helpdata); 51060786Sps return (ch_fsize); 51160786Sps} 51260786Sps 51360786Sps/* 51460786Sps * Return the current position in the file. 51560786Sps */ 51660786Sps public POSITION 51760786Spsch_tell() 51860786Sps{ 51989019Sps return (ch_block * LBUFSIZE) + ch_offset; 52060786Sps} 52160786Sps 52260786Sps/* 52360786Sps * Get the current char and post-increment the read pointer. 52460786Sps */ 52560786Sps public int 52660786Spsch_forw_get() 52760786Sps{ 52860786Sps register int c; 52960786Sps 53060786Sps c = ch_get(); 53160786Sps if (c == EOI) 53260786Sps return (EOI); 53360786Sps if (ch_offset < LBUFSIZE-1) 53460786Sps ch_offset++; 53560786Sps else 53660786Sps { 53760786Sps ch_block ++; 53860786Sps ch_offset = 0; 53960786Sps } 54060786Sps return (c); 54160786Sps} 54260786Sps 54360786Sps/* 54460786Sps * Pre-decrement the read pointer and get the new current char. 54560786Sps */ 54660786Sps public int 54760786Spsch_back_get() 54860786Sps{ 54960786Sps if (ch_offset > 0) 55060786Sps ch_offset --; 55160786Sps else 55260786Sps { 55360786Sps if (ch_block <= 0) 55460786Sps return (EOI); 55560786Sps if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) 55660786Sps return (EOI); 55760786Sps ch_block--; 55860786Sps ch_offset = LBUFSIZE-1; 55960786Sps } 56060786Sps return (ch_get()); 56160786Sps} 56260786Sps 56360786Sps/* 56460786Sps * Allocate buffers. 56560786Sps * Caller wants us to have a total of at least want_nbufs buffers. 56660786Sps */ 56760786Sps public int 56860786Spsch_nbuf(want_nbufs) 56960786Sps int want_nbufs; 57060786Sps{ 57160786Sps PARG parg; 57260786Sps 57360786Sps while (ch_nbufs < want_nbufs) 57460786Sps { 57560786Sps if (ch_addbuf()) 57660786Sps { 57760786Sps /* 57860786Sps * Cannot allocate enough buffers. 57960786Sps * If we don't have ANY, then quit. 58060786Sps * Otherwise, just report the error and return. 58160786Sps */ 58260786Sps parg.p_int = want_nbufs - ch_nbufs; 58360786Sps error("Cannot allocate %d buffers", &parg); 58460786Sps if (ch_nbufs == 0) 58560786Sps quit(QUIT_ERROR); 58660786Sps break; 58760786Sps } 58860786Sps } 58960786Sps return (ch_nbufs); 59060786Sps} 59160786Sps 59260786Sps/* 59360786Sps * Flush (discard) any saved file state, including buffer contents. 59460786Sps */ 59560786Sps public void 59660786Spsch_flush() 59760786Sps{ 59860786Sps register struct buf *bp; 59960786Sps 60060786Sps if (!(ch_flags & CH_CANSEEK)) 60160786Sps { 60260786Sps /* 60360786Sps * If input is a pipe, we don't flush buffer contents, 60460786Sps * since the contents can't be recovered. 60560786Sps */ 60660786Sps ch_fsize = NULL_POSITION; 60760786Sps return; 60860786Sps } 60960786Sps 61060786Sps /* 61160786Sps * Initialize all the buffers. 61260786Sps */ 61360786Sps for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next) 61489019Sps bp->block = -1; 61560786Sps 61660786Sps /* 61760786Sps * Figure out the size of the file, if we can. 61860786Sps */ 61960786Sps ch_fsize = filesize(ch_file); 62060786Sps 62160786Sps /* 62260786Sps * Seek to a known position: the beginning of the file. 62360786Sps */ 62460786Sps ch_fpos = 0; 62560786Sps ch_block = 0; /* ch_fpos / LBUFSIZE; */ 62660786Sps ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 62760786Sps 62860786Sps#if 1 62960786Sps /* 63060786Sps * This is a kludge to workaround a Linux kernel bug: files in 63160786Sps * /proc have a size of 0 according to fstat() but have readable 63260786Sps * data. They are sometimes, but not always, seekable. 63360786Sps * Force them to be non-seekable here. 63460786Sps */ 63560786Sps if (ch_fsize == 0) 63660786Sps { 63760786Sps ch_fsize = NULL_POSITION; 63860786Sps ch_flags &= ~CH_CANSEEK; 63960786Sps } 64060786Sps#endif 64160786Sps 64260786Sps if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK) 64360786Sps { 64460786Sps /* 64560786Sps * Warning only; even if the seek fails for some reason, 64660786Sps * there's a good chance we're at the beginning anyway. 64760786Sps * {{ I think this is bogus reasoning. }} 64860786Sps */ 64960786Sps error("seek error to 0", NULL_PARG); 65060786Sps } 65160786Sps} 65260786Sps 65360786Sps/* 65460786Sps * Allocate a new buffer. 65560786Sps * The buffer is added to the tail of the buffer chain. 65660786Sps */ 65760786Sps static int 65860786Spsch_addbuf() 65960786Sps{ 66060786Sps register struct buf *bp; 66160786Sps 66260786Sps /* 66360786Sps * Allocate and initialize a new buffer and link it 66460786Sps * onto the tail of the buffer list. 66560786Sps */ 66660786Sps bp = (struct buf *) calloc(1, sizeof(struct buf)); 66760786Sps if (bp == NULL) 66860786Sps return (1); 66960786Sps ch_nbufs++; 67089019Sps bp->block = -1; 67160786Sps bp->next = END_OF_CHAIN; 67260786Sps bp->prev = ch_buftail; 67360786Sps ch_buftail->next = bp; 67460786Sps ch_buftail = bp; 67589019Sps HASH_INS(bp, 0); 67660786Sps return (0); 67760786Sps} 67860786Sps 67960786Sps/* 68089019Sps * 68189019Sps */ 68289019Sps static void 68389019Spsinit_hashtbl() 68489019Sps{ 68589019Sps register int h; 68689019Sps 68789019Sps for (h = 0; h < BUFHASH_SIZE; h++) 68889019Sps { 68989019Sps thisfile->hashtbl[h].buf_hnext = END_OF_HCHAIN(h); 69089019Sps thisfile->hashtbl[h].buf_hprev = END_OF_HCHAIN(h); 69189019Sps } 69289019Sps} 69389019Sps 69489019Sps/* 69560786Sps * Delete all buffers for this file. 69660786Sps */ 69760786Sps static void 69860786Spsch_delbufs() 69960786Sps{ 70060786Sps register struct buf *bp; 70160786Sps 70260786Sps while (ch_bufhead != END_OF_CHAIN) 70360786Sps { 70460786Sps bp = ch_bufhead; 70560786Sps bp->next->prev = bp->prev;; 70660786Sps bp->prev->next = bp->next; 70760786Sps free(bp); 70860786Sps } 70960786Sps ch_nbufs = 0; 71089019Sps init_hashtbl(); 71160786Sps} 71260786Sps 71360786Sps/* 71460786Sps * Is it possible to seek on a file descriptor? 71560786Sps */ 71660786Sps public int 71760786Spsseekable(f) 71860786Sps int f; 71960786Sps{ 72060786Sps#if MSDOS_COMPILER 72160786Sps extern int fd0; 72260786Sps if (f == fd0 && !isatty(fd0)) 72360786Sps { 72460786Sps /* 72560786Sps * In MS-DOS, pipes are seekable. Check for 72660786Sps * standard input, and pretend it is not seekable. 72760786Sps */ 72860786Sps return (0); 72960786Sps } 73060786Sps#endif 73160786Sps return (lseek(f, (off_t)1, 0) != BAD_LSEEK); 73260786Sps} 73360786Sps 73460786Sps/* 73560786Sps * Initialize file state for a new file. 73660786Sps */ 73760786Sps public void 73860786Spsch_init(f, flags) 73960786Sps int f; 74060786Sps int flags; 74160786Sps{ 74260786Sps /* 74360786Sps * See if we already have a filestate for this file. 74460786Sps */ 74560786Sps thisfile = (struct filestate *) get_filestate(curr_ifile); 74660786Sps if (thisfile == NULL) 74760786Sps { 74860786Sps /* 74960786Sps * Allocate and initialize a new filestate. 75060786Sps */ 75160786Sps thisfile = (struct filestate *) 75260786Sps calloc(1, sizeof(struct filestate)); 75360786Sps thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN; 75460786Sps thisfile->nbufs = 0; 75560786Sps thisfile->flags = 0; 75660786Sps thisfile->fpos = 0; 75760786Sps thisfile->block = 0; 75860786Sps thisfile->offset = 0; 75960786Sps thisfile->file = -1; 76060786Sps thisfile->fsize = NULL_POSITION; 76160786Sps ch_flags = flags; 76289019Sps init_hashtbl(); 76360786Sps /* 76460786Sps * Try to seek; set CH_CANSEEK if it works. 76560786Sps */ 76660786Sps if ((flags & CH_CANSEEK) && !seekable(f)) 76760786Sps ch_flags &= ~CH_CANSEEK; 76860786Sps set_filestate(curr_ifile, (void *) thisfile); 76960786Sps } 77060786Sps if (thisfile->file == -1) 77160786Sps thisfile->file = f; 77260786Sps ch_flush(); 77360786Sps} 77460786Sps 77560786Sps/* 77660786Sps * Close a filestate. 77760786Sps */ 77860786Sps public void 77960786Spsch_close() 78060786Sps{ 78160786Sps int keepstate = FALSE; 78260786Sps 78360786Sps if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) 78460786Sps { 78560786Sps /* 78660786Sps * We can seek or re-open, so we don't need to keep buffers. 78760786Sps */ 78860786Sps ch_delbufs(); 78960786Sps } else 79060786Sps keepstate = TRUE; 79160786Sps if (!(ch_flags & CH_KEEPOPEN)) 79260786Sps { 79360786Sps /* 79460786Sps * We don't need to keep the file descriptor open 79560786Sps * (because we can re-open it.) 79660786Sps * But don't really close it if it was opened via popen(), 79760786Sps * because pclose() wants to close it. 79860786Sps */ 79960786Sps if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 80060786Sps close(ch_file); 80160786Sps ch_file = -1; 80260786Sps } else 80360786Sps keepstate = TRUE; 80460786Sps if (!keepstate) 80560786Sps { 80660786Sps /* 80760786Sps * We don't even need to keep the filestate structure. 80860786Sps */ 80960786Sps free(thisfile); 81060786Sps thisfile = NULL; 81160786Sps set_filestate(curr_ifile, (void *) NULL); 81260786Sps } 81360786Sps} 81460786Sps 81560786Sps/* 81660786Sps * Return ch_flags for the current file. 81760786Sps */ 81860786Sps public int 81960786Spsch_getflags() 82060786Sps{ 82160786Sps return (ch_flags); 82260786Sps} 82360786Sps 82460786Sps#if 0 82560786Sps public void 82660786Spsch_dump(struct filestate *fs) 82760786Sps{ 82860786Sps struct buf *bp; 82960786Sps unsigned char *s; 83060786Sps 83160786Sps if (fs == NULL) 83260786Sps { 83360786Sps printf(" --no filestate\n"); 83460786Sps return; 83560786Sps } 83660786Sps printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 83760786Sps fs->file, fs->flags, fs->fpos, 83860786Sps fs->fsize, fs->block, fs->offset); 83960786Sps printf(" %d bufs:\n", fs->nbufs); 84060786Sps for (bp = fs->buf_next; bp != (struct buf *)fs; bp = bp->next) 84160786Sps { 84260786Sps printf("%x: blk %x, size %x \"", 84360786Sps bp, bp->block, bp->datasize); 84460786Sps for (s = bp->data; s < bp->data + 30; s++) 84560786Sps if (*s >= ' ' && *s < 0x7F) 84660786Sps printf("%c", *s); 84760786Sps else 84860786Sps printf("."); 84960786Sps printf("\"\n"); 85060786Sps } 85160786Sps} 85260786Sps#endif 853