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