v08i009: A Micro-Emacs variant that resembles GNU Emacs

sources-request at mirror.UUCP sources-request at mirror.UUCP
Wed Jan 28 04:14:51 AEST 1987


Submitted by: Bob Larson <seismo!usc-oberon!blarson>
Mod.sources: Volume 8, Issue 9
Archive-name: micrognu/Part02


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	basic.c
#	buffer.c
#	cinfo.c
#	echo.c
#	extend.c
#	file.c
#	match.c
# This archive created: Sat Nov 15 14:59:46 1986
export PATH; PATH=/bin:$PATH
if test -f 'basic.c'
then
	echo shar: will not over-write existing file "'basic.c'"
else
cat << \SHAR_EOF > 'basic.c'
/*
 *		Basic cursor motion commands.
 * 
 * The routines in this file are the basic
 * command functions for moving the cursor around on
 * the screen, setting mark, and swapping dot with
 * mark. Only moves between lines, which might make the
 * current buffer framing bad, are hard.
 */
#include	"def.h"

/*
 * Go to beginning of line.
 */
/*ARGSUSED*/
gotobol(f, n, k) {
	curwp->w_doto  = 0;
	return (TRUE);
}

/*
 * Move cursor backwards. Do the
 * right thing if the count is less than
 * 0. Error if you try to move back from
 * the beginning of the buffer.
 */
/*ARGSUSED*/
backchar(f, n, k) register int n; {
	register LINE	*lp;

	if (n < 0)
		return (forwchar(f, -n, k));
	while (n--) {
		if (curwp->w_doto == 0) {
			if ((lp=lback(curwp->w_dotp)) == curbp->b_linep) {
				if (k != KRANDOM)
					ewprintf("Beginning of buffer");
				return (FALSE);
			}
			curwp->w_dotp  = lp;
			curwp->w_doto  = llength(lp);
			curwp->w_flag |= WFMOVE;
		} else
			curwp->w_doto--;
	}
	return (TRUE);
}

/*
 * Go to end of line.
 */
/*ARGSUSED*/
gotoeol(f, n, k) {
	curwp->w_doto  = llength(curwp->w_dotp);
	return (TRUE);
}

/*
 * Move cursor forwards. Do the
 * right thing if the count is less than
 * 0. Error if you try to move forward
 * from the end of the buffer.
 */
/*ARGSUSED*/
forwchar(f, n, k) register int n; {
	if (n < 0)
		return (backchar(f, -n, k));
	while (n--) {
		if (curwp->w_doto == llength(curwp->w_dotp)) {
			if (curwp->w_dotp == curbp->b_linep) {
				if (k != KRANDOM)
					ewprintf("End of buffer");
				return (FALSE);
			}
			curwp->w_dotp  = lforw(curwp->w_dotp);
			curwp->w_doto  = 0;
			curwp->w_flag |= WFMOVE;
		} else
			curwp->w_doto++;
	}
	return (TRUE);
}

/*
 * Go to the beginning of the
 * buffer. Setting WFHARD is conservative,
 * but almost always the case.
 */
gotobob(f, n, k) {
	(VOID) setmark(f, n, k) ;
	curwp->w_dotp  = lforw(curbp->b_linep);
	curwp->w_doto  = 0;
	curwp->w_flag |= WFHARD;
	return (TRUE);
}

/*
 * Go to the end of the buffer.
 * Setting WFHARD is conservative, but
 * almost always the case.
 */
gotoeob(f, n, k) {
	(VOID) setmark(f, n, k) ;
	curwp->w_dotp  = curbp->b_linep;
	curwp->w_doto  = 0;
	curwp->w_flag |= WFHARD;
	return (TRUE);
}

/*
 * Move forward by full lines.
 * If the number of lines to move is less
 * than zero, call the backward line function to
 * actually do it. The last command controls how
 * the goal column is set.
 */
/*ARGSUSED*/
forwline(f, n, k) {
	register LINE	*dlp;

	if (n < 0)
		return (backline(f, -n, KRANDOM));
	if ((lastflag&CFCPCN) == 0)		/* Fix goal.		*/
		setgoal();
	thisflag |= CFCPCN;
	dlp = curwp->w_dotp;
	while (n-- && dlp!=curbp->b_linep)
		dlp = lforw(dlp);
	curwp->w_dotp  = dlp;
	curwp->w_doto  = getgoal(dlp);
	curwp->w_flag |= WFMOVE;
	return (TRUE);
}

/*
 * This function is like "forwline", but
 * goes backwards. The scheme is exactly the same.
 * Check for arguments that are less than zero and
 * call your alternate. Figure out the new line and
 * call "movedot" to perform the motion.
 */
/*ARGSUSED*/
backline(f, n, k) {
	register LINE	*dlp;

	if (n < 0)
		return (forwline(f, -n, KRANDOM));
	if ((lastflag&CFCPCN) == 0)		/* Fix goal.		*/
		setgoal();
	thisflag |= CFCPCN;
	dlp = curwp->w_dotp;
	while (n-- && lback(dlp)!=curbp->b_linep)
		dlp = lback(dlp);
	curwp->w_dotp  = dlp;
	curwp->w_doto  = getgoal(dlp);
	curwp->w_flag |= WFMOVE;
	return (TRUE);
}

/*
 * Set the current goal column,
 * which is saved in the external variable "curgoal",
 * to the current cursor column. The column is never off
 * the edge of the screen; it's more like display then
 * show position.
 */
setgoal() {

	curgoal = getcolpos() - 1;		/* Get the position.	*/
	if (curgoal >= ncol)			/* Chop to tty width.	*/
		curgoal = ncol-1;
}

/*
 * This routine looks at a line (pointed
 * to by the LINE pointer "dlp") and the current
 * vertical motion goal column (set by the "setgoal"
 * routine above) and returns the best offset to use
 * when a vertical motion is made into the line.
 */
getgoal(dlp) register LINE *dlp; {
	register int	c;
	register int	col;
	register int	newcol;
	register int	dbo;

	col = 0;
	dbo = 0;
	while (dbo != llength(dlp)) {
		c = lgetc(dlp, dbo);
		newcol = col;
		if (c == '\t')
			newcol |= 0x07;
		else if (ISCTRL(c) != FALSE)
			++newcol;
		++newcol;
		if (newcol > curgoal)
			break;
		col = newcol;
		++dbo;
	}
	return (dbo);
}

/*
 * Scroll forward by a specified number
 * of lines, or by a full page if no argument.
 * The "2" is the window overlap (this is the default
 * value from ITS EMACS). Because the top line in
 * the window is zapped, we have to do a hard
 * update and get it back.
 */
/*ARGSUSED*/
forwpage(f, n, k) register int n; {
	register LINE	*lp;

	if (f == FALSE) {
		n = curwp->w_ntrows - 2;	/* Default scroll.	*/
		if (n <= 0)			/* Forget the overlap	*/
			n = 1;			/* if tiny window.	*/
	} else if (n < 0)
		return (backpage(f, -n, KRANDOM));
#ifdef	CVMVAS
	else					/* Convert from pages	*/
		n *= curwp->w_ntrows;		/* to lines.		*/
#endif
	lp = curwp->w_linep;
	while (n-- && lp!=curbp->b_linep)
		lp = lforw(lp);
	curwp->w_linep = lp;
	curwp->w_dotp  = lp;
	curwp->w_doto  = 0;
	curwp->w_flag |= WFHARD;
	return (TRUE);
}

/*
 * This command is like "forwpage",
 * but it goes backwards. The "2", like above,
 * is the overlap between the two windows. The
 * value is from the ITS EMACS manual. The
 * hard update is done because the top line in
 * the window is zapped.
 */
/*ARGSUSED*/
backpage(f, n, k) register int n; {
	register LINE	*lp;

	if (f == FALSE) {
		n = curwp->w_ntrows - 2;	/* Default scroll.	*/
		if (n <= 0)			/* Don't blow up if the	*/
			n = 1;			/* window is tiny.	*/
	} else if (n < 0)
		return (forwpage(f, -n, KRANDOM));
#ifdef	CVMVAS
	else					/* Convert from pages	*/
		n *= curwp->w_ntrows;		/* to lines.		*/
#endif
	lp = curwp->w_linep;
	while (n-- && lback(lp)!=curbp->b_linep)
		lp = lback(lp);
	curwp->w_linep = lp;
	curwp->w_dotp  = lp;
	curwp->w_doto  = 0;
	curwp->w_flag |= WFHARD;
	return (TRUE);
}

/*
 * Page the other window. Check to make sure it exists, then
 * nextwind, forwpage and prevwind.
 */
pagenext(f, n, k) {
	if (wheadp->w_wndp == NULL) {
		ewprintf("No other window");
		return FALSE;
	}
	(VOID) nextwind(f, n, k);
	(VOID) forwpage(f, n, k);
	(VOID) prevwind(f, n, k);
	return TRUE;
}

/*
 * Set the mark in the current window
 * to the value of dot. A message is written to
 * the echo line unless we are running in a keyboard
 * macro, when it would be silly.
 */
/*ARGSUSED*/
setmark(f, n, k) {
	isetmark();
	if (kbdmop == NULL)
		ewprintf("Mark set");
	return (TRUE);
}

/* 
 * Internal set mark routine, used by other functions (daveb).
 */
isetmark()
{
	curwp->w_markp = curwp->w_dotp;
	curwp->w_marko = curwp->w_doto;
}

