[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.1

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