ch.c revision 173682
160786Sps/* 2170256Sdelphij * Copyright (C) 1984-2007 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 24173682Sdelphij#if HAVE_STAT_INO 25173682Sdelphij#include <sys/stat.h> 26173682Sdelphijextern dev_t curr_dev; 27173682Sdelphijextern ino_t curr_ino; 28173682Sdelphij#endif 29173682Sdelphij 3089019Spstypedef POSITION BLOCKNUM; 3189019Sps 3260786Spspublic int ignore_eoi; 3360786Sps 3460786Sps/* 3560786Sps * Pool of buffers holding the most recently used blocks of the input file. 3660786Sps * The buffer pool is kept as a doubly-linked circular list, 3760786Sps * in order from most- to least-recently used. 3860786Sps * The circular list is anchored by the file state "thisfile". 3960786Sps */ 4089019Sps#define LBUFSIZE 8192 4160786Spsstruct buf { 4289019Sps struct buf *next, *prev; 4389019Sps struct buf *hnext, *hprev; 4489019Sps BLOCKNUM block; 4560786Sps unsigned int datasize; 4660786Sps unsigned char data[LBUFSIZE]; 4760786Sps}; 4860786Sps 4989019Spsstruct buflist { 5089019Sps /* -- Following members must match struct buf */ 5189019Sps struct buf *buf_next, *buf_prev; 5289019Sps struct buf *buf_hnext, *buf_hprev; 5389019Sps}; 5489019Sps 5560786Sps/* 5660786Sps * The file state is maintained in a filestate structure. 5760786Sps * A pointer to the filestate is kept in the ifile structure. 5860786Sps */ 5989019Sps#define BUFHASH_SIZE 64 6060786Spsstruct filestate { 6160786Sps struct buf *buf_next, *buf_prev; 6289019Sps struct buflist hashtbl[BUFHASH_SIZE]; 6360786Sps int file; 6460786Sps int flags; 6560786Sps POSITION fpos; 6660786Sps int nbufs; 6789019Sps BLOCKNUM block; 6860786Sps unsigned int offset; 6960786Sps POSITION fsize; 7060786Sps}; 7160786Sps 7260786Sps#define ch_bufhead thisfile->buf_next 7360786Sps#define ch_buftail thisfile->buf_prev 7460786Sps#define ch_nbufs thisfile->nbufs 7560786Sps#define ch_block thisfile->block 7660786Sps#define ch_offset thisfile->offset 7760786Sps#define ch_fpos thisfile->fpos 7860786Sps#define ch_fsize thisfile->fsize 7960786Sps#define ch_flags thisfile->flags 8060786Sps#define ch_file thisfile->file 8160786Sps 8289019Sps#define END_OF_CHAIN ((struct buf *)&thisfile->buf_next) 8389019Sps#define END_OF_HCHAIN(h) ((struct buf *)&thisfile->hashtbl[h]) 8489019Sps#define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) 8589019Sps 8689019Sps#define FOR_BUFS_IN_CHAIN(h,bp) \ 8789019Sps for (bp = thisfile->hashtbl[h].buf_hnext; \ 8889019Sps bp != END_OF_HCHAIN(h); bp = bp->hnext) 8989019Sps 9089019Sps#define HASH_RM(bp) \ 9189019Sps (bp)->hnext->hprev = (bp)->hprev; \ 9289019Sps (bp)->hprev->hnext = (bp)->hnext; 9389019Sps 9489019Sps#define HASH_INS(bp,h) \ 9589019Sps (bp)->hnext = thisfile->hashtbl[h].buf_hnext; \ 9689019Sps (bp)->hprev = END_OF_HCHAIN(h); \ 9789019Sps thisfile->hashtbl[h].buf_hnext->hprev = (bp); \ 9889019Sps thisfile->hashtbl[h].buf_hnext = (bp); 9989019Sps 10060786Spsstatic struct filestate *thisfile; 10160786Spsstatic int ch_ungotchar = -1; 102128345Stjrstatic int maxbufs = -1; 10360786Sps 10460786Spsextern int autobuf; 10560786Spsextern int sigs; 10660786Spsextern int secure; 107173682Sdelphijextern int screen_trashed; 108173682Sdelphijextern int follow_mode; 10960786Spsextern constant char helpdata[]; 11060786Spsextern constant int size_helpdata; 11160786Spsextern IFILE curr_ifile; 11260786Sps#if LOGFILE 11360786Spsextern int logfile; 11460786Spsextern char *namelogfile; 11560786Sps#endif 11660786Sps 11760786Spsstatic int ch_addbuf(); 11860786Sps 11960786Sps 12060786Sps/* 12160786Sps * Get the character pointed to by the read pointer. 12260786Sps * ch_get() is a macro which is more efficient to call 12360786Sps * than fch_get (the function), in the usual case 12460786Sps * that the block desired is at the head of the chain. 12560786Sps */ 12660786Sps#define ch_get() ((ch_block == ch_bufhead->block && \ 12760786Sps ch_offset < ch_bufhead->datasize) ? \ 12860786Sps ch_bufhead->data[ch_offset] : fch_get()) 12960786Sps int 13060786Spsfch_get() 13160786Sps{ 13260786Sps register struct buf *bp; 13360786Sps register int n; 13460786Sps register int slept; 13589019Sps register int h; 13660786Sps POSITION pos; 13760786Sps POSITION len; 13860786Sps 139172468Sdelphij if (thisfile == NULL) 140172468Sdelphij return (EOI); 141172468Sdelphij 14260786Sps slept = FALSE; 14360786Sps 14460786Sps /* 14560786Sps * Look for a buffer holding the desired block. 14660786Sps */ 14789019Sps h = BUFHASH(ch_block); 14889019Sps FOR_BUFS_IN_CHAIN(h, bp) 14989019Sps { 15060786Sps if (bp->block == ch_block) 15160786Sps { 15260786Sps if (ch_offset >= bp->datasize) 15360786Sps /* 15460786Sps * Need more data in this buffer. 15560786Sps */ 15660786Sps goto read_more; 15760786Sps goto found; 15860786Sps } 15989019Sps } 16060786Sps /* 16160786Sps * Block is not in a buffer. 16260786Sps * Take the least recently used buffer 16360786Sps * and read the desired block into it. 16460786Sps * If the LRU buffer has data in it, 16560786Sps * then maybe allocate a new buffer. 16660786Sps */ 16789019Sps if (ch_buftail == END_OF_CHAIN || ch_buftail->block != -1) 16860786Sps { 16960786Sps /* 17060786Sps * There is no empty buffer to use. 17160786Sps * Allocate a new buffer if: 17260786Sps * 1. We can't seek on this file and -b is not in effect; or 17360786Sps * 2. We haven't allocated the max buffers for this file yet. 17460786Sps */ 17560786Sps if ((autobuf && !(ch_flags & CH_CANSEEK)) || 176128345Stjr (maxbufs < 0 || ch_nbufs < maxbufs)) 17760786Sps if (ch_addbuf()) 17860786Sps /* 17960786Sps * Allocation failed: turn off autobuf. 18060786Sps */ 18160786Sps autobuf = OPT_OFF; 18260786Sps } 18360786Sps bp = ch_buftail; 18489019Sps HASH_RM(bp); /* Remove from old hash chain. */ 18560786Sps bp->block = ch_block; 18660786Sps bp->datasize = 0; 18789019Sps HASH_INS(bp, h); /* Insert into new hash chain. */ 18860786Sps 18960786Sps read_more: 19060786Sps pos = (ch_block * LBUFSIZE) + bp->datasize; 19160786Sps if ((len = ch_length()) != NULL_POSITION && pos >= len) 19260786Sps /* 19360786Sps * At end of file. 19460786Sps */ 19560786Sps return (EOI); 19660786Sps 19760786Sps if (pos != ch_fpos) 19860786Sps { 19960786Sps /* 20060786Sps * Not at the correct position: must seek. 20160786Sps * If input is a pipe, we're in trouble (can't seek on a pipe). 20260786Sps * Some data has been lost: just return "?". 20360786Sps */ 20460786Sps if (!(ch_flags & CH_CANSEEK)) 20560786Sps return ('?'); 206173682Sdelphij if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK) 20760786Sps { 20860786Sps error("seek error", NULL_PARG); 20960786Sps clear_eol(); 21060786Sps return (EOI); 21160786Sps } 21260786Sps ch_fpos = pos; 21360786Sps } 21460786Sps 21560786Sps /* 21660786Sps * Read the block. 21760786Sps * If we read less than a full block, that's ok. 21860786Sps * We use partial block and pick up the rest next time. 21960786Sps */ 22060786Sps if (ch_ungotchar != -1) 22160786Sps { 22260786Sps bp->data[bp->datasize] = ch_ungotchar; 22360786Sps n = 1; 22460786Sps ch_ungotchar = -1; 22560786Sps } else if (ch_flags & CH_HELPFILE) 22660786Sps { 22760786Sps bp->data[bp->datasize] = helpdata[ch_fpos]; 22860786Sps n = 1; 22960786Sps } else 23060786Sps { 23160786Sps n = iread(ch_file, &bp->data[bp->datasize], 23260786Sps (unsigned int)(LBUFSIZE - bp->datasize)); 23360786Sps } 23460786Sps 23560786Sps if (n == READ_INTR) 23660786Sps return (EOI); 23760786Sps if (n < 0) 23860786Sps { 23960786Sps#if MSDOS_COMPILER==WIN32C 24060786Sps if (errno != EPIPE) 24160786Sps#endif 24260786Sps { 24360786Sps error("read error", NULL_PARG); 24460786Sps clear_eol(); 24560786Sps } 24660786Sps n = 0; 24760786Sps } 24860786Sps 24960786Sps#if LOGFILE 25060786Sps /* 25160786Sps * If we have a log file, write the new data to it. 25260786Sps */ 25360786Sps if (!secure && logfile >= 0 && n > 0) 25460786Sps write(logfile, (char *) &bp->data[bp->datasize], n); 25560786Sps#endif 25660786Sps 25760786Sps ch_fpos += n; 25860786Sps bp->datasize += n; 25960786Sps 26060786Sps /* 26160786Sps * If we have read to end of file, set ch_fsize to indicate 26260786Sps * the position of the end of file. 26360786Sps */ 26460786Sps if (n == 0) 26560786Sps { 26660786Sps ch_fsize = pos; 26760786Sps if (ignore_eoi) 26860786Sps { 26960786Sps /* 27060786Sps * We are ignoring EOF. 27160786Sps * Wait a while, then try again. 27260786Sps */ 27360786Sps if (!slept) 27489019Sps { 27589019Sps PARG parg; 27689019Sps parg.p_string = wait_message(); 27789019Sps ierror("%s", &parg); 27889019Sps } 27960786Sps#if !MSDOS_COMPILER 28060786Sps sleep(1); 28160786Sps#else 28260786Sps#if MSDOS_COMPILER==WIN32C 28360786Sps Sleep(1000); 28460786Sps#endif 28560786Sps#endif 28660786Sps slept = TRUE; 287173682Sdelphij 288173682Sdelphij#if HAVE_STAT_INO 289173682Sdelphij if (follow_mode == FOLLOW_NAME) 290173682Sdelphij { 291173682Sdelphij /* See whether the file's i-number has changed. 292173682Sdelphij * If so, force the file to be closed and 293173682Sdelphij * reopened. */ 294173682Sdelphij struct stat st; 295173682Sdelphij int r = stat(get_filename(curr_ifile), &st); 296173682Sdelphij if (r == 0 && (st.st_ino != curr_ino || 297173682Sdelphij st.st_dev != curr_dev)) 298173682Sdelphij { 299173682Sdelphij /* screen_trashed=2 causes 300173682Sdelphij * make_display to reopen the file. */ 301173682Sdelphij screen_trashed = 2; 302173682Sdelphij return (EOI); 303173682Sdelphij } 304173682Sdelphij } 305173682Sdelphij#endif 30660786Sps } 30760786Sps if (sigs) 30860786Sps return (EOI); 30960786Sps } 31060786Sps 31160786Sps found: 31260786Sps if (ch_bufhead != bp) 31360786Sps { 31460786Sps /* 31560786Sps * Move the buffer to the head of the buffer chain. 31660786Sps * This orders the buffer chain, most- to least-recently used. 31760786Sps */ 31860786Sps bp->next->prev = bp->prev; 31960786Sps bp->prev->next = bp->next; 32060786Sps bp->next = ch_bufhead; 32160786Sps bp->prev = END_OF_CHAIN; 32260786Sps ch_bufhead->prev = bp; 32360786Sps ch_bufhead = bp; 32489019Sps 32589019Sps /* 32689019Sps * Move to head of hash chain too. 32789019Sps */ 32889019Sps HASH_RM(bp); 32989019Sps HASH_INS(bp, h); 33060786Sps } 33160786Sps 33260786Sps if (ch_offset >= bp->datasize) 33360786Sps /* 33460786Sps * After all that, we still don't have enough data. 33560786Sps * Go back and try again. 33660786Sps */ 33760786Sps goto read_more; 33860786Sps 33960786Sps return (bp->data[ch_offset]); 34060786Sps} 34160786Sps 34260786Sps/* 34360786Sps * ch_ungetchar is a rather kludgy and limited way to push 34460786Sps * a single char onto an input file descriptor. 34560786Sps */ 34660786Sps public void 34760786Spsch_ungetchar(c) 34860786Sps int c; 34960786Sps{ 35060786Sps if (c != -1 && ch_ungotchar != -1) 35160786Sps error("ch_ungetchar overrun", NULL_PARG); 35260786Sps ch_ungotchar = c; 35360786Sps} 35460786Sps 35560786Sps#if LOGFILE 35660786Sps/* 35760786Sps * Close the logfile. 35860786Sps * If we haven't read all of standard input into it, do that now. 35960786Sps */ 36060786Sps public void 36160786Spsend_logfile() 36260786Sps{ 36360786Sps static int tried = FALSE; 36460786Sps 36560786Sps if (logfile < 0) 36660786Sps return; 36760786Sps if (!tried && ch_fsize == NULL_POSITION) 36860786Sps { 36960786Sps tried = TRUE; 37060786Sps ierror("Finishing logfile", NULL_PARG); 37160786Sps while (ch_forw_get() != EOI) 37260786Sps if (ABORT_SIGS()) 37360786Sps break; 37460786Sps } 37560786Sps close(logfile); 37660786Sps logfile = -1; 37760786Sps namelogfile = NULL; 37860786Sps} 37960786Sps 38060786Sps/* 38160786Sps * Start a log file AFTER less has already been running. 38260786Sps * Invoked from the - command; see toggle_option(). 38360786Sps * Write all the existing buffered data to the log file. 38460786Sps */ 38560786Sps public void 38660786Spssync_logfile() 38760786Sps{ 38860786Sps register struct buf *bp; 38960786Sps int warned = FALSE; 39089019Sps BLOCKNUM block; 39189019Sps BLOCKNUM nblocks; 39260786Sps 39360786Sps nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; 39460786Sps for (block = 0; block < nblocks; block++) 39560786Sps { 39660786Sps for (bp = ch_bufhead; ; bp = bp->next) 39760786Sps { 39860786Sps if (bp == END_OF_CHAIN) 39960786Sps { 40060786Sps if (!warned) 40160786Sps { 40260786Sps error("Warning: log file is incomplete", 40360786Sps NULL_PARG); 40460786Sps warned = TRUE; 40560786Sps } 40660786Sps break; 40760786Sps } 40860786Sps if (bp->block == block) 40960786Sps { 41060786Sps write(logfile, (char *) bp->data, bp->datasize); 41160786Sps break; 41260786Sps } 41360786Sps } 41460786Sps } 41560786Sps} 41660786Sps 41760786Sps#endif 41860786Sps 41960786Sps/* 42060786Sps * Determine if a specific block is currently in one of the buffers. 42160786Sps */ 42260786Sps static int 42360786Spsbuffered(block) 42489019Sps BLOCKNUM block; 42560786Sps{ 42660786Sps register struct buf *bp; 42789019Sps register int h; 42860786Sps 42989019Sps h = BUFHASH(block); 43089019Sps FOR_BUFS_IN_CHAIN(h, bp) 43189019Sps { 43260786Sps if (bp->block == block) 43360786Sps return (TRUE); 43489019Sps } 43560786Sps return (FALSE); 43660786Sps} 43760786Sps 43860786Sps/* 43960786Sps * Seek to a specified position in the file. 44060786Sps * Return 0 if successful, non-zero if can't seek there. 44160786Sps */ 44260786Sps public int 44360786Spsch_seek(pos) 44460786Sps register POSITION pos; 44560786Sps{ 44689019Sps BLOCKNUM new_block; 44760786Sps POSITION len; 44860786Sps 449172468Sdelphij if (thisfile == NULL) 450172468Sdelphij return (0); 451172468Sdelphij 45260786Sps len = ch_length(); 45360786Sps if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) 45460786Sps return (1); 45560786Sps 45660786Sps new_block = pos / LBUFSIZE; 45760786Sps if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) 45860786Sps { 45960786Sps if (ch_fpos > pos) 46060786Sps return (1); 46160786Sps while (ch_fpos < pos) 46260786Sps { 46360786Sps if (ch_forw_get() == EOI) 46460786Sps return (1); 46560786Sps if (ABORT_SIGS()) 46660786Sps return (1); 46760786Sps } 46860786Sps return (0); 46960786Sps } 47060786Sps /* 47160786Sps * Set read pointer. 47260786Sps */ 47360786Sps ch_block = new_block; 47460786Sps ch_offset = pos % LBUFSIZE; 47560786Sps return (0); 47660786Sps} 47760786Sps 47860786Sps/* 47960786Sps * Seek to the end of the file. 48060786Sps */ 48160786Sps public int 48260786Spsch_end_seek() 48360786Sps{ 48460786Sps POSITION len; 48560786Sps 486172468Sdelphij if (thisfile == NULL) 487172468Sdelphij return (0); 488172468Sdelphij 48960786Sps if (ch_flags & CH_CANSEEK) 49060786Sps ch_fsize = filesize(ch_file); 49160786Sps 49260786Sps len = ch_length(); 49360786Sps if (len != NULL_POSITION) 49460786Sps return (ch_seek(len)); 49560786Sps 49660786Sps /* 49760786Sps * Do it the slow way: read till end of data. 49860786Sps */ 49960786Sps while (ch_forw_get() != EOI) 50060786Sps if (ABORT_SIGS()) 50160786Sps return (1); 50260786Sps return (0); 50360786Sps} 50460786Sps 50560786Sps/* 50660786Sps * Seek to the beginning of the file, or as close to it as we can get. 50760786Sps * We may not be able to seek there if input is a pipe and the 50860786Sps * beginning of the pipe is no longer buffered. 50960786Sps */ 51060786Sps public int 51160786Spsch_beg_seek() 51260786Sps{ 51360786Sps register struct buf *bp, *firstbp; 51460786Sps 51560786Sps /* 51660786Sps * Try a plain ch_seek first. 51760786Sps */ 51860786Sps if (ch_seek(ch_zero()) == 0) 51960786Sps return (0); 52060786Sps 52160786Sps /* 52260786Sps * Can't get to position 0. 52360786Sps * Look thru the buffers for the one closest to position 0. 52460786Sps */ 52560786Sps firstbp = bp = ch_bufhead; 52660786Sps if (bp == END_OF_CHAIN) 52760786Sps return (1); 52860786Sps while ((bp = bp->next) != END_OF_CHAIN) 52960786Sps if (bp->block < firstbp->block) 53060786Sps firstbp = bp; 53160786Sps ch_block = firstbp->block; 53260786Sps ch_offset = 0; 53360786Sps return (0); 53460786Sps} 53560786Sps 53660786Sps/* 53760786Sps * Return the length of the file, if known. 53860786Sps */ 53960786Sps public POSITION 54060786Spsch_length() 54160786Sps{ 542172468Sdelphij if (thisfile == NULL) 543172468Sdelphij return (NULL_POSITION); 54460786Sps if (ignore_eoi) 54560786Sps return (NULL_POSITION); 54660786Sps if (ch_flags & CH_HELPFILE) 54760786Sps return (size_helpdata); 54860786Sps return (ch_fsize); 54960786Sps} 55060786Sps 55160786Sps/* 55260786Sps * Return the current position in the file. 55360786Sps */ 55460786Sps public POSITION 55560786Spsch_tell() 55660786Sps{ 557172468Sdelphij if (thisfile == NULL) 558172468Sdelphij return (NULL_POSITION); 55989019Sps return (ch_block * LBUFSIZE) + ch_offset; 56060786Sps} 56160786Sps 56260786Sps/* 56360786Sps * Get the current char and post-increment the read pointer. 56460786Sps */ 56560786Sps public int 56660786Spsch_forw_get() 56760786Sps{ 56860786Sps register int c; 56960786Sps 570172468Sdelphij if (thisfile == NULL) 571172468Sdelphij return (EOI); 57260786Sps c = ch_get(); 57360786Sps if (c == EOI) 57460786Sps return (EOI); 57560786Sps if (ch_offset < LBUFSIZE-1) 57660786Sps ch_offset++; 57760786Sps else 57860786Sps { 57960786Sps ch_block ++; 58060786Sps ch_offset = 0; 58160786Sps } 58260786Sps return (c); 58360786Sps} 58460786Sps 58560786Sps/* 58660786Sps * Pre-decrement the read pointer and get the new current char. 58760786Sps */ 58860786Sps public int 58960786Spsch_back_get() 59060786Sps{ 591172468Sdelphij if (thisfile == NULL) 592172468Sdelphij return (EOI); 59360786Sps if (ch_offset > 0) 59460786Sps ch_offset --; 59560786Sps else 59660786Sps { 59760786Sps if (ch_block <= 0) 59860786Sps return (EOI); 59960786Sps if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) 60060786Sps return (EOI); 60160786Sps ch_block--; 60260786Sps ch_offset = LBUFSIZE-1; 60360786Sps } 60460786Sps return (ch_get()); 60560786Sps} 60660786Sps 60760786Sps/* 608128345Stjr * Set max amount of buffer space. 609128345Stjr * bufspace is in units of 1024 bytes. -1 mean no limit. 61060786Sps */ 611128345Stjr public void 612128345Stjrch_setbufspace(bufspace) 613128345Stjr int bufspace; 61460786Sps{ 615128345Stjr if (bufspace < 0) 616128345Stjr maxbufs = -1; 617128345Stjr else 61860786Sps { 619128345Stjr maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; 620128345Stjr if (maxbufs < 1) 621128345Stjr maxbufs = 1; 62260786Sps } 62360786Sps} 62460786Sps 62560786Sps/* 62660786Sps * Flush (discard) any saved file state, including buffer contents. 62760786Sps */ 62860786Sps public void 62960786Spsch_flush() 63060786Sps{ 63160786Sps register struct buf *bp; 63260786Sps 633172468Sdelphij if (thisfile == NULL) 634172468Sdelphij return; 635172468Sdelphij 63660786Sps if (!(ch_flags & CH_CANSEEK)) 63760786Sps { 63860786Sps /* 63960786Sps * If input is a pipe, we don't flush buffer contents, 64060786Sps * since the contents can't be recovered. 64160786Sps */ 64260786Sps ch_fsize = NULL_POSITION; 64360786Sps return; 64460786Sps } 64560786Sps 64660786Sps /* 64760786Sps * Initialize all the buffers. 64860786Sps */ 64960786Sps for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next) 65089019Sps bp->block = -1; 65160786Sps 65260786Sps /* 65360786Sps * Figure out the size of the file, if we can. 65460786Sps */ 65560786Sps ch_fsize = filesize(ch_file); 65660786Sps 65760786Sps /* 65860786Sps * Seek to a known position: the beginning of the file. 65960786Sps */ 66060786Sps ch_fpos = 0; 66160786Sps ch_block = 0; /* ch_fpos / LBUFSIZE; */ 66260786Sps ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 66360786Sps 66460786Sps#if 1 66560786Sps /* 66660786Sps * This is a kludge to workaround a Linux kernel bug: files in 66760786Sps * /proc have a size of 0 according to fstat() but have readable 66860786Sps * data. They are sometimes, but not always, seekable. 66960786Sps * Force them to be non-seekable here. 67060786Sps */ 67160786Sps if (ch_fsize == 0) 67260786Sps { 67360786Sps ch_fsize = NULL_POSITION; 67460786Sps ch_flags &= ~CH_CANSEEK; 67560786Sps } 67660786Sps#endif 67760786Sps 678173682Sdelphij if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) 67960786Sps { 68060786Sps /* 68160786Sps * Warning only; even if the seek fails for some reason, 68260786Sps * there's a good chance we're at the beginning anyway. 68360786Sps * {{ I think this is bogus reasoning. }} 68460786Sps */ 68560786Sps error("seek error to 0", NULL_PARG); 68660786Sps } 68760786Sps} 68860786Sps 68960786Sps/* 69060786Sps * Allocate a new buffer. 69160786Sps * The buffer is added to the tail of the buffer chain. 69260786Sps */ 69360786Sps static int 69460786Spsch_addbuf() 69560786Sps{ 69660786Sps register struct buf *bp; 69760786Sps 69860786Sps /* 69960786Sps * Allocate and initialize a new buffer and link it 70060786Sps * onto the tail of the buffer list. 70160786Sps */ 70260786Sps bp = (struct buf *) calloc(1, sizeof(struct buf)); 70360786Sps if (bp == NULL) 70460786Sps return (1); 70560786Sps ch_nbufs++; 70689019Sps bp->block = -1; 70760786Sps bp->next = END_OF_CHAIN; 70860786Sps bp->prev = ch_buftail; 70960786Sps ch_buftail->next = bp; 71060786Sps ch_buftail = bp; 71189019Sps HASH_INS(bp, 0); 71260786Sps return (0); 71360786Sps} 71460786Sps 71560786Sps/* 71689019Sps * 71789019Sps */ 71889019Sps static void 71989019Spsinit_hashtbl() 72089019Sps{ 72189019Sps register int h; 72289019Sps 72389019Sps for (h = 0; h < BUFHASH_SIZE; h++) 72489019Sps { 72589019Sps thisfile->hashtbl[h].buf_hnext = END_OF_HCHAIN(h); 72689019Sps thisfile->hashtbl[h].buf_hprev = END_OF_HCHAIN(h); 72789019Sps } 72889019Sps} 72989019Sps 73089019Sps/* 73160786Sps * Delete all buffers for this file. 73260786Sps */ 73360786Sps static void 73460786Spsch_delbufs() 73560786Sps{ 73660786Sps register struct buf *bp; 73760786Sps 73860786Sps while (ch_bufhead != END_OF_CHAIN) 73960786Sps { 74060786Sps bp = ch_bufhead; 741173682Sdelphij bp->next->prev = bp->prev; 74260786Sps bp->prev->next = bp->next; 74360786Sps free(bp); 74460786Sps } 74560786Sps ch_nbufs = 0; 74689019Sps init_hashtbl(); 74760786Sps} 74860786Sps 74960786Sps/* 75060786Sps * Is it possible to seek on a file descriptor? 75160786Sps */ 75260786Sps public int 75360786Spsseekable(f) 75460786Sps int f; 75560786Sps{ 75660786Sps#if MSDOS_COMPILER 75760786Sps extern int fd0; 75860786Sps if (f == fd0 && !isatty(fd0)) 75960786Sps { 76060786Sps /* 76160786Sps * In MS-DOS, pipes are seekable. Check for 76260786Sps * standard input, and pretend it is not seekable. 76360786Sps */ 76460786Sps return (0); 76560786Sps } 76660786Sps#endif 767173682Sdelphij return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); 76860786Sps} 76960786Sps 77060786Sps/* 77160786Sps * Initialize file state for a new file. 77260786Sps */ 77360786Sps public void 77460786Spsch_init(f, flags) 77560786Sps int f; 77660786Sps int flags; 77760786Sps{ 77860786Sps /* 77960786Sps * See if we already have a filestate for this file. 78060786Sps */ 78160786Sps thisfile = (struct filestate *) get_filestate(curr_ifile); 78260786Sps if (thisfile == NULL) 78360786Sps { 78460786Sps /* 78560786Sps * Allocate and initialize a new filestate. 78660786Sps */ 78760786Sps thisfile = (struct filestate *) 78860786Sps calloc(1, sizeof(struct filestate)); 78960786Sps thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN; 79060786Sps thisfile->nbufs = 0; 79160786Sps thisfile->flags = 0; 79260786Sps thisfile->fpos = 0; 79360786Sps thisfile->block = 0; 79460786Sps thisfile->offset = 0; 79560786Sps thisfile->file = -1; 79660786Sps thisfile->fsize = NULL_POSITION; 79760786Sps ch_flags = flags; 79889019Sps init_hashtbl(); 79960786Sps /* 80060786Sps * Try to seek; set CH_CANSEEK if it works. 80160786Sps */ 80260786Sps if ((flags & CH_CANSEEK) && !seekable(f)) 80360786Sps ch_flags &= ~CH_CANSEEK; 80460786Sps set_filestate(curr_ifile, (void *) thisfile); 80560786Sps } 80660786Sps if (thisfile->file == -1) 80760786Sps thisfile->file = f; 80860786Sps ch_flush(); 80960786Sps} 81060786Sps 81160786Sps/* 81260786Sps * Close a filestate. 81360786Sps */ 81460786Sps public void 81560786Spsch_close() 81660786Sps{ 81760786Sps int keepstate = FALSE; 81860786Sps 819172468Sdelphij if (thisfile == NULL) 820172468Sdelphij return; 821172468Sdelphij 82260786Sps if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) 82360786Sps { 82460786Sps /* 82560786Sps * We can seek or re-open, so we don't need to keep buffers. 82660786Sps */ 82760786Sps ch_delbufs(); 82860786Sps } else 82960786Sps keepstate = TRUE; 83060786Sps if (!(ch_flags & CH_KEEPOPEN)) 83160786Sps { 83260786Sps /* 83360786Sps * We don't need to keep the file descriptor open 83460786Sps * (because we can re-open it.) 83560786Sps * But don't really close it if it was opened via popen(), 83660786Sps * because pclose() wants to close it. 83760786Sps */ 83860786Sps if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 83960786Sps close(ch_file); 84060786Sps ch_file = -1; 84160786Sps } else 84260786Sps keepstate = TRUE; 84360786Sps if (!keepstate) 84460786Sps { 84560786Sps /* 84660786Sps * We don't even need to keep the filestate structure. 84760786Sps */ 84860786Sps free(thisfile); 84960786Sps thisfile = NULL; 85060786Sps set_filestate(curr_ifile, (void *) NULL); 85160786Sps } 85260786Sps} 85360786Sps 85460786Sps/* 85560786Sps * Return ch_flags for the current file. 85660786Sps */ 85760786Sps public int 85860786Spsch_getflags() 85960786Sps{ 860172468Sdelphij if (thisfile == NULL) 861172468Sdelphij return (0); 86260786Sps return (ch_flags); 86360786Sps} 86460786Sps 86560786Sps#if 0 86660786Sps public void 86760786Spsch_dump(struct filestate *fs) 86860786Sps{ 86960786Sps struct buf *bp; 87060786Sps unsigned char *s; 87160786Sps 87260786Sps if (fs == NULL) 87360786Sps { 87460786Sps printf(" --no filestate\n"); 87560786Sps return; 87660786Sps } 87760786Sps printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 87860786Sps fs->file, fs->flags, fs->fpos, 87960786Sps fs->fsize, fs->block, fs->offset); 88060786Sps printf(" %d bufs:\n", fs->nbufs); 88160786Sps for (bp = fs->buf_next; bp != (struct buf *)fs; bp = bp->next) 88260786Sps { 88360786Sps printf("%x: blk %x, size %x \"", 88460786Sps bp, bp->block, bp->datasize); 88560786Sps for (s = bp->data; s < bp->data + 30; s++) 88660786Sps if (*s >= ' ' && *s < 0x7F) 88760786Sps printf("%c", *s); 88860786Sps else 88960786Sps printf("."); 89060786Sps printf("\"\n"); 89160786Sps } 89260786Sps} 89360786Sps#endif 894