ch.c revision 128345
160786Sps/* 2128345Stjr * Copyright (C) 1984-2002 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; 96128345Stjrstatic int maxbufs = -1; 9760786Sps 9860786Spsextern int autobuf; 9960786Spsextern int sigs; 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)) || 165128345Stjr (maxbufs < 0 || ch_nbufs < maxbufs)) 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/* 564128345Stjr * Set max amount of buffer space. 565128345Stjr * bufspace is in units of 1024 bytes. -1 mean no limit. 56660786Sps */ 567128345Stjr public void 568128345Stjrch_setbufspace(bufspace) 569128345Stjr int bufspace; 57060786Sps{ 571128345Stjr if (bufspace < 0) 572128345Stjr maxbufs = -1; 573128345Stjr else 57460786Sps { 575128345Stjr maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; 576128345Stjr if (maxbufs < 1) 577128345Stjr maxbufs = 1; 57860786Sps } 57960786Sps} 58060786Sps 58160786Sps/* 58260786Sps * Flush (discard) any saved file state, including buffer contents. 58360786Sps */ 58460786Sps public void 58560786Spsch_flush() 58660786Sps{ 58760786Sps register struct buf *bp; 58860786Sps 58960786Sps if (!(ch_flags & CH_CANSEEK)) 59060786Sps { 59160786Sps /* 59260786Sps * If input is a pipe, we don't flush buffer contents, 59360786Sps * since the contents can't be recovered. 59460786Sps */ 59560786Sps ch_fsize = NULL_POSITION; 59660786Sps return; 59760786Sps } 59860786Sps 59960786Sps /* 60060786Sps * Initialize all the buffers. 60160786Sps */ 60260786Sps for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next) 60389019Sps bp->block = -1; 60460786Sps 60560786Sps /* 60660786Sps * Figure out the size of the file, if we can. 60760786Sps */ 60860786Sps ch_fsize = filesize(ch_file); 60960786Sps 61060786Sps /* 61160786Sps * Seek to a known position: the beginning of the file. 61260786Sps */ 61360786Sps ch_fpos = 0; 61460786Sps ch_block = 0; /* ch_fpos / LBUFSIZE; */ 61560786Sps ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 61660786Sps 61760786Sps#if 1 61860786Sps /* 61960786Sps * This is a kludge to workaround a Linux kernel bug: files in 62060786Sps * /proc have a size of 0 according to fstat() but have readable 62160786Sps * data. They are sometimes, but not always, seekable. 62260786Sps * Force them to be non-seekable here. 62360786Sps */ 62460786Sps if (ch_fsize == 0) 62560786Sps { 62660786Sps ch_fsize = NULL_POSITION; 62760786Sps ch_flags &= ~CH_CANSEEK; 62860786Sps } 62960786Sps#endif 63060786Sps 63160786Sps if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK) 63260786Sps { 63360786Sps /* 63460786Sps * Warning only; even if the seek fails for some reason, 63560786Sps * there's a good chance we're at the beginning anyway. 63660786Sps * {{ I think this is bogus reasoning. }} 63760786Sps */ 63860786Sps error("seek error to 0", NULL_PARG); 63960786Sps } 64060786Sps} 64160786Sps 64260786Sps/* 64360786Sps * Allocate a new buffer. 64460786Sps * The buffer is added to the tail of the buffer chain. 64560786Sps */ 64660786Sps static int 64760786Spsch_addbuf() 64860786Sps{ 64960786Sps register struct buf *bp; 65060786Sps 65160786Sps /* 65260786Sps * Allocate and initialize a new buffer and link it 65360786Sps * onto the tail of the buffer list. 65460786Sps */ 65560786Sps bp = (struct buf *) calloc(1, sizeof(struct buf)); 65660786Sps if (bp == NULL) 65760786Sps return (1); 65860786Sps ch_nbufs++; 65989019Sps bp->block = -1; 66060786Sps bp->next = END_OF_CHAIN; 66160786Sps bp->prev = ch_buftail; 66260786Sps ch_buftail->next = bp; 66360786Sps ch_buftail = bp; 66489019Sps HASH_INS(bp, 0); 66560786Sps return (0); 66660786Sps} 66760786Sps 66860786Sps/* 66989019Sps * 67089019Sps */ 67189019Sps static void 67289019Spsinit_hashtbl() 67389019Sps{ 67489019Sps register int h; 67589019Sps 67689019Sps for (h = 0; h < BUFHASH_SIZE; h++) 67789019Sps { 67889019Sps thisfile->hashtbl[h].buf_hnext = END_OF_HCHAIN(h); 67989019Sps thisfile->hashtbl[h].buf_hprev = END_OF_HCHAIN(h); 68089019Sps } 68189019Sps} 68289019Sps 68389019Sps/* 68460786Sps * Delete all buffers for this file. 68560786Sps */ 68660786Sps static void 68760786Spsch_delbufs() 68860786Sps{ 68960786Sps register struct buf *bp; 69060786Sps 69160786Sps while (ch_bufhead != END_OF_CHAIN) 69260786Sps { 69360786Sps bp = ch_bufhead; 69460786Sps bp->next->prev = bp->prev;; 69560786Sps bp->prev->next = bp->next; 69660786Sps free(bp); 69760786Sps } 69860786Sps ch_nbufs = 0; 69989019Sps init_hashtbl(); 70060786Sps} 70160786Sps 70260786Sps/* 70360786Sps * Is it possible to seek on a file descriptor? 70460786Sps */ 70560786Sps public int 70660786Spsseekable(f) 70760786Sps int f; 70860786Sps{ 70960786Sps#if MSDOS_COMPILER 71060786Sps extern int fd0; 71160786Sps if (f == fd0 && !isatty(fd0)) 71260786Sps { 71360786Sps /* 71460786Sps * In MS-DOS, pipes are seekable. Check for 71560786Sps * standard input, and pretend it is not seekable. 71660786Sps */ 71760786Sps return (0); 71860786Sps } 71960786Sps#endif 72060786Sps return (lseek(f, (off_t)1, 0) != BAD_LSEEK); 72160786Sps} 72260786Sps 72360786Sps/* 72460786Sps * Initialize file state for a new file. 72560786Sps */ 72660786Sps public void 72760786Spsch_init(f, flags) 72860786Sps int f; 72960786Sps int flags; 73060786Sps{ 73160786Sps /* 73260786Sps * See if we already have a filestate for this file. 73360786Sps */ 73460786Sps thisfile = (struct filestate *) get_filestate(curr_ifile); 73560786Sps if (thisfile == NULL) 73660786Sps { 73760786Sps /* 73860786Sps * Allocate and initialize a new filestate. 73960786Sps */ 74060786Sps thisfile = (struct filestate *) 74160786Sps calloc(1, sizeof(struct filestate)); 74260786Sps thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN; 74360786Sps thisfile->nbufs = 0; 74460786Sps thisfile->flags = 0; 74560786Sps thisfile->fpos = 0; 74660786Sps thisfile->block = 0; 74760786Sps thisfile->offset = 0; 74860786Sps thisfile->file = -1; 74960786Sps thisfile->fsize = NULL_POSITION; 75060786Sps ch_flags = flags; 75189019Sps init_hashtbl(); 75260786Sps /* 75360786Sps * Try to seek; set CH_CANSEEK if it works. 75460786Sps */ 75560786Sps if ((flags & CH_CANSEEK) && !seekable(f)) 75660786Sps ch_flags &= ~CH_CANSEEK; 75760786Sps set_filestate(curr_ifile, (void *) thisfile); 75860786Sps } 75960786Sps if (thisfile->file == -1) 76060786Sps thisfile->file = f; 76160786Sps ch_flush(); 76260786Sps} 76360786Sps 76460786Sps/* 76560786Sps * Close a filestate. 76660786Sps */ 76760786Sps public void 76860786Spsch_close() 76960786Sps{ 77060786Sps int keepstate = FALSE; 77160786Sps 77260786Sps if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) 77360786Sps { 77460786Sps /* 77560786Sps * We can seek or re-open, so we don't need to keep buffers. 77660786Sps */ 77760786Sps ch_delbufs(); 77860786Sps } else 77960786Sps keepstate = TRUE; 78060786Sps if (!(ch_flags & CH_KEEPOPEN)) 78160786Sps { 78260786Sps /* 78360786Sps * We don't need to keep the file descriptor open 78460786Sps * (because we can re-open it.) 78560786Sps * But don't really close it if it was opened via popen(), 78660786Sps * because pclose() wants to close it. 78760786Sps */ 78860786Sps if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 78960786Sps close(ch_file); 79060786Sps ch_file = -1; 79160786Sps } else 79260786Sps keepstate = TRUE; 79360786Sps if (!keepstate) 79460786Sps { 79560786Sps /* 79660786Sps * We don't even need to keep the filestate structure. 79760786Sps */ 79860786Sps free(thisfile); 79960786Sps thisfile = NULL; 80060786Sps set_filestate(curr_ifile, (void *) NULL); 80160786Sps } 80260786Sps} 80360786Sps 80460786Sps/* 80560786Sps * Return ch_flags for the current file. 80660786Sps */ 80760786Sps public int 80860786Spsch_getflags() 80960786Sps{ 81060786Sps return (ch_flags); 81160786Sps} 81260786Sps 81360786Sps#if 0 81460786Sps public void 81560786Spsch_dump(struct filestate *fs) 81660786Sps{ 81760786Sps struct buf *bp; 81860786Sps unsigned char *s; 81960786Sps 82060786Sps if (fs == NULL) 82160786Sps { 82260786Sps printf(" --no filestate\n"); 82360786Sps return; 82460786Sps } 82560786Sps printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 82660786Sps fs->file, fs->flags, fs->fpos, 82760786Sps fs->fsize, fs->block, fs->offset); 82860786Sps printf(" %d bufs:\n", fs->nbufs); 82960786Sps for (bp = fs->buf_next; bp != (struct buf *)fs; bp = bp->next) 83060786Sps { 83160786Sps printf("%x: blk %x, size %x \"", 83260786Sps bp, bp->block, bp->datasize); 83360786Sps for (s = bp->data; s < bp->data + 30; s++) 83460786Sps if (*s >= ' ' && *s < 0x7F) 83560786Sps printf("%c", *s); 83660786Sps else 83760786Sps printf("."); 83860786Sps printf("\"\n"); 83960786Sps } 84060786Sps} 84160786Sps#endif 842