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

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