/*
 * Swap the values of "dot" and "mark" in
 * the current window. This is pretty easy, because
 * all of the hard work gets done by the standard routine
 * that moves the mark about. The only possible
 * error is "no mark".
 */
/*ARGSUSED*/
swapmark(f, n, k) {
	register LINE	*odotp;
	register int	odoto;

	if (curwp->w_markp == NULL) {
		ewprintf("No mark in this window");
		return (FALSE);
	}
	odotp = curwp->w_dotp;
	odoto = curwp->w_doto;
	curwp->w_dotp  = curwp->w_markp;
	curwp->w_doto  = curwp->w_marko;
	curwp->w_markp = odotp;
	curwp->w_marko = odoto;
	curwp->w_flag |= WFMOVE;
	return (TRUE);
}

/*
 * Go to a specific line, mostly for
 * looking up errors in C programs, which give the
 * error a line number. If an argument is present, then
 * it is the line number, else prompt for a line number
 * to use.
 */
/*ARGSUSED*/
gotoline(f, n, k) register int n; {
	register LINE	*clp;
	register int	s;
	char		buf[32];

	if (f == FALSE) {
		if ((s=ereply("Goto line: ", buf, sizeof(buf))) != TRUE)
			return (s);
		n = atoi(buf);
	}

	clp = lforw(curbp->b_linep);		/* "clp" is first line	*/
	while (n > 1) {
		if (lforw(clp) == curbp->b_linep) break;
		clp = lforw(clp);
		--n;
	}
	curwp->w_dotp = clp;
	curwp->w_doto = 0;
	curwp->w_flag |= WFMOVE;
	return (TRUE);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'buffer.c'
then
	echo shar: will not over-write existing file "'buffer.c'"
else
cat << \SHAR_EOF > 'buffer.c'
/*
 *		Buffer handling.
 */
#include	"def.h"

/*
 * Attach a buffer to a window. The
 * values of dot and mark come from the buffer
 * if the use count is 0. Otherwise, they come
 * from some other window.
 */
/*ARGSUSED*/
usebuffer(f, n, k) {
	register BUFFER	*bp;
	register int	s;
	char		bufn[NBUFN];

	/* Get buffer to use from user */
	if (curbp->b_altb != NULL)
		s=eread("Switch to buffer: (default %s) ", bufn, NBUFN,
			 EFNEW|EFBUF, 
#ifdef	VARARGS
			 curbp->b_altb->b_bname
#else
			 &(curbp->b_altb->b_bname), (char *) NULL
#endif
			 ) ;
	else
		s=eread("Switch to buffer: ", bufn, NBUFN, EFNEW|EFBUF
#ifndef VARARGS
			 ,(char *) NULL
#endif
			 );
	if (s == ABORT) return (s);
	if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ;
	else if ((bp=bfind(bufn, TRUE)) == NULL) return (FALSE);

	/* and put it in current window */
	curbp = bp;
	return showbuffer(bp, curwp, WFFORCE|WFHARD);
}

/*
 * pop to buffer asked for by the user.
 */
/*ARGSUSED*/
poptobuffer(f, n, k) {
	register BUFFER	*bp;
	register WINDOW	*wp;
	register int	s;
	char		bufn[NBUFN];

	/* Get buffer to use from user */
	if (curbp->b_altb != NULL)
		s=eread("Switch to buffer in other window: (default %s) ",
			 bufn, NBUFN, EFNEW|EFBUF, 
#ifdef	VARARGS
			 curbp->b_altb->b_bname
#else
			 &(curbp->b_altb->b_bname) ,(char *) NULL
#endif
			 ) ;
	else
		s=eread("Switch to buffer in other window: ", bufn, NBUFN,
		EFNEW|EFBUF
#ifndef VARARGS
		, (char *) NULL
#endif
		) ;
	if (s == ABORT) return (s);
	if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ;
	else if ((bp=bfind(bufn, TRUE)) == NULL) return (FALSE);

	/* and put it in a new window */
	if ((wp = popbuf(bp)) == NULL) return FALSE;
	curbp = bp;
	curwp = wp;
	return TRUE;
}

/*
 * Dispose of a buffer, by name.
 * Ask for the name. Look it up (don't get too
 * upset if it isn't there at all!). Clear the buffer (ask
 * if the buffer has been changed). Then free the header
 * line and the buffer header. Bound to "C-X K".
 */
/*ARGSUSED*/
killbuffer(f, n, k) {
	register BUFFER	*bp;
	register BUFFER	*bp1;
	register BUFFER	*bp2;
	register WINDOW	*wp;
	register int	s;
	char		bufn[NBUFN];

	if ((s=eread("Kill buffer: (default %s) ", bufn, NBUFN, EFNEW|EFBUF,
#ifdef	VARARGS
		    curbp->b_bname
#else
		    &(curbp->b_bname)
#endif
		    )) == ABORT) return (s);
	else if (s == FALSE) bp = curbp ;
	else if ((bp=bfind(bufn, FALSE)) == NULL) return FALSE;

	if (bp->b_nwnd != 0) {
		if ((bp1 = bp->b_altb) == NULL) return FALSE;
		if (bclear(bp) != TRUE) return TRUE;
		for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
			/* Special case - could use showbuf, but don't */
			if (wp->w_bufp == bp) {
				--bp->b_nwnd;
				++bp1->b_nwnd;
				wp->w_bufp  = bp1 ;
				wp->w_dotp  = bp1->b_dotp;
				wp->w_doto  = bp1->b_doto;
				wp->w_markp = bp1->b_markp;
				wp->w_marko = bp1->b_marko;
				wp->w_linep = bp1->b_linep;
				wp->w_flag |= WFMODE|WFFORCE|WFHARD;
			}
		}
	}
	else if (bclear(bp) != TRUE) return TRUE;
	if (bp == curbp) curbp = bp->b_altb;
	free((char *) bp->b_linep);		/* Release header line.	*/
	bp1 = NULL;				/* Find the header.	*/
	bp2 = bheadp;
	while (bp2 != bp) {
		if (bp2->b_altb == bp) 
			bp2->b_altb = (bp->b_altb == bp2) ? NULL : bp->b_altb;
		bp1 = bp2;
		bp2 = bp2->b_bufp;
	}
	bp2 = bp2->b_bufp;			/* Next one in chain.	*/
	if (bp1 == NULL)			/* Unlink it.		*/
		bheadp = bp2;
	else
		bp1->b_bufp = bp2;
	while (bp2 != NULL) {			/* Finish with altb's	*/
		if (bp2->b_altb == bp) 
			bp2->b_altb = (bp->b_altb == bp2) ? NULL : bp->b_altb;
		bp2 = bp2->b_bufp;
	}
	free(bp->b_bname);		/* Release name block	*/
	free((char *) bp);			/* Release buffer block	*/
	return (TRUE);
}

/*
 * Save some buffers - just call anycb with the arg flag.
 */
/*ARGSUSED*/
savebuffers(f, n, k) {
	if (anycb(f) == ABORT) return ABORT;
	return TRUE;
}

/*
 * Display the buffer list. This is done
 * in two parts. The "makelist" routine figures out
 * the text, and puts it in a buffer. "popbuf"
 * then pops the data onto the screen. Bound to
 * "C-X C-B".
 */
/*ARGSUSED*/
listbuffers(f, n, k) {
	register BUFFER	*bp;
	register WINDOW *wp;
	BUFFER	 	*makelist();
	WINDOW		*popb();

	if ((bp=makelist()) == NULL || (wp=popbuf(bp)) == NULL)
		return FALSE;
	wp->w_dotp = bp->b_dotp;	/* fix up if window already on screen */
	wp->w_doto = bp->b_doto;
	return TRUE;
}

/*
 * This routine rebuilds the text for the
 * list buffers command. Return TRUE if
 * everything works. Return FALSE if there
 * is an error (if there is no memory).
 */
BUFFER *
makelist() {
	register char	*cp1;
	register char	*cp2;
	register int	c;
	register BUFFER	*bp;
	register LINE	*lp;
	register RSIZE	nbytes;
	BUFFER		*blp;
	char		b[6+1];
	char		line[128];
	
	if ((blp = bfind("*Buffer List*", TRUE)) == NULL) return NULL;
	if (bclear(blp) != TRUE) return NULL;
	blp->b_flag &= ~BFCHG;			/* Blow away old.	*/

	(VOID) strcpy(line, " MR Buffer");
	cp1 = line + 10;
	while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' ';
	(VOID) strcpy(cp1, "Size   File");
	if (addline(blp, line) == FALSE) return NULL;
	(VOID) strcpy(line, " -- ------");
	cp1 = line + 10;
	while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' ';
	(VOID) strcpy(cp1, "----   ----");
	if (addline(blp, line) == FALSE) return NULL;
	bp = bheadp;				/* For all buffers	*/
	while (bp != NULL) {
		cp1 = &line[0];			/* Start at left edge	*/
		*cp1++ = (bp == curbp) ? '.' : ' ';
		*cp1++ = ((bp->b_flag&BFCHG) != 0) ? '*' : ' ';
		*cp1++ = ' ';			/* Gap.			*/
		*cp1++ = ' ';
		cp2 = &bp->b_bname[0];		/* Buffer name		*/
		while ((c = *cp2++) != 0)
			*cp1++ = c;
		while (cp1 < &line[4+NBUFN+1])
			*cp1++ = ' ';		
		nbytes = 0;			/* Count bytes in buf.	*/
		if (bp != blp) {
			lp = lforw(bp->b_linep);
			while (lp != bp->b_linep) {
				nbytes += llength(lp)+1;
				lp = lforw(lp);
			}
		}
		(VOID) itor(b, 6, nbytes);	/* 6 digit buffer size.	*/
		cp2 = &b[0];
		while ((c = *cp2++) != 0)
			*cp1++ = c;
		*cp1++ = ' ';			/* Gap..			*/
		cp2 = &bp->b_fname[0];		/* File name		*/
		if (*cp2 != 0) {
			while ((c = *cp2++) != 0) {
				if (cp1 < &line[128-1])
					*cp1++ = c;
			}
		}
		*cp1 = 0;			/* Add to the buffer.	*/
		if (addline(blp, line) == FALSE)
			return NULL;
		bp = bp->b_bufp;
	}
	blp->b_dotp = lforw(blp->b_linep);	/* put dot at beginning of buffer */
	blp->b_doto = 0;
	return blp;				/* All done		*/
}

