/*
 * Name:	MicroEMACS
 *		Symbol table stuff for GNU emacs compatability
 * Version:	29
 * Last edit:	19-Apr-86
 * By:		{sun, amdahl, cbosgd}!rtech!gonzo!daveb
 *
 * Symbol tables, and keymap setup.
 * The terminal specific parts of building the
 * keymap has been moved to a better place.
 *
 * This version matches the standard GNU Emacs 17.49 bindings.
 * With this file, MicroEMACS is a proper subset of GNU.
 *
 * If a GNU feature was misnamed, it was moved.
 * If a GNU feature was "nearly" right, it was noted for later work.
 * If a MicroEMACS feature was incompatible, it was dropped.
 *
 * Compatibility NOW for the future!
 */
#include	"def.h"

#define	DIRLIST	0			/* Disarmed!			*/

# define	DEL	0x7f
# define	ESC	0x1b

/* # define DAVEB	*//* Dave Brower specials, not GNU compatibility */

/*
 * Defined by "main.c".
 */
extern	int	ctrlg();		/* Abort out of things		*/
extern	int	quit();			/* Rude 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 "gnucmds.c"
 */
extern	int	savebuffs();		/* GNU style save-some-buffers	*/
extern	int	savequit();		/* GNU save-buffers-kill-emacs	*/
extern	int	suspend();		/* GNU style suspend		*/
extern	int	notmodified();		/* GNU make buffer undirty	*/
extern	int	scrollother();		/* GNU scroll other window	*/

/*
 * 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	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	killbuffer();		/* Make a buffer go away.	*/

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

/*
 * Defined by "file.c".
 */
extern	int	fileread();		/* Get a file, read only	*/
extern	int	filevisit();		/* Get a file, read write	*/
extern	int	filewrite();		/* Write a file			*/
extern	int	filesave();		/* Save current file		*/
extern	int	filename();		/* Adjust file name		*/

/*
 * 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	indent();		/* Insert CR-LF, then indent	*/
extern	int	forwdel();		/* Forward delete		*/
extern	int	backdel();		/* Backward delete		*/
extern	int	killline();		/* Kill forward			*/
extern	int	yank();			/* Yank back from killbuffer.	*/

/*
 * 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.		*/

/*
 * Defined by "spawn.c".
 */
extern	int	spawncli();		/* Run CLI in a subjob.		*/

/*
 * 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	mvdnwind();		/* Move window down		*/
extern	int	mvupwind();		/* Move window up		*/
extern	int	onlywind();		/* Make current window only one	*/
extern	int	splitwind();		/* Split 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	help();			/* Help key.			*/
extern	int	bindtokey();		/* Modify key bindings.		*/
extern	int	wallchart();		/* Make wall chart.		*/

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

/*
 * 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.
 */

/* GNU standard bindings that are missing are commented out with the
 * following notations:
 *
 *	/*B	Braindamaged in current implementation.
 *	/*D	Design restructuring is needed.
 *	/*E	Easy, a few hours.
 *	/*H	Hard, (days) but possible if desirable.
 *	/*I	Impossible (weeks), much too hard to do right.
 *	/*M	Moderate difficulty, a day or so.
 *	/*T	On the to do list.
 *	/*U	Undecided.
 *	/*X	Means unnecessary to do.
 *	/*W	Current version is wrong.
 *
 *   Well, braindamaged is a bit too strong, but doing all the argument
 *   processing in the main loop, and not having meta- ancd ctrl-x
 *   be bindable commands seems kinda funny to me (daveb).
 */

