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