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 }
712
713 if (less_lseek(ch_file, (less_off_t)0, SEEK_SET) == BAD_LSEEK)
714 {
715 /*
716 * Warning only; even if the seek fails for some reason,
717 * there's a good chance we're at the beginning anyway.
718 * {{ I think this is bogus reasoning. }}
719 */
720 error("seek error to 0", NULL_PARG);
721 }
722 }
723
724 /*
725 * Allocate a new buffer.
726 * The buffer is added to the tail of the buffer chain.
727 */
ch_addbuf(void)728 static int ch_addbuf(void)
729 {
730 struct buf *bp;
731 struct bufnode *bn;
732
733 /*
734 * Allocate and initialize a new buffer and link it
735 * onto the tail of the buffer list.
736 */
737 bp = (struct buf *) calloc(1, sizeof(struct buf));
738 if (bp == NULL)
739 return (1);
740 ch_nbufs++;
741 bp->block = -1;
742 bn = &bp->node;
743
744 BUF_INS_TAIL(bn);
745 BUF_HASH_INS(bn, 0);
746 return (0);
747 }
748
749 /*
750 *
751 */
init_hashtbl(void)752 static void init_hashtbl(void)
753 {
754 int h;
755
756 for (h = 0; h < BUFHASH_SIZE; h++)
757 {
758 thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
759 thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
760 }
761 }
762
763 /*
764 * Delete all buffers for this file.
765 */
ch_delbufs(void)766 static void ch_delbufs(void)
767 {
768 struct bufnode *bn;
769
770 while (ch_bufhead != END_OF_CHAIN)
771 {
772 bn = ch_bufhead;
773 BUF_RM(bn);
774 free(bufnode_buf(bn));
775 }
776 ch_nbufs = 0;
777 init_hashtbl();
778 }
779
780 /*
781 * Is it possible to seek on a file descriptor?
782 */
seekable(int f)783 public int seekable(int f)
784 {
785 #if MSDOS_COMPILER
786 extern int fd0;
787 if (f == fd0 && !isatty(fd0))
788 {
789 /*
790 * In MS-DOS, pipes are seekable. Check for
791 * standard input, and pretend it is not seekable.
792 */
793 return (0);
794 }
795 #endif
796 return (less_lseek(f, (less_off_t)1, SEEK_SET) != BAD_LSEEK);
797 }
798
799 /*
800 * Force EOF to be at the current read position.
801 * This is used after an ignore_eof read, during which the EOF may change.
802 */
ch_set_eof(void)803 public void ch_set_eof(void)
804 {
805 if (ch_fsize != NULL_POSITION && ch_fsize < ch_fpos)
806 ch_fsize = ch_fpos;
807 }
808
809
810 /*
811 * Initialize file state for a new file.
812 */
ch_init(int f,int flags,ssize_t nread)813 public void ch_init(int f, int flags, ssize_t nread)
814 {
815 /*
816 * See if we already have a filestate for this file.
817 */
818 thisfile = (struct filestate *) get_filestate(curr_ifile);
819 if (thisfile == NULL)
820 {
821 /*
822 * Allocate and initialize a new filestate.
823 */
824 thisfile = (struct filestate *)
825 ecalloc(1, sizeof(struct filestate));
826 thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
827 thisfile->nbufs = 0;
828 thisfile->flags = flags;
829 thisfile->fpos = 0;
830 thisfile->block = 0;
831 thisfile->offset = 0;
832 thisfile->file = -1;
833 thisfile->fsize = NULL_POSITION;
834 init_hashtbl();
835 /*
836 * Try to seek; set CH_CANSEEK if it works.
837 */
838 if ((flags & CH_CANSEEK) && !seekable(f))
839 ch_flags &= ~CH_CANSEEK;
840 set_filestate(curr_ifile, (void *) thisfile);
841 }
842 if (thisfile->file == -1)
843 thisfile->file = f;
844
845 /*
846 * Figure out the size of the file, if we can.
847 */
848 ch_fsize = (flags & CH_HELPFILE) ? size_helpdata : filesize(ch_file);
849
850 /*
851 * This is a kludge to workaround a Linux kernel bug: files in some
852 * pseudo filesystems like /proc and tracefs have a size of 0 according
853 * to fstat() but have readable data.
854 */
855 if (ch_fsize == 0 && nread > 0)
856 {
857 ch_flags |= CH_NOTRUSTSIZE;
858 }
859
860 ch_flush();
861 }
862
863 /*
864 * Close a filestate.
865 */
ch_close(void)866 public void ch_close(void)
867 {
868 lbool keepstate = FALSE;
869
870 if (thisfile == NULL)
871 return;
872
873 if ((ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) && !(ch_flags & CH_KEEPOPEN))
874 {
875 /*
876 * We can seek or re-open, so we don't need to keep buffers.
877 */
878 ch_delbufs();
879 } else
880 keepstate = TRUE;
881 if (!(ch_flags & CH_KEEPOPEN))
882 {
883 /*
884 * We don't need to keep the file descriptor open
885 * (because we can re-open it.)
886 * But don't really close it if it was opened via popen(),
887 * because pclose() wants to close it.
888 */
889 if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
890 close(ch_file);
891 ch_file = -1;
892 } else
893 keepstate = TRUE;
894 if (!keepstate)
895 {
896 /*
897 * We don't even need to keep the filestate structure.
898 */
899 free(thisfile);
900 thisfile = NULL;
901 set_filestate(curr_ifile, (void *) NULL);
902 }
903 }
904
905 /*
906 * Return ch_flags for the current file.
907 */
ch_getflags(void)908 public int ch_getflags(void)
909 {
910 if (thisfile == NULL)
911 return (0);
912 return (ch_flags);
913 }
914
915 #if 0
916 static void ch_dump(struct filestate *fs)
917 {
918 struct buf *bp;
919 struct bufnode *bn;
920 unsigned char *s;
921
922 if (fs == NULL)
923 {
924 printf(" --no filestate\n");
925 return;
926 }
927 printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
928 fs->file, fs->flags, fs->fpos,
929 fs->fsize, fs->block, fs->offset);
930 printf(" %d bufs:\n", fs->nbufs);
931 for (bn = fs->next; bn != &fs->buflist; bn = bn->next)
932 {
933 bp = bufnode_buf(bn);
934 printf("%x: blk %x, size %x \"",
935 bp, bp->block, bp->datasize);
936 for (s = bp->data; s < bp->data + 30; s++)
937 if (*s >= ' ' && *s < 0x7F)
938 printf("%c", *s);
939 else
940 printf(".");
941 printf("\"\n");
942 }
943 }
944 #endif
945