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