Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / miscellany / less-177 / ch.c
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 }