/*
 * Used above.
 */
static itor(buf, width, num)
register char buf[]; register int width; register RSIZE num; {
	register RSIZE r;

	if (num / 10 == 0) {
		buf[0] = (num % 10) + '0';
		for (r = 1; r < width; buf[r++] = ' ')
			;
		buf[width] = '\0';
		return 1;
	} else {
		buf[r = itor(buf, width, num / (RSIZE)10)] = 
				(num % (RSIZE)10) + '0';
		return r + 1;
	}
}

/*
 * The argument "text" points to
 * a string. Append this line to the
 * buffer. Handcraft the EOL
 * on the end. Return TRUE if it worked and
 * FALSE if you ran out of room.
 */
addline(bp, text) register BUFFER *bp; char *text; {
	register LINE	*lp;
	register int	i;
	register int	ntext;

	ntext = strlen(text);
	if ((lp=lalloc((RSIZE) ntext)) == NULL)
		return (FALSE);
	for (i=0; i<ntext; ++i)
		lputc(lp, i, text[i]);
	bp->b_linep->l_bp->l_fp = lp;		/* Hook onto the end	*/
	lp->l_bp = bp->b_linep->l_bp;
	bp->b_linep->l_bp = lp;
	lp->l_fp = bp->b_linep;
	if (bp->b_dotp == bp->b_linep)		/* If "." is at the end	*/
		bp->b_dotp = lp;		/* move it to new line	*/
	if (bp->b_markp == bp->b_linep)		/* ditto for mark 	*/
		bp->b_markp = lp;
	return (TRUE);
}

/*
 * Look through the list of buffers, giving the user
 * a chance to save them.  Return TRUE if there are
 * any changed buffers afterwards. Buffers that don't
 * have an associated file don't count. Return FALSE
 * if there are no changed buffers.
 */
anycb(f) {
	register BUFFER	*bp;
	register int	s = FALSE, save = FALSE;
	char		prompt[NFILEN + 11];

	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
		if (*(bp->b_fname) != '\0'
		&&  (bp->b_flag&BFCHG) != 0) {
			(VOID) strcpy(prompt, "Save file ");
			(VOID) strcpy(prompt + 10, bp->b_fname);
			if ((f == TRUE || (save = eyorn(prompt)) == TRUE)
			&&  writeout(bp, bp->b_fname) == TRUE) {
				bp->b_flag &= ~BFCHG;
				upmodes(bp);
			} else s = TRUE;
			if (save == ABORT) return (save);
			save = TRUE;
		}
	}
	if (save == FALSE && kbdmop == NULL)
		ewprintf("(No files need saving)");
	return s;
}

/*
 * Search for a buffer, by name.
 * If not found, and the "cflag" is TRUE,
 * create a buffer and put it in the list of
 * all buffers. Return pointer to the BUFFER
 * block for the buffer.
 */
BUFFER	*
bfind(bname, cflag) register char *bname; {
	register BUFFER	*bp;
	char		*malloc();
	register LINE	*lp;

	bp = bheadp;
	while (bp != NULL) {
		if (strcmp(bname, bp->b_bname) == 0)
			return (bp);
		bp = bp->b_bufp;
	}
	if (cflag!=TRUE) return NULL;
	/*NOSTRICT*/
	if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL) {
		ewprintf("Can't get %d bytes", sizeof(BUFFER));
		return NULL;
	}
	if ((bp->b_bname=malloc(strlen(bname)+1)) == NULL) {
		ewprintf("Can't get %d bytes", strlen(bname)+1);
		free((char *) bp);
		return NULL;
	}
	if ((lp=lalloc((RSIZE) 0)) == NULL) {
		free(bp->b_bname);
		free((char *) bp);
		return NULL;
	}
	bp->b_altb = bp->b_bufp  = NULL;
	bp->b_dotp  = lp;
	bp->b_doto  = 0;
	bp->b_markp = NULL;
	bp->b_marko = 0;
	bp->b_flag  = 0;
	bp->b_nwnd  = 0;
	bp->b_linep = lp;
	(VOID) strcpy(bp->b_fname, "");
	(VOID) strcpy(bp->b_bname, bname);
	lp->l_fp = lp;
	lp->l_bp = lp;
	bp->b_bufp = bheadp;
	bheadp = bp;
	return (bp);
}

/*
 * This routine blows away all of the text
 * in a buffer. If the buffer is marked as changed
 * then we ask if it is ok to blow it away; this is
 * to save the user the grief of losing text. The
 * window chain is nearly always wrong if this gets
 * called; the caller must arrange for the updates
 * that are required. Return TRUE if everything
 * looks good.
 */
bclear(bp) register BUFFER *bp; {
	register LINE	*lp;
	register int	s;
	
	if ((bp->b_flag&BFCHG) != 0		/* Changed.		*/
	&& (s=eyesno("Buffer modified; kill anyway")) != TRUE)
		return (s);
	bp->b_flag  &= ~BFCHG;			/* Not changed		*/
	while ((lp=lforw(bp->b_linep)) != bp->b_linep)
		lfree(lp);
	bp->b_dotp  = bp->b_linep;		/* Fix "."		*/
	bp->b_doto  = 0;
	bp->b_markp = NULL;			/* Invalidate "mark"	*/
	bp->b_marko = 0;
	return (TRUE);
}

/*
 * Display the given buffer in the given window. Flags indicated
 * action on redisplay.
 */
showbuffer(bp, wp, flags) register BUFFER *bp; register WINDOW *wp; {
	register BUFFER	*obp;
	register WINDOW	*owp;

	if (wp->w_bufp == bp) {			/* Easy case!	*/
		wp->w_flag |= flags;
		return TRUE ;
	}

	/* First, dettach the old buffer from the window */
	if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
		if (--obp->b_nwnd == 0) {
			obp->b_dotp  = wp->w_dotp;
			obp->b_doto  = wp->w_doto;
			obp->b_markp = wp->w_markp;
			obp->b_marko = wp->w_marko;
		}
	}

	/* Now, attach the new buffer to the window */
	wp->w_bufp = bp;

	if (bp->b_nwnd++ == 0) {		/* First use.		*/
		wp->w_dotp  = bp->b_dotp;
		wp->w_doto  = bp->b_doto;
		wp->w_markp = bp->b_markp;
		wp->w_marko = bp->b_marko;
	} else
	/* already on screen, steal values from other window */
		for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
			if (wp->w_bufp == bp && owp != wp) {
				wp->w_dotp  = owp->w_dotp;
				wp->w_doto  = owp->w_doto;
				wp->w_markp = owp->w_markp;
				wp->w_marko = owp->w_marko;
				break;
			}
	wp->w_flag |= WFMODE|flags;
	return TRUE;
}

/*
 * Pop the buffer we got passed onto the screen.
 * Returns a status.
 */
WINDOW *
popbuf(bp) register BUFFER *bp; {
	register WINDOW	*wp;

	if (bp->b_nwnd == 0) {		/* Not on screen yet.	*/
		if ((wp=wpopup()) == NULL) return NULL;
	} else
		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
			if (wp->w_bufp == bp) {
				wp->w_flag |= WFHARD|WFFORCE;
				return wp ;
			}
	if (showbuffer(bp, wp, WFHARD) != TRUE) return NULL;
	return wp;
}

/*
 * Insert another buffer at dot.  Very useful.
 */

bufferinsert(f, n, k)
{
	register BUFFER *bp;
	register LINE	*clp;
	register int	clo;
	register int	nline;
	int		s;
	char		bufn[NBUFN];

	/* Get buffer to use from user */
	if (curbp->b_altb != NULL)
		s=eread("Insert buffer: (default %s) ", bufn, NBUFN,
			 EFNEW|EFBUF, &(curbp->b_altb->b_bname),
			 (char *) NULL) ;
	else
		s=eread("Insert buffer: ", bufn, NBUFN, EFNEW|EFBUF,
			 (char *) NULL) ;
	if (s == ABORT) return (s);
	if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb;
	else if ((bp=bfind(bufn, FALSE)) == NULL) return (FALSE);

	if (bp==curbp) {
		ewprintf("Cannot insert buffer into self");
		return (FALSE);
	}

	/* insert the buffer */	
	nline = 0;
	for (clp = lforw(bp->b_linep); clp != bp->b_linep; clp = lforw(clp)) {
		for (clo = 0; clo < llength(clp); clo++)
			if (linsert((RSIZE)1, lgetc(clp, clo)) == FALSE)
				return (FALSE);
		if (newline(FALSE, 1, KRANDOM) == FALSE) /* fake newline */
			return (FALSE);
		nline++;
	}
	if (kbdmop==NULL) {
		if (nline == 1)
			ewprintf("[Inserted 1 line]");
		else
			ewprintf("[Inserted %d lines]", nline);
	}

	clp = curwp->w_linep;			/* cosmetic adjustment */
	if (curwp->w_dotp == clp) {		/* for offscreen insert */
		while (nline-- && lback(clp)!=curbp->b_linep)
			clp = lback(clp);
		curwp->w_linep = clp;		/* adjust framing.	*/
		curwp->w_flag |= WFHARD;
	}
	return (TRUE);
}

