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