ch.c revision 60786
1118611Snjl/*
2118611Snjl * Copyright (C) 1984-2000  Mark Nudelman
3118611Snjl *
4118611Snjl * You may distribute under the terms of either the GNU General Public
5118611Snjl * License or the Less License, as specified in the README file.
6118611Snjl *
7217365Sjkim * For more information about less, or for information on how to
8217365Sjkim * contact the author, see the README file.
9118611Snjl */
10118611Snjl
11217365Sjkim
12217365Sjkim/*
13217365Sjkim * Low level character input from the input file.
14217365Sjkim * We use these special purpose routines which optimize moving
15217365Sjkim * both forward and backward from the current read pointer.
16217365Sjkim */
17217365Sjkim
18217365Sjkim#include "less.h"
19217365Sjkim#if MSDOS_COMPILER==WIN32C
20217365Sjkim#include <errno.h>
21217365Sjkim#include <windows.h>
22217365Sjkim#endif
23217365Sjkim
24217365Sjkimpublic int ignore_eoi;
25118611Snjl
26217365Sjkim/*
27217365Sjkim * Pool of buffers holding the most recently used blocks of the input file.
28217365Sjkim * The buffer pool is kept as a doubly-linked circular list,
29118611Snjl * in order from most- to least-recently used.
30217365Sjkim * The circular list is anchored by the file state "thisfile".
31217365Sjkim */
32217365Sjkim#define LBUFSIZE	1024
33217365Sjkimstruct buf {
34217365Sjkim	struct buf *next, *prev;  /* Must be first to match struct filestate */
35217365Sjkim	long block;
36217365Sjkim	unsigned int datasize;
37217365Sjkim	unsigned char data[LBUFSIZE];
38217365Sjkim};
39217365Sjkim
40217365Sjkim/*
41217365Sjkim * The file state is maintained in a filestate structure.
42217365Sjkim * A pointer to the filestate is kept in the ifile structure.
43118611Snjl */
44118611Snjlstruct filestate {
45151937Sjkim	/* -- Following members must match struct buf */
46118611Snjl	struct buf *buf_next, *buf_prev;
47118611Snjl	long buf_block;
48193529Sjkim	/* -- End of struct buf copy */
49193529Sjkim	int file;
50193529Sjkim	int flags;
51193529Sjkim	POSITION fpos;
52118611Snjl	int nbufs;
53118611Snjl	long block;
54118611Snjl	unsigned int offset;
55118611Snjl	POSITION fsize;
56118611Snjl};
57151937Sjkim
58118611Snjl
59151937Sjkim#define	END_OF_CHAIN	((struct buf *)thisfile)
60151937Sjkim#define	ch_bufhead	thisfile->buf_next
61151937Sjkim#define	ch_buftail	thisfile->buf_prev
62151937Sjkim#define	ch_nbufs	thisfile->nbufs
63151937Sjkim#define	ch_block	thisfile->block
64151937Sjkim#define	ch_offset	thisfile->offset
65151937Sjkim#define	ch_fpos		thisfile->fpos
66151937Sjkim#define	ch_fsize	thisfile->fsize
67151937Sjkim#define	ch_flags	thisfile->flags
68151937Sjkim#define	ch_file		thisfile->file
69151937Sjkim
70151937Sjkimstatic struct filestate *thisfile;
71151937Sjkimstatic int ch_ungotchar = -1;
72151937Sjkim
73151937Sjkimextern int autobuf;
74151937Sjkimextern int sigs;
75151937Sjkimextern int cbufs;
76151937Sjkimextern int secure;
77151937Sjkimextern constant char helpdata[];
78151937Sjkimextern constant int size_helpdata;
79151937Sjkimextern IFILE curr_ifile;
80151937Sjkim#if LOGFILE
81151937Sjkimextern int logfile;
82151937Sjkimextern char *namelogfile;
83151937Sjkim#endif
84151937Sjkim
85151937Sjkimstatic int ch_addbuf();
86151937Sjkim
87151937Sjkim
88151937Sjkim/*
89151937Sjkim * Get the character pointed to by the read pointer.
90151937Sjkim * ch_get() is a macro which is more efficient to call
91151937Sjkim * than fch_get (the function), in the usual case
92151937Sjkim * that the block desired is at the head of the chain.
93151937Sjkim */
94151937Sjkim#define	ch_get()   ((ch_block == ch_bufhead->block && \
95151937Sjkim		     ch_offset < ch_bufhead->datasize) ? \
96151937Sjkim			ch_bufhead->data[ch_offset] : fch_get())
97167802Sjkim	int
98167802Sjkimfch_get()
99167802Sjkim{
100167802Sjkim	register struct buf *bp;
101167802Sjkim	register int n;
102167802Sjkim	register int slept;
103151937Sjkim	POSITION pos;
104193529Sjkim	POSITION len;
105193529Sjkim
106193529Sjkim	slept = FALSE;
107193529Sjkim
108193529Sjkim	/*
109193529Sjkim	 * Look for a buffer holding the desired block.
110167802Sjkim	 */
111212761Sjkim	for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
112193529Sjkim		if (bp->block == ch_block)
113193529Sjkim		{
114193529Sjkim			if (ch_offset >= bp->datasize)
115193529Sjkim				/*
116118611Snjl				 * Need more data in this buffer.
117118611Snjl				 */
118118611Snjl				goto read_more;
119118611Snjl			goto found;
120118611Snjl		}
121118611Snjl	/*
122118611Snjl	 * Block is not in a buffer.
123118611Snjl	 * Take the least recently used buffer
124118611Snjl	 * and read the desired block into it.
125118611Snjl	 * If the LRU buffer has data in it,
126118611Snjl	 * then maybe allocate a new buffer.
127118611Snjl	 */
128118611Snjl	if (ch_buftail == END_OF_CHAIN || ch_buftail->block != (long)(-1))
129151937Sjkim	{
130118611Snjl		/*
131118611Snjl		 * There is no empty buffer to use.
132118611Snjl		 * Allocate a new buffer if:
133118611Snjl		 * 1. We can't seek on this file and -b is not in effect; or
134118611Snjl		 * 2. We haven't allocated the max buffers for this file yet.
135118611Snjl		 */
136118611Snjl		if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
137151937Sjkim		    (cbufs == -1 || ch_nbufs < cbufs))
138118611Snjl			if (ch_addbuf())
139118611Snjl				/*
140118611Snjl				 * Allocation failed: turn off autobuf.
141118611Snjl				 */
142118611Snjl				autobuf = OPT_OFF;
143209746Sjkim	}
144167802Sjkim	bp = ch_buftail;
145167802Sjkim	bp->block = ch_block;
146167802Sjkim	bp->datasize = 0;
147118611Snjl
148151937Sjkim    read_more:
149151937Sjkim	pos = (ch_block * LBUFSIZE) + bp->datasize;
150118611Snjl	if ((len = ch_length()) != NULL_POSITION && pos >= len)
151151937Sjkim		/*
152118611Snjl		 * At end of file.
153151937Sjkim		 */
154151937Sjkim		return (EOI);
155151937Sjkim
156151937Sjkim	if (pos != ch_fpos)
157151937Sjkim	{
158151937Sjkim		/*
159167802Sjkim		 * Not at the correct position: must seek.
160151937Sjkim		 * If input is a pipe, we're in trouble (can't seek on a pipe).
161151937Sjkim		 * Some data has been lost: just return "?".
162118611Snjl		 */
163151937Sjkim		if (!(ch_flags & CH_CANSEEK))
164151937Sjkim			return ('?');
165151937Sjkim		if (lseek(ch_file, (off_t)pos, 0) == BAD_LSEEK)
166151937Sjkim		{
167151937Sjkim 			error("seek error", NULL_PARG);
168151937Sjkim			clear_eol();
169151937Sjkim			return (EOI);
170151937Sjkim 		}
171151937Sjkim 		ch_fpos = pos;
172151937Sjkim 	}
173151937Sjkim
174151937Sjkim	/*
175151937Sjkim	 * Read the block.
176151937Sjkim	 * If we read less than a full block, that's ok.
177151937Sjkim	 * We use partial block and pick up the rest next time.
178151937Sjkim	 */
179151937Sjkim	if (ch_ungotchar != -1)
180151937Sjkim	{
181118611Snjl		bp->data[bp->datasize] = ch_ungotchar;
182118611Snjl		n = 1;
183151937Sjkim		ch_ungotchar = -1;
184151937Sjkim	} else if (ch_flags & CH_HELPFILE)
185151937Sjkim	{
186118611Snjl		bp->data[bp->datasize] = helpdata[ch_fpos];
187118611Snjl		n = 1;
188118611Snjl	} else
189118611Snjl	{
190151937Sjkim		n = iread(ch_file, &bp->data[bp->datasize],
191151937Sjkim			(unsigned int)(LBUFSIZE - bp->datasize));
192151937Sjkim	}
193151937Sjkim
194118611Snjl	if (n == READ_INTR)
195118611Snjl		return (EOI);
196118611Snjl	if (n < 0)
197118611Snjl	{
198118611Snjl#if MSDOS_COMPILER==WIN32C
199151937Sjkim		if (errno != EPIPE)
200151937Sjkim#endif
201151937Sjkim		{
202151937Sjkim			error("read error", NULL_PARG);
203118611Snjl			clear_eol();
204151937Sjkim		}
205151937Sjkim		n = 0;
206151937Sjkim	}
207151937Sjkim
208118611Snjl#if LOGFILE
209151937Sjkim	/*
210118611Snjl	 * If we have a log file, write the new data to it.
211151937Sjkim	 */
212151937Sjkim	if (!secure && logfile >= 0 && n > 0)
213118611Snjl		write(logfile, (char *) &bp->data[bp->datasize], n);
214151937Sjkim#endif
215118611Snjl
216151937Sjkim	ch_fpos += n;
217151937Sjkim	bp->datasize += n;
218151937Sjkim
219118611Snjl	/*
220118611Snjl	 * If we have read to end of file, set ch_fsize to indicate
221118611Snjl	 * the position of the end of file.
222151937Sjkim	 */
223118611Snjl	if (n == 0)
224118611Snjl	{
225118611Snjl		ch_fsize = pos;
226118611Snjl		if (ignore_eoi)
227151937Sjkim		{
228118611Snjl			/*
229151937Sjkim			 * We are ignoring EOF.
230151937Sjkim			 * Wait a while, then try again.
231151937Sjkim			 */
232151937Sjkim			if (!slept)
233118611Snjl				ierror("Waiting for data", NULL_PARG);
234151937Sjkim#if !MSDOS_COMPILER
235151937Sjkim	 		sleep(1);
236151937Sjkim#else
237151937Sjkim#if MSDOS_COMPILER==WIN32C
238151937Sjkim			Sleep(1000);
239151937Sjkim#endif
240151937Sjkim#endif
241151937Sjkim			slept = TRUE;
242151937Sjkim		}
243151937Sjkim		if (sigs)
244151937Sjkim			return (EOI);
245151937Sjkim	}
246151937Sjkim
247151937Sjkim    found:
248151937Sjkim	if (ch_bufhead != bp)
249151937Sjkim	{
250151937Sjkim		/*
251151937Sjkim		 * Move the buffer to the head of the buffer chain.
252151937Sjkim		 * This orders the buffer chain, most- to least-recently used.
253151937Sjkim		 */
254151937Sjkim		bp->next->prev = bp->prev;
255151937Sjkim		bp->prev->next = bp->next;
256151937Sjkim
257151937Sjkim		bp->next = ch_bufhead;
258151937Sjkim		bp->prev = END_OF_CHAIN;
259151937Sjkim		ch_bufhead->prev = bp;
260151937Sjkim		ch_bufhead = bp;
261151937Sjkim	}
262151937Sjkim
263151937Sjkim	if (ch_offset >= bp->datasize)
264151937Sjkim		/*
265151937Sjkim		 * After all that, we still don't have enough data.
266151937Sjkim		 * Go back and try again.
267118611Snjl		 */
268118611Snjl		goto read_more;
269118611Snjl
270151937Sjkim	return (bp->data[ch_offset]);
271118611Snjl}
272151937Sjkim
273151937Sjkim/*
274151937Sjkim * ch_ungetchar is a rather kludgy and limited way to push
275151937Sjkim * a single char onto an input file descriptor.
276118611Snjl */
277118611Snjl	public void
278118611Snjlch_ungetchar(c)
279151937Sjkim	int c;
280151937Sjkim{
281151937Sjkim	if (c != -1 && ch_ungotchar != -1)
282151937Sjkim		error("ch_ungetchar overrun", NULL_PARG);
283151937Sjkim	ch_ungotchar = c;
284151937Sjkim}
285151937Sjkim
286151937Sjkim#if LOGFILE
287151937Sjkim/*
288151937Sjkim * Close the logfile.
289151937Sjkim * If we haven't read all of standard input into it, do that now.
290151937Sjkim */
291151937Sjkim	public void
292151937Sjkimend_logfile()
293151937Sjkim{
294151937Sjkim	static int tried = FALSE;
295151937Sjkim
296151937Sjkim	if (logfile < 0)
297118611Snjl		return;
298118611Snjl	if (!tried && ch_fsize == NULL_POSITION)
299151937Sjkim	{
300151937Sjkim		tried = TRUE;
301151937Sjkim		ierror("Finishing logfile", NULL_PARG);
302151937Sjkim		while (ch_forw_get() != EOI)
303151937Sjkim			if (ABORT_SIGS())
304151937Sjkim				break;
305151937Sjkim	}
306167802Sjkim	close(logfile);
307151937Sjkim	logfile = -1;
308151937Sjkim	namelogfile = NULL;
309151937Sjkim}
310151937Sjkim
311151937Sjkim/*
312118611Snjl * Start a log file AFTER less has already been running.
313118611Snjl * Invoked from the - command; see toggle_option().
314118611Snjl * Write all the existing buffered data to the log file.
315151937Sjkim */
316151937Sjkim	public void
317151937Sjkimsync_logfile()
318151937Sjkim{
319151937Sjkim	register struct buf *bp;
320151937Sjkim	int warned = FALSE;
321151937Sjkim	long block;
322151937Sjkim	long nblocks;
323167802Sjkim
324167802Sjkim	nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
325167802Sjkim	for (block = 0;  block < nblocks;  block++)
326167802Sjkim	{
327167802Sjkim		for (bp = ch_bufhead;  ;  bp = bp->next)
328167802Sjkim		{
329167802Sjkim			if (bp == END_OF_CHAIN)
330167802Sjkim			{
331167802Sjkim				if (!warned)
332167802Sjkim				{
333167802Sjkim					error("Warning: log file is incomplete",
334167802Sjkim						NULL_PARG);
335167802Sjkim					warned = TRUE;
336167802Sjkim				}
337167802Sjkim				break;
338167802Sjkim			}
339167802Sjkim			if (bp->block == block)
340167802Sjkim			{
341167802Sjkim				write(logfile, (char *) bp->data, bp->datasize);
342167802Sjkim				break;
343167802Sjkim			}
344167802Sjkim		}
345167802Sjkim	}
346118611Snjl}
347118611Snjl
348118611Snjl#endif
349118611Snjl
350118611Snjl/*
351118611Snjl * Determine if a specific block is currently in one of the buffers.
352118611Snjl */
353118611Snjl	static int
354118611Snjlbuffered(block)
355118611Snjl	long block;
356118611Snjl{
357193529Sjkim	register struct buf *bp;
358193529Sjkim
359193529Sjkim	for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
360193529Sjkim		if (bp->block == block)
361193529Sjkim			return (TRUE);
362193529Sjkim	return (FALSE);
363193529Sjkim}
364193529Sjkim
365193529Sjkim/*
366193529Sjkim * Seek to a specified position in the file.
367193529Sjkim * Return 0 if successful, non-zero if can't seek there.
368193529Sjkim */
369167802Sjkim	public int
370193529Sjkimch_seek(pos)
371193529Sjkim	register POSITION pos;
372167802Sjkim{
373167802Sjkim	long new_block;
374167802Sjkim	POSITION len;
375167802Sjkim
376167802Sjkim	len = ch_length();
377167802Sjkim	if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
378167802Sjkim		return (1);
379118611Snjl
380118611Snjl	new_block = pos / LBUFSIZE;
381193529Sjkim	if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
382193529Sjkim	{
383193529Sjkim		if (ch_fpos > pos)
384193529Sjkim			return (1);
385193529Sjkim		while (ch_fpos < pos)
386193529Sjkim		{
387193529Sjkim			if (ch_forw_get() == EOI)
388193529Sjkim				return (1);
389193529Sjkim			if (ABORT_SIGS())
390193529Sjkim				return (1);
391193529Sjkim		}
392193529Sjkim		return (0);
393193529Sjkim	}
394193529Sjkim	/*
395193529Sjkim	 * Set read pointer.
396193529Sjkim	 */
397193529Sjkim	ch_block = new_block;
398193529Sjkim	ch_offset = pos % LBUFSIZE;
399193529Sjkim	return (0);
400193529Sjkim}
401193529Sjkim
402193529Sjkim/*
403193529Sjkim * Seek to the end of the file.
404193529Sjkim */
405193529Sjkim	public int
406193529Sjkimch_end_seek()
407193529Sjkim{
408193529Sjkim	POSITION len;
409193529Sjkim
410193529Sjkim	if (ch_flags & CH_CANSEEK)
411193529Sjkim		ch_fsize = filesize(ch_file);
412193529Sjkim
413193529Sjkim	len = ch_length();
414193529Sjkim	if (len != NULL_POSITION)
415193529Sjkim		return (ch_seek(len));
416193529Sjkim
417193529Sjkim	/*
418193529Sjkim	 * Do it the slow way: read till end of data.
419118611Snjl	 */
420118611Snjl	while (ch_forw_get() != EOI)
421118611Snjl		if (ABORT_SIGS())
422118611Snjl			return (1);
423151937Sjkim	return (0);
424118611Snjl}
425118611Snjl
426118611Snjl/*
427118611Snjl * Seek to the beginning of the file, or as close to it as we can get.
428118611Snjl * We may not be able to seek there if input is a pipe and the
429118611Snjl * beginning of the pipe is no longer buffered.
430118611Snjl */
431118611Snjl	public int
432118611Snjlch_beg_seek()
433118611Snjl{
434118611Snjl	register struct buf *bp, *firstbp;
435118611Snjl
436118611Snjl	/*
437118611Snjl	 * Try a plain ch_seek first.
438118611Snjl	 */
439118611Snjl	if (ch_seek(ch_zero()) == 0)
440118611Snjl		return (0);
441118611Snjl
442118611Snjl	/*
443167802Sjkim	 * Can't get to position 0.
444167802Sjkim	 * Look thru the buffers for the one closest to position 0.
445118611Snjl	 */
446118611Snjl	firstbp = bp = ch_bufhead;
447118611Snjl	if (bp == END_OF_CHAIN)
448118611Snjl		return (1);
449118611Snjl	while ((bp = bp->next) != END_OF_CHAIN)
450118611Snjl		if (bp->block < firstbp->block)
451118611Snjl			firstbp = bp;
452118611Snjl	ch_block = firstbp->block;
453199337Sjkim	ch_offset = 0;
454151937Sjkim	return (0);
455193529Sjkim}
456193529Sjkim
457193529Sjkim/*
458193529Sjkim * Return the length of the file, if known.
459193529Sjkim */
460193529Sjkim	public POSITION
461199337Sjkimch_length()
462193529Sjkim{
463193529Sjkim	if (ignore_eoi)
464118611Snjl		return (NULL_POSITION);
465118611Snjl	if (ch_flags & CH_HELPFILE)
466118611Snjl		return (size_helpdata);
467118611Snjl	return (ch_fsize);
468118611Snjl}
469118611Snjl
470118611Snjl/*
471118611Snjl * Return the current position in the file.
472118611Snjl */
473118611Snjl#define	tellpos(blk,off)   ((POSITION)((((long)(blk)) * LBUFSIZE) + (off)))
474118611Snjl
475118611Snjl	public POSITION
476118611Snjlch_tell()
477118611Snjl{
478118611Snjl	return (tellpos(ch_block, ch_offset));
479118611Snjl}
480151937Sjkim
481118611Snjl/*
482118611Snjl * Get the current char and post-increment the read pointer.
483118611Snjl */
484118611Snjl	public int
485118611Snjlch_forw_get()
486118611Snjl{
487118611Snjl	register int c;
488118611Snjl
489118611Snjl	c = ch_get();
490118611Snjl	if (c == EOI)
491118611Snjl		return (EOI);
492118611Snjl	if (ch_offset < LBUFSIZE-1)
493118611Snjl		ch_offset++;
494118611Snjl	else
495118611Snjl	{
496118611Snjl		ch_block ++;
497118611Snjl		ch_offset = 0;
498118611Snjl	}
499118611Snjl	return (c);
500118611Snjl}
501118611Snjl
502118611Snjl/*
503118611Snjl * Pre-decrement the read pointer and get the new current char.
504118611Snjl */
505118611Snjl	public int
506118611Snjlch_back_get()
507118611Snjl{
508118611Snjl	if (ch_offset > 0)
509118611Snjl		ch_offset --;
510118611Snjl	else
511118611Snjl	{
512118611Snjl		if (ch_block <= 0)
513118611Snjl			return (EOI);
514118611Snjl		if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
515151937Sjkim			return (EOI);
516118611Snjl		ch_block--;
517118611Snjl		ch_offset = LBUFSIZE-1;
518118611Snjl	}
519118611Snjl	return (ch_get());
520118611Snjl}
521118611Snjl
522118611Snjl/*
523118611Snjl * Allocate buffers.
524118611Snjl * Caller wants us to have a total of at least want_nbufs buffers.
525199337Sjkim */
526151937Sjkim	public int
527118611Snjlch_nbuf(want_nbufs)
528118611Snjl	int want_nbufs;
529118611Snjl{
530118611Snjl	PARG parg;
531118611Snjl
532118611Snjl	while (ch_nbufs < want_nbufs)
533118611Snjl	{
534118611Snjl		if (ch_addbuf())
535118611Snjl		{
536118611Snjl			/*
537118611Snjl			 * Cannot allocate enough buffers.
538118611Snjl			 * If we don't have ANY, then quit.
539118611Snjl			 * Otherwise, just report the error and return.
540167802Sjkim			 */
541167802Sjkim			parg.p_int = want_nbufs - ch_nbufs;
542167802Sjkim			error("Cannot allocate %d buffers", &parg);
543167802Sjkim			if (ch_nbufs == 0)
544167802Sjkim				quit(QUIT_ERROR);
545167802Sjkim			break;
546167802Sjkim		}
547167802Sjkim	}
548167802Sjkim	return (ch_nbufs);
549167802Sjkim}
550212761Sjkim
551167802Sjkim/*
552167802Sjkim * Flush (discard) any saved file state, including buffer contents.
553167802Sjkim */
554167802Sjkim	public void
555167802Sjkimch_flush()
556167802Sjkim{
557167802Sjkim	register struct buf *bp;
558167802Sjkim
559167802Sjkim	if (!(ch_flags & CH_CANSEEK))
560167802Sjkim	{
561167802Sjkim		/*
562167802Sjkim		 * If input is a pipe, we don't flush buffer contents,
563167802Sjkim		 * since the contents can't be recovered.
564167802Sjkim		 */
565167802Sjkim		ch_fsize = NULL_POSITION;
566167802Sjkim		return;
567167802Sjkim	}
568167802Sjkim
569167802Sjkim	/*
570167802Sjkim	 * Initialize all the buffers.
571167802Sjkim	 */
572167802Sjkim	for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
573167802Sjkim		bp->block = (long)(-1);
574167802Sjkim
575167802Sjkim	/*
576167802Sjkim	 * Figure out the size of the file, if we can.
577167802Sjkim	 */
578167802Sjkim	ch_fsize = filesize(ch_file);
579167802Sjkim
580167802Sjkim	/*
581167802Sjkim	 * Seek to a known position: the beginning of the file.
582167802Sjkim	 */
583167802Sjkim	ch_fpos = 0;
584167802Sjkim	ch_block = 0; /* ch_fpos / LBUFSIZE; */
585167802Sjkim	ch_offset = 0; /* ch_fpos % LBUFSIZE; */
586167802Sjkim
587167802Sjkim#if 1
588167802Sjkim	/*
589167802Sjkim	 * This is a kludge to workaround a Linux kernel bug: files in
590167802Sjkim	 * /proc have a size of 0 according to fstat() but have readable
591167802Sjkim	 * data.  They are sometimes, but not always, seekable.
592167802Sjkim	 * Force them to be non-seekable here.
593167802Sjkim	 */
594167802Sjkim	if (ch_fsize == 0)
595167802Sjkim	{
596167802Sjkim		ch_fsize = NULL_POSITION;
597167802Sjkim		ch_flags &= ~CH_CANSEEK;
598167802Sjkim	}
599167802Sjkim#endif
600167802Sjkim
601167802Sjkim	if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK)
602167802Sjkim	{
603167802Sjkim		/*
604167802Sjkim		 * Warning only; even if the seek fails for some reason,
605167802Sjkim		 * there's a good chance we're at the beginning anyway.
606167802Sjkim		 * {{ I think this is bogus reasoning. }}
607167802Sjkim		 */
608167802Sjkim		error("seek error to 0", NULL_PARG);
609167802Sjkim	}
610167802Sjkim}
611167802Sjkim
612167802Sjkim/*
613167802Sjkim * Allocate a new buffer.
614167802Sjkim * The buffer is added to the tail of the buffer chain.
615167802Sjkim */
616167802Sjkim	static int
617167802Sjkimch_addbuf()
618167802Sjkim{
619167802Sjkim	register struct buf *bp;
620167802Sjkim
621167802Sjkim	/*
622167802Sjkim	 * Allocate and initialize a new buffer and link it
623167802Sjkim	 * onto the tail of the buffer list.
624167802Sjkim	 */
625167802Sjkim	bp = (struct buf *) calloc(1, sizeof(struct buf));
626167802Sjkim	if (bp == NULL)
627167802Sjkim		return (1);
628167802Sjkim	ch_nbufs++;
629167802Sjkim	bp->block = (long)(-1);
630167802Sjkim	bp->next = END_OF_CHAIN;
631167802Sjkim	bp->prev = ch_buftail;
632167802Sjkim	ch_buftail->next = bp;
633167802Sjkim	ch_buftail = bp;
634167802Sjkim	return (0);
635167802Sjkim}
636167802Sjkim
637167802Sjkim/*
638167802Sjkim * Delete all buffers for this file.
639167802Sjkim */
640167802Sjkim	static void
641167802Sjkimch_delbufs()
642167802Sjkim{
643167802Sjkim	register struct buf *bp;
644167802Sjkim
645167802Sjkim	while (ch_bufhead != END_OF_CHAIN)
646167802Sjkim	{
647167802Sjkim		bp = ch_bufhead;
648167802Sjkim		bp->next->prev = bp->prev;;
649167802Sjkim		bp->prev->next = bp->next;
650167802Sjkim		free(bp);
651167802Sjkim	}
652167802Sjkim	ch_nbufs = 0;
653167802Sjkim}
654167802Sjkim
655167802Sjkim/*
656167802Sjkim * Is it possible to seek on a file descriptor?
657167802Sjkim */
658167802Sjkim	public int
659167802Sjkimseekable(f)
660167802Sjkim	int f;
661167802Sjkim{
662167802Sjkim#if MSDOS_COMPILER
663167802Sjkim	extern int fd0;
664167802Sjkim	if (f == fd0 && !isatty(fd0))
665167802Sjkim	{
666167802Sjkim		/*
667167802Sjkim		 * In MS-DOS, pipes are seekable.  Check for
668167802Sjkim		 * standard input, and pretend it is not seekable.
669167802Sjkim		 */
670167802Sjkim		return (0);
671167802Sjkim	}
672167802Sjkim#endif
673167802Sjkim	return (lseek(f, (off_t)1, 0) != BAD_LSEEK);
674167802Sjkim}
675167802Sjkim
676167802Sjkim/*
677167802Sjkim * Initialize file state for a new file.
678167802Sjkim */
679167802Sjkim	public void
680199337Sjkimch_init(f, flags)
681167802Sjkim	int f;
682167802Sjkim	int flags;
683167802Sjkim{
684167802Sjkim	/*
685167802Sjkim	 * See if we already have a filestate for this file.
686167802Sjkim	 */
687118611Snjl	thisfile = (struct filestate *) get_filestate(curr_ifile);
688118611Snjl	if (thisfile == NULL)
689118611Snjl	{
690118611Snjl		/*
691118611Snjl		 * Allocate and initialize a new filestate.
692118611Snjl		 */
693118611Snjl		thisfile = (struct filestate *)
694118611Snjl				calloc(1, sizeof(struct filestate));
695118611Snjl		thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN;
696118611Snjl		thisfile->buf_block = (long)(-1);
697118611Snjl		thisfile->nbufs = 0;
698118611Snjl		thisfile->flags = 0;
699118611Snjl		thisfile->fpos = 0;
700118611Snjl		thisfile->block = 0;
701118611Snjl		thisfile->offset = 0;
702118611Snjl		thisfile->file = -1;
703118611Snjl		thisfile->fsize = NULL_POSITION;
704118611Snjl		ch_flags = flags;
705118611Snjl		/*
706118611Snjl		 * Try to seek; set CH_CANSEEK if it works.
707118611Snjl		 */
708118611Snjl		if ((flags & CH_CANSEEK) && !seekable(f))
709118611Snjl			ch_flags &= ~CH_CANSEEK;
710118611Snjl		set_filestate(curr_ifile, (void *) thisfile);
711118611Snjl	}
712118611Snjl	if (thisfile->file == -1)
713118611Snjl		thisfile->file = f;
714118611Snjl	ch_flush();
715118611Snjl}
716118611Snjl
717118611Snjl/*
718118611Snjl * Close a filestate.
719118611Snjl */
720118611Snjl	public void
721118611Snjlch_close()
722118611Snjl{
723118611Snjl	int keepstate = FALSE;
724118611Snjl
725118611Snjl	if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE))
726118611Snjl	{
727118611Snjl		/*
728118611Snjl		 * We can seek or re-open, so we don't need to keep buffers.
729118611Snjl		 */
730118611Snjl		ch_delbufs();
731118611Snjl	} else
732118611Snjl		keepstate = TRUE;
733118611Snjl	if (!(ch_flags & CH_KEEPOPEN))
734118611Snjl	{
735118611Snjl		/*
736118611Snjl		 * We don't need to keep the file descriptor open
737118611Snjl		 * (because we can re-open it.)
738118611Snjl		 * But don't really close it if it was opened via popen(),
739118611Snjl		 * because pclose() wants to close it.
740118611Snjl		 */
741118611Snjl		if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
742118611Snjl			close(ch_file);
743118611Snjl		ch_file = -1;
744118611Snjl	} else
745118611Snjl		keepstate = TRUE;
746118611Snjl	if (!keepstate)
747118611Snjl	{
748118611Snjl		/*
749118611Snjl		 * We don't even need to keep the filestate structure.
750151937Sjkim		 */
751118611Snjl		free(thisfile);
752118611Snjl		thisfile = NULL;
753118611Snjl		set_filestate(curr_ifile, (void *) NULL);
754118611Snjl	}
755118611Snjl}
756118611Snjl
757118611Snjl/*
758118611Snjl * Return ch_flags for the current file.
759118611Snjl */
760151937Sjkim	public int
761118611Snjlch_getflags()
762118611Snjl{
763118611Snjl	return (ch_flags);
764118611Snjl}
765118611Snjl
766118611Snjl#if 0
767118611Snjl	public void
768118611Snjlch_dump(struct filestate *fs)
769118611Snjl{
770118611Snjl	struct buf *bp;
771118611Snjl	unsigned char *s;
772118611Snjl
773118611Snjl	if (fs == NULL)
774118611Snjl	{
775118611Snjl		printf(" --no filestate\n");
776118611Snjl		return;
777118611Snjl	}
778118611Snjl	printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
779118611Snjl		fs->file, fs->flags, fs->fpos,
780118611Snjl		fs->fsize, fs->block, fs->offset);
781118611Snjl	printf(" %d bufs:\n", fs->nbufs);
782118611Snjl	for (bp = fs->buf_next; bp != (struct buf *)fs;  bp = bp->next)
783118611Snjl	{
784118611Snjl		printf("%x: blk %x, size %x \"",
785118611Snjl			bp, bp->block, bp->datasize);
786118611Snjl		for (s = bp->data;  s < bp->data + 30;  s++)
787118611Snjl			if (*s >= ' ' && *s < 0x7F)
788118611Snjl				printf("%c", *s);
789118611Snjl			else
790118611Snjl				printf(".");
791118611Snjl		printf("\"\n");
792118611Snjl	}
793118611Snjl}
794118611Snjl#endif
795118611Snjl