/*
 * Turn off the dirty bit on this buffer.
 */
/*ARGSUSED*/
notmodified(f, n, k)
{
	register WINDOW *wp;
	
	curbp->b_flag &= ~BFCHG;
	wp = wheadp;				/* Update mode lines.	*/
	while (wp != NULL) {
		if (wp->w_bufp == curbp)
			wp->w_flag |= WFMODE;
		wp = wp->w_wndp;
	}
	ewprintf("Modification-flag cleared");
	return (TRUE);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'cinfo.c'
then
	echo shar: will not over-write existing file "'cinfo.c'"
else
cat << \SHAR_EOF > 'cinfo.c'
/*
 *		Character class tables.
 * Do it yourself character classification
 * macros, that understand the multinational character set,
 * and let me ask some questions the standard macros (in
 * ctype.h) don't let you ask.
 */
#include	"def.h"

/*
 * This table, indexed by a character drawn
 * from the 256 member character set, is used by my
 * own character type macros to answer questions about the
 * type of a character. It handles the full multinational
 * character set, and lets me ask some questions that the
 * standard "ctype" macros cannot ask.
 */
char	cinfo[256] = {
	_C,		_C,		_C,		_C,	/* 0x0X	*/
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,	/* 0x1X	*/
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	0,		_P,		0,		0,	/* 0x2X	*/
	_W,		_W,		0,		_W,
	0,		0,		0,		0,
	0,		0,		_P,		0,
	_W,		_W,		_W,		_W,	/* 0x3X	*/
	_W,		_W,		_W,		_W,
	_W,		_W,		0,		0,
	0,		0,		0,		_P,
	0,		_U|_W,		_U|_W,		_U|_W,	/* 0x4X	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,	/* 0x5X	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		0,
	0,		0,		0,		0,
	0,		_L|_W,		_L|_W,		_L|_W,	/* 0x6X	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,	/* 0x7X	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		0,
	0,		0,		0,		_C,
	0,		0,		0,		0,	/* 0x8X	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,	/* 0x9X	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,	/* 0xAX	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,	/* 0xBX	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,	/* 0xCX	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	0,		_U|_W,		_U|_W,		_U|_W,	/* 0xDX	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		0,		_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,	/* 0xEX	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	0,		_L|_W,		_L|_W,		_L|_W,	/* 0xFX	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		0,		0
};
SHAR_EOF
fi # end of overwriting check
if test -f 'echo.c'
then
	echo shar: will not over-write existing file "'echo.c'"
else
cat << \SHAR_EOF > 'echo.c'
/*
 *		Echo line reading and writing.
 *
 * Common routines for reading
 * and writing characters in the echo line area
 * of the display screen. Used by the entire
 * known universe.
 */
#include	"def.h"
#ifdef	VARARGS
#  include	<varargs.h>
	static veread();
#endif
static eformat();

int	epresf	= FALSE;		/* Stuff in echo line flag.	*/
/*
 * Erase the echo line.
 */
eerase() {
	ttcolor(CTEXT);
	ttmove(nrow-1, 0);
	tteeol();
	ttflush();
	epresf = FALSE;
}

/*
 * Ask "yes" or "no" question.
 * Return ABORT if the user answers the question
 * with the abort ("^G") character. Return FALSE
 * for "no" and TRUE for "yes". No formatting
 * services are available. No newline required.
 */
eyorn(sp) char *sp; {
	register KEY	s;

	if (kbdmop == NULL) ewprintf("%s? (y or n) ", sp);
	for (;;) {
		s = getkey(0);
		if (s == 'y' || s == 'Y') return (TRUE);
		if (s == 'n' || s == 'N') return (FALSE);
		if (s == (KCTRL|'G') || s == (KCTLX|KCTRL|'G')
		||  s == (KMETA|KCTRL|'G')) {
			(VOID) ctrlg(FALSE, 1, KRANDOM);
			return ABORT;
		}
		if (kbdmop == NULL)
			ewprintf("Please answer y or n.  %s? (y or n) ", sp);
	}
}

/*
 * Like eyorn, but for more important question. User must type either all of
 * "yes" or "no", and the trainling newline.
 */
eyesno(sp) char *sp; {
	register int	s;
	char		buf[64];

	s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp);
	for (;;) {
		if (s == ABORT) return ABORT;
		if (s != FALSE) {
			if ((buf[0] == 'y' || buf[0] == 'Y')
			&&  (buf[1] == 'e' || buf[1] == 'E')
			&&  (buf[2] == 's' || buf[2] == 'S')) return TRUE;
			if ((buf[0] == 'n' || buf[0] == 'N')
			&&  (buf[1] == 'o' || buf[0] == 'O')) return FALSE;
		}
		s = ereply("Please answer yes or no.  %s? (yes or no) ",
			   buf, sizeof(buf), sp);
	}
}
/*
 * Write out a prompt, and read back a
 * reply. The prompt is now written out with full "ewprintf"
 * formatting, although the arguments are in a rather strange
 * place. This is always a new message, there is no auto
 * completion, and the return is echoed as such.
 */
#ifdef	VARARGS
ereply(va_alist)
va_dcl
{
	register int i;
	va_list pvar;
	register char *fp, *buf;
	register int nbuf;
	
	va_start(pvar);
	fp = va_arg(pvar, char *);
	buf = va_arg(pvar, char *);
	nbuf = va_arg(pvar, int);
	i = veread(fp, buf, nbuf, EFNEW|EFCR, &pvar);
	va_end(pvar);
	return i;
}
#else
/* VARARGS3 */
ereply(fp, buf, nbuf, arg) char *fp, *buf; int nbuf; long arg; {
	return (eread(fp, buf, nbuf, EFNEW|EFCR, (char *)&arg));
}
#endif

/*
 * This is the general "read input from the
 * echo line" routine. The basic idea is that the prompt
 * string "prompt" is written to the echo line, and a one
 * line reply is read back into the supplied "buf" (with
 * maximum length "len"). The "flag" contains EFNEW (a
 * new prompt), an EFFUNC (autocomplete), or EFCR (echo
 * the carriage return as CR).
 */
/* VARARGS4 */
#ifdef	VARARGS
eread(va_alist)
va_dcl
{
	va_list pvar;
	char *fp, *buf;
	int nbuf, flag, i;
	va_start(pvar);
	fp   = va_arg(pvar, char *);
	buf  = va_arg(pvar, char *);
	nbuf = va_arg(pvar, int);
	flag = va_arg(pvar, int);
	i = veread(fp, buf, nbuf, flag, &pvar);
	va_end(pvar);
	return i;
}
#endif

#ifdef	VARARGS
static veread(fp, buf, nbuf, flag, ap) char *fp; char *buf; va_list *ap; {
#else
eread(fp, buf, nbuf, flag, ap) char *fp; char *buf; char *ap; {
#endif
	register int	cpos;
	register int	i;
	register KEY	c;

	cpos = 0;
	if (kbdmop != NULL) {			/* In a macro.		*/
		while ((c = *kbdmop++) != '\0')
			buf[cpos++] = (char) c;
		buf[cpos] = '\0';
		goto done;
	}
	if ((flag&EFNEW)!=0 || ttrow!=nrow-1) {
		ttcolor(CTEXT);
		ttmove(nrow-1, 0);
		epresf = TRUE;
	} else
		eputc(' ');
	eformat(fp, ap);
	tteeol();
	ttflush();
	for (;;) {
		c = getkey(KQUOTE|KNOMAC);
		if ((flag&EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) {
			cpos += complete(flag, c, buf, cpos);
			continue;
		}
		switch (c) {
		case 0x0D:			/* Return, done.	*/
			if ((flag&EFFUNC) != 0) {
				if ((i = complete(flag, c, buf, cpos)) == 0)
					continue;
				if (i > 0) cpos += i;
			}
			buf[cpos] = '\0';
			if ((flag&EFCR) != 0) {
				ttputc(0x0D);
				ttflush();
			}
			if (kbdmip != NULL) {
				if (kbdmip+cpos+1 > &kbdm[NKBDM-3]) {
					ewprintf("Keyboard macro overflow");
					ttflush();
					return FALSE;
				}
				for (i = 0; i <= cpos; ++i)
					*kbdmip++ = (KEY) buf[i];
			}
			goto done;

		case CCHR('G'):			/* Bell, abort.		*/
			eputc(CCHR('G'));
			(VOID) ctrlg(FALSE, 0, KRANDOM);
			ttflush();
			return (ABORT);

		case 0x7F:			/* Rubout, erase.	*/
			if (cpos != 0) {
				ttputc('\b');
				ttputc(' ');
				ttputc('\b');
				--ttcol;
				if (ISCTRL(buf[--cpos]) != FALSE) {
					ttputc('\b');
					ttputc(' ');
					ttputc('\b');
					--ttcol;
				}
				ttflush();
			}
			break;

		case CCHR('X'):			/* C-X			*/
		case CCHR('U'):			/* C-U, kill line.	*/
			while (cpos != 0) {
				ttputc('\b');
				ttputc(' ');
				ttputc('\b');
				--ttcol;
				if (ISCTRL(buf[--cpos]) != FALSE) {
					ttputc('\b');
					ttputc(' ');
					ttputc('\b');
					--ttcol;
				}
			}
			ttflush();
			break;

		case CCHR('Q'):			/* C-Q, quote next	*/
			c = getkey(KQUOTE|KNOMAC) ;
		default:			/* All the rest.	*/
			if (cpos < nbuf-1) {
				buf[cpos++] = (char) c;
				eputc((char) c);
				ttflush();
			}
		}
	}
done:
	if (buf[0] == '\0')
		return (FALSE);
	return (TRUE);
}

/*
 * do completion on a list of objects.
 */
complete(flags, c, buf, cpos) register char *buf; register int cpos; {
	register LIST	*lh, *lh2;
#ifndef	MANX
	register	/* too many registers mess Manx up */
#endif
	int		i, nxtra;
	int		nhits, bxtra;
	int		wflag = FALSE;
	int		msglen, nshown;
	char		*msg;

	if ((flags&EFFUNC) != 0) lh = &(symbol[0]->s_list);
	else if ((flags&EFBUF) != 0) lh = &(bheadp->b_list);
	else panic("broken complete call: flags");

	if (c == ' ') wflag = TRUE;
	else if (c != '\t' && c != 0x0D) panic("broken complete call: c");

	nhits = 0;
	nxtra = HUGE;

	while (lh != NULL) {
		for (i=0; i<cpos; ++i) {
			if (buf[i] != lh->l_name[i])
				break;
		}
		if (i == cpos) {
			if (nhits == 0)
				lh2 = lh;
			++nhits;
			if (lh->l_name[i] == '\0') nxtra = -1;
			else {
				bxtra = getxtra(lh, lh2, cpos, wflag);
				if (bxtra < nxtra) nxtra = bxtra;
				lh2 = lh;
			}
		}
		lh = lh->l_next;
	}
	if (nhits == 0)
		msg = " [No match]";
	else if (nhits > 1 && nxtra == 0)
		msg = " [Ambiguous]";
	else {		/* Got a match, do it to it */
		/*
		 * Being lazy - ought to check length, but all things
		 * autocompleted have known types/lengths.
		 */ 
		if (nxtra < 0 && nhits > 1 && c == ' ') nxtra = 1;
		for (i = 0; i < nxtra; ++i) {
			buf[cpos] = lh2->l_name[cpos];
			eputc(buf[cpos++]);
		}
		ttflush();
		if (nxtra < 0 && c != 0x0D) return 0;
		return nxtra;
	}
        /* Set up backspaces, etc., being mindful of echo line limit */
	msglen = strlen(msg);
	nshown = (ttcol + msglen + 2 > ncol) ? 
			ncol - ttcol - 2 : msglen;
	eputs(msg);
	ttcol -= (i = nshown);		/* update ttcol!		*/
	while (i--)			/* move back before msg		*/
		ttputc('\b');
	ttflush();			/* display to user		*/
	i = nshown;
	while (i--)			/* blank out	on next flush	*/
		eputc(' ');
	ttcol -= (i = nshown);		/* update ttcol on BS's		*/
	while (i--)
		ttputc('\b');		/* update ttcol again!		*/
	return 0;

}

/*
 * The "lp1" and "lp2" point to list structures. The
 * "cpos" is a horizontal position in the name.
 * Return the longest block of characters that can be
 * autocompleted at this point. Sometimes the two
 * symbols are the same, but this is normal.
  */
getxtra(lp1, lp2, cpos, wflag) register LIST *lp1, *lp2; register int wflag; {
	register int	i;

	i = cpos;
	for (;;) {
		if (lp1->l_name[i] != lp2->l_name[i]) break;
		if (lp1->l_name[i] == '\0') break;
		++i;
		if (wflag && !ISWORD(lp1->l_name[i-1])) break;
	}
	return (i - cpos);
}

/*
 * Special "printf" for the echo line.
 * Each call to "ewprintf" starts a new line in the
 * echo area, and ends with an erase to end of the
 * echo line. The formatting is done by a call
 * to the standard formatting routine.
 */
#ifdef	VARARGS
ewprintf(va_alist)
va_dcl
{
	va_list pvar;
	register char *fp;

	va_start(pvar);
	fp = va_arg(pvar, char *);
#else
/* VARARGS1 */
ewprintf(fp, arg) char *fp; {
#endif
	ttcolor(CTEXT);
	ttmove(nrow-1, 0);
#ifdef	VARARGS
	eformat(fp, &pvar);
	va_end(pvar);
#else
	eformat(fp, (char *)&arg);
#endif
	tteeol();
	ttflush();
	epresf = TRUE;
}

/*
 * Printf style formatting. This is
 * called by both "ewprintf" and "ereply" to provide
 * formatting services to their clients. The move to the
 * start of the echo line, and the erase to the end of
 * the echo line, is done by the caller.
 * Note: %c works, and prints the "name" of the key. However
 * the key must be cast to an int to avoid tripping over
 * various oddities in C argument passing.
 */
static eformat(fp, ap) register char *fp;
#ifdef VARARGS
register va_list *ap;
#else
register char *ap;
#endif
{
	register int	c;
	char		kname[NKNAME];

	while ((c = *fp++) != '\0') {
		if (c != '%')
			eputc(c);
		else {
			c = *fp++;
			switch (c) {
			case 'c':
#ifdef	VARARGS
				keyname(kname, va_arg(*ap, int));
#else
				/*NOSTRICT*/
				keyname(kname, *(int *)ap);
				ap += sizeof(int);
#endif
				eputs(kname);
				break;

			case 'd':
#ifdef	VARARGS
				eputi(va_arg(*ap, int), 10);
#else
				/*NOSTRICT*/
				eputi(*(int *)ap, 10);
				ap += sizeof(int);
#endif
				break;

			case 'o':
#ifdef	VARARGS
				eputi(va_arg(*ap, int), 8);
#else
				/*NOSTRICT*/
				eputi(*(int *)ap,  8);
				ap += sizeof(int);
#endif
				break;

			case 's':
#ifdef	VARARGS
				eputs(va_arg(*ap, char *));
#else
				/*NOSTRICT*/
				eputs(*(char **)ap);
				ap += sizeof(char *);
#endif
				break;
			case 'l':/* explicit longword */
				c = *fp++;
				switch(c) {
				case 'd':
#ifdef	VARARGS
					eputl((long)va_arg(*ap, long), 10L);
#else
					/*NOSTRICT*/
					eputl(*(long *)ap, 10L);
					ap += sizeof(long);
#endif
					break;
				default:
					eputc(c);
					break;
				}
				break;

			default:
				eputc(c);
			}
		}
	}
}

/*
 * Put integer, in radix "r".
 */
static eputi(i, r) register int i; register int r; {
	register int	q;

	if ((q=i/r) != 0)
		eputi(q, r);
	eputc(i%r+'0');
}

/*
 * Put long, in radix "r".
 */
static eputl(l, r) register long l; register long r; {
	register long	q;

	if ((q=l/r) != 0)
		eputl(q, r);
	eputc((int)(l%r)+'0');
}

/*
 * Put string.
 */
eputs(s) register char *s; {
	register int	c;

	while ((c = *s++) != '\0')
		eputc(c);
}

/*
 * Put character. Watch for
 * control characters, and for the line
 * getting too long.
 */
static eputc(c) register char c; {
	if (ttcol+2 < ncol) {
		if (ISCTRL(c) != FALSE) {
			eputc('^');
			c ^= 0x40;
		}
		ttputc(c);
		++ttcol;
	}
}

SHAR_EOF
fi # end of overwriting check
if test -f 'extend.c'
then
	echo shar: will not over-write existing file "'extend.c'"
else
cat << \SHAR_EOF > 'extend.c'
/*
 *		Extended (M-X) commands.
 */
#include	"def.h"

/*
 * This function modifies the keyboard
 * binding table, by adjusting the entries in the
 * big "bindings" array. Most of the grief deals with the
 * prompting for additional arguments.
 */
/*ARGSUSED*/
bindtokey(f, n, k) {
	register int	s;
	register SYMBOL	*sp;
	int		c;
	char		xname[NXNAME];

	if (kbdmop == NULL)
		ewprintf("Set key globally: ") ;
	c = (int) getkey(0);
	if ((s=eread("Set key %c to command: ", xname, NXNAME, EFNEW|EFFUNC,
#ifdef VARARGS
		     c
#else
		     (char *) &c, (char *) NULL
#endif
		     )) != TRUE)
		return (s);
	if ((sp=symlookup(xname)) == NULL) {
		ewprintf("[No match]");
		return (FALSE);
	}
	binding[(KEY) c] = sp;			/* rebind new.		*/
	return (TRUE);
}

/*
 * User function to unbind keys. Just call the unbind we already have. 
 */
/*ARGSUSED*/
unsetkey(f, n, k) {
	register KEY	key;
	
	if (kbdmop == NULL) ewprintf("Unset key globally: ") ;
	key = getkey(0);
	if (key == (KCTRL|'G') || key == (KCTLX|KCTRL|'G')
	|| key == (KMETA|KCTRL|'G')) {
		(VOID) ctrlg(FALSE, 1, KRANDOM);
		return ABORT;
	}
	binding[key] = NULL;
	return TRUE;
}

/*
 * Extended command. Call the message line
 * routine to read in the command name and apply autocompletion
 * to it. When it comes back, look the name up in the symbol table
 * and run the command if it is found and has the right type.
 * Print an error if there is anything wrong.
 */
/*ARGSUSED*/
extend(f, n, k) {
	register SYMBOL	*sp;
	register int	s;
	char		xname[NXNAME];

	if (f == FALSE)
		s = eread("M-x ", xname, NXNAME, EFNEW|EFFUNC
#ifndef VARARGS
			 , (char *) NULL
#endif
			 ) ;
	else
		s = eread("%d M-x ", xname, NXNAME, EFNEW|EFFUNC, 
#ifdef	VARARGS
			 n
#else
			 (char *) &n, (char *) NULL
#endif
			 ) ;
	if (s != TRUE) return (s);
	if ((sp=symlookup(xname)) != NULL)
		return ((*sp->s_funcp)(f, n, KRANDOM));
	ewprintf("[No match]");
	return FALSE;
}

/*
 * Read a key from the keyboard, and look it
 * up in the binding table. Display the name of the function
 * currently bound to the key. Say that the key is not bound
 * if it is indeed not bound, or if the type is not a
 * "builtin". This is a bit of overkill, because this is the
 * only kind of function there is.
 */
/*ARGSUSED*/
desckey(f, n, k) {
	register SYMBOL	*sp;
	register KEY	c;

	if (kbdmop == NULL) ewprintf("Describe key briefly: ");
	c = getkey(0);
	if (kbdmop != NULL) return TRUE;
	if ((sp=binding[c]) == NULL)
		ewprintf("%c is undefined", (int) c);
	else
		ewprintf("%c runs the command %s", (int) c, sp->s_name);
	return (TRUE);
}

/*
 * This function creates a table, listing all
 * of the command keys and their current bindings, and stores
 * the table in the standard pop-op buffer (the one used by the
 * directory list command, the buffer list command, etc.). This
 * lets MicroEMACS produce it's own wall chart. The bindings to
 * "ins-self" are only displayed if there is an argument.
 */
/*ARGSUSED*/
wallchart(f, n, k) {
	register int	key;
	register SYMBOL	*sp;
	register char	*cp1;
	register char	*cp2;
	BUFFER		*bp;
	char		buf[64];

	bp = bfind("*Help*", TRUE);
	if (bclear(bp) != TRUE)			/* Clear it out.	*/
		return TRUE;
	for (key=0; key<NKEYS; ++key) {		/* For all keys.	*/
		sp = binding[key];
		if (sp != NULL
		&& (f!=FALSE
		|| strcmp(sp->s_name, "self-insert-command")!=0)) {
			keyname(buf, key);
			cp1 = &buf[0];		/* Find end.		*/
			while (*cp1 != 0)
				++cp1;
			while (cp1 < &buf[32])	/* Goto column 32.	*/
				*cp1++ = ' ';				
			cp2 = sp->s_name;	/* Add function name.	*/
			while (*cp1++ = *cp2++)
				;
			if (addline(bp, buf) == FALSE)
				return (FALSE);
		}
	}
	return popbuf(bp) == NULL ? FALSE : TRUE;
}

#ifdef	STARTUP
/*
 * Define the commands needed to do startup-file processing.
 * This code is mostly a kludge just so we can get startup-file processing.
 *
 * If you're serious about having this code, you should rewrite it.
 * To wit: 
 *	It has lots of funny things in it to make the startup-file look
 *	like a GNU startup file; mostly dealing with parens and semicolons.
 *	This should all vanish.
 *
 *	It uses the same buffer as keyboard macros. The fix is easy (make
 *	a new function "execmacro" that takes a pointer to char and
 *	does what ctlxe does on it. Make ctlxe and excline both call it.)
 *	but would slow down the non-micro version.
 *
 * We define eval-expression because it's easy. It's pretty useless,
 * since it duplicates the functionality of execute-extended-command.
 * All of this is just to support startup files, and should be turned
 * off for micros.
 */

/*
 * evalexpr - get one line from the user, and run it. Identical in function
 *	to extend, but easy.
 */
/*ARGSUSED*/
evalexpr(f, n, k) {
	register int	s;
	char		exbuf[NKBDM];

	if ((s = ereply("Eval: ", exbuf, NKBDM)) != TRUE)
		return s;
	return excline(exbuf);
}
/*
 * evalbuffer - evaluate the current buffer as line commands. Useful
 *	for testing startup files.
 */
/*ARGSUSED*/
evalbuffer(f, n, k) {
	register LINE	*lp;
	register BUFFER	*bp = curbp;
	register int	s;
	static char	excbuf[NKBDM];
	char		*strncpy();

	for (lp = lforw(bp->b_linep); lp != bp->b_linep; lp = lforw(lp)) {
		if (llength(lp) >= NKBDM + 1) return FALSE ;
		(VOID) strncpy(excbuf, ltext(lp), NKBDM);
		if ((s = excline(excbuf)) != TRUE) return s;
	}
	return TRUE;
}
/*
 * evalfile - go get a file and evaluate it as line commands. You can
 *	go get your own startup file if need be.
 */
/*ARGSUSED*/
evalfile(f, n, k) {
	register int	s;
	char		fname[NFILEN];

	if ((s = ereply("Load file: ", fname, NFILEN)) != TRUE)
		return s;
	return load(fname);
}

/*
 * load - go load the file name we got passed.
 */
load(fname) char *fname; {
	register int	s;
	char		excbuf[NKBDM];

	if (ffropen(fname) == FIOERR)
		return FALSE;
	while ((s = ffgetline(excbuf, NKBDM)) == FIOSUC)
		if (excline(excbuf) != TRUE) break;
	(VOID) ffclose();
	return s == FIOEOF;
}

/*
 * excline - run a line from a load file or eval-expression.
 */
excline(line) register char *line; {
	register char	*funcp, *argp = NULL;
	char		*skipwhite(), *parsetoken(), *backquote();
	int		status;

	/* Don't know if it works; don't care - mwm */
	if (kbdmip != NULL || kbdmop != NULL) {
		ewprintf("Not now!") ;
		return FALSE;
	}

	funcp = skipwhite(line);
	if (*funcp == '\0') return TRUE;	/* No error on blank lines */
	line = parsetoken(funcp);
	if (*line != '\0') {
		*line++ = '\0';
		line = skipwhite(line);
		if ((*line >= '0' && *line <= '9') || *line == '-') {
			argp = line;
			line = parsetoken(line);
		}
	}

	kbdmip = &kbdm[0];
	if (argp != NULL) {
		*kbdmip++ = (KEY) (KCTRL|'U');
		*kbdmip++ = (KEY) atoi(argp);
	}
	*kbdmip++ = (KEY) (KMETA|'X');
	/* Pack in function */
	while (*funcp != '\0')
		if (kbdmip+1 <= &kbdm[NKBDM-3]) *kbdmip++ = (KEY) *funcp++;
		else {
			ewprintf("eval-expression macro overflow");
			ttflush();
			return FALSE;
		}
	*kbdmip++ = '\0';	/* done with function */
	/* Pack away all the args now...	*/
	while (*line != '\0') {
		argp = skipwhite(line);
		if (*argp == '\0') break ;
		line = parsetoken(argp) ;
		/* Slightly bogus for strings. But they should be SHORT! */
		if (kbdmip+(line-argp)+1 > &kbdm[NKBDM-3]) {
			ewprintf("eval-expression macro overflow");
			ttflush();
			return FALSE;
		}
		if (*line != '\0') *line++ = '\0';
		if (*argp != '"') {
			if (*argp == '\'') ++argp;
			while (*argp != '\0')
				*kbdmip++ = (KEY) *argp++;
			*kbdmip++ = '\0';
		}
		else {	/* Quoted strings special again */
			++argp;
			while (*argp != '"' && *argp != '\0')
				if (*argp != '\\') *kbdmip++ = (KEY) *argp++;
				else argp = backquote(++argp, TRUE);
			/* Quotes strings are gotkey'ed, so no trailing null */
		}
	}
	*kbdmip++ = (KEY) (KCTLX|')');
	*kbdmip++ = '\0';
	kbdmip = NULL;
	status = ctlxe(FALSE, 1, KRANDOM);
	kbdm[0] = (KCTLX|')');
	return status;
}
/*
 * a pair of utility functions for the above
 */
char *
skipwhite(s) register char *s; {

	while ((*s == ' ' || *s == '\t' || *s == ')' || *s == '(')
	    && *s != '\0')
		if (*s == ';') *s = '\0' ;
		else s++;
	return s;
}

char *
parsetoken(s) register char *s; {

	if (*s != '"')
		while (*s != ' ' && *s != '\t' && *s != '(' && *s != ')'
		    && *s != '\0') {
			if (*s == ';') *s = '\0';
			else s++;
		}
	else	/* Strings get special treatment */
		do {
			/* Beware: You can \ out the end of the string! */
			if (*s == '\\') ++s;
			if (ISLOWER(*s)) *s = TOUPPER(*s);
			} while (*++s != '"' && *s != '\0');
	return s;
}
/*
 * Put a backquoted string element into the keyboard macro. Return pointer
 * to char following backquoted stuff.
 */
char *
backquote(in, flag) char *in; {
	switch (*in++) {
	    case 'T': *kbdmip++ = (KEY) (KCTRL|'I');
		      break;
	    case 'N': *kbdmip++ = (KEY) (KCTRL|'J');
		      break; 
	    case 'R': *kbdmip++ = (KEY) (KCTRL|'M');
		      break;
	    case '^': *kbdmip = (KEY) (KCTRL|*in++);
		      if (flag != FALSE && *kbdmip == (KEY) (KCTRL|'X')) {
			      if (*in == '\\') in = backquote(++in, FALSE);
			      else *kbdmip++ = (KEY) *in++;
			      kbdmip[-1] |= (KEY) KCTLX;
		      } else ++kbdmip;
		      break;
	    case 'E':
		      if (flag != TRUE) *kbdmip++ = (KEY) (KCTRL|'[');
		      else if (*in != '\\') *kbdmip++ = (KEY) (KMETA|*in++);
		      else {
			      in = backquote(++in, FALSE);
			      kbdmip[-1] |= (KEY) KMETA;
		      }
		      break;
	}
	return in;
}
#endif	STARTUP
SHAR_EOF
fi # end of overwriting check
if test -f 'file.c'
then
	echo shar: will not over-write existing file "'file.c'"
else
cat << \SHAR_EOF > 'file.c'
/*
 * 		File commands.
 */
#include	"def.h"

BUFFER	*findbuffer();

/*
 * insert a file into the current buffer. Real easy - just call the
 * insertfile routine with the file name.
 */
/*ARGSUSED*/
fileinsert(f, n, k) {
	register int	s;
	char		fname[NFILEN];

	if ((s=ereply("Insert file: ", fname, NFILEN)) != TRUE)
		return (s);
	adjustcase(fname);
	return (insertfile(fname, (char *) NULL)); /* don't set buffer name */
}

/*
 * Select a file for editing.
 * Look around to see if you can find the
 * fine in another buffer; if you can find it
 * just switch to the buffer. If you cannot find
 * the file, create a new buffer, read in the
 * text, and switch to the new buffer.
 */
/*ARGSUSED*/
filevisit(f, n, k) {
	register BUFFER	*bp;
	int		s;
	char		fname[NFILEN];

	if ((s=ereply("Find file: ", fname, NFILEN)) != TRUE)
		return (s);
	if ((bp = findbuffer(fname, &s)) == NULL) return s;
	curbp = bp;
	if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE;
	if (bp->b_fname[0] == 0)
		return (readin(fname));		/* Read it in.		*/
	return TRUE;
}

/*
 * Pop to a file in the other window. Same as last function, just
 * popbuf instead of showbuffer.
 */
/*ARGSUSED*/
poptofile(f, n, k) {
	register BUFFER	*bp;
	register WINDOW	*wp;
	int		s;
	char		fname[NFILEN];

	if ((s=ereply("Find file in other window: ", fname, NFILEN)) != TRUE)
		return (s);
	if ((bp = findbuffer(fname, &s)) == NULL) return s;
	if ((wp = popbuf(bp)) == NULL) return FALSE;
	curbp = bp;
	curwp = wp;
	if (bp->b_fname[0] == 0)
		return (readin(fname));		/* Read it in.		*/
	return TRUE;
}

/*
 * given a file name, either find the buffer it uses, or create a new
 * empty buffer to put it in.
 */
BUFFER *
findbuffer(fname, s) char *fname; int *s; {
	register BUFFER	*bp;
	char		bname[NBUFN];

	adjustcase(fname);
	for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
		if (strcmp(bp->b_fname, fname) == 0) {
			return bp;
		}
	}
	makename(bname, fname);			/* New buffer name.	*/
	while ((bp=bfind(bname, FALSE)) != NULL) {
		*s = ereply("Buffer name: ", bname, NBUFN);
		if (*s == ABORT)		/* ^G to just quit	*/
			return NULL;
		if (*s == FALSE) {		/* CR to clobber it	*/
			bp->b_fname[0] = '\0';
			break;
		}
	}
	if (bp == NULL) bp = bfind(bname, TRUE);
	*s = FALSE;
	return bp;
}

/*
 * Read the file "fname" into the current buffer.
 * Make all of the text in the buffer go away, after checking
 * for unsaved changes. This is called by the "read" command, the
 * "visit" command, and the mainline (for "uemacs file").
 */
readin(fname) char *fname; {
	register int		status;
	register WINDOW		*wp;

	if (bclear(curbp) != TRUE)		/* Might be old.	*/
		return TRUE;
	status = insertfile(fname, fname) ;
	curbp->b_flag &= ~BFCHG;		/* No change.		*/
	for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
		if (wp->w_bufp == curbp) {
			wp->w_linep = lforw(curbp->b_linep);
			wp->w_dotp  = lforw(curbp->b_linep);
			wp->w_doto  = 0;
			wp->w_markp = NULL;
			wp->w_marko = 0;
		}
	}
	return status;
}
/*
 * insert a file in the current buffer, after dot. Set mark
 * at the end of the text inserted, point at the beginning.
 * Return a standard status. Print a summary (lines read,
 * error message) out as well. If the
 * BACKUP conditional is set, then this routine also does the read
 * end of backup processing. The BFBAK flag, if set in a buffer,
 * says that a backup should be taken. It is set when a file is
 * read in, but not on a new file (you don't need to make a backup
 * copy of nothing).
 *
 * Warning: Adds a trainling nl to files that don't end in one!
 * Need to fix, but later (I suspect that it will require a change
 * in the fileio files for all systems involved).
 */
insertfile(fname, newname) char fname[], newname[]; {
	register LINE	*lp1;
	register LINE	*lp2;
	LINE		*olp;			/* Line we started at */
	int		opos;			/* and offset into it */
	register WINDOW	*wp;
	register int	i;
	register int	nbytes;
	int		s, nline;
	BUFFER		*bp;
	char		line[NLINE];

	bp = curbp;				/* Cheap.		*/
	if (newname != (char *) NULL)
		(VOID) strcpy(bp->b_fname, newname);
	if ((s=ffropen(fname)) == FIOERR) 	/* Hard file open.	*/
		goto out;
	if (s == FIOFNF) {			/* File not found.	*/
		if (kbdmop == NULL)
			if (newname != NULL)
				ewprintf("(New file)");
			else	ewprintf("(File not found)");
		goto out;
	}
	opos = curwp->w_doto;
	/* Open a new line, at point, and start inserting after it */
	(VOID) lnewline();
	olp = lback(curwp->w_dotp);
	nline = 0;			/* Don't count fake line at end */
	while ((s=ffgetline(line, NLINE)) == FIOSUC) {
		nbytes = strlen(line);
		if ((lp1=lalloc((RSIZE) nbytes)) == NULL) {
			s = FIOERR;		/* Keep message on the	*/
			break;			/* display.		*/
		}
		lp2 = lback(curwp->w_dotp);
		lp2->l_fp = lp1;
		lp1->l_fp = curwp->w_dotp;
		lp1->l_bp = lp2;
		curwp->w_dotp->l_bp = lp1;
		for (i=0; i<nbytes; ++i)
			lputc(lp1, i, line[i]);
		++nline;
	}
	(VOID) ffclose();			/* Ignore errors.	*/
	if (s==FIOEOF && kbdmop==NULL) {	/* Don't zap an error.	*/
		if (nline == 1)
			ewprintf("(Read 1 line)");
		else
			ewprintf("(Read %d lines)", nline);
	}
	/* Set mark at the end of the text */
	curwp->w_markp = curwp->w_dotp;
	curwp->w_marko = curwp->w_doto;
	/* Now, delete the results of the lnewline we started with */
	curwp->w_dotp = olp;
	curwp->w_doto = opos;
	(VOID) ldelnewline();
	curwp->w_doto = opos;			/* and dot is right	*/
#ifdef	BACKUP
	if (newname != NULL)
		bp->b_flag |= BFCHG | BFBAK;	/* Need a backup.	*/
	else	bp->b_flag |= BFCHG;
#else
	bp->b_flag |= BFCHG;
#endif
	/* if the insert was at the end of buffer, set lp1 to the end of
	 * buffer line, and lp2 to the beginning of the newly inserted
	 * text.  (Otherwise lp2 is set to NULL.)  This is 
	 * used below to set pointers in other windows correctly if they
	 * are also at the end of buffer.
	 */
	lp1 = bp->b_linep;
	if (curwp->w_markp == lp1)
		lp2 = curwp->w_dotp;
	else {
out:		lp2 = NULL;
	}
	for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
		if (wp->w_bufp == curbp) {
			wp->w_flag |= WFMODE|WFEDIT;
			if (wp != curwp && lp2 != NULL) {
				if (wp->w_dotp == lp1)
					wp->w_dotp = lp2;
				if (wp->w_markp == lp1)
					wp->w_markp = lp2;
				if (wp->w_linep == lp1)
					wp->w_linep = lp2;
			}
		}
	}
	if (s == FIOERR)			/* False if error.	*/
		return (FALSE);
	return (TRUE);
}

/*
 * Take a file name, and from it
 * fabricate a buffer name. This routine knows
 * about the syntax of file names on the target system.
 * BDC1		left scan delimiter.
 * BDC2		optional second left scan delimiter.
 * BDC3		optional right scan delimiter.
 */
makename(bname, fname) char bname[]; char fname[]; {
	register char	*cp1;
	register char	*cp2;

	cp1 = &fname[0];
	while (*cp1 != 0)
		++cp1;
#ifdef	BDC2
	while (cp1!=&fname[0] && cp1[-1]!=BDC1 && cp1[-1]!=BDC2)
		--cp1;
#else
	while (cp1!=&fname[0] && cp1[-1]!=BDC1)
		--cp1;
#endif
	cp2 = &bname[0];
#ifdef	BDC3
	while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=BDC3)
		*cp2++ = *cp1++;
#else
	while (cp2!=&bname[NBUFN-1] && *cp1!=0)
		*cp2++ = *cp1++;
#endif
	*cp2 = 0;
}

