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:
11: /*
12: * Pool of buffers holding the most recently used blocks of the input file.
13: */
14: #define BUFSIZ 1024
15: static struct buf {
16: struct buf *next, *prev;
17: long block;
18: char data[BUFSIZ];
19: };
20: static struct buf *bufs = NULL;
21: public int nbufs;
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 buf_anchor.
27: */
28: static struct {
29: struct buf *next, *prev;
30: } buf_anchor;
31: #define END_OF_CHAIN ((struct buf *)&buf_anchor)
32: #define buf_head buf_anchor.next
33: #define buf_tail buf_anchor.prev
34:
35: /*
36: * If we fail to allocate enough memory for buffers, we try to limp
37: * along with a minimum number of buffers.
38: */
39: #define DEF_NBUFS 2 /* Minimum number of buffers */
40:
41: extern int clean_data;
42: extern int ispipe;
43:
44: /*
45: * Current position in file.
46: * Stored as a block number and an offset into the block.
47: */
48: static long ch_block;
49: static int ch_offset;
50:
51: /*
52: * Length of file, needed if input is a pipe.
53: */
54: static POSITION ch_fsize;
55:
56: /*
57: * Largest block number read if input is standard input (a pipe).
58: */
59: static long last_piped_block;
60:
61: /*
62: * Get the character pointed to by the read pointer.
63: * ch_get() is a macro which is more efficient to call
64: * than fch_get (the function), in the usual case
65: * that the block desired is at the head of the chain.
66: */
67: #define ch_get() ((buf_head->block == ch_block) ? \
68: buf_head->data[ch_offset] : fch_get())
69: static int
70: fch_get()
71: {
72: register struct buf *bp;
73: register int n;
74: register int end;
75: POSITION pos;
76:
77: /*
78: * Look for a buffer holding the desired block.
79: */
80: for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
81: if (bp->block == ch_block)
82: goto found;
83: /*
84: * Block is not in a buffer.
85: * Take the least recently used buffer
86: * and read the desired block into it.
87: */
88: bp = buf_tail;
89: bp->block = ch_block;
90: pos = ch_block * BUFSIZ;
91: if (ispipe)
92: {
93: /*
94: * The block requested should be one more than
95: * the last block read.
96: */
97: if (ch_block != ++last_piped_block)
98: {
99: /* This "should not happen". */
100: char message[80];
101: sprintf(message, "Pipe error: last %ld, want %ld\n",
102: last_piped_block-1, ch_block);
103: error(message);
104: quit();
105: }
106: } else
107: lseek(file, pos, 0);
108:
109: /*
110: * Read the block. This may take several reads if the input
111: * is coming from standard input, due to the nature of pipes.
112: */
113: end = 0;
114: while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
115: if ((end += n) >= BUFSIZ)
116: break;
117:
118: if (n < 0)
119: {
120: error("read error");
121: quit();
122: }
123:
124: /*
125: * Set an EOF marker in the buffered data itself.
126: * Then ensure the data is "clean": there are no
127: * extra EOF chars in the data and that the "meta"
128: * bit (the 0200 bit) is reset in each char.
129: */
130: if (end < BUFSIZ)
131: {
132: ch_fsize = pos + end;
133: bp->data[end] = EOF;
134: }
135:
136: if (!clean_data)
137: while (--end >= 0)
138: {
139: bp->data[end] &= 0177;
140: if (bp->data[end] == EOF)
141: bp->data[end] = '@';
142: }
143:
144: found:
145: /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
146: {
147: /*
148: * Move the buffer to the head of the buffer chain.
149: * This orders the buffer chain, most- to least-recently used.
150: */
151: bp->next->prev = bp->prev;
152: bp->prev->next = bp->next;
153:
154: bp->next = buf_head;
155: bp->prev = END_OF_CHAIN;
156: buf_head->prev = bp;
157: buf_head = bp;
158: }
159: return (bp->data[ch_offset]);
160: }
161:
162: /*
163: * Determine if a specific block is currently in one of the buffers.
164: */
165: static int
166: buffered(block)
167: long block;
168: {
169: register struct buf *bp;
170:
171: for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
172: if (bp->block == block)
173: return (1);
174: return (0);
175: }
176:
177: /*
178: * Seek to a specified position in the file.
179: * Return 0 if successful, non-zero if can't seek there.
180: */
181: public int
182: ch_seek(pos)
183: register POSITION pos;
184: {
185: long new_block;
186:
187: new_block = pos / BUFSIZ;
188: if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
189: {
190: /*
191: * Set read pointer.
192: */
193: ch_block = new_block;
194: ch_offset = pos % BUFSIZ;
195: return (0);
196: }
197: return (1);
198: }
199:
200: /*
201: * Seek to the end of the file.
202: */
203: public int
204: ch_end_seek()
205: {
206: if (ispipe)
207: {
208: /*
209: * Do it the slow way: read till end of data.
210: */
211: while (ch_forw_get() != EOF)
212: ;
213: } else
214: {
215: (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2)));
216: }
217: return (0);
218: }
219:
220: /*
221: * Return the length of the file, if known.
222: */
223: public POSITION
224: ch_length()
225: {
226: if (ispipe)
227: return (ch_fsize);
228: return ((POSITION)(lseek(file, (off_t)0, 2)));
229: }
230:
231: /*
232: * Return the current position in the file.
233: */
234: public POSITION
235: ch_tell()
236: {
237: return (ch_block * BUFSIZ + ch_offset);
238: }
239:
240: /*
241: * Get the current char and post-increment the read pointer.
242: */
243: public int
244: ch_forw_get()
245: {
246: register int c;
247:
248: c = ch_get();
249: if (c != EOF && ++ch_offset >= BUFSIZ)
250: {
251: ch_offset = 0;
252: ch_block ++;
253: }
254: return (c);
255: }
256:
257: /*
258: * Pre-decrement the read pointer and get the new current char.
259: */
260: public int
261: ch_back_get()
262: {
263: register int c;
264:
265: if (--ch_offset < 0)
266: {
267: if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
268: {
269: ch_offset = 0;
270: return (EOF);
271: }
272: ch_offset = BUFSIZ - 1;
273: ch_block--;
274: }
275: c = ch_get();
276: return (c);
277: }
278:
279: /*
280: * Initialize the buffer pool to all empty.
281: * Caller suggests that we use want_nbufs buffers.
282: */
283: public void
284: ch_init(want_nbufs)
285: int want_nbufs;
286: {
287: register struct buf *bp;
288: char *calloc();
289:
290: if (nbufs < want_nbufs)
291: {
292: /*
293: * We don't have enough buffers.
294: * Free what we have (if any) and allocate some new ones.
295: */
296: if (bufs != NULL)
297: free((char *)bufs);
298: bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
299: nbufs = want_nbufs;
300: if (bufs == NULL)
301: {
302: /*
303: * Couldn't get that many.
304: * Try for a small default number of buffers.
305: */
306: char message[80];
307: sprintf(message,
308: "Cannot allocate %d buffers. Using %d buffers.",
309: nbufs, DEF_NBUFS);
310: error(message);
311: bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
312: nbufs = DEF_NBUFS;
313: if (bufs == NULL)
314: {
315: /*
316: * Couldn't even get the smaller number of bufs.
317: * Something is wrong here, don't continue.
318: */
319: sprintf(message,
320: "Cannot even allocate %d buffers! Quitting.\n",
321: DEF_NBUFS);
322: error(message);
323: quit();
324: /*NOTREACHED*/
325: }
326: }
327: }
328:
329: /*
330: * Initialize the buffers to empty.
331: * Set up the circular list.
332: */
333: for (bp = &bufs[0]; bp < &bufs[nbufs]; bp++)
334: {
335: bp->next = bp + 1;
336: bp->prev = bp - 1;
337: bp->block = (long)(-1);
338: }
339: bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
340: buf_head = &bufs[0];
341: buf_tail = &bufs[nbufs-1];
342: last_piped_block = -1;
343: ch_fsize = NULL_POSITION;
344: (void) ch_seek((POSITION)0);
345: }
Defined functions
Defined variables
bufs
defined in line
20; used 12 times
file
defined in line
9; used 10 times
nbufs
defined in line
21; used 9 times
Defined struct's
buf
defined in line
15; used 20 times
Defined macros
BUFSIZ
defined in line
14; used 10 times