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