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