[BACK]Return to ch.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/ch.c, Revision 1.3

1.3     ! mpech       1: /*     $OpenBSD: ch.c,v 1.2 2001/01/29 01:58:00 niklas Exp $   */
1.2       niklas      2:
1.1       etheisen    3: /*
                      4:  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice in the documentation and/or other materials provided with
                     14:  *    the distribution.
                     15:  *
                     16:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
                     17:  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     18:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     19:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
                     20:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     21:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
                     22:  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
                     23:  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     24:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
                     25:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
                     26:  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     27:  */
                     28:
                     29:
                     30: /*
                     31:  * Low level character input from the input file.
                     32:  * We use these special purpose routines which optimize moving
                     33:  * both forward and backward from the current read pointer.
                     34:  */
                     35:
                     36: #include "less.h"
                     37:
                     38: public int ignore_eoi;
                     39:
                     40: /*
                     41:  * Pool of buffers holding the most recently used blocks of the input file.
                     42:  * The buffer pool is kept as a doubly-linked circular list,
                     43:  * in order from most- to least-recently used.
                     44:  * The circular list is anchored by the file state "thisfile".
                     45:  */
                     46: #define LBUFSIZE       1024
                     47: struct buf {
                     48:        struct buf *next, *prev;  /* Must be first to match struct filestate */
                     49:        long block;
                     50:        unsigned int datasize;
                     51:        unsigned char data[LBUFSIZE];
                     52: };
                     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: struct filestate {
                     59:        /* -- Following members must match struct buf */
                     60:        struct buf *buf_next, *buf_prev;
                     61:        long buf_block;
                     62:        /* -- End of struct buf copy */
                     63:        int file;
                     64:        int flags;
                     65:        POSITION fpos;
                     66:        int nbufs;
                     67:        long block;
                     68:        int offset;
                     69:        POSITION fsize;
                     70: };
                     71:
                     72:
                     73: #define        END_OF_CHAIN    ((struct buf *)thisfile)
                     74: #define        ch_bufhead      thisfile->buf_next
                     75: #define        ch_buftail      thisfile->buf_prev
                     76: #define        ch_nbufs        thisfile->nbufs
                     77: #define        ch_block        thisfile->block
                     78: #define        ch_offset       thisfile->offset
                     79: #define        ch_fpos         thisfile->fpos
                     80: #define        ch_fsize        thisfile->fsize
                     81: #define        ch_flags        thisfile->flags
                     82: #define        ch_file         thisfile->file
                     83:
                     84: static struct filestate *thisfile;
                     85: static int ch_ungotchar = -1;
                     86:
                     87: extern int autobuf;
                     88: extern int sigs;
                     89: extern int cbufs;
                     90: extern IFILE curr_ifile;
                     91: #if LOGFILE
                     92: extern int logfile;
                     93: extern char *namelogfile;
                     94: #endif
                     95:
                     96: static int ch_addbuf();
                     97:
                     98:
                     99: /*
                    100:  * Get the character pointed to by the read pointer.
                    101:  * ch_get() is a macro which is more efficient to call
                    102:  * than fch_get (the function), in the usual case
                    103:  * that the block desired is at the head of the chain.
                    104:  */
                    105: #define        ch_get()   ((ch_block == ch_bufhead->block && \
                    106:                     ch_offset < ch_bufhead->datasize) ? \
                    107:                        ch_bufhead->data[ch_offset] : fch_get())
                    108:        int
                    109: fch_get()
                    110: {
1.3     ! mpech     111:        struct buf *bp;
        !           112:        int n;
        !           113:        int slept;
1.1       etheisen  114:        POSITION pos;
                    115:        POSITION len;
                    116:
                    117:        slept = FALSE;
                    118:
                    119:        /*
                    120:         * Look for a buffer holding the desired block.
                    121:         */
                    122:        for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
                    123:                if (bp->block == ch_block)
                    124:                {
                    125:                        if (ch_offset >= bp->datasize)
                    126:                                /*
                    127:                                 * Need more data in this buffer.
                    128:                                 */
                    129:                                goto read_more;
                    130:                        goto found;
                    131:                }
                    132:        /*
                    133:         * Block is not in a buffer.
                    134:         * Take the least recently used buffer
                    135:         * and read the desired block into it.
                    136:         * If the LRU buffer has data in it,
                    137:         * then maybe allocate a new buffer.
                    138:         */
                    139:        if (ch_buftail == END_OF_CHAIN || ch_buftail->block != (long)(-1))
                    140:        {
                    141:                /*
                    142:                 * There is no empty buffer to use.
                    143:                 * Allocate a new buffer if:
                    144:                 * 1. We can't seek on this file and -b is not in effect; or
                    145:                 * 2. We haven't allocated the max buffers for this file yet.
                    146:                 */
                    147:                if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
                    148:                    (cbufs == -1 || ch_nbufs < cbufs))
                    149:                        if (ch_addbuf())
                    150:                                /*
                    151:                                 * Allocation failed: turn off autobuf.
                    152:                                 */
                    153:                                autobuf = OPT_OFF;
                    154:        }
                    155:        bp = ch_buftail;
                    156:        bp->block = ch_block;
                    157:        bp->datasize = 0;
                    158:
                    159:     read_more:
                    160:        pos = (ch_block * LBUFSIZE) + bp->datasize;
                    161:        if ((len = ch_length()) != NULL_POSITION && pos >= len)
                    162:                /*
                    163:                 * At end of file.
                    164:                 */
                    165:                return (EOI);
                    166:
                    167:        if (pos != ch_fpos)
                    168:        {
                    169:                /*
                    170:                 * Not at the correct position: must seek.
                    171:                 * If input is a pipe, we're in trouble (can't seek on a pipe).
                    172:                 * Some data has been lost: just return "?".
                    173:                 */
                    174:                if (!(ch_flags & CH_CANSEEK))
                    175:                        return ('?');
                    176:                if (lseek(ch_file, (off_t)pos, 0) == BAD_LSEEK)
                    177:                {
                    178:                        error("seek error", NULL_PARG);
                    179:                        clear_eol();
                    180:                        return (EOI);
                    181:                }
                    182:                ch_fpos = pos;
                    183:        }
                    184:
                    185:        /*
                    186:         * Read the block.
                    187:         * If we read less than a full block, that's ok.
                    188:         * We use partial block and pick up the rest next time.
                    189:         */
                    190:        if (ch_ungotchar == -1)
                    191:        {
                    192:                n = iread(ch_file, &bp->data[bp->datasize],
                    193:                        (unsigned int)(LBUFSIZE - bp->datasize));
                    194:        } else
                    195:        {
                    196:                bp->data[bp->datasize] = ch_ungotchar;
                    197:                n = 1;
                    198:                ch_ungotchar = -1;
                    199:        }
                    200:
                    201:        if (n == READ_INTR)
                    202:                return (EOI);
                    203:        if (n < 0)
                    204:        {
                    205:                error("read error", NULL_PARG);
                    206:                clear_eol();
                    207:                n = 0;
                    208:        }
                    209:
                    210: #if LOGFILE
                    211:        /*
                    212:         * If we have a log file, write the new data to it.
                    213:         */
                    214:        if (logfile >= 0 && n > 0)
                    215:                write(logfile, (char *) &bp->data[bp->datasize], n);
                    216: #endif
                    217:
                    218:        ch_fpos += n;
                    219:        bp->datasize += n;
                    220:
                    221:        /*
                    222:         * If we have read to end of file, set ch_fsize to indicate
                    223:         * the position of the end of file.
                    224:         */
                    225:        if (n == 0)
                    226:        {
                    227:                ch_fsize = pos;
                    228:                if (ignore_eoi)
                    229:                {
                    230:                        /*
                    231:                         * We are ignoring EOF.
                    232:                         * Wait a while, then try again.
                    233:                         */
                    234:                        if (!slept)
                    235:                                ierror("Waiting for data", NULL_PARG);
                    236: #if !MSOFTC
                    237:                        sleep(1);
                    238: #endif
                    239:                        slept = TRUE;
                    240:                }
                    241:                if (ABORT_SIGS())
                    242:                        return (EOI);
                    243:        }
                    244:
                    245:     found:
                    246:        if (ch_bufhead != bp)
                    247:        {
                    248:                /*
                    249:                 * Move the buffer to the head of the buffer chain.
                    250:                 * This orders the buffer chain, most- to least-recently used.
                    251:                 */
                    252:                bp->next->prev = bp->prev;
                    253:                bp->prev->next = bp->next;
                    254:
                    255:                bp->next = ch_bufhead;
                    256:                bp->prev = END_OF_CHAIN;
                    257:                ch_bufhead->prev = bp;
                    258:                ch_bufhead = bp;
                    259:        }
                    260:
                    261:        if (ch_offset >= bp->datasize)
                    262:                /*
                    263:                 * After all that, we still don't have enough data.
                    264:                 * Go back and try again.
                    265:                 */
                    266:                goto read_more;
                    267:
                    268:        return (bp->data[ch_offset]);
                    269: }
                    270:
                    271: /*
                    272:  * ch_ungetchar is a rather kludgy and limited way to push
                    273:  * a single char onto an input file descriptor.
                    274:  */
                    275:        public void
                    276: ch_ungetchar(c)
                    277:        int c;
                    278: {
                    279:        if (c != -1 && ch_ungotchar != -1)
                    280:                error("ch_ungetchar overrun", NULL_PARG);
                    281:        ch_ungotchar = c;
                    282: }
                    283:
                    284: #if LOGFILE
                    285: /*
                    286:  * Close the logfile.
                    287:  * If we haven't read all of standard input into it, do that now.
                    288:  */
                    289:        public void
                    290: end_logfile()
                    291: {
                    292:        static int tried = FALSE;
                    293:
                    294:        if (logfile < 0)
                    295:                return;
                    296:        if (!tried && ch_fsize == NULL_POSITION)
                    297:        {
                    298:                tried = TRUE;
                    299:                ierror("Finishing logfile", NULL_PARG);
                    300:                while (ch_forw_get() != EOI)
                    301:                        if (ABORT_SIGS())
                    302:                                break;
                    303:        }
                    304:        close(logfile);
                    305:        logfile = -1;
                    306:        namelogfile = NULL;
                    307: }
                    308:
                    309: /*
                    310:  * Start a log file AFTER less has already been running.
                    311:  * Invoked from the - command; see toggle_option().
                    312:  * Write all the existing buffered data to the log file.
                    313:  */
                    314:        public void
                    315: sync_logfile()
                    316: {
1.3     ! mpech     317:        struct buf *bp;
1.1       etheisen  318:        int warned = FALSE;
                    319:        long block;
                    320:        long nblocks;
                    321:
                    322:        nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
                    323:        for (block = 0;  block < nblocks;  block++)
                    324:        {
                    325:                for (bp = ch_bufhead;  ;  bp = bp->next)
                    326:                {
                    327:                        if (bp == END_OF_CHAIN)
                    328:                        {
                    329:                                if (!warned)
                    330:                                {
                    331:                                        error("Warning: log file is incomplete",
                    332:                                                NULL_PARG);
                    333:                                        warned = TRUE;
                    334:                                }
                    335:                                break;
                    336:                        }
                    337:                        if (bp->block == block)
                    338:                        {
                    339:                                write(logfile, (char *) bp->data, bp->datasize);
                    340:                                break;
                    341:                        }
                    342:                }
                    343:        }
                    344: }
                    345:
                    346: #endif
                    347:
                    348: /*
                    349:  * Determine if a specific block is currently in one of the buffers.
                    350:  */
                    351:        static int
                    352: buffered(block)
                    353:        long block;
                    354: {
1.3     ! mpech     355:        struct buf *bp;
1.1       etheisen  356:
                    357:        for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
                    358:                if (bp->block == block)
                    359:                        return (TRUE);
                    360:        return (FALSE);
                    361: }
                    362:
                    363: /*
                    364:  * Seek to a specified position in the file.
                    365:  * Return 0 if successful, non-zero if can't seek there.
                    366:  */
                    367:        public int
                    368: ch_seek(pos)
