723 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			723 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* Copyright (c) 1985 Ceriel J.H. Jacobs */
 | 
						|
 | 
						|
# ifndef lint
 | 
						|
static char rcsid[] = "$Header$";
 | 
						|
# endif
 | 
						|
 | 
						|
# define _GETLINE_
 | 
						|
 | 
						|
# include <errno.h>
 | 
						|
# include "in_all.h"
 | 
						|
# include "getline.h"
 | 
						|
# include "options.h"
 | 
						|
# include "process.h"
 | 
						|
# include "term.h"
 | 
						|
# include "main.h"
 | 
						|
# include "display.h"
 | 
						|
# include "output.h"
 | 
						|
# include "assert.h"
 | 
						|
 | 
						|
extern int errno;
 | 
						|
 | 
						|
# define BLOCKSIZE 2048		/* size of blocks */
 | 
						|
# define CHUNK 50		/* # of blockheaders allocated at a time */
 | 
						|
 | 
						|
/*
 | 
						|
 * The blockheaders of the blocks that are in core are kept in a linked list.
 | 
						|
 * The last added block is indicated by b_head,
 | 
						|
 * the tail of the list is indicated by b_tail.
 | 
						|
 * The links go from b_tail to b_head.
 | 
						|
 * The blockheaders are all in an array, in the order of the line numbers.
 | 
						|
 * Also, the blockheaders must always be in core, so they have to be rather
 | 
						|
 * small. On systems with a small address space, yap can run out of core,
 | 
						|
 * and panic. However, this should only happen with very large files (>> 1M).
 | 
						|
 */
 | 
						|
 | 
						|
struct block {
 | 
						|
	int		b_flags;	/* Contains the following flags: */
 | 
						|
# define DUMPED		01		/* block dumped on temporary file */
 | 
						|
# define PARTLY		02		/* block not filled completely (eof) */
 | 
						|
	int		b_next;		/* ptr in linked list */
 | 
						|
	long		b_end;		/* line number of last line in block */
 | 
						|
	char	 *	b_info;		/* the block */
 | 
						|
	int      *	b_offs;		/* line offsets within the block */
 | 
						|
	long		b_foff;		/* offset of block in file */
 | 
						|
};
 | 
						|
 | 
						|
static struct block *	blocklist,	/* beginning of the list of blocks */
 | 
						|
		    *	maxblocklist,	/* first free entry in the list */
 | 
						|
		    *	topblocklist;	/* end of allocated core for the list */
 | 
						|
static int	b_head,
 | 
						|
		b_tail;
 | 
						|
static int	tfdes, ifdes;		/* File descriptors for temporary's */
 | 
						|
static long	lastreadline;		/* lineno of last line read */
 | 
						|
static int	ENDseen;
 | 
						|
 | 
						|
STATIC VOID readblock();
 | 
						|
STATIC VOID nextblock();
 | 
						|
STATIC char *re_alloc();
 | 
						|
 | 
						|
STATIC struct block *
 | 
						|