/*
 * Ask for a file name, and write the
 * contents of the current buffer to that file.
 * Update the remembered file name and clear the
 * buffer changed flag. This handling of file names
 * is different from the earlier versions, and
 * is more compatable with Gosling EMACS than
 * with ITS EMACS.
 */
/*ARGSUSED*/
filewrite(f, n, k) {
	register int	s;
	char		fname[NFILEN];

	if ((s=ereply("Write file: ", fname, NFILEN)) != TRUE)
		return (s);
	adjustcase(fname);
	if ((s=writeout(curbp, fname)) == TRUE) {
		(VOID) strcpy(curbp->b_fname, fname);
#ifdef	BACKUP
		curbp->b_flag &= ~(BFBAK | BFCHG);
#else
		curbp->b_flag &= ~BFCHG;
#endif
		upmodes(curbp);
	}
	return (s);
}

/*
 * Save the contents of the current buffer back into
 * its associated file. Do nothing if there have been no changes
 * (is this a bug, or a feature). Error if there is no remembered
 * file name. If this is the first write since the read or visit,
 * then a backup copy of the file is made.
 */
/*ARGSUSED*/
filesave(f, n, k) {
	register int	s;

	if ((curbp->b_flag&BFCHG) == 0)	{	/* Return, no changes.	*/
		if (kbdmop == NULL) ewprintf("(No changes need to be saved)");
		return (TRUE);
	}
	if (curbp->b_fname[0] == 0) {		/* Must have a name.	*/
		ewprintf("No file name");
		return (FALSE);
	}
#ifdef	BACKUP
	if ((curbp->b_flag&BFBAK) != 0) {
		s = fbackupfile(curbp->b_fname);
		if (s == ABORT)			/* Hard error.		*/
			return FALSE;
		if (s == FALSE			/* Softer error.	*/
		&& (s=eyesno("Backup error, save anyway")) != TRUE)
			return (s);
	}
#endif
	if ((s=writeout(curbp, curbp->b_fname)) == TRUE) {
#ifdef	BACKUP
		curbp->b_flag &= ~(BFCHG | BFBAK);
#else
		curbp->b_flag &= ~BFCHG;
#endif
		upmodes(curbp);
	}
	return (s);
}

