comparison miscellany/less-177/ch.c @ 0:bce86c4163a3

Initial revision
author kono
date Mon, 18 Apr 2005 23:46:02 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:bce86c4163a3
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 }