"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/ch.c" of archive less-424.tar.gz:


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