Annotation of src/usr.bin/less/ch.c, Revision 1.1.1.1
1.1 etheisen 1: /*
2: * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice in the documentation and/or other materials provided with
12: * the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
18: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27:
28: /*
29: * Low level character input from the input file.
30: * We use these special purpose routines which optimize moving
31: * both forward and backward from the current read pointer.
32: */
33:
34: #include "less.h"
35:
36: public int ignore_eoi;
37:
38: /*
39: * Pool of buffers holding the most recently used blocks of the input file.
40: * The buffer pool is kept as a doubly-linked circular list,
41: * in order from most- to least-recently used.
42: * The circular list is anchored by the file state "thisfile".
43: */
44: #define LBUFSIZE 1024
45: struct buf {
46: struct buf *next, *prev; /* Must be first to match struct filestate */
47: long block;
48: unsigned int datasize;
49: unsigned char data[LBUFSIZE];
50: };
51:
52: /*
53: * The file state is maintained in a filestate structure.
54: * A pointer to the filestate is kept in the ifile structure.
55: */
56: struct filestate {
57: /* -- Following members must match struct buf */
58: struct buf *buf_next, *buf_prev;
59: long buf_block;
60: /* -- End of struct buf copy */
61: int file;
62: int flags;
63: POSITION fpos;
64: int nbufs;
65: long block;
66: int offset;
67: POSITION fsize;
68: };
69:
70:
71: #define END_OF_CHAIN ((struct buf *)thisfile)
72: #define ch_bufhead thisfile->buf_next
73: #define ch_buftail thisfile->buf_prev
74: #define ch_nbufs thisfile->nbufs
75: #define ch_block thisfile->block
76: #define ch_offset thisfile->offset
77: #define ch_fpos thisfile->fpos
78: #define ch_fsize thisfile->fsize
79: #define ch_flags thisfile->flags
80: #define ch_file thisfile->file
81:
82: static struct filestate *thisfile;
83: static int ch_ungotchar = -1;
84:
85: extern int autobuf;
86: extern int sigs;
87: extern int cbufs;
88: extern IFILE curr_ifile;
89: #if LOGFILE
90: extern int logfile;
91: extern char *namelogfile;
92: #endif
93:
94: static int ch_addbuf();
95:
96:
97: /*
98: * Get the character pointed to by the read pointer.
99: * ch_get() is a macro which is more efficient to call
100: * than fch_get (the function), in the usual case
101: * that the block desired is at the head of the chain.
102: */
103: #define ch_get() ((ch_block == ch_bufhead->block && \
104: ch_offset < ch_bufhead->datasize) ? \
105: ch_bufhead->data[ch_offset] : fch_get())
106: int
107: fch_get()
108: {
109: register struct buf *bp;
110: register int n;
111: register int slept;
112: POSITION pos;
113: POSITION len;
114:
115: slept = FALSE;
116:
117: /*
118: * Look for a buffer holding the desired block.
119: */
120: for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next)
121: if (bp->block == ch_block)
122: {
123: if (ch_offset >= bp->datasize)
124: /*
125: * Need more data in this buffer.
126: */
127: goto read_more;
128: goto found;
129: }
130: /*
131: * Block is not in a buffer.
132: * Take the least recently used buffer
133: * and read the desired block into it.
134: * If the LRU buffer has data in it,
135: * then maybe allocate a new buffer.
136: */
137: if (ch_buftail == END_OF_CHAIN || ch_buftail->block != (long)(-1))
138: {
139: /*
140: * There is no empty buffer to use.
141: * Allocate a new buffer if:
142: * 1. We can't seek on this file and -b is not in effect; or
143: * 2. We haven't allocated the max buffers for this file yet.
144: */
145: if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
146: (cbufs == -1 || ch_nbufs < cbufs))
147: if (ch_addbuf())
148: /*
149: * Allocation failed: turn off autobuf.
150: */
151: autobuf = OPT_OFF;
152: }
153: bp = ch_buftail;
154: bp->block = ch_block;
155: bp->datasize = 0;
156:
157: read_more:
158: pos = (ch_block * LBUFSIZE) + bp->datasize;
159: if ((len = ch_length()) != NULL_POSITION && pos >= len)
160: /*
161: * At end of file.
162: */
163: return (EOI);
164:
165: if (pos != ch_fpos)
166: {
167: /*
168: * Not at the correct position: must seek.
169: * If input is a pipe, we're in trouble (can't seek on a pipe).
170: * Some data has been lost: just return "?".
171: */
172: if (!(ch_flags & CH_CANSEEK))
173: return ('?');
174: if (lseek(ch_file, (off_t)pos, 0) == BAD_LSEEK)
175: {
176: error("seek error", NULL_PARG);
177: clear_eol();
178: return (EOI);
179: }
180: ch_fpos = pos;
181: }
182:
183: /*
184: * Read the block.
185: * If we read less than a full block, that's ok.
186: * We use partial block and pick up the rest next time.
187: */
188: if (ch_ungotchar == -1)
189: {
190: n = iread(ch_file, &bp->data[bp->datasize],
191: (unsigned int)(LBUFSIZE - bp->datasize));
192: } else
193: {
194: bp->data[bp->datasize] = ch_ungotchar;
195: n = 1;
196: ch_ungotchar = -1;
197: }
198:
199: if (n == READ_INTR)
200: return (EOI);
201: if (n < 0)
202: {
203: error("read error", NULL_PARG);
204: clear_eol();
205: n = 0;
206: }
207:
208: #if LOGFILE
209: /*
210: * If we have a log file, write the new data to it.
211: */
212: if (logfile >= 0 && n > 0)
213: write(logfile, (char *) &bp->data[bp->datasize], n);
214: #endif
215:
216: ch_fpos += n;
217: bp->datasize += n;
218:
219: /*
220: * If we have read to end of file, set ch_fsize to indicate
221: * the position of the end of file.
222: */
223: if (n == 0)
224: {
225: ch_fsize = pos;
226: if (ignore_eoi)
227: {
228: /*
229: * We are ignoring EOF.
230: * Wait a while, then try again.
231: */
232: if (!slept)
233: ierror("Waiting for data", NULL_PARG);
234: #if !MSOFTC
235: sleep(1);
236: #endif
237: slept = TRUE;
238: }
239: if (ABORT_SIGS())
240: return (EOI);
241: }
242:
243: found:
244: if (ch_bufhead != bp)
245: {
246: /*
247: * Move the buffer to the head of the buffer chain.
248: * This orders the buffer chain, most- to least-recently used.
249: */
250: bp->next->prev = bp->prev;
251: bp->prev->next = bp->next;
252:
253: bp->next = ch_bufhead;
254: bp->prev = END_OF_CHAIN;
255: ch_bufhead->prev = bp;
256: ch_bufhead = bp;
257: }
258:
259: if (ch_offset >= bp->datasize)
260: /*
261: * After all that, we still don't have enough data.
262: * Go back and try again.
263: */
264: goto read_more;
265:
266: return (bp->data[ch_offset]);
267: }
268:
269: /*
270: * ch_ungetchar is a rather kludgy and limited way to push
271: * a single char onto an input file descriptor.
272: */
273: public void
274: ch_ungetchar(c)
275: int c;
276: {
277: if (c != -1 && ch_ungotchar != -1)
278: error("ch_ungetchar overrun", NULL_PARG);
279: ch_ungotchar = c;
280: }
281:
282: #if LOGFILE
283: /*
284: * Close the logfile.
285: * If we haven't read all of standard input into it, do that now.
286: */
287: public void
288: end_logfile()
289: {
290: static int tried = FALSE;
291:
292: if (logfile < 0)
293: return;
294: if (!tried && ch_fsize == NULL_POSITION)
295: {
296: tried = TRUE;
297: ierror("Finishing logfile", NULL_PARG);
298: while (ch_forw_get() != EOI)
299: if (ABORT_SIGS())
300: break;
301: }
302: close(logfile);
303: logfile = -1;
304: namelogfile = NULL;
305: }
306:
307: /*
308: * Start a log file AFTER less has already been running.
309: * Invoked from the - command; see toggle_option().
310: * Write all the existing buffered data to the log file.
311: */
312: public void
313: sync_logfile()
314: {
315: register struct buf *bp;
316: int warned = FALSE;
317: long block;
318: long nblocks;
319:
320: nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
321: for (block = 0; block < nblocks; block++)
322: {
323: for (bp = ch_bufhead; ; bp = bp->next)
324: {
325: if (bp == END_OF_CHAIN)
326: {
327: if (!warned)
328: {
329: error("Warning: log file is incomplete",
330: NULL_PARG);
331: warned = TRUE;
332: }
333: break;
334: }
335: if (bp->block == block)
336: {
337: write(logfile, (char *) bp->data, bp->datasize);
338: break;
339: }
340: }
341: }
342: }
343:
344: #endif
345:
346: /*
347: * Determine if a specific block is currently in one of the buffers.
348: */
349: static int
350: buffered(block)
351: long block;
352: {
353: register struct buf *bp;
354:
355: for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next)
356: if (bp->block == block)
357: return (TRUE);
358: return (FALSE);
359: }
360:
361: /*
362: * Seek to a specified position in the file.
363: * Return 0 if successful, non-zero if can't seek there.
364: */
365: public int
366: ch_seek(pos)
367: register POSITION pos;
368: {
369: long new_block;
370: POSITION len;
371:
372: len = ch_length();
373: if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
374: return (1);
375:
376: new_block = pos / LBUFSIZE;
377: if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
378: {
379: if (ch_fpos > pos)
380: return (1);
381: while (ch_fpos < pos)
382: {
383: if (ch_forw_get() == EOI)
384: return (1);
385: if (ABORT_SIGS())
386: return (1);
387: }
388: return (0);
389: }
390: /*
391: * Set read pointer.
392: */
393: ch_block = new_block;
394: ch_offset = pos % LBUFSIZE;
395: return (0);
396: }
397:
398: /*
399: * Seek to the end of the file.
400: */
401: public int
402: ch_end_seek()
403: {
404: POSITION len;
405:
406: if (ch_flags & CH_CANSEEK)
407: ch_fsize = filesize(ch_file);
408:
409: len = ch_length();
410: if (len != NULL_POSITION)
411: return (ch_seek(len));
412:
413: /*
414: * Do it the slow way: read till end of data.
415: */
416: while (ch_forw_get() != EOI)
417: if (ABORT_SIGS())
418: return (1);
419: return (0);
420: }
421:
422: /*
423: * Seek to the beginning of the file, or as close to it as we can get.
424: * We may not be able to seek there if input is a pipe and the
425: * beginning of the pipe is no longer buffered.
426: */
427: public int
428: ch_beg_seek()
429: {
430: register struct buf *bp, *firstbp;
431:
432: /*
433: * Try a plain ch_seek first.
434: */
435: if (ch_seek(ch_zero()) == 0)
436: return (0);
437:
438: /*
439: * Can't get to position 0.
440: * Look thru the buffers for the one closest to position 0.
441: */
442: firstbp = bp = ch_bufhead;
443: if (bp == END_OF_CHAIN)
444: return (1);
445: while ((bp = bp->next) != END_OF_CHAIN)
446: if (bp->block < firstbp->block)
447: firstbp = bp;
448: ch_block = firstbp->block;
449: ch_offset = 0;
450: return (0);
451: }
452:
453: /*
454: * Return the length of the file, if known.
455: */
456: public POSITION
457: ch_length()
458: {
459: if (ignore_eoi)
460: return (NULL_POSITION);
461: return (ch_fsize);
462: }
463:
464: /*
465: * Return the current position in the file.
466: */
467: #define tellpos(blk,off) ((POSITION)((((long)(blk)) * LBUFSIZE) + (off)))
468:
469: public POSITION
470: ch_tell()
471: {
472: return (tellpos(ch_block, ch_offset));
473: }
474:
475: /*
476: * Get the current char and post-increment the read pointer.
477: */
478: public int
479: ch_forw_get()
480: {
481: register int c;
482:
483: c = ch_get();
484: if (c == EOI)
485: return (EOI);
486: if (ch_offset < LBUFSIZE-1)
487: ch_offset++;
488: else
489: {
490: ch_block ++;
491: ch_offset = 0;
492: }
493: return (c);
494: }
495:
496: /*
497: * Pre-decrement the read pointer and get the new current char.
498: */
499: public int
500: ch_back_get()
501: {
502: if (ch_offset > 0)
503: ch_offset --;
504: else
505: {
506: if (ch_block <= 0)
507: return (EOI);
508: if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
509: return (EOI);
510: ch_block--;
511: ch_offset = LBUFSIZE-1;
512: }
513: return (ch_get());
514: }
515:
516: /*
517: * Allocate buffers.
518: * Caller wants us to have a total of at least want_nbufs buffers.
519: */
520: public int
521: ch_nbuf(want_nbufs)
522: int want_nbufs;
523: {
524: PARG parg;
525:
526: while (ch_nbufs < want_nbufs)
527: {
528: if (ch_addbuf())
529: {
530: /*
531: * Cannot allocate enough buffers.
532: * If we don't have ANY, then quit.
533: * Otherwise, just report the error and return.
534: */
535: parg.p_int = want_nbufs - ch_nbufs;
536: error("Cannot allocate %d buffers", &parg);
537: if (ch_nbufs == 0)
538: quit(QUIT_ERROR);
539: break;
540: }
541: }
542: return (ch_nbufs);
543: }
544:
545: /*
546: * Flush (discard) any saved file state, including buffer contents.
547: */
548: public void
549: ch_flush()
550: {
551: register struct buf *bp;
552:
553: if (!(ch_flags & CH_CANSEEK))
554: {
555: /*
556: * If input is a pipe, we don't flush buffer contents,
557: * since the contents can't be recovered.
558: */
559: ch_fsize = NULL_POSITION;
560: return;
561: }
562:
563: /*
564: * Initialize all the buffers.
565: */
566: for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next)
567: bp->block = (long)(-1);
568:
569: /*
570: * Figure out the size of the file, if we can.
571: */
572: ch_fsize = filesize(ch_file);
573:
574: /*
575: * Seek to a known position: the beginning of the file.
576: */
577: ch_fpos = 0;
578: ch_block = 0; /* ch_fpos / LBUFSIZE; */
579: ch_offset = 0; /* ch_fpos % LBUFSIZE; */
580:
581: if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK)
582: {
583: /*
584: * Warning only; even if the seek fails for some reason,
585: * there's a good chance we're at the beginning anyway.
586: * {{ I think this is bogus reasoning. }}
587: */
588: error("seek error to 0", NULL_PARG);
589: }
590: }
591:
592: /*
593: * Allocate a new buffer.
594: * The buffer is added to the tail of the buffer chain.
595: */
596: static int
597: ch_addbuf()
598: {
599: register struct buf *bp;
600:
601: /*
602: * Allocate and initialize a new buffer and link it
603: * onto the tail of the buffer list.
604: */
605: bp = (struct buf *) calloc(1, sizeof(struct buf));
606: if (bp == NULL)
607: return (1);
608: ch_nbufs++;
609: bp->block = (long)(-1);
610: bp->next = END_OF_CHAIN;
611: bp->prev = ch_buftail;
612: ch_buftail->next = bp;
613: ch_buftail = bp;
614: return (0);
615: }
616:
617: /*
618: * Delete all buffers for this file.
619: */
620: static void
621: ch_delbufs()
622: {
623: register struct buf *bp;
624:
625: while (ch_bufhead != END_OF_CHAIN)
626: {
627: bp = ch_bufhead;
628: bp->next->prev = bp->prev;;
629: bp->prev->next = bp->next;
630: free(bp);
631: }
632: ch_nbufs = 0;
633: }
634:
635: /*
636: * Is it possible to seek on a file descriptor?
637: */
638: public int
639: seekable(f)
640: int f;
641: {
642: return (lseek(f, (off_t)1, 0) != BAD_LSEEK);
643: }
644:
645: /*
646: * Initialize file state for a new file.
647: */
648: public void
649: ch_init(f, flags)
650: int f;
651: int flags;
652: {
653: /*
654: * See if we already have a filestate for this file.
655: */
656: thisfile = (struct filestate *) get_filestate(curr_ifile);
657: if (thisfile == NULL)
658: {
659: /*
660: * Allocate and initialize a new filestate.
661: */
662: thisfile = (struct filestate *)
663: calloc(1, sizeof(struct filestate));
664: thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN;
665: thisfile->buf_block = (long)(-1);
666: thisfile->nbufs = 0;
667: thisfile->flags = 0;
668: thisfile->fpos = 0;
669: thisfile->block = 0;
670: thisfile->offset = 0;
671: thisfile->file = -1;
672: thisfile->fsize = NULL_POSITION;
673: ch_flags = flags;
674: /*
675: * Try to seek; set CH_CANSEEK if it works.
676: */
677: if (seekable(f))
678: ch_flags |= CH_CANSEEK;
679: set_filestate(curr_ifile, (void *) thisfile);
680: }
681: if (thisfile->file == -1)
682: thisfile->file = f;
683: ch_flush();
684: }
685:
686: /*
687: * Close a filestate.
688: */
689: public void
690: ch_close()
691: {
692: int keepstate = FALSE;
693:
694: if (ch_flags & (CH_CANSEEK|CH_POPENED))
695: {
696: /*
697: * We can seek or re-open, so we don't need to keep buffers.
698: */
699: ch_delbufs();
700: } else
701: keepstate = TRUE;
702: if (!(ch_flags & CH_KEEPOPEN))
703: {
704: /*
705: * We don't need to keep the file descriptor open
706: * (because we can re-open it.)
707: * But don't really close it if it was opened via popen(),
708: * because pclose() wants to close it.
709: */
710: if (!(ch_flags & CH_POPENED))
711: close(ch_file);
712: ch_file = -1;
713: } else
714: keepstate = TRUE;
715: if (!keepstate)
716: {
717: /*
718: * We don't even need to keep the filestate structure.
719: */
720: free(thisfile);
721: thisfile = NULL;
722: set_filestate(curr_ifile, (void *) NULL);
723: }
724: }
725:
726: /*
727: * Return ch_flags for the current file.
728: */
729: public int
730: ch_getflags()
731: {
732: return (ch_flags);
733: }
734:
735: #if 0
736: public void
737: ch_dump(struct filestate *fs)
738: {
739: struct buf *bp;
740: unsigned char *s;
741:
742: if (fs == NULL)
743: {
744: printf(" --no filestate\n");
745: return;
746: }
747: printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
748: fs->file, fs->flags, fs->fpos,
749: fs->fsize, fs->block, fs->offset);
750: printf(" %d bufs:\n", fs->nbufs);
751: for (bp = fs->buf_next; bp != (struct buf *)fs; bp = bp->next)
752: {
753: printf("%x: blk %x, size %x \"",
754: bp, bp->block, bp->datasize);
755: for (s = bp->data; s < bp->data + 30; s++)
756: if (*s >= ' ' && *s < 0x7F)
757: printf("%c", *s);
758: else
759: printf(".");
760: printf("\"\n");
761: }
762: }
763: #endif