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