160786Sps/*
2240121Sdelphij * Copyright (C) 1984-2012  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 *
7240121Sdelphij * For more information, see the README file.
860786Sps */
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 */
5789019Sps#define	BUFHASH_SIZE	64
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{
14960786Sps	register struct buf *bp;
150191930Sdelphij	register struct bufnode *bn;
15160786Sps	register int n;
15260786Sps	register int slept;
15389019Sps	register 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			{
326173682Sdelphij				/* See whether the file's i-number has changed.
327173682Sdelphij				 * If so, force the file to be closed and
328173682Sdelphij				 * reopened. */
329173682Sdelphij				struct stat st;
330173682Sdelphij				int r = stat(get_filename(curr_ifile), &st);
331173682Sdelphij				if (r == 0 && (st.st_ino != curr_ino ||
332173682Sdelphij					st.st_dev != curr_dev))
333173682Sdelphij				{
334173682Sdelphij					/* screen_trashed=2 causes
335173682Sdelphij					 * make_display to reopen the file. */
336173682Sdelphij					screen_trashed = 2;
337173682Sdelphij					return (EOI);
338173682Sdelphij				}
339173682Sdelphij			}
340173682Sdelphij#endif
34160786Sps		}
34260786Sps		if (sigs)
34360786Sps			return (EOI);
34460786Sps	}
34560786Sps
34660786Sps    found:
347191930Sdelphij	if (ch_bufhead != bn)
34860786Sps	{
34960786Sps		/*
35060786Sps		 * Move the buffer to the head of the buffer chain.
35160786Sps		 * This orders the buffer chain, most- to least-recently used.
35260786Sps		 */
353191930Sdelphij		BUF_RM(bn);
354191930Sdelphij		BUF_INS_HEAD(bn);
35589019Sps
35689019Sps		/*
35789019Sps		 * Move to head of hash chain too.
35889019Sps		 */
359191930Sdelphij		BUF_HASH_RM(bn);
360191930Sdelphij		BUF_HASH_INS(bn, h);
36160786Sps	}
36260786Sps
36360786Sps	if (ch_offset >= bp->datasize)
36460786Sps		/*
36560786Sps		 * After all that, we still don't have enough data.
36660786Sps		 * Go back and try again.
36760786Sps		 */
36860786Sps		goto read_more;
36960786Sps
37060786Sps	return (bp->data[ch_offset]);
37160786Sps}
37260786Sps
37360786Sps/*
37460786Sps * ch_ungetchar is a rather kludgy and limited way to push
37560786Sps * a single char onto an input file descriptor.
37660786Sps */
37760786Sps	public void
37860786Spsch_ungetchar(c)
37960786Sps	int c;
38060786Sps{
38160786Sps	if (c != -1 && ch_ungotchar != -1)
38260786Sps		error("ch_ungetchar overrun", NULL_PARG);
38360786Sps	ch_ungotchar = c;
38460786Sps}
38560786Sps
38660786Sps#if LOGFILE
38760786Sps/*
38860786Sps * Close the logfile.
38960786Sps * If we haven't read all of standard input into it, do that now.
39060786Sps */
39160786Sps	public void
39260786Spsend_logfile()
39360786Sps{
39460786Sps	static int tried = FALSE;
39560786Sps
39660786Sps	if (logfile < 0)
39760786Sps		return;
39860786Sps	if (!tried && ch_fsize == NULL_POSITION)
39960786Sps	{
40060786Sps		tried = TRUE;
40160786Sps		ierror("Finishing logfile", NULL_PARG);
40260786Sps		while (ch_forw_get() != EOI)
40360786Sps			if (ABORT_SIGS())
40460786Sps				break;
40560786Sps	}
40660786Sps	close(logfile);
40760786Sps	logfile = -1;
40860786Sps	namelogfile = NULL;
40960786Sps}
41060786Sps
41160786Sps/*
41260786Sps * Start a log file AFTER less has already been running.
41360786Sps * Invoked from the - command; see toggle_option().
41460786Sps * Write all the existing buffered data to the log file.
41560786Sps */
41660786Sps	public void
41760786Spssync_logfile()
41860786Sps{
41960786Sps	register struct buf *bp;
420191930Sdelphij	register struct bufnode *bn;
42160786Sps	int warned = FALSE;
42289019Sps	BLOCKNUM block;
42389019Sps	BLOCKNUM nblocks;
42460786Sps
42560786Sps	nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
42660786Sps	for (block = 0;  block < nblocks;  block++)
42760786Sps	{
428191930Sdelphij		int wrote = FALSE;
429191930Sdelphij		FOR_BUFS(bn)
43060786Sps		{
431191930Sdelphij			bp = bufnode_buf(bn);
43260786Sps			if (bp->block == block)
43360786Sps			{
43460786Sps				write(logfile, (char *) bp->data, bp->datasize);
435191930Sdelphij				wrote = TRUE;
43660786Sps				break;
43760786Sps			}
43860786Sps		}
439191930Sdelphij		if (!wrote && !warned)
440191930Sdelphij		{
441191930Sdelphij			error("Warning: log file is incomplete",
442191930Sdelphij				NULL_PARG);
443191930Sdelphij			warned = TRUE;
444191930Sdelphij		}
44560786Sps	}
44660786Sps}
44760786Sps
44860786Sps#endif
44960786Sps
45060786Sps/*
45160786Sps * Determine if a specific block is currently in one of the buffers.
45260786Sps */
45360786Sps	static int
45460786Spsbuffered(block)
45589019Sps	BLOCKNUM block;
45660786Sps{
45760786Sps	register struct buf *bp;
458191930Sdelphij	register struct bufnode *bn;
45989019Sps	register int h;
46060786Sps
46189019Sps	h = BUFHASH(block);
462191930Sdelphij	FOR_BUFS_IN_CHAIN(h, bn)
46389019Sps	{
464191930Sdelphij		bp = bufnode_buf(bn);
46560786Sps		if (bp->block == block)
46660786Sps			return (TRUE);
46789019Sps	}
46860786Sps	return (FALSE);
46960786Sps}
47060786Sps
47160786Sps/*
47260786Sps * Seek to a specified position in the file.
47360786Sps * Return 0 if successful, non-zero if can't seek there.
47460786Sps */
47560786Sps	public int
47660786Spsch_seek(pos)
47760786Sps	register POSITION pos;
47860786Sps{
47989019Sps	BLOCKNUM new_block;
48060786Sps	POSITION len;
48160786Sps
482172468Sdelphij	if (thisfile == NULL)
483172468Sdelphij		return (0);
484172468Sdelphij
48560786Sps	len = ch_length();
48660786Sps	if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
48760786Sps		return (1);
48860786Sps
48960786Sps	new_block = pos / LBUFSIZE;
49060786Sps	if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
49160786Sps	{
49260786Sps		if (ch_fpos > pos)
49360786Sps			return (1);
49460786Sps		while (ch_fpos < pos)
49560786Sps		{
49660786Sps			if (ch_forw_get() == EOI)
49760786Sps				return (1);
49860786Sps			if (ABORT_SIGS())
49960786Sps				return (1);
50060786Sps		}
50160786Sps		return (0);
50260786Sps	}
50360786Sps	/*
50460786Sps	 * Set read pointer.
50560786Sps	 */
50660786Sps	ch_block = new_block;
50760786Sps	ch_offset = pos % LBUFSIZE;
50860786Sps	return (0);
50960786Sps}
51060786Sps
51160786Sps/*
51260786Sps * Seek to the end of the file.
51360786Sps */
51460786Sps	public int
51560786Spsch_end_seek()
51660786Sps{
51760786Sps	POSITION len;
51860786Sps
519172468Sdelphij	if (thisfile == NULL)
520172468Sdelphij		return (0);
521172468Sdelphij
52260786Sps	if (ch_flags & CH_CANSEEK)
52360786Sps		ch_fsize = filesize(ch_file);
52460786Sps
52560786Sps	len = ch_length();
52660786Sps	if (len != NULL_POSITION)
52760786Sps		return (ch_seek(len));
52860786Sps
52960786Sps	/*
53060786Sps	 * Do it the slow way: read till end of data.
53160786Sps	 */
53260786Sps	while (ch_forw_get() != EOI)
53360786Sps		if (ABORT_SIGS())
53460786Sps			return (1);
53560786Sps	return (0);
53660786Sps}
53760786Sps
53860786Sps/*
53960786Sps * Seek to the beginning of the file, or as close to it as we can get.
54060786Sps * We may not be able to seek there if input is a pipe and the
54160786Sps * beginning of the pipe is no longer buffered.
54260786Sps */
54360786Sps	public int
54460786Spsch_beg_seek()
54560786Sps{
546191930Sdelphij	register struct bufnode *bn;
547191930Sdelphij	register struct bufnode *firstbn;
54860786Sps
54960786Sps	/*
55060786Sps	 * Try a plain ch_seek first.
55160786Sps	 */
55260786Sps	if (ch_seek(ch_zero()) == 0)
55360786Sps		return (0);
55460786Sps
55560786Sps	/*
55660786Sps	 * Can't get to position 0.
55760786Sps	 * Look thru the buffers for the one closest to position 0.
55860786Sps	 */
559191930Sdelphij	firstbn = ch_bufhead;
560191930Sdelphij	if (firstbn == END_OF_CHAIN)
56160786Sps		return (1);
562191930Sdelphij	FOR_BUFS(bn)
563191930Sdelphij	{
564191930Sdelphij		if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block)
565191930Sdelphij			firstbn = bn;
566191930Sdelphij	}
567191930Sdelphij	ch_block = bufnode_buf(firstbn)->block;
56860786Sps	ch_offset = 0;
56960786Sps	return (0);
57060786Sps}
57160786Sps
57260786Sps/*
57360786Sps * Return the length of the file, if known.
57460786Sps */
57560786Sps	public POSITION
57660786Spsch_length()
57760786Sps{
578172468Sdelphij	if (thisfile == NULL)
579172468Sdelphij		return (NULL_POSITION);
58060786Sps	if (ignore_eoi)
58160786Sps		return (NULL_POSITION);
58260786Sps	if (ch_flags & CH_HELPFILE)
58360786Sps		return (size_helpdata);
584240121Sdelphij	if (ch_flags & CH_NODATA)
585240121Sdelphij		return (0);
58660786Sps	return (ch_fsize);
58760786Sps}
58860786Sps
58960786Sps/*
59060786Sps * Return the current position in the file.
59160786Sps */
59260786Sps	public POSITION
59360786Spsch_tell()
59460786Sps{
595172468Sdelphij	if (thisfile == NULL)
596172468Sdelphij		return (NULL_POSITION);
59789019Sps	return (ch_block * LBUFSIZE) + ch_offset;
59860786Sps}
59960786Sps
60060786Sps/*
60160786Sps * Get the current char and post-increment the read pointer.
60260786Sps */
60360786Sps	public int
60460786Spsch_forw_get()
60560786Sps{
60660786Sps	register int c;
60760786Sps
608172468Sdelphij	if (thisfile == NULL)
609172468Sdelphij		return (EOI);
61060786Sps	c = ch_get();
61160786Sps	if (c == EOI)
61260786Sps		return (EOI);
61360786Sps	if (ch_offset < LBUFSIZE-1)
61460786Sps		ch_offset++;
61560786Sps	else
61660786Sps	{
61760786Sps		ch_block ++;
61860786Sps		ch_offset = 0;
61960786Sps	}
62060786Sps	return (c);
62160786Sps}
62260786Sps
62360786Sps/*
62460786Sps * Pre-decrement the read pointer and get the new current char.
62560786Sps */
62660786Sps	public int
62760786Spsch_back_get()
62860786Sps{
629172468Sdelphij	if (thisfile == NULL)
630172468Sdelphij		return (EOI);
63160786Sps	if (ch_offset > 0)
63260786Sps		ch_offset --;
63360786Sps	else
63460786Sps	{
63560786Sps		if (ch_block <= 0)
63660786Sps			return (EOI);
63760786Sps		if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
63860786Sps			return (EOI);
63960786Sps		ch_block--;
64060786Sps		ch_offset = LBUFSIZE-1;
64160786Sps	}
64260786Sps	return (ch_get());
64360786Sps}
64460786Sps
64560786Sps/*
646128345Stjr * Set max amount of buffer space.
647128345Stjr * bufspace is in units of 1024 bytes.  -1 mean no limit.
64860786Sps */
649128345Stjr	public void
650128345Stjrch_setbufspace(bufspace)
651128345Stjr	int bufspace;
65260786Sps{
653128345Stjr	if (bufspace < 0)
654128345Stjr		maxbufs = -1;
655128345Stjr	else
65660786Sps	{
657128345Stjr		maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE;
658128345Stjr		if (maxbufs < 1)
659128345Stjr			maxbufs = 1;
66060786Sps	}
66160786Sps}
66260786Sps
66360786Sps/*
66460786Sps * Flush (discard) any saved file state, including buffer contents.
66560786Sps */
66660786Sps	public void
66760786Spsch_flush()
66860786Sps{
669191930Sdelphij	register struct bufnode *bn;
67060786Sps
671172468Sdelphij	if (thisfile == NULL)
672172468Sdelphij		return;
673172468Sdelphij
67460786Sps	if (!(ch_flags & CH_CANSEEK))
67560786Sps	{
67660786Sps		/*
67760786Sps		 * If input is a pipe, we don't flush buffer contents,
67860786Sps		 * since the contents can't be recovered.
67960786Sps		 */
68060786Sps		ch_fsize = NULL_POSITION;
68160786Sps		return;
68260786Sps	}
68360786Sps
68460786Sps	/*
68560786Sps	 * Initialize all the buffers.
68660786Sps	 */
687191930Sdelphij	FOR_BUFS(bn)
688191930Sdelphij	{
689191930Sdelphij		bufnode_buf(bn)->block = -1;
690191930Sdelphij	}
69160786Sps
69260786Sps	/*
69360786Sps	 * Figure out the size of the file, if we can.
69460786Sps	 */
69560786Sps	ch_fsize = filesize(ch_file);
69660786Sps
69760786Sps	/*
69860786Sps	 * Seek to a known position: the beginning of the file.
69960786Sps	 */
70060786Sps	ch_fpos = 0;
70160786Sps	ch_block = 0; /* ch_fpos / LBUFSIZE; */
70260786Sps	ch_offset = 0; /* ch_fpos % LBUFSIZE; */
70360786Sps
70460786Sps#if 1
70560786Sps	/*
70660786Sps	 * This is a kludge to workaround a Linux kernel bug: files in
70760786Sps	 * /proc have a size of 0 according to fstat() but have readable
70860786Sps	 * data.  They are sometimes, but not always, seekable.
70960786Sps	 * Force them to be non-seekable here.
71060786Sps	 */
71160786Sps	if (ch_fsize == 0)
71260786Sps	{
71360786Sps		ch_fsize = NULL_POSITION;
71460786Sps		ch_flags &= ~CH_CANSEEK;
71560786Sps	}
71660786Sps#endif
71760786Sps
718173682Sdelphij	if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK)
71960786Sps	{
72060786Sps		/*
72160786Sps		 * Warning only; even if the seek fails for some reason,
72260786Sps		 * there's a good chance we're at the beginning anyway.
72360786Sps		 * {{ I think this is bogus reasoning. }}
72460786Sps		 */
72560786Sps		error("seek error to 0", NULL_PARG);
72660786Sps	}
72760786Sps}
72860786Sps
72960786Sps/*
73060786Sps * Allocate a new buffer.
73160786Sps * The buffer is added to the tail of the buffer chain.
73260786Sps */
73360786Sps	static int
73460786Spsch_addbuf()
73560786Sps{
73660786Sps	register struct buf *bp;
737191930Sdelphij	register struct bufnode *bn;
73860786Sps
73960786Sps	/*
74060786Sps	 * Allocate and initialize a new buffer and link it
74160786Sps	 * onto the tail of the buffer list.
74260786Sps	 */
74360786Sps	bp = (struct buf *) calloc(1, sizeof(struct buf));
74460786Sps	if (bp == NULL)
74560786Sps		return (1);
74660786Sps	ch_nbufs++;
74789019Sps	bp->block = -1;
748191930Sdelphij	bn = &bp->node;
749191930Sdelphij
750191930Sdelphij	BUF_INS_TAIL(bn);
751191930Sdelphij	BUF_HASH_INS(bn, 0);
75260786Sps	return (0);
75360786Sps}
75460786Sps
75560786Sps/*
75689019Sps *
75789019Sps */
75889019Sps	static void
75989019Spsinit_hashtbl()
76089019Sps{
76189019Sps	register int h;
76289019Sps
76389019Sps	for (h = 0;  h < BUFHASH_SIZE;  h++)
76489019Sps	{
765191930Sdelphij		thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
766191930Sdelphij		thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
76789019Sps	}
76889019Sps}
76989019Sps
77089019Sps/*
77160786Sps * Delete all buffers for this file.
77260786Sps */
77360786Sps	static void
77460786Spsch_delbufs()
77560786Sps{
776191930Sdelphij	register struct bufnode *bn;
77760786Sps
77860786Sps	while (ch_bufhead != END_OF_CHAIN)
77960786Sps	{
780191930Sdelphij		bn = ch_bufhead;
781191930Sdelphij		BUF_RM(bn);
782191930Sdelphij		free(bufnode_buf(bn));
78360786Sps	}
78460786Sps	ch_nbufs = 0;
78589019Sps	init_hashtbl();
78660786Sps}
78760786Sps
78860786Sps/*
78960786Sps * Is it possible to seek on a file descriptor?
79060786Sps */
79160786Sps	public int
79260786Spsseekable(f)
79360786Sps	int f;
79460786Sps{
79560786Sps#if MSDOS_COMPILER
79660786Sps	extern int fd0;
79760786Sps	if (f == fd0 && !isatty(fd0))
79860786Sps	{
79960786Sps		/*
80060786Sps		 * In MS-DOS, pipes are seekable.  Check for
80160786Sps		 * standard input, and pretend it is not seekable.
80260786Sps		 */
80360786Sps		return (0);
80460786Sps	}
80560786Sps#endif
806173682Sdelphij	return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK);
80760786Sps}
80860786Sps
80960786Sps/*
810240121Sdelphij * Force EOF to be at the current read position.
811240121Sdelphij * This is used after an ignore_eof read, during which the EOF may change.
812240121Sdelphij */
813240121Sdelphij	public void
814240121Sdelphijch_set_eof()
815240121Sdelphij{
816240121Sdelphij	ch_fsize = ch_fpos;
817240121Sdelphij}
818240121Sdelphij
819240121Sdelphij
820240121Sdelphij/*
82160786Sps * Initialize file state for a new file.
82260786Sps */
82360786Sps	public void
82460786Spsch_init(f, flags)
82560786Sps	int f;
82660786Sps	int flags;
82760786Sps{
82860786Sps	/*
82960786Sps	 * See if we already have a filestate for this file.
83060786Sps	 */
83160786Sps	thisfile = (struct filestate *) get_filestate(curr_ifile);
83260786Sps	if (thisfile == NULL)
83360786Sps	{
83460786Sps		/*
83560786Sps		 * Allocate and initialize a new filestate.
83660786Sps		 */
83760786Sps		thisfile = (struct filestate *)
83860786Sps				calloc(1, sizeof(struct filestate));
839191930Sdelphij		thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
84060786Sps		thisfile->nbufs = 0;
84160786Sps		thisfile->flags = 0;
84260786Sps		thisfile->fpos = 0;
84360786Sps		thisfile->block = 0;
84460786Sps		thisfile->offset = 0;
84560786Sps		thisfile->file = -1;
84660786Sps		thisfile->fsize = NULL_POSITION;
84760786Sps		ch_flags = flags;
84889019Sps		init_hashtbl();
84960786Sps		/*
85060786Sps		 * Try to seek; set CH_CANSEEK if it works.
85160786Sps		 */
85260786Sps		if ((flags & CH_CANSEEK) && !seekable(f))
85360786Sps			ch_flags &= ~CH_CANSEEK;
85460786Sps		set_filestate(curr_ifile, (void *) thisfile);
85560786Sps	}
85660786Sps	if (thisfile->file == -1)
85760786Sps		thisfile->file = f;
85860786Sps	ch_flush();
85960786Sps}
86060786Sps
86160786Sps/*
86260786Sps * Close a filestate.
86360786Sps */
86460786Sps	public void
86560786Spsch_close()
86660786Sps{
86760786Sps	int keepstate = FALSE;
86860786Sps
869172468Sdelphij	if (thisfile == NULL)
870172468Sdelphij		return;
871172468Sdelphij
87260786Sps	if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE))
87360786Sps	{
87460786Sps		/*
87560786Sps		 * We can seek or re-open, so we don't need to keep buffers.
87660786Sps		 */
87760786Sps		ch_delbufs();
87860786Sps	} else
87960786Sps		keepstate = TRUE;
88060786Sps	if (!(ch_flags & CH_KEEPOPEN))
88160786Sps	{
88260786Sps		/*
88360786Sps		 * We don't need to keep the file descriptor open
88460786Sps		 * (because we can re-open it.)
88560786Sps		 * But don't really close it if it was opened via popen(),
88660786Sps		 * because pclose() wants to close it.
88760786Sps		 */
88860786Sps		if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
88960786Sps			close(ch_file);
89060786Sps		ch_file = -1;
89160786Sps	} else
89260786Sps		keepstate = TRUE;
89360786Sps	if (!keepstate)
89460786Sps	{
89560786Sps		/*
89660786Sps		 * We don't even need to keep the filestate structure.
89760786Sps		 */
89860786Sps		free(thisfile);
89960786Sps		thisfile = NULL;
90060786Sps		set_filestate(curr_ifile, (void *) NULL);
90160786Sps	}
90260786Sps}
90360786Sps
90460786Sps/*
90560786Sps * Return ch_flags for the current file.
90660786Sps */
90760786Sps	public int
90860786Spsch_getflags()
90960786Sps{
910172468Sdelphij	if (thisfile == NULL)
911172468Sdelphij		return (0);
91260786Sps	return (ch_flags);
91360786Sps}
91460786Sps
91560786Sps#if 0
91660786Sps	public void
91760786Spsch_dump(struct filestate *fs)
91860786Sps{
91960786Sps	struct buf *bp;
920191930Sdelphij	struct bufnode *bn;
92160786Sps	unsigned char *s;
92260786Sps
92360786Sps	if (fs == NULL)
92460786Sps	{
92560786Sps		printf(" --no filestate\n");
92660786Sps		return;
92760786Sps	}
92860786Sps	printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
92960786Sps		fs->file, fs->flags, fs->fpos,
93060786Sps		fs->fsize, fs->block, fs->offset);
93160786Sps	printf(" %d bufs:\n", fs->nbufs);
932191930Sdelphij	for (bn = fs->next; bn != &fs->buflist;  bn = bn->next)
93360786Sps	{
934191930Sdelphij		bp = bufnode_buf(bn);
93560786Sps		printf("%x: blk %x, size %x \"",
93660786Sps			bp, bp->block, bp->datasize);
93760786Sps		for (s = bp->data;  s < bp->data + 30;  s++)
93860786Sps			if (*s >= ' ' && *s < 0x7F)
93960786Sps				printf("%c", *s);
94060786Sps			else
94160786Sps				printf(".");
94260786Sps		printf("\"\n");
94360786Sps	}
94460786Sps}
94560786Sps#endif
946