home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / ch.c < prev    next >
C/C++ Source or Header  |  2002-01-14  |  16KB  |  842 lines

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