1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 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 */ 57294286Sdelphij#define BUFHASH_SIZE 1024 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{ 149330571Sdelphij struct buf *bp; 150330571Sdelphij struct bufnode *bn; 151330571Sdelphij int n; 152330571Sdelphij int slept; 153330571Sdelphij 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 { 326294286Sdelphij /* See whether the file's i-number has changed, 327294286Sdelphij * or the file has shrunk. 328173682Sdelphij * If so, force the file to be closed and 329173682Sdelphij * reopened. */ 330173682Sdelphij struct stat st; 331294286Sdelphij POSITION curr_pos = ch_tell(); 332173682Sdelphij int r = stat(get_filename(curr_ifile), &st); 333173682Sdelphij if (r == 0 && (st.st_ino != curr_ino || 334294286Sdelphij st.st_dev != curr_dev || 335294286Sdelphij (curr_pos != NULL_POSITION && st.st_size < curr_pos))) 336173682Sdelphij { 337173682Sdelphij /* screen_trashed=2 causes 338173682Sdelphij * make_display to reopen the file. */ 339173682Sdelphij screen_trashed = 2; 340173682Sdelphij return (EOI); 341173682Sdelphij } 342173682Sdelphij } 343173682Sdelphij#endif 34460786Sps } 34560786Sps if (sigs) 34660786Sps return (EOI); 34760786Sps } 34860786Sps 34960786Sps found: 350191930Sdelphij if (ch_bufhead != bn) 35160786Sps { 35260786Sps /* 35360786Sps * Move the buffer to the head of the buffer chain. 35460786Sps * This orders the buffer chain, most- to least-recently used. 35560786Sps */ 356191930Sdelphij BUF_RM(bn); 357191930Sdelphij BUF_INS_HEAD(bn); 35889019Sps 35989019Sps /* 36089019Sps * Move to head of hash chain too. 36189019Sps */ 362191930Sdelphij BUF_HASH_RM(bn); 363191930Sdelphij BUF_HASH_INS(bn, h); 36460786Sps } 36560786Sps 36660786Sps if (ch_offset >= bp->datasize) 36760786Sps /* 36860786Sps * After all that, we still don't have enough data. 36960786Sps * Go back and try again. 37060786Sps */ 37160786Sps goto read_more; 37260786Sps 37360786Sps return (bp->data[ch_offset]); 37460786Sps} 37560786Sps 37660786Sps/* 37760786Sps * ch_ungetchar is a rather kludgy and limited way to push 37860786Sps * a single char onto an input file descriptor. 37960786Sps */ 38060786Sps public void 38160786Spsch_ungetchar(c) 38260786Sps int c; 38360786Sps{ 38460786Sps if (c != -1 && ch_ungotchar != -1) 38560786Sps error("ch_ungetchar overrun", NULL_PARG); 38660786Sps ch_ungotchar = c; 38760786Sps} 38860786Sps 38960786Sps#if LOGFILE 39060786Sps/* 39160786Sps * Close the logfile. 39260786Sps * If we haven't read all of standard input into it, do that now. 39360786Sps */ 39460786Sps public void 39560786Spsend_logfile() 39660786Sps{ 39760786Sps static int tried = FALSE; 39860786Sps 39960786Sps if (logfile < 0) 40060786Sps return; 40160786Sps if (!tried && ch_fsize == NULL_POSITION) 40260786Sps { 40360786Sps tried = TRUE; 40460786Sps ierror("Finishing logfile", NULL_PARG); 40560786Sps while (ch_forw_get() != EOI) 40660786Sps if (ABORT_SIGS()) 40760786Sps break; 40860786Sps } 40960786Sps close(logfile); 41060786Sps logfile = -1; 41160786Sps namelogfile = NULL; 41260786Sps} 41360786Sps 41460786Sps/* 41560786Sps * Start a log file AFTER less has already been running. 41660786Sps * Invoked from the - command; see toggle_option(). 41760786Sps * Write all the existing buffered data to the log file. 41860786Sps */ 41960786Sps public void 42060786Spssync_logfile() 42160786Sps{ 422330571Sdelphij struct buf *bp; 423330571Sdelphij struct bufnode *bn; 42460786Sps int warned = FALSE; 42589019Sps BLOCKNUM block; 42689019Sps BLOCKNUM nblocks; 42760786Sps 42860786Sps nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; 42960786Sps for (block = 0; block < nblocks; block++) 43060786Sps { 431191930Sdelphij int wrote = FALSE; 432191930Sdelphij FOR_BUFS(bn) 43360786Sps { 434191930Sdelphij bp = bufnode_buf(bn); 43560786Sps if (bp->block == block) 43660786Sps { 43760786Sps write(logfile, (char *) bp->data, bp->datasize); 438191930Sdelphij wrote = TRUE; 43960786Sps break; 44060786Sps } 44160786Sps } 442191930Sdelphij if (!wrote && !warned) 443191930Sdelphij { 444191930Sdelphij error("Warning: log file is incomplete", 445191930Sdelphij NULL_PARG); 446191930Sdelphij warned = TRUE; 447191930Sdelphij } 44860786Sps } 44960786Sps} 45060786Sps 45160786Sps#endif 45260786Sps 45360786Sps/* 45460786Sps * Determine if a specific block is currently in one of the buffers. 45560786Sps */ 45660786Sps static int 45760786Spsbuffered(block) 45889019Sps BLOCKNUM block; 45960786Sps{ 460330571Sdelphij struct buf *bp; 461330571Sdelphij struct bufnode *bn; 462330571Sdelphij int h; 46360786Sps 46489019Sps h = BUFHASH(block); 465191930Sdelphij FOR_BUFS_IN_CHAIN(h, bn) 46689019Sps { 467191930Sdelphij bp = bufnode_buf(bn); 46860786Sps if (bp->block == block) 46960786Sps return (TRUE); 47089019Sps } 47160786Sps return (FALSE); 47260786Sps} 47360786Sps 47460786Sps/* 47560786Sps * Seek to a specified position in the file. 47660786Sps * Return 0 if successful, non-zero if can't seek there. 47760786Sps */ 47860786Sps public int 47960786Spsch_seek(pos) 480330571Sdelphij POSITION pos; 48160786Sps{ 48289019Sps BLOCKNUM new_block; 48360786Sps POSITION len; 48460786Sps 485172468Sdelphij if (thisfile == NULL) 486172468Sdelphij return (0); 487172468Sdelphij 48860786Sps len = ch_length(); 48960786Sps if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) 49060786Sps return (1); 49160786Sps 49260786Sps new_block = pos / LBUFSIZE; 49360786Sps if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) 49460786Sps { 49560786Sps if (ch_fpos > pos) 49660786Sps return (1); 49760786Sps while (ch_fpos < pos) 49860786Sps { 49960786Sps if (ch_forw_get() == EOI) 50060786Sps return (1); 50160786Sps if (ABORT_SIGS()) 50260786Sps return (1); 50360786Sps } 50460786Sps return (0); 50560786Sps } 50660786Sps /* 50760786Sps * Set read pointer. 50860786Sps */ 50960786Sps ch_block = new_block; 51060786Sps ch_offset = pos % LBUFSIZE; 51160786Sps return (0); 51260786Sps} 51360786Sps 51460786Sps/* 51560786Sps * Seek to the end of the file. 51660786Sps */ 51760786Sps public int 51860786Spsch_end_seek() 51960786Sps{ 52060786Sps POSITION len; 52160786Sps 522172468Sdelphij if (thisfile == NULL) 523172468Sdelphij return (0); 524172468Sdelphij 52560786Sps if (ch_flags & CH_CANSEEK) 52660786Sps ch_fsize = filesize(ch_file); 52760786Sps 52860786Sps len = ch_length(); 52960786Sps if (len != NULL_POSITION) 53060786Sps return (ch_seek(len)); 53160786Sps 53260786Sps /* 53360786Sps * Do it the slow way: read till end of data. 53460786Sps */ 53560786Sps while (ch_forw_get() != EOI) 53660786Sps if (ABORT_SIGS()) 53760786Sps return (1); 53860786Sps return (0); 53960786Sps} 54060786Sps 54160786Sps/* 542294286Sdelphij * Seek to the last position in the file that is currently buffered. 543294286Sdelphij */ 544294286Sdelphij public int 545294286Sdelphijch_end_buffer_seek() 546294286Sdelphij{ 547330571Sdelphij struct buf *bp; 548330571Sdelphij struct bufnode *bn; 549294286Sdelphij POSITION buf_pos; 550294286Sdelphij POSITION end_pos; 551294286Sdelphij 552294286Sdelphij if (thisfile == NULL || (ch_flags & CH_CANSEEK)) 553294286Sdelphij return (ch_end_seek()); 554294286Sdelphij 555294286Sdelphij end_pos = 0; 556294286Sdelphij FOR_BUFS(bn) 557294286Sdelphij { 558294286Sdelphij bp = bufnode_buf(bn); 559294286Sdelphij buf_pos = (bp->block * LBUFSIZE) + bp->datasize; 560294286Sdelphij if (buf_pos > end_pos) 561294286Sdelphij end_pos = buf_pos; 562294286Sdelphij } 563294286Sdelphij 564294286Sdelphij return (ch_seek(end_pos)); 565294286Sdelphij} 566294286Sdelphij 567294286Sdelphij/* 56860786Sps * Seek to the beginning of the file, or as close to it as we can get. 56960786Sps * We may not be able to seek there if input is a pipe and the 57060786Sps * beginning of the pipe is no longer buffered. 57160786Sps */ 57260786Sps public int 57360786Spsch_beg_seek() 57460786Sps{ 575330571Sdelphij struct bufnode *bn; 576330571Sdelphij struct bufnode *firstbn; 57760786Sps 57860786Sps /* 57960786Sps * Try a plain ch_seek first. 58060786Sps */ 58160786Sps if (ch_seek(ch_zero()) == 0) 58260786Sps return (0); 58360786Sps 58460786Sps /* 58560786Sps * Can't get to position 0. 58660786Sps * Look thru the buffers for the one closest to position 0. 58760786Sps */ 588191930Sdelphij firstbn = ch_bufhead; 589191930Sdelphij if (firstbn == END_OF_CHAIN) 59060786Sps return (1); 591191930Sdelphij FOR_BUFS(bn) 592191930Sdelphij { 593191930Sdelphij if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) 594191930Sdelphij firstbn = bn; 595191930Sdelphij } 596191930Sdelphij ch_block = bufnode_buf(firstbn)->block; 59760786Sps ch_offset = 0; 59860786Sps return (0); 59960786Sps} 60060786Sps 60160786Sps/* 60260786Sps * Return the length of the file, if known. 60360786Sps */ 60460786Sps public POSITION 60560786Spsch_length() 60660786Sps{ 607172468Sdelphij if (thisfile == NULL) 608172468Sdelphij return (NULL_POSITION); 60960786Sps if (ignore_eoi) 61060786Sps return (NULL_POSITION); 61160786Sps if (ch_flags & CH_HELPFILE) 61260786Sps return (size_helpdata); 613237613Sdelphij if (ch_flags & CH_NODATA) 614237613Sdelphij return (0); 61560786Sps return (ch_fsize); 61660786Sps} 61760786Sps 61860786Sps/* 61960786Sps * Return the current position in the file. 62060786Sps */ 62160786Sps public POSITION 62260786Spsch_tell() 62360786Sps{ 624172468Sdelphij if (thisfile == NULL) 625172468Sdelphij return (NULL_POSITION); 62689019Sps return (ch_block * LBUFSIZE) + ch_offset; 62760786Sps} 62860786Sps 62960786Sps/* 63060786Sps * Get the current char and post-increment the read pointer. 63160786Sps */ 63260786Sps public int 63360786Spsch_forw_get() 63460786Sps{ 635330571Sdelphij int c; 63660786Sps 637172468Sdelphij if (thisfile == NULL) 638172468Sdelphij return (EOI); 63960786Sps c = ch_get(); 64060786Sps if (c == EOI) 64160786Sps return (EOI); 64260786Sps if (ch_offset < LBUFSIZE-1) 64360786Sps ch_offset++; 64460786Sps else 64560786Sps { 64660786Sps ch_block ++; 64760786Sps ch_offset = 0; 64860786Sps } 64960786Sps return (c); 65060786Sps} 65160786Sps 65260786Sps/* 65360786Sps * Pre-decrement the read pointer and get the new current char. 65460786Sps */ 65560786Sps public int 65660786Spsch_back_get() 65760786Sps{ 658172468Sdelphij if (thisfile == NULL) 659172468Sdelphij return (EOI); 66060786Sps if (ch_offset > 0) 66160786Sps ch_offset --; 66260786Sps else 66360786Sps { 66460786Sps if (ch_block <= 0) 66560786Sps return (EOI); 66660786Sps if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) 66760786Sps return (EOI); 66860786Sps ch_block--; 66960786Sps ch_offset = LBUFSIZE-1; 67060786Sps } 67160786Sps return (ch_get()); 67260786Sps} 67360786Sps 67460786Sps/* 675128345Stjr * Set max amount of buffer space. 676128345Stjr * bufspace is in units of 1024 bytes. -1 mean no limit. 67760786Sps */ 678128345Stjr public void 679128345Stjrch_setbufspace(bufspace) 680128345Stjr int bufspace; 68160786Sps{ 682128345Stjr if (bufspace < 0) 683128345Stjr maxbufs = -1; 684128345Stjr else 68560786Sps { 686128345Stjr maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; 687128345Stjr if (maxbufs < 1) 688128345Stjr maxbufs = 1; 68960786Sps } 69060786Sps} 69160786Sps 69260786Sps/* 69360786Sps * Flush (discard) any saved file state, including buffer contents. 69460786Sps */ 69560786Sps public void 69660786Spsch_flush() 69760786Sps{ 698330571Sdelphij struct bufnode *bn; 69960786Sps 700172468Sdelphij if (thisfile == NULL) 701172468Sdelphij return; 702172468Sdelphij 70360786Sps if (!(ch_flags & CH_CANSEEK)) 70460786Sps { 70560786Sps /* 70660786Sps * If input is a pipe, we don't flush buffer contents, 70760786Sps * since the contents can't be recovered. 70860786Sps */ 70960786Sps ch_fsize = NULL_POSITION; 71060786Sps return; 71160786Sps } 71260786Sps 71360786Sps /* 71460786Sps * Initialize all the buffers. 71560786Sps */ 716191930Sdelphij FOR_BUFS(bn) 717191930Sdelphij { 718191930Sdelphij bufnode_buf(bn)->block = -1; 719191930Sdelphij } 72060786Sps 72160786Sps /* 72260786Sps * Figure out the size of the file, if we can. 72360786Sps */ 72460786Sps ch_fsize = filesize(ch_file); 72560786Sps 72660786Sps /* 72760786Sps * Seek to a known position: the beginning of the file. 72860786Sps */ 72960786Sps ch_fpos = 0; 73060786Sps ch_block = 0; /* ch_fpos / LBUFSIZE; */ 73160786Sps ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 73260786Sps 73360786Sps#if 1 73460786Sps /* 73560786Sps * This is a kludge to workaround a Linux kernel bug: files in 73660786Sps * /proc have a size of 0 according to fstat() but have readable 73760786Sps * data. They are sometimes, but not always, seekable. 73860786Sps * Force them to be non-seekable here. 73960786Sps */ 74060786Sps if (ch_fsize == 0) 74160786Sps { 74260786Sps ch_fsize = NULL_POSITION; 74360786Sps ch_flags &= ~CH_CANSEEK; 74460786Sps } 74560786Sps#endif 74660786Sps 747173682Sdelphij if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) 74860786Sps { 74960786Sps /* 75060786Sps * Warning only; even if the seek fails for some reason, 75160786Sps * there's a good chance we're at the beginning anyway. 75260786Sps * {{ I think this is bogus reasoning. }} 75360786Sps */ 75460786Sps error("seek error to 0", NULL_PARG); 75560786Sps } 75660786Sps} 75760786Sps 75860786Sps/* 75960786Sps * Allocate a new buffer. 76060786Sps * The buffer is added to the tail of the buffer chain. 76160786Sps */ 76260786Sps static int 76360786Spsch_addbuf() 76460786Sps{ 765330571Sdelphij struct buf *bp; 766330571Sdelphij struct bufnode *bn; 76760786Sps 76860786Sps /* 76960786Sps * Allocate and initialize a new buffer and link it 77060786Sps * onto the tail of the buffer list. 77160786Sps */ 77260786Sps bp = (struct buf *) calloc(1, sizeof(struct buf)); 77360786Sps if (bp == NULL) 77460786Sps return (1); 77560786Sps ch_nbufs++; 77689019Sps bp->block = -1; 777191930Sdelphij bn = &bp->node; 778191930Sdelphij 779191930Sdelphij BUF_INS_TAIL(bn); 780191930Sdelphij BUF_HASH_INS(bn, 0); 78160786Sps return (0); 78260786Sps} 78360786Sps 78460786Sps/* 78589019Sps * 78689019Sps */ 78789019Sps static void 78889019Spsinit_hashtbl() 78989019Sps{ 790330571Sdelphij int h; 79189019Sps 79289019Sps for (h = 0; h < BUFHASH_SIZE; h++) 79389019Sps { 794191930Sdelphij thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); 795191930Sdelphij thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); 79689019Sps } 79789019Sps} 79889019Sps 79989019Sps/* 80060786Sps * Delete all buffers for this file. 80160786Sps */ 80260786Sps static void 80360786Spsch_delbufs() 80460786Sps{ 805330571Sdelphij struct bufnode *bn; 80660786Sps 80760786Sps while (ch_bufhead != END_OF_CHAIN) 80860786Sps { 809191930Sdelphij bn = ch_bufhead; 810191930Sdelphij BUF_RM(bn); 811191930Sdelphij free(bufnode_buf(bn)); 81260786Sps } 81360786Sps ch_nbufs = 0; 81489019Sps init_hashtbl(); 81560786Sps} 81660786Sps 81760786Sps/* 81860786Sps * Is it possible to seek on a file descriptor? 81960786Sps */ 82060786Sps public int 82160786Spsseekable(f) 82260786Sps int f; 82360786Sps{ 82460786Sps#if MSDOS_COMPILER 82560786Sps extern int fd0; 82660786Sps if (f == fd0 && !isatty(fd0)) 82760786Sps { 82860786Sps /* 82960786Sps * In MS-DOS, pipes are seekable. Check for 83060786Sps * standard input, and pretend it is not seekable. 83160786Sps */ 83260786Sps return (0); 83360786Sps } 83460786Sps#endif 835173682Sdelphij return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); 83660786Sps} 83760786Sps 83860786Sps/* 839238730Sdelphij * Force EOF to be at the current read position. 840238730Sdelphij * This is used after an ignore_eof read, during which the EOF may change. 841238730Sdelphij */ 842238730Sdelphij public void 843238730Sdelphijch_set_eof() 844238730Sdelphij{ 845238730Sdelphij ch_fsize = ch_fpos; 846238730Sdelphij} 847238730Sdelphij 848238730Sdelphij 849238730Sdelphij/* 85060786Sps * Initialize file state for a new file. 85160786Sps */ 85260786Sps public void 85360786Spsch_init(f, flags) 85460786Sps int f; 85560786Sps int flags; 85660786Sps{ 85760786Sps /* 85860786Sps * See if we already have a filestate for this file. 85960786Sps */ 86060786Sps thisfile = (struct filestate *) get_filestate(curr_ifile); 86160786Sps if (thisfile == NULL) 86260786Sps { 86360786Sps /* 86460786Sps * Allocate and initialize a new filestate. 86560786Sps */ 86660786Sps thisfile = (struct filestate *) 86760786Sps calloc(1, sizeof(struct filestate)); 868191930Sdelphij thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; 86960786Sps thisfile->nbufs = 0; 870330571Sdelphij thisfile->flags = flags; 87160786Sps thisfile->fpos = 0; 87260786Sps thisfile->block = 0; 87360786Sps thisfile->offset = 0; 87460786Sps thisfile->file = -1; 87560786Sps thisfile->fsize = NULL_POSITION; 87689019Sps init_hashtbl(); 87760786Sps /* 87860786Sps * Try to seek; set CH_CANSEEK if it works. 87960786Sps */ 88060786Sps if ((flags & CH_CANSEEK) && !seekable(f)) 88160786Sps ch_flags &= ~CH_CANSEEK; 88260786Sps set_filestate(curr_ifile, (void *) thisfile); 88360786Sps } 88460786Sps if (thisfile->file == -1) 88560786Sps thisfile->file = f; 88660786Sps ch_flush(); 88760786Sps} 88860786Sps 88960786Sps/* 89060786Sps * Close a filestate. 89160786Sps */ 89260786Sps public void 89360786Spsch_close() 89460786Sps{ 89560786Sps int keepstate = FALSE; 89660786Sps 897172468Sdelphij if (thisfile == NULL) 898172468Sdelphij return; 899172468Sdelphij 900330571Sdelphij if ((ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) && !(ch_flags & CH_KEEPOPEN)) 90160786Sps { 90260786Sps /* 90360786Sps * We can seek or re-open, so we don't need to keep buffers. 90460786Sps */ 90560786Sps ch_delbufs(); 90660786Sps } else 90760786Sps keepstate = TRUE; 90860786Sps if (!(ch_flags & CH_KEEPOPEN)) 90960786Sps { 91060786Sps /* 91160786Sps * We don't need to keep the file descriptor open 91260786Sps * (because we can re-open it.) 91360786Sps * But don't really close it if it was opened via popen(), 91460786Sps * because pclose() wants to close it. 91560786Sps */ 91660786Sps if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 91760786Sps close(ch_file); 91860786Sps ch_file = -1; 91960786Sps } else 92060786Sps keepstate = TRUE; 92160786Sps if (!keepstate) 92260786Sps { 92360786Sps /* 92460786Sps * We don't even need to keep the filestate structure. 92560786Sps */ 92660786Sps free(thisfile); 92760786Sps thisfile = NULL; 92860786Sps set_filestate(curr_ifile, (void *) NULL); 92960786Sps } 93060786Sps} 93160786Sps 93260786Sps/* 93360786Sps * Return ch_flags for the current file. 93460786Sps */ 93560786Sps public int 93660786Spsch_getflags() 93760786Sps{ 938172468Sdelphij if (thisfile == NULL) 939172468Sdelphij return (0); 94060786Sps return (ch_flags); 94160786Sps} 94260786Sps 94360786Sps#if 0 94460786Sps public void 94560786Spsch_dump(struct filestate *fs) 94660786Sps{ 94760786Sps struct buf *bp; 948191930Sdelphij struct bufnode *bn; 94960786Sps unsigned char *s; 95060786Sps 95160786Sps if (fs == NULL) 95260786Sps { 95360786Sps printf(" --no filestate\n"); 95460786Sps return; 95560786Sps } 95660786Sps printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 95760786Sps fs->file, fs->flags, fs->fpos, 95860786Sps fs->fsize, fs->block, fs->offset); 95960786Sps printf(" %d bufs:\n", fs->nbufs); 960191930Sdelphij for (bn = fs->next; bn != &fs->buflist; bn = bn->next) 96160786Sps { 962191930Sdelphij bp = bufnode_buf(bn); 96360786Sps printf("%x: blk %x, size %x \"", 96460786Sps bp, bp->block, bp->datasize); 96560786Sps for (s = bp->data; s < bp->data + 30; s++) 96660786Sps if (*s >= ' ' && *s < 0x7F) 96760786Sps printf("%c", *s); 96860786Sps else 96960786Sps printf("."); 97060786Sps printf("\"\n"); 97160786Sps } 97260786Sps} 97360786Sps#endif 974