ch.c revision 89019
1114402Sru/*
2151497Sru * Copyright (C) 1984-2000  Mark Nudelman
3151497Sru *
4114402Sru * You may distribute under the terms of either the GNU General Public
5114402Sru * License or the Less License, as specified in the README file.
6114402Sru *
7114402Sru * For more information about less, or for information on how to
8114402Sru * contact the author, see the README file.
9114402Sru */
10114402Sru
11114402Sru
12114402Sru/*
13114402Sru * Low level character input from the input file.
14114402Sru * We use these special purpose routines which optimize moving
15114402Sru * both forward and backward from the current read pointer.
16114402Sru */
17114402Sru
18114402Sru#include "less.h"
19114402Sru#if MSDOS_COMPILER==WIN32C
20151497Sru#include <errno.h>
21114402Sru#include <windows.h>
22114402Sru#endif
23114402Sru
24114402Srutypedef POSITION BLOCKNUM;
25114402Sru
26114402Srupublic int ignore_eoi;
27114402Sru
28114402Sru/*
29114402Sru * Pool of buffers holding the most recently used blocks of the input file.
30114402Sru * The buffer pool is kept as a doubly-linked circular list,
31114402Sru * in order from most- to least-recently used.
32114402Sru * The circular list is anchored by the file state "thisfile".
33114402Sru */
34114402Sru#define	LBUFSIZE	8192
35114402Srustruct buf {
36114402Sru	struct buf *next, *prev;
37114402Sru	struct buf *hnext, *hprev;
38114402Sru	BLOCKNUM block;
39114402Sru	unsigned int datasize;
40114402Sru	unsigned char data[LBUFSIZE];
41114402Sru};
42114402Sru
43114402Srustruct buflist {
44114402Sru	/* -- Following members must match struct buf */
45114402Sru	struct buf *buf_next, *buf_prev;
46114402Sru	struct buf *buf_hnext, *buf_hprev;
47114402Sru};
48114402Sru
49114402Sru/*
50114402Sru * The file state is maintained in a filestate structure.
51114402Sru * A pointer to the filestate is kept in the ifile structure.
52114402Sru */
53114402Sru#define	BUFHASH_SIZE	64
54114402Srustruct filestate {
55114402Sru	struct buf *buf_next, *buf_prev;
56114402Sru	struct buflist hashtbl[BUFHASH_SIZE];
57114402Sru	int file;
58114402Sru	int flags;
59114402Sru	POSITION fpos;
60114402Sru	int nbufs;
61114402Sru	BLOCKNUM block;
62114402Sru	unsigned int offset;
63114402Sru	POSITION fsize;
64114402Sru};
65114402Sru
66114402Sru#define	ch_bufhead	thisfile->buf_next
67114402Sru#define	ch_buftail	thisfile->buf_prev
68114402Sru#define	ch_nbufs	thisfile->nbufs
69114402Sru#define	ch_block	thisfile->block
70114402Sru#define	ch_offset	thisfile->offset
71114402Sru#define	ch_fpos		thisfile->fpos
72114402Sru#define	ch_fsize	thisfile->fsize
73114402Sru#define	ch_flags	thisfile->flags
74114402Sru#define	ch_file		thisfile->file
75114402Sru
76114402Sru#define	END_OF_CHAIN	((struct buf *)&thisfile->buf_next)
77114402Sru#define	END_OF_HCHAIN(h) ((struct buf *)&thisfile->hashtbl[h])
78114402Sru#define BUFHASH(blk)	((blk) & (BUFHASH_SIZE-1))
79114402Sru
80114402Sru#define	FOR_BUFS_IN_CHAIN(h,bp) \
81114402Sru	for (bp = thisfile->hashtbl[h].buf_hnext;  \
82114402Sru	     bp != END_OF_HCHAIN(h);  bp = bp->hnext)
83114402Sru
84114402Sru#define	HASH_RM(bp) \
85114402Sru	(bp)->hnext->hprev = (bp)->hprev; \
86114402Sru	(bp)->hprev->hnext = (bp)->hnext;
87114402Sru
88114402Sru#define	HASH_INS(bp,h) \
89114402Sru	(bp)->hnext = thisfile->hashtbl[h].buf_hnext; \
90114402Sru	(bp)->hprev = END_OF_HCHAIN(h); \
91114402Sru	thisfile->hashtbl[h].buf_hnext->hprev = (bp); \
92114402Sru	thisfile->hashtbl[h].buf_hnext = (bp);
93114402Sru
94114402Srustatic struct filestate *thisfile;
95114402Srustatic int ch_ungotchar = -1;
96114402Sru
97114402Sruextern int autobuf;
98114402Sruextern int sigs;
99114402Sruextern int cbufs;
100114402Sruextern int secure;
101114402Sruextern constant char helpdata[];
102114402Sruextern constant int size_helpdata;
103114402Sruextern IFILE curr_ifile;
104114402Sru#if LOGFILE
105114402Sruextern int logfile;
106114402Sruextern char *namelogfile;
107114402Sru#endif
108114402Sru
109114402Srustatic int ch_addbuf();
110114402Sru
111114402Sru
112114402Sru/*
113114402Sru * Get the character pointed to by the read pointer.
114114402Sru * ch_get() is a macro which is more efficient to call
115114402Sru * than fch_get (the function), in the usual case
116114402Sru * that the block desired is at the head of the chain.
117114402Sru */
118114402Sru#define	ch_get()   ((ch_block == ch_bufhead->block && \
119114402Sru		     ch_offset < ch_bufhead->datasize) ? \
120114402Sru			ch_bufhead->data[ch_offset] : fch_get())
121114402Sru	int
122114402Srufch_get()
123114402Sru{
124114402Sru	register struct buf *bp;
125114402Sru	register int n;
126114402Sru	register int slept;
127114402Sru	register int h;
128114402Sru	POSITION pos;
129114402Sru	POSITION len;
130114402Sru
131114402Sru	slept = FALSE;
132114402Sru
133114402Sru	/*
134114402Sru	 * Look for a buffer holding the desired block.
135114402Sru	 */
136114402Sru	h = BUFHASH(ch_block);
137114402Sru	FOR_BUFS_IN_CHAIN(h, bp)
138114402Sru	{
139114402Sru		if (bp->block == ch_block)
140114402Sru		{
141114402Sru			if (ch_offset >= bp->datasize)
142114402Sru				/*
143114402Sru				 * Need more data in this buffer.
144114402Sru				 */
145114402Sru				goto read_more;
146114402Sru			goto found;
147114402Sru		}
148114402Sru	}
149114402Sru	/*
150114402Sru	 * Block is not in a buffer.
151114402Sru	 * Take the least recently used buffer
152114402Sru	 * and read the desired block into it.
153114402Sru	 * If the LRU buffer has data in it,
154114402Sru	 * then maybe allocate a new buffer.
155114402Sru	 */
156114402Sru	if (ch_buftail == END_OF_CHAIN || ch_buftail->block != -1)
157114402Sru	{
158114402Sru		/*
159114402Sru		 * There is no empty buffer to use.
160114402Sru		 * Allocate a new buffer if:
161114402Sru		 * 1. We can't seek on this file and -b is not in effect; or
162114402Sru		 * 2. We haven't allocated the max buffers for this file yet.
163114402Sru		 */
164114402Sru		if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
165114402Sru		    (cbufs == -1 || ch_nbufs < cbufs))
166114402Sru			if (ch_addbuf())
167114402Sru				/*
168114402Sru				 * Allocation failed: turn off autobuf.
169114402Sru				 */
170114402Sru				autobuf = OPT_OFF;
171114402Sru	}
172114402Sru	bp = ch_buftail;
173114402Sru	HASH_RM(bp); /* Remove from old hash chain. */
174114402Sru	bp->block = ch_block;
175114402Sru	bp->datasize = 0;
176114402Sru	HASH_INS(bp, h); /* Insert into new hash chain. */
177114402Sru
178114402Sru    read_more:
179114402Sru	pos = (ch_block * LBUFSIZE) + bp->datasize;
180114402Sru	if ((len = ch_length()) != NULL_POSITION && pos >= len)
181114402Sru		/*
182114402Sru		 * At end of file.
183114402Sru		 */
184114402Sru		return (EOI);
185114402Sru
186114402Sru	if (pos != ch_fpos)
187114402Sru	{
188114402Sru		/*
189114402Sru		 * Not at the correct position: must seek.
190114402Sru		 * If input is a pipe, we're in trouble (can't seek on a pipe).
191114402Sru		 * Some data has been lost: just return "?".
192114402Sru		 */
193114402Sru		if (!(ch_flags & CH_CANSEEK))
194114402Sru			return ('?');
195114402Sru		if (lseek(ch_file, (off_t)pos, 0) == BAD_LSEEK)
196114402Sru		{
197114402Sru 			error("seek error", NULL_PARG);
198114402Sru			clear_eol();
199114402Sru			return (EOI);
200114402Sru 		}
201114402Sru 		ch_fpos = pos;
202114402Sru 	}
203114402Sru
204114402Sru	/*
205114402Sru	 * Read the block.
206114402Sru	 * If we read less than a full block, that's ok.
207114402Sru	 * We use partial block and pick up the rest next time.
208114402Sru	 */
209114402Sru	if (ch_ungotchar != -1)
210114402Sru	{
211114402Sru		bp->data[bp->datasize] = ch_ungotchar;
212114402Sru		n = 1;
213114402Sru		ch_ungotchar = -1;
214114402Sru	} else if (ch_flags & CH_HELPFILE)
215114402Sru	{
216114402Sru		bp->data[bp->datasize] = helpdata[ch_fpos];
217114402Sru		n = 1;
218114402Sru	} else
219114402Sru	{
220114402Sru		n = iread(ch_file, &bp->data[bp->datasize],
221114402Sru			(unsigned int)(LBUFSIZE - bp->datasize));
222114402Sru	}
223114402Sru
224114402Sru	if (n == READ_INTR)
225114402Sru		return (EOI);
226114402Sru	if (n < 0)
227114402Sru	{
228114402Sru#if MSDOS_COMPILER==WIN32C
229114402Sru		if (errno != EPIPE)
230114402Sru#endif
231114402Sru		{
232114402Sru			error("read error", NULL_PARG);
233114402Sru			clear_eol();
234114402Sru		}
235114402Sru		n = 0;
236114402Sru	}
237114402Sru
238114402Sru#if LOGFILE
239114402Sru	/*
240114402Sru	 * If we have a log file, write the new data to it.
241114402Sru	 */
242114402Sru	if (!secure && logfile >= 0 && n > 0)
243114402Sru		write(logfile, (char *) &bp->data[bp->datasize], n);
244114402Sru#endif
245114402Sru
246114402Sru	ch_fpos += n;
247114402Sru	bp->datasize += n;
248114402Sru
249114402Sru	/*
250114402Sru	 * If we have read to end of file, set ch_fsize to indicate
251114402Sru	 * the position of the end of file.
252114402Sru	 */
253114402Sru	if (n == 0)
254114402Sru	{
255114402Sru		ch_fsize = pos;
256114402Sru		if (ignore_eoi)
257114402Sru		{
258114402Sru			/*
259114402Sru			 * We are ignoring EOF.
260114402Sru			 * Wait a while, then try again.
261114402Sru			 */
262114402Sru			if (!slept)
263114402Sru			{
264114402Sru				PARG parg;
265114402Sru				parg.p_string = wait_message();
266114402Sru				ierror("%s", &parg);
267114402Sru			}
268114402Sru#if !MSDOS_COMPILER
269114402Sru	 		sleep(1);
270114402Sru#else
271114402Sru#if MSDOS_COMPILER==WIN32C
272114402Sru			Sleep(1000);
273114402Sru#endif
274114402Sru#endif
275114402Sru			slept = TRUE;
276114402Sru		}
277114402Sru		if (sigs)
278114402Sru			return (EOI);
279114402Sru	}
280114402Sru
281114402Sru    found:
282114402Sru	if (ch_bufhead != bp)
283114402Sru	{
284114402Sru		/*
285114402Sru		 * Move the buffer to the head of the buffer chain.
286114402Sru		 * This orders the buffer chain, most- to least-recently used.
287114402Sru		 */
288114402Sru		bp->next->prev = bp->prev;
289114402Sru		bp->prev->next = bp->next;
290114402Sru		bp->next = ch_bufhead;
291114402Sru		bp->prev = END_OF_CHAIN;
292114402Sru		ch_bufhead->prev = bp;
293114402Sru		ch_bufhead = bp;
294114402Sru
295114402Sru		/*
296114402Sru		 * Move to head of hash chain too.
297114402Sru		 */
298114402Sru		HASH_RM(bp);
299114402Sru		HASH_INS(bp, h);
300114402Sru	}
301114402Sru
302114402Sru	if (ch_offset >= bp->datasize)
303114402Sru		/*
304114402Sru		 * After all that, we still don't have enough data.
305114402Sru		 * Go back and try again.
306114402Sru		 */
307114402Sru		goto read_more;
308114402Sru
309114402Sru	return (bp->data[ch_offset]);
310114402Sru}
311114402Sru
312114402Sru/*
313114402Sru * ch_ungetchar is a rather kludgy and limited way to push
314114402Sru * a single char onto an input file descriptor.
315114402Sru */
316114402Sru	public void
317114402Sruch_ungetchar(c)
318114402Sru	int c;
319114402Sru{
320114402Sru	if (c != -1 && ch_ungotchar != -1)
321114402Sru		error("ch_ungetchar overrun", NULL_PARG);
322114402Sru	ch_ungotchar = c;
323114402Sru}
324114402Sru
325114402Sru#if LOGFILE
326114402Sru/*
327114402Sru * Close the logfile.
328114402Sru * If we haven't read all of standard input into it, do that now.
329114402Sru */
330114402Sru	public void
331114402Sruend_logfile()
332114402Sru{
333114402Sru	static int tried = FALSE;
334114402Sru
335114402Sru	if (logfile < 0)
336114402Sru		return;
337114402Sru	if (!tried && ch_fsize == NULL_POSITION)
338114402Sru	{
339114402Sru		tried = TRUE;
340114402Sru		ierror("Finishing logfile", NULL_PARG);
341114402Sru		while (ch_forw_get() != EOI)
342114402Sru			if (ABORT_SIGS())
343114402Sru				break;
344114402Sru	}
345114402Sru	close(logfile);
346114402Sru	logfile = -1;
347114402Sru	namelogfile = NULL;
348114402Sru}
349114402Sru
350114402Sru/*
351114402Sru * Start a log file AFTER less has already been running.
352114402Sru * Invoked from the - command; see toggle_option().
353151497Sru * Write all the existing buffered data to the log file.
354114402Sru */
355114402Sru	public void
356114402Srusync_logfile()
357114402Sru{
358114402Sru	register struct buf *bp;
359114402Sru	int warned = FALSE;
360114402Sru	BLOCKNUM block;
361114402Sru	BLOCKNUM nblocks;
362114402Sru
363114402Sru	nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
364114402Sru	for (block = 0;  block < nblocks;  block++)
365114402Sru	{
366114402Sru		for (bp = ch_bufhead;  ;  bp = bp->next)
367114402Sru		{
368114402Sru			if (bp == END_OF_CHAIN)
369114402Sru			{
370114402Sru				if (!warned)
371114402Sru				{
372114402Sru					error("Warning: log file is incomplete",
373114402Sru						NULL_PARG);
374114402Sru					warned = TRUE;
375114402Sru				}
376114402Sru				break;
377114402Sru			}
378114402Sru			if (bp->block == block)
379114402Sru			{
380114402Sru				write(logfile, (char *) bp->data, bp->datasize);
381114402Sru				break;
382114402Sru			}
383114402Sru		}
384114402Sru	}
385114402Sru}
386114402Sru
387114402Sru#endif
388114402Sru
389114402Sru/*
390114402Sru * Determine if a specific block is currently in one of the buffers.
391114402Sru */
392114402Sru	static int
393114402Srubuffered(block)
394114402Sru	BLOCKNUM block;
395114402Sru{
396114402Sru	register struct buf *bp;
397114402Sru	register int h;
398114402Sru
399114402Sru	h = BUFHASH(block);
400114402Sru	FOR_BUFS_IN_CHAIN(h, bp)
401114402Sru	{
402114402Sru		if (bp->block == block)
403114402Sru			return (TRUE);
404114402Sru	}
405114402Sru	return (FALSE);
406114402Sru}
407114402Sru
408114402Sru/*
409114402Sru * Seek to a specified position in the file.
410114402Sru * Return 0 if successful, non-zero if can't seek there.
411114402Sru */
412114402Sru	public int
413114402Sruch_seek(pos)
414114402Sru	register POSITION pos;
415114402Sru{
416114402Sru	BLOCKNUM new_block;
417114402Sru	POSITION len;
418114402Sru
419114402Sru	len = ch_length();
420114402Sru	if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
421114402Sru		return (1);
422114402Sru
423114402Sru	new_block = pos / LBUFSIZE;
424114402Sru	if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
425114402Sru	{
426114402Sru		if (ch_fpos > pos)
427114402Sru			return (1);
428114402Sru		while (ch_fpos < pos)
429151497Sru		{
430114402Sru			if (ch_forw_get() == EOI)
431114402Sru				return (1);
432114402Sru			if (ABORT_SIGS())
433114402Sru				return (1);
434114402Sru		}
435114402Sru		return (0);
436114402Sru	}
437114402Sru	/*
438114402Sru	 * Set read pointer.
439114402Sru	 */
440114402Sru	ch_block = new_block;
441114402Sru	ch_offset = pos % LBUFSIZE;
442114402Sru	return (0);
443114402Sru}
444114402Sru
445114402Sru/*
446114402Sru * Seek to the end of the file.
447114402Sru */
448114402Sru	public int
449114402Sruch_end_seek()
450114402Sru{
451114402Sru	POSITION len;
452114402Sru
453114402Sru	if (ch_flags & CH_CANSEEK)
454114402Sru		ch_fsize = filesize(ch_file);
455114402Sru
456114402Sru	len = ch_length();
457114402Sru	if (len != NULL_POSITION)
458114402Sru		return (ch_seek(len));
459114402Sru
460114402Sru	/*
461114402Sru	 * Do it the slow way: read till end of data.
462114402Sru	 */
463114402Sru	while (ch_forw_get() != EOI)
464114402Sru		if (ABORT_SIGS())
465114402Sru			return (1);
466114402Sru	return (0);
467114402Sru}
468114402Sru
469114402Sru/*
470114402Sru * Seek to the beginning of the file, or as close to it as we can get.
471114402Sru * We may not be able to seek there if input is a pipe and the
472114402Sru * beginning of the pipe is no longer buffered.
473114402Sru */
474114402Sru	public int
475114402Sruch_beg_seek()
476114402Sru{
477114402Sru	register struct buf *bp, *firstbp;
478114402Sru
479114402Sru	/*
480114402Sru	 * Try a plain ch_seek first.
481114402Sru	 */
482114402Sru	if (ch_seek(ch_zero()) == 0)
483114402Sru		return (0);
484114402Sru
485114402Sru	/*
486114402Sru	 * Can't get to position 0.
487114402Sru	 * Look thru the buffers for the one closest to position 0.
488114402Sru	 */
489114402Sru	firstbp = bp = ch_bufhead;
490114402Sru	if (bp == END_OF_CHAIN)
491114402Sru		return (1);
492114402Sru	while ((bp = bp->next) != END_OF_CHAIN)
493114402Sru		if (bp->block < firstbp->block)
494114402Sru			firstbp = bp;
495114402Sru	ch_block = firstbp->block;
496114402Sru	ch_offset = 0;
497114402Sru	return (0);
498114402Sru}
499114402Sru
500114402Sru/*
501114402Sru * Return the length of the file, if known.
502114402Sru */
503114402Sru	public POSITION
504114402Sruch_length()
505114402Sru{
506114402Sru	if (ignore_eoi)
507114402Sru		return (NULL_POSITION);
508114402Sru	if (ch_flags & CH_HELPFILE)
509114402Sru		return (size_helpdata);
510114402Sru	return (ch_fsize);
511114402Sru}
512114402Sru
513114402Sru/*
514114402Sru * Return the current position in the file.
515114402Sru */
516114402Sru	public POSITION
517114402Sruch_tell()
518114402Sru{
519114402Sru	return (ch_block * LBUFSIZE) + ch_offset;
520114402Sru}
521114402Sru
522114402Sru/*
523114402Sru * Get the current char and post-increment the read pointer.
524114402Sru */
525114402Sru	public int
526114402Sruch_forw_get()
527114402Sru{
528114402Sru	register int c;
529114402Sru
530114402Sru	c = ch_get();
531114402Sru	if (c == EOI)
532114402Sru		return (EOI);
533114402Sru	if (ch_offset < LBUFSIZE-1)
534114402Sru		ch_offset++;
535114402Sru	else
536114402Sru	{
537114402Sru		ch_block ++;
538114402Sru		ch_offset = 0;
539114402Sru	}
540114402Sru	return (c);
541114402Sru}
542114402Sru
543114402Sru/*
544114402Sru * Pre-decrement the read pointer and get the new current char.
545114402Sru */
546114402Sru	public int
547114402Sruch_back_get()
548114402Sru{
549114402Sru	if (ch_offset > 0)
550114402Sru		ch_offset --;
551114402Sru	else
552114402Sru	{
553114402Sru		if (ch_block <= 0)
554114402Sru			return (EOI);
555114402Sru		if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
556114402Sru			return (EOI);
557114402Sru		ch_block--;
558114402Sru		ch_offset = LBUFSIZE-1;
559114402Sru	}
560114402Sru	return (ch_get());
561114402Sru}
562114402Sru
563114402Sru/*
564114402Sru * Allocate buffers.
565114402Sru * Caller wants us to have a total of at least want_nbufs buffers.
566114402Sru */
567114402Sru	public int
568114402Sruch_nbuf(want_nbufs)
569114402Sru	int want_nbufs;
570114402Sru{
571114402Sru	PARG parg;
572114402Sru
573114402Sru	while (ch_nbufs < want_nbufs)
574114402Sru	{
575114402Sru		if (ch_addbuf())
576114402Sru		{
577114402Sru			/*
578114402Sru			 * Cannot allocate enough buffers.
579114402Sru			 * If we don't have ANY, then quit.
580114402Sru			 * Otherwise, just report the error and return.
581114402Sru			 */
582114402Sru			parg.p_int = want_nbufs - ch_nbufs;
583114402Sru			error("Cannot allocate %d buffers", &parg);
584114402Sru			if (ch_nbufs == 0)
585114402Sru				quit(QUIT_ERROR);
586114402Sru			break;
587114402Sru		}
588114402Sru	}
589114402Sru	return (ch_nbufs);
590114402Sru}
591114402Sru
592114402Sru/*
593114402Sru * Flush (discard) any saved file state, including buffer contents.
594114402Sru */
595114402Sru	public void
596114402Sruch_flush()
597114402Sru{
598114402Sru	register struct buf *bp;
599114402Sru
600114402Sru	if (!(ch_flags & CH_CANSEEK))
601114402Sru	{
602114402Sru		/*
603114402Sru		 * If input is a pipe, we don't flush buffer contents,
604114402Sru		 * since the contents can't be recovered.
605114402Sru		 */
606114402Sru		ch_fsize = NULL_POSITION;
607114402Sru		return;
608114402Sru	}
609114402Sru
610114402Sru	/*
611114402Sru	 * Initialize all the buffers.
612114402Sru	 */
613114402Sru	for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
614114402Sru		bp->block = -1;
615114402Sru
616114402Sru	/*
617114402Sru	 * Figure out the size of the file, if we can.
618114402Sru	 */
619114402Sru	ch_fsize = filesize(ch_file);
620114402Sru
621114402Sru	/*
622114402Sru	 * Seek to a known position: the beginning of the file.
623114402Sru	 */
624114402Sru	ch_fpos = 0;
625114402Sru	ch_block = 0; /* ch_fpos / LBUFSIZE; */
626114402Sru	ch_offset = 0; /* ch_fpos % LBUFSIZE; */
627114402Sru
628114402Sru#if 1
629114402Sru	/*
630114402Sru	 * This is a kludge to workaround a Linux kernel bug: files in
631114402Sru	 * /proc have a size of 0 according to fstat() but have readable
632114402Sru	 * data.  They are sometimes, but not always, seekable.
633114402Sru	 * Force them to be non-seekable here.
634114402Sru	 */
635114402Sru	if (ch_fsize == 0)
636114402Sru	{
637114402Sru		ch_fsize = NULL_POSITION;
638114402Sru		ch_flags &= ~CH_CANSEEK;
639114402Sru	}
640114402Sru#endif
641
642	if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK)
643	{
644		/*
645		 * Warning only; even if the seek fails for some reason,
646		 * there's a good chance we're at the beginning anyway.
647		 * {{ I think this is bogus reasoning. }}
648		 */
649		error("seek error to 0", NULL_PARG);
650	}
651}
652
653/*
654 * Allocate a new buffer.
655 * The buffer is added to the tail of the buffer chain.
656 */
657	static int
658ch_addbuf()
659{
660	register struct buf *bp;
661
662	/*
663	 * Allocate and initialize a new buffer and link it
664	 * onto the tail of the buffer list.
665	 */
666	bp = (struct buf *) calloc(1, sizeof(struct buf));
667	if (bp == NULL)
668		return (1);
669	ch_nbufs++;
670	bp->block = -1;
671	bp->next = END_OF_CHAIN;
672	bp->prev = ch_buftail;
673	ch_buftail->next = bp;
674	ch_buftail = bp;
675	HASH_INS(bp, 0);
676	return (0);
677}
678
679/*
680 *
681 */
682	static void
683init_hashtbl()
684{
685	register int h;
686
687	for (h = 0;  h < BUFHASH_SIZE;  h++)
688	{
689		thisfile->hashtbl[h].buf_hnext = END_OF_HCHAIN(h);
690		thisfile->hashtbl[h].buf_hprev = END_OF_HCHAIN(h);
691	}
692}
693
694/*
695 * Delete all buffers for this file.
696 */
697	static void
698ch_delbufs()
699{
700	register struct buf *bp;
701
702	while (ch_bufhead != END_OF_CHAIN)
703	{
704		bp = ch_bufhead;
705		bp->next->prev = bp->prev;;
706		bp->prev->next = bp->next;
707		free(bp);
708	}
709	ch_nbufs = 0;
710	init_hashtbl();
711}
712
713/*
714 * Is it possible to seek on a file descriptor?
715 */
716	public int
717seekable(f)
718	int f;
719{
720#if MSDOS_COMPILER
721	extern int fd0;
722	if (f == fd0 && !isatty(fd0))
723	{
724		/*
725		 * In MS-DOS, pipes are seekable.  Check for
726		 * standard input, and pretend it is not seekable.
727		 */
728		return (0);
729	}
730#endif
731	return (lseek(f, (off_t)1, 0) != BAD_LSEEK);
732}
733
734/*
735 * Initialize file state for a new file.
736 */
737	public void
738ch_init(f, flags)
739	int f;
740	int flags;
741{
742	/*
743	 * See if we already have a filestate for this file.
744	 */
745	thisfile = (struct filestate *) get_filestate(curr_ifile);
746	if (thisfile == NULL)
747	{
748		/*
749		 * Allocate and initialize a new filestate.
750		 */
751		thisfile = (struct filestate *)
752				calloc(1, sizeof(struct filestate));
753		thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN;
754		thisfile->nbufs = 0;
755		thisfile->flags = 0;
756		thisfile->fpos = 0;
757		thisfile->block = 0;
758		thisfile->offset = 0;
759		thisfile->file = -1;
760		thisfile->fsize = NULL_POSITION;
761		ch_flags = flags;
762		init_hashtbl();
763		/*
764		 * Try to seek; set CH_CANSEEK if it works.
765		 */
766		if ((flags & CH_CANSEEK) && !seekable(f))
767			ch_flags &= ~CH_CANSEEK;
768		set_filestate(curr_ifile, (void *) thisfile);
769	}
770	if (thisfile->file == -1)
771		thisfile->file = f;
772	ch_flush();
773}
774
775/*
776 * Close a filestate.
777 */
778	public void
779ch_close()
780{
781	int keepstate = FALSE;
782
783	if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE))
784	{
785		/*
786		 * We can seek or re-open, so we don't need to keep buffers.
787		 */
788		ch_delbufs();
789	} else
790		keepstate = TRUE;
791	if (!(ch_flags & CH_KEEPOPEN))
792	{
793		/*
794		 * We don't need to keep the file descriptor open
795		 * (because we can re-open it.)
796		 * But don't really close it if it was opened via popen(),
797		 * because pclose() wants to close it.
798		 */
799		if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
800			close(ch_file);
801		ch_file = -1;
802	} else
803		keepstate = TRUE;
804	if (!keepstate)
805	{
806		/*
807		 * We don't even need to keep the filestate structure.
808		 */
809		free(thisfile);
810		thisfile = NULL;
811		set_filestate(curr_ifile, (void *) NULL);
812	}
813}
814
815/*
816 * Return ch_flags for the current file.
817 */
818	public int
819ch_getflags()
820{
821	return (ch_flags);
822}
823
824#if 0
825	public void
826ch_dump(struct filestate *fs)
827{
828	struct buf *bp;
829	unsigned char *s;
830
831	if (fs == NULL)
832	{
833		printf(" --no filestate\n");
834		return;
835	}
836	printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
837		fs->file, fs->flags, fs->fpos,
838		fs->fsize, fs->block, fs->offset);
839	printf(" %d bufs:\n", fs->nbufs);
840	for (bp = fs->buf_next; bp != (struct buf *)fs;  bp = bp->next)
841	{
842		printf("%x: blk %x, size %x \"",
843			bp, bp->block, bp->datasize);
844		for (s = bp->data;  s < bp->data + 30;  s++)
845			if (*s >= ' ' && *s < 0x7F)
846				printf("%c", *s);
847			else
848				printf(".");
849		printf("\"\n");
850	}
851}
852#endif
853