1 |
|
|
/* |
2 |
|
|
* Copyright (C) 1984-2012 Mark Nudelman |
3 |
|
|
* Modified for use with illumos by Garrett D'Amore. |
4 |
|
|
* Copyright 2014 Garrett D'Amore <garrett@damore.org> |
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 |
|
|
* Low level character input from the input file. |
14 |
|
|
* We use these special purpose routines which optimize moving |
15 |
|
|
* both forward and backward from the current read pointer. |
16 |
|
|
*/ |
17 |
|
|
|
18 |
|
|
#include <sys/stat.h> |
19 |
|
|
|
20 |
|
|
#include "less.h" |
21 |
|
|
|
22 |
|
|
extern dev_t curr_dev; |
23 |
|
|
extern ino_t curr_ino; |
24 |
|
|
extern int less_is_more; |
25 |
|
|
|
26 |
|
|
typedef off_t BLOCKNUM; |
27 |
|
|
|
28 |
|
|
int ignore_eoi; |
29 |
|
|
|
30 |
|
|
/* |
31 |
|
|
* Pool of buffers holding the most recently used blocks of the input file. |
32 |
|
|
* The buffer pool is kept as a doubly-linked circular list, |
33 |
|
|
* in order from most- to least-recently used. |
34 |
|
|
* The circular list is anchored by the file state "thisfile". |
35 |
|
|
*/ |
36 |
|
|
struct bufnode { |
37 |
|
|
struct bufnode *next, *prev; |
38 |
|
|
struct bufnode *hnext, *hprev; |
39 |
|
|
}; |
40 |
|
|
|
41 |
|
|
#define LBUFSIZE 8192 |
42 |
|
|
struct buf { |
43 |
|
|
struct bufnode node; |
44 |
|
|
BLOCKNUM block; |
45 |
|
|
unsigned int datasize; |
46 |
|
|
unsigned char data[LBUFSIZE]; |
47 |
|
|
}; |
48 |
|
|
#define bufnode_buf(bn) ((struct buf *)bn) |
49 |
|
|
|
50 |
|
|
/* |
51 |
|
|
* The file state is maintained in a filestate structure. |
52 |
|
|
* A pointer to the filestate is kept in the ifile structure. |
53 |
|
|
*/ |
54 |
|
|
#define BUFHASH_SIZE 64 |
55 |
|
|
struct filestate { |
56 |
|
|
struct bufnode buflist; |
57 |
|
|
struct bufnode hashtbl[BUFHASH_SIZE]; |
58 |
|
|
int file; |
59 |
|
|
int flags; |
60 |
|
|
off_t fpos; |
61 |
|
|
int nbufs; |
62 |
|
|
BLOCKNUM block; |
63 |
|
|
unsigned int offset; |
64 |
|
|
off_t fsize; |
65 |
|
|
}; |
66 |
|
|
|
67 |
|
|
#define ch_bufhead thisfile->buflist.next |
68 |
|
|
#define ch_buftail thisfile->buflist.prev |
69 |
|
|
#define ch_nbufs thisfile->nbufs |
70 |
|
|
#define ch_block thisfile->block |
71 |
|
|
#define ch_offset thisfile->offset |
72 |
|
|
#define ch_fpos thisfile->fpos |
73 |
|
|
#define ch_fsize thisfile->fsize |
74 |
|
|
#define ch_flags thisfile->flags |
75 |
|
|
#define ch_file thisfile->file |
76 |
|
|
|
77 |
|
|
#define END_OF_CHAIN (&thisfile->buflist) |
78 |
|
|
#define END_OF_HCHAIN(h) (&thisfile->hashtbl[h]) |
79 |
|
|
#define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) |
80 |
|
|
|
81 |
|
|
/* |
82 |
|
|
* Macros to manipulate the list of buffers in thisfile->buflist. |
83 |
|
|
*/ |
84 |
|
|
#define FOR_BUFS(bn) \ |
85 |
|
|
for ((bn) = ch_bufhead; (bn) != END_OF_CHAIN; (bn) = (bn)->next) |
86 |
|
|
|
87 |
|
|
#define BUF_RM(bn) \ |
88 |
|
|
(bn)->next->prev = (bn)->prev; \ |
89 |
|
|
(bn)->prev->next = (bn)->next; |
90 |
|
|
|
91 |
|
|
#define BUF_INS_HEAD(bn) \ |
92 |
|
|
(bn)->next = ch_bufhead; \ |
93 |
|
|
(bn)->prev = END_OF_CHAIN; \ |
94 |
|
|
ch_bufhead->prev = (bn); \ |
95 |
|
|
ch_bufhead = (bn); |
96 |
|
|
|
97 |
|
|
#define BUF_INS_TAIL(bn) \ |
98 |
|
|
(bn)->next = END_OF_CHAIN; \ |
99 |
|
|
(bn)->prev = ch_buftail; \ |
100 |
|
|
ch_buftail->next = (bn); \ |
101 |
|
|
ch_buftail = (bn); |
102 |
|
|
|
103 |
|
|
/* |
104 |
|
|
* Macros to manipulate the list of buffers in thisfile->hashtbl[n]. |
105 |
|
|
*/ |
106 |
|
|
#define FOR_BUFS_IN_CHAIN(h, bn) \ |
107 |
|
|
for ((bn) = thisfile->hashtbl[h].hnext; \ |
108 |
|
|
(bn) != END_OF_HCHAIN(h); (bn) = (bn)->hnext) |
109 |
|
|
|
110 |
|
|
#define BUF_HASH_RM(bn) \ |
111 |
|
|
(bn)->hnext->hprev = (bn)->hprev; \ |
112 |
|
|
(bn)->hprev->hnext = (bn)->hnext; |
113 |
|
|
|
114 |
|
|
#define BUF_HASH_INS(bn, h) \ |
115 |
|
|
(bn)->hnext = thisfile->hashtbl[h].hnext; \ |
116 |
|
|
(bn)->hprev = END_OF_HCHAIN(h); \ |
117 |
|
|
thisfile->hashtbl[h].hnext->hprev = (bn); \ |
118 |
|
|
thisfile->hashtbl[h].hnext = (bn); |
119 |
|
|
|
120 |
|
|
static struct filestate *thisfile; |
121 |
|
|
static int ch_ungotchar = -1; |
122 |
|
|
static int maxbufs = -1; |
123 |
|
|
|
124 |
|
|
extern int autobuf; |
125 |
|
|
extern volatile sig_atomic_t sigs; |
126 |
|
|
extern int secure; |
127 |
|
|
extern int screen_trashed; |
128 |
|
|
extern int follow_mode; |
129 |
|
|
extern IFILE curr_ifile; |
130 |
|
|
extern int logfile; |
131 |
|
|
extern char *namelogfile; |
132 |
|
|
|
133 |
|
|
static int ch_addbuf(void); |
134 |
|
|
|
135 |
|
|
|
136 |
|
|
/* |
137 |
|
|
* Get the character pointed to by the read pointer. |
138 |
|
|
*/ |
139 |
|
|
int |
140 |
|
|
ch_get(void) |
141 |
|
|
{ |
142 |
|
|
struct buf *bp; |
143 |
|
|
struct bufnode *bn; |
144 |
|
|
int n; |
145 |
|
|
int slept; |
146 |
|
|
int h; |
147 |
|
|
off_t pos; |
148 |
|
|
off_t len; |
149 |
|
|
|
150 |
✗✓ |
1065278 |
if (thisfile == NULL) |
151 |
|
|
return (EOI); |
152 |
|
|
|
153 |
|
|
/* |
154 |
|
|
* Quick check for the common case where |
155 |
|
|
* the desired char is in the head buffer. |
156 |
|
|
*/ |
157 |
✓✓ |
532639 |
if (ch_bufhead != END_OF_CHAIN) { |
158 |
|
532631 |
bp = bufnode_buf(ch_bufhead); |
159 |
✓✓✓✓
|
1065209 |
if (ch_block == bp->block && ch_offset < bp->datasize) |
160 |
|
532286 |
return (bp->data[ch_offset]); |
161 |
|
|
} |
162 |
|
|
|
163 |
|
|
slept = FALSE; |
164 |
|
|
|
165 |
|
|
/* |
166 |
|
|
* Look for a buffer holding the desired block. |
167 |
|
|
*/ |
168 |
|
353 |
h = BUFHASH(ch_block); |
169 |
✓✓ |
706 |
FOR_BUFS_IN_CHAIN(h, bn) { |
170 |
|
292 |
bp = bufnode_buf(bn); |
171 |
✓✗ |
292 |
if (bp->block == ch_block) { |
172 |
✓✗ |
292 |
if (ch_offset >= bp->datasize) |
173 |
|
|
/* |
174 |
|
|
* Need more data in this buffer. |
175 |
|
|
*/ |
176 |
|
|
break; |
177 |
|
|
goto found; |
178 |
|
|
} |
179 |
|
|
} |
180 |
✓✓ |
706 |
if (bn == END_OF_HCHAIN(h)) { |
181 |
|
|
/* |
182 |
|
|
* Block is not in a buffer. |
183 |
|
|
* Take the least recently used buffer |
184 |
|
|
* and read the desired block into it. |
185 |
|
|
* If the LRU buffer has data in it, |
186 |
|
|
* then maybe allocate a new buffer. |
187 |
|
|
*/ |
188 |
✓✓✓✗
|
114 |
if (ch_buftail == END_OF_CHAIN || |
189 |
|
53 |
bufnode_buf(ch_buftail)->block != -1) { |
190 |
|
|
/* |
191 |
|
|
* There is no empty buffer to use. |
192 |
|
|
* Allocate a new buffer if: |
193 |
|
|
* 1. We can't seek on this file and -b is not in |
194 |
|
|
* effect; or |
195 |
|
|
* 2. We haven't allocated the max buffers for this |
196 |
|
|
* file yet. |
197 |
|
|
*/ |
198 |
✓✗✓✗ ✓✓ |
244 |
if ((autobuf && !(ch_flags & CH_CANSEEK)) || |
199 |
✗✗ |
122 |
(maxbufs < 0 || ch_nbufs < maxbufs)) |
200 |
✗✓ |
15 |
if (ch_addbuf()) |
201 |
|
|
/* |
202 |
|
|
* Allocation failed: turn off autobuf. |
203 |
|
|
*/ |
204 |
|
|
autobuf = OPT_OFF; |
205 |
|
|
} |
206 |
|
61 |
bn = ch_buftail; |
207 |
|
61 |
bp = bufnode_buf(bn); |
208 |
|
61 |
BUF_HASH_RM(bn); /* Remove from old hash chain. */ |
209 |
|
61 |
bp->block = ch_block; |
210 |
|
61 |
bp->datasize = 0; |
211 |
|
61 |
BUF_HASH_INS(bn, h); /* Insert into new hash chain. */ |
212 |
|
61 |
} |
213 |
|
|
|
214 |
|
|
read_more: |
215 |
|
353 |
pos = (ch_block * LBUFSIZE) + bp->datasize; |
216 |
✓✗✓✓
|
706 |
if ((len = ch_length()) != -1 && pos >= len) |
217 |
|
|
/* |
218 |
|
|
* At end of file. |
219 |
|
|
*/ |
220 |
|
292 |
return (EOI); |
221 |
|
|
|
222 |
✓✓ |
61 |
if (pos != ch_fpos) { |
223 |
|
|
/* |
224 |
|
|
* Not at the correct position: must seek. |
225 |
|
|
* If input is a pipe, we're in trouble (can't seek on a pipe). |
226 |
|
|
* Some data has been lost: just return "?". |
227 |
|
|
*/ |
228 |
✗✓ |
2 |
if (!(ch_flags & CH_CANSEEK)) |
229 |
|
|
return ('?'); |
230 |
✗✓ |
2 |
if (lseek(ch_file, (off_t)pos, SEEK_SET) == (off_t)-1) { |
231 |
|
|
error("seek error", NULL); |
232 |
|
|
clear_eol(); |
233 |
|
|
return (EOI); |
234 |
|
|
} |
235 |
|
2 |
ch_fpos = pos; |
236 |
|
2 |
} |
237 |
|
|
|
238 |
|
|
/* |
239 |
|
|
* Read the block. |
240 |
|
|
* If we read less than a full block, that's ok. |
241 |
|
|
* We use partial block and pick up the rest next time. |
242 |
|
|
*/ |
243 |
✗✓ |
61 |
if (ch_ungotchar != -1) { |
244 |
|
|
bp->data[bp->datasize] = (unsigned char)ch_ungotchar; |
245 |
|
|
n = 1; |
246 |
|
|
ch_ungotchar = -1; |
247 |
|
|
} else { |
248 |
|
122 |
n = iread(ch_file, &bp->data[bp->datasize], |
249 |
|
61 |
(unsigned int)(LBUFSIZE - bp->datasize)); |
250 |
|
|
} |
251 |
|
|
|
252 |
✗✓ |
61 |
if (n == READ_INTR) |
253 |
|
|
return (EOI); |
254 |
✗✓ |
61 |
if (n < 0) { |
255 |
|
|
error("read error", NULL); |
256 |
|
|
clear_eol(); |
257 |
|
|
n = 0; |
258 |
|
|
} |
259 |
|
|
|
260 |
|
|
/* |
261 |
|
|
* If we have a log file, write the new data to it. |
262 |
|
|
*/ |
263 |
✗✓ |
61 |
if (!secure && logfile >= 0 && n > 0) |
264 |
|
|
(void) write(logfile, (char *)&bp->data[bp->datasize], n); |
265 |
|
|
|
266 |
|
61 |
ch_fpos += n; |
267 |
|
61 |
bp->datasize += n; |
268 |
|
|
|
269 |
|
|
/* |
270 |
|
|
* If we have read to end of file, set ch_fsize to indicate |
271 |
|
|
* the position of the end of file. |
272 |
|
|
*/ |
273 |
✗✓ |
61 |
if (n == 0) { |
274 |
|
|
ch_fsize = pos; |
275 |
|
|
if (ignore_eoi) { |
276 |
|
|
/* |
277 |
|
|
* We are ignoring EOF. |
278 |
|
|
* Wait a while, then try again. |
279 |
|
|
*/ |
280 |
|
|
if (!slept) { |
281 |
|
|
PARG parg; |
282 |
|
|
parg.p_string = wait_message(); |
283 |
|
|
ierror("%s", &parg); |
284 |
|
|
} |
285 |
|
|
sleep(1); |
286 |
|
|
slept = TRUE; |
287 |
|
|
|
288 |
|
|
if (follow_mode == FOLLOW_NAME) { |
289 |
|
|
/* |
290 |
|
|
* See whether the file's i-number has changed. |
291 |
|
|
* If so, force the file to be closed and |
292 |
|
|
* reopened. |
293 |
|
|
*/ |
294 |
|
|
struct stat st; |
295 |
|
|
int r = stat(get_filename(curr_ifile), &st); |
296 |
|
|
if (r == 0 && (st.st_ino != curr_ino || |
297 |
|
|
st.st_dev != curr_dev)) { |
298 |
|
|
/* |
299 |
|
|
* screen_trashed=2 causes |
300 |
|
|
* make_display to reopen the file. |
301 |
|
|
*/ |
302 |
|
|
screen_trashed = 2; |
303 |
|
|
return (EOI); |
304 |
|
|
} |
305 |
|
|
} |
306 |
|
|
} |
307 |
|
|
if (sigs) |
308 |
|
|
return (EOI); |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
found: |
312 |
✓✓ |
61 |
if (ch_bufhead != bn) { |
313 |
|
|
/* |
314 |
|
|
* Move the buffer to the head of the buffer chain. |
315 |
|
|
* This orders the buffer chain, most- to least-recently used. |
316 |
|
|
*/ |
317 |
|
53 |
BUF_RM(bn); |
318 |
|
53 |
BUF_INS_HEAD(bn); |
319 |
|
|
|
320 |
|
|
/* |
321 |
|
|
* Move to head of hash chain too. |
322 |
|
|
*/ |
323 |
|
53 |
BUF_HASH_RM(bn); |
324 |
|
53 |
BUF_HASH_INS(bn, h); |
325 |
|
53 |
} |
326 |
|
|
|
327 |
✗✓ |
61 |
if (ch_offset >= bp->datasize) |
328 |
|
|
/* |
329 |
|
|
* After all that, we still don't have enough data. |
330 |
|
|
* Go back and try again. |
331 |
|
|
*/ |
332 |
|
|
goto read_more; |
333 |
|
|
|
334 |
|
61 |
return (bp->data[ch_offset]); |
335 |
|
532639 |
} |
336 |
|
|
|
337 |
|
|
/* |
338 |
|
|
* ch_ungetchar is a rather kludgy and limited way to push |
339 |
|
|
* a single char onto an input file descriptor. |
340 |
|
|
*/ |
341 |
|
|
void |
342 |
|
|
ch_ungetchar(int c) |
343 |
|
|
{ |
344 |
✗✓ |
16 |
if (c != -1 && ch_ungotchar != -1) |
345 |
|
|
error("ch_ungetchar overrun", NULL); |
346 |
|
8 |
ch_ungotchar = c; |
347 |
|
8 |
} |
348 |
|
|
|
349 |
|
|
/* |
350 |
|
|
* Close the logfile. |
351 |
|
|
* If we haven't read all of standard input into it, do that now. |
352 |
|
|
*/ |
353 |
|
|
void |
354 |
|
|
end_logfile(void) |
355 |
|
|
{ |
356 |
|
|
static int tried = FALSE; |
357 |
|
|
|
358 |
✗✓ |
32 |
if (logfile < 0) |
359 |
|
|
return; |
360 |
|
|
if (!tried && ch_fsize == -1) { |
361 |
|
|
tried = TRUE; |
362 |
|
|
ierror("Finishing logfile", NULL); |
363 |
|
|
while (ch_forw_get() != EOI) |
364 |
|
|
if (ABORT_SIGS()) |
365 |
|
|
break; |
366 |
|
|
} |
367 |
|
|
close(logfile); |
368 |
|
|
logfile = -1; |
369 |
|
|
namelogfile = NULL; |
370 |
|
16 |
} |
371 |
|
|
|
372 |
|
|
/* |
373 |
|
|
* Start a log file AFTER less has already been running. |
374 |
|
|
* Invoked from the - command; see toggle_option(). |
375 |
|
|
* Write all the existing buffered data to the log file. |
376 |
|
|
*/ |
377 |
|
|
void |
378 |
|
|
sync_logfile(void) |
379 |
|
|
{ |
380 |
|
|
struct buf *bp; |
381 |
|
|
struct bufnode *bn; |
382 |
|
|
int warned = FALSE; |
383 |
|
|
BLOCKNUM block; |
384 |
|
|
BLOCKNUM nblocks; |
385 |
|
|
|
386 |
|
|
nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; |
387 |
|
|
for (block = 0; block < nblocks; block++) { |
388 |
|
|
int wrote = FALSE; |
389 |
|
|
FOR_BUFS(bn) { |
390 |
|
|
bp = bufnode_buf(bn); |
391 |
|
|
if (bp->block == block) { |
392 |
|
|
(void) write(logfile, (char *)bp->data, |
393 |
|
|
bp->datasize); |
394 |
|
|
wrote = TRUE; |
395 |
|
|
break; |
396 |
|
|
} |
397 |
|
|
} |
398 |
|
|
if (!wrote && !warned) { |
399 |
|
|
error("Warning: log file is incomplete", NULL); |
400 |
|
|
warned = TRUE; |
401 |
|
|
} |
402 |
|
|
} |
403 |
|
|
} |
404 |
|
|
|
405 |
|
|
/* |
406 |
|
|
* Determine if a specific block is currently in one of the buffers. |
407 |
|
|
*/ |
408 |
|
|
static int |
409 |
|
|
buffered(BLOCKNUM block) |
410 |
|
|
{ |
411 |
|
|
struct buf *bp; |
412 |
|
|
struct bufnode *bn; |
413 |
|
|
int h; |
414 |
|
|
|
415 |
|
|
h = BUFHASH(block); |
416 |
|
|
FOR_BUFS_IN_CHAIN(h, bn) { |
417 |
|
|
bp = bufnode_buf(bn); |
418 |
|
|
if (bp->block == block) |
419 |
|
|
return (TRUE); |
420 |
|
|
} |
421 |
|
|
return (FALSE); |
422 |
|
|
} |
423 |
|
|
|
424 |
|
|
/* |
425 |
|
|
* Seek to a specified position in the file. |
426 |
|
|
* Return 0 if successful, non-zero if can't seek there. |
427 |
|
|
*/ |
428 |
|
|
int |
429 |
|
|
ch_seek(off_t pos) |
430 |
|
|
{ |
431 |
|
|
BLOCKNUM new_block; |
432 |
|
|
off_t len; |
433 |
|
|
|
434 |
✗✓ |
27356 |
if (thisfile == NULL) |
435 |
|
|
return (0); |
436 |
|
|
|
437 |
|
13678 |
len = ch_length(); |
438 |
✓✗✓✗ ✗✓ |
41034 |
if (pos < ch_zero() || (len != -1 && pos > len)) |
439 |
|
|
return (1); |
440 |
|
|
|
441 |
|
13678 |
new_block = pos / LBUFSIZE; |
442 |
✗✓✗✗ ✗✗ |
13678 |
if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && |
443 |
|
|
!buffered(new_block)) { |
444 |
|
|
if (ch_fpos > pos) |
445 |
|
|
return (1); |
446 |
|
|
while (ch_fpos < pos) { |
447 |
|
|
if (ch_forw_get() == EOI) |
448 |
|
|
return (1); |
449 |
|
|
if (ABORT_SIGS()) |
450 |
|
|
return (1); |
451 |
|
|
} |
452 |
|
|
return (0); |
453 |
|
|
} |
454 |
|
|
/* |
455 |
|
|
* Set read pointer. |
456 |
|
|
*/ |
457 |
|
13678 |
ch_block = new_block; |
458 |
|
13678 |
ch_offset = pos % LBUFSIZE; |
459 |
|
13678 |
return (0); |
460 |
|
13678 |
} |
461 |
|
|
|
462 |
|
|
/* |
463 |
|
|
* Seek to the end of the file. |
464 |
|
|
*/ |
465 |
|
|
int |
466 |
|
|
ch_end_seek(void) |
467 |
|
|
{ |
468 |
|
|
off_t len; |
469 |
|
|
|
470 |
|
|
if (thisfile == NULL) |
471 |
|
|
return (0); |
472 |
|
|
|
473 |
|
|
if (ch_flags & CH_CANSEEK) |
474 |
|
|
ch_fsize = filesize(ch_file); |
475 |
|
|
|
476 |
|
|
len = ch_length(); |
477 |
|
|
if (len != -1) |
478 |
|
|
return (ch_seek(len)); |
479 |
|
|
|
480 |
|
|
/* |
481 |
|
|
* Do it the slow way: read till end of data. |
482 |
|
|
*/ |
483 |
|
|
while (ch_forw_get() != EOI) |
484 |
|
|
if (ABORT_SIGS()) |
485 |
|
|
return (1); |
486 |
|
|
return (0); |
487 |
|
|
} |
488 |
|
|
|
489 |
|
|
/* |
490 |
|
|
* Seek to the beginning of the file, or as close to it as we can get. |
491 |
|
|
* We may not be able to seek there if input is a pipe and the |
492 |
|
|
* beginning of the pipe is no longer buffered. |
493 |
|
|
*/ |
494 |
|
|
int |
495 |
|
|
ch_beg_seek(void) |
496 |
|
|
{ |
497 |
|
|
struct bufnode *bn; |
498 |
|
|
struct bufnode *firstbn; |
499 |
|
|
|
500 |
|
|
/* |
501 |
|
|
* Try a plain ch_seek first. |
502 |
|
|
*/ |
503 |
|
|
if (ch_seek(ch_zero()) == 0) |
504 |
|
|
return (0); |
505 |
|
|
|
506 |
|
|
/* |
507 |
|
|
* Can't get to position 0. |
508 |
|
|
* Look thru the buffers for the one closest to position 0. |
509 |
|
|
*/ |
510 |
|
|
firstbn = ch_bufhead; |
511 |
|
|
if (firstbn == END_OF_CHAIN) |
512 |
|
|
return (1); |
513 |
|
|
FOR_BUFS(bn) { |
514 |
|
|
if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) |
515 |
|
|
firstbn = bn; |
516 |
|
|
} |
517 |
|
|
ch_block = bufnode_buf(firstbn)->block; |
518 |
|
|
ch_offset = 0; |
519 |
|
|
return (0); |
520 |
|
|
} |
521 |
|
|
|
522 |
|
|
/* |
523 |
|
|
* Return the length of the file, if known. |
524 |
|
|
*/ |
525 |
|
|
off_t |
526 |
|
|
ch_length(void) |
527 |
|
|
{ |
528 |
✗✓ |
28314 |
if (thisfile == NULL) |
529 |
|
|
return (-1); |
530 |
✗✓ |
14157 |
if (ignore_eoi) |
531 |
|
|
return (-1); |
532 |
✗✓ |
14157 |
if (ch_flags & CH_NODATA) |
533 |
|
|
return (0); |
534 |
|
14157 |
return (ch_fsize); |
535 |
|
14157 |
} |
536 |
|
|
|
537 |
|
|
/* |
538 |
|
|
* Return the current position in the file. |
539 |
|
|
*/ |
540 |
|
|
off_t |
541 |
|
|
ch_tell(void) |
542 |
|
|
{ |
543 |
✗✓ |
97650 |
if (thisfile == NULL) |
544 |
|
|
return (-1); |
545 |
|
48825 |
return ((ch_block * LBUFSIZE) + ch_offset); |
546 |
|
48825 |
} |
547 |
|
|
|
548 |
|
|
/* |
549 |
|
|
* Get the current char and post-increment the read pointer. |
550 |
|
|
*/ |
551 |
|
|
int |
552 |
|
|
ch_forw_get(void) |
553 |
|
|
{ |
554 |
|
|
int c; |
555 |
|
|
|
556 |
✗✓ |
1040478 |
if (thisfile == NULL) |
557 |
|
|
return (EOI); |
558 |
|
520239 |
c = ch_get(); |
559 |
✓✓ |
520239 |
if (c == EOI) |
560 |
|
292 |
return (EOI); |
561 |
✓✓ |
519947 |
if (ch_offset < LBUFSIZE-1) { |
562 |
|
519896 |
ch_offset++; |
563 |
|
519896 |
} else { |
564 |
|
51 |
ch_block ++; |
565 |
|
51 |
ch_offset = 0; |
566 |
|
|
} |
567 |
|
519947 |
return (c); |
568 |
|
520239 |
} |
569 |
|
|
|
570 |
|
|
/* |
571 |
|
|
* Pre-decrement the read pointer and get the new current char. |
572 |
|
|
*/ |
573 |
|
|
int |
574 |
|
|
ch_back_get(void) |
575 |
|
|
{ |
576 |
✗✓ |
24832 |
if (thisfile == NULL) |
577 |
|
|
return (EOI); |
578 |
✓✓ |
12416 |
if (ch_offset > 0) { |
579 |
|
12400 |
ch_offset --; |
580 |
|
12400 |
} else { |
581 |
✓✗ |
16 |
if (ch_block <= 0) |
582 |
|
16 |
return (EOI); |
583 |
|
|
if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) |
584 |
|
|
return (EOI); |
585 |
|
|
ch_block--; |
586 |
|
|
ch_offset = LBUFSIZE-1; |
587 |
|
|
} |
588 |
|
12400 |
return (ch_get()); |
589 |
|
12416 |
} |
590 |
|
|
|
591 |
|
|
/* |
592 |
|
|
* Set max amount of buffer space. |
593 |
|
|
* bufspace is in units of 1024 bytes. -1 mean no limit. |
594 |
|
|
*/ |
595 |
|
|
void |
596 |
|
|
ch_setbufspace(int bufspace) |
597 |
|
|
{ |
598 |
✓✗ |
16 |
if (bufspace < 0) { |
599 |
|
|
maxbufs = -1; |
600 |
|
|
} else { |
601 |
|
8 |
maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; |
602 |
|
8 |
if (maxbufs < 1) |
603 |
|
|
maxbufs = 1; |
604 |
|
|
} |
605 |
|
8 |
} |
606 |
|
|
|
607 |
|
|
/* |
608 |
|
|
* Flush (discard) any saved file state, including buffer contents. |
609 |
|
|
*/ |
610 |
|
|
void |
611 |
|
|
ch_flush(void) |
612 |
|
|
{ |
613 |
|
|
struct bufnode *bn; |
614 |
|
|
|
615 |
✗✓ |
16 |
if (thisfile == NULL) |
616 |
|
|
return; |
617 |
|
|
|
618 |
✗✓ |
8 |
if (!(ch_flags & CH_CANSEEK)) { |
619 |
|
|
/* |
620 |
|
|
* If input is a pipe, we don't flush buffer contents, |
621 |
|
|
* since the contents can't be recovered. |
622 |
|
|
*/ |
623 |
|
|
ch_fsize = -1; |
624 |
|
|
return; |
625 |
|
|
} |
626 |
|
|
|
627 |
|
|
/* |
628 |
|
|
* Initialize all the buffers. |
629 |
|
|
*/ |
630 |
✗✓ |
16 |
FOR_BUFS(bn) { |
631 |
|
|
bufnode_buf(bn)->block = -1; |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
/* |
635 |
|
|
* Figure out the size of the file, if we can. |
636 |
|
|
*/ |
637 |
|
8 |
ch_fsize = filesize(ch_file); |
638 |
|
|
|
639 |
|
|
/* |
640 |
|
|
* Seek to a known position: the beginning of the file. |
641 |
|
|
*/ |
642 |
|
8 |
ch_fpos = 0; |
643 |
|
8 |
ch_block = 0; /* ch_fpos / LBUFSIZE; */ |
644 |
|
8 |
ch_offset = 0; /* ch_fpos % LBUFSIZE; */ |
645 |
|
|
|
646 |
|
|
#if 1 |
647 |
|
|
/* |
648 |
|
|
* This is a kludge to workaround a Linux kernel bug: files in |
649 |
|
|
* /proc have a size of 0 according to fstat() but have readable |
650 |
|
|
* data. They are sometimes, but not always, seekable. |
651 |
|
|
* Force them to be non-seekable here. |
652 |
|
|
*/ |
653 |
✗✓ |
8 |
if (ch_fsize == 0) { |
654 |
|
|
ch_fsize = -1; |
655 |
|
|
ch_flags &= ~CH_CANSEEK; |
656 |
|
|
} |
657 |
|
|
#endif |
658 |
|
|
|
659 |
✗✓ |
8 |
if (lseek(ch_file, (off_t)0, SEEK_SET) == (off_t)-1) { |
660 |
|
|
/* |
661 |
|
|
* Warning only; even if the seek fails for some reason, |
662 |
|
|
* there's a good chance we're at the beginning anyway. |
663 |
|
|
* {{ I think this is bogus reasoning. }} |
664 |
|
|
*/ |
665 |
|
|
error("seek error to 0", NULL); |
666 |
|
|
} |
667 |
|
16 |
} |
668 |
|
|
|
669 |
|
|
/* |
670 |
|
|
* Allocate a new buffer. |
671 |
|
|
* The buffer is added to the tail of the buffer chain. |
672 |
|
|
*/ |
673 |
|
|
static int |
674 |
|
|
ch_addbuf(void) |
675 |
|
|
{ |
676 |
|
|
struct buf *bp; |
677 |
|
|
struct bufnode *bn; |
678 |
|
|
|
679 |
|
|
/* |
680 |
|
|
* Allocate and initialize a new buffer and link it |
681 |
|
|
* onto the tail of the buffer list. |
682 |
|
|
*/ |
683 |
|
30 |
bp = calloc(1, sizeof (struct buf)); |
684 |
✗✓ |
15 |
if (bp == NULL) |
685 |
|
|
return (1); |
686 |
|
15 |
ch_nbufs++; |
687 |
|
15 |
bp->block = -1; |
688 |
|
15 |
bn = &bp->node; |
689 |
|
|
|
690 |
|
15 |
BUF_INS_TAIL(bn); |
691 |
|
15 |
BUF_HASH_INS(bn, 0); |
692 |
|
15 |
return (0); |
693 |
|
15 |
} |
694 |
|
|
|
695 |
|
|
/* |
696 |
|
|
* |
697 |
|
|
*/ |
698 |
|
|
static void |
699 |
|
|
init_hashtbl(void) |
700 |
|
|
{ |
701 |
|
|
int h; |
702 |
|
|
|
703 |
✓✓ |
2096 |
for (h = 0; h < BUFHASH_SIZE; h++) { |
704 |
|
1024 |
thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); |
705 |
|
1024 |
thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); |
706 |
|
|
} |
707 |
|
16 |
} |
708 |
|
|
|
709 |
|
|
/* |
710 |
|
|
* Delete all buffers for this file. |
711 |
|
|
*/ |
712 |
|
|
static void |
713 |
|
|
ch_delbufs(void) |
714 |
|
|
{ |
715 |
|
|
struct bufnode *bn; |
716 |
|
|
|
717 |
✓✓ |
54 |
while (ch_bufhead != END_OF_CHAIN) { |
718 |
|
|
bn = ch_bufhead; |
719 |
|
15 |
BUF_RM(bn); |
720 |
|
15 |
free(bufnode_buf(bn)); |
721 |
|
|
} |
722 |
|
8 |
ch_nbufs = 0; |
723 |
|
8 |
init_hashtbl(); |
724 |
|
8 |
} |
725 |
|
|
|
726 |
|
|
/* |
727 |
|
|
* Is it possible to seek on a file descriptor? |
728 |
|
|
*/ |
729 |
|
|
int |
730 |
|
|
seekable(int f) |
731 |
|
|
{ |
732 |
|
32 |
return (lseek(f, (off_t)1, SEEK_SET) != (off_t)-1); |
733 |
|
|
} |
734 |
|
|
|
735 |
|
|
/* |
736 |
|
|
* Force EOF to be at the current read position. |
737 |
|
|
* This is used after an ignore_eof read, during which the EOF may change. |
738 |
|
|
*/ |
739 |
|
|
void |
740 |
|
|
ch_set_eof(void) |
741 |
|
|
{ |
742 |
|
|
ch_fsize = ch_fpos; |
743 |
|
|
} |
744 |
|
|
|
745 |
|
|
|
746 |
|
|
/* |
747 |
|
|
* Initialize file state for a new file. |
748 |
|
|
*/ |
749 |
|
|
void |
750 |
|
|
ch_init(int f, int flags) |
751 |
|
|
{ |
752 |
|
|
/* |
753 |
|
|
* See if we already have a filestate for this file. |
754 |
|
|
*/ |
755 |
|
16 |
thisfile = get_filestate(curr_ifile); |
756 |
✓✗ |
8 |
if (thisfile == NULL) { |
757 |
|
|
/* |
758 |
|
|
* Allocate and initialize a new filestate. |
759 |
|
|
*/ |
760 |
|
8 |
thisfile = calloc(1, sizeof (struct filestate)); |
761 |
|
8 |
thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; |
762 |
|
8 |
thisfile->nbufs = 0; |
763 |
|
8 |
thisfile->flags = 0; |
764 |
|
8 |
thisfile->fpos = 0; |
765 |
|
8 |
thisfile->block = 0; |
766 |
|
8 |
thisfile->offset = 0; |
767 |
|
8 |
thisfile->file = -1; |
768 |
|
8 |
thisfile->fsize = -1; |
769 |
|
8 |
ch_flags = flags; |
770 |
|
8 |
init_hashtbl(); |
771 |
|
|
/* |
772 |
|
|
* Try to seek; set CH_CANSEEK if it works. |
773 |
|
|
*/ |
774 |
✓✗✗✓
|
16 |
if ((flags & CH_CANSEEK) && !seekable(f)) |
775 |
|
|
ch_flags &= ~CH_CANSEEK; |
776 |
|
8 |
set_filestate(curr_ifile, (void *) thisfile); |
777 |
|
8 |
} |
778 |
✓✗ |
8 |
if (thisfile->file == -1) |
779 |
|
8 |
thisfile->file = f; |
780 |
|
8 |
ch_flush(); |
781 |
|
8 |
} |
782 |
|
|
|
783 |
|
|
/* |
784 |
|
|
* Close a filestate. |
785 |
|
|
*/ |
786 |
|
|
void |
787 |
|
|
ch_close(void) |
788 |
|
|
{ |
789 |
|
|
int keepstate = FALSE; |
790 |
|
|
|
791 |
✗✓ |
16 |
if (thisfile == NULL) |
792 |
|
|
return; |
793 |
|
|
|
794 |
✓✗ |
8 |
if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) { |
795 |
|
|
/* |
796 |
|
|
* We can seek or re-open, so we don't need to keep buffers. |
797 |
|
|
*/ |
798 |
|
8 |
ch_delbufs(); |
799 |
|
8 |
} else { |
800 |
|
|
keepstate = TRUE; |
801 |
|
|
} |
802 |
✓✗ |
8 |
if (!(ch_flags & CH_KEEPOPEN)) { |
803 |
|
|
/* |
804 |
|
|
* We don't need to keep the file descriptor open |
805 |
|
|
* (because we can re-open it.) |
806 |
|
|
* But don't really close it if it was opened via popen(), |
807 |
|
|
* because pclose() wants to close it. |
808 |
|
|
*/ |
809 |
✓✗ |
8 |
if (!(ch_flags & CH_POPENED)) |
810 |
|
8 |
close(ch_file); |
811 |
|
8 |
ch_file = -1; |
812 |
|
8 |
} else { |
813 |
|
|
keepstate = TRUE; |
814 |
|
|
} |
815 |
✓✗ |
8 |
if (!keepstate) { |
816 |
|
|
/* |
817 |
|
|
* We don't even need to keep the filestate structure. |
818 |
|
|
*/ |
819 |
|
8 |
free(thisfile); |
820 |
|
8 |
thisfile = NULL; |
821 |
|
8 |
set_filestate(curr_ifile, NULL); |
822 |
|
8 |
} |
823 |
|
16 |
} |
824 |
|
|
|
825 |
|
|
/* |
826 |
|
|
* Return ch_flags for the current file. |
827 |
|
|
*/ |
828 |
|
|
int |
829 |
|
|
ch_getflags(void) |
830 |
|
|
{ |
831 |
✗✓ |
23054 |
if (thisfile == NULL) |
832 |
|
|
return (0); |
833 |
|
11527 |
return (ch_flags); |
834 |
|
11527 |
} |