v08i011: A Micro-Emacs variant that resembles GNU Emacs

sources-request at mirror.UUCP sources-request at mirror.UUCP
Wed Jan 28 04:18:48 AEST 1987


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


#! /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:
#	search.c
#	symbol.c
#	window.c
#	tty/termcap/tty.c
#	tty/termcap/readme
#	tty/termcap/ttydef.h
# This archive created: Sat Nov 15 15:06:50 1986
export PATH; PATH=/bin:$PATH
if test ! -d sys
then
mkdir sys
fi
if test ! -d tty
then
mkdir tty
fi
if test ! -d tty/termcap
then
mkdir tty/termcap
fi
if test -f 'search.c'
then
	echo shar: will not over-write existing file "'search.c'"
else
cat << \SHAR_EOF > 'search.c'
/*
 * 		Search commands.
 * The functions in this file implement the
 * search commands (both plain and incremental searches
 * are supported) and the query-replace command.
 *
 * The plain old search code is part of the original
 * MicroEMACS "distribution". The incremental search code,
 * and the query-replace code, is by Rich Ellison.
 */
#include	"def.h"

#define SRCH_BEGIN	(0)			/* Search sub-codes.	*/
#define	SRCH_FORW	(-1)
#define SRCH_BACK	(-2)
#define SRCH_NOPR	(-3)
#define SRCH_ACCM	(-4)
#define	SRCH_MARK	(-5)

typedef struct  {
	int	s_code;
	LINE	*s_dotp;
	int	s_doto;
}	SRCHCOM;

static	SRCHCOM	cmds[NSRCH];
static	int	cip;

int	srch_lastdir = SRCH_NOPR;		/* Last search flags.	*/

/*
 * Search forward.
 * Get a search string from the user, and search for it,
 * starting at ".". If found, "." gets moved to just after the
 * matched characters, and display does all the hard stuff.
 * If not found, it just prints a message.
 */
/*ARGSUSED*/
forwsearch(f, n, k) {
	register int	s;

	if ((s=readpattern("Search")) != TRUE)
		return (s);
	if (forwsrch() == FALSE) {
		ewprintf("Search failed: \"%s\"", pat);
		return (FALSE);
	}
	srch_lastdir = SRCH_FORW;
	return (TRUE);
}

/*
 * Reverse search.
 * Get a search string from the  user, and search, starting at "."
 * and proceeding toward the front of the buffer. If found "." is left
 * pointing at the first character of the pattern [the last character that
 * was matched].
 */
/*ARGSUSED*/
backsearch(f, n, k) {
	register int	s;

	if ((s=readpattern("Search backward")) != TRUE)
		return (s);
	if (backsrch() == FALSE) {
		ewprintf("Search failed: \"%s\"", pat);
		return (FALSE);
	}
	srch_lastdir = SRCH_BACK;
	return (TRUE);
}

/* 
 * Search again, using the same search string
 * and direction as the last search command. The direction
 * has been saved in "srch_lastdir", so you know which way
 * to go.
 */
/*ARGSUSED*/
searchagain(f, n, k) {
	if (srch_lastdir == SRCH_FORW) {
		if (forwsrch() == FALSE) {
			ewprintf("Search failed: \"%s\"", pat);
			return (FALSE);
		}
		return (TRUE);
	}
	if (srch_lastdir == SRCH_BACK) {
		if (backsrch() == FALSE) {
			ewprintf("Search failed: \"%s\"", pat);
			return (FALSE);
		}
		return (TRUE);
	}
	ewprintf("No last search");
	return (FALSE);
}

/*
 * Use incremental searching, initially in the forward direction.
 * isearch ignores any explicit arguments.
 */
/*ARGSUSED*/
forwisearch(f, n, k) {
	return (isearch(SRCH_FORW));
}

/*
 * Use incremental searching, initially in the reverse direction.
 * isearch ignores any explicit arguments.
 */
/*ARGSUSED*/
backisearch(f, n, k) {
	return (isearch(SRCH_BACK));
}

/*
 * Incremental Search.
 *	dir is used as the initial direction to search.
 *	^S	switch direction to forward
 *	^R	switch direction to reverse
 *	^Q	quote next character (allows searching for ^N etc.)
 *	<ESC>	exit from Isearch
 *	<DEL>	undoes last character typed. (tricky job to do this correctly).
 *	other ^	exit search, don't set mark
 *	else	accumulate into search string
 */
isearch(dir) {
	register int	c;
	register LINE	*clp;
	register int	cbo;
	register int	success;
	int		pptr;
	char		opat[NPAT];

	for (cip=0; cip<NSRCH; cip++)
		cmds[cip].s_code = SRCH_NOPR;
	(VOID) strcpy(opat, pat);
	cip = 0;
	pptr = -1;
	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	is_lpush();
	is_cpush(SRCH_BEGIN);
	success = TRUE;
	is_prompt(dir, TRUE, success);
	for (;;) {
		update();
		switch (c = (char) getkey(KQUOTE)) {
		case METACH:
			srch_lastdir = dir;
			curwp->w_markp = clp;
			curwp->w_marko = cbo;
			if (kbdmop == NULL) ewprintf("Mark set");
			return (TRUE);

		case CCHR('G'):
			if (success != TRUE) {
				while (is_peek() == SRCH_ACCM)
					if (is_undo(&pptr, &dir) == FALSE)
						break;
				success = TRUE;
				is_prompt(dir, pptr < 0, success);
				break;
			}
			curwp->w_dotp = clp;
			curwp->w_doto = cbo;
			curwp->w_flag |= WFMOVE;
			srch_lastdir = dir;
			(VOID) ctrlg(FALSE, 0, KRANDOM);
			(VOID) strcpy(pat, opat);
			return ABORT;

		case CCHR('S'):
			if (dir == SRCH_BACK) {
				dir = SRCH_FORW;
				is_lpush();
				is_cpush(SRCH_FORW);
				success = TRUE;
			}
			if (success==FALSE && dir==SRCH_FORW)
				break;
			is_lpush();
			pptr = strlen(pat);
			(VOID) forwchar(FALSE, 1, KRANDOM);
			if (is_find(SRCH_FORW) != FALSE) is_cpush(SRCH_MARK);
			else {
				(VOID) backchar(FALSE, 1, KRANDOM);
				ttbeep();
				success = FALSE;
			}
			is_prompt(dir, pptr < 0, success);
			break;

		case CCHR('R'):
			if (dir == SRCH_FORW) {
				dir = SRCH_BACK;
				is_lpush();
				is_cpush(SRCH_BACK);
				success = TRUE;
			}
			if (success==FALSE && dir==SRCH_BACK)
				break;
			is_lpush();
			pptr = strlen(pat);
			(VOID) backchar(FALSE, 1, KRANDOM);
			if (is_find(SRCH_BACK) != FALSE) is_cpush(SRCH_MARK);
			else {
				(VOID) forwchar(FALSE, 1, KRANDOM);
				ttbeep();
				success = FALSE;
			}
			is_prompt(dir, pptr < 0, success);
			break;

		case 0x7F:
			if (is_undo(&pptr, &dir) != TRUE) return FALSE;
			if (is_peek() != SRCH_ACCM) success = TRUE;
			is_prompt(dir, pptr < 0, success);
			break;

		case CCHR('Q'):
			c = (char) getkey(KQUOTE);
			goto  addchar;
		case CCHR('M'):
			c = CCHR('J');
		case CCHR('J'):
			goto  addchar;

		default:
			if (ISCTRL(c) != FALSE) {
				c += '@';
				c |= KCTRL;
				success = execute((KEY) c, FALSE, 1);
				curwp->w_markp = clp;
				curwp->w_marko = cbo;
				if (kbdmop == NULL) ewprintf("Mark set");
				curwp->w_flag |= WFMOVE;
				return (success);
			}				
		addchar:
			if (pptr == -1)
				pptr = 0;
			if (pptr == 0)
				success = TRUE;
			pat[pptr++] = c;
			if (pptr == NPAT) {
				ewprintf("Pattern too long");
				return FALSE;
			}
			pat[pptr] = '\0';
			is_lpush();
			if (success != FALSE) {
				if (is_find(dir) != FALSE)
					is_cpush(c);
				else {
					success = FALSE;
					ttbeep();
					is_cpush(SRCH_ACCM);
				}
			} else
				is_cpush(SRCH_ACCM);
			is_prompt(dir, FALSE, success);
		}
	}
}

is_cpush(cmd) register int cmd; {
	if (++cip >= NSRCH)
		cip = 0;
	cmds[cip].s_code = cmd;
}

is_lpush() {
	register int	ctp;

	ctp = cip+1;
	if (ctp >= NSRCH)
		ctp = 0;
	cmds[ctp].s_code = SRCH_NOPR;
	cmds[ctp].s_doto = curwp->w_doto;
	cmds[ctp].s_dotp = curwp->w_dotp;
}

is_pop() {
	if (cmds[cip].s_code != SRCH_NOPR) {
		curwp->w_doto  = cmds[cip].s_doto; 
		curwp->w_dotp  = cmds[cip].s_dotp;
		curwp->w_flag |= WFMOVE;
		cmds[cip].s_code = SRCH_NOPR;
	}
	if (--cip <= 0)
		cip = NSRCH-1;
}

