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