KEY	key[] = {
	KCTRL|'@',	setmark,	"set-mark-command",
	KCTRL|'A',	gotobol,	"beginning-of-line",
	KCTRL|'B',	backchar,	"backward-char",
/*DHX	KCTRL|'C',	???,		"mode-specific-command-prefix",	*/
	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-command",
/*DHX	KCTRL|'I',	???,		"indent-for-tab-command",	*/
	KCTRL|'J',	indent,		"newline-and-indent",
	KCTRL|'K',	killline,	"kill-line",
	KCTRL|'L',	refresh,	"recenter",
			/*W actually doesn't recenter...		*/

	KCTRL|'M',	newline,	"newline",
	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-characters",
/*BMT	KCTRL|'U',	???,		"universal-argument,"		*/
	KCTRL|'V',	forwpage,	"scroll-up",
	KCTRL|'W',	killregion,	"kill-region",
/*BMT	KCTRL|'X',	???,		"Control-X-prefix",		*/
	KCTRL|'Y',	yank,		"yank",
			/*WET doesn't set point! */

	KCTRL|'Z',	suspend,	"suspend-emacs",

/*BMT	KMETA,		???,		"ESC-prefix",			*/
/*DX	KCTRL|']',	???,		"abort-recursive-edit",		*/
/*DIX	KCTRL|'_',	???,		"undo",				*/

	DEL,		backdel,	"delete-backward-char",

/*DIX	KCTLX|KCTRL|'A',???,		"add-mode-abbrev",		*/
	KCTLX|KCTRL|'B',listbuffers,	"list-buffers",
	KCTLX|KCTRL|'C',savequit,	"save-buffers-kill-emacs",

#if	DIRLIST
	KCTLX|KCTRL|'D',dirlist,	"list-directory",
#endif

/*DIX	KCTLX|KCTRL|'E',???,		"eval-last-sexp",		*/
	KCTLX|KCTRL|'F',filevisit,	"find-file",
/*DIX	KCTLX|KCTRL|'H',???,		"inverse-add-mode=abbrev",	*/
/*MT	KCTLX|KCTRL|'I',???,		"indent-rigidly",		*/
	KCTLX|KCTRL|'L',lowerregion,	"downcase-region",
/*ET	KCTLX|KCTRL|'N', ???,		"set-goal-column"		*/
	KCTLX|KCTRL|'O',deblank,	"delete-blank-lines",
/*MU	KCTLX|KCTRL|'P',???,		"mark-page"			 */
 	KCTLX|KCTRL|'R',fileread,	"file-file-read-only",
				/*WU incorrect behaviour...		*/
	KCTLX|KCTRL|'S',filesave,	"file-buffer",

/*EU	KCTLX|KCTRL|'T',???,		"transpose-lines",		*/
	KCTLX|KCTRL|'U',upperregion,	"upcase-region",
/*EU	KCTLX|KCTRL|'V',findalternate,	"find-alternate-file",		*/
	KCTLX|KCTRL|'W',filewrite,	"write-file",
	KCTLX|KCTRL|'X',swapmark,	"exchange-point-and-mark",
	KCTLX|KCTRL|'Z',spawncli,	"suspend-emacs",

/*MT	KCTLX|ESC	???,		"repeat-complex-command",	*/
/*U	KCTLX|'$'	???,		"set-selective-display",	*/
	KCTLX|'(',	ctlxlp,		"start-kbd-macro",
	KCTLX|')',	ctlxrp,		"end-kbd-macro",
/*DIX	KCTLX|'+',	???,		"add-global-abbrev",		*/
/*DIX	KCTLX|'-',	???,		"inverse-add-global-abbrev",	*/
/*MX	KCTLX|'.',	???,		"set-fill-prefix",		*/
/*HX	KCTLX|'/',	???,		"point-to-register",		*/
/*MT	KCTLX|'0',	???,		"delete-window",		*/
	KCTLX|'1',	onlywind,	"delete-other-windows",
	KCTLX|'2',	splitwind,	"split-window-vertically",
/*MX	KCTLX|'4',	???,		"ctl-x-4-prefix",		*/
/*DHX	KCTLX|'5',	???,		"split-window-horizontally",	*/
/*EX	KCTLX|';',	???,		"set-comment-column",		*/
/*HU	KCTLX|'<',	???,		"scroll-left",			*/
	KCTLX|'=',	showcpos,	"what-cursor-position",
/*HU	KCTLX|'>',	???,		"scroll-right",			*/
/*MU	KCTLX|'[',	???,		"backward-page",		*/
/*MU	KCTLX|']',	???,		"forward-page",			*/
	KCTLX|'^',	enlargewind,	"enlarge-window",
/*HX	KCTLX|'`',	???,		"next-error",			*/
/*MU	KCTLX|'A',	???,		"append-to-buffer",		*/
	KCTLX|'B',	usebuffer,	"switch-to-buffer",
/*MX	KCTLX|'D',	???,		"dired",			*/
	KCTLX|'E',	ctlxe,		"call-last-kbd-macro",
/*EU	KCTLX|'F',	ctlxe,		"set-fill-column",		*/
/*HX	KCTLX|'G',	???,		"insert-register",		*/
/*EU	KCTLX|'H',	???,		"mark-whole-buffer",		*/
/*MU	KCTLX|'I',	???,		"insert-file",			*/
/*HX	KCTLX|'J',	???,		"register-to-point",		*/
	KCTLX|'K',	killbuffer,	"kill-buffer",
/*MU	KCTLX|'L',	???,		"count-lines-page",		*/
/*HU	KCTLX|'N',	???,		"narrow-to-region",		*/
	KCTLX|'O',	nextwind,	"other-window",
/*HU	KCTLX|'P',	???,		"narrow-to-page",		*/
/*MX	KCTLX|'Q',	???,		"kbd-macro-qry",		*/
/*HX	KCTLX|'R',	???,		"copy-rectangle-to-register",	*/
	KCTLX|'S',	savebuffs,	"save-some-buffers",
/*HX	KCTLX|'U',	???,		"advertised-undo",		*/
/*HX	KCTLX|'W',	???,		"widen",			*/
/*HX	KCTLX|'{',	???,		"shrink-window-horizontally",	*/
/*HX	KCTLX|'}',	???,		"enlarge-window-horizontally",	*/
/*MT	KCTLX|DEL,	???,		"backward-kill-sentence,"	*/

/*MU	KMETA|KCTRL|'@',???,		"mark-sexp",			*/
/*MU	KMETA|KCTRL|'A',???,		"beginning-of-defun",		*/
/*MU	KMETA|KCTRL|'B',???,		"backwards-sexp",		*/
/*MX	KMETA|KCTRL|'C',???,		"exit-recursive-edit",		*/
/*MU	KMETA|KCTRL|'D',???,		"down-list",			*/
/*MU	KMETA|KCTRL|'E',???,		"end-of-defun",			*/
/*MU	KMETA|KCTRL|'F',???,		"forward-sexp",			*/
/*MU	KMETA|KCTRL|'H',???,		"mark-defun",			*/
/*MU	KMETA|KCTRL|'J',???,		"indent-new-comment-line",	*/
/*MU	KMETA|KCTRL|'K',???,		"kill-sexp",			*/
/*MU	KMETA|KCTRL|'N',???,		"foward-list",			*/
/*EU	KMETA|KCTRL|'O',???,		"split-line",			*/
/*MU	KMETA|KCTRL|'P',???,		"backward-list",		*/
/*HX	KMETA|KCTRL|'S',???,		"isearch-forward-regexp",	*/
/*MU	KMETA|KCTRL|'T',???,		"transpose-sexps",		*/
/*MU	KMETA|KCTRL|'U',???,		"backward-up-list",		*/
	KMETA|KCTRL|'V',scrollother,	"scroll-other-window",
/*ET	KMETA|KCTRL|'W',???,		"append-next-kill",		*/

/*IX	KMETA|ESC,	???		"eval-expression",		*/
/*IX	KMETA|CTRL|'\',	???		"indent-region",		*/
/*MU	KMETA|' ',	???		"just-one-space",		*/
/*HT	KMETA|'!',	???,		"shell-command", */
/*HU	KMETA|'$',	???,		"spell-word", */
	KMETA|'%',	queryrepl,	"query-replace",
/*IX	KMETA|'\'',	???,		"abbrev-prefix-mark",		*/
/*TU	KMETA|'(',	???,		"insert-parenthesis", */
/*HU	KMETA|')',	???,		"move-past-close-and-reindent", */
/*HX	KMETA|',',	???,		"tags-loop-continue",		*/
/*DT	KMETA|'-',	???,		"negative-argument",		*/
	KMETA|'>',	gotoeob,	"end-of-buffer",
/*MU	KMETA|'=',	???,		"count-lines-region",		*/
	KMETA|'<',	gotobob,	"beginning-of-buffer",
/*TT	KMETA|'@',	???,		"mark-word",			*/
/*MU	KMETA|'[',	???,		"backward-paragraph",		*/
/*TT	KMETA|'\\',	???,		"delete-horizontal-space",	*/
/*MU	KMETA|']',	???,		"forward-paragraph",		*/
/*TT	KMETA|'^',	???,		"delete-indentation",		*/
/*MU	KMETA|'A',	???,		"backward-sentence",		*/
	KMETA|'B',	backword,	"backward-word",
	KMETA|'C',	capword,	"capitalize-word",
	KMETA|'D',	delfword,	"kill-word",
/*MU	KMETA|'E',	???,		"forward-sentence",		*/
	KMETA|'F',	forwword,	"forward-word",
/*HT	KMETA|'G',	???,		"fill-region",			*/
/*MU	KMETA|'H',	???,		"mark-paragraph",		*/
/*DMX	KMETA|'I',	???,		"tab-to-tab-stop",		*/
/*MU	KMETA|'J',	???,		"indent-new-comment-line",	*/
	KMETA|'L',	lowerword,	"downcase-word",
/*EU	KMETA|'M',	???,		"back-to-indentation",		*/
/*HT	KMETA|'Q',	???,		"fill-paragraph",		*/
	KMETA|'R',	reposition,	"move-to-window-line",
/*ET	KMETA|'T',	???,		"transpose-words",		*/
	KMETA|'U',	upperword,	"upcase-word",
	KMETA|'V',	backpage,	"scroll-down",
	KMETA|'W',	copyregion,	"copy-region-as-kill",
	KMETA|'X',	extend,		"execute-extended-command",
/*HDX	KMETA|'Y',	???,		"yank-pop",			*/
/*MU	KMETA|'Z',	???,		"zap-to-char",			*/
/*HT	KMETA|'|',	???,		"shell-command-on-region",	*/
	KMETA|'~',	notmodified,	"not-modified",
	KMETA|DEL,	delbword,	"backward-kill-word",

	/*
	** These are unbound functions, callable by name.
	** They are GNU compatible (to various degrees).
	*/


/*	-1,		???,		"append-next-kill",	*/
/*	-1,		???,		"apropos",		*/
	-1,		wallchart,	"describe-bindings",
/*MT	-1,		???,		"digit-argument",	*/
	-1,		showversion,	"emacs-version",
	-1,		bindtokey,	"global-set-key",
	-1,		gotoline,	"goto-line",
	-1,		quit,		"kill-emacs",
	-1,		backsearch,	"search-backward",
	-1,		forwsearch,	"search-forward",
	-1,		selfinsert,	"self-insert-command",
	-1,		filename,	"set-visited-file-name",

	/* These are duplicate names for GNU compatible functions,
	** or incompatible commands I've left lying around for now.
	** The old command names get used as args to keydup() in the
	** various tty/xxx/ttykbd.c modules for function keys.
	*/

	-1,		backchar,	"back-char",
	-1,		backline,	"back-line",
	-1,		backpage,	"back-page",
	-1,		prevwind,	"back-window",	
	-1,		mvdnwind,	"down-window",
	-1,		enlargewind,	"enlarge-window",
	-1,		ctlxe,		"execute-macro",
	-1,		forwchar,	"forw-char",
	-1,		forwline,	"forw-line",
	-1,		forwpage,	"forw-page",
	-1,		nextwind,	"forw-window",
	-1,		help,		"help",
	-1,		selfinsert,	"ins-self",
	-1,		killregion,	"kill-region",
	-1,		searchagain,	"search-again",
	-1,		setmark,	"set-mark",
	-1,		shrinkwind,	"shrink-window",
	-1,		mvupwind,	"up-window",
			/*MT incorrect behaviour */

};