new_block()
 | 
						|
{
 | 
						|
	register struct block *pblock = maxblocklist - 1;
 | 
						|
 | 
						|
	if (!maxblocklist || !(pblock->b_flags & PARTLY)) {
 | 
						|
		/*
 | 
						|
		 * There is no last block, or it was filled completely,
 | 
						|
		 * so allocate a new blockheader.
 | 
						|
		 */
 | 
						|
		register int siz;
 | 
						|
 | 
						|
		pblock = blocklist;
 | 
						|
		if (maxblocklist == topblocklist) {
 | 
						|
			/*
 | 
						|
			 * No blockheaders left. Allocate new ones
 | 
						|
			 */
 | 
						|
			siz = topblocklist - pblock;
 | 
						|
			blocklist = pblock = (struct block *)
 | 
						|
			re_alloc((char *) pblock,
 | 
						|
			(unsigned) (siz * sizeof(*pblock)),
 | 
						|
			(unsigned) ((siz + CHUNK) * sizeof(*pblock)));
 | 
						|
			pblock += siz;
 | 
						|
			topblocklist = pblock + CHUNK;
 | 
						|
			maxblocklist = pblock;
 | 
						|
			for (; pblock < topblocklist; pblock++) {
 | 
						|
				pblock->b_end = 0;
 | 
						|
				pblock->b_info = 0;
 | 
						|
				pblock->b_flags = 0;
 | 
						|
			}
 | 
						|
			if (!siz) {
 | 
						|
				/*
 | 
						|
				 * Create dummy header cell.
 | 
						|
				 */
 | 
						|
				maxblocklist++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		pblock = maxblocklist++;
 | 
						|
	}
 | 
						|
	nextblock(pblock);
 | 
						|
	return pblock;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Return the block in which line 'n' of the current file can be found.
 | 
						|
 * If "disable_interrupt" = 0, the call may be interrupted, in which
 | 
						|
 * case it returns 0.
 | 
						|
 */
 | 
						|
 | 
						|
STATIC struct block *
 | 
						|
getblock(n, disable_interrupt) register long n; {
 | 
						|
	register struct block * pblock;
 | 
						|
 | 
						|
	if (stdf < 0) {
 | 
						|
		/*
 | 
						|
		 * Not file descriptor, so return end of file
 | 
						|
		 */
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	pblock = maxblocklist - 1;
 | 
						|
	if (n < lastreadline ||
 | 
						|
	    (n == lastreadline && !(pblock->b_flags & PARTLY))) {
 | 
						|
		/*
 | 
						|
		 * The line asked for has been read already.
 | 
						|
		 * Perform binary search in the blocklist to find the block
 | 
						|
		 * where it's in.
 | 
						|
		 */
 | 
						|
		register struct block *min, *mid;
 | 
						|
 | 
						|
		min = blocklist + 1;
 | 
						|
		do {
 | 
						|
			mid = min + (pblock - min) / 2;
 | 
						|
			if (n > mid->b_end) {
 | 
						|
				min = mid + 1;
 | 
						|
			}
 | 
						|
			else pblock = mid;
 | 
						|
		} while (min < pblock);
 | 
						|
		/* Found, pblock is now a reference to the block wanted */
 | 
						|
		if (!pblock->b_info) readblock(pblock);
 | 
						|
		return pblock;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * The line was'nt read yet, so read blocks until found
 | 
						|
	 */
 | 
						|
	for (;;) {
 | 
						|
		if (interrupt && !disable_interrupt) return 0;
 | 
						|
		pblock = new_block();
 | 
						|
		if (pblock->b_end >= n) {
 | 
						|
			return pblock;
 | 
						|
		}
 | 
						|
		if (pblock->b_flags & PARTLY) {
 | 
						|
			/*
 | 
						|
			 * We did not find it, and the last block could not be
 | 
						|
			 * read completely, so return 0;
 | 
						|
			 */
 | 
						|
			return	0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* NOTREACHED */
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
getline(n, disable_interrupt) long n; {
 | 
						|
	register struct block *pblock;
 | 
						|
 | 
						|
	if (!(pblock = getblock(n, disable_interrupt))) {
 | 
						|
		return (char *) 0;
 | 
						|
	}
 | 
						|
	return pblock->b_info + pblock->b_offs[n - ((pblock-1)->b_end + 1)];
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Find the last line of the input, and return its number
 | 
						|
 */
 | 
						|
 | 
						|
long
 | 
						|
to_lastline() {
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		if (!getline(lastreadline + 1, 0)) {
 | 
						|
			/*
 | 
						|
			 * "lastreadline" always contains the linenumber of
 | 
						|
			 * the last line read. So, if the call to getline
 | 
						|
			 * succeeds, "lastreadline" is affected
 | 
						|
			 */
 | 
						|
			if (interrupt) return -1L;
 | 
						|
			return lastreadline;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* NOTREACHED */
 | 
						|
}
 | 
						|
 | 
						|
#if MAXNBLOCKS
 | 
						|
int nblocks;		/* Count number of large blocks */
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
 * Allocate some memory. If unavailable, free some and try again.
 | 
						|
 * If all fails, panic.
 | 
						|
 */
 | 
						|
 | 
						|
char *
 | 
						|
alloc(size, isblock) unsigned size; {
 | 
						|
 | 
						|
	register char *pmem;
 | 
						|
	register struct block *pblock, *bllist;
 | 
						|
	char *malloc();
 | 
						|
	long lseek();
 | 
						|
	register long i;
 | 
						|
 | 
						|
	bllist = blocklist;
 | 
						|
	while (
 | 
						|
#if MAXNBLOCKS
 | 
						|
	   (isblock && nblocks >= MAXNBLOCKS) ||
 | 
						|
#endif
 | 
						|
	   !(pmem = malloc(size))   /* No space */
 | 
						|
	) {
 | 
						|
		if (b_tail == 0) {
 | 
						|
			/*
 | 
						|
			 * Also, no blocks in core. Pity
 | 
						|
			 */
 | 
						|
			panic("No core");
 | 
						|
		}
 | 
						|
#if MAXNBLOCKS
 | 
						|
		nblocks--;
 | 
						|
#endif
 | 
						|
		pblock = bllist + b_tail;
 | 
						|
		b_tail = pblock->b_next;
 | 
						|
		if (!nopipe && !(pblock->b_flags & DUMPED)) {
 | 
						|
			/*
 | 
						|
			 * Dump the block on a temporary file
 | 
						|
			 */
 | 
						|
			if (!tfdes) {
 | 
						|
				/*
 | 
						|
				 * create and open temporary files
 | 
						|
				 */
 | 
						|
				tfdes = opentemp(0);
 | 
						|
				ifdes = opentemp(1);
 | 
						|
			}
 | 
						|
			pblock->b_flags |= DUMPED;
 | 
						|
			/*
 | 
						|
			 * Find out where to dump the block, and dump it
 | 
						|
			 */
 | 
						|
			i = (pblock-1)->b_end * sizeof(int);
 | 
						|
			(VOID) lseek(tfdes,
 | 
						|
				((long) BLOCKSIZE * (pblock - bllist)), 0);
 | 
						|
			if (write(tfdes, pblock->b_info, BLOCKSIZE)
 | 
						|
			    != BLOCKSIZE) {
 | 
						|
				panic("write failed");
 | 
						|
			}
 | 
						|
			/*
 | 
						|
			 * Also dump the offsets of the lines in the block
 | 
						|
			 */
 | 
						|
			(VOID) lseek(ifdes, i, 0);
 | 
						|
			i = pblock->b_end * sizeof(int) - i;
 | 
						|
			if (write(ifdes, (char *) pblock->b_offs, (int) i)
 | 
						|
			    != (int) i) {
 | 
						|
				panic("Write failed");
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * Now that the block is dumped, the space taken by it can
 | 
						|
		 * be freed
 | 
						|
		 */
 | 
						|
		free((char *) pblock->b_offs);
 | 
						|
		free(pblock->b_info);
 | 
						|
		pblock->b_info = (char *) 0;
 | 
						|
	}
 | 
						|
#if MAXNBLOCKS
 | 
						|
	if (isblock) nblocks++;
 | 
						|
#endif
 | 
						|
	return pmem;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Re-allocate the memorychunk pointed to by ptr, to let it
 | 
						|
 * grow or shrink.
 | 
						|
 * realloc of the standard C library is useless, as it is destructive
 | 
						|
 * if the malloc fails.
 | 
						|
 */
 | 
						|
 | 
						|
STATIC char *
 | 
						|
re_alloc(ptr,oldsize, newsize)
 | 
						|
char *ptr; unsigned oldsize; unsigned newsize; {
 | 
						|
	register char *pmem;
 | 
						|
	register char *c1, *c2;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We could be smarter here, by checking if newsize < oldsize, and in
 | 
						|
	 * that case using realloc, but this depends on realloc using the
 | 
						|
	 * same block if the block shrinks. The question is, wether all
 | 
						|
	 * reallocs in the world do this.
 | 
						|
	 */
 | 
						|
	pmem = alloc(newsize, 0);
 | 
						|
	if (oldsize) {
 | 
						|
		/*
 | 
						|
		 * This test makes re_alloc also work if there was no old block
 | 
						|
		 */
 | 
						|
		c1 = pmem;
 | 
						|
		c2 = ptr;
 | 
						|
		if (newsize > oldsize) {
 | 
						|
			newsize = oldsize;
 | 
						|
		}
 | 
						|
		while (newsize--) {
 | 
						|
			*c1++ = *c2++;
 | 
						|
		}
 | 
						|
		free(ptr);
 | 
						|
	}
 | 
						|
	return pmem;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Append a block to the linked list of blockheaders of blocks that are
 | 
						|
 * in core.
 | 
						|
 */
 | 
						|
 | 
						|
STATIC VOID
 | 
						|
addtolist(pblock) register struct block *pblock; {
 | 
						|
	register struct block *bllist = blocklist;
 | 
						|
 | 
						|
	pblock->b_next = 0;
 | 
						|
	(bllist + b_head)->b_next = pblock - bllist;
 | 
						|
	b_head = pblock - bllist;
 | 
						|
	if (!b_tail) {
 | 
						|
		/*
 | 
						|
		 * The list was empty, initialize
 | 
						|
		 */
 | 
						|
		b_tail = b_head;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static char *saved;
 | 
						|
static long filldegree;
 | 
						|
 | 
						|
/*
 | 
						|
 * Try to read the block indicated by pblock
 | 
						|
 */
 | 
						|
 | 
						|
STATIC VOID
 | 
						|
nextblock(pblock) register struct block *pblock; {
 | 
						|
	register char *c,	/* Run through pblock->b_info */
 | 
						|
		      *c1;	/* indicate end of pblock->b_info */
 | 
						|
	register int *poff;	/* pointer in line-offset list */
 | 
						|
	register int cnt;	/* # of characters read */
 | 
						|
	register unsigned siz;	/* Size of allocated line-offset list */
 | 
						|
	static unsigned savedsiz;	/* saved "siz" */
 | 
						|
	static int *savedpoff;		/* saved "poff" */
 | 
						|
	static char *savedc1;		/* saved "c1" */
 | 
						|
 | 
						|
	if (pblock->b_flags & PARTLY) {
 | 
						|
		/*
 | 
						|
		 * The block was already partly filled. Initialize locals
 | 
						|
		 * accordingly
 | 
						|
		 */
 | 
						|
		poff = savedpoff;
 | 
						|
		siz = savedsiz;
 | 
						|
		pblock->b_flags = 0;
 | 
						|
		c1 = savedc1;
 | 
						|
		if (c1 == pblock->b_info || *(c1 - 1)) {
 | 
						|
			/*
 | 
						|
			 * We had incremented "lastreadline" temporarily,
 | 
						|
			 * because the last line could not be completely read
 | 
						|
			 * last time we tried. Undo this increment
 | 
						|
			 */
 | 
						|
			poff--;
 | 
						|
			--lastreadline;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		if (nopipe) pblock->b_foff = lseek(stdf, 0L, 1);
 | 
						|
		if (saved) {
 | 
						|
			/*
 | 
						|
			 * There were leftovers from the previous block
 | 
						|
			 */
 | 
						|
			pblock->b_info = saved;
 | 
						|
			if (nopipe) pblock->b_foff -= savedc1 - saved;
 | 
						|
			c1 = savedc1;
 | 
						|
			saved = 0;
 | 
						|
		}
 | 
						|
		else {	/* Allocate new block */
 | 
						|
			pblock->b_info = c1 = alloc(BLOCKSIZE + 1, 1);
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * Allocate some space for line-offsets
 | 
						|
		 */
 | 
						|
		pblock->b_offs = poff = (int *)
 | 
						|
			alloc((unsigned) (100 * sizeof(int)), 0);
 | 
						|
		siz = 99;
 | 
						|
		*poff++ = 0;
 | 
						|
	}
 | 
						|
	c = c1;
 | 
						|
	for (;;) {
 | 
						|
		/*
 | 
						|
		 * Read loop
 | 
						|
		 */
 | 
						|
		cnt = read(stdf, c1, BLOCKSIZE - (c1 - pblock->b_info));
 | 
						|
		if (cnt < 0) {
 | 
						|
			/*
 | 
						|
			 * Interrupted read
 | 
						|
			 */
 | 
						|
			if (errno == EINTR) continue;
 | 
						|
			error("Could not read input file");
 | 
						|
			cnt = 0;
 | 
						|
		}
 | 
						|
		c1 += cnt;
 | 
						|
		if (c1 != pblock->b_info + BLOCKSIZE) {
 | 
						|
			ENDseen = 1;
 | 
						|
			pblock->b_flags |= PARTLY;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	assert(c <= c1);
 | 
						|
	while (c < c1) {
 | 
						|
		/*
 | 
						|
		 * Now process the block
 | 
						|
		 */
 | 
						|
		*c &= 0177;	/* Most significant bit ignored */
 | 
						|
		if (*c == '\n') {
 | 
						|
			/*
 | 
						|
			 * Newlines are replaced by '\0', so that "getline"
 | 
						|
			 * can deliver one line at a time
 | 
						|
			 */
 | 
						|
			*c = 0;
 | 
						|
			lastreadline++;
 | 
						|
			/*
 | 
						|
			 * Remember the line-offset
 | 
						|
			 */
 | 
						|
			if (poff == pblock->b_offs + siz) {
 | 
						|
				/*
 | 
						|
				 * No space for it, allocate some more
 | 
						|
				 */
 | 
						|
				pblock->b_offs = (int *)
 | 
						|
					re_alloc((char *) pblock->b_offs,
 | 
						|
						 (siz+1) * sizeof(int),
 | 
						|
						 (siz + 51) * sizeof(int));
 | 
						|
				poff = pblock->b_offs + siz;
 | 
						|
				siz += 50;
 | 
						|
			}
 | 
						|
			*poff++ = c - pblock->b_info + 1;
 | 
						|
		}
 | 
						|
		else if (*c == '\0') {
 | 
						|
			/*
 | 
						|
			 * 0-bytes are replaced by 0200, because newlines are
 | 
						|
			 * replaced by 0, and 0200 & 0177 gives again 0 ...
 | 
						|
			 */
 | 
						|
			*c = 0200;
 | 
						|
		}
 | 
						|
		c++;
 | 
						|
	}
 | 
						|
	assert(c==c1);
 | 
						|
	*c = 0;
 | 
						|
	if (c != pblock->b_info && *(c-1) != 0) {
 | 
						|
		/*
 | 
						|
		 * The last line read does not end with a newline, so add one
 | 
						|
		 */
 | 
						|
		lastreadline++;
 | 
						|
		*poff++ = c - pblock->b_info + 1;
 | 
						|
		if (!(pblock->b_flags & PARTLY) && *(poff - 2) != 0) {
 | 
						|
			/*
 | 
						|
			 * Save the started line; it will be in the next block.
 | 
						|
			 * Remove the newline we added just now.
 | 
						|
			 */
 | 
						|
			saved = c1 = alloc(BLOCKSIZE + 1, 1);
 | 
						|
			c = pblock->b_info + *(--poff - 1);
 | 
						|
			while (*c) *c1++ = *c++;
 | 
						|
			c = pblock->b_info + *(poff - 1);
 | 
						|
			savedc1 = c1;
 | 
						|
			--lastreadline;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	pblock->b_end = lastreadline;
 | 
						|
	if (pblock->b_flags & PARTLY) {
 | 
						|
		/*
 | 
						|
		 * Take care, that we can call "nextblock" again, to fill in
 | 
						|
		 * the rest of this block
 | 
						|
		 */
 | 
						|
		savedsiz = siz;
 | 
						|
		savedpoff = poff;
 | 
						|
		savedc1 = c;
 | 
						|
		if (c == pblock->b_info) {
 | 
						|
			lastreadline++;
 | 
						|
			pblock->b_end = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		/*
 | 
						|
		 * Not completely read blocks are not in the linked list,
 | 
						|
		 * so can never be "swapped out".
 | 
						|
		 */
 | 
						|
		addtolist(pblock);
 | 
						|
		cnt = pblock - blocklist;
 | 
						|
		filldegree = ((c-pblock->b_info) + (cnt-1) * filldegree) / cnt;
 | 
						|
	}
 | 
						|
	assert(pblock->b_end - (pblock-1)->b_end <= poff - pblock->b_offs);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Allocate core for the block, and read it back from
 | 
						|
 * the temporary file.
 | 
						|
 */
 | 
						|
 | 
						|
STATIC VOID
 | 
						|
readblock(pblock) register struct block *pblock; {
 | 
						|
 | 
						|
	register int size;
 | 
						|
	register long i;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Find out where the block is, and read it
 | 
						|
	 */
 | 
						|
	pblock->b_info = alloc(BLOCKSIZE + 1, 1);
 | 
						|
	i = (pblock - 1)->b_end * sizeof(int);
 | 
						|
	size = (int) (pblock->b_end * sizeof(int) - i);
 | 
						|
	pblock->b_offs	= (int *) alloc((unsigned) size, 0);
 | 
						|
	if (nopipe) {
 | 
						|
		register char *c;
 | 
						|
		register int line_index;
 | 
						|
		int cnt;
 | 
						|
		long l = lseek(stdf, 0L, 1);
 | 
						|
 | 
						|
		(VOID) lseek(stdf, pblock->b_foff, 0);
 | 
						|
		cnt = read(stdf, pblock->b_info, BLOCKSIZE);
 | 
						|
		(VOID) lseek(stdf, l, 0);
 | 
						|
		c = pblock->b_info;
 | 
						|
		pblock->b_offs[0] = 0;
 | 
						|
		line_index = 1;
 | 
						|
		size /= sizeof(int);
 | 
						|
		while (c < pblock->b_info + cnt) {
 | 
						|
			*c &= 0177;
 | 
						|
			if (*c == '\n') {
 | 
						|
				*c = '\0';
 | 
						|
				if (line_index < size)
 | 
						|
					pblock->b_offs[line_index++] =
 | 
						|
						(c - pblock->b_info) + 1;
 | 
						|
			}
 | 
						|
			else if (*c == '\0') *c = 0200;
 | 
						|
			c++;
 | 
						|
		}
 | 
						|
		*c = '\0';
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		(VOID) lseek(tfdes, (long) ((long) BLOCKSIZE * (pblock - blocklist)),0);
 | 
						|
		if (read(tfdes, pblock->b_info,BLOCKSIZE) != BLOCKSIZE) {
 | 
						|
			panic("read error");
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * Find out where the line-offset list is, and read it
 | 
						|
		 */
 | 
						|
		(VOID) lseek(ifdes, i, 0);
 | 
						|
		if (read(ifdes, (char *) pblock->b_offs, size) != size) {
 | 
						|
			panic("read error");
 | 
						|
		}
 | 
						|
		pblock->b_info[BLOCKSIZE] = '\0';
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * Add this block to the list of incore blocks
 | 
						|
	 */
 | 
						|
	addtolist(pblock);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Called after processing a file.
 | 
						|
 * Free all core.
 | 
						|
 */
 | 
						|
 | 
						|
VOID
 | 
						|
do_clean() {
 | 
						|
 | 
						|
	register struct block *pblock;
 | 
						|
	register char *p;
 | 
						|
 | 
						|
	for (pblock = blocklist; pblock < maxblocklist; pblock++) {
 | 
						|
		if (p = pblock->b_info) {
 | 
						|
			free(p);
 | 
						|
			free((char *) pblock->b_offs);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (p = (char *) blocklist) {
 | 
						|
		free(p);
 | 
						|
	}
 | 
						|
	blocklist = 0;
 | 
						|
	maxblocklist = 0;
 | 
						|
	topblocklist = 0;
 | 
						|
	lastreadline = 0;
 | 
						|
	filldegree = 0;
 | 
						|
	ENDseen = 0;
 | 
						|
	if (p = saved) free(p);
 | 
						|
	saved = 0;
 | 
						|
	b_head = 0;
 | 
						|
	b_tail = 0;
 | 
						|
# if MAXNBLOCKS
 | 
						|
	nblocks = 0;
 | 
						|
# endif
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Close a file with file-descriptor "file", if it indeed is one
 | 
						|
 */
 | 
						|
 | 
						|
STATIC VOID
 | 
						|
cls(file) {
 | 
						|
	if (file) (VOID) close(file);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Close all files
 | 
						|
 */
 | 
						|
 | 
						|
VOID
 | 
						|
cls_files() {
 | 
						|
 | 
						|
	cls(tfdes);
 | 
						|
	cls(ifdes);
 | 
						|
	cls(stdf);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Get a character. If possible, do some workahead.
 | 
						|
 */
 | 
						|
 | 
						|
int
 | 
						|
getch() {
 | 
						|
# if USG_OPEN
 | 
						|
# include <fcntl.h>
 | 
						|
# include <sys/stat.h>
 | 
						|
 | 
						|
	register int i,j;
 | 
						|
	struct stat buf;
 | 
						|
# else
 | 
						|
# ifdef FIONREAD
 | 
						|
# include <sys/stat.h>
 | 
						|
 | 
						|
	struct stat buf;
 | 
						|
	long i;
 | 
						|
# endif
 | 
						|
# endif
 | 
						|
 | 
						|
	char c;
 | 
						|
	int retval;
 | 
						|
 | 
						|
	flush();
 | 
						|
	if (startcomm) {
 | 
						|
		/*
 | 
						|
		 * Command line option command
 | 
						|
		 */
 | 
						|
		if (*startcomm) return *startcomm++;
 | 
						|
		return '\n';
 | 
						|
	}
 | 
						|
# if USG_OPEN
 | 
						|
	if (stdf >= 0) {
 | 
						|
		/*
 | 
						|
		 * Make reads from the terminal non-blocking, so that
 | 
						|
		 * we can see if the user typed something
 | 
						|
		 */
 | 
						|
		i = fcntl(0,F_GETFL,0);
 | 
						|
		if (i != -1 && fcntl(0, F_SETFL, i|O_NDELAY) != -1) {
 | 
						|
			j = 0;
 | 
						|
			while (! ENDseen && 
 | 
						|
			       ((j = read(0,&c,1)) == 0
 | 
						|
#ifdef EWOULDBLOCK
 | 
						|
			        || (j < 0 && errno == EWOULDBLOCK)
 | 
						|
#endif
 | 
						|
			       )
 | 
						|
			       &&
 | 
						|
			       (nopipe || 
 | 
						|
				(fstat(stdf,&buf) >= 0 && buf.st_size > 0))) {
 | 
						|
				/*
 | 
						|
				 * Do some read ahead, after making sure there
 | 
						|
				 * is input and the user did not type a command
 | 
						|
				 */
 | 
						|
				new_block();
 | 
						|
			}
 | 
						|
			(VOID) fcntl(0,F_SETFL,i);
 | 
						|
			if (j < 0) {
 | 
						|
				/*
 | 
						|
				 * Could this have happened?
 | 
						|
				 * I'm not sure, because the read is
 | 
						|
				 * nonblocking. Can it be interrupted then?
 | 
						|
				 */
 | 
						|
				return -1;
 | 
						|
			}
 | 
						|
			if (j > 0) return c;
 | 
						|
		}
 | 
						|
	}
 | 
						|
# else
 | 
						|
# ifdef FIONREAD
 | 
						|
	if (stdf >= 0) {
 | 
						|
		/*
 | 
						|
		 * See if there are any characters waiting in the terminal input
 | 
						|
		 * queue. If there are not, read ahead.
 | 
						|
		 */
 | 
						|
		while (! ENDseen &&
 | 
						|
		       ( ioctl(0, FIONREAD, (char *) &i) >= 0 && i == 0) &&
 | 
						|
		       ( nopipe || fstat(stdf,&buf) >= 0 && buf.st_size > 0)) {
 | 
						|
			/*
 | 
						|
			 * While the user does'nt type anything, and there is
 | 
						|
			 * input to be processed, work ahead
 | 
						|
			 */
 | 
						|
			if (interrupt) return -1;
 | 
						|
			new_block();
 | 
						|
		}
 | 
						|
	}
 | 
						|
# endif
 | 
						|
# endif
 | 
						|
	if (read(0,&c,1) <= 0) retval = -1; else retval = c & 0177;
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Get the position of line "ln" in the file.
 | 
						|
 */
 | 
						|
 | 
						|
long
 | 
						|
getpos(ln) long ln; {
 | 
						|
	register struct block *pblock;
 | 
						|
	register long i;
 | 
						|
 | 
						|
	pblock = getblock(ln,1);
 | 
						|
	assert(pblock != 0);
 | 
						|
	i = filldegree * (pblock - blocklist);
 | 
						|
	return i - (filldegree - pblock->b_offs[ln - (pblock-1)->b_end]);
 | 
						|
}
 |