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

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