#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;

	sp = symbol[symhash(cp)];
	while (sp != NULL) {
		if (strcmp(cp, sp->s_name) == 0)
			return (sp);
		sp = sp->s_symp;
	}
	return (NULL);
}

/*
 * 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);
}

/*
 * 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 KEY	*kp;
	register int	i;
	register int	hash;

	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);

	/* Multiple bindings of standard commands */

#ifdef DAVEB
        keydup(KCTRL|'Z', "scroll-down");
#endif
	keydup(KCTLX|KCTRL|'G',	"keyboard-quit");
	keydup(KMETA|KCTRL|'G',	"keyboard-quit");
	keydup(KMETA|' ', "set-mark-command");

	/*
	 * Self insert should be in hash table already, bound to -1.
	 * Bind all printing chars without an explicit binding already.
	 */
	if ((sp=symlookup("self-insert-command")) == NULL)
		abort();
	binding[ KCTRL|'I' ] = sp;
	for ( i = ' ' ; i < DEL ; ++i ) {
		if (binding[i] != NULL)
			continue;
		binding[i] = sp;
		++sp->s_nkey;
	}

	ttykeymapinit();
}

/*
 * 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)
int	(*funcp)();
char	*name;
{
	register SYMBOL	*sp;
	register int	hash;

	if ((sp=(SYMBOL *)malloc(sizeof(SYMBOL))) == NULL)
		abort();
	hash = symhash(name);
	sp->s_symp = symbol[hash];
	symbol[hash] = sp;
	sp->s_nkey = 0;
	sp->s_name = name;
	sp->s_funcp = funcp;
	if (new >= 0) {				/* Bind this key.	*/
		if (binding[new] != NULL)
			abort();
		binding[new] = sp;
		++sp->s_nkey;
	}
}

/*
 * Bind key "new" to the existing routine "name".
 * If the routine doesn't exist, just give a warning.
 * It's no error to rebind a key.
 */
keydup(new, name)
register int	new;
char		*name;
{
	register SYMBOL	*sp;

	if ( (sp=symlookup(name))==NULL )
		return;

	if( new >= 0 )
		binding[new] = sp;

	++sp->s_nkey;
}
