main
getline.c
1/* Copyright (c) 1985 Ceriel J.H. Jacobs */
2
3#include "in_all.h"
4#include "getline.h"
5#include "options.h"
6#include "process.h"
7#include "term.h"
8#include "main.h"
9#include "display.h"
10#include "output.h"
11#include "prompt.h"
12#include "assert.h"
13
14#define BLOCKSIZE 2048 /* size of blocks */
15#define CHUNK 50 /* # of blockheaders allocated at a time */
16
17/*
18 * Blocks are kept in an array in line-number order. Each block stores the raw
19 * text and the offsets of the lines parsed from it.
20 */
21
22struct block {
23 int b_flags; /* Contains the following flags: */
24#define PARTLY 02 /* block not filled completely (eof) */
25 long b_end; /* line number of last line in block */
26 char *b_info; /* the block */
27 int *b_offs; /* line offsets within the block */
28};
29
30static struct block *blocklist, /* beginning of the list of blocks */
31 *maxblocklist, /* first free entry in the list */
32 *topblocklist; /* end of allocated core for the list */
33static long lastreadline; /* lineno of last line read */
34static int ENDseen;
35
36static void nextblock(struct block *pblock);
37static char *re_alloc(char *ptr, unsigned newsize);
38static struct block *getblock(long n, int disable_interrupt);
39
40static struct block *
41new_block()
42{
43 struct block *pblock = maxblocklist - 1;
44
45 if (!maxblocklist || !(pblock->b_flags & PARTLY)) {
46 /*
47 * There is no last block, or it was filled completely,
48 * so allocate a new blockheader.
49 */
50 int siz;
51
52 pblock = blocklist;
53 if (maxblocklist == topblocklist) {
54 /*
55 * No blockheaders left. Allocate new ones
56 */
57 siz = topblocklist - pblock;
58 blocklist = pblock = (struct block *)
59 re_alloc((char *)pblock,
60 (unsigned)((siz + CHUNK) * sizeof(*pblock)));
61 pblock += siz;
62 topblocklist = pblock + CHUNK;
63 maxblocklist = pblock;
64 for (; pblock < topblocklist; pblock++) {
65 pblock->b_end = 0;
66 pblock->b_info = 0;
67 pblock->b_flags = 0;
68 }
69 if (!siz) {
70 /*
71 * Create dummy header cell.
72 */
73 maxblocklist++;
74 }
75 }
76 pblock = maxblocklist++;
77 }
78 nextblock(pblock);
79 return pblock;
80}
81
82/*
83 * Return the block in which line 'n' of the current file can be found.
84 * If "disable_interrupt" = 0, the call may be interrupted, in which
85 * case it returns 0.
86 */
87
88static struct block *
89getblock(long n, int disable_interrupt)
90{
91 struct block *pblock;
92
93 if (stdf < 0) {
94 /*
95 * Not file descriptor, so return end of file
96 */
97 return 0;
98 }
99 pblock = maxblocklist - 1;
100 if (n < lastreadline ||
101 (n == lastreadline && !(pblock->b_flags & PARTLY))) {
102 /*
103 * The line asked for has been read already.
104 * Perform binary search in the blocklist to find the block
105 * where it's in.
106 */
107 struct block *min, *mid;
108
109 min = blocklist + 1;
110 do {
111 mid = min + (pblock - min) / 2;
112 if (n > mid->b_end) {
113 min = mid + 1;
114 } else
115 pblock = mid;
116 } while (min < pblock);
117 /* Found, pblock is now a reference to the block wanted */
118 return pblock;
119 }
120
121 /*
122 * The line was'nt read yet, so read blocks until found
123 */
124 for (;;) {
125 if (interrupt && !disable_interrupt)
126 return 0;
127 pblock = new_block();
128 if (pblock->b_end >= n) {
129 return pblock;
130 }
131 if (pblock->b_flags & PARTLY) {
132 /*
133 * We did not find it, and the last block could not be
134 * read completely, so return 0;
135 */
136 return 0;
137 }
138 }
139 /* NOTREACHED */
140}
141
142char *
143getline(long n, int disable_interrupt)
144{
145 struct block *pblock;
146
147 if (!(pblock = getblock(n, disable_interrupt))) {
148 return (char *)0;
149 }
150 return pblock->b_info + pblock->b_offs[n - ((pblock - 1)->b_end + 1)];
151}
152
153/*
154 * Find the last line of the input, and return its number
155 */
156
157long
158to_lastline()
159{
160
161 for (;;) {
162 if (!getline(lastreadline + 1, 0)) {
163 /*
164 * "lastreadline" always contains the linenumber of
165 * the last line read. So, if the call to getline
166 * succeeds, "lastreadline" is affected
167 */
168 if (interrupt)
169 return -1L;
170 return lastreadline;
171 }
172 }
173 /* NOTREACHED */
174}
175
176char *
177alloc(unsigned size)
178{
179 char *pmem;
180
181 pmem = malloc(size);
182 if (!pmem && size != 0) {
183 panic("No core");
184 }
185 return pmem;
186}
187
188/*
189 * Re-allocate the memorychunk pointed to by ptr, to let it
190 * grow or shrink.
191 */
192
193static char *
194re_alloc(char *ptr, unsigned newsize)
195{
196 char *pmem;
197
198 pmem = realloc(ptr, newsize);
199 if (!pmem && newsize != 0) {
200 panic("No core");
201 }
202 return pmem;
203}
204
205static char *saved;
206static long filldegree;
207
208/*
209 * Try to read the block indicated by pblock
210 */
211
212static void
213nextblock(struct block *pblock)
214{
215 char *c, /* Run through pblock->b_info */
216 *c1; /* indicate end of pblock->b_info */
217 int *poff; /* pointer in line-offset list */
218 int cnt; /* # of characters read */
219 unsigned siz; /* Size of allocated line-offset list */
220 static unsigned savedsiz; /* saved "siz" */
221 static int *savedpoff; /* saved "poff" */
222 static char *savedc1; /* saved "c1" */
223
224 if (pblock->b_flags & PARTLY) {
225 /*
226 * The block was already partly filled. Initialize locals
227 * accordingly
228 */
229 poff = savedpoff;
230 siz = savedsiz;
231 pblock->b_flags = 0;
232 c1 = savedc1;
233 if (c1 == pblock->b_info || *(c1 - 1)) {
234 /*
235 * We had incremented "lastreadline" temporarily,
236 * because the last line could not be completely read
237 * last time we tried. Undo this increment
238 */
239 poff--;
240 --lastreadline;
241 }
242 } else {
243 if (saved) {
244 /*
245 * There were leftovers from the previous block
246 */
247 pblock->b_info = saved;
248 c1 = savedc1;
249 saved = 0;
250 } else { /* Allocate new block */
251 pblock->b_info = c1 = alloc(BLOCKSIZE + 1);
252 }
253 /*
254 * Allocate some space for line-offsets
255 */
256 pblock->b_offs = poff = (int *)
257 alloc((unsigned)(100 * sizeof(int)));
258 siz = 99;
259 *poff++ = 0;
260 }
261 c = c1;
262 for (;;) {
263 /*
264 * Read loop
265 */
266 cnt = read(stdf, c1, BLOCKSIZE - (c1 - pblock->b_info));
267 if (cnt < 0) {
268 /*
269 * Interrupted read
270 */
271 if (errno == EINTR)
272 continue;
273 error("Could not read input file");
274 cnt = 0;
275 }
276 c1 += cnt;
277 if (c1 != pblock->b_info + BLOCKSIZE) {
278 ENDseen = 1;
279 pblock->b_flags |= PARTLY;
280 }
281 break;
282 }
283 assert(c <= c1);
284 while (c < c1) {
285 /*
286 * Now process the block
287 */
288 if (*c == '\n') {
289 /*
290 * Newlines are replaced by '\0', so that "getline"
291 * can deliver one line at a time
292 */
293 *c = 0;
294 lastreadline++;
295 /*
296 * Remember the line-offset
297 */
298 if (poff == pblock->b_offs + siz) {
299 /*
300 * No space for it, allocate some more
301 */
302 pblock->b_offs = (int *)
303 re_alloc((char *)pblock->b_offs,
304 (unsigned)((siz + 51) * sizeof(int)));
305 poff = pblock->b_offs + siz;
306 siz += 50;
307 }
308 *poff++ = c - pblock->b_info + 1;
309 }
310 c++;
311 }
312 assert(c == c1);
313 *c = 0;
314 if (c != pblock->b_info && *(c - 1) != 0) {
315 /*
316 * The last line read does not end with a newline, so add one
317 */
318 lastreadline++;
319 *poff++ = c - pblock->b_info + 1;
320 if (!(pblock->b_flags & PARTLY) && *(poff - 2) != 0) {
321 /*
322 * Save the started line; it will be in the next block.
323 * Remove the newline we added just now.
324 */
325 saved = c1 = alloc(BLOCKSIZE + 1);
326 c = pblock->b_info + *(--poff - 1);
327 while (*c)
328 *c1++ = *c++;
329 c = pblock->b_info + *(poff - 1);
330 savedc1 = c1;
331 --lastreadline;
332 }
333 }
334 pblock->b_end = lastreadline;
335 if (pblock->b_flags & PARTLY) {
336 /*
337 * Take care, that we can call "nextblock" again, to fill in
338 * the rest of this block
339 */
340 savedsiz = siz;
341 savedpoff = poff;
342 savedc1 = c;
343 if (c == pblock->b_info) {
344 lastreadline++;
345 pblock->b_end = 0;
346 }
347 } else {
348 cnt = pblock - blocklist;
349 filldegree = ((c - pblock->b_info) + (cnt - 1) * filldegree) / cnt;
350 }
351 assert(pblock->b_end - (pblock - 1)->b_end <= poff - pblock->b_offs);
352}
353
354/*
355 * Called after processing a file.
356 * Free all core.
357 */
358
359void
360do_clean()
361{
362
363 struct block *pblock;
364 char *p;
365
366 for (pblock = blocklist; pblock < maxblocklist; pblock++) {
367 if ((p = pblock->b_info) != 0) {
368 free(p);
369 free((char *)pblock->b_offs);
370 }
371 }
372 if ((p = (char *)blocklist) != 0) {
373 free(p);
374 }
375 blocklist = 0;
376 maxblocklist = 0;
377 topblocklist = 0;
378 lastreadline = 0;
379 filldegree = 0;
380 ENDseen = 0;
381 if ((p = saved) != 0)
382 free(p);
383 saved = 0;
384}
385
386/*
387 * Get a character. If possible, do some workahead.
388 */
389
390int
391getch()
392{
393 int flags, bytes_ready, bytes_read;
394 struct stat buf;
395 char c;
396
397 flush();
398 if (startcomm) {
399 /*
400 * Command line option command
401 */
402 if (*startcomm)
403 return *startcomm++;
404 return '\n';
405 }
406 if (stdf >= 0) {
407 /*
408 * Make reads from the terminal non-blocking, so that
409 * we can see if the user typed something
410 */
411 flags = fcntl(0, F_GETFL, 0);
412 if (flags != -1 && fcntl(0, F_SETFL, flags | O_NONBLOCK) != -1) {
413 bytes_read = 0;
414 while (!ENDseen &&
415 ((bytes_read = read(0, &c, 1)) == 0
416#ifdef EWOULDBLOCK
417 || (bytes_read < 0 && errno == EWOULDBLOCK)
418#endif
419#ifdef EAGAIN
420 || (bytes_read < 0 && errno == EAGAIN)
421#endif
422 ) &&
423 (nopipe ||
424 (fstat(stdf, &buf) >= 0 && buf.st_size > 0))) {
425 /*
426 * Do some read ahead, after making sure there
427 * is input and the user did not type a command
428 */
429 new_block();
430 }
431 (void)fcntl(0, F_SETFL, flags);
432 if (bytes_read < 0) {
433 /*
434 * Could this have happened?
435 * I'm not sure, because the read is
436 * nonblocking. Can it be interrupted then?
437 */
438 return -1;
439 }
440 if (bytes_read > 0)
441 return c & 0x7f;
442 }
443 }
444 if (ioctl(0, FIONREAD, (char *)&bytes_ready) >= 0 && stdf >= 0) {
445 while (!ENDseen &&
446 bytes_ready == 0 &&
447 (nopipe || (fstat(stdf, &buf) >= 0 && buf.st_size > 0))) {
448 if (interrupt)
449 return -1;
450 new_block();
451 if (ioctl(0, FIONREAD, (char *)&bytes_ready) < 0) {
452 break;
453 }
454 }
455 }
456 if (read(0, &c, 1) <= 0)
457 return -1;
458 return c & 0x7f;
459}
460
461/*
462 * Get the position of line "ln" in the file.
463 */
464
465long
466getpos(long ln)
467{
468 struct block *pblock;
469 long i;
470
471 pblock = getblock(ln, 1);
472 assert(pblock != 0);
473 i = filldegree * (pblock - blocklist);
474 return i - (filldegree - pblock->b_offs[ln - (pblock - 1)->b_end]);
475}