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

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.19      deraadt    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.19      deraadt   107:        for ((bn) = thisfile->hashtbl[h].hnext; \
1.17      mmcc      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.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.15      deraadt   229:                if (lseek(ch_file, (off_t)pos, SEEK_SET) == (off_t)-1) {
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.21      deraadt   306:                if (any_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)
1.21      deraadt   363:                        if (abort_sigs())
1.1       etheisen  364:                                break;
                    365:        }
                    366:        close(logfile);
                    367:        logfile = -1;
1.20      krw       368:        free(namelogfile);
1.1       etheisen  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.19      deraadt   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);
1.21      deraadt   449:                        if (abort_sigs())
1.1       etheisen  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)
1.21      deraadt   484:                if (abort_sigs())
1.1       etheisen  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.15      deraadt   646:        if (lseek(ch_file, (off_t)0, SEEK_SET) == (off_t)-1) {
1.1       etheisen  647:                /*
                    648:                 * Warning only; even if the seek fails for some reason,
                    649:                 * there's a good chance we're at the beginning anyway.
                    650:                 * {{ I think this is bogus reasoning. }}
                    651:                 */
1.14      deraadt   652:                error("seek error to 0", NULL);
1.1       etheisen  653:        }
                    654: }
                    655:
                    656: /*
                    657:  * Allocate a new buffer.
                    658:  * The buffer is added to the tail of the buffer chain.
                    659:  */
1.11      nicm      660: static int
                    661: ch_addbuf(void)
1.1       etheisen  662: {
1.11      nicm      663:        struct buf *bp;
                    664:        struct bufnode *bn;
1.1       etheisen  665:
                    666:        /*
1.11      nicm      667:         * Allocate and initialize a new buffer and link it
1.1       etheisen  668:         * onto the tail of the buffer list.
                    669:         */
1.11      nicm      670:        bp = calloc(1, sizeof (struct buf));
1.1       etheisen  671:        if (bp == NULL)
                    672:                return (1);
                    673:        ch_nbufs++;
1.4       millert   674:        bp->block = -1;
1.8       shadchin  675:        bn = &bp->node;
                    676:
                    677:        BUF_INS_TAIL(bn);
                    678:        BUF_HASH_INS(bn, 0);
1.1       etheisen  679:        return (0);
                    680: }
                    681:
                    682: /*
1.4       millert   683:  *
                    684:  */
1.11      nicm      685: static void
                    686: init_hashtbl(void)
1.4       millert   687: {
1.11      nicm      688:        int h;
1.4       millert   689:
1.11      nicm      690:        for (h = 0; h < BUFHASH_SIZE; h++) {
1.8       shadchin  691:                thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
                    692:                thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
1.4       millert   693:        }
                    694: }
                    695:
                    696: /*
1.1       etheisen  697:  * Delete all buffers for this file.
                    698:  */
1.11      nicm      699: static void
                    700: ch_delbufs(void)
1.1       etheisen  701: {
1.11      nicm      702:        struct bufnode *bn;
1.1       etheisen  703:
1.11      nicm      704:        while (ch_bufhead != END_OF_CHAIN) {
1.8       shadchin  705:                bn = ch_bufhead;
                    706:                BUF_RM(bn);
                    707:                free(bufnode_buf(bn));
1.1       etheisen  708:        }
                    709:        ch_nbufs = 0;
1.4       millert   710:        init_hashtbl();
1.1       etheisen  711: }
                    712:
                    713: /*
                    714:  * Is it possible to seek on a file descriptor?
                    715:  */
1.11      nicm      716: int
                    717: seekable(int f)
                    718: {
1.15      deraadt   719:        return (lseek(f, (off_t)1, SEEK_SET) != (off_t)-1);
1.1       etheisen  720: }
1.10      shadchin  721:
                    722: /*
                    723:  * Force EOF to be at the current read position.
                    724:  * This is used after an ignore_eof read, during which the EOF may change.
                    725:  */
1.11      nicm      726: void
                    727: ch_set_eof(void)
1.10      shadchin  728: {
                    729:        ch_fsize = ch_fpos;
                    730: }
                    731:
1.1       etheisen  732:
                    733: /*
                    734:  * Initialize file state for a new file.
                    735:  */
1.11      nicm      736: void
                    737: ch_init(int f, int flags)
1.1       etheisen  738: {
                    739:        /*
                    740:         * See if we already have a filestate for this file.
                    741:         */
1.11      nicm      742:        thisfile = get_filestate(curr_ifile);
                    743:        if (thisfile == NULL) {
1.1       etheisen  744:                /*
                    745:                 * Allocate and initialize a new filestate.
                    746:                 */
1.11      nicm      747:                thisfile = calloc(1, sizeof (struct filestate));
1.8       shadchin  748:                thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
1.1       etheisen  749:                thisfile->nbufs = 0;
                    750:                thisfile->flags = 0;
                    751:                thisfile->fpos = 0;
                    752:                thisfile->block = 0;
                    753:                thisfile->offset = 0;
                    754:                thisfile->file = -1;
1.11      nicm      755:                thisfile->fsize = -1;
1.1       etheisen  756:                ch_flags = flags;
1.4       millert   757:                init_hashtbl();
1.1       etheisen  758:                /*
                    759:                 * Try to seek; set CH_CANSEEK if it works.
                    760:                 */
1.4       millert   761:                if ((flags & CH_CANSEEK) && !seekable(f))
                    762:                        ch_flags &= ~CH_CANSEEK;
1.1       etheisen  763:                set_filestate(curr_ifile, (void *) thisfile);
                    764:        }
                    765:        if (thisfile->file == -1)
                    766:                thisfile->file = f;
                    767:        ch_flush();
                    768: }
                    769:
                    770: /*
                    771:  * Close a filestate.
                    772:  */
1.11      nicm      773: void
                    774: ch_close(void)
1.1       etheisen  775: {
                    776:        int keepstate = FALSE;
                    777:
1.8       shadchin  778:        if (thisfile == NULL)
                    779:                return;
                    780:
1.23    ! guenther  781:        if (ch_flags & (CH_CANSEEK|CH_HELPFILE)) {
1.1       etheisen  782:                /*
                    783:                 * We can seek or re-open, so we don't need to keep buffers.
                    784:                 */
                    785:                ch_delbufs();
1.11      nicm      786:        } else {
1.1       etheisen  787:                keepstate = TRUE;
1.11      nicm      788:        }
                    789:        if (!(ch_flags & CH_KEEPOPEN)) {
1.1       etheisen  790:                /*
                    791:                 * We don't need to keep the file descriptor open
                    792:                 * (because we can re-open it.)
                    793:                 */
1.23    ! guenther  794:                close(ch_file);
1.1       etheisen  795:                ch_file = -1;
1.11      nicm      796:        } else {
1.1       etheisen  797:                keepstate = TRUE;
1.11      nicm      798:        }
                    799:        if (!keepstate) {
1.1       etheisen  800:                /*
                    801:                 * We don't even need to keep the filestate structure.
                    802:                 */
                    803:                free(thisfile);
                    804:                thisfile = NULL;
1.11      nicm      805:                set_filestate(curr_ifile, NULL);
1.1       etheisen  806:        }
                    807: }
                    808:
                    809: /*
                    810:  * Return ch_flags for the current file.
                    811:  */
1.11      nicm      812: int
                    813: ch_getflags(void)
1.1       etheisen  814: {
1.8       shadchin  815:        if (thisfile == NULL)
                    816:                return (0);
1.1       etheisen  817:        return (ch_flags);
                    818: }