Annotation of 43BSDReno/contrib/mh/miscellany/less/ch.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Low level character input from the input file.
                      3:  * We use these special purpose routines which optimize moving
                      4:  * both forward and backward from the current read pointer.
                      5:  */
                      6: 
                      7: #include "less.h"
                      8: 
                      9: public int file = -1;  /* File descriptor of the input file */
                     10: 
                     11: /*
                     12:  * Pool of buffers holding the most recently used blocks of the input file.
                     13:  */
                     14: #define BUFSIZ 1024
                     15: static struct buf {
                     16:        struct buf *next, *prev;
                     17:        long block;
                     18:        char data[BUFSIZ];
                     19: };
                     20: static struct buf *bufs = NULL;
                     21: public int nbufs;
                     22: 
                     23: /*
                     24:  * The buffer pool is kept as a doubly-linked circular list,
                     25:  * in order from most- to least-recently used.
                     26:  * The circular list is anchored by buf_anchor.
                     27:  */
                     28: static struct {
                     29:        struct buf *next, *prev;
                     30: } buf_anchor;
                     31: #define        END_OF_CHAIN    ((struct buf *)&buf_anchor)
                     32: #define        buf_head        buf_anchor.next
                     33: #define        buf_tail        buf_anchor.prev
                     34: 
                     35: /*
                     36:  * If we fail to allocate enough memory for buffers, we try to limp
                     37:  * along with a minimum number of buffers.  
                     38:  */
                     39: #define        DEF_NBUFS       2       /* Minimum number of buffers */
                     40: 
                     41: extern int clean_data;
                     42: extern int ispipe;
                     43: 
                     44: /*
                     45:  * Current position in file.
                     46:  * Stored as a block number and an offset into the block.
                     47:  */
                     48: static long ch_block;
                     49: static int ch_offset;
                     50: 
                     51: /* 
                     52:  * Length of file, needed if input is a pipe.
                     53:  */
                     54: static POSITION ch_fsize;
                     55: 
                     56: /*
                     57:  * Largest block number read if input is standard input (a pipe).
                     58:  */
                     59: static long last_piped_block;
                     60: 
                     61: /*
                     62:  * Get the character pointed to by the read pointer.
                     63:  * ch_get() is a macro which is more efficient to call
                     64:  * than fch_get (the function), in the usual case 
                     65:  * that the block desired is at the head of the chain.
                     66:  */
                     67: #define        ch_get()   ((buf_head->block == ch_block) ? \
                     68:                        buf_head->data[ch_offset] : fch_get())
                     69:        static int
                     70: fch_get()
                     71: {
                     72:        register struct buf *bp;
                     73:        register int n;
                     74:        register int end;
                     75:        POSITION pos;
                     76: 
                     77:        /*
                     78:         * Look for a buffer holding the desired block.
                     79:         */
                     80:        for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
                     81:                if (bp->block == ch_block)
                     82:                        goto found;
                     83:        /*
                     84:         * Block is not in a buffer.  
                     85:         * Take the least recently used buffer 
                     86:         * and read the desired block into it.
                     87:         */
                     88:        bp = buf_tail;
                     89:        bp->block = ch_block;
                     90:        pos = ch_block * BUFSIZ;
                     91:        if (ispipe)
                     92:        {
                     93:                /*
                     94:                 * The block requested should be one more than
                     95:                 * the last block read.
                     96:                 */
                     97:                if (ch_block != ++last_piped_block)
                     98:                {
                     99:                        /* This "should not happen". */
                    100:                        char message[80];
                    101:                        sprintf(message, "Pipe error: last %ld, want %ld\n",
                    102:                                last_piped_block-1, ch_block);
                    103:                        error(message);
                    104:                        quit();
                    105:                }
                    106:        } else
                    107:                lseek(file, pos, 0);
                    108: 
                    109:        /*
                    110:         * Read the block.  This may take several reads if the input
                    111:         * is coming from standard input, due to the nature of pipes.
                    112:         */
                    113:        end = 0;
                    114:        while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
                    115:                if ((end += n) >= BUFSIZ)
                    116:                        break;
                    117: 
                    118:        if (n < 0)
                    119:        {
                    120:                error("read error");
                    121:                quit();
                    122:        }
                    123: 
                    124:        /*
                    125:         * Set an EOF marker in the buffered data itself.
                    126:         * Then ensure the data is "clean": there are no 
                    127:         * extra EOF chars in the data and that the "meta"
                    128:         * bit (the 0200 bit) is reset in each char.
                    129:         */
                    130:        if (end < BUFSIZ)
                    131:        {
                    132:                ch_fsize = pos + end;
                    133:                bp->data[end] = EOF;
                    134:        }
                    135: 
                    136:        if (!clean_data)
                    137:                while (--end >= 0)
                    138:                {
                    139:                        bp->data[end] &= 0177;
                    140:                        if (bp->data[end] == EOF)
                    141:                                bp->data[end] = '@';
                    142:                }
                    143: 
                    144:     found:
                    145:        /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
                    146:        {
                    147:                /*
                    148:                 * Move the buffer to the head of the buffer chain.
                    149:                 * This orders the buffer chain, most- to least-recently used.
                    150:                 */
                    151:                bp->next->prev = bp->prev;
                    152:                bp->prev->next = bp->next;
                    153: 
                    154:                bp->next = buf_head;
                    155:                bp->prev = END_OF_CHAIN;
                    156:                buf_head->prev = bp;
                    157:                buf_head = bp;
                    158:        }
                    159:        return (bp->data[ch_offset]);
                    160: }
                    161: 
                    162: /*
                    163:  * Determine if a specific block is currently in one of the buffers.
                    164:  */
                    165:        static int
                    166: buffered(block)
                    167:        long block;
                    168: {
                    169:        register struct buf *bp;
                    170: 
                    171:        for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
                    172:                if (bp->block == block)
                    173:                        return (1);
                    174:        return (0);
                    175: }
                    176: 
                    177: /*
                    178:  * Seek to a specified position in the file.
                    179:  * Return 0 if successful, non-zero if can't seek there.
                    180:  */
                    181:        public int
                    182: ch_seek(pos)
                    183:        register POSITION pos;
                    184: {
                    185:        long new_block;
                    186: 
                    187:        new_block = pos / BUFSIZ;
                    188:        if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
                    189:        {
                    190:                /*
                    191:                 * Set read pointer.
                    192:                 */
                    193:                ch_block = new_block;
                    194:                ch_offset = pos % BUFSIZ;
                    195:                return (0);
                    196:        }
                    197:        return (1);
                    198: }
                    199: 
                    200: /*
                    201:  * Seek to the end of the file.
                    202:  */
                    203:        public int
                    204: ch_end_seek()
                    205: {
                    206:        if (ispipe)
                    207:        {
                    208:                /*
                    209:                 * Do it the slow way: read till end of data.
                    210:                 */
                    211:                while (ch_forw_get() != EOF)
                    212:                        ;
                    213:        } else
                    214:        {
                    215:                (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2)));
                    216:        }
                    217:        return (0);
                    218: }
                    219: 
                    220: /*
                    221:  * Return the length of the file, if known.
                    222:  */
                    223:        public POSITION
                    224: ch_length()
                    225: {
                    226:        if (ispipe)
                    227:                return (ch_fsize);
                    228:        return ((POSITION)(lseek(file, (off_t)0, 2)));
                    229: }
                    230: 
                    231: /*
                    232:  * Return the current position in the file.
                    233:  */
                    234:        public POSITION
                    235: ch_tell()
                    236: {
                    237:        return (ch_block * BUFSIZ + ch_offset);
                    238: }
                    239: 
                    240: /*
                    241:  * Get the current char and post-increment the read pointer.
                    242:  */
                    243:        public int
                    244: ch_forw_get()
                    245: {
                    246:        register int c;
                    247: 
                    248:        c = ch_get();
                    249:        if (c != EOF && ++ch_offset >= BUFSIZ)
                    250:        {
                    251:                ch_offset = 0;
                    252:                ch_block ++;
                    253:        }
                    254:        return (c);
                    255: }
                    256: 
                    257: /*
                    258:  * Pre-decrement the read pointer and get the new current char.
                    259:  */
                    260:        public int
                    261: ch_back_get()
                    262: {
                    263:        register int c;
                    264: 
                    265:        if (--ch_offset < 0)
                    266:        {
                    267:                if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
                    268:                {
                    269:                        ch_offset = 0;
                    270:                        return (EOF);
                    271:                }
                    272:                ch_offset = BUFSIZ - 1;
                    273:                ch_block--;
                    274:        }
                    275:        c = ch_get();
                    276:        return (c);
                    277: }
                    278: 
                    279: /*
                    280:  * Initialize the buffer pool to all empty.
                    281:  * Caller suggests that we use want_nbufs buffers.
                    282:  */
                    283:        public void
                    284: ch_init(want_nbufs)
                    285:        int want_nbufs;
                    286: {
                    287:        register struct buf *bp;
                    288:        char *calloc();
                    289: 
                    290:        if (nbufs < want_nbufs)
                    291:        {
                    292:                /*
                    293:                 * We don't have enough buffers.  
                    294:                 * Free what we have (if any) and allocate some new ones.
                    295:                 */
                    296:                if (bufs != NULL)
                    297:                        free((char *)bufs);
                    298:                bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
                    299:                nbufs = want_nbufs;
                    300:                if (bufs == NULL)
                    301:                {
                    302:                        /*
                    303:                         * Couldn't get that many.
                    304:                         * Try for a small default number of buffers.
                    305:                         */
                    306:                        char message[80];
                    307:                        sprintf(message,
                    308:                          "Cannot allocate %d buffers.  Using %d buffers.", 
                    309:                          nbufs, DEF_NBUFS);
                    310:                        error(message);
                    311:                        bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
                    312:                        nbufs = DEF_NBUFS;
                    313:                        if (bufs == NULL)
                    314:                        {
                    315:                                /*
                    316:                                 * Couldn't even get the smaller number of bufs.
                    317:                                 * Something is wrong here, don't continue.
                    318:                                 */
                    319:                                sprintf(message, 
                    320:                                "Cannot even allocate %d buffers!  Quitting.\n",
                    321:                                  DEF_NBUFS);
                    322:                                error(message);
                    323:                                quit();
                    324:                                /*NOTREACHED*/
                    325:                        }
                    326:                }
                    327:        }
                    328: 
                    329:        /*
                    330:         * Initialize the buffers to empty.
                    331:         * Set up the circular list.
                    332:         */
                    333:        for (bp = &bufs[0];  bp < &bufs[nbufs];  bp++)
                    334:        {
                    335:                bp->next = bp + 1;
                    336:                bp->prev = bp - 1;
                    337:                bp->block = (long)(-1);
                    338:        }
                    339:        bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
                    340:        buf_head = &bufs[0];
                    341:        buf_tail = &bufs[nbufs-1];
                    342:        last_piped_block = -1;
                    343:        ch_fsize = NULL_POSITION;
                    344:        (void) ch_seek((POSITION)0);
                    345: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.