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

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