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

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