0
|
1 /*
|
|
2 * Low level character input from the input file.
|
|
3 * We use these special purpose routines which optimize moving
|
|
4 * both forward and backward from the current read pointer.
|
|
5 */
|
|
6
|
|
7 #include "less.h"
|
|
8
|
|
9 public int file = -1; /* File descriptor of the input file */
|
|
10 public int ignore_eoi;
|
|
11
|
|
12 /*
|
|
13 * Pool of buffers holding the most recently used blocks of the input file.
|
|
14 */
|
|
15 #define BUFSIZ 1024
|
|
16 struct buf {
|
|
17 struct buf *next, *prev; /* Must be first to match struct filestate */
|
|
18 long block;
|
|
19 unsigned int datasize;
|
|
20 unsigned char data[BUFSIZ];
|
|
21 };
|
|
22
|
|
23 /*
|
|
24 * The buffer pool is kept as a doubly-linked circular list,
|
|
25 * in order from most- to least-recently used.
|
|
26 * The circular list is anchored by the file state "thisfile".
|
|
27 *
|
|
28 * The file state is maintained in a filestate structure.
|
|
29 * There are two such structures, one used when input is a pipe
|
|
30 * and the other when input is an ordinary file.
|
|
31 * This is so that we can leave a pipe, look and other files,
|
|
32 * and return to the pipe without losing buffered data.
|
|
33 * Buffered data can be reconstructed for a non-pipe file by
|
|
34 * simply re-reading the file, but a pipe cannot be re-read.
|
|
35 */
|
|
36
|
|
37 struct filestate {
|
|
38 struct buf *next, *prev; /* Must be first to match struct buf */
|
|
39 POSITION fpos;
|
|
40 int nbufs;
|
|
41 long block;
|
|
42 int offset;
|
|
43 POSITION fsize;
|
|
44 };
|
|
45
|
|
46 #define END_OF_CHAIN ((struct buf *)thisfile)
|
|
47 #define buf_head thisfile->next
|
|
48 #define buf_tail thisfile->prev
|
|
49 #define ch_nbufs thisfile->nbufs
|
|
50 #define ch_block thisfile->block
|
|
51 #define ch_offset thisfile->offset
|
|
52 #define ch_fpos thisfile->fpos
|
|
53 #define ch_fsize thisfile->fsize
|
|
54
|
|
55 static struct filestate pipefile =
|
|
56 { (struct buf *)&pipefile, (struct buf *)&pipefile };
|
|
57
|
|
58 static struct filestate nonpipefile =
|
|
59 { (struct buf *)&nonpipefile, (struct buf *)&nonpipefile };
|
|
60
|
|
61 static struct filestate *thisfile;
|
|
62
|
|
63 extern int ispipe;
|
|
64 extern int autobuf;
|
|
65 extern int sigs;
|
|
66 #if LOGFILE
|
|
67 extern int logfile;
|
|
68 extern char *namelogfile;
|
|
69 #endif
|
|
70
|
|
71 static int ch_addbuf();
|
|
72
|
|
73
|
|
74 /*
|
|
75 * Get the character pointed to by the read pointer.
|
|
76 * ch_get() is a macro which is more efficient to call
|
|
77 * than fch_get (the function), in the usual case
|
|
78 * that the block desired is at the head of the chain.
|
|
79 */
|
|
80 #define ch_get() ((ch_block == buf_head->block && \
|
|
81 ch_offset < buf_head->datasize) ? \
|
|
82 buf_head->data[ch_offset] : fch_get())
|
|
83 static int
|
|
84 fch_get()
|
|
85 {
|
|
86 register struct buf *bp;
|
|
87 register int n;
|
|
88 register int slept;
|
|
89 POSITION pos;
|
|
90 POSITION len;
|
|
91
|
|
92 slept = 0;
|
|
93
|
|
94 /*
|
|
95 * Look for a buffer holding the desired block.
|
|
96 */
|
|
97 for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
|
|
98 if (bp->block == ch_block)
|
|
99 {
|
|
100 if (ch_offset >= bp->datasize)
|
|
101 /*
|
|
102 * Need more data in this buffer.
|
|
103 */
|
|
104 goto read_more;
|
|
105 goto found;
|
|
106 }
|
|
107 /*
|
|
108 * Block is not in a buffer.
|
|
109 * Take the least recently used buffer
|
|
110 * and read the desired block into it.
|
|
111 * If the LRU buffer has data in it,
|
|
112 * and autobuf is true, and input is a pipe,
|
|
113 * then try to allocate a new buffer first.
|
|
114 */
|
|
115 if (autobuf && ispipe && buf_tail->block != (long)(-1))
|
|
116 if (ch_addbuf(1))
|
|
117 /*
|
|
118 * Allocation failed: turn off autobuf.
|
|
119 */
|
|
120 autobuf = 0;
|
|
121 bp = buf_tail;
|
|
122 bp->block = ch_block;
|
|
123 bp->datasize = 0;
|
|
124
|
|
125 read_more:
|
|
126 pos = (ch_block * BUFSIZ) + bp->datasize;
|
|
127 if ((len = ch_length()) != NULL_POSITION && pos >= len)
|
|
128 /*
|
|
129 * At end of file.
|
|
130 */
|
|
131 return (EOI);
|
|
132
|
|
133 if (pos != ch_fpos)
|
|
134 {
|
|
135 /*
|
|
136 * Not at the correct position: must seek.
|
|
137 * If input is a pipe, we're in trouble (can't seek on a pipe).
|
|
138 * Some data has been lost: just return "?".
|
|
139 */
|
|
140 if (ispipe)
|
|
141 return ('?');
|
|
142 if (lseek(file, (offset_t)pos, 0) == BAD_LSEEK)
|
|
143 {
|
|
144 error("seek error", NULL_PARG);
|
|
145 quit(1);
|
|
146 }
|
|
147 ch_fpos = pos;
|
|
148 }
|
|
149
|
|
150 /*
|
|
151 * Read the block.
|
|
152 * If we read less than a full block, that's ok.
|
|
153 * We use partial block and pick up the rest next time.
|
|
154 */
|
|
155 n = iread(file, &bp->data[bp->datasize],
|
|
156 (unsigned int)(BUFSIZ - bp->datasize));
|
|
157 if (n == READ_INTR)
|
|
158 return (EOI);
|
|
159 if (n < 0)
|
|
160 {
|
|
161 error("read error", NULL_PARG);
|
|
162 quit(1);
|
|
163 }
|
|
164 ch_fpos += n;
|
|
165
|
|
166 #if LOGFILE
|
|
167 /*
|
|
168 * If we have a log file, write the new data to it.
|
|
169 */
|
|
170 if (logfile >= 0 && n > 0)
|
|
171 write(logfile, (char *) &bp->data[bp->datasize], n);
|
|
172 #endif
|
|
173
|
|
174 bp->datasize += n;
|
|
175
|
|
176 /*
|
|
177 * If we have read to end of file, set ch_fsize to indicate
|
|
178 * the position of the end of file.
|
|
179 */
|
|
180 if (n == 0)
|
|
181 {
|
|
182 ch_fsize = pos;
|
|
183 if (ignore_eoi)
|
|
184 {
|
|
185 /*
|
|
186 * We are ignoring EOF.
|
|
187 * Wait a while, then try again.
|
|
188 */
|
|
189 if (!slept)
|
|
190 ierror("Waiting for data", NULL_PARG);
|
|
191 sleep(1);
|
|
192 slept = 1;
|
|
193 }
|
|
194 if (sigs)
|
|
195 return (EOI);
|
|
196 }
|
|
197
|
|
198 found:
|
|
199 if (buf_head != bp)
|
|
200 {
|
|
201 /*
|
|
202 * Move the buffer to the head of the buffer chain.
|
|
203 * This orders the buffer chain, most- to least-recently used.
|
|
204 */
|
|
205 bp->next->prev = bp->prev;
|
|
206 bp->prev->next = bp->next;
|
|
207
|
|
208 bp->next = buf_head;
|
|
209 bp->prev = END_OF_CHAIN;
|
|
210 buf_head->prev = bp;
|
|
211 buf_head = bp;
|
|
212 }
|
|
213
|
|
214 if (ch_offset >= bp->datasize)
|
|
215 /*
|
|
216 * After all that, we still don't have enough data.
|
|
217 * Go back and try again.
|
|
218 */
|
|
219 goto read_more;
|
|
220
|
|
221 return (bp->data[ch_offset]);
|
|
222 }
|
|
223
|
|
224 #if LOGFILE
|
|
225 /*
|
|
226 * Close the logfile.
|
|
227 * If we haven't read all of standard input into it, do that now.
|
|
228 */
|
|
229 public void
|
|
230 end_logfile()
|
|
231 {
|
|
232 static int tried = 0;
|
|
233
|
|
234 if (logfile < 0)
|
|
235 return;
|
|
236 if (!tried && ch_fsize == NULL_POSITION)
|
|
237 {
|
|
238 tried = 1;
|
|
239 ierror("Finishing logfile", NULL_PARG);
|
|
240 while (ch_forw_get() != EOI)
|
|
241 if (sigs)
|
|
242 break;
|
|
243 }
|
|
244 close(logfile);
|
|
245 logfile = -1;
|
|
246 namelogfile = NULL;
|
|
247 }
|
|
248
|
|
249 /*
|
|
250 * Start a log file AFTER less has already been running.
|
|
251 * Invoked from the - command; see toggle_option().
|
|
252 * Write all the existing buffered data to the log file.
|
|
253 */
|
|
254 public void
|
|
255 sync_logfile()
|
|
256 {
|
|
257 register struct buf *bp;
|
|
258 long block;
|
|
259 long last_block;
|
|
260
|
|
261 last_block = (ch_fpos + BUFSIZ - 1) / BUFSIZ;
|
|
262 for (block = 0; block <= last_block; block++)
|
|
263 for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
|
|
264 if (bp->block == block)
|
|
265 {
|
|
266 write(logfile, (char *) bp->data, bp->datasize);
|
|
267 break;
|
|
268 }
|
|
269 }
|
|
270
|
|
271 #endif
|
|
272
|
|
273 /*
|
|
274 * Determine if a specific block is currently in one of the buffers.
|
|
275 */
|
|
276 static int
|
|
277 buffered(block)
|
|
278 long block;
|
|
279 {
|
|
280 register struct buf *bp;
|
|
281
|
|
282 for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
|
|
283 if (bp->block == block)
|
|
284 return (1);
|
|
285 return (0);
|
|
286 }
|
|
287
|
|
288 /*
|
|
289 * Seek to a specified position in the file.
|
|
290 * Return 0 if successful, non-zero if can't seek there.
|
|
291 */
|
|
292 public int
|
|
293 ch_seek(pos)
|
|
294 register POSITION pos;
|
|
295 {
|
|
296 long new_block;
|
|
297 POSITION len;
|
|
298
|
|
299 len = ch_length();
|
|
300 if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
|
|
301 return (1);
|
|
302
|
|
303 new_block = pos / BUFSIZ;
|
|
304 if (ispipe && pos != ch_fpos && !buffered(new_block))
|
|
305 return (1);
|
|
306 /*
|
|
307 * Set read pointer.
|
|
308 */
|
|
309 ch_block = new_block;
|
|
310 ch_offset = pos % BUFSIZ;
|
|
311 return (0);
|
|
312 }
|
|
313
|
|
314 /*
|
|
315 * Seek to the end of the file.
|
|
316 */
|
|
317 public int
|
|
318 ch_end_seek()
|
|
319 {
|
|
320 POSITION len;
|
|
321
|
|
322 if (!ispipe)
|
|
323 ch_fsize = filesize(file);
|
|
324
|
|
325 len = ch_length();
|
|
326 if (len != NULL_POSITION)
|
|
327 return (ch_seek(len));
|
|
328
|
|
329 /*
|
|
330 * Do it the slow way: read till end of data.
|
|
331 */
|
|
332 while (ch_forw_get() != EOI)
|
|
333 if (sigs)
|
|
334 return (1);
|
|
335 return (0);
|
|
336 }
|
|
337
|
|
338 /*
|
|
339 * Seek to the beginning of the file, or as close to it as we can get.
|
|
340 * We may not be able to seek there if input is a pipe and the
|
|
341 * beginning of the pipe is no longer buffered.
|
|
342 */
|
|
343 public int
|
|
344 ch_beg_seek()
|
|
345 {
|
|
346 register struct buf *bp, *firstbp;
|
|
347
|
|
348 /*
|
|
349 * Try a plain ch_seek first.
|
|
350 */
|
|
351 if (ch_seek(ch_zero()) == 0)
|
|
352 return (0);
|
|
353
|
|
354 /*
|
|
355 * Can't get to position 0.
|
|
356 * Look thru the buffers for the one closest to position 0.
|
|
357 */
|
|
358 firstbp = bp = buf_head;
|
|
359 if (bp == END_OF_CHAIN)
|
|
360 return (1);
|
|
361 while ((bp = bp->next) != END_OF_CHAIN)
|
|
362 if (bp->block < firstbp->block)
|
|
363 firstbp = bp;
|
|
364 ch_block = firstbp->block;
|
|
365 ch_offset = 0;
|
|
366 return (0);
|
|
367 }
|
|
368
|
|
369 /*
|
|
370 * Return the length of the file, if known.
|
|
371 */
|
|
372 public POSITION
|
|
373 ch_length()
|
|
374 {
|
|
375 if (ignore_eoi)
|
|
376 return (NULL_POSITION);
|
|
377 return (ch_fsize);
|
|
378 }
|
|
379
|
|
380 /*
|
|
381 * Return the current position in the file.
|
|
382 */
|
|
383 #define tellpos(blk,off) ((POSITION)((((long)(blk)) * BUFSIZ) + (off)))
|
|
384
|
|
385 public POSITION
|
|
386 ch_tell()
|
|
387 {
|
|
388 return (tellpos(ch_block, ch_offset));
|
|
389 }
|
|
390
|
|
391 /*
|
|
392 * Get the current char and post-increment the read pointer.
|
|
393 */
|
|
394 public int
|
|
395 ch_forw_get()
|
|
396 {
|
|
397 register int c;
|
|
398
|
|
399 c = ch_get();
|
|
400 if (c == EOI)
|
|
401 return (EOI);
|
|
402 if (ch_offset < BUFSIZ-1)
|
|
403 ch_offset++;
|
|
404 else
|
|
405 {
|
|
406 #if __ZOFFSET /* NOT WORKING */
|
|
407 if (ch_fsize != NULL_POSITION &&
|
|
408 tellpos(ch_block+1, 0) >= ch_fsize)
|
|
409 return (EOI);
|
|
410 #endif
|
|
411 ch_block ++;
|
|
412 ch_offset = 0;
|
|
413 }
|
|
414 return (c);
|
|
415 }
|
|
416
|
|
417 /*
|
|
418 * Pre-decrement the read pointer and get the new current char.
|
|
419 */
|
|
420 public int
|
|
421 ch_back_get()
|
|
422 {
|
|
423 if (ch_offset > 0)
|
|
424 ch_offset --;
|
|
425 else
|
|
426 {
|
|
427 #if __ZOFFSET /* NOT WORKING */
|
|
428 if (tellpos(ch_block-1, BUFSIZ-1) < ch_zero())
|
|
429 return (EOI);
|
|
430 #else
|
|
431 if (ch_block <= 0)
|
|
432 return (EOI);
|
|
433 #endif
|
|
434 if (ispipe && !buffered(ch_block-1))
|
|
435 return (EOI);
|
|
436 ch_block--;
|
|
437 ch_offset = BUFSIZ-1;
|
|
438 }
|
|
439 return (ch_get());
|
|
440 }
|
|
441
|
|
442 /*
|
|
443 * Allocate buffers.
|
|
444 * Caller wants us to have a total of at least want_nbufs buffers.
|
|
445 */
|
|
446 public int
|
|
447 ch_nbuf(want_nbufs)
|
|
448 int want_nbufs;
|
|
449 {
|
|
450 PARG parg;
|
|
451
|
|
452 if (ch_nbufs < want_nbufs && ch_addbuf(want_nbufs - ch_nbufs))
|
|
453 {
|
|
454 /*
|
|
455 * Cannot allocate enough buffers.
|
|
456 * If we don't have ANY, then quit.
|
|
457 * Otherwise, just report the error and return.
|
|
458 */
|
|
459 parg.p_int = want_nbufs - ch_nbufs;
|
|
460 error("Cannot allocate %d buffers", &parg);
|
|
461 if (ch_nbufs == 0)
|
|
462 quit(1);
|
|
463 }
|
|
464 return (ch_nbufs);
|
|
465 }
|
|
466
|
|
467 /*
|
|
468 * Flush any saved file state, including buffer contents.
|
|
469 */
|
|
470 public void
|
|
471 ch_flush()
|
|
472 {
|
|
473 register struct buf *bp;
|
|
474
|
|
475 if (ispipe)
|
|
476 {
|
|
477 /*
|
|
478 * If input is a pipe, we don't flush buffer contents,
|
|
479 * since the contents can't be recovered.
|
|
480 */
|
|
481 ch_fsize = NULL_POSITION;
|
|
482 return;
|
|
483 }
|
|
484
|
|
485 /*
|
|
486 * Initialize all the buffers.
|
|
487 */
|
|
488 for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
|
|
489 bp->block = (long)(-1);
|
|
490
|
|
491 /*
|
|
492 * Figure out the size of the file, if we can.
|
|
493 */
|
|
494 ch_fsize = filesize(file);
|
|
495
|
|
496 /*
|
|
497 * Seek to a known position: the beginning of the file.
|
|
498 */
|
|
499 ch_fpos = 0;
|
|
500 ch_block = ch_fpos / BUFSIZ;
|
|
501 ch_offset = ch_fpos % BUFSIZ;
|
|
502
|
|
503 if (lseek(file, (offset_t)0, 0) == BAD_LSEEK)
|
|
504 {
|
|
505 /*
|
|
506 * Warning only; even if the seek fails for some reason,
|
|
507 * there's a good chance we're at the beginning anyway.
|
|
508 * {{ I think this is bogus reasoning. }}
|
|
509 */
|
|
510 error("seek error to 0", NULL_PARG);
|
|
511 }
|
|
512 }
|
|
513
|
|
514 /*
|
|
515 * Allocate some new buffers.
|
|
516 * The buffers are added to the tail of the buffer chain.
|
|
517 */
|
|
518 static int
|
|
519 ch_addbuf(nnew)
|
|
520 int nnew;
|
|
521 {
|
|
522 register struct buf *bp;
|
|
523 register struct buf *newbufs;
|
|
524
|
|
525 /*
|
|
526 * We don't have enough buffers.
|
|
527 * Allocate some new ones.
|
|
528 */
|
|
529 newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
|
|
530 if (newbufs == NULL)
|
|
531 return (1);
|
|
532
|
|
533 /*
|
|
534 * Initialize the new buffers and link them together.
|
|
535 * Link them all onto the tail of the buffer list.
|
|
536 */
|
|
537 ch_nbufs += nnew;
|
|
538 for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++)
|
|
539 {
|
|
540 bp->next = bp + 1;
|
|
541 bp->prev = bp - 1;
|
|
542 bp->block = (long)(-1);
|
|
543 }
|
|
544 newbufs[nnew-1].next = END_OF_CHAIN;
|
|
545 newbufs[0].prev = buf_tail;
|
|
546 buf_tail->next = &newbufs[0];
|
|
547 buf_tail = &newbufs[nnew-1];
|
|
548 return (0);
|
|
549 }
|
|
550
|
|
551 /*
|
|
552 * Use the pipe file state.
|
|
553 */
|
|
554 public void
|
|
555 ch_pipe()
|
|
556 {
|
|
557 thisfile = &pipefile;
|
|
558 }
|
|
559
|
|
560 /*
|
|
561 * Use the non-pipe file state.
|
|
562 */
|
|
563 public void
|
|
564 ch_nonpipe()
|
|
565 {
|
|
566 thisfile = &nonpipefile;
|
|
567 }
|