/*
 * This function performs the details of file
 * writing; writing the file in buffer bp to
 * file fn. Uses the file management routines
 * in the "fileio.c" package. Most of the grief
 * is checking of some sort.
 */
writeout(bp, fn) register BUFFER *bp; char *fn; {
	register int	s;
	register LINE	*lp;

	if ((s=ffwopen(fn)) != FIOSUC)		/* Open writes message.	*/
		return (FALSE);
	lp = lforw(bp->b_linep);		/* First line.		*/
	while (lp != bp->b_linep) {
		if ((s=ffputline(&(ltext(lp))[0], llength(lp))) != FIOSUC)
			break;
		lp = lforw(lp);
	}
	if (s == FIOSUC) {			/* No write error.	*/
		s = ffclose();
		if (s==FIOSUC && kbdmop==NULL)
			ewprintf("Wrote %s", fn);
	} else					/* Ignore close error	*/
		(VOID) ffclose();		/* if a write error.	*/
	if (s != FIOSUC)			/* Some sort of error.	*/
		return (FALSE);
	return (TRUE);
}

/*
 * Tag all windows for bp (all windows if bp NULL) as needing their
 * mode line updated.
 */
upmodes(bp) register BUFFER *bp; {
	register WINDOW	*wp;

	for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
		if (bp == NULL || curwp->w_bufp == bp) wp->w_flag |= WFMODE;
}

