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