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