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}