160786Sps/* 2240121Sdelphij * Copyright (C) 1984-2012 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 * 7240121Sdelphij * For more information, see the README file. 860786Sps */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * Low level character input from the input file. 1360786Sps * We use these special purpose routines which optimize moving 1460786Sps * both forward and backward from the current read pointer. 1560786Sps */ 1660786Sps 1760786Sps#include "less.h" 1860786Sps#if MSDOS_COMPILER==WIN32C 1960786Sps#include <errno.h> 2060786Sps#include <windows.h> 2160786Sps#endif 2260786Sps 23173682Sdelphij#if HAVE_STAT_INO 24173682Sdelphij#include <sys/stat.h> 25173682Sdelphijextern dev_t curr_dev; 26173682Sdelphijextern ino_t curr_ino; 27173682Sdelphij#endif 28173682Sdelphij 2989019Spstypedef POSITION BLOCKNUM; 3089019Sps 3160786Spspublic int ignore_eoi; 3260786Sps 3360786Sps/* 3460786Sps * Pool of buffers holding the most recently used blocks of the input file. 3560786Sps * The buffer pool is kept as a doubly-linked circular list, 3660786Sps * in order from most- to least-recently used. 3760786Sps * The circular list is anchored by the file state "thisfile". 3860786Sps */ 39191930Sdelphijstruct bufnode { 40191930Sdelphij struct bufnode *next, *prev; 41191930Sdelphij struct bufnode *hnext, *hprev; 42191930Sdelphij}; 43191930Sdelphij 4489019Sps#define LBUFSIZE 8192 4560786Spsstruct buf { 46191930Sdelphij struct bufnode node; 4789019Sps BLOCKNUM block; 4860786Sps unsigned int datasize; 4960786Sps unsigned char data[LBUFSIZE]; 5060786Sps}; 51191930Sdelphij#define bufnode_buf(bn) ((struct buf *) bn) 5260786Sps 5360786Sps/* 5460786Sps * The file state is maintained in a filestate structure. 5560786Sps * A pointer to the filestate is kept in the ifile structure. 5660786Sps */ 5789019Sps#define BUFHASH_SIZE 64 5860786Spsstruct filestate { 59191930Sdelphij struct bufnode buflist; 60191930Sdelphij struct bufnode hashtbl[BUFHASH_SIZE]; 6160786Sps int file; 6260786Sps int flags; 6360786Sps POSITION fpos; 6460786Sps int nbufs; 6589019Sps BLOCKNUM block; 6660786Sps unsigned int offset; 6760786Sps POSITION fsize; 6860786Sps}; 6960786Sps 70191930Sdelphij#define ch_bufhead thisfile->buflist.next 71191930Sdelphij#define ch_buftail thisfile->buflist.prev 7260786Sps#define ch_nbufs thisfile->nbufs 7360786Sps#define ch_block thisfile->block 7460786Sps#define ch_offset thisfile->offset 7560786Sps#define ch_fpos thisfile->fpos 7660786Sps#define ch_fsize thisfile->fsize 7760786Sps#define ch_flags thisfile->flags 7860786Sps#define ch_file thisfile->file 7960786Sps 80191930Sdelphij#define END_OF_CHAIN (&thisfile->buflist) 81191930Sdelphij#define END_OF_HCHAIN(h) (&thisfile->hashtbl[h]) 8289019Sps#define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) 8389019Sps 84191930Sdelphij/* 85191930Sdelphij * Macros to manipulate the list of buffers in thisfile->buflist. 86191930Sdelphij */ 87191930Sdelphij#define FOR_BUFS(bn) \ 88191930Sdelphij for (bn = ch_bufhead; bn != END_OF_CHAIN; bn = bn->next) 8989019Sps 90191930Sdelphij#define BUF_RM(bn) \ 91191930Sdelphij (bn)->next->prev = (bn)->prev; \ 92191930Sdelphij (bn)->prev->next = (bn)->next; 9389019Sps 94191930Sdelphij#define BUF_INS_HEAD(bn) \ 95191930Sdelphij (bn)->next = ch_bufhead; \ 96191930Sdelphij (bn)->prev = END_OF_CHAIN; \ 97191930Sdelphij ch_bufhead->prev = (bn); \ 98191930Sdelphij ch_bufhead = (bn); 9989019Sps 100191930Sdelphij#define BUF_INS_TAIL(bn) \ 101191930Sdelphij (bn)->next = END_OF_CHAIN; \ 102191930Sdelphij (bn)->prev = ch_buftail; \ 103191930Sdelphij ch_buftail->next = (bn); \ 104191930Sdelphij ch_buftail = (bn); 105191930Sdelphij 106191930Sdelphij/* 107191930Sdelphij * Macros to manipulate the list of buffers in thisfile->hashtbl[n]. 108191930Sdelphij */ 109191930Sdelphij#define FOR_BUFS_IN_CHAIN(h,bn) \ 110191930Sdelphij for (bn = thisfile->hashtbl[h].hnext; \ 111191930Sdelphij bn != END_OF_HCHAIN(h); bn = bn->hnext) 112191930Sdelphij 113191930Sdelphij#define BUF_HASH_RM(bn) \ 114191930Sdelphij (bn)->hnext->hprev = (bn)->hprev; \ 115191930Sdelphij (bn)->hprev->hnext = (bn)->hnext; 116191930Sdelphij 117191930Sdelphij#define BUF_HASH_INS(bn,h) \ 118191930Sdelphij (bn)->hnext = thisfile->hashtbl[h].hnext; \ 119191930Sdelphij (bn)->hprev = END_OF_HCHAIN(h); \ 120191930Sdelphij thisfile->hashtbl[h].hnext->hprev = (bn); \ 121191930Sdelphij thisfile->hashtbl[h].hnext = (bn); 122191930Sdelphij 12360786Spsstatic struct filestate *thisfile; 12460786Spsstatic int ch_ungotchar = -1; 125128345Stjrstatic int maxbufs = -1; 12660786Sps 12760786Spsextern int autobuf; 12860786Spsextern int sigs; 12960786Spsextern int secure; 130173682Sdelphijextern int screen_trashed; 131173682Sdelphijextern int follow_mode; 13260786Spsextern constant char helpdata[]; 13360786Spsextern constant int size_helpdata; 13460786Spsextern IFILE curr_ifile; 13560786Sps#if LOGFILE 13660786Spsextern int logfile; 13760786Spsextern char *namelogfile; 13860786Sps#endif 13960786Sps 14060786Spsstatic int ch_addbuf(); 14160786Sps 14260786Sps 14360786Sps/* 14460786Sps * Get the character pointed to by the read pointer. 14560786Sps */ 14660786Sps int 147191930Sdelphijch_get() 14860786Sps{ 14960786Sps register struct buf *bp; 150191930Sdelphij register struct bufnode *bn; 15160786Sps register int n; 15260786Sps register int slept; 15389019Sps register int h; 15460786Sps POSITION pos; 15560786Sps POSITION len; 15660786Sps 157172468Sdelphij if (thisfile == NULL) 158172468Sdelphij return (EOI); 159172468Sdelphij 160191930Sdelphij /* 161191930Sdelphij * Quick check for the common case where 162191930Sdelphij * the desired char is in the head buffer. 163191930Sdelphij */ 164191930Sdelphij if (ch_bufhead != END_OF_CHAIN) 165191930Sdelphij { 166191930Sdelphij bp = bufnode_buf(ch_bufhead); 167191930Sdelphij if (ch_block == bp->block && ch_offset < bp->datasize) 168191930Sdelphij return bp->data[ch_offset]; 169191930Sdelphij } 170191930Sdelphij 17160786Sps slept = FALSE; 17260786Sps 17360786Sps /* 17460786Sps * Look for a buffer holding the desired block. 17560786Sps */ 17689019Sps h = BUFHASH(ch_block); 177191930Sdelphij FOR_BUFS_IN_CHAIN(h, bn) 17889019Sps { 179191930Sdelphij bp = bufnode_buf(bn); 18060786Sps if (bp->block == ch_block) 18160786Sps { 18260786Sps if (ch_offset >= bp->datasize) 18360786Sps /* 18460786Sps * Need more data in this buffer. 18560786Sps */ 186191930Sdelphij break; 18760786Sps goto found; 18860786Sps } 18989019Sps } 190191930Sdelphij if (bn == END_OF_HCHAIN(h)) 19160786Sps { 19260786Sps /* 193191930Sdelphij * Block is not in a buffer. 194191930Sdelphij * Take the least recently used buffer 195191930Sdelphij * and read the desired block into it. 196191930Sdelphij * If the LRU buffer has data in it, 197191930Sdelphij * then maybe allocate a new buffer. 19860786Sps */ 199191930Sdelphij if (ch_buftail == END_OF_CHAIN || 200191930Sdelphij bufnode_buf(ch_buftail)->block != -1) 201191930Sdelphij { 202191930Sdelphij /* 203191930Sdelphij * There is no empty buffer to use. 204191930Sdelphij * Allocate a new buffer if: 205191930Sdelphij * 1. We can't seek on this file and -b is not in effect; or 206191930Sdelphij * 2. We haven't allocated the max buffers for this file yet. 207191930Sdelphij */ 208191930Sdelphij if ((autobuf && !(ch_flags & CH_CANSEEK)) || 209191930Sdelphij (maxbufs < 0 || ch_nbufs < maxbufs)) 210191930Sdelphij if (ch_addbuf()) 211191930Sdelphij /* 212191930Sdelphij * Allocation failed: turn off autobuf. 213191930Sdelphij */ 214191930Sdelphij autobuf = OPT_OFF; 215191930Sdelphij } 216191930Sdelphij bn = ch_buftail; 217191930Sdelphij bp = bufnode_buf(bn); 218191930Sdelphij BUF_HASH_RM(bn); /* Remove from old hash chain. */ 219191930Sdelphij bp->block = ch_block; 220191930Sdelphij bp->datasize = 0; 221191930Sdelphij BUF_HASH_INS(bn, h); /* Insert into new hash chain. */ 22260786Sps } 22360786Sps 22460786Sps read_more: 22560786Sps pos = (ch_block * LBUFSIZE) + bp->datasize; 22660786Sps if ((len = ch_length()) != NULL_POSITION && pos >= len) 22760786Sps /* 22860786Sps * At end of file. 22960786Sps */ 23060786Sps return (EOI); 23160786Sps 23260786Sps if (pos != ch_fpos) 23360786Sps { 23460786Sps /* 23560786Sps * Not at the correct position: must seek. 23660786Sps * If input is a pipe, we're in trouble (can't seek on a pipe). 23760786Sps * Some data has been lost: just return "?". 23860786Sps */ 23960786Sps if (!(ch_flags & CH_CANSEEK)) 24060786Sps return ('?'); 241173682Sdelphij if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK) 24260786Sps { 24360786Sps error("seek error", NULL_PARG); 24460786Sps clear_eol(); 24560786Sps return (EOI); 24660786Sps } 24760786Sps ch_fpos = pos; 24860786Sps } 24960786Sps 25060786Sps /* 25160786Sps * Read the block. 25260786Sps * If we read less than a full block, that's ok. 25360786Sps * We use partial block and pick up the rest next time. 25460786Sps */ 25560786Sps if (ch_ungotchar != -1) 25660786Sps { 25760786Sps bp->data[bp->datasize] = ch_ungotchar; 25860786Sps n = 1; 25960786Sps ch_ungotchar = -1; 26060786Sps } else if (ch_flags & CH_HELPFILE) 26160786Sps { 26260786Sps bp->data[bp->datasize] = helpdata[ch_fpos]; 26360786Sps n = 1; 26460786Sps } else 26560786Sps { 26660786Sps n = iread(ch_file, &bp->data[bp->datasize], 26760786Sps (unsigned int)(LBUFSIZE - bp->datasize)); 26860786Sps } 26960786Sps 27060786Sps if (n == READ_INTR) 27160786Sps return (EOI); 27260786Sps if (n < 0) 27360786Sps { 27460786Sps#if MSDOS_COMPILER==WIN32C 27560786Sps if (errno != EPIPE) 27660786Sps#endif 27760786Sps { 27860786Sps error("read error", NULL_PARG); 27960786Sps clear_eol(); 28060786Sps } 28160786Sps n = 0; 28260786Sps } 28360786Sps 28460786Sps#if LOGFILE 28560786Sps /* 28660786Sps * If we have a log file, write the new data to it. 28760786Sps */ 28860786Sps if (!secure && logfile >= 0 && n > 0) 28960786Sps write(logfile, (char *) &bp->data[bp->datasize], n); 29060786Sps#endif 29160786Sps 29260786Sps ch_fpos += n; 29360786Sps bp->datasize += n; 29460786Sps 29560786Sps /* 29660786Sps * If we have read to end of file, set ch_fsize to indicate 29760786Sps * the position of the end of file. 29860786Sps */ 29960786Sps if (n == 0) 30060786Sps { 30160786Sps ch_fsize = pos; 30260786Sps if (ignore_eoi) 30360786Sps { 30460786Sps /* 30560786Sps * We are ignoring EOF. 30660786Sps * Wait a while, then try again. 30760786Sps */ 30860786Sps if (!slept) 30989019Sps { 31089019Sps PARG parg; 31189019Sps parg.p_string = wait_message(); 31289019Sps ierror("%s", &parg); 31389019Sps } 31460786Sps#if !MSDOS_COMPILER 31560786Sps sleep(1); 31660786Sps#else 31760786Sps#if MSDOS_COMPILER==WIN32C 31860786Sps Sleep(1000); 31960786Sps#endif 32060786Sps#endif 32160786Sps slept = TRUE; 322173682Sdelphij 323173682Sdelphij#if HAVE_STAT_INO 324173682Sdelphij if (follow_mode == FOLLOW_NAME) 325173682Sdelphij { 326173682Sdelphij /* See whether the file's i-number has changed. 327173682Sdelphij * If so, force the file to be closed and 328173682Sdelphij * reopened. */ 329173682Sdelphij struct stat st; 330173682Sdelphij int r = stat(get_filename(curr_ifile), &st); 331173682Sdelphij if (r == 0 && (st.st_ino != curr_ino || 332173682Sdelphij st.st_dev != curr_dev)) 333173682Sdelphij { 334173682Sdelphij /* screen_trashed=2 causes 335173682Sdelphij * make_display to reopen the file. */ 336173682Sdelphij screen_trashed = 2; 337173682Sdelphij return (EOI); 338173682Sdelphij } 339173682Sdelphij } 340173682Sdelphij#endif 34160786Sps } 34260786Sps if (sigs) 34360786Sps return (EOI); 34460786Sps } 34560786Sps 34660786Sps found: 347191930Sdelphij if (ch_bufhead != bn) 34860786Sps { 34960786Sps /* 35060786Sps * Move the buffer to the head of the buffer chain. 35160786Sps * This orders the buffer chain, most- to least-recently used. 35260786Sps */ 353191930Sdelphij BUF_RM(bn); 354191930Sdelphij BUF_INS_HEAD(bn); 35589019Sps 35689019Sps /* 35789019Sps * Move to head of hash chain too. 35889019Sps */ 359191930Sdelphij BUF_HASH_RM(bn); 360191930Sdelphij BUF_HASH_INS(bn, h); 36160786Sps } 36260786Sps 36360786Sps if (ch_offset >= bp->datasize) 36460786Sps /* 36560786Sps * After all that, we still don't have enough data. 36660786Sps * Go back and try again. 36760786Sps */ 36860786Sps goto read_more; 36960786Sps 37060786Sps return (bp->data[ch_offset]); 37160786Sps} 37260786Sps 37360786Sps/* 37460786Sps * ch_ungetchar is a rather kludgy and limited way to push 37560786Sps * a single char onto an input file descriptor. 37660786Sps */ 37760786Sps public void 37860786Spsch_ungetchar(c) 37960786Sps int c; 38060786Sps{ 38160786Sps if (c != -1 && ch_ungotchar != -1) 38260786Sps error("ch_ungetchar overrun", NULL_PARG); 38360786Sps ch_ungotchar = c; 38460786Sps} 38560786Sps 38660786Sps#if LOGFILE 38760786Sps/* 38860786Sps * Close the logfile. 38960786Sps * If we haven't read all of standard input into it, do that now. 39060786Sps */ 39160786Sps public void 39260786Spsend_logfile() 39360786Sps{ 39460786Sps static int tried = FALSE; 39560786Sps 39660786Sps if (logfile < 0) 39760786Sps return; 39860786Sps if (!tried && ch_fsize == NULL_POSITION) 39960786Sps { 40060786Sps tried = TRUE; 40160786Sps ierror("Finishing logfile", NULL_PARG); 40260786Sps while (ch_forw_get() != EOI) 40360786Sps if (ABORT_SIGS()) 40460786Sps break; 40560786Sps } 40660786Sps close(logfile); 40760786Sps logfile = -1; 40860786Sps namelogfile = NULL; 40960786Sps} 41060786Sps 41160786Sps/* 41260786Sps * Start a log file AFTER less has already been running. 41360786Sps * Invoked from the - command; see toggle_option(). 41460786Sps * Write all the existing buffered data to the log file. 41560786Sps */ 41660786Sps public void 41760786Spssync_logfile() 41860786Sps{ 41960786Sps register struct buf *bp; 420191930Sdelphij register struct bufnode *bn; 42160786Sps int warned = FALSE; 42289019Sps BLOCKNUM block; 42389019Sps BLOCKNUM nblocks; 42460786Sps 42560786Sps nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; 42660786Sps for (block = 0; block < nblocks; block++) 42760786Sps { 428191930Sdelphij int wrote = FALSE; 429191930Sdelphij FOR_BUFS(bn) 43060786Sps { 431191930Sdelphij bp = bufnode_buf(bn); 43260786Sps if (bp->block == block) 43360786Sps { 43460786Sps write(logfile, (char *) bp->data, bp->datasize); 435191930Sdelphij wrote = TRUE; 43660786Sps break; 43760786Sps } 43860786Sps } 439191930Sdelphij if (!wrote && !warned) 440191930Sdelphij { 441191930Sdelphij error("Warning: log file is incomplete", 442191930Sdelphij NULL_PARG); 443191930Sdelphij warned = TRUE; 444191930Sdelphij } 44560786Sps } 44660786Sps} 44760786Sps 44860786Sps#endif 44960786Sps 45060786Sps/* 45160786Sps * Determine if a specific block is currently in one of the buffers. 45260786Sps */ 45360786Sps static int 45460786Spsbuffered(block) 45589019Sps BLOCKNUM block; 45660786Sps{ 45760786Sps register struct buf *bp; 458191930Sdelphij register struct bufnode *bn; 45989019Sps register int h; 46060786Sps 46189019Sps h = BUFHASH(block); 462191930Sdelphij FOR_BUFS_IN_CHAIN(h, bn) 46389019Sps { 464191930Sdelphij bp = bufnode_buf(bn); 46560786Sps if (bp->block == block) 46660786Sps return (TRUE); 46789019Sps } 46860786Sps return (FALSE); 46960786Sps} 47060786Sps 47160786Sps/* 47260786Sps * Seek to a specified position in the file. 47360786Sps * Return 0 if successful, non-zero if can't seek there. 47460786Sps */ 47560786Sps public int 47660786Spsch_seek(pos) 47760786Sps register POSITION pos; 47860786Sps{ 47989019Sps BLOCKNUM new_block; 48060786Sps POSITION len; 48160786Sps 482172468Sdelphij if (thisfile == NULL) 483172468Sdelphij return (0); 484172468Sdelphij 48560786Sps len = ch_length(); 48660786Sps if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) 48760786Sps return (1); 48860786Sps 48960786Sps new_block = pos / LBUFSIZE; 49060786Sps if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) 49160786Sps { 49260786Sps if (ch_fpos > pos) 49360786Sps return (1); 49460786Sps while (ch_fpos < pos) 49560786Sps { 49660786Sps if (ch_forw_get() == EOI) 49760786Sps return (1); 49860786Sps if (ABORT_SIGS()) 49960786Sps return (1); 50060786Sps } 50160786Sps return (0); 50260786Sps } 50360786Sps /* 50460786Sps * Set read pointer. 50560786Sps */ 50660786Sps ch_block = new_block; 50760786Sps ch_offset = pos % LBUFSIZE; 50860786Sps return (0); 50960786Sps} 51060786Sps 51160786Sps/* 51260786Sps * Seek to the end of the file. 51360786Sps */ 51460786Sps public int 51560786Spsch_end_seek() 51660786Sps{ 51760786Sps POSITION len; 51860786Sps 519172468Sdelphij if (thisfile == NULL) 520172468Sdelphij return (0); 521172468Sdelphij 52260786Sps if (ch_flags & CH_CANSEEK) 52360786Sps ch_fsize = filesize(ch_file); 52460786Sps 52560786Sps len = ch_length(); 52660786Sps if (len != NULL_POSITION) 52760786Sps return (ch_seek(len)); 52860786Sps 52960786Sps /* 53060786Sps * Do it the slow way: read till end of data. 53160786Sps */ 53260786Sps while (ch_forw_get() != EOI) 53360786Sps if (ABORT_SIGS()) 53460786Sps return (1); 53560786Sps return (0); 53660786Sps} 53760786Sps 53860786Sps/* 53960786Sps * Seek to the beginning of the file, or as close to it as we can get. 54060786Sps * We may not be able to seek there if input is a pipe and the 54160786Sps * beginning of the pipe is no longer buffered. 54260786Sps */ 54360786Sps public int 54460786Spsch_beg_seek() 54560786Sps{ 546191930Sdelphij register struct bufnode *bn; 547191930Sdelphij register struct bufnode *firstbn; 54860786Sps 54960786Sps /* 55060786Sps * Try a plain ch_seek first. 55160786Sps */ 55260786Sps if (ch_seek(ch_zero()) == 0) 55360786Sps return (0); 55460786Sps 55560786Sps /* 55660786Sps * Can't get to position 0. 55760786Sps * Look thru the buffers for the one closest to position 0. 55860786Sps */ 559191930Sdelphij firstbn = ch_bufhead; 560191930Sdelphij if (firstbn == END_OF_CHAIN) 56160786Sps return (1); 562191930Sdelphij FOR_BUFS(bn) 563191930Sdelphij { 564191930Sdelphij if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) 565191930Sdelphij firstbn = bn; 566191930Sdelphij } 567191930Sdelphij ch_block = bufnode_buf(firstbn)->block; 56860786Sps ch_offset = 0; 56960786Sps return (0); 57060786Sps} 57160786Sps 57260786Sps/* 57360786Sps * Return the length of the file, if known. 57460786Sps */ 57560786Sps public POSITION 57660786Spsch_length() 57760786Sps{ 578172468Sdelphij if (thisfile == NULL) 579172468Sdelphij return (NULL_POSITION); 58060786Sps if (ignore_eoi) 58160786Sps return (NULL_POSITION); 58260786Sps if (ch_flags & CH_HELPFILE) 58360786Sps return (size_helpdata); 584240121Sdelphij if (ch_flags & CH_NODATA) 585240121Sdelphij return (0); 58660786Sps return (ch_fsize); 58760786Sps} 58860786Sps 58960786Sps/* 59060786Sps * Return the current position in the file. 59160786Sps */ 59260786Sps public POSITION 59360786Spsch_tell() 59460786Sps{ 595172468Sdelphij if (thisfile == NULL) 596172468Sdelphij return (NULL_POSITION); 59789019Sps return (ch_block * LBUFSIZE) + ch_offset; 59860786Sps} 59960786Sps 60060786Sps/* 60160786Sps * Get the current char and post-increment the read pointer. 60260786Sps */ 60360786Sps public int 60460786Spsch_forw_get() 60560786Sps{ 60660786Sps register int c; 60760786Sps 608172468Sdelphij if (thisfile == NULL) 609172468Sdelphij return (EOI); 61060786Sps c = ch_get(); 61160786Sps if (c == EOI) 61260786Sps return (EOI); 61360786Sps if (ch_offset < LBUFSIZE-1) 61460786Sps ch_offset++; 61560786Sps else 61660786Sps { 61760786Sps ch_block ++; 61860786Sps ch_offset = 0; 61960786Sps } 62060786Sps return (c); 62160786Sps} 62260786Sps 62360786Sps/* 62460786Sps * Pre-decrement the read pointer and get the new current char. 62560786Sps */ 62660786Sps public int 62760786Spsch_back_get() 62860786Sps{ 629172468Sdelphij if (thisfile == NULL) 630172468Sdelphij return (EOI); 63160786Sps if (ch_offset > 0) 63260786Sps ch_offset --; 63360786Sps else 63460786Sps { 63560786Sps if (ch_block <= 0) 63660786Sps return (EOI); 63760786Sps if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) 63860786Sps return (EOI); 63960786Sps ch_block--; 64060786Sps ch_offset = LBUFSIZE-1; 64160786Sps } 64260786Sps return (ch_get()); 64360786Sps} 64460786Sps 64560786Sps/* 646128345Stjr * Set max amount of buffer space. 647128345Stjr * bufspace is in units of 1024 bytes. -1 mean no limit. 64860786Sps */ 649128345Stjr public void 650128345Stjrch_setbufspace(bufspace) 651128345Stjr int bufspace; 65260786Sps{ 653128345Stjr if (bufspace < 0) 654128345Stjr maxbufs = -1; 655128345Stjr else 65660786Sps { 657128345Stjr maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; 658128345Stjr if (maxbufs < 1) 659128345Stjr maxbufs = 1; 66060786Sps } 66160786Sps} 66260786Sps 66360786Sps/* 66460786Sps * Flush (discard) any saved file state, including buffer contents. 66560786Sps */ 66660786Sps public void 66760786Spsch_flush() 66860786Sps{ 669191930Sdelphij register struct bufnode *bn; 67060786Sps 671172468Sdelphij if (thisfile == NULL) 672172468Sdelphij return; 673172468Sdelphij 67460786Sps if (!(ch_flags & CH_CANSEEK)) 67560786Sps { 67660786Sps /* 67760786Sps * If input is a pipe, we don't flush buffer contents, 67860786Sps * since the contents can't be recovered. 67960786Sps */ 68060786Sps ch_fsize = NULL_POSITION; 68160786Sps return; 68260786Sps } 68360786Sps 68460786Sps /* 68560786Sps * Initialize all the buffers. 68660786Sps */ 687191930Sdelphij FOR_BUFS(bn) 688191930Sdelphij { 689191930Sdelphij bufnode_buf(bn)->block = -1; 690191930Sdelphij } 69160786Sps 69260786Sps /* 69360786Sps * Figure out the size of the file, if we can. 69460786Sps */ 69560786Sps ch_fsize = filesize(ch_file); 69660786Sps 69760786Sps /* 69860786Sps * Seek to a known position: the beginning of the file. 69960786Sps */ 70060786Sps ch_fpos = 0; 70160786Sps ch_block = 0; /* ch_fpos / LBUFSIZE; */ 70260786Sps ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 70360786Sps 70460786Sps#if 1 70560786Sps /* 70660786Sps * This is a kludge to workaround a Linux kernel bug: files in 70760786Sps * /proc have a size of 0 according to fstat() but have readable 70860786Sps * data. They are sometimes, but not always, seekable. 70960786Sps * Force them to be non-seekable here. 71060786Sps */ 71160786Sps if (ch_fsize == 0) 71260786Sps { 71360786Sps ch_fsize = NULL_POSITION; 71460786Sps ch_flags &= ~CH_CANSEEK; 71560786Sps } 71660786Sps#endif 71760786Sps 718173682Sdelphij if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) 71960786Sps { 72060786Sps /* 72160786Sps * Warning only; even if the seek fails for some reason, 72260786Sps * there's a good chance we're at the beginning anyway. 72360786Sps * {{ I think this is bogus reasoning. }} 72460786Sps */ 72560786Sps error("seek error to 0", NULL_PARG); 72660786Sps } 72760786Sps} 72860786Sps 72960786Sps/* 73060786Sps * Allocate a new buffer. 73160786Sps * The buffer is added to the tail of the buffer chain. 73260786Sps */ 73360786Sps static int 73460786Spsch_addbuf() 73560786Sps{ 73660786Sps register struct buf *bp; 737191930Sdelphij register struct bufnode *bn; 73860786Sps 73960786Sps /* 74060786Sps * Allocate and initialize a new buffer and link it 74160786Sps * onto the tail of the buffer list. 74260786Sps */ 74360786Sps bp = (struct buf *) calloc(1, sizeof(struct buf)); 74460786Sps if (bp == NULL) 74560786Sps return (1); 74660786Sps ch_nbufs++; 74789019Sps bp->block = -1; 748191930Sdelphij bn = &bp->node; 749191930Sdelphij 750191930Sdelphij BUF_INS_TAIL(bn); 751191930Sdelphij BUF_HASH_INS(bn, 0); 75260786Sps return (0); 75360786Sps} 75460786Sps 75560786Sps/* 75689019Sps * 75789019Sps */ 75889019Sps static void 75989019Spsinit_hashtbl() 76089019Sps{ 76189019Sps register int h; 76289019Sps 76389019Sps for (h = 0; h < BUFHASH_SIZE; h++) 76489019Sps { 765191930Sdelphij thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); 766191930Sdelphij thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); 76789019Sps } 76889019Sps} 76989019Sps 77089019Sps/* 77160786Sps * Delete all buffers for this file. 77260786Sps */ 77360786Sps static void 77460786Spsch_delbufs() 77560786Sps{ 776191930Sdelphij register struct bufnode *bn; 77760786Sps 77860786Sps while (ch_bufhead != END_OF_CHAIN) 77960786Sps { 780191930Sdelphij bn = ch_bufhead; 781191930Sdelphij BUF_RM(bn); 782191930Sdelphij free(bufnode_buf(bn)); 78360786Sps } 78460786Sps ch_nbufs = 0; 78589019Sps init_hashtbl(); 78660786Sps} 78760786Sps 78860786Sps/* 78960786Sps * Is it possible to seek on a file descriptor? 79060786Sps */ 79160786Sps public int 79260786Spsseekable(f) 79360786Sps int f; 79460786Sps{ 79560786Sps#if MSDOS_COMPILER 79660786Sps extern int fd0; 79760786Sps if (f == fd0 && !isatty(fd0)) 79860786Sps { 79960786Sps /* 80060786Sps * In MS-DOS, pipes are seekable. Check for 80160786Sps * standard input, and pretend it is not seekable. 80260786Sps */ 80360786Sps return (0); 80460786Sps } 80560786Sps#endif 806173682Sdelphij return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); 80760786Sps} 80860786Sps 80960786Sps/* 810240121Sdelphij * Force EOF to be at the current read position. 811240121Sdelphij * This is used after an ignore_eof read, during which the EOF may change. 812240121Sdelphij */ 813240121Sdelphij public void 814240121Sdelphijch_set_eof() 815240121Sdelphij{ 816240121Sdelphij ch_fsize = ch_fpos; 817240121Sdelphij} 818240121Sdelphij 819240121Sdelphij 820240121Sdelphij/* 82160786Sps * Initialize file state for a new file. 82260786Sps */ 82360786Sps public void 82460786Spsch_init(f, flags) 82560786Sps int f; 82660786Sps int flags; 82760786Sps{ 82860786Sps /* 82960786Sps * See if we already have a filestate for this file. 83060786Sps */ 83160786Sps thisfile = (struct filestate *) get_filestate(curr_ifile); 83260786Sps if (thisfile == NULL) 83360786Sps { 83460786Sps /* 83560786Sps * Allocate and initialize a new filestate. 83660786Sps */ 83760786Sps thisfile = (struct filestate *) 83860786Sps calloc(1, sizeof(struct filestate)); 839191930Sdelphij thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; 84060786Sps thisfile->nbufs = 0; 84160786Sps thisfile->flags = 0; 84260786Sps thisfile->fpos = 0; 84360786Sps thisfile->block = 0; 84460786Sps thisfile->offset = 0; 84560786Sps thisfile->file = -1; 84660786Sps thisfile->fsize = NULL_POSITION; 84760786Sps ch_flags = flags; 84889019Sps init_hashtbl(); 84960786Sps /* 85060786Sps * Try to seek; set CH_CANSEEK if it works. 85160786Sps */ 85260786Sps if ((flags & CH_CANSEEK) && !seekable(f)) 85360786Sps ch_flags &= ~CH_CANSEEK; 85460786Sps set_filestate(curr_ifile, (void *) thisfile); 85560786Sps } 85660786Sps if (thisfile->file == -1) 85760786Sps thisfile->file = f; 85860786Sps ch_flush(); 85960786Sps} 86060786Sps 86160786Sps/* 86260786Sps * Close a filestate. 86360786Sps */ 86460786Sps public void 86560786Spsch_close() 86660786Sps{ 86760786Sps int keepstate = FALSE; 86860786Sps 869172468Sdelphij if (thisfile == NULL) 870172468Sdelphij return; 871172468Sdelphij 87260786Sps if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) 87360786Sps { 87460786Sps /* 87560786Sps * We can seek or re-open, so we don't need to keep buffers. 87660786Sps */ 87760786Sps ch_delbufs(); 87860786Sps } else 87960786Sps keepstate = TRUE; 88060786Sps if (!(ch_flags & CH_KEEPOPEN)) 88160786Sps { 88260786Sps /* 88360786Sps * We don't need to keep the file descriptor open 88460786Sps * (because we can re-open it.) 88560786Sps * But don't really close it if it was opened via popen(), 88660786Sps * because pclose() wants to close it. 88760786Sps */ 88860786Sps if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 88960786Sps close(ch_file); 89060786Sps ch_file = -1; 89160786Sps } else 89260786Sps keepstate = TRUE; 89360786Sps if (!keepstate) 89460786Sps { 89560786Sps /* 89660786Sps * We don't even need to keep the filestate structure. 89760786Sps */ 89860786Sps free(thisfile); 89960786Sps thisfile = NULL; 90060786Sps set_filestate(curr_ifile, (void *) NULL); 90160786Sps } 90260786Sps} 90360786Sps 90460786Sps/* 90560786Sps * Return ch_flags for the current file. 90660786Sps */ 90760786Sps public int 90860786Spsch_getflags() 90960786Sps{ 910172468Sdelphij if (thisfile == NULL) 911172468Sdelphij return (0); 91260786Sps return (ch_flags); 91360786Sps} 91460786Sps 91560786Sps#if 0 91660786Sps public void 91760786Spsch_dump(struct filestate *fs) 91860786Sps{ 91960786Sps struct buf *bp; 920191930Sdelphij struct bufnode *bn; 92160786Sps unsigned char *s; 92260786Sps 92360786Sps if (fs == NULL) 92460786Sps { 92560786Sps printf(" --no filestate\n"); 92660786Sps return; 92760786Sps } 92860786Sps printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 92960786Sps fs->file, fs->flags, fs->fpos, 93060786Sps fs->fsize, fs->block, fs->offset); 93160786Sps printf(" %d bufs:\n", fs->nbufs); 932191930Sdelphij for (bn = fs->next; bn != &fs->buflist; bn = bn->next) 93360786Sps { 934191930Sdelphij bp = bufnode_buf(bn); 93560786Sps printf("%x: blk %x, size %x \"", 93660786Sps bp, bp->block, bp->datasize); 93760786Sps for (s = bp->data; s < bp->data + 30; s++) 93860786Sps if (*s >= ' ' && *s < 0x7F) 93960786Sps printf("%c", *s); 94060786Sps else 94160786Sps printf("."); 94260786Sps printf("\"\n"); 94360786Sps } 94460786Sps} 94560786Sps#endif 946