Commit | Line | Data |
---|---|---|
bfe13c81 KB |
1 | /* |
2 | * Copyright (c) 1988 Mark Nudleman | |
3 | * Copyright (c) 1988 Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
f15db449 | 6 | * %sccs.include.redist.c% |
bfe13c81 KB |
7 | */ |
8 | ||
9 | #ifndef lint | |
f15db449 | 10 | static char sccsid[] = "@(#)ch.c 5.8 (Berkeley) %G%"; |
bfe13c81 KB |
11 | #endif /* not lint */ |
12 | ||
13 | /* | |
14 | * Low level character input from the input file. | |
15 | * We use these special purpose routines which optimize moving | |
16 | * both forward and backward from the current read pointer. | |
17 | */ | |
18 | ||
bc258617 KB |
19 | #include <sys/types.h> |
20 | #include <sys/file.h> | |
21 | #include <stdio.h> | |
22 | #include <less.h> | |
bfe13c81 | 23 | |
bc258617 | 24 | int file = -1; /* File descriptor of the input file */ |
bfe13c81 KB |
25 | |
26 | /* | |
27 | * Pool of buffers holding the most recently used blocks of the input file. | |
28 | */ | |
bc258617 | 29 | #define BUFSIZ 1024 |
bfe13c81 KB |
30 | struct buf { |
31 | struct buf *next, *prev; | |
32 | long block; | |
33 | int datasize; | |
34 | char data[BUFSIZ]; | |
35 | }; | |
bc258617 | 36 | int nbufs; |
bfe13c81 KB |
37 | |
38 | /* | |
bc258617 KB |
39 | * The buffer pool is kept as a doubly-linked circular list, in order from |
40 | * most- to least-recently used. The circular list is anchored by buf_anchor. | |
bfe13c81 KB |
41 | */ |
42 | #define END_OF_CHAIN ((struct buf *)&buf_anchor) | |
43 | #define buf_head buf_anchor.next | |
44 | #define buf_tail buf_anchor.prev | |
45 | ||
46 | static struct { | |
47 | struct buf *next, *prev; | |
48 | } buf_anchor = { END_OF_CHAIN, END_OF_CHAIN }; | |
49 | ||
bc258617 | 50 | extern int ispipe, cbufs, sigs; |
bfe13c81 KB |
51 | |
52 | /* | |
53 | * Current position in file. | |
54 | * Stored as a block number and an offset into the block. | |
55 | */ | |
56 | static long ch_block; | |
57 | static int ch_offset; | |
58 | ||
bc258617 KB |
59 | /* Length of file, needed if input is a pipe. */ |
60 | static off_t ch_fsize; | |
bfe13c81 | 61 | |
bc258617 KB |
62 | /* Number of bytes read, if input is standard input (a pipe). */ |
63 | static off_t last_piped_pos; | |
bfe13c81 KB |
64 | |
65 | /* | |
bc258617 KB |
66 | * Get the character pointed to by the read pointer. ch_get() is a macro |
67 | * which is more efficient to call than fch_get (the function), in the usual | |
68 | * case that the block desired is at the head of the chain. | |
bfe13c81 | 69 | */ |
bc258617 KB |
70 | #define ch_get() \ |
71 | ((buf_head->block == ch_block && \ | |
72 | ch_offset < buf_head->datasize) ? \ | |
73 | buf_head->data[ch_offset] : fch_get()) | |
74 | ||
75 | static | |
bfe13c81 KB |
76 | fch_get() |
77 | { | |
dacf700d | 78 | extern int bs_mode; |
bfe13c81 | 79 | register struct buf *bp; |
dacf700d KB |
80 | register int n, ch; |
81 | register char *p, *t; | |
bc258617 | 82 | off_t pos, lseek(); |
bfe13c81 | 83 | |
bc258617 | 84 | /* look for a buffer holding the desired block. */ |
bfe13c81 | 85 | for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) |
bc258617 | 86 | if (bp->block == ch_block) { |
bfe13c81 KB |
87 | if (ch_offset >= bp->datasize) |
88 | /* | |
89 | * Need more data in this buffer. | |
90 | */ | |
91 | goto read_more; | |
92 | /* | |
93 | * On a pipe, we don't sort the buffers LRU | |
94 | * because this can cause gaps in the buffers. | |
95 | * For example, suppose we've got 12 1K buffers, | |
96 | * and a 15K input stream. If we read the first 12K | |
97 | * sequentially, then jump to line 1, then jump to | |
98 | * the end, the buffers have blocks 0,4,5,6,..,14. | |
99 | * If we then jump to line 1 again and try to | |
100 | * read sequentially, we're out of luck when we | |
101 | * get to block 1 (we'd get the "pipe error" below). | |
102 | * To avoid this, we only sort buffers on a pipe | |
103 | * when we actually READ the data, not when we | |
104 | * find it already buffered. | |
105 | */ | |
106 | if (ispipe) | |
bc258617 | 107 | return(bp->data[ch_offset]); |
bfe13c81 KB |
108 | goto found; |
109 | } | |
110 | /* | |
bc258617 KB |
111 | * Block is not in a buffer. Take the least recently used buffer |
112 | * and read the desired block into it. If the LRU buffer has data | |
113 | * in it, and input is a pipe, then try to allocate a new buffer first. | |
bfe13c81 | 114 | */ |
bc258617 KB |
115 | if (ispipe && buf_tail->block != (long)(-1)) |
116 | (void)ch_addbuf(1); | |
bfe13c81 KB |
117 | bp = buf_tail; |
118 | bp->block = ch_block; | |
119 | bp->datasize = 0; | |
120 | ||
bc258617 | 121 | read_more: |
bfe13c81 | 122 | pos = (ch_block * BUFSIZ) + bp->datasize; |
bc258617 | 123 | if (ispipe) { |
bfe13c81 KB |
124 | /* |
125 | * The data requested should be immediately after | |
126 | * the last data read from the pipe. | |
127 | */ | |
bc258617 | 128 | if (pos != last_piped_pos) { |
bfe13c81 KB |
129 | error("pipe error"); |
130 | quit(); | |
131 | } | |
132 | } else | |
083e784b | 133 | (void)lseek(file, pos, L_SET); |
bfe13c81 KB |
134 | |
135 | /* | |
136 | * Read the block. | |
137 | * If we read less than a full block, we just return the | |
138 | * partial block and pick up the rest next time. | |
139 | */ | |
140 | n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize); | |
141 | if (n == READ_INTR) | |
142 | return (EOI); | |
bc258617 | 143 | if (n < 0) { |
bfe13c81 KB |
144 | error("read error"); |
145 | quit(); | |
146 | } | |
147 | if (ispipe) | |
148 | last_piped_pos += n; | |
149 | ||
dacf700d | 150 | p = &bp->data[bp->datasize]; |
bfe13c81 KB |
151 | bp->datasize += n; |
152 | ||
153 | /* | |
bc258617 KB |
154 | * Set an EOI marker in the buffered data itself. Then ensure the |
155 | * data is "clean": there are no extra EOI chars in the data and | |
dacf700d KB |
156 | * that the "meta" bit (the 0200 bit) is reset in each char; |
157 | * also translate \r\n sequences to \n if -u flag not set. | |
bfe13c81 | 158 | */ |
bc258617 | 159 | if (n == 0) { |
bfe13c81 KB |
160 | ch_fsize = pos; |
161 | bp->data[bp->datasize++] = EOI; | |
162 | } | |
163 | ||
dacf700d KB |
164 | if (bs_mode) { |
165 | for (p = &bp->data[bp->datasize]; --n >= 0;) { | |
166 | *--p &= 0177; | |
167 | if (*p == EOI) | |
168 | *p = 0200; | |
169 | } | |
170 | } | |
171 | else { | |
172 | for (t = p; --n >= 0; ++p) { | |
173 | ch = *p & 0177; | |
174 | if (ch == '\r' && n && (p[1] & 0177) == '\n') { | |
175 | ++p; | |
176 | *t++ = '\n'; | |
177 | } | |
178 | else | |
179 | *t++ = (ch == EOI) ? 0200 : ch; | |
180 | } | |
181 | if (p != t) { | |
182 | bp->datasize -= p - t; | |
183 | if (ispipe) | |
184 | last_piped_pos -= p - t; | |
185 | } | |
bfe13c81 KB |
186 | } |
187 | ||
bc258617 KB |
188 | found: |
189 | if (buf_head != bp) { | |
bfe13c81 KB |
190 | /* |
191 | * Move the buffer to the head of the buffer chain. | |
192 | * This orders the buffer chain, most- to least-recently used. | |
193 | */ | |
194 | bp->next->prev = bp->prev; | |
195 | bp->prev->next = bp->next; | |
196 | ||
197 | bp->next = buf_head; | |
198 | bp->prev = END_OF_CHAIN; | |
199 | buf_head->prev = bp; | |
200 | buf_head = bp; | |
201 | } | |
202 | ||
203 | if (ch_offset >= bp->datasize) | |
204 | /* | |
205 | * After all that, we still don't have enough data. | |
206 | * Go back and try again. | |
207 | */ | |
208 | goto read_more; | |
209 | ||
bc258617 | 210 | return(bp->data[ch_offset]); |
bfe13c81 KB |
211 | } |
212 | ||
bfe13c81 KB |
213 | /* |
214 | * Determine if a specific block is currently in one of the buffers. | |
215 | */ | |
bc258617 | 216 | static |
bfe13c81 KB |
217 | buffered(block) |
218 | long block; | |
219 | { | |
220 | register struct buf *bp; | |
221 | ||
bc258617 | 222 | for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) |
bfe13c81 | 223 | if (bp->block == block) |
bc258617 KB |
224 | return(1); |
225 | return(0); | |
bfe13c81 KB |
226 | } |
227 | ||
228 | /* | |
229 | * Seek to a specified position in the file. | |
230 | * Return 0 if successful, non-zero if can't seek there. | |
231 | */ | |
bfe13c81 | 232 | ch_seek(pos) |
bc258617 | 233 | register off_t pos; |
bfe13c81 KB |
234 | { |
235 | long new_block; | |
236 | ||
237 | new_block = pos / BUFSIZ; | |
bc258617 | 238 | if (!ispipe || pos == last_piped_pos || buffered(new_block)) { |
bfe13c81 KB |
239 | /* |
240 | * Set read pointer. | |
241 | */ | |
242 | ch_block = new_block; | |
243 | ch_offset = pos % BUFSIZ; | |
bc258617 | 244 | return(0); |
bfe13c81 | 245 | } |
bc258617 | 246 | return(1); |
bfe13c81 KB |
247 | } |
248 | ||
249 | /* | |
250 | * Seek to the end of the file. | |
251 | */ | |
bfe13c81 KB |
252 | ch_end_seek() |
253 | { | |
bc258617 KB |
254 | off_t ch_length(); |
255 | ||
bfe13c81 | 256 | if (!ispipe) |
bc258617 | 257 | return(ch_seek(ch_length())); |
bfe13c81 KB |
258 | |
259 | /* | |
260 | * Do it the slow way: read till end of data. | |
261 | */ | |
262 | while (ch_forw_get() != EOI) | |
263 | if (sigs) | |
bc258617 KB |
264 | return(1); |
265 | return(0); | |
bfe13c81 KB |
266 | } |
267 | ||
268 | /* | |
269 | * Seek to the beginning of the file, or as close to it as we can get. | |
270 | * We may not be able to seek there if input is a pipe and the | |
271 | * beginning of the pipe is no longer buffered. | |
272 | */ | |
bfe13c81 KB |
273 | ch_beg_seek() |
274 | { | |
275 | register struct buf *bp, *firstbp; | |
276 | ||
277 | /* | |
278 | * Try a plain ch_seek first. | |
279 | */ | |
bc258617 KB |
280 | if (ch_seek((off_t)0) == 0) |
281 | return(0); | |
bfe13c81 KB |
282 | |
283 | /* | |
284 | * Can't get to position 0. | |
285 | * Look thru the buffers for the one closest to position 0. | |
286 | */ | |
287 | firstbp = bp = buf_head; | |
288 | if (bp == END_OF_CHAIN) | |
bc258617 | 289 | return(1); |
bfe13c81 KB |
290 | while ((bp = bp->next) != END_OF_CHAIN) |
291 | if (bp->block < firstbp->block) | |
292 | firstbp = bp; | |
293 | ch_block = firstbp->block; | |
294 | ch_offset = 0; | |
bc258617 | 295 | return(0); |
bfe13c81 KB |
296 | } |
297 | ||
298 | /* | |
299 | * Return the length of the file, if known. | |
300 | */ | |
bc258617 | 301 | off_t |
bfe13c81 KB |
302 | ch_length() |
303 | { | |
bc258617 KB |
304 | off_t lseek(); |
305 | ||
bfe13c81 | 306 | if (ispipe) |
bc258617 KB |
307 | return(ch_fsize); |
308 | return((off_t)(lseek(file, (off_t)0, L_XTND))); | |
bfe13c81 KB |
309 | } |
310 | ||
311 | /* | |
312 | * Return the current position in the file. | |
313 | */ | |
bc258617 | 314 | off_t |
bfe13c81 KB |
315 | ch_tell() |
316 | { | |
bc258617 | 317 | return(ch_block * BUFSIZ + ch_offset); |
bfe13c81 KB |
318 | } |
319 | ||
320 | /* | |
321 | * Get the current char and post-increment the read pointer. | |
322 | */ | |
bfe13c81 KB |
323 | ch_forw_get() |
324 | { | |
325 | register int c; | |
326 | ||
327 | c = ch_get(); | |
bc258617 | 328 | if (c != EOI && ++ch_offset >= BUFSIZ) { |
bfe13c81 | 329 | ch_offset = 0; |
bc258617 | 330 | ++ch_block; |
bfe13c81 | 331 | } |
bc258617 | 332 | return(c); |
bfe13c81 KB |
333 | } |
334 | ||
335 | /* | |
336 | * Pre-decrement the read pointer and get the new current char. | |
337 | */ | |
bfe13c81 KB |
338 | ch_back_get() |
339 | { | |
bc258617 KB |
340 | if (--ch_offset < 0) { |
341 | if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) { | |
bfe13c81 | 342 | ch_offset = 0; |
bc258617 | 343 | return(EOI); |
bfe13c81 KB |
344 | } |
345 | ch_offset = BUFSIZ - 1; | |
346 | ch_block--; | |
347 | } | |
bc258617 | 348 | return(ch_get()); |
bfe13c81 KB |
349 | } |
350 | ||
351 | /* | |
352 | * Allocate buffers. | |
353 | * Caller wants us to have a total of at least want_nbufs buffers. | |
354 | * keep==1 means keep the data in the current buffers; | |
355 | * otherwise discard the old data. | |
356 | */ | |
bfe13c81 KB |
357 | ch_init(want_nbufs, keep) |
358 | int want_nbufs; | |
359 | int keep; | |
360 | { | |
361 | register struct buf *bp; | |
362 | char message[80]; | |
363 | ||
364 | cbufs = nbufs; | |
bc258617 | 365 | if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs)) { |
bfe13c81 KB |
366 | /* |
367 | * Cannot allocate enough buffers. | |
368 | * If we don't have ANY, then quit. | |
369 | * Otherwise, just report the error and return. | |
370 | */ | |
083e784b | 371 | (void)sprintf(message, "cannot allocate %d buffers", |
bc258617 | 372 | want_nbufs - nbufs); |
bfe13c81 KB |
373 | error(message); |
374 | if (nbufs == 0) | |
375 | quit(); | |
376 | return; | |
377 | } | |
378 | ||
379 | if (keep) | |
380 | return; | |
381 | ||
382 | /* | |
383 | * We don't want to keep the old data, | |
384 | * so initialize all the buffers now. | |
385 | */ | |
386 | for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) | |
387 | bp->block = (long)(-1); | |
bc258617 | 388 | last_piped_pos = (off_t)0; |
bfe13c81 | 389 | ch_fsize = NULL_POSITION; |
bc258617 | 390 | (void)ch_seek((off_t)0); |
bfe13c81 KB |
391 | } |
392 | ||
393 | /* | |
394 | * Allocate some new buffers. | |
395 | * The buffers are added to the tail of the buffer chain. | |
396 | */ | |
bc258617 | 397 | static |
bfe13c81 KB |
398 | ch_addbuf(nnew) |
399 | int nnew; | |
400 | { | |
401 | register struct buf *bp; | |
402 | register struct buf *newbufs; | |
bc258617 | 403 | char *calloc(); |
bfe13c81 KB |
404 | |
405 | /* | |
406 | * We don't have enough buffers. | |
407 | * Allocate some new ones. | |
408 | */ | |
bc258617 | 409 | newbufs = (struct buf *)calloc((u_int)nnew, sizeof(struct buf)); |
bfe13c81 | 410 | if (newbufs == NULL) |
bc258617 | 411 | return(1); |
bfe13c81 KB |
412 | |
413 | /* | |
414 | * Initialize the new buffers and link them together. | |
415 | * Link them all onto the tail of the buffer list. | |
416 | */ | |
417 | nbufs += nnew; | |
418 | cbufs = nbufs; | |
bc258617 | 419 | for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++) { |
bfe13c81 KB |
420 | bp->next = bp + 1; |
421 | bp->prev = bp - 1; | |
422 | bp->block = (long)(-1); | |
423 | } | |
424 | newbufs[nnew-1].next = END_OF_CHAIN; | |
425 | newbufs[0].prev = buf_tail; | |
426 | buf_tail->next = &newbufs[0]; | |
427 | buf_tail = &newbufs[nnew-1]; | |
bc258617 | 428 | return(0); |
bfe13c81 | 429 | } |