SHAR_EOF
fi # end of overwriting check
if test -f 'match.c'
then
	echo shar: will not over-write existing file "'match.c'"
else
cat << \SHAR_EOF > 'match.c'
/*
 * Name:	MicroEMACS
 * 		Limited parenthesis matching routines
 * Version:	Gnu30
 * Last edit:	13-Jul-86
 * Created:	19-May-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic
 *
 * The hacks in this file implement automatic matching
 * of (), [], {}, and other characters.  It would be
 * better to have a full-blown syntax table, but there's
 * enough overhead in the editor as it is.
 *
 * Since I often edit Scribe code, I've made it possible to
 * blink arbitrary characters -- just bind delimiter characters
 * to "blink-matching-paren-hack"
 */
#include	"def.h"

/* Balance table. When balance() encounters a character
 * that is to be matched, it first searches this table
 * for a balancing left-side character.  If the character
 * is not in the table, the character is balanced by itself.
 * This is to allow delimiters in Scribe documents to be matched.
 */	

static struct balance {
	char left, right;
} bal[] = {
	{ '(', ')' },
	{ '[', ']' },
	{ '{', '}' },
	{ '<', '>' },
	{ '\0','\0'}
};

/*
 * Fake the GNU "blink-matching-paren" variable.
 * If the argument exists, nonzero means show,
 * zero means don't.  If it doesn't exist,
 * pretend it's nonzero.
 */

