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

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