Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/less/ch.c
101160 views
1
/*
2
* Copyright (C) 1984-2025 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 lbool ignore_eoi = FALSE;
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
*/
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
*/
147
static int ch_get(void)
148
{
149
struct buf *bp;
150
struct bufnode *bn;
151
ssize_t n;
152
int h;
153
154
if (thisfile == NULL)
155
return (EOI);
156
157
/*
158
* Quick check for the common case where
159
* the desired char is in the head buffer.
160
*/
161
if (ch_bufhead != END_OF_CHAIN)
162
{
163
bp = bufnode_buf(ch_bufhead);
164
if (ch_block == bp->block && ch_offset < bp->datasize)
165
return bp->data[ch_offset];
166
}
167
168
/*
169
* Look for a buffer holding the desired block.
170
*/
171
waiting_for_data = FALSE;
172
h = BUFHASH(ch_block);
173
FOR_BUFS_IN_CHAIN(h, bn)
174
{
175
bp = bufnode_buf(bn);
176
if (bp->block == ch_block)
177
{
178
if (ch_offset >= bp->datasize)
179
/*
180
* Need more data in this buffer.
181
*/
182
break;
183
goto found;
184
}
185
}
186
if (ABORT_SIGS())
187
return (EOI);
188
if (bn == END_OF_HCHAIN(h))
189
{
190
/*
191
* Block is not in a buffer.
192
* Take the least recently used buffer
193
* and read the desired block into it.
194
* If the LRU buffer has data in it,
195
* then maybe allocate a new buffer.
196
*/
197
if (ch_buftail == END_OF_CHAIN ||
198
bufnode_buf(ch_buftail)->block != -1)
199
{
200
/*
201
* There is no empty buffer to use.
202
* Allocate a new buffer if:
203
* 1. We can't seek on this file and -b is not in effect; or
204
* 2. We haven't allocated the max buffers for this file yet.
205
*/
206
if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
207
(maxbufs < 0 || ch_nbufs < maxbufs))
208
if (ch_addbuf())
209
/*
210
* Allocation failed: turn off autobuf.
211
*/
212
autobuf = OPT_OFF;
213
}
214
bn = ch_buftail;
215
bp = bufnode_buf(bn);
216
BUF_HASH_RM(bn); /* Remove from old hash chain. */
217
bp->block = ch_block;
218
bp->datasize = 0;
219
BUF_HASH_INS(bn, h); /* Insert into new hash chain. */
220
}
221
222
for (;;)
223
{
224
lbool read_again;
225
POSITION len;
226
POSITION pos = ch_position(ch_block, bp->datasize);
227
lbool read_pipe_at_eof = FALSE;
228
if ((len = ch_length()) != NULL_POSITION && pos >= len)
229
{
230
/*
231
* Apparently at end of file.
232
* Double-check the file size in case it has changed.
233
*/
234
ch_resize();
235
if ((len = ch_length()) != NULL_POSITION && pos >= len)
236
{
237
if (ch_flags & (CH_CANSEEK|CH_HELPFILE))
238
return (EOI);
239
/* ch_length doesn't work for pipes, so just try to
240
* read from the pipe to see if more data has appeared.
241
* This can happen only in limited situations, such as
242
* a fifo that the writer has closed and reopened. */
243
read_pipe_at_eof = TRUE;
244
}
245
}
246
247
if (pos != ch_fpos)
248
{
249
/*
250
* Not at the correct position: must seek.
251
* If input is a pipe, we're in trouble (can't seek on a pipe).
252
* Some data has been lost: just return "?".
253
*/
254
if (!(ch_flags & CH_CANSEEK))
255
return ('?');
256
if (less_lseek(ch_file, (less_off_t)pos, SEEK_SET) == BAD_LSEEK)
257
{
258
error("seek error", NULL_PARG);
259
clear_eol();
260
return (EOI);
261
}
262
ch_fpos = pos;
263
}
264
265
/*
266
* Read the block.
267
* If we read less than a full block, that's ok.
268
* We use partial block and pick up the rest next time.
269
*/
270
if (ch_have_ungotchar)
271
{
272
bp->data[bp->datasize] = ch_ungotchar;
273
n = 1;
274
ch_have_ungotchar = FALSE;
275
} else if (ch_flags & CH_HELPFILE)
276
{
277
bp->data[bp->datasize] = (unsigned char) helpdata[ch_fpos];
278
n = 1;
279
} else
280
{
281
n = iread(ch_file, &bp->data[bp->datasize], LBUFSIZE - bp->datasize);
282
}
283
284
read_again = FALSE;
285
if (n == READ_INTR)
286
return (EOI);
287
if (n == READ_AGAIN)
288
{
289
read_again = TRUE;
290
n = 0;
291
}
292
if (n < 0)
293
{
294
#if MSDOS_COMPILER==WIN32C
295
if (errno != EPIPE)
296
#endif
297
{
298
error("read error", NULL_PARG);
299
clear_eol();
300
}
301
n = 0;
302
}
303
304
#if LOGFILE
305
/*
306
* If we have a log file, write the new data to it.
307
*/
308
if (secure_allow(SF_LOGFILE))
309
{
310
if (logfile >= 0 && n > 0)
311
write(logfile, &bp->data[bp->datasize], (size_t) n);
312
}
313
#endif
314
315
ch_fpos += n;
316
bp->datasize += (size_t) n;
317
if (read_pipe_at_eof)
318
ch_set_eof(); /* update length of pipe */
319
320
if (n == 0)
321
{
322
/* Either end of file or no data available.
323
* read_again indicates the latter. */
324
if (!read_again)
325
ch_fsize = pos;
326
if (ignore_eoi || read_again)
327
{
328
/* Wait a while, then try again. */
329
if (!waiting_for_data)
330
{
331
PARG parg;
332
parg.p_string = wait_message();
333
ixerror("%s", &parg);
334
waiting_for_data = TRUE;
335
}
336
sleep_ms(50); /* Reduce system load */
337
}
338
if (ignore_eoi && follow_mode == FOLLOW_NAME && curr_ifile_changed())
339
{
340
/* screen_trashed=2 causes make_display to reopen the file. */
341
screen_trashed_num(2);
342
return (EOI);
343
}
344
if (sigs)
345
return (EOI);
346
if (read_pipe_at_eof)
347
/* No new data; we are still at EOF on the pipe. */
348
return (EOI);
349
}
350
351
found:
352
if (ch_bufhead != bn)
353
{
354
/*
355
* Move the buffer to the head of the buffer chain.
356
* This orders the buffer chain, most- to least-recently used.
357
*/
358
BUF_RM(bn);
359
BUF_INS_HEAD(bn);
360
361
/*
362
* Move to head of hash chain too.
363
*/
364
BUF_HASH_RM(bn);
365
BUF_HASH_INS(bn, h);
366
}
367
368
if (ch_offset < bp->datasize)
369
break;
370
/*
371
* After all that, we still don't have enough data.
372
* Go back and try again.
373
*/
374
}
375
return (bp->data[ch_offset]);
376
}
377
378
/*
379
* ch_ungetchar is a rather kludgy and limited way to push
380
* a single char onto an input file descriptor.
381
*/
382
public void ch_ungetchar(int c)
383
{
384
if (c < 0)
385
ch_have_ungotchar = FALSE;
386
else
387
{
388
if (ch_have_ungotchar)
389
error("ch_ungetchar overrun", NULL_PARG);
390
ch_ungotchar = (unsigned char) c;
391
ch_have_ungotchar = TRUE;
392
}
393
}
394
395
#if LOGFILE
396
/*
397
* Close the logfile.
398
* If we haven't read all of standard input into it, do that now.
399
*/
400
public void end_logfile(void)
401
{
402
static lbool tried = FALSE;
403
404
if (logfile < 0)
405
return;
406
if (!tried && ch_fsize == NULL_POSITION)
407
{
408
tried = TRUE;
409
ierror("Finishing logfile", NULL_PARG);
410
while (ch_forw_get() != EOI)
411
if (ABORT_SIGS())
412
break;
413
}
414
close(logfile);
415
logfile = -1;
416
free(namelogfile);
417
namelogfile = NULL;
418
putstr("\n");
419
flush();
420
}
421
422
/*
423
* Start a log file AFTER less has already been running.
424
* Invoked from the - command; see toggle_option().
425
* Write all the existing buffered data to the log file.
426
*/
427
public void sync_logfile(void)
428
{
429
struct buf *bp;
430
struct bufnode *bn;
431
lbool warned = FALSE;
432
int h;
433
BLOCKNUM block;
434
BLOCKNUM nblocks;
435
436
if (logfile < 0)
437
return;
438
nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
439
for (block = 0; block < nblocks; block++)
440
{
441
lbool wrote = FALSE;
442
h = BUFHASH(block);
443
FOR_BUFS_IN_CHAIN(h, bn)
444
{
445
bp = bufnode_buf(bn);
446
if (bp->block == block)
447
{
448
write(logfile, bp->data, bp->datasize);
449
wrote = TRUE;
450
break;
451
}
452
}
453
if (!wrote && !warned)
454
{
455
error("Warning: log file is incomplete",
456
NULL_PARG);
457
warned = TRUE;
458
}
459
}
460
}
461
462
#endif
463
464
/*
465
* Determine if a specific block is currently in one of the buffers.
466
*/
467
static lbool buffered(BLOCKNUM block)
468
{
469
struct buf *bp;
470
struct bufnode *bn;
471
int h;
472
473
h = BUFHASH(block);
474
FOR_BUFS_IN_CHAIN(h, bn)
475
{
476
bp = bufnode_buf(bn);
477
if (bp->block == block)
478
return (TRUE);
479
}
480
return (FALSE);
481
}
482
483
/*
484
* Seek to a specified position in the file.
485
* Return 0 if successful, non-zero if can't seek there.
486
*/
487
public int ch_seek(POSITION pos)
488
{
489
BLOCKNUM new_block;
490
POSITION len;
491
492
if (thisfile == NULL)
493
return (0);
494
495
len = ch_length();
496
if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
497
return (1);
498
499
new_block = pos / LBUFSIZE;
500
if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
501
{
502
if (ch_fpos > pos)
503
return (1);
504
while (ch_fpos < pos)
505
{
506
if (ch_forw_get() == EOI)
507
return (1);
508
if (ABORT_SIGS())
509
return (1);
510
}
511
return (0);
512
}
513
/*
514
* Set read pointer.
515
*/
516
ch_block = new_block;
517
ch_offset = (size_t) (pos % LBUFSIZE);
518
return (0);
519
}
520
521
/*
522
* Seek to the end of the file.
523
*/
524
public int ch_end_seek(void)
525
{
526
POSITION len;
527
528
if (thisfile == NULL)
529
return (0);
530
531
if (ch_flags & CH_CANSEEK)
532
ch_fsize = filesize(ch_file);
533
534
len = ch_length();
535
if (len != NULL_POSITION)
536
return (ch_seek(len));
537
538
/*
539
* Do it the slow way: read till end of data.
540
*/
541
while (ch_forw_get() != EOI)
542
if (ABORT_SIGS())
543
return (1);
544
return (0);
545
}
546
547
/*
548
* Seek to the last position in the file that is currently buffered.
549
*/
550
public int ch_end_buffer_seek(void)
551
{
552
struct buf *bp;
553
struct bufnode *bn;
554
POSITION buf_pos;
555
POSITION end_pos;
556
557
if (thisfile == NULL || (ch_flags & CH_CANSEEK))
558
return (ch_end_seek());
559
560
end_pos = 0;
561
FOR_BUFS(bn)
562
{
563
bp = bufnode_buf(bn);
564
buf_pos = ch_position(bp->block, bp->datasize);
565
if (buf_pos > end_pos)
566
end_pos = buf_pos;
567
}
568
569
return (ch_seek(end_pos));
570
}
571
572
/*
573
* Seek to the beginning of the file, or as close to it as we can get.
574
* We may not be able to seek there if input is a pipe and the
575
* beginning of the pipe is no longer buffered.
576
*/
577
public int ch_beg_seek(void)
578
{
579
struct bufnode *bn;
580
struct bufnode *firstbn;
581
582
/*
583
* Try a plain ch_seek first.
584
*/
585
if (ch_seek(ch_zero()) == 0)
586
return (0);
587
588
/*
589
* Can't get to position 0.
590
* Look thru the buffers for the one closest to position 0.
591
*/
592
firstbn = ch_bufhead;
593
if (firstbn == END_OF_CHAIN)
594
return (1);
595
FOR_BUFS(bn)
596
{
597
if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block)
598
firstbn = bn;
599
}
600
ch_block = bufnode_buf(firstbn)->block;
601
ch_offset = 0;
602
return (0);
603
}
604
605
/*
606
* Return the length of the file, if known.
607
*/
608
public POSITION ch_length(void)
609
{
610
if (thisfile == NULL)
611
return (NULL_POSITION);
612
if (ignore_eoi)
613
return (NULL_POSITION);
614
if (ch_flags & CH_HELPFILE)
615
return (size_helpdata);
616
if (ch_flags & CH_NODATA)
617
return (0);
618
return (ch_fsize);
619
}
620
621
/*
622
* Update the file size, in case it has changed.
623
*/
624
public void ch_resize(void)
625
{
626
POSITION fsize;
627
628
if (!(ch_flags & CH_CANSEEK))
629
return;
630
fsize = filesize(ch_file);
631
if (fsize != NULL_POSITION)
632
ch_fsize = fsize;
633
}
634
635
/*
636
* Return the current position in the file.
637
*/
638
public POSITION ch_tell(void)
639
{
640
if (thisfile == NULL)
641
return (NULL_POSITION);
642
return ch_position(ch_block, ch_offset);
643
}
644
645
/*
646
* Get the current char and post-increment the read pointer.
647
*/
648
public int ch_forw_get(void)
649
{
650
int c;
651
652
if (thisfile == NULL)
653
return (EOI);
654
c = ch_get();
655
if (c == EOI)
656
return (EOI);
657
if (ch_offset < LBUFSIZE-1)
658
ch_offset++;
659
else
660
{
661
ch_block ++;
662
ch_offset = 0;
663
}
664
return (c);
665
}
666
667
/*
668
* Pre-decrement the read pointer and get the new current char.
669
*/
670
public int ch_back_get(void)
671
{
672
if (thisfile == NULL)
673
return (EOI);
674
if (ch_offset > 0)
675
ch_offset --;
676
else
677
{
678
if (ch_block <= 0)
679
return (EOI);
680
if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
681
return (EOI);
682
ch_block--;
683
ch_offset = LBUFSIZE-1;
684
}
685
return (ch_get());
686
}
687
688
/*
689
* Set max amount of buffer space.
690
* bufspace is in units of 1024 bytes. -1 mean no limit.
691
*/
692
public void ch_setbufspace(ssize_t bufspace)
693
{
694
if (bufspace < 0)
695
maxbufs = -1;
696
else
697
{
698
size_t lbufk = LBUFSIZE / 1024;
699
maxbufs = (int) (bufspace / lbufk + (bufspace % lbufk != 0));
700
if (maxbufs < 1)
701
maxbufs = 1;
702
}
703
}
704
705
/*
706
* Flush (discard) any saved file state, including buffer contents.
707
*/
708
public void ch_flush(void)
709
{
710
struct bufnode *bn;
711
712
if (thisfile == NULL)
713
return;
714
715
if (!(ch_flags & CH_CANSEEK))
716
{
717
/*
718
* If input is a pipe, we don't flush buffer contents,
719
* since the contents can't be recovered.
720
*/
721
ch_fsize = NULL_POSITION;
722
return;
723
}
724
725
/*
726
* Initialize all the buffers.
727
*/
728
FOR_BUFS(bn)
729
{
730
bufnode_buf(bn)->block = -1;
731
}
732
733
/*
734
* Seek to a known position: the beginning of the file.
735
*/
736
ch_fpos = 0;
737
ch_block = 0; /* ch_fpos / LBUFSIZE; */
738
ch_offset = 0; /* ch_fpos % LBUFSIZE; */
739
740
if (ch_flags & CH_NOTRUSTSIZE)
741
{
742
ch_fsize = NULL_POSITION;
743
ch_flags &= ~CH_CANSEEK;
744
} else
745
{
746
ch_fsize = (ch_flags & CH_HELPFILE) ? size_helpdata : filesize(ch_file);
747
}
748
749
if (less_lseek(ch_file, (less_off_t)0, SEEK_SET) == BAD_LSEEK)
750
{
751
/*
752
* Warning only; even if the seek fails for some reason,
753
* there's a good chance we're at the beginning anyway.
754
* {{ I think this is bogus reasoning. }}
755
*/
756
error("seek error to 0", NULL_PARG);
757
}
758
}
759
760
/*
761
* Allocate a new buffer.
762
* The buffer is added to the tail of the buffer chain.
763
*/
764
static int ch_addbuf(void)
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 init_hashtbl(void)
789
{
790
int h;
791
792
for (h = 0; h < BUFHASH_SIZE; h++)
793
{
794
thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
795
thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
796
}
797
}
798
799
/*
800
* Delete all buffers for this file.
801
*/
802
static void ch_delbufs(void)
803
{
804
struct bufnode *bn;
805
806
while (ch_bufhead != END_OF_CHAIN)
807
{
808
bn = ch_bufhead;
809
BUF_RM(bn);
810
free(bufnode_buf(bn));
811
}
812
ch_nbufs = 0;
813
init_hashtbl();
814
}
815
816
/*
817
* Is it possible to seek on a file descriptor?
818
*/
819
public int seekable(int f)
820
{
821
#if MSDOS_COMPILER
822
extern int fd0;
823
if (f == fd0 && !isatty(fd0))
824
{
825
/*
826
* In MS-DOS, pipes are seekable. Check for
827
* standard input, and pretend it is not seekable.
828
*/
829
return (0);
830
}
831
#endif
832
return (less_lseek(f, (less_off_t)1, SEEK_SET) != BAD_LSEEK);
833
}
834
835
/*
836
* Force EOF to be at the current read position.
837
* This is used after an ignore_eof read, during which the EOF may change.
838
*/
839
public void ch_set_eof(void)
840
{
841
if (ch_fsize != NULL_POSITION && ch_fsize < ch_fpos)
842
ch_fsize = ch_fpos;
843
}
844
845
846
/*
847
* Initialize file state for a new file.
848
*/
849
public void ch_init(int f, int flags, ssize_t nread)
850
{
851
/*
852
* See if we already have a filestate for this file.
853
*/
854
thisfile = (struct filestate *) get_filestate(curr_ifile);
855
if (thisfile == NULL)
856
{
857
/*
858
* Allocate and initialize a new filestate.
859
*/
860
thisfile = (struct filestate *)
861
ecalloc(1, sizeof(struct filestate));
862
thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
863
thisfile->nbufs = 0;
864
thisfile->flags = flags;
865
thisfile->fpos = 0;
866
thisfile->block = 0;
867
thisfile->offset = 0;
868
thisfile->file = -1;
869
thisfile->fsize = NULL_POSITION;
870
init_hashtbl();
871
/*
872
* Try to seek; set CH_CANSEEK if it works.
873
*/
874
if ((flags & CH_CANSEEK) && !seekable(f))
875
ch_flags &= ~CH_CANSEEK;
876
set_filestate(curr_ifile, (void *) thisfile);
877
}
878
if (thisfile->file == -1)
879
thisfile->file = f;
880
881
/*
882
* Figure out the size of the file, if we can.
883
*/
884
ch_fsize = (flags & CH_HELPFILE) ? size_helpdata : filesize(ch_file);
885
886
if (ch_fsize == 0 && nread > 0)
887
{
888
/*
889
* This is a kludge to workaround a Linux kernel bug: files in some
890
* pseudo filesystems like /proc and tracefs have a size of 0 according
891
* to fstat() but have readable data.
892
*/
893
ch_flags |= CH_NOTRUSTSIZE;
894
}
895
896
ch_flush();
897
}
898
899
/*
900
* Close a filestate.
901
*/
902
public void ch_close(void)
903
{
904
lbool keepstate = FALSE;
905
906
if (thisfile == NULL)
907
return;
908
909
if ((ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) && !(ch_flags & CH_KEEPOPEN))
910
{
911
/*
912
* We can seek or re-open, so we don't need to keep buffers.
913
*/
914
ch_delbufs();
915
} else
916
keepstate = TRUE;
917
if (!(ch_flags & CH_KEEPOPEN))
918
{
919
/*
920
* We don't need to keep the file descriptor open
921
* (because we can re-open it.)
922
* But don't really close it if it was opened via popen(),
923
* because pclose() wants to close it.
924
*/
925
if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
926
close(ch_file);
927
ch_file = -1;
928
} else
929
keepstate = TRUE;
930
if (!keepstate)
931
{
932
/*
933
* We don't even need to keep the filestate structure.
934
*/
935
free(thisfile);
936
thisfile = NULL;
937
set_filestate(curr_ifile, (void *) NULL);
938
}
939
}
940
941
/*
942
* Return ch_flags for the current file.
943
*/
944
public int ch_getflags(void)
945
{
946
if (thisfile == NULL)
947
return (0);
948
return (ch_flags);
949
}
950
951
#if 0
952
static void ch_dump(struct filestate *fs)
953
{
954
struct buf *bp;
955
struct bufnode *bn;
956
unsigned char *s;
957
958
if (fs == NULL)
959
{
960
printf(" --no filestate\n");
961
return;
962
}
963
printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
964
fs->file, fs->flags, fs->fpos,
965
fs->fsize, fs->block, fs->offset);
966
printf(" %d bufs:\n", fs->nbufs);
967
for (bn = fs->next; bn != &fs->buflist; bn = bn->next)
968
{
969
bp = bufnode_buf(bn);
970
printf("%x: blk %x, size %x \"",
971
bp, bp->block, bp->datasize);
972
for (s = bp->data; s < bp->data + 30; s++)
973
if (*s >= ' ' && *s < 0x7F)
974
printf("%c", *s);
975
else
976
printf(".");
977
printf("\"\n");
978
}
979
}
980
#endif
981
982