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