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