is_peek() {
	return cmds[cip].s_code;
}

is_undo(pptr, dir) register int *pptr; register int *dir; {
	register int	redo = FALSE ;
	switch (cmds[cip].s_code) {
	case SRCH_BEGIN:
	case SRCH_NOPR:
		*pptr = -1;
	case SRCH_MARK:
		break;

	case SRCH_FORW:
		*dir = SRCH_BACK;
		redo = TRUE;
		break;

	case SRCH_BACK:
		*dir = SRCH_FORW;
		redo = TRUE;
		break;

	case SRCH_ACCM:
	default:
		*pptr -= 1;
		if (*pptr < 0)
			*pptr = 0;
		pat[*pptr] = '\0';
		break;
	}
	is_pop();
	if (redo) return is_undo(pptr, dir);
	return (TRUE);
}

is_find(dir) register int dir; {
	register int	plen, odoto;
	register LINE	*odotp ;

	odoto = curwp->w_doto;
	odotp = curwp->w_dotp;
	plen = strlen(pat);
	if (plen != 0) {
		if (dir==SRCH_FORW) {
			(VOID) backchar(TRUE, plen, KRANDOM);
			if (forwsrch() == FALSE) {
				curwp->w_doto = odoto;
				curwp->w_dotp = odotp;
				return (FALSE);
			}
			return (TRUE);
		}
		if (dir==SRCH_BACK) {
			(VOID) forwchar(TRUE, plen, KRANDOM);
			if (backsrch() == FALSE) {
				curwp->w_doto = odoto;
				curwp->w_dotp = odotp;
				return (FALSE);
			}
			return (TRUE);
		}
		ewprintf("bad call to is_find");
		return FALSE;
	}
	return (FALSE);
}

/*
 * If called with "dir" not one of SRCH_FORW
 * or SRCH_BACK, this routine used to print an error
 * message. It also used to return TRUE or FALSE,
 * depending on if it liked the "dir". However, none
 * of the callers looked at the status, so I just
 * made the checking vanish.
 */
is_prompt(dir, flag, success) {
	VOID is_dspl();

	if (dir == SRCH_FORW) {
		if (success != FALSE)
			is_dspl("I-search", flag);
		else
			is_dspl("Failing I-search", flag);
	} else if (dir == SRCH_BACK) {
		if (success != FALSE)
			is_dspl("I-search backward", flag);
		else
			is_dspl("Failing I-search backward", flag);
	} else ewprintf("Broken call to is_prompt");
}

/*
 * Prompt writing routine for the incremental search. 
 * The "prompt" is just a string. The "flag" determines
 * whether pat should be printed.
 */
VOID
is_dspl(prompt, flag) char *prompt; {

	if (kbdmop != NULL) return;
	if (flag != FALSE)
		ewprintf("%s: ", prompt);
	else
		ewprintf("%s: %s", prompt, pat);
}

/*
 * Query Replace.
 *	Replace strings selectively.  Does a search and replace operation.
 */
/*ARGSUSED*/
queryrepl(f, n, k) {
	register int	s;
	register int	rcnt = 0;	/* Replacements made so far	*/
	register int	plen;		/* length of found string	*/
	char		news[NPAT];	/* replacement string		*/

	if ((s=readpattern("Query replace")) != TRUE)
		return (s);
	if ((s=ereply("Query replace %s with: ",news, NPAT, pat)) == ABORT)
		return (s);
	if (s == FALSE)
		news[0] = '\0';
	if (kbdmop == NULL) ewprintf("Query replacing %s with %s:", pat, news);
	plen = strlen(pat);

	/*
	 * Search forward repeatedly, checking each time whether to insert
	 * or not.  The "!" case makes the check always true, so it gets put
	 * into a tighter loop for efficiency.
	 */

	while (forwsrch() == TRUE) {
	retry:
		update();
		switch (getkey(KQUOTE)) {
		case ' ':
			if (lreplace((RSIZE) plen, news, f) == FALSE)
				return (FALSE);
			rcnt++;
			break;

		case '.':
			if (lreplace((RSIZE) plen, news, f) == FALSE)
				return (FALSE);
			rcnt++;
			goto stopsearch;

		case CCHR('G'):	/* ^G or ESC */
			(VOID) ctrlg(FALSE, 0, KRANDOM);
		case 033:
			goto stopsearch;

		case '!':
			do {
				if (lreplace((RSIZE) plen, news, f) == FALSE)
					return (FALSE);
				rcnt++;
			} while (forwsrch() == TRUE);
			goto stopsearch;

		case 0x7F:		/* To not replace */
			break;

		default:
ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
			goto retry;
		}
	}
stopsearch:
	curwp->w_flag |= WFHARD;
	update();
	if (kbdmop == NULL) {
		if (rcnt == 0)
			ewprintf("(No replacements done)");
		else if (rcnt == 1)
			ewprintf("(1 replacement done)");
		else
			ewprintf("(%d replacements done)", rcnt);
	}
	return TRUE;
}

/*
 * This routine does the real work of a
 * forward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
forwsrch() {
	register LINE	*clp;
	register int	cbo;
	register LINE	*tlp;
	register int	tbo;
	register char	*pp;
	register int	c;

	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	while (clp != curbp->b_linep) {
		if (cbo == llength(clp)) {
			clp = lforw(clp);
			cbo = 0;
			c = SEOL;
		} else
			c = lgetc(clp, cbo++);
		if (eq(c, pat[0]) != FALSE) {
			tlp = clp;
			tbo = cbo;
			pp  = &pat[1];
			while (*pp != 0) {
				if (tlp == curbp->b_linep)
					goto fail;
				if (tbo == llength(tlp)) {
					tlp = lforw(tlp);
					if (tlp == curbp->b_linep)
						goto fail;
					tbo = 0;
					c = SEOL;
				} else
					c = lgetc(tlp, tbo++);
				if (eq(c, *pp++) == FALSE)
					goto fail;
			}
			curwp->w_dotp  = tlp;
			curwp->w_doto  = tbo;
			curwp->w_flag |= WFMOVE;
			return (TRUE);
		}
	fail:	;
	}
	return (FALSE);
}

/*
 * This routine does the real work of a
 * backward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
backsrch() {
	register LINE	*clp;
	register int	cbo;
	register LINE	*tlp;
	register int	tbo;
	register int	c;
	register char	*epp;
	register char	*pp;

	for (epp = &pat[0]; epp[1] != 0; ++epp)
		;
	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	for (;;) {
		if (cbo == 0) {
			clp = lback(clp);
			if (clp == curbp->b_linep)
				return (FALSE);
			cbo = llength(clp)+1;
		}
		if (--cbo == llength(clp))
			c = SEOL;
		else
			c = lgetc(clp,cbo);
		if (eq(c, *epp) != FALSE) {
			tlp = clp;
			tbo = cbo;
			pp  = epp;
			while (pp != &pat[0]) {
				if (tbo == 0) {
					tlp = lback(tlp);
					if (tlp == curbp->b_linep)
						goto fail;
					tbo = llength(tlp)+1;
				}
				if (--tbo == llength(tlp))
					c = SEOL;
				else
					c = lgetc(tlp,tbo);
				if (eq(c, *--pp) == FALSE)
					goto fail;
			}
			curwp->w_dotp  = tlp;
			curwp->w_doto  = tbo;
			curwp->w_flag |= WFMOVE;
			return (TRUE);
		}
	fail:	;
	}
}

/*
 * Compare two characters.
 * The "bc" comes from the buffer.
 * It has its case folded out. The
 * "pc" is from the pattern.
 */
eq(bc, pc) {
	register int	ibc;
	register int	ipc;

	ibc = bc & 0xFF;
	ipc = pc & 0xFF;
	if (ISLOWER(ibc) != FALSE)
		ibc = TOUPPER(ibc);
	if (ISLOWER(ipc) != FALSE)
		ipc = TOUPPER(ipc);
	if (ibc == ipc)
		return (TRUE);
	return (FALSE);
}

/*
 * Read a pattern.
 * Stash it in the external variable "pat". The "pat" is
 * not updated if the user types in an empty line. If the user typed
 * an empty line, and there is no old pattern, it is an error.
 * Display the old pattern, in the style of Jeff Lomicka. There is
 * some do-it-yourself control expansion.
 */
