1 /* $NetBSD: ch.c,v 1.4 2013/09/04 19:44:21 tron Exp $ */ 2 3 /* 4 * Copyright (C) 1984-2012 Mark Nudelman 5 * 6 * You may distribute under the terms of either the GNU General Public 7 * License or the Less License, as specified in the README file. 8 * 9 * For more information, see the README file. 10 */ 11 12 13 /* 14 * Low level character input from the input file. 15 * We use these special purpose routines which optimize moving 16 * both forward and backward from the current read pointer. 17 */ 18 19 #include "less.h" 20 #if MSDOS_COMPILER==WIN32C 21 #include <errno.h> 22 #include <windows.h> 23 #endif 24 25 #if HAVE_STAT_INO 26 #include <sys/stat.h> 27 extern dev_t curr_dev; 28 extern ino_t curr_ino; 29 #endif 30 31 typedef POSITION BLOCKNUM; 32 33 public int ignore_eoi; 34 35 /* 36 * Pool of buffers holding the most recently used blocks of the input file. 37 * The buffer pool is kept as a doubly-linked circular list, 38 * in order from most- to least-recently used. 39 * The circular list is anchored by the file state "thisfile". 40 */ 41 struct bufnode { 42 struct bufnode *next, *prev; 43 struct bufnode *hnext, *hprev; 44 }; 45 46 #define LBUFSIZE 8192 47 struct buf { 48 struct bufnode node; 49 BLOCKNUM block; 50 unsigned int datasize; 51 unsigned char data[LBUFSIZE]; 52 }; 53 #define bufnode_buf(bn) ((struct buf *) bn) 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 bufnode buflist; 62 struct bufnode 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->buflist.next 73 #define ch_buftail thisfile->buflist.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 (&thisfile->buflist) 83 #define END_OF_HCHAIN(h) (&thisfile->hashtbl[h]) 84 #define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) 85 86 /* 87 * Macros to manipulate the list of buffers in thisfile->buflist. 88 */ 89 #define FOR_BUFS(bn) \ 90 for (bn = ch_bufhead; bn != END_OF_CHAIN; bn = bn->next) 91 92 #define BUF_RM(bn) \ 93 (bn)->next->prev = (bn)->prev; \ 94 (bn)->prev->next = (bn)->next; 95 96 #define BUF_INS_HEAD(bn) \ 97 (bn)->next = ch_bufhead; \ 98 (bn)->prev = END_OF_CHAIN; \ 99 ch_bufhead->prev = (bn); \ 100 ch_bufhead = (bn); 101 102 #define BUF_INS_TAIL(bn) \ 103 (bn)->next = END_OF_CHAIN; \ 104 (bn)->prev = ch_buftail; \ 105 ch_buftail->next = (bn); \ 106 ch_buftail = (bn); 107 108 /* 109 * Macros to manipulate the list of buffers in thisfile->hashtbl[n]. 110 */ 111 #define FOR_BUFS_IN_CHAIN(h,bn) \ 112 for (bn = thisfile->hashtbl[h].hnext; \ 113 bn != END_OF_HCHAIN(h); bn = bn->hnext) 114 115 #define BUF_HASH_RM(bn) \ 116 (bn)->hnext->hprev = (bn)->hprev; \ 117 (bn)->hprev->hnext = (bn)->hnext; 118 119 #define BUF_HASH_INS(bn,h) \ 120 (bn)->hnext = thisfile->hashtbl[h].hnext; \ 121 (bn)->hprev = END_OF_HCHAIN(h); \ 122 thisfile->hashtbl[h].hnext->hprev = (bn); \ 123 thisfile->hashtbl[h].hnext = (bn); 124 125 static struct filestate *thisfile; 126 static int ch_ungotchar = -1; 127 static int maxbufs = -1; 128 129 extern int autobuf; 130 extern int sigs; 131 extern int secure; 132 extern int screen_trashed; 133 extern int follow_mode; 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 __P((void)); 143 static int buffered __P((BLOCKNUM)); 144 static void ch_delbufs __P((void)); 145 146 147 /* 148 * Get the character pointed to by the read pointer. 149 */ 150 int 151 ch_get() 152 { 153 register struct buf *bp; 154 register struct bufnode *bn; 155 register int n; 156 register int slept; 157 register 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 #if !MSDOS_COMPILER 319 sleep(1); 320 #else 321 #if MSDOS_COMPILER==WIN32C 322 Sleep(1000); 323 #endif 324 #endif 325 slept = TRUE; 326 327 #if HAVE_STAT_INO 328 if (follow_mode == FOLLOW_NAME) 329 { 330 /* See whether the file's i-number has changed. 331 * If so, force the file to be closed and 332 * reopened. */ 333 struct stat st; 334 int r = stat(get_filename(curr_ifile), &st); 335 if (r == 0 && (st.st_ino != curr_ino || 336 st.st_dev != curr_dev)) 337 { 338 /* screen_trashed=2 causes 339 * make_display to reopen the file. */ 340 screen_trashed = 2; 341 return (EOI); 342 } 343 } 344 #endif 345 } 346 if (sigs) 347 return (EOI); 348 } 349 350 found: 351 if (ch_bufhead != bn) 352 { 353 /* 354 * Move the buffer to the head of the buffer chain. 355 * This orders the buffer chain, most- to least-recently used. 356 */ 357 BUF_RM(bn); 358 BUF_INS_HEAD(bn); 359 360 /* 361 * Move to head of hash chain too. 362 */ 363 BUF_HASH_RM(bn); 364 BUF_HASH_INS(bn, h); 365 } 366 367 if (ch_offset >= bp->datasize) 368 /* 369 * After all that, we still don't have enough data. 370 * Go back and try again. 371 */ 372 goto read_more; 373 374 return (bp->data[ch_offset]); 375 } 376 377 /* 378 * ch_ungetchar is a rather kludgy and limited way to push 379 * a single char onto an input file descriptor. 380 */ 381 public void 382 ch_ungetchar(c) 383 int c; 384 { 385 if (c != -1 && ch_ungotchar != -1) 386 error("ch_ungetchar overrun", NULL_PARG); 387 ch_ungotchar = c; 388 } 389 390 #if LOGFILE 391 /* 392 * Close the logfile. 393 * If we haven't read all of standard input into it, do that now. 394 */ 395 public void 396 end_logfile() 397 { 398 static int tried = FALSE; 399 400 if (logfile < 0) 401 return; 402 if (!tried && ch_fsize == NULL_POSITION) 403 { 404 tried = TRUE; 405 ierror("Finishing logfile", NULL_PARG); 406 while (ch_forw_get() != EOI) 407 if (ABORT_SIGS()) 408 break; 409 } 410 close(logfile); 411 logfile = -1; 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 421 sync_logfile() 422 { 423 register struct buf *bp; 424 register 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 458 buffered(block) 459 BLOCKNUM block; 460 { 461 register struct buf *bp; 462 register struct bufnode *bn; 463 register 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 480 ch_seek(pos) 481 register 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 519 ch_end_seek() 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 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 548 ch_beg_seek() 549 { 550 register struct bufnode *bn; 551 register struct bufnode *firstbn; 552 553 /* 554 * Try a plain ch_seek first. 555 */ 556 if (ch_seek(ch_zero()) == 0) 557 return (0); 558 559 /* 560 * Can't get to position 0. 561 * Look thru the buffers for the one closest to position 0. 562 */ 563 firstbn = ch_bufhead; 564 if (firstbn == END_OF_CHAIN) 565 return (1); 566 FOR_BUFS(bn) 567 { 568 if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) 569 firstbn = bn; 570 } 571 ch_block = bufnode_buf(firstbn)->block; 572 ch_offset = 0; 573 return (0); 574 } 575 576 /* 577 * Return the length of the file, if known. 578 */ 579 public POSITION 580 ch_length() 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 597 ch_tell() 598 { 599 if (thisfile == NULL) 600 return (NULL_POSITION); 601 return (ch_block * LBUFSIZE) + ch_offset; 602 } 603 604 /* 605 * Get the current char and post-increment the read pointer. 606 */ 607 public int 608 ch_forw_get() 609 { 610 register int c; 611 612 if (thisfile == NULL) 613 return (EOI); 614 c = ch_get(); 615 if (c == EOI) 616 return (EOI); 617 if (ch_offset < LBUFSIZE-1) 618 ch_offset++; 619 else 620 { 621 ch_block ++; 622 ch_offset = 0; 623 } 624 return (c); 625 } 626 627 /* 628 * Pre-decrement the read pointer and get the new current char. 629 */ 630 public int 631 ch_back_get() 632 { 633 if (thisfile == NULL) 634 return (EOI); 635 if (ch_offset > 0) 636 ch_offset --; 637 else 638 { 639 if (ch_block <= 0) 640 return (EOI); 641 if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) 642 return (EOI); 643 ch_block--; 644 ch_offset = LBUFSIZE-1; 645 } 646 return (ch_get()); 647 } 648 649 /* 650 * Set max amount of buffer space. 651 * bufspace is in units of 1024 bytes. -1 mean no limit. 652 */ 653 public void 654 ch_setbufspace(bufspace) 655 int bufspace; 656 { 657 if (bufspace < 0) 658 maxbufs = -1; 659 else 660 { 661 maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; 662 if (maxbufs < 1) 663 maxbufs = 1; 664 } 665 } 666 667 /* 668 * Flush (discard) any saved file state, including buffer contents. 669 */ 670 public void 671 ch_flush() 672 { 673 register struct bufnode *bn; 674 675 if (thisfile == NULL) 676 return; 677 678 if (!(ch_flags & CH_CANSEEK)) 679 { 680 /* 681 * If input is a pipe, we don't flush buffer contents, 682 * since the contents can't be recovered. 683 */ 684 ch_fsize = NULL_POSITION; 685 return; 686 } 687 688 /* 689 * Initialize all the buffers. 690 */ 691 FOR_BUFS(bn) 692 { 693 bufnode_buf(bn)->block = -1; 694 } 695 696 /* 697 * Figure out the size of the file, if we can. 698 */ 699 ch_fsize = filesize(ch_file); 700 701 /* 702 * Seek to a known position: the beginning of the file. 703 */ 704 ch_fpos = 0; 705 ch_block = 0; /* ch_fpos / LBUFSIZE; */ 706 ch_offset = 0; /* ch_fpos % LBUFSIZE; */ 707 708 #if 1 709 /* 710 * This is a kludge to workaround a Linux kernel bug: files in 711 * /proc have a size of 0 according to fstat() but have readable 712 * data. They are sometimes, but not always, seekable. 713 * Force them to be non-seekable here. 714 */ 715 if (ch_fsize == 0) 716 { 717 ch_fsize = NULL_POSITION; 718 ch_flags &= ~CH_CANSEEK; 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 738 ch_addbuf() 739 { 740 register struct buf *bp; 741 register struct bufnode *bn; 742 743 /* 744 * Allocate and initialize a new buffer and link it 745 * onto the tail of the buffer list. 746 */ 747 bp = (struct buf *) calloc(1, sizeof(struct buf)); 748 if (bp == NULL) 749 return (1); 750 ch_nbufs++; 751 bp->block = -1; 752 bn = &bp->node; 753 754 BUF_INS_TAIL(bn); 755 BUF_HASH_INS(bn, 0); 756 return (0); 757 } 758 759 /* 760 * 761 */ 762 static void 763 init_hashtbl() 764 { 765 register int h; 766 767 for (h = 0; h < BUFHASH_SIZE; h++) 768 { 769 thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); 770 thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); 771 } 772 } 773 774 /* 775 * Delete all buffers for this file. 776 */ 777 static void 778 ch_delbufs() 779 { 780 register struct bufnode *bn; 781 782 while (ch_bufhead != END_OF_CHAIN) 783 { 784 bn = ch_bufhead; 785 BUF_RM(bn); 786 free(bufnode_buf(bn)); 787 } 788 ch_nbufs = 0; 789 init_hashtbl(); 790 } 791 792 /* 793 * Is it possible to seek on a file descriptor? 794 */ 795 public int 796 seekable(f) 797 int f; 798 { 799 #if MSDOS_COMPILER 800 extern int fd0; 801 if (f == fd0 && !isatty(fd0)) 802 { 803 /* 804 * In MS-DOS, pipes are seekable. Check for 805 * standard input, and pretend it is not seekable. 806 */ 807 return (0); 808 } 809 #endif 810 return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); 811 } 812 813 /* 814 * Force EOF to be at the current read position. 815 * This is used after an ignore_eof read, during which the EOF may change. 816 */ 817 public void 818 ch_set_eof() 819 { 820 ch_fsize = ch_fpos; 821 } 822 823 824 /* 825 * Initialize file state for a new file. 826 */ 827 public void 828 ch_init(f, flags) 829 int f; 830 int flags; 831 { 832 /* 833 * See if we already have a filestate for this file. 834 */ 835 thisfile = (struct filestate *) get_filestate(curr_ifile); 836 if (thisfile == NULL) 837 { 838 /* 839 * Allocate and initialize a new filestate. 840 */ 841 thisfile = (struct filestate *) 842 calloc(1, sizeof(struct filestate)); 843 thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; 844 thisfile->nbufs = 0; 845 thisfile->flags = 0; 846 thisfile->fpos = 0; 847 thisfile->block = 0; 848 thisfile->offset = 0; 849 thisfile->file = -1; 850 thisfile->fsize = NULL_POSITION; 851 ch_flags = flags; 852 init_hashtbl(); 853 /* 854 * Try to seek; set CH_CANSEEK if it works. 855 */ 856 if ((flags & CH_CANSEEK) && !seekable(f)) 857 ch_flags &= ~CH_CANSEEK; 858 set_filestate(curr_ifile, (void *) thisfile); 859 } 860 if (thisfile->file == -1) 861 thisfile->file = f; 862 ch_flush(); 863 } 864 865 /* 866 * Close a filestate. 867 */ 868 public void 869 ch_close() 870 { 871 int keepstate = FALSE; 872 873 if (thisfile == NULL) 874 return; 875 876 if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) 877 { 878 /* 879 * We can seek or re-open, so we don't need to keep buffers. 880 */ 881 ch_delbufs(); 882 } else 883 keepstate = TRUE; 884 if (!(ch_flags & CH_KEEPOPEN)) 885 { 886 /* 887 * We don't need to keep the file descriptor open 888 * (because we can re-open it.) 889 * But don't really close it if it was opened via popen(), 890 * because pclose() wants to close it. 891 */ 892 if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) 893 close(ch_file); 894 ch_file = -1; 895 } else 896 keepstate = TRUE; 897 if (!keepstate) 898 { 899 /* 900 * We don't even need to keep the filestate structure. 901 */ 902 free(thisfile); 903 thisfile = NULL; 904 set_filestate(curr_ifile, (void *) NULL); 905 } 906 } 907 908 /* 909 * Return ch_flags for the current file. 910 */ 911 public int 912 ch_getflags() 913 { 914 if (thisfile == NULL) 915 return (0); 916 return (ch_flags); 917 } 918 919 #if 0 920 public void 921 ch_dump(struct filestate *fs) 922 { 923 struct buf *bp; 924 struct bufnode *bn; 925 unsigned char *s; 926 927 if (fs == NULL) 928 { 929 printf(" --no filestate\n"); 930 return; 931 } 932 printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", 933 fs->file, fs->flags, fs->fpos, 934 fs->fsize, fs->block, fs->offset); 935 printf(" %d bufs:\n", fs->nbufs); 936 for (bn = fs->next; bn != &fs->buflist; bn = bn->next) 937 { 938 bp = bufnode_buf(bn); 939 printf("%x: blk %x, size %x \"", 940 bp, bp->block, bp->datasize); 941 for (s = bp->data; s < bp->data + 30; s++) 942 if (*s >= ' ' && *s < 0x7F) 943 printf("%c", *s); 944 else 945 printf("."); 946 printf("\"\n"); 947 } 948 } 949 #endif 950