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

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