blinkparen(f, n, k)
{
	register char	*command;
	register SYMBOL	*sp;

	if (f == FALSE)
		n = 1;
	command = (n == 0) ? "self-insert-command" :
			     "blink-matching-paren-hack";
	if ((sp=symlookup(command)) == NULL) {
		ewprintf("blinkparen: no binding for %s",command);
		return (FALSE);
	}
	binding[(KEY) ')'] = sp;		/* rebind paren		*/
	return (TRUE);	
}

/*
 * Self-insert character, then show matching character,
 * if any.  Bound to "blink-matching-paren-command".
 */

showmatch(f, n, k)
{
	register int  i, s;

	if (k == KRANDOM)
		return(FALSE);
	for (i = 0; i < n; i++) {
		if ((s = selfinsert(f, 1, k)) != TRUE)
			return(s);
		if (balance(k) != TRUE)	/* unbalanced -- warn user */
			ttbeep();
	}
	return (TRUE);
}

/*
 * Search for and display a matching character.
 *
 * This routine does the real work of searching backward
 * for a balancing character.  If such a balancing character
 * is found, it uses displaymatch() to display the match.
 */

static balance(k)
int k;
{
	register LINE	*clp;
	register int	cbo;
	int	c;
	int	i;
	int	rbal, lbal;
	int	depth;

	rbal = k & KCHAR;
	if ((k&KCTRL)!=0 && rbal>='@' && rbal<='_') /* ASCII-ify.	*/
		rbal -= '@';

	/* See if there is a matching character -- default to the same */

	lbal = rbal;
	for (i = 0; bal[i].right != '\0'; i++)
		if (bal[i].right == rbal) {
			lbal = bal[i].left;
			break;
		}

	/* Move behind the inserted character.  We are always guaranteed    */
	/* that there is at least one character on the line, since one was  */
	/* just self-inserted by blinkparen.				    */

	clp = curwp->w_dotp;
	cbo = curwp->w_doto - 1;

	depth = 0;			/* init nesting depth		*/

	for (;;) {
		if (cbo == 0) {			/* beginning of line	*/
			clp = lback(clp);
			if (clp == curbp->b_linep)
				return (FALSE);
			cbo = llength(clp)+1;
		}
		if (--cbo == llength(clp))	/* end of line		*/
			c = '\n';
		else
			c = lgetc(clp,cbo);	/* somewhere in middle	*/

		/* Check for a matching character.  If still in a nested */
		/* level, pop out of it and continue search.  This check */
		/* is done before the nesting check so single-character	 */
		/* matches will work too.				 */
		if (c == lbal) {
			if (depth == 0) {
				displaymatch(clp,cbo);
				return (TRUE);
			}
			else
				depth--;
		}
		/* Check for another level of nesting.  */
		if (c == rbal)
			depth++;
	}
}


/*
 * Display matching character.
 * Matching characters that are not in the current window
 * are displayed in the echo line. If in the current
 * window, move dot to the matching character,
 * sit there a while, then move back.
 */

static displaymatch(clp, cbo)
register LINE *clp;
register int  cbo;
{
	register LINE	*tlp;
	register int	tbo;
	register int	cp;
	register int	bufo;
	register int	c;
	int		inwindow;
	char	 	buf[NLINE];

	/* Figure out if matching char is in current window by	*/
	/* searching from the top of the window to dot.		*/

	inwindow = FALSE;
	for (tlp = curwp->w_linep; tlp != lforw(curwp->w_dotp); tlp = lforw(tlp))
		if (tlp == clp)
			inwindow = TRUE;

	if (inwindow == TRUE) {
		tlp = curwp->w_dotp;	/* save current position */
		tbo = curwp->w_doto;

		curwp->w_dotp  = clp;	/* move to new position */
		curwp->w_doto  = cbo;
		curwp->w_flag |= WFMOVE;

		update();		/* show match */
		sleep(1);		/* wait a bit */

		curwp->w_dotp   = tlp;	/* return to old position */
		curwp->w_doto   = tbo;
		curwp->w_flag  |= WFMOVE;
		update();
	}
	else {	/* match not in this window so display line in echo area */
		bufo = 0;
		for (cp = 0; cp < llength(clp); cp++) {	/* expand tabs	*/
			c = lgetc(clp,cp);
			if (c != '\t')
				buf[bufo++] = c;
			else
				do {
					buf[bufo++] = ' ';
				} while (bufo & 7);
		}
		buf[bufo++] = '\0';
		ewprintf("Matches %s",buf);
	}
	return (TRUE);
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0



More information about the Mod.sources mailing list