1.3     ! mpech     369:        POSITION pos;
1.1       etheisen  370: {
                    371:        long new_block;
                    372:        POSITION len;
                    373:
                    374:        len = ch_length();
                    375:        if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
                    376:                return (1);
                    377:
                    378:        new_block = pos / LBUFSIZE;
                    379:        if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
                    380:        {
                    381:                if (ch_fpos > pos)
                    382:                        return (1);
                    383:                while (ch_fpos < pos)
                    384:                {
                    385:                        if (ch_forw_get() == EOI)
                    386:                                return (1);
                    387:                        if (ABORT_SIGS())
                    388:                                return (1);
                    389:                }
                    390:                return (0);
                    391:        }
                    392:        /*
                    393:         * Set read pointer.
                    394:         */
                    395:        ch_block = new_block;
                    396:        ch_offset = pos % LBUFSIZE;
                    397:        return (0);
                    398: }
                    399:
                    400: /*
                    401:  * Seek to the end of the file.
                    402:  */
                    403:        public int
                    404: ch_end_seek()
                    405: {
                    406:        POSITION len;
                    407:
                    408:        if (ch_flags & CH_CANSEEK)
                    409:                ch_fsize = filesize(ch_file);
                    410:
                    411:        len = ch_length();
                    412:        if (len != NULL_POSITION)
                    413:                return (ch_seek(len));
                    414:
                    415:        /*
                    416:         * Do it the slow way: read till end of data.
                    417:         */
                    418:        while (ch_forw_get() != EOI)
                    419:                if (ABORT_SIGS())
                    420:                        return (1);
                    421:        return (0);
                    422: }
                    423:
                    424: /*
                    425:  * Seek to the beginning of the file, or as close to it as we can get.
                    426:  * We may not be able to seek there if input is a pipe and the
                    427:  * beginning of the pipe is no longer buffered.
                    428:  */
                    429:        public int
                    430: ch_beg_seek()
                    431: {
1.3     ! mpech     432:        struct buf *bp, *firstbp;
1.1       etheisen  433:
                    434:        /*
                    435:         * Try a plain ch_seek first.
                    436:         */
                    437:        if (ch_seek(ch_zero()) == 0)
                    438:                return (0);
                    439:
                    440:        /*
                    441:         * Can't get to position 0.
                    442:         * Look thru the buffers for the one closest to position 0.
                    443:         */
                    444:        firstbp = bp = ch_bufhead;
                    445:        if (bp == END_OF_CHAIN)
                    446:                return (1);
                    447:        while ((bp = bp->next) != END_OF_CHAIN)
                    448:                if (bp->block < firstbp->block)
                    449:                        firstbp = bp;
                    450:        ch_block = firstbp->block;
                    451:        ch_offset = 0;
                    452:        return (0);
                    453: }
                    454:
                    455: /*
                    456:  * Return the length of the file, if known.
                    457:  */
                    458:        public POSITION
                    459: ch_length()
                    460: {
                    461:        if (ignore_eoi)
                    462:                return (NULL_POSITION);
                    463:        return (ch_fsize);
                    464: }
                    465:
                    466: /*
                    467:  * Return the current position in the file.
                    468:  */
                    469: #define        tellpos(blk,off)   ((POSITION)((((long)(blk)) * LBUFSIZE) + (off)))
                    470:
                    471:        public POSITION
                    472: ch_tell()
                    473: {
                    474:        return (tellpos(ch_block, ch_offset));
                    475: }
                    476:
                    477: /*
                    478:  * Get the current char and post-increment the read pointer.
                    479:  */
                    480:        public int
                    481: ch_forw_get()
                    482: {
1.3     ! mpech     483:        int c;
1.1       etheisen  484:
                    485:        c = ch_get();
                    486:        if (c == EOI)
                    487:                return (EOI);
                    488:        if (ch_offset < LBUFSIZE-1)
                    489:                ch_offset++;
                    490:        else
                    491:        {
                    492:                ch_block ++;
                    493:                ch_offset = 0;
                    494:        }
                    495:        return (c);
                    496: }
                    497:
                    498: /*
                    499:  * Pre-decrement the read pointer and get the new current char.
                    500:  */
                    501:        public int
                    502: ch_back_get()
                    503: {
                    504:        if (ch_offset > 0)
                    505:                ch_offset --;
                    506:        else
                    507:        {
                    508:                if (ch_block <= 0)
                    509:                        return (EOI);
                    510:                if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
                    511:                        return (EOI);
                    512:                ch_block--;
                    513:                ch_offset = LBUFSIZE-1;
                    514:        }
                    515:        return (ch_get());
                    516: }
                    517:
                    518: /*
                    519:  * Allocate buffers.
                    520:  * Caller wants us to have a total of at least want_nbufs buffers.
                    521:  */
                    522:        public int
                    523: ch_nbuf(want_nbufs)
                    524:        int want_nbufs;
                    525: {
                    526:        PARG parg;
                    527:
                    528:        while (ch_nbufs < want_nbufs)
                    529:        {
                    530:                if (ch_addbuf())
                    531:                {
                    532:                        /*
                    533:                         * Cannot allocate enough buffers.
                    534:                         * If we don't have ANY, then quit.
                    535:                         * Otherwise, just report the error and return.
                    536:                         */
                    537:                        parg.p_int = want_nbufs - ch_nbufs;
                    538:                        error("Cannot allocate %d buffers", &parg);
                    539:                        if (ch_nbufs == 0)
                    540:                                quit(QUIT_ERROR);
                    541:                        break;
                    542:                }
                    543:        }
                    544:        return (ch_nbufs);
                    545: }
                    546:
                    547: /*
                    548:  * Flush (discard) any saved file state, including buffer contents.
                    549:  */
                    550:        public void
                    551: ch_flush()
                    552: {
1.3     ! mpech     553:        struct buf *bp;
1.1       etheisen  554:
                    555:        if (!(ch_flags & CH_CANSEEK))
                    556:        {
                    557:                /*
                    558:                 * If input is a pipe, we don't flush buffer contents,
                    559:                 * since the contents can't be recovered.
                    560:                 */
                    561:                ch_fsize = NULL_POSITION;
                    562:                return;
                    563:        }
                    564:
                    565:        /*
                    566:         * Initialize all the buffers.
                    567:         */
                    568:        for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
                    569:                bp->block = (long)(-1);
                    570:
                    571:        /*
                    572:         * Figure out the size of the file, if we can.
                    573:         */
                    574:        ch_fsize = filesize(ch_file);
                    575:
                    576:        /*
                    577:         * Seek to a known position: the beginning of the file.
                    578:         */
                    579:        ch_fpos = 0;
                    580:        ch_block = 0; /* ch_fpos / LBUFSIZE; */
                    581:        ch_offset = 0; /* ch_fpos % LBUFSIZE; */
                    582:
                    583:        if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK)
                    584:        {
                    585:                /*
                    586:                 * Warning only; even if the seek fails for some reason,
                    587:                 * there's a good chance we're at the beginning anyway.
                    588:                 * {{ I think this is bogus reasoning. }}
                    589:                 */
                    590:                error("seek error to 0", NULL_PARG);
                    591:        }
                    592: }
                    593:
                    594: /*
                    595:  * Allocate a new buffer.
                    596:  * The buffer is added to the tail of the buffer chain.
                    597:  */
                    598:        static int
                    599: ch_addbuf()
                    600: {
1.3     ! mpech     601:        struct buf *bp;
1.1       etheisen  602:
                    603:        /*
                    604:         * Allocate and initialize a new buffer and link it
                    605:         * onto the tail of the buffer list.
                    606:         */
                    607:        bp = (struct buf *) calloc(1, sizeof(struct buf));
                    608:        if (bp == NULL)
                    609:                return (1);
                    610:        ch_nbufs++;
                    611:        bp->block = (long)(-1);
                    612:        bp->next = END_OF_CHAIN;
                    613:        bp->prev = ch_buftail;
                    614:        ch_buftail->next = bp;
                    615:        ch_buftail = bp;
                    616:        return (0);
                    617: }
                    618:
                    619: /*
                    620:  * Delete all buffers for this file.
                    621:  */
                    622:        static void
                    623: ch_delbufs()
                    624: {
1.3     ! mpech     625:        struct buf *bp;
1.1       etheisen  626:
                    627:        while (ch_bufhead != END_OF_CHAIN)
                    628:        {
                    629:                bp = ch_bufhead;
                    630:                bp->next->prev = bp->prev;;
                    631:                bp->prev->next = bp->next;
                    632:                free(bp);
                    633:        }
                    634:        ch_nbufs = 0;
                    635: }
                    636:
                    637: /*
                    638:  * Is it possible to seek on a file descriptor?
                    639:  */
                    640:        public int
                    641: seekable(f)
                    642:        int f;
                    643: {
                    644:        return (lseek(f, (off_t)1, 0) != BAD_LSEEK);
                    645: }
                    646:
                    647: /*
                    648:  * Initialize file state for a new file.
                    649:  */
                    650:        public void
                    651: ch_init(f, flags)
                    652:        int f;
                    653:        int flags;
                    654: {
                    655:        /*
                    656:         * See if we already have a filestate for this file.
                    657:         */
                    658:        thisfile = (struct filestate *) get_filestate(curr_ifile);
                    659:        if (thisfile == NULL)
                    660:        {
                    661:                /*
                    662:                 * Allocate and initialize a new filestate.
                    663:                 */
                    664:                thisfile = (struct filestate *)
                    665:                                calloc(1, sizeof(struct filestate));
                    666:                thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN;
                    667:                thisfile->buf_block = (long)(-1);
                    668:                thisfile->nbufs = 0;
                    669:                thisfile->flags = 0;
                    670:                thisfile->fpos = 0;
                    671:                thisfile->block = 0;
                    672:                thisfile->offset = 0;
                    673:                thisfile->file = -1;
                    674:                thisfile->fsize = NULL_POSITION;
                    675:                ch_flags = flags;
                    676:                /*
                    677:                 * Try to seek; set CH_CANSEEK if it works.
                    678:                 */
                    679:                if (seekable(f))
                    680:                        ch_flags |= CH_CANSEEK;
                    681:                set_filestate(curr_ifile, (void *) thisfile);
                    682:        }
                    683:        if (thisfile->file == -1)
                    684:                thisfile->file = f;
                    685:        ch_flush();
                    686: }
                    687:
                    688: /*
                    689:  * Close a filestate.
                    690:  */
                    691:        public void
                    692: ch_close()
                    693: {
                    694:        int keepstate = FALSE;
                    695:
                    696:        if (ch_flags & (CH_CANSEEK|CH_POPENED))
                    697:        {
                    698:                /*
                    699:                 * We can seek or re-open, so we don't need to keep buffers.
                    700:                 */
                    701:                ch_delbufs();
                    702:        } else
                    703:                keepstate = TRUE;
                    704:        if (!(ch_flags & CH_KEEPOPEN))
                    705:        {
                    706:                /*
                    707:                 * We don't need to keep the file descriptor open
                    708:                 * (because we can re-open it.)
                    709:                 * But don't really close it if it was opened via popen(),
                    710:                 * because pclose() wants to close it.
                    711:                 */
                    712:                if (!(ch_flags & CH_POPENED))
                    713:                        close(ch_file);
                    714:                ch_file = -1;
                    715:        } else
                    716:                keepstate = TRUE;
                    717:        if (!keepstate)
                    718:        {
                    719:                /*
                    720:                 * We don't even need to keep the filestate structure.
                    721:                 */
                    722:                free(thisfile);
                    723:                thisfile = NULL;
                    724:                set_filestate(curr_ifile, (void *) NULL);
                    725:        }
                    726: }
                    727:
                    728: /*
                    729:  * Return ch_flags for the current file.
                    730:  */
                    731:        public int
                    732: ch_getflags()
                    733: {
                    734:        return (ch_flags);
                    735: }
                    736:
                    737: #if 0
                    738:        public void
                    739: ch_dump(struct filestate *fs)
                    740: {
                    741:        struct buf *bp;
                    742:        unsigned char *s;
                    743:
                    744:        if (fs == NULL)
                    745:        {
                    746:                printf(" --no filestate\n");
                    747:                return;
                    748:        }
                    749:        printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
                    750:                fs->file, fs->flags, fs->fpos,
                    751:                fs->fsize, fs->block, fs->offset);
                    752:        printf(" %d bufs:\n", fs->nbufs);
                    753:        for (bp = fs->buf_next; bp != (struct buf *)fs;  bp = bp->next)
                    754:        {
                    755:                printf("%x: blk %x, size %x \"",
                    756:                        bp, bp->block, bp->datasize);
                    757:                for (s = bp->data;  s < bp->data + 30;  s++)
                    758:                        if (*s >= ' ' && *s < 0x7F)
                    759:                                printf("%c", *s);
                    760:                        else
                    761:                                printf(".");
                    762:                printf("\"\n");
                    763:        }
                    764: }
                    765: #endif