File: | src/usr.bin/less/less/../ch.c |
Warning: | line 214, column 32 Access to field 'datasize' results in a dereference of an undefined pointer value (loaded from variable 'bp') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 LBUFSIZE8192 8192 | |||
42 | struct buf { | |||
43 | struct bufnode node; | |||
44 | BLOCKNUM block; | |||
45 | unsigned int datasize; | |||
46 | unsigned char data[LBUFSIZE8192]; | |||
47 | }; | |||
48 | #define bufnode_buf(bn)((struct 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_SIZE64 64 | |||
55 | struct filestate { | |||
56 | struct bufnode buflist; | |||
57 | struct bufnode hashtbl[BUFHASH_SIZE64]; | |||
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_bufheadthisfile->buflist.next thisfile->buflist.next | |||
68 | #define ch_buftailthisfile->buflist.prev thisfile->buflist.prev | |||
69 | #define ch_nbufsthisfile->nbufs thisfile->nbufs | |||
70 | #define ch_blockthisfile->block thisfile->block | |||
71 | #define ch_offsetthisfile->offset thisfile->offset | |||
72 | #define ch_fposthisfile->fpos thisfile->fpos | |||
73 | #define ch_fsizethisfile->fsize thisfile->fsize | |||
74 | #define ch_flagsthisfile->flags thisfile->flags | |||
75 | #define ch_filethisfile->file thisfile->file | |||
76 | ||||
77 | #define END_OF_CHAIN(&thisfile->buflist) (&thisfile->buflist) | |||
78 | #define END_OF_HCHAIN(h)(&thisfile->hashtbl[h]) (&thisfile->hashtbl[h]) | |||
79 | #define BUFHASH(blk)((blk) & (64 -1)) ((blk) & (BUFHASH_SIZE64-1)) | |||
80 | ||||
81 | /* | |||
82 | * Macros to manipulate the list of buffers in thisfile->buflist. | |||
83 | */ | |||
84 | #define FOR_BUFS(bn)for ((bn) = thisfile->buflist.next; (bn) != (&thisfile ->buflist); (bn) = (bn)->next) \ | |||
85 | for ((bn) = ch_bufheadthisfile->buflist.next; (bn) != END_OF_CHAIN(&thisfile->buflist); (bn) = (bn)->next) | |||
86 | ||||
87 | #define BUF_RM(bn)(bn)->next->prev = (bn)->prev; (bn)->prev->next = (bn)->next; \ | |||
88 | (bn)->next->prev = (bn)->prev; \ | |||
89 | (bn)->prev->next = (bn)->next; | |||
90 | ||||
91 | #define BUF_INS_HEAD(bn)(bn)->next = thisfile->buflist.next; (bn)->prev = (& thisfile->buflist); thisfile->buflist.next->prev = ( bn); thisfile->buflist.next = (bn); \ | |||
92 | (bn)->next = ch_bufheadthisfile->buflist.next; \ | |||
93 | (bn)->prev = END_OF_CHAIN(&thisfile->buflist); \ | |||
94 | ch_bufheadthisfile->buflist.next->prev = (bn); \ | |||
95 | ch_bufheadthisfile->buflist.next = (bn); | |||
96 | ||||
97 | #define BUF_INS_TAIL(bn)(bn)->next = (&thisfile->buflist); (bn)->prev = thisfile ->buflist.prev; thisfile->buflist.prev->next = (bn); thisfile->buflist.prev = (bn); \ | |||
98 | (bn)->next = END_OF_CHAIN(&thisfile->buflist); \ | |||
99 | (bn)->prev = ch_buftailthisfile->buflist.prev; \ | |||
100 | ch_buftailthisfile->buflist.prev->next = (bn); \ | |||
101 | ch_buftailthisfile->buflist.prev = (bn); | |||
102 | ||||
103 | /* | |||
104 | * Macros to manipulate the list of buffers in thisfile->hashtbl[n]. | |||
105 | */ | |||
106 | #define FOR_BUFS_IN_CHAIN(h, bn)for ((bn) = thisfile->hashtbl[h].hnext; (bn) != (&thisfile ->hashtbl[h]); (bn) = (bn)->hnext) \ | |||
107 | for ((bn) = thisfile->hashtbl[h].hnext; \ | |||
108 | (bn) != END_OF_HCHAIN(h)(&thisfile->hashtbl[h]); (bn) = (bn)->hnext) | |||
109 | ||||
110 | #define BUF_HASH_RM(bn)(bn)->hnext->hprev = (bn)->hprev; (bn)->hprev-> hnext = (bn)->hnext; \ | |||
111 | (bn)->hnext->hprev = (bn)->hprev; \ | |||
112 | (bn)->hprev->hnext = (bn)->hnext; | |||
113 | ||||
114 | #define BUF_HASH_INS(bn, h)(bn)->hnext = thisfile->hashtbl[h].hnext; (bn)->hprev = (&thisfile->hashtbl[h]); thisfile->hashtbl[h].hnext ->hprev = (bn); thisfile->hashtbl[h].hnext = (bn); \ | |||
115 | (bn)->hnext = thisfile->hashtbl[h].hnext; \ | |||
116 | (bn)->hprev = END_OF_HCHAIN(h)(&thisfile->hashtbl[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 int secure; | |||
126 | extern int screen_trashed; | |||
127 | extern int follow_mode; | |||
128 | extern IFILEvoid * curr_ifile; | |||
129 | extern int logfile; | |||
130 | extern char *namelogfile; | |||
131 | ||||
132 | static int ch_addbuf(void); | |||
133 | ||||
134 | ||||
135 | /* | |||
136 | * Get the character pointed to by the read pointer. | |||
137 | */ | |||
138 | int | |||
139 | ch_get(void) | |||
140 | { | |||
141 | struct buf *bp; | |||
142 | struct bufnode *bn; | |||
143 | int n; | |||
144 | int slept; | |||
145 | int h; | |||
146 | off_t pos; | |||
147 | off_t len; | |||
148 | ||||
149 | if (thisfile
| |||
150 | return (EOI(-1)); | |||
151 | ||||
152 | /* | |||
153 | * Quick check for the common case where | |||
154 | * the desired char is in the head buffer. | |||
155 | */ | |||
156 | if (ch_bufheadthisfile->buflist.next != END_OF_CHAIN(&thisfile->buflist)) { | |||
157 | bp = bufnode_buf(ch_bufhead)((struct buf *)thisfile->buflist.next); | |||
158 | if (ch_blockthisfile->block == bp->block && ch_offsetthisfile->offset < bp->datasize) | |||
159 | return (bp->data[ch_offsetthisfile->offset]); | |||
160 | } | |||
161 | ||||
162 | slept = FALSE0; | |||
163 | ||||
164 | /* | |||
165 | * Look for a buffer holding the desired block. | |||
166 | */ | |||
167 | h = BUFHASH(ch_block)((thisfile->block) & (64 -1)); | |||
168 | FOR_BUFS_IN_CHAIN(h, bn)for ((bn) = thisfile->hashtbl[h].hnext; (bn) != (&thisfile ->hashtbl[h]); (bn) = (bn)->hnext) { | |||
169 | bp = bufnode_buf(bn)((struct buf *)bn); | |||
170 | if (bp->block == ch_blockthisfile->block) { | |||
171 | if (ch_offsetthisfile->offset >= bp->datasize) | |||
172 | /* | |||
173 | * Need more data in this buffer. | |||
174 | */ | |||
175 | break; | |||
176 | goto found; | |||
177 | } | |||
178 | } | |||
179 | if (bn == END_OF_HCHAIN(h)(&thisfile->hashtbl[h])) { | |||
180 | /* | |||
181 | * Block is not in a buffer. | |||
182 | * Take the least recently used buffer | |||
183 | * and read the desired block into it. | |||
184 | * If the LRU buffer has data in it, | |||
185 | * then maybe allocate a new buffer. | |||
186 | */ | |||
187 | if (ch_buftailthisfile->buflist.prev == END_OF_CHAIN(&thisfile->buflist) || | |||
188 | bufnode_buf(ch_buftail)((struct buf *)thisfile->buflist.prev)->block != -1) { | |||
189 | /* | |||
190 | * There is no empty buffer to use. | |||
191 | * Allocate a new buffer if: | |||
192 | * 1. We can't seek on this file and -b is not in | |||
193 | * effect; or | |||
194 | * 2. We haven't allocated the max buffers for this | |||
195 | * file yet. | |||
196 | */ | |||
197 | if ((autobuf && !(ch_flagsthisfile->flags & CH_CANSEEK001)) || | |||
198 | (maxbufs < 0 || ch_nbufsthisfile->nbufs < maxbufs)) | |||
199 | if (ch_addbuf()) | |||
200 | /* | |||
201 | * Allocation failed: turn off autobuf. | |||
202 | */ | |||
203 | autobuf = OPT_OFF0; | |||
204 | } | |||
205 | bn = ch_buftailthisfile->buflist.prev; | |||
206 | bp = bufnode_buf(bn)((struct buf *)bn); | |||
207 | BUF_HASH_RM(bn)(bn)->hnext->hprev = (bn)->hprev; (bn)->hprev-> hnext = (bn)->hnext;; /* Remove from old hash chain. */ | |||
208 | bp->block = ch_blockthisfile->block; | |||
209 | bp->datasize = 0; | |||
210 | BUF_HASH_INS(bn, h)(bn)->hnext = thisfile->hashtbl[h].hnext; (bn)->hprev = (&thisfile->hashtbl[h]); thisfile->hashtbl[h].hnext ->hprev = (bn); thisfile->hashtbl[h].hnext = (bn);; /* Insert into new hash chain. */ | |||
211 | } | |||
212 | ||||
213 | read_more: | |||
214 | pos = (ch_blockthisfile->block * LBUFSIZE8192) + bp->datasize; | |||
| ||||
215 | if ((len = ch_length()) != -1 && pos >= len) | |||
216 | /* | |||
217 | * At end of file. | |||
218 | */ | |||
219 | return (EOI(-1)); | |||
220 | ||||
221 | if (pos != ch_fposthisfile->fpos) { | |||
222 | /* | |||
223 | * Not at the correct position: must seek. | |||
224 | * If input is a pipe, we're in trouble (can't seek on a pipe). | |||
225 | * Some data has been lost: just return "?". | |||
226 | */ | |||
227 | if (!(ch_flagsthisfile->flags & CH_CANSEEK001)) | |||
228 | return ('?'); | |||
229 | if (lseek(ch_filethisfile->file, (off_t)pos, SEEK_SET0) == (off_t)-1) { | |||
230 | error("seek error", NULL((void *)0)); | |||
231 | clear_eol(); | |||
232 | return (EOI(-1)); | |||
233 | } | |||
234 | ch_fposthisfile->fpos = pos; | |||
235 | } | |||
236 | ||||
237 | /* | |||
238 | * Read the block. | |||
239 | * If we read less than a full block, that's ok. | |||
240 | * We use partial block and pick up the rest next time. | |||
241 | */ | |||
242 | if (ch_ungotchar != -1) { | |||
243 | bp->data[bp->datasize] = (unsigned char)ch_ungotchar; | |||
244 | n = 1; | |||
245 | ch_ungotchar = -1; | |||
246 | } else { | |||
247 | n = iread(ch_filethisfile->file, &bp->data[bp->datasize], | |||
248 | (unsigned int)(LBUFSIZE8192 - bp->datasize)); | |||
249 | } | |||
250 | ||||
251 | if (n == READ_INTR(-2)) | |||
252 | return (EOI(-1)); | |||
253 | if (n < 0) { | |||
254 | error("read error", NULL((void *)0)); | |||
255 | clear_eol(); | |||
256 | n = 0; | |||
257 | } | |||
258 | ||||
259 | /* | |||
260 | * If we have a log file, write the new data to it. | |||
261 | */ | |||
262 | if (!secure && logfile >= 0 && n > 0) | |||
263 | (void) write(logfile, (char *)&bp->data[bp->datasize], n); | |||
264 | ||||
265 | ch_fposthisfile->fpos += n; | |||
266 | bp->datasize += n; | |||
267 | ||||
268 | /* | |||
269 | * If we have read to end of file, set ch_fsize to indicate | |||
270 | * the position of the end of file. | |||
271 | */ | |||
272 | if (n == 0) { | |||
273 | ch_fsizethisfile->fsize = pos; | |||
274 | if (ignore_eoi) { | |||
275 | /* | |||
276 | * We are ignoring EOF. | |||
277 | * Wait a while, then try again. | |||
278 | */ | |||
279 | if (!slept) { | |||
280 | PARG parg; | |||
281 | parg.p_string = wait_message(); | |||
282 | ierror("%s", &parg); | |||
283 | } | |||
284 | sleep(1); | |||
285 | slept = TRUE1; | |||
286 | ||||
287 | if (follow_mode == FOLLOW_NAME1) { | |||
288 | /* | |||
289 | * See whether the file's i-number has changed. | |||
290 | * If so, force the file to be closed and | |||
291 | * reopened. | |||
292 | */ | |||
293 | struct stat st; | |||
294 | int r = stat(get_filename(curr_ifile), &st); | |||
295 | if (r == 0 && (st.st_ino != curr_ino || | |||
296 | st.st_dev != curr_dev)) { | |||
297 | /* | |||
298 | * screen_trashed=2 causes | |||
299 | * make_display to reopen the file. | |||
300 | */ | |||
301 | screen_trashed = 2; | |||
302 | return (EOI(-1)); | |||
303 | } | |||
304 | } | |||
305 | } | |||
306 | if (any_sigs()) | |||
307 | return (EOI(-1)); | |||
308 | } | |||
309 | ||||
310 | found: | |||
311 | if (ch_bufheadthisfile->buflist.next != bn) { | |||
312 | /* | |||
313 | * Move the buffer to the head of the buffer chain. | |||
314 | * This orders the buffer chain, most- to least-recently used. | |||
315 | */ | |||
316 | BUF_RM(bn)(bn)->next->prev = (bn)->prev; (bn)->prev->next = (bn)->next;; | |||
317 | BUF_INS_HEAD(bn)(bn)->next = thisfile->buflist.next; (bn)->prev = (& thisfile->buflist); thisfile->buflist.next->prev = ( bn); thisfile->buflist.next = (bn);; | |||
318 | ||||
319 | /* | |||
320 | * Move to head of hash chain too. | |||
321 | */ | |||
322 | BUF_HASH_RM(bn)(bn)->hnext->hprev = (bn)->hprev; (bn)->hprev-> hnext = (bn)->hnext;; | |||
323 | BUF_HASH_INS(bn, h)(bn)->hnext = thisfile->hashtbl[h].hnext; (bn)->hprev = (&thisfile->hashtbl[h]); thisfile->hashtbl[h].hnext ->hprev = (bn); thisfile->hashtbl[h].hnext = (bn);; | |||
324 | } | |||
325 | ||||
326 | if (ch_offsetthisfile->offset >= bp->datasize) | |||
327 | /* | |||
328 | * After all that, we still don't have enough data. | |||
329 | * Go back and try again. | |||
330 | */ | |||
331 | goto read_more; | |||
332 | ||||
333 | return (bp->data[ch_offsetthisfile->offset]); | |||
334 | } | |||
335 | ||||
336 | /* | |||
337 | * ch_ungetchar is a rather kludgy and limited way to push | |||
338 | * a single char onto an input file descriptor. | |||
339 | */ | |||
340 | void | |||
341 | ch_ungetchar(int c) | |||
342 | { | |||
343 | if (c != -1 && ch_ungotchar != -1) | |||
344 | error("ch_ungetchar overrun", NULL((void *)0)); | |||
345 | ch_ungotchar = c; | |||
346 | } | |||
347 | ||||
348 | /* | |||
349 | * Close the logfile. | |||
350 | * If we haven't read all of standard input into it, do that now. | |||
351 | */ | |||
352 | void | |||
353 | end_logfile(void) | |||
354 | { | |||
355 | static int tried = FALSE0; | |||
356 | ||||
357 | if (logfile < 0) | |||
358 | return; | |||
359 | if (!tried && ch_fsizethisfile->fsize == -1) { | |||
360 | tried = TRUE1; | |||
361 | ierror("Finishing logfile", NULL((void *)0)); | |||
362 | while (ch_forw_get() != EOI(-1)) | |||
363 | if (abort_sigs()) | |||
364 | break; | |||
365 | } | |||
366 | close(logfile); | |||
367 | logfile = -1; | |||
368 | free(namelogfile); | |||
369 | namelogfile = NULL((void *)0); | |||
370 | } | |||
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 = FALSE0; | |||
383 | BLOCKNUM block; | |||
384 | BLOCKNUM nblocks; | |||
385 | ||||
386 | nblocks = (ch_fposthisfile->fpos + LBUFSIZE8192 - 1) / LBUFSIZE8192; | |||
387 | for (block = 0; block < nblocks; block++) { | |||
388 | int wrote = FALSE0; | |||
389 | FOR_BUFS(bn)for ((bn) = thisfile->buflist.next; (bn) != (&thisfile ->buflist); (bn) = (bn)->next) { | |||
390 | bp = bufnode_buf(bn)((struct buf *)bn); | |||
391 | if (bp->block == block) { | |||
392 | (void) write(logfile, (char *)bp->data, | |||
393 | bp->datasize); | |||
394 | wrote = TRUE1; | |||
395 | break; | |||
396 | } | |||
397 | } | |||
398 | if (!wrote && !warned) { | |||
399 | error("Warning: log file is incomplete", NULL((void *)0)); | |||
400 | warned = TRUE1; | |||
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)((block) & (64 -1)); | |||
416 | FOR_BUFS_IN_CHAIN(h, bn)for ((bn) = thisfile->hashtbl[h].hnext; (bn) != (&thisfile ->hashtbl[h]); (bn) = (bn)->hnext) { | |||
417 | bp = bufnode_buf(bn)((struct buf *)bn); | |||
418 | if (bp->block == block) | |||
419 | return (TRUE1); | |||
420 | } | |||
421 | return (FALSE0); | |||
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 | if (thisfile == NULL((void *)0)) | |||
435 | return (0); | |||
436 | ||||
437 | len = ch_length(); | |||
438 | if (pos < ch_zero()(0) || (len != -1 && pos > len)) | |||
439 | return (1); | |||
440 | ||||
441 | new_block = pos / LBUFSIZE8192; | |||
442 | if (!(ch_flagsthisfile->flags & CH_CANSEEK001) && pos != ch_fposthisfile->fpos && | |||
443 | !buffered(new_block)) { | |||
444 | if (ch_fposthisfile->fpos > pos) | |||
445 | return (1); | |||
446 | while (ch_fposthisfile->fpos < pos) { | |||
447 | if (ch_forw_get() == EOI(-1)) | |||
448 | return (1); | |||
449 | if (abort_sigs()) | |||
450 | return (1); | |||
451 | } | |||
452 | return (0); | |||
453 | } | |||
454 | /* | |||
455 | * Set read pointer. | |||
456 | */ | |||
457 | ch_blockthisfile->block = new_block; | |||
458 | ch_offsetthisfile->offset = pos % LBUFSIZE8192; | |||
459 | return (0); | |||
460 | } | |||
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((void *)0)) | |||
471 | return (0); | |||
472 | ||||
473 | if (ch_flagsthisfile->flags & CH_CANSEEK001) | |||
474 | ch_fsizethisfile->fsize = filesize(ch_filethisfile->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(-1)) | |||
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)) == 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_bufheadthisfile->buflist.next; | |||
511 | if (firstbn == END_OF_CHAIN(&thisfile->buflist)) | |||
512 | return (1); | |||
513 | FOR_BUFS(bn)for ((bn) = thisfile->buflist.next; (bn) != (&thisfile ->buflist); (bn) = (bn)->next) { | |||
514 | if (bufnode_buf(bn)((struct buf *)bn)->block < bufnode_buf(firstbn)((struct buf *)firstbn)->block) | |||
515 | firstbn = bn; | |||
516 | } | |||
517 | ch_blockthisfile->block = bufnode_buf(firstbn)((struct buf *)firstbn)->block; | |||
518 | ch_offsetthisfile->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 | if (thisfile == NULL((void *)0)) | |||
529 | return (-1); | |||
530 | if (ignore_eoi) | |||
531 | return (-1); | |||
532 | if (ch_flagsthisfile->flags & CH_NODATA020) | |||
533 | return (0); | |||
534 | return (ch_fsizethisfile->fsize); | |||
535 | } | |||
536 | ||||
537 | /* | |||
538 | * Return the current position in the file. | |||
539 | */ | |||
540 | off_t | |||
541 | ch_tell(void) | |||
542 | { | |||
543 | if (thisfile == NULL((void *)0)) | |||
544 | return (-1); | |||
545 | return ((ch_blockthisfile->block * LBUFSIZE8192) + ch_offsetthisfile->offset); | |||
546 | } | |||
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 | if (thisfile == NULL((void *)0)) | |||
557 | return (EOI(-1)); | |||
558 | c = ch_get(); | |||
559 | if (c == EOI(-1)) | |||
560 | return (EOI(-1)); | |||
561 | if (ch_offsetthisfile->offset < LBUFSIZE8192-1) { | |||
562 | ch_offsetthisfile->offset++; | |||
563 | } else { | |||
564 | ch_blockthisfile->block ++; | |||
565 | ch_offsetthisfile->offset = 0; | |||
566 | } | |||
567 | return (c); | |||
568 | } | |||
569 | ||||
570 | /* | |||
571 | * Pre-decrement the read pointer and get the new current char. | |||
572 | */ | |||
573 | int | |||
574 | ch_back_get(void) | |||
575 | { | |||
576 | if (thisfile == NULL((void *)0)) | |||
| ||||
577 | return (EOI(-1)); | |||
578 | if (ch_offsetthisfile->offset > 0) { | |||
579 | ch_offsetthisfile->offset --; | |||
580 | } else { | |||
581 | if (ch_blockthisfile->block <= 0) | |||
582 | return (EOI(-1)); | |||
583 | if (!(ch_flagsthisfile->flags & CH_CANSEEK001) && !buffered(ch_blockthisfile->block-1)) | |||
584 | return (EOI(-1)); | |||
585 | ch_blockthisfile->block--; | |||
586 | ch_offsetthisfile->offset = LBUFSIZE8192-1; | |||
587 | } | |||
588 | return (ch_get()); | |||
589 | } | |||
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 | if (bufspace < 0) { | |||
599 | maxbufs = -1; | |||
600 | } else { | |||
601 | maxbufs = ((bufspace * 1024) + LBUFSIZE8192-1) / LBUFSIZE8192; | |||
602 | if (maxbufs < 1) | |||
603 | maxbufs = 1; | |||
604 | } | |||
605 | } | |||
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 | if (thisfile == NULL((void *)0)) | |||
616 | return; | |||
617 | ||||
618 | if (!(ch_flagsthisfile->flags & CH_CANSEEK001)) { | |||
619 | /* | |||
620 | * If input is a pipe, we don't flush buffer contents, | |||
621 | * since the contents can't be recovered. | |||
622 | */ | |||
623 | ch_fsizethisfile->fsize = -1; | |||
624 | return; | |||
625 | } | |||
626 | ||||
627 | /* | |||
628 | * Initialize all the buffers. | |||
629 | */ | |||
630 | FOR_BUFS(bn)for ((bn) = thisfile->buflist.next; (bn) != (&thisfile ->buflist); (bn) = (bn)->next) { | |||
631 | bufnode_buf(bn)((struct buf *)bn)->block = -1; | |||
632 | } | |||
633 | ||||
634 | /* | |||
635 | * Figure out the size of the file, if we can. | |||
636 | */ | |||
637 | ch_fsizethisfile->fsize = filesize(ch_filethisfile->file); | |||
638 | ||||
639 | /* | |||
640 | * Seek to a known position: the beginning of the file. | |||
641 | */ | |||
642 | ch_fposthisfile->fpos = 0; | |||
643 | ch_blockthisfile->block = 0; /* ch_fpos / LBUFSIZE; */ | |||
644 | ch_offsetthisfile->offset = 0; /* ch_fpos % LBUFSIZE; */ | |||
645 | ||||
646 | if (lseek(ch_filethisfile->file, (off_t)0, SEEK_SET0) == (off_t)-1) { | |||
647 | /* | |||
648 | * Warning only; even if the seek fails for some reason, | |||
649 | * there's a good chance we're at the beginning anyway. | |||
650 | * {{ I think this is bogus reasoning. }} | |||
651 | */ | |||
652 | error("seek error to 0", NULL((void *)0)); | |||
653 | } | |||
654 | } | |||
655 | ||||
656 | /* | |||
657 | * Allocate a new buffer. | |||
658 | * The buffer is added to the tail of the buffer chain. | |||
659 | */ | |||
660 | static int | |||
661 | ch_addbuf(void) | |||
662 | { | |||
663 | struct buf *bp; | |||
664 | struct bufnode *bn; | |||
665 | ||||
666 | /* | |||
667 | * Allocate and initialize a new buffer and link it | |||
668 | * onto the tail of the buffer list. | |||
669 | */ | |||
670 | bp = calloc(1, sizeof (struct buf)); | |||
671 | if (bp == NULL((void *)0)) | |||
672 | return (1); | |||
673 | ch_nbufsthisfile->nbufs++; | |||
674 | bp->block = -1; | |||
675 | bn = &bp->node; | |||
676 | ||||
677 | BUF_INS_TAIL(bn)(bn)->next = (&thisfile->buflist); (bn)->prev = thisfile ->buflist.prev; thisfile->buflist.prev->next = (bn); thisfile->buflist.prev = (bn);; | |||
678 | BUF_HASH_INS(bn, 0)(bn)->hnext = thisfile->hashtbl[0].hnext; (bn)->hprev = (&thisfile->hashtbl[0]); thisfile->hashtbl[0].hnext ->hprev = (bn); thisfile->hashtbl[0].hnext = (bn);; | |||
679 | return (0); | |||
680 | } | |||
681 | ||||
682 | /* | |||
683 | * | |||
684 | */ | |||
685 | static void | |||
686 | init_hashtbl(void) | |||
687 | { | |||
688 | int h; | |||
689 | ||||
690 | for (h = 0; h < BUFHASH_SIZE64; h++) { | |||
691 | thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h)(&thisfile->hashtbl[h]); | |||
692 | thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h)(&thisfile->hashtbl[h]); | |||
693 | } | |||
694 | } | |||
695 | ||||
696 | /* | |||
697 | * Delete all buffers for this file. | |||
698 | */ | |||
699 | static void | |||
700 | ch_delbufs(void) | |||
701 | { | |||
702 | struct bufnode *bn; | |||
703 | ||||
704 | while (ch_bufheadthisfile->buflist.next != END_OF_CHAIN(&thisfile->buflist)) { | |||
705 | bn = ch_bufheadthisfile->buflist.next; | |||
706 | BUF_RM(bn)(bn)->next->prev = (bn)->prev; (bn)->prev->next = (bn)->next;; | |||
707 | free(bufnode_buf(bn)((struct buf *)bn)); | |||
708 | } | |||
709 | ch_nbufsthisfile->nbufs = 0; | |||
710 | init_hashtbl(); | |||
711 | } | |||
712 | ||||
713 | /* | |||
714 | * Is it possible to seek on a file descriptor? | |||
715 | */ | |||
716 | int | |||
717 | seekable(int f) | |||
718 | { | |||
719 | return (lseek(f, (off_t)1, SEEK_SET0) != (off_t)-1); | |||
720 | } | |||
721 | ||||
722 | /* | |||
723 | * Force EOF to be at the current read position. | |||
724 | * This is used after an ignore_eof read, during which the EOF may change. | |||
725 | */ | |||
726 | void | |||
727 | ch_set_eof(void) | |||
728 | { | |||
729 | ch_fsizethisfile->fsize = ch_fposthisfile->fpos; | |||
730 | } | |||
731 | ||||
732 | ||||
733 | /* | |||
734 | * Initialize file state for a new file. | |||
735 | */ | |||
736 | void | |||
737 | ch_init(int f, int flags) | |||
738 | { | |||
739 | /* | |||
740 | * See if we already have a filestate for this file. | |||
741 | */ | |||
742 | thisfile = get_filestate(curr_ifile); | |||
743 | if (thisfile == NULL((void *)0)) { | |||
744 | /* | |||
745 | * Allocate and initialize a new filestate. | |||
746 | */ | |||
747 | thisfile = calloc(1, sizeof (struct filestate)); | |||
748 | thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN(&thisfile->buflist); | |||
749 | thisfile->nbufs = 0; | |||
750 | thisfile->flags = 0; | |||
751 | thisfile->fpos = 0; | |||
752 | thisfile->block = 0; | |||
753 | thisfile->offset = 0; | |||
754 | thisfile->file = -1; | |||
755 | thisfile->fsize = -1; | |||
756 | ch_flagsthisfile->flags = flags; | |||
757 | init_hashtbl(); | |||
758 | /* | |||
759 | * Try to seek; set CH_CANSEEK if it works. | |||
760 | */ | |||
761 | if ((flags & CH_CANSEEK001) && !seekable(f)) | |||
762 | ch_flagsthisfile->flags &= ~CH_CANSEEK001; | |||
763 | set_filestate(curr_ifile, (void *) thisfile); | |||
764 | } | |||
765 | if (thisfile->file == -1) | |||
766 | thisfile->file = f; | |||
767 | ch_flush(); | |||
768 | } | |||
769 | ||||
770 | /* | |||
771 | * Close a filestate. | |||
772 | */ | |||
773 | void | |||
774 | ch_close(void) | |||
775 | { | |||
776 | int keepstate = FALSE0; | |||
777 | ||||
778 | if (thisfile == NULL((void *)0)) | |||
779 | return; | |||
780 | ||||
781 | if (ch_flagsthisfile->flags & (CH_CANSEEK001|CH_POPENED004|CH_HELPFILE010)) { | |||
782 | /* | |||
783 | * We can seek or re-open, so we don't need to keep buffers. | |||
784 | */ | |||
785 | ch_delbufs(); | |||
786 | } else { | |||
787 | keepstate = TRUE1; | |||
788 | } | |||
789 | if (!(ch_flagsthisfile->flags & CH_KEEPOPEN002)) { | |||
790 | /* | |||
791 | * We don't need to keep the file descriptor open | |||
792 | * (because we can re-open it.) | |||
793 | * But don't really close it if it was opened via popen(), | |||
794 | * because pclose() wants to close it. | |||
795 | */ | |||
796 | if (!(ch_flagsthisfile->flags & CH_POPENED004)) | |||
797 | close(ch_filethisfile->file); | |||
798 | ch_filethisfile->file = -1; | |||
799 | } else { | |||
800 | keepstate = TRUE1; | |||
801 | } | |||
802 | if (!keepstate) { | |||
803 | /* | |||
804 | * We don't even need to keep the filestate structure. | |||
805 | */ | |||
806 | free(thisfile); | |||
807 | thisfile = NULL((void *)0); | |||
808 | set_filestate(curr_ifile, NULL((void *)0)); | |||
809 | } | |||
810 | } | |||
811 | ||||
812 | /* | |||
813 | * Return ch_flags for the current file. | |||
814 | */ | |||
815 | int | |||
816 | ch_getflags(void) | |||
817 | { | |||
818 | if (thisfile == NULL((void *)0)) | |||
819 | return (0); | |||
820 | return (ch_flagsthisfile->flags); | |||
821 | } |