xref: /openbsd-src/usr.bin/less/ch.c (revision be38755c412cc72cb8d40f51ea70c9893196afff)
1 /*
2  * Copyright (C) 1984-2011  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information about less, or for information on how to
8  * contact the author, see the README file.
9  */
10 
11 
12 /*
13  * Low level character input from the input file.
14  * We use these special purpose routines which optimize moving
15  * both forward and backward from the current read pointer.
16  */
17 
18 #include "less.h"
19 #if MSDOS_COMPILER==WIN32C
20 #include <errno.h>
21 #include <windows.h>
22 #endif
23 
24 #if HAVE_STAT_INO
25 #include <sys/stat.h>
26 extern dev_t curr_dev;
27 extern ino_t curr_ino;
28 #endif
29 
30 typedef POSITION BLOCKNUM;
31 
32 public int ignore_eoi;
33 
34 /*
35  * Pool of buffers holding the most recently used blocks of the input file.
36  * The buffer pool is kept as a doubly-linked circular list,
37  * in order from most- to least-recently used.
38  * The circular list is anchored by the file state "thisfile".
39  */
40 struct bufnode {
41 	struct bufnode *next, *prev;
42 	struct bufnode *hnext, *hprev;
43 };
44 
45 #define	LBUFSIZE	8192
46 struct buf {
47 	struct bufnode node;
48 	BLOCKNUM block;
49 	unsigned int datasize;
50 	unsigned char data[LBUFSIZE];
51 };
52 #define bufnode_buf(bn)  ((struct buf *) bn)
53 
54 /*
55  * The file state is maintained in a filestate structure.
56  * A pointer to the filestate is kept in the ifile structure.
57  */
58 #define	BUFHASH_SIZE	64
59 struct filestate {
60 	struct bufnode buflist;
61 	struct bufnode hashtbl[BUFHASH_SIZE];
62 	int file;
63 	int flags;
64 	POSITION fpos;
65 	int nbufs;
66 	BLOCKNUM block;
67 	unsigned int offset;
68 	POSITION fsize;
69 };
70 
71 #define	ch_bufhead	thisfile->buflist.next
72 #define	ch_buftail	thisfile->buflist.prev
73 #define	ch_nbufs	thisfile->nbufs
74 #define	ch_block	thisfile->block
75 #define	ch_offset	thisfile->offset
76 #define	ch_fpos		thisfile->fpos
77 #define	ch_fsize	thisfile->fsize
78 #define	ch_flags	thisfile->flags
79 #define	ch_file		thisfile->file
80 
81 #define	END_OF_CHAIN	(&thisfile->buflist)
82 #define	END_OF_HCHAIN(h) (&thisfile->hashtbl[h])
83 #define BUFHASH(blk)	((blk) & (BUFHASH_SIZE-1))
84 
85 /*
86  * Macros to manipulate the list of buffers in thisfile->buflist.
87  */
88 #define	FOR_BUFS(bn) \
89 	for (bn = ch_bufhead;  bn != END_OF_CHAIN;  bn = bn->next)
90 
91 #define BUF_RM(bn) \
92 	(bn)->next->prev = (bn)->prev; \
93 	(bn)->prev->next = (bn)->next;
94 
95 #define BUF_INS_HEAD(bn) \
96 	(bn)->next = ch_bufhead; \
97 	(bn)->prev = END_OF_CHAIN; \
98 	ch_bufhead->prev = (bn); \
99 	ch_bufhead = (bn);
100 
101 #define BUF_INS_TAIL(bn) \
102 	(bn)->next = END_OF_CHAIN; \
103 	(bn)->prev = ch_buftail; \
104 	ch_buftail->next = (bn); \
105 	ch_buftail = (bn);
106 
107 /*
108  * Macros to manipulate the list of buffers in thisfile->hashtbl[n].
109  */
110 #define	FOR_BUFS_IN_CHAIN(h,bn) \
111 	for (bn = thisfile->hashtbl[h].hnext;  \
112 	     bn != END_OF_HCHAIN(h);  bn = bn->hnext)
113 
114 #define	BUF_HASH_RM(bn) \
115 	(bn)->hnext->hprev = (bn)->hprev; \
116 	(bn)->hprev->hnext = (bn)->hnext;
117 
118 #define	BUF_HASH_INS(bn,h) \
119 	(bn)->hnext = thisfile->hashtbl[h].hnext; \
120 	(bn)->hprev = END_OF_HCHAIN(h); \
121 	thisfile->hashtbl[h].hnext->hprev = (bn); \
122 	thisfile->hashtbl[h].hnext = (bn);
123 
124 static struct filestate *thisfile;
125 static int ch_ungotchar = -1;
126 static int maxbufs = -1;
127 
128 extern int autobuf;
129 extern int sigs;
130 extern int secure;
131 extern int screen_trashed;
132 extern int follow_mode;
133 extern IFILE curr_ifile;
134 #if LOGFILE
135 extern int logfile;
136 extern char *namelogfile;
137 #endif
138 
139 static int ch_addbuf();
140 
141 
142 /*
143  * Get the character pointed to by the read pointer.
144  */
145 	int
146 ch_get()
147 {
148 	register struct buf *bp;
149 	register struct bufnode *bn;
150 	register int n;
151 	register int slept;
152 	register int h;
153 	POSITION pos;
154 	POSITION len;
155 
156 	if (thisfile == NULL)
157 		return (EOI);
158 
159 	/*
160 	 * Quick check for the common case where
161 	 * the desired char is in the head buffer.
162 	 */
163 	if (ch_bufhead != END_OF_CHAIN)
164 	{
165 		bp = bufnode_buf(ch_bufhead);
166 		if (ch_block == bp->block && ch_offset < bp->datasize)
167 			return bp->data[ch_offset];
168 	}
169 
170 	slept = FALSE;
171 
172 	/*
173 	 * Look for a buffer holding the desired block.
174 	 */
175 	h = BUFHASH(ch_block);
176 	FOR_BUFS_IN_CHAIN(h, bn)
177 	{
178 		bp = bufnode_buf(bn);
179 		if (bp->block == ch_block)
180 		{
181 			if (ch_offset >= bp->datasize)
182 				/*
183 				 * Need more data in this buffer.
184 				 */
185 				break;
186 			goto found;
187 		}
188 	}
189 	if (bn == END_OF_HCHAIN(h))
190 	{
191 		/*
192 		 * Block is not in a buffer.
193 		 * Take the least recently used buffer
194 		 * and read the desired block into it.
195 		 * If the LRU buffer has data in it,
196 		 * then maybe allocate a new buffer.
197 		 */
198 		if (ch_buftail == END_OF_CHAIN ||
199 			bufnode_buf(ch_buftail)->block != -1)
200 		{
201 			/*
202 			 * There is no empty buffer to use.
203 			 * Allocate a new buffer if:
204 			 * 1. We can't seek on this file and -b is not in effect; or
205 			 * 2. We haven't allocated the max buffers for this file yet.
206 			 */
207 			if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
208 				(maxbufs < 0 || ch_nbufs < maxbufs))
209 				if (ch_addbuf())
210 					/*
211 					 * Allocation failed: turn off autobuf.
212 					 */
213 					autobuf = OPT_OFF;
214 		}
215 		bn = ch_buftail;
216 		bp = bufnode_buf(bn);
217 		BUF_HASH_RM(bn); /* Remove from old hash chain. */
218 		bp->block = ch_block;
219 		bp->datasize = 0;
220 		BUF_HASH_INS(bn, h); /* Insert into new hash chain. */
221 	}
222 
223     read_more:
224 	pos = (ch_block * LBUFSIZE) + bp->datasize;
225 	if ((len = ch_length()) != NULL_POSITION && pos >= len)
226 		/*
227 		 * At end of file.
228 		 */
229 		return (EOI);
230 
231 	if (pos != ch_fpos)
232 	{
233 		/*
234 		 * Not at the correct position: must seek.
235 		 * If input is a pipe, we're in trouble (can't seek on a pipe).
236 		 * Some data has been lost: just return "?".
237 		 */
238 		if (!(ch_flags & CH_CANSEEK))
239 			return ('?');
240 		if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK)
241 		{
242  			error("seek error", NULL_PARG);
243 			clear_eol();
244 			return (EOI);
245  		}
246  		ch_fpos = pos;
247  	}
248 
249 	/*
250 	 * Read the block.
251 	 * If we read less than a full block, that's ok.
252 	 * We use partial block and pick up the rest next time.
253 	 */
254 	if (ch_ungotchar != -1)
255 	{
256 		bp->data[bp->datasize] = ch_ungotchar;
257 		n = 1;
258 		ch_ungotchar = -1;
259 	} else
260 	{
261 		n = iread(ch_file, &bp->data[bp->datasize],
262 			(unsigned int)(LBUFSIZE - bp->datasize));
263 	}
264 
265 	if (n == READ_INTR)
266 		return (EOI);
267 	if (n < 0)
268 	{
269 #if MSDOS_COMPILER==WIN32C
270 		if (errno != EPIPE)
271 #endif
272 		{
273 			error("read error", NULL_PARG);
274 			clear_eol();
275 		}
276 		n = 0;
277 	}
278 
279 #if LOGFILE
280 	/*
281 	 * If we have a log file, write the new data to it.
282 	 */
283 	if (!secure && logfile >= 0 && n > 0)
284 		write(logfile, (char *) &bp->data[bp->datasize], n);
285 #endif
286 
287 	ch_fpos += n;
288 	bp->datasize += n;
289 
290 	/*
291 	 * If we have read to end of file, set ch_fsize to indicate
292 	 * the position of the end of file.
293 	 */
294 	if (n == 0)
295 	{
296 		ch_fsize = pos;
297 		if (ignore_eoi)
298 		{
299 			/*
300 			 * We are ignoring EOF.
301 			 * Wait a while, then try again.
302 			 */
303 			if (!slept)
304 			{
305 				PARG parg;
306 				parg.p_string = wait_message();
307 				ierror("%s", &parg);
308 			}
309 #if !MSDOS_COMPILER
310 	 		sleep(1);
311 #else
312 #if MSDOS_COMPILER==WIN32C
313 			Sleep(1000);
314 #endif
315 #endif
316 			slept = TRUE;
317 
318 #if HAVE_STAT_INO
319 			if (follow_mode == FOLLOW_NAME)
320 			{
321 				/* See whether the file's i-number has changed.
322 				 * If so, force the file to be closed and
323 				 * reopened. */
324 				struct stat st;
325 				int r = stat(get_filename(curr_ifile), &st);
326 				if (r == 0 && (st.st_ino != curr_ino ||
327 					st.st_dev != curr_dev))
328 				{
329 					/* screen_trashed=2 causes
330 					 * make_display to reopen the file. */
331 					screen_trashed = 2;
332 					return (EOI);
333 				}
334 			}
335 #endif
336 		}
337 		if (sigs)
338 			return (EOI);
339 	}
340 
341     found:
342 	if (ch_bufhead != bn)
343 	{
344 		/*
345 		 * Move the buffer to the head of the buffer chain.
346 		 * This orders the buffer chain, most- to least-recently used.
347 		 */
348 		BUF_RM(bn);
349 		BUF_INS_HEAD(bn);
350 
351 		/*
352 		 * Move to head of hash chain too.
353 		 */
354 		BUF_HASH_RM(bn);
355 		BUF_HASH_INS(bn, h);
356 	}
357 
358 	if (ch_offset >= bp->datasize)
359 		/*
360 		 * After all that, we still don't have enough data.
361 		 * Go back and try again.
362 		 */
363 		goto read_more;
364 
365 	return (bp->data[ch_offset]);
366 }
367 
368 /*
369  * ch_ungetchar is a rather kludgy and limited way to push
370  * a single char onto an input file descriptor.
371  */
372 	public void
373 ch_ungetchar(c)
374 	int c;
375 {
376 	if (c != -1 && ch_ungotchar != -1)
377 		error("ch_ungetchar overrun", NULL_PARG);
378 	ch_ungotchar = c;
379 }
380 
381 #if LOGFILE
382 /*
383  * Close the logfile.
384  * If we haven't read all of standard input into it, do that now.
385  */
386 	public void
387 end_logfile()
388 {
389 	static int tried = FALSE;
390 
391 	if (logfile < 0)
392 		return;
393 	if (!tried && ch_fsize == NULL_POSITION)
394 	{
395 		tried = TRUE;
396 		ierror("Finishing logfile", NULL_PARG);
397 		while (ch_forw_get() != EOI)
398 			if (ABORT_SIGS())
399 				break;
400 	}
401 	close(logfile);
402 	logfile = -1;
403 	namelogfile = NULL;
404 }
405 
406 /*
407  * Start a log file AFTER less has already been running.
408  * Invoked from the - command; see toggle_option().
409  * Write all the existing buffered data to the log file.
410  */
411 	public void
412 sync_logfile()
413 {
414 	register struct buf *bp;
415 	register struct bufnode *bn;
416 	int warned = FALSE;
417 	BLOCKNUM block;
418 	BLOCKNUM nblocks;
419 
420 	nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
421 	for (block = 0;  block < nblocks;  block++)
422 	{
423 		int wrote = FALSE;
424 		FOR_BUFS(bn)
425 		{
426 			bp = bufnode_buf(bn);
427 			if (bp->block == block)
428 			{
429 				write(logfile, (char *) bp->data, bp->datasize);
430 				wrote = TRUE;
431 				break;
432 			}
433 		}
434 		if (!wrote && !warned)
435 		{
436 			error("Warning: log file is incomplete",
437 				NULL_PARG);
438 			warned = TRUE;
439 		}
440 	}
441 }
442 
443 #endif
444 
445 /*
446  * Determine if a specific block is currently in one of the buffers.
447  */
448 	static int
449 buffered(block)
450 	BLOCKNUM block;
451 {
452 	register struct buf *bp;
453 	register struct bufnode *bn;
454 	register int h;
455 
456 	h = BUFHASH(block);
457 	FOR_BUFS_IN_CHAIN(h, bn)
458 	{
459 		bp = bufnode_buf(bn);
460 		if (bp->block == block)
461 			return (TRUE);
462 	}
463 	return (FALSE);
464 }
465 
466 /*
467  * Seek to a specified position in the file.
468  * Return 0 if successful, non-zero if can't seek there.
469  */
470 	public int
471 ch_seek(pos)
472 	register POSITION pos;
473 {
474 	BLOCKNUM new_block;
475 	POSITION len;
476 
477 	if (thisfile == NULL)
478 		return (0);
479 
480 	len = ch_length();
481 	if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
482 		return (1);
483 
484 	new_block = pos / LBUFSIZE;
485 	if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
486 	{
487 		if (ch_fpos > pos)
488 			return (1);
489 		while (ch_fpos < pos)
490 		{
491 			if (ch_forw_get() == EOI)
492 				return (1);
493 			if (ABORT_SIGS())
494 				return (1);
495 		}
496 		return (0);
497 	}
498 	/*
499 	 * Set read pointer.
500 	 */
501 	ch_block = new_block;
502 	ch_offset = pos % LBUFSIZE;
503 	return (0);
504 }
505 
506 /*
507  * Seek to the end of the file.
508  */
509 	public int
510 ch_end_seek()
511 {
512 	POSITION len;
513 
514 	if (thisfile == NULL)
515 		return (0);
516 
517 	if (ch_flags & CH_CANSEEK)
518 		ch_fsize = filesize(ch_file);
519 
520 	len = ch_length();
521 	if (len != NULL_POSITION)
522 		return (ch_seek(len));
523 
524 	/*
525 	 * Do it the slow way: read till end of data.
526 	 */
527 	while (ch_forw_get() != EOI)
528 		if (ABORT_SIGS())
529 			return (1);
530 	return (0);
531 }
532 
533 /*
534  * Seek to the beginning of the file, or as close to it as we can get.
535  * We may not be able to seek there if input is a pipe and the
536  * beginning of the pipe is no longer buffered.
537  */
538 	public int
539 ch_beg_seek()
540 {
541 	register struct bufnode *bn;
542 	register struct bufnode *firstbn;
543 
544 	/*
545 	 * Try a plain ch_seek first.
546 	 */
547 	if (ch_seek(ch_zero()) == 0)
548 		return (0);
549 
550 	/*
551 	 * Can't get to position 0.
552 	 * Look thru the buffers for the one closest to position 0.
553 	 */
554 	firstbn = ch_bufhead;
555 	if (firstbn == END_OF_CHAIN)
556 		return (1);
557 	FOR_BUFS(bn)
558 	{
559 		if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block)
560 			firstbn = bn;
561 	}
562 	ch_block = bufnode_buf(firstbn)->block;
563 	ch_offset = 0;
564 	return (0);
565 }
566 
567 /*
568  * Return the length of the file, if known.
569  */
570 	public POSITION
571 ch_length()
572 {
573 	if (thisfile == NULL)
574 		return (NULL_POSITION);
575 	if (ignore_eoi)
576 		return (NULL_POSITION);
577 	return (ch_fsize);
578 }
579 
580 /*
581  * Return the current position in the file.
582  */
583 	public POSITION
584 ch_tell()
585 {
586 	if (thisfile == NULL)
587 		return (NULL_POSITION);
588 	return (ch_block * LBUFSIZE) + ch_offset;
589 }
590 
591 /*
592  * Get the current char and post-increment the read pointer.
593  */
594 	public int
595 ch_forw_get()
596 {
597 	register int c;
598 
599 	if (thisfile == NULL)
600 		return (EOI);
601 	c = ch_get();
602 	if (c == EOI)
603 		return (EOI);
604 	if (ch_offset < LBUFSIZE-1)
605 		ch_offset++;
606 	else
607 	{
608 		ch_block ++;
609 		ch_offset = 0;
610 	}
611 	return (c);
612 }
613 
614 /*
615  * Pre-decrement the read pointer and get the new current char.
616  */
617 	public int
618 ch_back_get()
619 {
620 	if (thisfile == NULL)
621 		return (EOI);
622 	if (ch_offset > 0)
623 		ch_offset --;
624 	else
625 	{
626 		if (ch_block <= 0)
627 			return (EOI);
628 		if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
629 			return (EOI);
630 		ch_block--;
631 		ch_offset = LBUFSIZE-1;
632 	}
633 	return (ch_get());
634 }
635 
636 /*
637  * Set max amount of buffer space.
638  * bufspace is in units of 1024 bytes.  -1 mean no limit.
639  */
640 	public void
641 ch_setbufspace(bufspace)
642 	int bufspace;
643 {
644 	if (bufspace < 0)
645 		maxbufs = -1;
646 	else
647 	{
648 		maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE;
649 		if (maxbufs < 1)
650 			maxbufs = 1;
651 	}
652 }
653 
654 /*
655  * Flush (discard) any saved file state, including buffer contents.
656  */
657 	public void
658 ch_flush()
659 {
660 	register struct bufnode *bn;
661 
662 	if (thisfile == NULL)
663 		return;
664 
665 	if (!(ch_flags & CH_CANSEEK))
666 	{
667 		/*
668 		 * If input is a pipe, we don't flush buffer contents,
669 		 * since the contents can't be recovered.
670 		 */
671 		ch_fsize = NULL_POSITION;
672 		return;
673 	}
674 
675 	/*
676 	 * Initialize all the buffers.
677 	 */
678 	FOR_BUFS(bn)
679 	{
680 		bufnode_buf(bn)->block = -1;
681 	}
682 
683 	/*
684 	 * Figure out the size of the file, if we can.
685 	 */
686 	ch_fsize = filesize(ch_file);
687 
688 	/*
689 	 * Seek to a known position: the beginning of the file.
690 	 */
691 	ch_fpos = 0;
692 	ch_block = 0; /* ch_fpos / LBUFSIZE; */
693 	ch_offset = 0; /* ch_fpos % LBUFSIZE; */
694 
695 #if 1
696 	/*
697 	 * This is a kludge to workaround a Linux kernel bug: files in
698 	 * /proc have a size of 0 according to fstat() but have readable
699 	 * data.  They are sometimes, but not always, seekable.
700 	 * Force them to be non-seekable here.
701 	 */
702 	if (ch_fsize == 0)
703 	{
704 		ch_fsize = NULL_POSITION;
705 		ch_flags &= ~CH_CANSEEK;
706 	}
707 #endif
708 
709 	if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK)
710 	{
711 		/*
712 		 * Warning only; even if the seek fails for some reason,
713 		 * there's a good chance we're at the beginning anyway.
714 		 * {{ I think this is bogus reasoning. }}
715 		 */
716 		error("seek error to 0", NULL_PARG);
717 	}
718 }
719 
720 /*
721  * Allocate a new buffer.
722  * The buffer is added to the tail of the buffer chain.
723  */
724 	static int
725 ch_addbuf()
726 {
727 	register struct buf *bp;
728 	register struct bufnode *bn;
729 
730 	/*
731 	 * Allocate and initialize a new buffer and link it
732 	 * onto the tail of the buffer list.
733 	 */
734 	bp = (struct buf *) calloc(1, sizeof(struct buf));
735 	if (bp == NULL)
736 		return (1);
737 	ch_nbufs++;
738 	bp->block = -1;
739 	bn = &bp->node;
740 
741 	BUF_INS_TAIL(bn);
742 	BUF_HASH_INS(bn, 0);
743 	return (0);
744 }
745 
746 /*
747  *
748  */
749 	static void
750 init_hashtbl()
751 {
752 	register int h;
753 
754 	for (h = 0;  h < BUFHASH_SIZE;  h++)
755 	{
756 		thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
757 		thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
758 	}
759 }
760 
761 /*
762  * Delete all buffers for this file.
763  */
764 	static void
765 ch_delbufs()
766 {
767 	register struct bufnode *bn;
768 
769 	while (ch_bufhead != END_OF_CHAIN)
770 	{
771 		bn = ch_bufhead;
772 		BUF_RM(bn);
773 		free(bufnode_buf(bn));
774 	}
775 	ch_nbufs = 0;
776 	init_hashtbl();
777 }
778 
779 /*
780  * Is it possible to seek on a file descriptor?
781  */
782 	public int
783 seekable(f)
784 	int f;
785 {
786 #if MSDOS_COMPILER
787 	extern int fd0;
788 	if (f == fd0 && !isatty(fd0))
789 	{
790 		/*
791 		 * In MS-DOS, pipes are seekable.  Check for
792 		 * standard input, and pretend it is not seekable.
793 		 */
794 		return (0);
795 	}
796 #endif
797 	return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK);
798 }
799 
800 /*
801  * Initialize file state for a new file.
802  */
803 	public void
804 ch_init(f, flags)
805 	int f;
806 	int flags;
807 {
808 	/*
809 	 * See if we already have a filestate for this file.
810 	 */
811 	thisfile = (struct filestate *) get_filestate(curr_ifile);
812 	if (thisfile == NULL)
813 	{
814 		/*
815 		 * Allocate and initialize a new filestate.
816 		 */
817 		thisfile = (struct filestate *)
818 				calloc(1, sizeof(struct filestate));
819 		thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
820 		thisfile->nbufs = 0;
821 		thisfile->flags = 0;
822 		thisfile->fpos = 0;
823 		thisfile->block = 0;
824 		thisfile->offset = 0;
825 		thisfile->file = -1;
826 		thisfile->fsize = NULL_POSITION;
827 		ch_flags = flags;
828 		init_hashtbl();
829 		/*
830 		 * Try to seek; set CH_CANSEEK if it works.
831 		 */
832 		if ((flags & CH_CANSEEK) && !seekable(f))
833 			ch_flags &= ~CH_CANSEEK;
834 		set_filestate(curr_ifile, (void *) thisfile);
835 	}
836 	if (thisfile->file == -1)
837 		thisfile->file = f;
838 	ch_flush();
839 }
840 
841 /*
842  * Close a filestate.
843  */
844 	public void
845 ch_close()
846 {
847 	int keepstate = FALSE;
848 
849 	if (thisfile == NULL)
850 		return;
851 
852 	if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE))
853 	{
854 		/*
855 		 * We can seek or re-open, so we don't need to keep buffers.
856 		 */
857 		ch_delbufs();
858 	} else
859 		keepstate = TRUE;
860 	if (!(ch_flags & CH_KEEPOPEN))
861 	{
862 		/*
863 		 * We don't need to keep the file descriptor open
864 		 * (because we can re-open it.)
865 		 * But don't really close it if it was opened via popen(),
866 		 * because pclose() wants to close it.
867 		 */
868 		if (!(ch_flags & CH_POPENED))
869 			close(ch_file);
870 		ch_file = -1;
871 	} else
872 		keepstate = TRUE;
873 	if (!keepstate)
874 	{
875 		/*
876 		 * We don't even need to keep the filestate structure.
877 		 */
878 		free(thisfile);
879 		thisfile = NULL;
880 		set_filestate(curr_ifile, (void *) NULL);
881 	}
882 }
883 
884 /*
885  * Return ch_flags for the current file.
886  */
887 	public int
888 ch_getflags()
889 {
890 	if (thisfile == NULL)
891 		return (0);
892 	return (ch_flags);
893 }
894 
895 #if 0
896 	public void
897 ch_dump(struct filestate *fs)
898 {
899 	struct buf *bp;
900 	struct bufnode *bn;
901 	unsigned char *s;
902 
903 	if (fs == NULL)
904 	{
905 		printf(" --no filestate\n");
906 		return;
907 	}
908 	printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
909 		fs->file, fs->flags, fs->fpos,
910 		fs->fsize, fs->block, fs->offset);
911 	printf(" %d bufs:\n", fs->nbufs);
912 	for (bn = fs->next; bn != &fs->buflist;  bn = bn->next)
913 	{
914 		bp = bufnode_buf(bn);
915 		printf("%x: blk %x, size %x \"",
916 			bp, bp->block, bp->datasize);
917 		for (s = bp->data;  s < bp->data + 30;  s++)
918 			if (*s >= ' ' && *s < 0x7F)
919 				printf("%c", *s);
920 			else
921 				printf(".");
922 		printf("\"\n");
923 	}
924 }
925 #endif
926