readpattern(prompt) char *prompt; {
	register int	s;
	char		tpat[NPAT];

	if (tpat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
	else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);

	if (s == TRUE)				/* Specified		*/
		(VOID) strcpy(pat, tpat);
	else if (s==FALSE && pat[0]!=0)		/* CR, but old one	*/
		s = TRUE;
	return (s);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'symbol.c'
then
	echo shar: will not over-write existing file "'symbol.c'"
else
cat << \SHAR_EOF > 'symbol.c'
/*
 * Symbol tables, and keymap setup.
 * The terminal specific parts of building the
 * keymap has been moved to a better place.
 */
#include	"def.h"

#ifdef	HASH
Since you're seeing this, you must have defined HASH to try and get the
hashing code back. You're getting an error because I (mwm at ucbvax) want you
to read this.

With the change in function completion, there is at least one linear search
through the function list for every hash lookup (ignoring the startup code).
Given that there are probably actually MANY more linear searches for
completion than fullname lookups, some structure other than a hash table is
better suited to this purpose. I suggest trying sorting the lists for more
speed, then going to a binary search tree, and finally going to a trie.
#endif	HASH

/*
 * Defined here so to collect the #ifdef MEYN config stuff in one file
 * If you set either MINDENT or MFILL, then you need to change the bindings
 * in this file to match: KCTRL|'M' -> newline-and-indent and KCTRL|'J' ->
 * insert-newline for MINDENT, and ' ' -> insert-with-wrap for MFILL.
 * MEYN is used for compile-time customization of the system for micros.
 */
#ifndef	MEYN
int	mode = 0;			/* All modes off		*/
#else
int	mode = MBSMAP|MINDENT;
#endif

/*
 * Defined by "main.c".
 */
extern	int	ctrlg();		/* Abort out of things		*/
extern	int	quit();			/* Quit				*/
extern	int	ctlxlp();		/* Begin macro			*/
extern	int	ctlxrp();		/* End macro			*/
extern	int	ctlxe();		/* Execute macro		*/
extern  int	showversion();		/* Show version numbers, etc.	*/

/*
 * Defined by "search.c".
 */
extern	int	forwsearch();		/* Search forward		*/
extern	int	backsearch();		/* Search backwards		*/
extern  int	searchagain();		/* Repeat last search command	*/
extern  int	forwisearch();		/* Incremental search forward	*/
extern  int	backisearch();		/* Incremental search backwards	*/
extern  int	queryrepl();		/* Query replace		*/

/*
 * Defined by "basic.c".
 */
extern	int	gotobol();		/* Move to start of line	*/
extern	int	backchar();		/* Move backward by characters	*/
extern	int	gotoeol();		/* Move to end of line		*/
extern	int	forwchar();		/* Move forward by characters	*/
extern	int	gotobob();		/* Move to start of buffer	*/
extern	int	gotoeob();		/* Move to end of buffer	*/
extern	int	forwline();		/* Move forward by lines	*/
extern	int	backline();		/* Move backward by lines	*/
extern	int	forwpage();		/* Move forward by pages	*/
extern	int	backpage();		/* Move backward by pages	*/
extern	int	pagenext();		/* Page forward next window	*/
extern	int	setmark();		/* Set mark			*/
extern	int	swapmark();		/* Swap "." and mark		*/
extern	int	gotoline();		/* Go to a specified line.	*/

/*
 * Defined by "buffer.c".
 */
extern	int	listbuffers();		/* Display list of buffers	*/
extern	int	usebuffer();		/* Switch a window to a buffer	*/
extern	int	poptobuffer();		/* Other window to a buffer	*/
extern	int	killbuffer();		/* Make a buffer go away.	*/
extern	int	savebuffers();		/* Save unmodified buffers	*/
extern	int	bufferinsert();		/* Insert buffer into another	*/
extern	int	notmodified();		/* Reset modification flag	*/

#ifdef	DIRLIST
/*
 * Defined by "dirlist.c".
 */
extern	int	dirlist();		/* Directory list.		*/
#endif

/*
 * Defined by "file.c".
 */
extern	int	filevisit();		/* Get a file, read write	*/
extern	int	poptofile();		/* Get a file, other window	*/
extern	int	filewrite();		/* Write a file			*/
extern	int	filesave();		/* Save current file		*/
extern	int	fileinsert();		/* Insert file into buffer	*/

/*
 * Defined by "match.c"
 */
extern	int	blinkparen();		/* Fake blink-matching-paren var */
extern	int	showmatch();		/* Hack to show matching paren	 */

/*
 * Defined by "random.c".
 */
extern	int	selfinsert();		/* Insert character		*/
extern	int	showcpos();		/* Show the cursor position	*/
extern	int	twiddle();		/* Twiddle characters		*/
extern	int	quote();		/* Insert literal		*/
extern	int	openline();		/* Open up a blank line		*/
extern	int	newline();		/* Insert CR-LF			*/
extern	int	deblank();		/* Delete blank lines		*/
extern	int	delwhite();		/* Delete extra whitespace	*/
extern	int	indent();		/* Insert CR-LF, then indent	*/
extern	int	forwdel();		/* Forward delete		*/
extern	int	backdel();		/* Backward delete in		*/
extern	int	killline();		/* Kill forward			*/
extern	int	yank();			/* Yank back from killbuffer.	*/
extern	int	bsmapmode();		/* set bsmap mode		*/
extern	int	flowmode();		/* set flow mode		*/
extern	int	indentmode();		/* set auto-indent mode		*/
extern	int	fillmode();		/* set word-wrap mode		*/

/*
 * Defined by "region.c".
 */
extern	int	killregion();		/* Kill region.			*/
extern	int	copyregion();		/* Copy region to kill buffer.	*/
extern	int	lowerregion();		/* Lower case region.		*/
extern	int	upperregion();		/* Upper case region.		*/
#ifdef	PREFIXREGION
extern	int	prefixregion();		/* Prefix all lines in region	*/
extern	int	setprefix();		/* Set line prefix string	*/
#endif

/*
 * Defined by "spawn.c".
 */
extern	int	spawncli();		/* Run CLI in a subjob.		*/
#ifdef	VMS
extern	int	attachtoparent();	/* Attach to parent process	*/
#endif

/*
 * Defined by "window.c".
 */
extern	int	reposition();		/* Reposition window		*/
extern	int	refresh();		/* Refresh the screen		*/
extern	int	nextwind();		/* Move to the next window	*/
extern  int	prevwind();		/* Move to the previous window	*/
extern	int	onlywind();		/* Make current window only one	*/
extern	int	splitwind();		/* Split current window		*/
extern	int	delwind();		/* Delete current window	*/
extern	int	enlargewind();		/* Enlarge display window.	*/
extern	int	shrinkwind();		/* Shrink window.		*/

/*
 * Defined by "word.c".
 */
extern	int	backword();		/* Backup by words		*/
extern	int	forwword();		/* Advance by words		*/
extern	int	upperword();		/* Upper case word.		*/
extern	int	lowerword();		/* Lower case word.		*/
extern	int	capword();		/* Initial capitalize word.	*/
extern	int	delfword();		/* Delete forward word.		*/
extern	int	delbword();		/* Delete backward word.	*/

/*
 * Defined by "extend.c".
 */
extern	int	extend();		/* Extended commands.		*/
extern	int	desckey();		/* Help key.			*/
extern	int	bindtokey();		/* Modify key bindings.		*/
extern	int	unsetkey();		/* Unbind a key.		*/
extern	int	wallchart();		/* Make wall chart.		*/
#ifdef	STARTUP
extern	int	evalexpr();		/* Extended commands (again)	*/
extern	int	evalbuffer();		/* Evaluate current buffer	*/
extern	int	evalfile();		/* Evaluate a file		*/
#endif

/*
 * defined by "paragraph.c" - the paragraph justification code.
 */
extern	int	gotobop();		/* Move to start of paragraph.	*/
extern	int	gotoeop();		/* Move to end of paragraph.	*/
extern	int	fillpara();		/* Justify a paragraph.		*/
extern	int	killpara();		/* Delete a paragraph.		*/
extern	int	setfillcol();		/* Set fill column for justify.	*/
extern	int	fillword();		/* Insert char with word wrap.	*/

/*
 * defined by prefix.c
 */
extern	int	help();			/* Parse help key.		*/
extern	int	ctlx4hack();		/* Parse a pop-to key.		*/

typedef	struct	{
	KEY	k_key;			/* Key to bind.			*/
	int	(*k_funcp)();		/* Function.			*/
	char	*k_name;		/* Function name string.	*/
}	KEYTAB;

/*
 * Default key binding table. This contains
 * the function names, the symbol table name, and (possibly)
 * a key binding for the builtin functions. There are no
 * bindings for C-U or C-X. These are done with special
 * code, but should be done normally.
 */
KEYTAB	key[] = {
#ifdef	MEYN		/* Add meyer's peculiar bindings */
	KCTRL|'J',	newline,	"insert-newline",
	KCTRL|'M',	indent,		"newline-and-indent",
	KCTLX|'N',	nextwind, 	"next-window",
	KCTLX|'P',	prevwind,	"previous-window",
	KMETA|KCTRL|'C',quit,		"save-buffers-kill-emacs",
	KMETA|KCTRL|'L',refresh,	"redraw-display",
	KMETA|'G',	gotoline,	"goto-line",
	KMETA|'J',	fillpara,	"fill-paragraph",
	KMETA|'Q',	queryrepl,	"query-replace",
#endif
	KCTRL|'@',	setmark,	"set-mark-command",
	KCTRL|'A',	gotobol,	"beginning-of-line",
	KCTRL|'B',	backchar,	"backward-char",
	KCTRL|'D',	forwdel,	"delete-char",
	KCTRL|'E',	gotoeol,	"end-of-line",
	KCTRL|'F',	forwchar,	"forward-char",
	KCTRL|'G',	ctrlg,		"keyboard-quit",
	KCTRL|'H',	help,		"help",
	KCTRL|'I',	selfinsert,	"self-insert-command",
#ifndef	MEYN
	KCTRL|'J',	indent,		"newline-and-indent",
#endif
	KCTRL|'L',	reposition,	"recenter",
	KCTRL|'K',	killline,	"kill-line",
#ifndef	MEYN
	KCTRL|'M',	newline,	"insert-newline",
#endif
	KCTRL|'N',	forwline,	"next-line",
	KCTRL|'O',	openline,	"open-line",
	KCTRL|'P',	backline,	"previous-line",
	KCTRL|'Q',	quote,		"quoted-insert",
	KCTRL|'R',	backisearch,	"isearch-backward",
	KCTRL|'S',	forwisearch,	"isearch-forward",
	KCTRL|'T',	twiddle,	"transpose-chars",
	KCTRL|'V',	forwpage,	"scroll-up",
	KCTRL|'W',	killregion,	"kill-region",
	KCTRL|'Y',	yank,		"yank",
#ifdef	VMS
	KCTRL|'Z',	attachtoparent,	"suspend-emacs",
#else
	KCTRL|'Z',	spawncli,	"suspend-emacs",
#endif
	KCTLX|KCTRL|'B',listbuffers,	"list-buffers",
#ifndef	MEYN
	KCTLX|KCTRL|'C',quit,		"save-buffers-kill-emacs",
#endif
#ifdef	DIRLIST
	KCTLX|KCTRL|'D',dirlist,	"display-directory",
#endif
	KCTLX|KCTRL|'F',filevisit,	"find-file",
	KCTLX|KCTRL|'L',lowerregion,	"downcase-region",
	KCTLX|KCTRL|'O',deblank,	"delete-blank-lines",
	KCTLX|KCTRL|'S',filesave,	"save-buffer",
	KCTLX|KCTRL|'U',upperregion,	"upcase-region",
	KCTLX|KCTRL|'W',filewrite,	"write-file",
	KCTLX|KCTRL|'X',swapmark,	"exchange-point-and-mark",
	KCTLX|'=',	showcpos,	"what-cursor-position",
	KCTLX|'(',	ctlxlp,		"start-kbd-macro",
	KCTLX|')',	ctlxrp,		"end-kbd-macro",
	KCTLX|'^',	enlargewind,	"enlarge-window",
	KCTLX|'0',	delwind,	"delete-window",
	KCTLX|'1',	onlywind,	"delete-other-windows",
	KCTLX|'2',	splitwind,	"split-window-vertically",
	KCTLX|'4',	ctlx4hack,	"ctrlx-four-hack",
	KCTLX|'B',	usebuffer,	"switch-to-buffer",
	KCTLX|'E',	ctlxe,		"call-last-kbd-macro",
	KCTLX|'F',	setfillcol,	"set-fill-column",
	KCTLX|'I',	fileinsert,	"insert-file",
	KCTLX|'K',	killbuffer,	"kill-buffer",
	KCTLX|'S',	savebuffers,	"save-some-buffers",
#ifndef	MEYN
	KCTLX|'O',	nextwind,	"next-window",
	KMETA|'%',	queryrepl,	"query-replace",
#endif
	KMETA|KCTRL|'V',pagenext,	"scroll-other-window",
	KMETA|'>',	gotoeob,	"end-of-buffer",
	KMETA|'<',	gotobob,	"beginning-of-buffer",
	KMETA|'[',	gotobop,	"backward-paragraph",
	KMETA|']',	gotoeop,	"forward-paragraph",
	KMETA|' ',	delwhite,	"just-one-space",
	KMETA|'B',	backword,	"backward-word",
	KMETA|'C',	capword,	"capitalize-word",
	KMETA|'D',	delfword,	"kill-word",
	KMETA|'F',	forwword,	"forward-word",
	KMETA|'L',	lowerword,	"downcase-word",
#ifndef	MEYN
	KMETA|'Q',	fillpara,	"fill-paragraph",
#endif
	KMETA|'R',	backsearch,	"search-backward",
	KMETA|'S',	forwsearch,	"search-forward",
	KMETA|'U',	upperword,	"upcase-word",
	KMETA|'V',	backpage,	"scroll-down",
	KMETA|'W',	copyregion,	"copy-region-as-kill",
	KMETA|'X',	extend,		"execute-extended-command",
	KMETA|'~',	notmodified,	"not-modified",
#ifndef	MEYN
	-1,		prevwind,	"previous-window",
	-1,		refresh,	"redraw-display",
	-1,		gotoline,	"goto-line",
#endif
#ifdef	STARTUP
	-1,		evalexpr,	"eval-expression",
	-1,		evalbuffer,	"eval-current-buffer",
	-1,		evalfile,	"load",
#endif
	-1,		bsmapmode,	"bsmap-mode",
	-1,		flowmode,	"flow-mode",
	-1,		indentmode,	"auto-indent-mode",
	-1,		fillmode,	"auto-fill-mode",
	-1,		fillword,	"insert-with-wrap",
	-1,		shrinkwind,	"shrink-window",
	-1,		searchagain,	"search-again",
	-1,		unsetkey,	"global-unset-key",
	-1,		bindtokey,	"global-set-key",
	-1,		killpara,	"kill-paragraph",
	-1,		showversion,	"emacs-version",
	-1,		blinkparen,	"blink-matching-paren",
	-1,		showmatch,	"blink-matching-paren-hack",
	-1,		bufferinsert,	"insert-buffer",
#ifdef	VMS
	-1,		spawncli,	"push-to-dcl",
#endif
#ifdef	PREFIXREGION
	-1,		prefixregion,	"prefix-region",
	-1,		setprefix,	"set-prefix-string",
#endif
	/* You can actually get to these with keystrokes. See prefix.c */
	-1,		poptobuffer,	"switch-to-buffer-other-window",
	-1,		poptofile,	"find-file-other-window",
	-1,		desckey,	"describe-key-briefly",
	-1,		wallchart,	"describe-bindings",
};

#define	NKEY	(sizeof(key) / sizeof(key[0]))

/*
 * Symbol table lookup.
 * Return a pointer to the SYMBOL node, or NULL if
 * the symbol is not found.
 */
SYMBOL	*
symlookup(cp) register char *cp; {
	register SYMBOL	*sp;

#ifdef	HASH
	sp = symbol[symhash(cp)];
#else
	sp = symbol[0];
#endif
	while (sp != NULL) {
		if (strcmp(cp, sp->s_name) == 0)
			return (sp);
#ifdef	HASH
		if ((sp->s_flags&SFEND) != 0) break;
#endif
		sp = sp->s_symp;
	}
	return (NULL);
}

#ifdef	HASH
/*
 * Take a string, and compute the symbol table
 * bucket number. This is done by adding all of the characters
 * together, and taking the sum mod NSHASH. The string probably
 * should not contain any GR characters; if it does the "*cp"
 * may get a nagative number on some machines, and the "%"
 * will return a negative number!
 */
symhash(cp) register char *cp; {
	register int	c;
	register int	n;

	n = 0;
	while ((c = *cp++) != 0)
		n += c;
	return (n % NSHASH);
}
#endif

/*
 * Build initial keymap. The funny keys
 * (commands, odd control characters) are mapped using
 * a big table and calls to "keyadd". The printing characters
 * are done with some do-it-yourself handwaving. The terminal
 * specific keymap initialization code is called at the
 * very end to finish up. All errors are fatal.
 */
keymapinit() {
	register SYMBOL	*sp;
	register KEYTAB	*kp;
	register int	i;

	for (i=0; i<NKEYS; ++i)
		binding[i] = NULL;
	for (kp = &key[0]; kp < &key[NKEY]; ++kp)
		keyadd(kp->k_key, kp->k_funcp, kp->k_name);
	keydup((KEY) (KCTLX|KCTRL|'G'),	"keyboard-quit");
	keydup((KEY) (KMETA|KCTRL|'G'),	"keyboard-quit");
	keyadd((KEY) (KMETA|0x7F), delbword,
				"backward-kill-word");
	keyadd((KEY) 0x7F, backdel,	"backward-delete-char");
	/*
	 * add duplicates (GNU-stuff)
	 */
	keydup((KEY) (KCTLX|KCTRL|'Z'), "suspend-emacs");
	/*
	 * Should be bound by "tab" already.
	 */
	if ((sp=symlookup("self-insert-command")) == NULL)
		panic("no self-insert-command in keymapinit");
	for (i=0x20; i<0x7F; ++i) {
		if (binding[i] != NULL)
			panic("nonull binding in keymapinit");
		binding[i] = sp;
	}
	ttykeymapinit();
#ifdef	HASH
	/* Link up the symbol table entries	*/
	for (sp = symbol[i = 0]; i < NSHASH-1; sp = sp->s_symp)
		if (sp->s_symp == NULL) sp->s_symp = symbol[++i];
#endif			
}

/*
 * Create a new builtin function "name"
 * with function "funcp". If the "new" is a real
 * key, bind it as a side effect. All errors
 * are fatal.
 */
keyadd(new, funcp, name) register KEY new; int (*funcp)(); char *name; {
	register SYMBOL	*sp;
#ifdef	HASH
	register int	hash;
#endif

	if ((sp=(SYMBOL *)malloc(sizeof(SYMBOL))) == NULL)
		panic("No memory");
#ifdef	HASH
	hash = symhash(name);
	if (symbol[hash] == NULL) sp->s_flags |= SFEND;
	sp->s_symp = symbol[hash];
	symbol[hash] = sp;
#else
	sp->s_symp = symbol[0];
        symbol[0] = sp;
#endif
	sp->s_name = name;
	sp->s_funcp = funcp;
	if (new >= 0) {				/* Bind this key.	*/
		if (binding[new] != NULL)
			panic("rebinding old symbol");
		binding[new] = sp;
	}
}

/*
 * Bind key "new" to the existing
 * routine "name". If the name cannot be found,
 * or the key is already bound, abort.
 */
keydup(new, name) register KEY new; char *name; {
	register SYMBOL	*sp;

	if (binding[new]!=NULL || (sp=symlookup(name))==NULL) {
#ifdef	KEYDUP_ERROR
		fprintf (stderr, "keydup: binding[%d] = %x",
				new, binding[new]);
		fprintf (stderr, " and symlookup(%s) == %x\n", name, sp);
#endif
		panic("keydup");
	}
	binding[new] = sp;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'window.c'
then
	echo shar: will not over-write existing file "'window.c'"
else
cat << \SHAR_EOF > 'window.c'
/*
 *		Window handling.
 */
#include	"def.h"

/*
 * Reposition dot in the current
 * window to line "n". If the argument is
 * positive, it is that line. If it is negative it
 * is that line from the bottom. If it is 0 the window
 * is centered (this is what the standard redisplay code
 * does).  If GOSREC is undefined, default is 0, so it acts like GNU.
 * If GOSREC is defined, with no argument it defaults to 1
 * and works like in Gosling.
 */
/*ARGSUSED*/
reposition(f, n, k) {
#ifndef	GOSREC
	curwp->w_force = ((f == FALSE) ? 0 : n) ;
#else
	curwp->w_force = n;
#endif
	curwp->w_flag |= WFFORCE;
	sgarbf = TRUE;
	return (TRUE);
}

/*
 * Refresh the display. A call is made to the
 * "ttresize" entry in the terminal handler, which tries
 * to reset "nrow" and "ncol". They will, however, never
 * be set outside of the NROW or NCOL range. If the display
 * changed size, arrange that everything is redone, then
 * call "update" to fix the display. We do this so the
 * new size can be displayed. In the normal case the
 * call to "update" in "main.c" refreshes the screen,
 * and all of the windows need not be recomputed.
 * Note that when you get to the "display unusable"
 * message, the screen will be messed up. If you make
 * the window bigger again, and send another command,
 * everything will get fixed!
 */
/*ARGSUSED*/
refresh(f, n, k) {
	register WINDOW	*wp;
	register int	oldnrow;
	register int	oldncol;

	oldnrow = nrow;
	oldncol = ncol;
	ttresize();
	if (nrow!=oldnrow || ncol!=oldncol) {
		wp = wheadp;			/* Find last.		*/
		while (wp->w_wndp != NULL)
			wp = wp->w_wndp;
		if (nrow < wp->w_toprow+3) {	/* Check if too small.	*/
			ewprintf("Display unusable");
			return (FALSE);
		}		
		wp->w_ntrows = nrow-wp->w_toprow-2;
		sgarbf = TRUE;
		update();
		if (kbdmop == NULL) ewprintf("New size %d by %d", nrow, ncol);
	} else
		sgarbf = TRUE;
	return (TRUE);
}

/*
 * The command to make the next
 * window (next => down the screen)
 * the current window. There are no real
 * errors, although the command does
 * nothing if there is only 1 window on
 * the screen.
 */
/*ARGSUSED*/
nextwind(f, n, k) {
	register WINDOW	*wp;

	if ((wp=curwp->w_wndp) == NULL)
		wp = wheadp;
	curwp = wp;
	curbp = wp->w_bufp;
	return (TRUE);
}

/*
 * This command makes the previous
 * window (previous => up the screen) the
 * current window. There arn't any errors,
 * although the command does not do a lot
 * if there is 1 window.
 */
/*ARGSUSED*/
prevwind(f, n, k) {
	register WINDOW	*wp1;
	register WINDOW	*wp2;

	wp1 = wheadp;
	wp2 = curwp;
	if (wp1 == wp2)
		wp2 = NULL;
	while (wp1->w_wndp != wp2)
		wp1 = wp1->w_wndp;
	curwp = wp1;
	curbp = wp1->w_bufp;
	return (TRUE);
}

/*
 * This command makes the current
 * window the only window on the screen.
 * Try to set the framing
 * so that "." does not have to move on
 * the display. Some care has to be taken
 * to keep the values of dot and mark
 * in the buffer structures right if the
 * distruction of a window makes a buffer
 * become undisplayed.
 */
/*ARGSUSED*/
onlywind(f, n, k) {
	register WINDOW	*wp;
	register LINE	*lp;
	register int	i;

	while (wheadp != curwp) {
		wp = wheadp;
		wheadp = wp->w_wndp;
		if (--wp->w_bufp->b_nwnd == 0) {
			wp->w_bufp->b_dotp  = wp->w_dotp;
			wp->w_bufp->b_doto  = wp->w_doto;
			wp->w_bufp->b_markp = wp->w_markp;
			wp->w_bufp->b_marko = wp->w_marko;
		}
		free((char *) wp);
	}
	while (curwp->w_wndp != NULL) {
		wp = curwp->w_wndp;
		curwp->w_wndp = wp->w_wndp;
		if (--wp->w_bufp->b_nwnd == 0) {
			wp->w_bufp->b_dotp  = wp->w_dotp;
			wp->w_bufp->b_doto  = wp->w_doto;
			wp->w_bufp->b_markp = wp->w_markp;
			wp->w_bufp->b_marko = wp->w_marko;
		}
		free((char *) wp);
	}
	lp = curwp->w_linep;
	i  = curwp->w_toprow;
	while (i!=0 && lback(lp)!=curbp->b_linep) {
		--i;
		lp = lback(lp);
	}
	curwp->w_toprow = 0;
	curwp->w_ntrows = nrow-2;		/* 2 = mode, echo.	*/
	curwp->w_linep  = lp;
	curwp->w_flag  |= WFMODE|WFHARD;
	return (TRUE);
}

/*
 * Split the current window. A window
 * smaller than 3 lines cannot be split.
 * The only other error that is possible is
 * a "malloc" failure allocating the structure
 * for the new window.
 */
/*ARGSUSED*/
splitwind(f, n, k) {
	register WINDOW	*wp;
	register LINE	*lp;
	register int	ntru;
	register int	ntrd;
	int		ntrl;
	WINDOW		*wp1, *wp2;

	if (curwp->w_ntrows < 3) {
		ewprintf("Cannot split a %d line window", curwp->w_ntrows);
		return (FALSE);
	}
	if ((wp = (WINDOW *)malloc(sizeof(WINDOW))) == NULL) {
		ewprintf("Can't get %d", sizeof(WINDOW));
		return (FALSE);
	}
	++curbp->b_nwnd;			/* Displayed twice.	*/
	wp->w_bufp  = curbp;
	wp->w_dotp  = curwp->w_dotp;
	wp->w_doto  = curwp->w_doto;
	wp->w_markp = curwp->w_markp;
	wp->w_marko = curwp->w_marko;
	wp->w_flag  = 0;
	wp->w_force = 0;
	ntru = (curwp->w_ntrows-1) / 2;		/* Upper size		*/
	ntrl = (curwp->w_ntrows-1) - ntru;	/* Lower size		*/
	lp = curwp->w_linep;
	ntrd = 0;
	while (lp != curwp->w_dotp) {
		++ntrd;
		lp = lforw(lp);
	}
	lp = curwp->w_linep;
	if (ntrd <= ntru) {			/* Old is upper window.	*/
		if (ntrd == ntru)		/* Hit mode line.	*/
			lp = lforw(lp);
		curwp->w_ntrows = ntru;
		wp->w_wndp = curwp->w_wndp;
		curwp->w_wndp = wp;
		wp->w_toprow = curwp->w_toprow+ntru+1;
		wp->w_ntrows = ntrl;
	} else {				/* Old is lower window	*/
		wp1 = NULL;
		wp2 = wheadp;
		while (wp2 != curwp) {
			wp1 = wp2;
			wp2 = wp2->w_wndp;
		}
		if (wp1 == NULL)
			wheadp = wp;
		else
			wp1->w_wndp = wp;
		wp->w_wndp   = curwp;
		wp->w_toprow = curwp->w_toprow;
		wp->w_ntrows = ntru;
		++ntru;				/* Mode line.		*/
		curwp->w_toprow += ntru;
		curwp->w_ntrows  = ntrl;
		while (ntru--)
			lp = lforw(lp);
	}
	curwp->w_linep = lp;			/* Adjust the top lines	*/
	wp->w_linep = lp;			/* if necessary.	*/
	curwp->w_flag |= WFMODE|WFHARD;
	wp->w_flag |= WFMODE|WFHARD;
	return (TRUE);
}

/*
 * Enlarge the current window.
 * Find the window that loses space. Make
 * sure it is big enough. If so, hack the window
 * descriptions, and ask redisplay to do all the
 * hard work. You don't just set "force reframe"
 * because dot would move.
 */
/*ARGSUSED*/
enlargewind(f, n, k) {
	register WINDOW	*adjwp;
	register LINE	*lp;
	register int	i;

	if (n < 0)
		return (shrinkwind(f, -n, KRANDOM));
	if (wheadp->w_wndp == NULL) {
		ewprintf("Only one window");
		return (FALSE);
	}
	if ((adjwp=curwp->w_wndp) == NULL) {
		adjwp = wheadp;
		while (adjwp->w_wndp != curwp)
			adjwp = adjwp->w_wndp;
	}
	if (adjwp->w_ntrows <= n) {
		ewprintf("Impossible change");
		return (FALSE);
	}
	if (curwp->w_wndp == adjwp) {		/* Shrink below.	*/
		lp = adjwp->w_linep;
		for (i=0; i<n && lp!=adjwp->w_bufp->b_linep; ++i)
			lp = lforw(lp);
		adjwp->w_linep  = lp;
		adjwp->w_toprow += n;
	} else {				/* Shrink above.	*/
		lp = curwp->w_linep;
		for (i=0; i<n && lback(lp)!=curbp->b_linep; ++i)
			lp = lback(lp);
		curwp->w_linep  = lp;
		curwp->w_toprow -= n;
	}
	curwp->w_ntrows += n;
	adjwp->w_ntrows -= n;
	curwp->w_flag |= WFMODE|WFHARD;
	adjwp->w_flag |= WFMODE|WFHARD;
	return (TRUE);
}

/*
 * Shrink the current window.
 * Find the window that gains space. Hack at
 * the window descriptions. Ask the redisplay to
 * do all the hard work.
 */
shrinkwind(f, n, k) {
	register WINDOW	*adjwp;
	register LINE	*lp;
	register int	i;

	if (n < 0)
		return (enlargewind(f, -n, KRANDOM));
	if (wheadp->w_wndp == NULL) {
		ewprintf("Only one window");
		return (FALSE);
	}
	/*
	 * Bit of flakiness - KRANDOM means it was an internal call, and
	 * to be trusted implicitly about sizes.
	 */
	if (k != KRANDOM && curwp->w_ntrows <= n) {
		ewprintf("Impossible change");
		return (FALSE);
	}
	if ((adjwp=curwp->w_wndp) == NULL) {
		adjwp = wheadp;
		while (adjwp->w_wndp != curwp)
			adjwp = adjwp->w_wndp;
	}
	if (curwp->w_wndp == adjwp) {		/* Grow below.		*/
		lp = adjwp->w_linep;
		for (i=0; i<n && lback(lp)!=adjwp->w_bufp->b_linep; ++i)
			lp = lback(lp);
		adjwp->w_linep  = lp;
		adjwp->w_toprow -= n;
	} else {				/* Grow above.		*/
		lp = curwp->w_linep;
		for (i=0; i<n && lp!=curbp->b_linep; ++i)
			lp = lforw(lp);
		curwp->w_linep  = lp;
		curwp->w_toprow += n;
	}
	curwp->w_ntrows -= n;
	adjwp->w_ntrows += n;
	curwp->w_flag |= WFMODE|WFHARD;
	adjwp->w_flag |= WFMODE|WFHARD;
	return (TRUE);
}

/*
 * Delete current window. Call shrink-window to do the screen
 * updating, then throw away the window.
 */
/*ARGSUSED*/
delwind(f, n, k) {
	register WINDOW	*wp, *nwp;

	wp = curwp;			/* Cheap...		*/
	/* shrinkwind returning false means only one window... */
	if (shrinkwind(FALSE, wp->w_ntrows + 1, KRANDOM) == FALSE)
		return FALSE;
	if (--wp->w_bufp->b_nwnd == 0) {
		wp->w_bufp->b_dotp  = wp->w_dotp;
		wp->w_bufp->b_doto  = wp->w_doto;
		wp->w_bufp->b_markp = wp->w_markp;
		wp->w_bufp->b_marko = wp->w_marko;
	}
	/* since shrinkwind did't crap out, we know we have a second window */
	if (wp == wheadp) wheadp = curwp = wp->w_wndp;
	else if ((curwp = wp->w_wndp) == NULL) curwp = wheadp;
	curbp = curwp->w_bufp;
	for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp)
		if (nwp->w_wndp == wp) {
			nwp->w_wndp = wp->w_wndp;
			break ;
		}
	free((char *) wp);
	return TRUE;
}
/*
 * Pick a window for a pop-up.
 * Split the screen if there is only
 * one window. Pick the uppermost window that
 * isn't the current window. An LRU algorithm
 * might be better. Return a pointer, or
 * NULL on error.
 */
WINDOW	*
wpopup() {
	register WINDOW	*wp;

	if (wheadp->w_wndp == NULL
	&& splitwind(FALSE, 0, KRANDOM) == FALSE)
		return (NULL);
	wp = wheadp;				/* Find window to use	*/
	while (wp!=NULL && wp==curwp)
		wp = wp->w_wndp;
	return (wp);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'tty/termcap/tty.c'
then
	echo shar: will not over-write existing file "'tty/termcap/tty.c'"
else
cat << \SHAR_EOF > 'tty/termcap/tty.c'
/*
 * Termcap/terminfo display driver
 *
 * Termcap is a terminal information database and routines to describe 
 * terminals on most UNIX systems.  Many other systems have adopted
 * this as a reasonable way to allow for widly varying and ever changing
 * varieties of terminal types.  This should be used where practical.
 */
/* Known problems:
 *	tputs is always called with the number of lines affected set to
 *	one.  Therefore, padding may be insufficient on some sequences
 *	dispite termcap being set up correctly.
 *
 *	If you have a terminal with no clear to end of screen and
 *	memory of lines below the ones visible on the screen, display
 *	will be wrong in some cases.  I doubt that any such terminal
 *	was ever made, but I thought everyone with delete line would
 *	have clear to end of screen too...
 *
 *	Code for terminals without clear to end of screen and/or clear
 *	to end of line has not been extensivly tested.
 *
 *	Cost calculations are very rough.  Costs of insert/delete line
 *	may be far from the truth.  This is accentuated by display.c
 *	not knowing about multi-line insert/delete.
 *
 *	Using scrolling region vs insert/delete line should probably
 *	be based on cost rather than the assuption that scrolling
 *	region operations look better.
 */
#include	"def.h"

#define	BEL	0x07			/* BEL character.		*/
#define	LF	0x0A			/* Line feed.			*/

extern	int	ttrow;
extern	int	ttcol;
extern	int	tttop;
extern	int	ttbot;
extern	int	tthue;

int	tceeol;			/* Costs are set later */
int	tcinsl;
int	tcdell;

static	int	insdel;		/* Do we have both insert & delete line? */

char	*tgetstr();
char	*tgoto();
int	ttputc();

#define TCAPSLEN 1024

char tcapbuf[TCAPSLEN];

/* PC, UP, and BC are used by termlib, so must be extern and have these
 * names unless you have a non-standard termlib.
 */

int	LI;			/* standard # lines */
char    PC,
        *CM,
        *CE,
        *UP,
	*BC,
	*IM,			/* insert mode */
	*IC,			/* insert a single space */
	*EI,			/* end insert mode */
	*DC,
	*AL,			/* add line */
	*DL,			/* del line */
	*pAL,			/* parameterized add line */
	*pDL,			/* parameterized delete line */
	*TI,			/* term init -- start using cursor motion */
	*TE,			/* term end --- end using cursor motion */
	*SO,
	*SE,
        *CD,
	*CS,			/* set scroll region			*/
	*SR;			/* back index (used with scroll region	*/
#ifdef	XKEYS
char	*K[NFKEYS],		/* other function key codes		*/
	*L[NFKEYS],		/* labels for other functions keys	*/
	*KS, *KE,		/* enter keypad mode, exit keypad mode	*/
	*KH, *KU, *KD, *KL, *KR; /* home, arrow keys			*/
#endif
int	SG;	/* number of glitches, 0 for invisable, -1 for none	*/
	/* (yes virginia, there are terminals with invisible glitches)	*/

/*
 * Initialize the terminal when the editor
 * gets started up.
 */
static char tcbuf[1024];

ttinit() {
        char *getenv();
        char *t, *p, *tgetstr();
        char *tv_stype;
#ifdef	XKEYS
	char kname[3], lname[3];
	int i;
#endif

#ifdef	VAXC
        if ((tv_stype = trnlnm("TERM")) == NULL)
#else
        if ((tv_stype = getenv("TERM")) == NULL)/* Don't want VAX C getenv() */
#endif
                panic("Environment variable TERM not defined!");

        if((tgetent(tcbuf, tv_stype)) != 1)
	{
		(VOID) strcpy(tcbuf, "Unknown terminal type ");
		(VOID) strcat(tcbuf, tv_stype);
                panic(tcbuf);
        }

        p = tcapbuf;
        t = tgetstr("pc", &p);
        if(t) PC = *t;

	LI = tgetnum("li");
        CD = tgetstr("cd", &p);
        CM = tgetstr("cm", &p);
        CE = tgetstr("ce", &p);
        UP = tgetstr("up", &p);
	BC = tgetstr("bc", &p);
	IM = tgetstr("im", &p);
	IC = tgetstr("ic", &p);
	EI = tgetstr("ei", &p);
	DC = tgetstr("dc", &p);
	AL = tgetstr("al", &p);
	DL = tgetstr("dl", &p);
	pAL= tgetstr("AL", &p);	/* parameterized insert and del. line */
	pDL= tgetstr("DL", &p);
	TI = tgetstr("ti", &p);
	TE = tgetstr("te", &p);
	SO = tgetstr("so", &p);
	SE = tgetstr("se", &p);
	CS = tgetstr("cs", &p); /* set scrolling region */
	SR = tgetstr("sr", &p);
	SG = tgetnum("sg");	/* standout glitch 	*/
#ifdef	XKEYS
	/* get the 10 standard termcap keys */
	strcpy(kname,"kx");
	strcpy(lname,"lx");
	for (i = 0; i < 10; i++) {
		kname[1] = i + '0';
		K[i] = tgetstr(kname, &p);
		lname[1] = i + '0';
		L[i] = tgetstr(lname, &p);
	}
	/* Hack to get another bunch */
	strcpy(kname,"Kx");
	strcpy(lname,"Lx");
	for (i = 0; i < 10; i++) {
		kname[1] = i + '0';
		K[10 + i] = tgetstr(kname, &p);
		lname[1] = i + '0';
		L[10 + i] = tgetstr(lname, &p);
	}

	/* Get the rest of the sequences */
	KS = tgetstr("ks", &p);
	KE = tgetstr("ke", &p);
	KH = tgetstr("kh", &p);
	KU = tgetstr("ku", &p);
	KD = tgetstr("kd", &p);
	KL = tgetstr("kl", &p);
	KR = tgetstr("kr", &p);
#endif

        if(CM == NULL || UP == NULL)
            panic("This terminal is to stupid to run MicroGnuEmacs\n");
	ttresize();			/* set nrow & ncol	*/

	/* watch out for empty capabilities (sure to be wrong)	*/
	if (CE && !*CE) CE = NULL;
	if (CS && !*CS) CS = NULL;
	if (SR && !*SR) SR = NULL;
	if (AL && !*AL) AL = NULL;
	if (DL && !*DL) DL = NULL;
	if (pAL && !*pAL) pAL = NULL;
	if (pDL && !*pDL) pDL = NULL;
	if (CD && !*CD) CD = NULL;

	if(!CE)	tceeol = ncol;
	else 	tceeol = charcost(CE);

	/* Estimate cost of inserting a line */
	if (CS && SR)	tcinsl = charcost(CS)*2 + charcost(SR);
	else if (pAL)   tcinsl = charcost(pAL);
	else if (AL)    tcinsl = charcost(AL);
	else		tcinsl = NROW * NCOL;	/* make this cost high enough */

	/* Estimate cost of deleting a line */
	if (CS)		tcdell = charcost(CS)*2 + 1;
	else if (pDL)   tcdell = charcost(pDL);
	else if (DL)    tcdell = charcost(DL);
	else		tcdell = NROW * NCOL;	/* make this cost high enough */

	/* Flag to indicate that we can both insert and delete lines */
	insdel = (AL || pAL) && (DL || pDL);

        if (p >= &tcapbuf[TCAPSLEN])
		panic("Terminal description too big!\n");
	if (TI && *TI) putpad (TI);	/* init the term */
}

/*
 * Clean up the terminal, in anticipation of
 * a return to the command interpreter. This is a no-op
 * on the ANSI display. On the SCALD display, it sets the
 * window back to half screen scrolling. Perhaps it should
 * query the display for the increment, and put it
 * back to what it was.
 */
tttidy() {
	if (TE && *TE) putpad (TE);	/* set the term back to normal mode */
#ifdef	XKEYS
	ttykeymaptidy();
#endif
}

/*
 * Move the cursor to the specified
 * origin 0 row and column position. Try to
 * optimize out extra moves; redisplay may
 * have left the cursor in the right
 * location last time!
 */
ttmove(row, col) {
    char        *tgoto();

    if (ttrow!=row || ttcol!=col) {
	putpad(tgoto(CM, col, row));
	ttrow = row;
	ttcol = col;
    }
}

/*
 * Erase to end of line.
 */
tteeol() {
    if(CE) putpad(CE);
    else {
	register int i=ncol-ttcol;
	while(i--) ttputc(" ");
	ttrow = ttcol = HUGE;
    }
}

/*
 * Erase to end of page.
 */
tteeop() {
    if(CD) putpad(CD);
    else {
    	putpad(CE);
	if (insdel) ttdell(ttrow + 1, LI, LI - ttrow - 1);
	else {		/* do it by hand */
	    register int line;
	    for (line = ttrow + 1; line <= LI; ++line) {
	    	ttmove(line, 0);
		tteeol();
	    }
	}
	ttrow = ttcol = HUGE;
    }
}

/*
 * Make a noise.
 */
ttbeep() {
	ttputc(BEL);
	ttflush();
}

/*
 * Insert nchunk blank line(s) onto the
 * screen, scrolling the last line on the
 * screen off the bottom.  Use the scrolling
 * region if possible for a smoother display.
 * If no scrolling region, use a set
 * of insert and delete line sequences
 */
ttinsl(row, bot, nchunk) {
    register int	i;
    
    if (row == bot) {		/* Case of one line insert is 	*/
	ttmove(row, 0);		/*	special			*/
	tteeol();
	return;
    }
    if (CS && SR) {		/* Use scroll region and back index	*/
	ttwindow(row,bot);
	ttmove(row, 0);
	while (nchunk--) putpad(SR);
	ttnowindow();
	return;
    } else if (insdel) {
	ttmove(1+bot-nchunk, 0);
	if (pDL) putpad (tgoto(pDL, 0, nchunk));
	else for (i=0; i<nchunk; i++)	/* For all lines in the chunk	*/
		putpad(DL);
	ttmove(row, 0);
	if (pAL) putpad (tgoto(pAL, 0, nchunk));
	else for (i=0; i<nchunk; i++)	/* For all lines in the chunk	*/
		putpad(AL);
	ttrow = HUGE;
	ttcol = HUGE;
    } else panic("ttinsl: Can't insert/delete line");
}

/*
 * Delete nchunk line(s) from "row", replacing the
 * bottom line on the screen with a blank line. 
 * Unless we're using the scrolling region, this is 
 * done with a crafty sequences of insert and delete 
 * lines.  The presence of the echo area makes a
 * boundry condition go away.
 */
ttdell(row, bot, nchunk)
{
    register int	i;
    
    if (row == bot) {		/* One line special case	*/
	ttmove(row, 0);
	tteeol();
	return;
    }
    if (CS) {			/* scrolling region	*/
	ttwindow(row, bot);
	ttmove(bot, 0);
	while (nchunk--) ttputc(LF);
	ttnowindow();
    }
    else if(insdel) {
	ttmove(row, 0);			/* Else use insert/delete line	*/
	if (pDL) putpad (tgoto(pDL, 0, nchunk));
	else for (i=0; i<nchunk; i++)	/* For all lines in the chunk	*/
		putpad(DL);
	ttmove(1+bot-nchunk,0);
	if (pAL) putpad (tgoto(pAL, 0, nchunk));
	else for (i=0; i<nchunk; i++)	/* For all lines in the chunk	*/
		putpad(AL);
	ttrow = HUGE;
	ttcol = HUGE;
    } else panic("ttdell: Can't insert/delete line");
}

/*
 * This routine sets the scrolling window
 * on the display to go from line "top" to line
 * "bot" (origin 0, inclusive). The caller checks
 * for the pathalogical 1 line scroll window that
 * doesn't work right, and avoids it. The "ttrow"
 * and "ttcol" variables are set to a crazy value
 * to ensure that the next call to "ttmove" does
 * not turn into a no-op (the window adjustment
 * moves the cursor).
 * 
 */
ttwindow(top, bot)
{
	if (CS && (tttop!=top || ttbot!=bot)) {
		putpad(tgoto(CS, bot, top));
		ttrow = HUGE;			/* Unknown.		*/
		ttcol = HUGE;
		tttop = top;			/* Remember region.	*/
		ttbot = bot;
	}
}

/*
 * Switch to full screen scroll. This is
 * used by "spawn.c" just before is suspends the
 * editor, and by "display.c" when it is getting ready
 * to exit.  This function gets to full screen scroll 
 * by telling the terminal to set a scrolling regin
 * that is LI or nrow rows high, whichever is larger.
 * This behavior seems to work right on systems
 * where you can set your terminal size.
 */
ttnowindow()
{
    if (CS) {
	putpad(tgoto(CS, (nrow > LI ? nrow : LI) - 1, 0));
	ttrow = HUGE;			/* Unknown.		*/
	ttcol = HUGE;
	tttop = HUGE;			/* No scroll region.	*/
	ttbot = HUGE;
    }
}

/*
 * Set the current writing color to the
 * specified color. Watch for color changes that are
 * not going to do anything (the color is already right)
 * and don't send anything to the display.
 * The rainbow version does this in putline.s on a
 * line by line basis, so don't bother sending
 * out the color shift.
 */
ttcolor(color) register int color; {
    if (color != tthue) {
	if (color == CTEXT) {		/* Normal video.	*/
	    putpad(SE);
	} else if (color == CMODE) {	/* Reverse video.	*/
	    putpad(SO);
	}
	tthue = color;			/* Save the color.	*/
    }
}

/*
 * This routine is called by the
 * "refresh the screen" command to try and resize
 * the display. The new size, which must be deadstopped
 * to not exceed the NROW and NCOL limits, it stored
 * back into "nrow" and "ncol". Display can always deal
 * with a screen NROW by NCOL. Look in "window.c" to
 * see how the caller deals with a change.
 */
ttresize() {
	setttysize();			/* found in "ttyio.c",	*/
					/* ask OS for tty size	*/
	if (nrow < 1)			/* Check limits.	*/
		nrow = 1;
	else if (nrow > NROW)
		nrow = NROW;
	if (ncol < 1)
		ncol = 1;
	else if (ncol > NCOL)
		ncol = NCOL;
}

#ifdef NO_RESIZE
static setttysize() {
	nrow = tgetnum("li");
	ncol = tgetnum("co");
}
#endif

static int cci;

static			/* fake char output for charcost() */
fakec(c) char c; {
#ifdef	lint
	c++;
#endif
	cci++;
}

/* calculate the cost of doing string s */
charcost (s) char *s; {
    cci = 0;

    tputs(s, nrow, fakec);
    return cci;
}

putpad(str) char *str; {
        tputs(str, 1, ttputc);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'tty/termcap/readme'
then
	echo shar: will not over-write existing file "'tty/termcap/readme'"
else
cat << \SHAR_EOF > 'tty/termcap/readme'

	MicroGnuEmacs Termcap Terminal Capabilities

The termcap library needs to know where to get the terminal type and
termcap capibilities file from.  UNIX and Os9/68k users should "setenv
TERM" to their terminal type, and "setenv TERMCAP" if they are using a
non-standard termcap file.  VMS users should see AAAREADME.1ST for
information on how to define the logical names TERM and ETC to point
to the termcap definition file.  Users of other operating systems
should do the aproprate thing.  For an example of a termcap file, UNIX
users may look in /etc/termcap, Os9/68k users may look at
/dd/sys/termcap (if present), and VMS users should see the file
[.SYS.VMS.TERMCAP]TERMCAP. 

MicroGnuEmacs requires that certain terminal capabilities exist in the
specified termcap entry.  The "cm" (cursor motion) capability *must*
be available to use MicroGnuEmacs.  (Yes, it is possible to fake cm
with some other capibilities, but MicroGnuEmacs doesn't try.)  If your
terminal is one that uses control characters in the paramater portion
of the "cm" string, the "up" and "bc" capabilites may also be needed.
(See your termlib documentation for when this is so.)

If the following capabilities are available, they are used.  The AL
and DL sequences are not totally standard, but having them improves
the performance of the editor, since it doesn't have to redraw the
screen to delete a line.  They should not be used if they need control
characters as paramaters.

	cd	-- clear display
	ce	-- clear to eol

	al	-- insert 1 line
	dl	-- delete 1 line

	AL	-- parametrized insert line (note capitalization)
	DL	-- parametrized delete line (note capitalization)

	ti	-- cursor movement initialization string
	te	-- cursor movement end string

The cs capability is not as standard as some of the other
capibilities, but is used by MicroGnuEmacs when available.  It is used
to define a "scrolling region", which defines a window within the
screen where all the action takes place.  A newline character at the
bottom of this area scrolls the rest of the text in the area up one
line, just like the normal screen; a reverse linefeed (sr) at the top
of the window moves all the text in the area down a line.
MicroGnuEmacs does not properly handle "cs" if your terminal needs
control characters as paramaters, and in this case "cs" should not be
defined.  

If the cs and sr capabilities are available, the termcap driver uses
these to make the insert/delete line functions work more smoothly. If
only the cs capability is present, it is still used for the delete
line function, but not for inserting lines.

The definition of the cs capability is: the first parameter in the
sequence defines the first row (origin 0) that is in the scrolling
region, and the second argument defines the last row to include in the
scrolling region.

	cs	-- set scrolling region (arg1 = top, arg2 = bottom)
	sr	-- reverse index

The following capabilities provide for an enhanced (reverse-video
or otherwise rendered) mode line.  The sg entry should not be present
on terminals that do this to characters as they are placed on the
screen.  Terminals that	put a region of the screen in the standout
mode should have sg defined as numeric: :sg#0: for terminals that do
this on regions but don't take any character positions to do this,
(this may be a non-standard interprition of the meaning of sg) and the
number of character positions taken by any other terminal.

	so	-- enter standout mode
	se	-- leave standout mode
	sg	-- number of character positions used by standout

			Function Keys

If MicroGnuEmacs is compiled with XKEYS defined, a new feature of the
termcap driver is support for function keys, based on the termcap
entry that defines the terminal.  This may not be be deisriable in all
cases, especially those in which the terminal in use does not use ESC
as the first character of all arrow and function keys.  If you do
deside to use this feature, don't expect it to work with all terminal
types.  Those termial types it doesn't work with will have to use
modified termcaps that do not include the termcap sequences described
below to be useful with MicroGnuEmacs compiled with the XKEYS option. 
XKEYS also interferes with the proper operation of delayed prompts.

The termcap "standard" provides for a number of sequences that define
how to activate the function keys, what the function key sequences
are, and the names of the function keys.  The termcap driver uses the
following capabilities to parse for function key sequences:

	ks	-- start using function keys
	ke	-- finish using the function keypad
	kh	-- home key
	ku	-- up arrow
	kd	-- down arrow
	kl	-- left arrow
	kr	-- right arrow
	k0-k9	-- standard termcap function keys (1-10)
	l0-l9	-- labels for same

The following key capabilities are not standard, but are used if
they are in the termcap:

	K0-K9	-- function keys 10 through 19
	L0-L9	-- labels for same

For example, the DEC LK201 (vt200-series, VAXstation) keyboard has an
editing keypad. A VT200 termcap entry could include the following
capabilities to go into application keypad mode, set up the arrow keys,
and map the editing keypad to internal function key codes KF0-KF7:

	...the beginning of the termcap entry....
	:ks=\E[?1h\E=:ke=\E[?1l\E>:\
	:ku=\EOA:kd=\EOB:kr=\EOC:kl=\EOD:kh=\E[H:\
	:k0=\E[28~:l0=Help:\
	:k1=\E[29~:l1=Do:\
	:k2=\E[1~:l2=Find:\
	:k3=\E[2~:l3=Insert Here:\
	:k4=\E[3~:l4=Remove:\
	:k5=\E[4~:l5=Select:\
	:k6=\E[5~:l6=Prev Screen:\
	:k7=\E[6~:l7=Next Screen:\

There is one problem with supporting function keys: If the META
introducer key (usually ESC) is used as the initial character of a
function key sequence, how is the parser to know when the user intends
the introducer to be taken at face value?  The parser doesn't have
enough information.

The approach the current code takes is that if the META introducer is
the first character in a function sequence, and the second character c
isn't part of a function key sequence, the parser returns (KMETA | c).
If it sees *two* META introducers in a row, it returns one instance of
METACH. This approach is subject to discussion and debate, but it
works right most of the time.
SHAR_EOF
fi # end of overwriting check
if test -f 'tty/termcap/ttydef.h'
then
	echo shar: will not over-write existing file "'tty/termcap/ttydef.h'"
else
cat << \SHAR_EOF > 'tty/termcap/ttydef.h'
/*
 *	Termcap terminal file, nothing special, just make it big
 *	enough for windowing systems.
 */

#define	GOSLING			/* Compile in fancy display.	*/
/* #define	MEMMAP		*/	/* Not memory mapped video.	*/

#define	NROW	66    			/* Rows.			*/
#define	NCOL	132			/* Columns.			*/
/* #define	MOVE_STANDOUT		/* don't move in standout mode	*/
#define	STANDOUT_GLITCH			/* possible standout glitch	*/
#define	TERMCAP				/* for possible use in ttyio.c	*/

/*
 * Termcap function keys.  The last 10 keys correspond to the
 * non-standard termcap entries K0-K9 (instead of k0-k9).
 */
#ifdef	XKEYS
#define	KFIRST	K01
#define KLAST	K1A

#define KHOME	K01
#define KDOWN	K02
#define	KUP	K03
#define	KLEFT	K04
#define	KRIGHT	K05
#define	KF0	K06
#define	KF1	K07
#define KF2	K08
#define KF3	K09
#define KF4	K0A
#define KF5	K0B
#define KF6	K0C
#define KF7	K0D
#define KF8	K0E
#define	KF9	K0F
#define KF10	K10
#define	KF11	K11
#define	KF12	K12
#define	KF13	K13
#define	KF14	K14
#define	KF15	K15
#define	KF16	K16
#define	KF17	K17
#define	KF18	K18
#define	KF19	K19
#define	KF20	K1A

#define	NFKEYS	20		/* # of function keys (k0-k9, K0-K9) */
#endif
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0



More information about the Mod.sources mailing list