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 if (logfile < 0) 410 return; 411 nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; 412 for (block = 0; block < nblocks; block++) 413 { 414 int wrote = FALSE; 415 FOR_BUFS(bn) 416 { 417 bp = bufnode_buf(bn); 418 if (bp->block == block) 419 { 420 write(logfile, (char *) bp->data, bp->datasize); 421 wrote = TRUE; 422 break; 423 } 424 } 425 if (!wrote && !warned) 426 { 427 error("Warning: log file is incomplete", 428 NULL_PARG); 429 warned = TRUE; 430 } 431 } 432 } 433 434 #endif 435 436 /* 437 * Determine if a specific block is currently in one of the buffers. 438 */ 439 static int buffered(BLOCKNUM block) 440 { 441 struct buf *bp; 442 struct bufnode *bn; 443 int h; 444 445 h = BUFHASH(block); 446 FOR_BUFS_IN_CHAIN(h, bn) 447 { 448 bp = bufnode_buf(bn); 449 if (bp->block == block) 450 return (TRUE); 451 } 452 return (FALSE); 453 } 454 455 /* 456 * Seek to a specified position in the file. 457 * Return 0 if successful, non-zero if can't seek there. 458 */ 459 public int ch_seek(POSITION pos) 460 { 461 BLOCKNUM new_block; 462 POSITION len; 463 464 if (thisfile == NULL) 465 return (0); 466 467 len = ch_length(); 468 if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) 469 return (1); 470 471 new_block = pos / LBUFSIZE; 472 if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) 473 { 474 if (ch_fpos > pos) 475 return (1); 476 while (ch_fpos < pos) 477 { 478 if (ch_forw_get() == EOI) 479 return (1); 480 if (ABORT_SIGS()) 481 return (1); 482 } 483 return (0); 484 } 485 /* 486 * Set read pointer. 487 */ 488 ch_block = new_block; 489 ch_offset = pos % LBUFSIZE; 490 return (0); 491 } 492 493 /* 494 * Seek to the end of the file. 495 */ 496 public int ch_end_seek(void) 497 { 498 POSITION len; 499 500 if (thisfile == NULL) 501 return (0); 502 503 if (ch_flags & CH_CANSEEK) 504 ch_fsize = filesize(ch_file); 505 506 len = ch_length(); 507 if (len != NULL_POSITION) 508 return (ch_seek(len)); 509 510 /* 511 * Do it the slow way: read till end of data. 512 */ 513 while (ch_forw_get() != EOI) 514 if (ABORT_SIGS()) 515 return (1); 516 return (0); 517 } 518 519 /* 520 * Seek to the last position in the file that is currently buffered. 521 */ 522 public int ch_end_buffer_seek(void) 523 { 524 struct buf *bp; 525 struct bufnode *bn; 526 POSITION buf_pos; 527 POSITION end_pos; 528 529 if (thisfile == NULL || (ch_flags & CH_CANSEEK)) 530 return (ch_end_seek()); 531 532 end_pos = 0; 533 FOR_BUFS(bn) 534 { 535 bp = bufnode_buf(bn); 536 buf_pos = (bp->block * LBUFSIZE) + bp->datasize; 537 if (buf_pos > end_pos) 538 end_pos = buf_pos; 539 } 540 541 return (ch_seek(end_pos)); 542 } 543 544 /* 545 * Seek to the beginning of the file, or as close to it as we can get. 546 * We may not be able to seek there if input is a pipe and the 547 * beginning of the pipe is no longer buffered. 548 */ 549 public int ch_beg_seek(void) 550 { 551 struct bufnode *bn; 552 struct bufnode *firstbn; 553 554 /* 555 * Try a plain ch_seek first. 556 */ 557 if (ch_seek(ch_zero()) == 0) 558 return (0); 559 560 /* 561 * Can't get to position 0. 562 * Look thru the buffers for the one closest to position 0. 563 */ 564 firstbn = ch_bufhead; 565 if (firstbn == END_OF_CHAIN) 566 return (1); 567 FOR_BUFS(bn) 568 { 569 if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) 570 firstbn = bn; 571 } 572 ch_block = bufnode_buf(firstbn)->block; 573 ch_offset = 0; 574 return (0); 575 } 576 577 /* 578 * Return the length of the file, if known. 579 */ 580 public POSITION ch_length(void) 581 { 582 if (thisfile == NULL) 583 return (NULL_POSITION); 584 if (ignore_eoi) 585 return (NULL_POSITION); 586 if (ch_flags & CH_HELPFILE) 587 return (size_helpdata); 588 if (ch_flags & CH_NODATA) 589 return (0); 590 return (ch_fsize); 591 } 592 593 /* 594 * Return the current position in the file. 595 */ 596 public POSITION ch_tell(void) 597 { 598 if (thisfile == NULL) 599 return (NULL_POSITION); 600 return (ch_block * LBUFSIZE) + ch_offset; 601 } 602 603 /* 604 * Get the current char and post-increment the read pointer. 605 */ 606 public int ch_forw_get(void) 607 { 608 int c; 609 610 if (thisfile == NULL) 611 return (EOI); 612 c = ch_get(); 613 if (c == EOI) 614 return (EOI); 615 if (ch_offset < LBUFSIZE-1) 616 ch_offset++; 617 else 618 { 619 ch_block ++; 620 ch_offset = 0; 621 } 622 return (c); 623 } 624 625 /* 626 * Pre-decrement the read pointer and get the new current char. 627 */ 628 public int ch_back_get(void) 629 { 630 if (thisfile == NULL) 631 return (EOI); 632 if (ch_offset > 0) 633 ch_offset --; 634 else 635 { 636 if (ch_block <= 0) 637 return (EOI); 638 if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) 639 return (EOI); 640 ch_block--; 641 ch_offset = LBUFSIZE-1; 642 } 643 return (ch_get()); 644 } 645 646 /* 647 * Set max amount of buffer space. 648 * bufspace is in units of 1024 bytes. -1 mean no limit. 649 */ 650 public void ch_setbufspace(int bufspace) 651 { 652 if (bufspace < 0) 653 maxbufs = -1; 654 else 655 { 656 int lbufk = LBUFSIZE / 1024; 657 maxbufs = bufspace / lbufk + (bufspace % lbufk != 0); 658 if (maxbufs < 1) 659 maxbufs = 1; 660 } 661 } 662 663 /* 664 * Flush (discard) any saved file state, including buffer contents. 665 */ 666 public void ch_flush(void) 667 { 668 struct bufnode *bn; 669 670 if (thisfile == NULL) 671 return; 672 673 if (!(ch_flags & CH_CANSEEK)) 674 { 675 /* 676 * If input is a pipe, we don't flush buffer contents, 677 * since the contents can't be recovered. 678 */ 679 ch_fsize = NULL_POSITION; 680 return; 681 } 682 683 /* 684 * Initialize all the buffers. 685 */ 686 FOR_BUFS(bn) 687 { 688 bufnode_buf(bn)->block = -1; 689 } 690 691 /* 692 * Figure out the size of the file, if we can. 693 */ 694 ch_fsize = filesize(ch_file); 695 696 /* 697 * Seek to a known position: the beginning of the file. 698 */ 699 ch_fpos = 0; 700 ch_block = 0; /* ch_fpos / LBUFSIZE; */ 701 ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 702 703 /* 704 * This is a kludge to workaround a Linux kernel bug: files in 705 * /proc have a size of 0 according to fstat() but have readable 706 * data. They are sometimes, but not always, seekable. 707 * Force them to be non-seekable here. 708 */ 709 if (ch_fsize == 0) 710 { 711 ch_fsize = NULL_POSITION; 712 ch_flags &= ~CH_CANSEEK; 713 } 714 715 if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) 716 { 717 /* 718 * Warning only; even if the seek fails for some reason, 719 * there's a good chance we're at the beginning anyway. 720 * {{ I think this is bogus reasoning. }} 721 */ 722 error("seek error to 0", NULL_PARG); 723 } 724 } 725 726 /* 727 * Allocate a new buffer. 728 * The buffer is added to the tail of the buffer chain. 729 */ 730 static int ch_addbuf(void) 731 { 732 struct buf *bp; 733 struct bufnode *bn; 734 735 /* 736 * Allocate and initialize a new buffer and link it 737 * onto the tail of the buffer list. 738 */ 739 bp = (struct buf *) calloc(1, sizeof(struct buf)); 740 if (bp == NULL) 741 return (1); 742 ch_nbufs++; 743 bp->block = -1; 744 bn = &bp->node; 745 746 BUF_INS_TAIL(bn); 747 BUF_HASH_INS(bn, 0); 748 return (0); 749 } 750 751 /* 752 * 753 */ 754 static void init_hashtbl(void) 755 { 756 int h; 757 758 for (h = 0; h < BUFHASH_SIZE; h++) 759 { 760 thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); 761 thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); 762 } 763 } 764 765 /* 766 * Delete all buffers for this file. 767 */ 768 static void ch_delbufs(void) 769 { 770 struct bufnode *bn; 771 772 while (ch_bufhead != END_OF_CHAIN) 773 { 774 bn = ch_bufhead; 775 BUF_RM(bn); 776 free(bufnode_buf(bn)); 777 } 778 ch_nbufs = 0; 779 init_hashtbl(); 780 } 781 782 /* 783 * Is it possible to seek on a file descriptor? 784 */ 785 public int seekable(int f) 786 { 787 #if MSDOS_COMPILER 788 extern int fd0; 789 if (f == fd0 && !isatty(fd0)) 790 { 791 /* 792 * In MS-DOS, pipes are seekable. Check for 793 * standard input, and pretend it is not seekable. 794 */ 795 return (0); 796 } 797 #endif 798 return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); 799 } 800 801 /* 802 * Force EOF to be at the current read position. 803 * This is used after an ignore_eof read, during which the EOF may change. 804 */ 805 public void ch_set_eof(void) 806 { 807 if (ch_fsize != NULL_POSITION && ch_fsize < ch_fpos) 808 ch_fsize = ch_fpos; 809 } 810 811 812 /* 813 * Initialize file state for a new file. 814 */ 815 public void ch_init(int f, int flags) 816 { 817 /* 818 * See if we already have a filestate for this file. 819 */ 820 thisfile = (struct filestate *) get_filestate(curr_ifile); 821 if (thisfile == NULL) 822 { 823 /* 824 * Allocate and initialize a new filestate. 825 */ 826 thisfile = (struct filestate *) 827 ecalloc(1, sizeof(struct filestate)); 828 thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; 829 thisfile->nbufs = 0; 830 thisfile->flags = flags; 831 thisfile->fpos = 0; 832 thisfile->block = 0; 833 thisfile->offset = 0; 834 thisfile->file = -1; 835 thisfile->fsize = NULL_POSITION; 836 init_hashtbl(); 837 /* 838 * Try to seek; set CH_CANSEEK if it works. 839 */ 840 if ((flags & CH_CANSEEK) && !seekable(f)) 841 ch_flags &= ~CH_CANSEEK; 842 set_filestate(curr_ifile, (void *) thisfile); 843 } 844 if (thisfile->file == -1) 845 thisfile->file = f; 846 ch_flush(); 847 } 848 849 /* 850 * Close a filestate. 851 */ 852 public void ch_close(void) 853 { 854 int keepstate = FALSE; 855 856 if (thisfile == NULL) 857 return; 858 859 if ((ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) && !(ch_flags & CH_KEEPOPEN)) 860 { 861 /* 862 * We can seek or re-open, so we don't need to keep buffers. 863 */ 864 ch_delbufs(); 865 } else 866 keepstate = TRUE; 867 if (!(ch_flags & CH_KEEPOPEN)) 868 { 869 /* 870 * We don't need to keep the file descriptor open 871 * (because we can re-open it.) 872 * But don't really close it if it was opened via popen(), 873 * because pclose() wants to close it. 874 */ 875 if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 876 close(ch_file); 877 ch_file = -1; 878 } else 879 keepstate = TRUE; 880 if (!keepstate) 881 { 882 /* 883 * We don't even need to keep the filestate structure. 884 */ 885 free(thisfile); 886 thisfile = NULL; 887 set_filestate(curr_ifile, (void *) NULL); 888 } 889 } 890 891 /* 892 * Return ch_flags for the current file. 893 */ 894 public int ch_getflags(void) 895 { 896 if (thisfile == NULL) 897 return (0); 898 return (ch_flags); 899 } 900 901 #if 0 902 static void ch_dump(struct filestate *fs) 903 { 904 struct buf *bp; 905 struct bufnode *bn; 906 unsigned char *s; 907 908 if (fs == NULL) 909 { 910 printf(" --no filestate\n"); 911 return; 912 } 913 printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 914 fs->file, fs->flags, fs->fpos, 915 fs->fsize, fs->block, fs->offset); 916 printf(" %d bufs:\n", fs->nbufs); 917 for (bn = fs->next; bn != &fs->buflist; bn = bn->next) 918 { 919 bp = bufnode_buf(bn); 920 printf("%x: blk %x, size %x \"", 921 bp, bp->block, bp->datasize); 922 for (s = bp->data; s < bp->data + 30; s++) 923 if (*s >= ' ' && *s < 0x7F) 924 printf("%c", *s); 925 else 926 printf("."); 927 printf("\"\n"); 928 } 929 } 930 #endif 931