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: 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: extern int sigs; 44: 45: #if LOGFILE 46: extern int logfile; 47: #endif 48: 49: /* 50: * Current position in file. 51: * Stored as a block number and an offset into the block. 52: */ 53: static long ch_block; 54: static int ch_offset; 55: 56: /* 57: * Length of file, needed if input is a pipe. 58: */ 59: static POSITION ch_fsize; 60: 61: /* 62: * Largest block number read if input is standard input (a pipe). 63: */ 64: static long last_piped_block; 65: 66: /* 67: * Get the character pointed to by the read pointer. 68: * ch_get() is a macro which is more efficient to call 69: * than fch_get (the function), in the usual case 70: * that the block desired is at the head of the chain. 71: */ 72: #define ch_get() ((buf_head->block == ch_block) ? \ 73: buf_head->data[ch_offset] : fch_get()) 74: static int 75: fch_get() 76: { 77: register struct buf *bp; 78: register int n; 79: register int end; 80: POSITION pos; 81: 82: /* 83: * Look for a buffer holding the desired block. 84: */ 85: for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) 86: if (bp->block == ch_block) 87: goto found; 88: /* 89: * Block is not in a buffer. 90: * Take the least recently used buffer 91: * and read the desired block into it. 92: */ 93: bp = buf_tail; 94: bp->block = ch_block; 95: pos = ch_block * BUFSIZ; 96: if (ispipe) 97: { 98: /* 99: * The block requested should be one more than 100: * the last block read. 101: */ 102: if (ch_block != ++last_piped_block) 103: { 104: /* This "should not happen". */ 105: char message[80]; 106: sprintf(message, "Pipe error: last %ld, want %ld\n", 107: (long)last_piped_block-1, (long)ch_block); 108: error(message); 109: quit(); 110: } 111: } else 112: lseek(file, pos, 0); 113: 114: /* 115: * Read the block. This may take several reads if the input 116: * is coming from standard input, due to the nature of pipes. 117: */ 118: end = 0; 119: while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0) 120: if ((end += n) >= BUFSIZ) 121: break; 122: 123: if (n < 0) 124: { 125: error("read error"); 126: quit(); 127: } 128: 129: #if LOGFILE 130: /* 131: * If we have a log file, write this block to it. 132: */ 133: if (logfile >= 0 && end > 0) 134: write(logfile, bp->data, end); 135: #endif 136: 137: /* 138: * Set an EOF marker in the buffered data itself. 139: * Then ensure the data is "clean": there are no 140: * extra EOF chars in the data and that the "meta" 141: * bit (the 0200 bit) is reset in each char. 142: */ 143: if (end < BUFSIZ) 144: { 145: ch_fsize = pos + end; 146: bp->data[end] = EOF; 147: } 148: 149: if (!clean_data) 150: while (--end >= 0) 151: { 152: bp->data[end] &= 0177; 153: if (bp->data[end] == EOF) 154: bp->data[end] = '@'; 155: } 156: 157: found: 158: /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */ 159: { 160: /* 161: * Move the buffer to the head of the buffer chain. 162: * This orders the buffer chain, most- to least-recently used. 163: */ 164: bp->next->prev = bp->prev; 165: bp->prev->next = bp->next; 166: 167: bp->next = buf_head; 168: bp->prev = END_OF_CHAIN; 169: buf_head->prev = bp; 170: buf_head = bp; 171: } 172: return (bp->data[ch_offset]); 173: } 174: 175: #if LOGFILE 176: /* 177: * Close the logfile. 178: * If we haven't read all of standard input into it, do that now. 179: */ 180: public void 181: end_logfile() 182: { 183: static int tried; 184: 185: if (logfile < 0) 186: return; 187: if (!tried && ch_fsize == NULL_POSITION) 188: { 189: tried = 1; 190: lower_left(); 191: clear_eol(); 192: so_enter(); 193: puts("finishing logfile... (interrupt to abort)"); 194: so_exit(); 195: flush(); 196: while (sigs == 0 && ch_forw_get() != EOF) 197: ; 198: } 199: close(logfile); 200: logfile = -1; 201: } 202: #endif 203: 204: /* 205: * Determine if a specific block is currently in one of the buffers. 206: */ 207: static int 208: buffered(block) 209: long block; 210: { 211: register struct buf *bp; 212: 213: for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) 214: if (bp->block == block) 215: return (1); 216: return (0); 217: } 218: 219: /* 220: * Seek to a specified position in the file. 221: * Return 0 if successful, non-zero if can't seek there. 222: */ 223: public int 224: ch_seek(pos) 225: register POSITION pos; 226: { 227: long new_block; 228: 229: new_block = pos / BUFSIZ; 230: if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block)) 231: { 232: /* 233: * Set read pointer. 234: */ 235: ch_block = new_block; 236: ch_offset = pos % BUFSIZ; 237: return (0); 238: } 239: return (1); 240: } 241: 242: /* 243: * Seek to the end of the file. 244: */ 245: public int 246: ch_end_seek() 247: { 248: if (ispipe) 249: { 250: /* 251: * Do it the slow way: read till end of data. 252: */ 253: while (ch_forw_get() != EOF) 254: ; 255: } else 256: { 257: (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2))); 258: } 259: return (0); 260: } 261: 262: /* 263: * Seek to the beginning of the file, or as close to it as we can get. 264: * We may not be able to seek there if input is a pipe and the 265: * beginning of the pipe is no longer buffered. 266: */ 267: public int 268: ch_beg_seek() 269: { 270: register struct buf *bp, *firstbp; 271: 272: /* 273: * Try a plain ch_seek first. 274: */ 275: if (ch_seek((POSITION)0) == 0) 276: return (0); 277: 278: /* 279: * Can't get to position 0. 280: * Look thru the buffers for the one closest to position 0. 281: */ 282: firstbp = bp = buf_head; 283: if (bp == END_OF_CHAIN) 284: return (1); 285: while ((bp = bp->next) != END_OF_CHAIN) 286: if (bp->block < firstbp->block) 287: firstbp = bp; 288: ch_block = firstbp->block; 289: ch_offset = 0; 290: return (0); 291: } 292: 293: /* 294: * Return the length of the file, if known. 295: */ 296: public POSITION 297: ch_length() 298: { 299: if (ispipe) 300: return (ch_fsize); 301: return ((POSITION)(lseek(file, (off_t)0, 2))); 302: } 303: 304: /* 305: * Return the current position in the file. 306: */ 307: public POSITION 308: ch_tell() 309: { 310: return (ch_block * BUFSIZ + ch_offset); 311: } 312: 313: /* 314: * Get the current char and post-increment the read pointer. 315: */ 316: public int 317: ch_forw_get() 318: { 319: register int c; 320: 321: c = ch_get(); 322: if (c != EOF && ++ch_offset >= BUFSIZ) 323: { 324: ch_offset = 0; 325: ch_block ++; 326: } 327: return (c); 328: } 329: 330: /* 331: * Pre-decrement the read pointer and get the new current char. 332: */ 333: public int 334: ch_back_get() 335: { 336: register int c; 337: 338: if (--ch_offset < 0) 339: { 340: if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) 341: { 342: ch_offset = 0; 343: return (EOF); 344: } 345: ch_offset = BUFSIZ - 1; 346: ch_block--; 347: } 348: c = ch_get(); 349: return (c); 350: } 351: 352: /* 353: * Initialize the buffer pool to all empty. 354: * Caller suggests that we use want_nbufs buffers. 355: */ 356: public void 357: ch_init(want_nbufs) 358: int want_nbufs; 359: { 360: register struct buf *bp; 361: char *calloc(); 362: 363: if (nbufs < want_nbufs) 364: { 365: /* 366: * We don't have enough buffers. 367: * Free what we have (if any) and allocate some new ones. 368: */ 369: if (bufs != NULL) 370: free((char *)bufs); 371: bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf)); 372: nbufs = want_nbufs; 373: if (bufs == NULL) 374: { 375: /* 376: * Couldn't get that many. 377: * Try for a small default number of buffers. 378: */ 379: char message[80]; 380: sprintf(message, 381: "Cannot allocate %d buffers. Using %d buffers.", 382: nbufs, DEF_NBUFS); 383: error(message); 384: bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf)); 385: nbufs = DEF_NBUFS; 386: if (bufs == NULL) 387: { 388: /* 389: * Couldn't even get the smaller number of bufs. 390: * Something is wrong here, don't continue. 391: */ 392: sprintf(message, 393: "Cannot even allocate %d buffers! Quitting.", 394: DEF_NBUFS); 395: error(message); 396: quit(); 397: /*NOTREACHED*/ 398: } 399: } 400: } 401: 402: /* 403: * Initialize the buffers to empty. 404: * Set up the circular list. 405: */ 406: for (bp = &bufs[0]; bp < &bufs[nbufs]; bp++) 407: { 408: bp->next = bp + 1; 409: bp->prev = bp - 1; 410: bp->block = (long)(-1); 411: } 412: bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN; 413: buf_head = &bufs[0]; 414: buf_tail = &bufs[nbufs-1]; 415: last_piped_block = -1; 416: ch_fsize = NULL_POSITION; 417: (void) ch_seek((POSITION)0); 418: }