/*	This file is for functions having to do with key bindings,
	descriptions, help commands and startup file.

	written 11-feb-86 by Daniel Lawrence
							   */

#include	<stdio.h>
#include	"estruct.h"
#include	"etype.h"
#include	"edef.h"
#include	"elang.h"
#include	"epath.h"

PASCAL NEAR help(f, n)	/* give me some help!!!!
		   bring up a fake buffer and read the help file
		   into it with view mode			*/
{
	register BUFFER *bp;	/* buffer pointer to help */
	unsigned char *fname;	/* file name of help file */

	/* first check if we are already here */

	bp = bfind("emacs.hlp", FALSE, BFINVS);

	if (bp == NULL)
	{
#if SHARED
		strcpy(tname, pathname[1]);
		fname = flook(tname, FALSE);
#else
		fname = flook(pathname[1], FALSE);
#endif
		if (fname == NULL)
		{
			mlwrite(TEXT12);
/*				"[Help file is not online]" */
			return(FALSE);
		}
	}

	/* split the current window to make room for the help stuff */

	if (splitwind(FALSE, 1) == FALSE)
			return(FALSE);

	if (bp == NULL)
	{
		/* and read the stuff in */

		if (getfile(fname, FALSE) == FALSE)
			return(FALSE);
	}
	else
		swbuffer(bp);

	/* make this window in VIEW mode, update all mode lines */

	curwp->w_bufp->b_mode |= MDVIEW;
	curwp->w_bufp->b_flag |= BFINVS;
	upmode();
	return(TRUE);
}

PASCAL NEAR deskey(f, n)	/* describe the command for a certain key */
{
	register int c; 		/* key to describe */
	register unsigned char *ptr;	/* string pointer to scan output strings */
	unsigned char outseq[NSTRING];	/* output buffer for command sequence */

	/* prompt the user to type us a key to describe */

	mlwrite(TEXT13);
/*		": describe-key " */

	/* get the command sequence to describe
	   change it to something we can print as well */

	cmdstr(c = getckey(FALSE), &outseq[0]);

	/* and dump it out */

	ostring(outseq);
	ostring(" ");

	/* find the right ->function */

	if ((ptr = getfname(getbind(c))) == NULL)
		ptr = "Not Bound";

	/* output the command sequence */

	ostring(ptr);
}

/* bindtokey:	add a new key to the key binding table		*/

PASCAL NEAR bindtokey(f, n)
int f, n;	/* command arguments [IGNORED] */
{
	register unsigned int c;	/* command key to bind */
	register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
	register KEYTAB *ktp;		/* pointer into the command table */
	register int found;		/* matched command flag */
	unsigned char outseq[80];	/* output buffer for keystroke sequence */

	/* prompt the user to type in a key to bind */
	/* get the function name to bind it to */

	kfunc = getname(TEXT15);
/*			": bind-to-key " */

	if (kfunc == NULL)
	{
		mlwrite(TEXT16);
/*			"[No such function]" */
		return(FALSE);
	}
	ostring(" ");
	TTflush();

	/* get the command sequence to bind */

	c = getckey((kfunc == meta) || (kfunc == cex) ||
		    (kfunc == unarg) || (kfunc == ctrlg));

	/* change it to something we can print as well */

	cmdstr(c, &outseq[0]);

	/* and dump it out */

	ostring(outseq);

	/* if the function is a unique prefix key */

	if (kfunc == unarg || kfunc == ctrlg)
	{

		/* search for an existing binding for the prefix key */

		ktp = &keytab[0];

		while (ktp->k_ptr.fp != NULL)
		{
			if (ktp->k_ptr.fp == kfunc)
				unbindchar(ktp->k_code);
			++ktp;
		}

		/* reset the appropriate global prefix variable */

		if (kfunc == unarg)
			reptc = c;

		if (kfunc == ctrlg)
			abortc = c;
	}

	/* search the table to see if it exists */

	ktp = &keytab[0];
	found = FALSE;

	while (ktp->k_ptr.fp != NULL)
	{
		if (ktp->k_code == c)
		{
			found = TRUE;
			break;
		}
		++ktp;
	}

	if (found)	/* it exists, just change it then */
	{
		ktp->k_ptr.fp = kfunc;
		ktp->k_type = BINDFNC;
	}
	else   /* otherwise we need to add it to the end */
	{	/* if we run out of binding room, bitch */

		if (ktp >= &keytab[NBINDS])
		{
			mlwrite(TEXT17);
/*				"Binding table FULL!" */
			return(FALSE);
		}

		ktp->k_code = c;	/* add keycode */
		ktp->k_ptr.fp = kfunc;	/* and the function pointer */
		ktp->k_type = BINDFNC;	/* and the binding type */
		++ktp;			/* and make sure the next is null */
		ktp->k_code = 0;
		ktp->k_type = BINDNUL;
		ktp->k_ptr.fp = NULL;
	}

	/* if we have rebound the meta key, make the
	   search terminator follow it		       */

	if (kfunc == meta)
		sterm = c;

	return(TRUE);
}

/* macrotokey:	Bind a key to a macro in the key binding table */

PASCAL NEAR macrotokey(f, n)
int f, n;	/* command arguments [IGNORED] */
{
	register unsigned int c;	/* command key to bind */
	register BUFFER *kmacro;	/* ptr to buffer of macro to bind to key */
	register KEYTAB *ktp;		/* pointer into the command table */
	register int found;		/* matched command flag */
	register int status;		/* error return */
	unsigned char outseq[80];	/* output buffer for keystroke sequence */
	unsigned char bufn[NBUFN];	/* buffer to hold macro name */

	/* get the buffer name to use */

	if ((status=mlreply(TEXT215, &bufn[1], NBUFN-2)) != TRUE)
/*		": macro-to-key " */
		return(status);

	/* build the responce string for later */

	strcpy(outseq, TEXT215);
/*		   ": macro-to-key " */
	strcat(outseq, &bufn[1]);

	/* translate it to a buffer pointer */

	bufn[0] = '[';
	strcat(bufn, "]");

	if ((kmacro=bfind(bufn, FALSE, 0)) == NULL)
	{
		mlwrite(TEXT130);
/*		"Macro not defined"*/
		return(FALSE);
	}

	strcat(outseq, " ");
	mlwrite(outseq);

	/* get the command sequence to bind */

	c = getckey(FALSE);

	/* change it to something we can print as well */

	cmdstr(c, &outseq[0]);

	/* and dump it out */

	ostring(outseq);

	/* search the table to see if it exists */

	ktp = &keytab[0];
	found = FALSE;

	while (ktp->k_type != BINDNUL)
	{
		if (ktp->k_code == c)
		{
			found = TRUE;
			break;
		}
		++ktp;
	}

	if (found)	/* it exists, just change it then */
	{
		ktp->k_ptr.buf = kmacro;
		ktp->k_type = BINDBUF;
	}
	else	/* otherwise we need to add it to the end */
	{	/* if we run out of binding room, bitch */

		if (ktp >= &keytab[NBINDS])
		{
			mlwrite(TEXT17);
/*				"Binding table FULL!" */
			return(FALSE);
		}

		ktp->k_code = c;		/* add keycode */
		ktp->k_ptr.buf = kmacro;	/* and the function pointer */
		ktp->k_type = BINDBUF;		/* and the binding type */
		++ktp;				/* and make sure the next is null */
		ktp->k_code = 0;
		ktp->k_type = BINDNUL;
		ktp->k_ptr.fp = NULL;
	}

	return(TRUE);
}

/* unbindkey:	delete a key from the key binding table */

PASCAL NEAR unbindkey(f, n)
int f, n;	/* command arguments [IGNORED] */
{
	register int c; 		/* command key to unbind */
	unsigned char outseq[80];	/* output buffer for keystroke sequence */

	/* prompt the user to type in a key to unbind */

	mlwrite(TEXT18);
/*		": unbind-key " */

	/* get the command sequence to unbind */

	c = getckey(FALSE);		/* get a command sequence */

	/* change it to something we can print as well */

	cmdstr(c, &outseq[0]);

	/* and dump it out */

	ostring(outseq);

	/* if it isn't bound, bitch */

	if (unbindchar(c) == FALSE)
	{
		mlwrite(TEXT19);
/*			"[Key not bound]" */
		return(FALSE);
	}
	return(TRUE);
}

PASCAL NEAR unbindchar(c)
int c;		/* command key to unbind */
{
	register KEYTAB *ktp;	/* pointer into the command table */
	register KEYTAB *sktp;	/* saved pointer into the command table */
	register int found;	/* matched command flag */

	/* search the table to see if the key exists */

	ktp = &keytab[0];
	found = FALSE;

	while (ktp->k_type != BINDNUL)
	{
		if (ktp->k_code == c)
		{
			found = TRUE;
			break;
		}
		++ktp;
	}

	/* if it isn't bound, bitch */

	if (!found)
		return(FALSE);

	/* save the pointer and scan to the end of the table */

	sktp = ktp;

	while (ktp->k_ptr.fp != NULL)
		++ktp;
	--ktp;		/* backup to the last legit entry */

	/* copy the last entry to the current one */

	sktp->k_code = ktp->k_code;
	sktp->k_type = ktp->k_type;
	sktp->k_ptr.fp	 = ktp->k_ptr.fp;

	/* null out the last one */

	ktp->k_code = 0;
	ktp->k_type = BINDNUL;
	ktp->k_ptr.fp = NULL;
	return(TRUE);
}

/* Describe bindings:

	   bring up a fake buffer and list the key bindings
	   into it with view mode
*/

PASCAL NEAR desbind(f, n)

#if	APROP
{
	return(buildlist(TRUE, ""));
}

PASCAL NEAR apro(f, n)	/* Apropos (List functions that match a substring) */
{
	unsigned char mstring[NSTRING];	/* string to match cmd names to */
	int status;			/* status return */

	status = mlreply(TEXT20, mstring, NSTRING - 1);
/*			 "Apropos string: " */

	if (status != TRUE)
		return(status);

	return(buildlist(FALSE, mstring));
}

PASCAL NEAR buildlist(type, mstring)  /* build a binding list (limited or full) */
int type;		/* true = full list,   false = partial list */
unsigned char *mstring;	/* match string if a partial list */

#endif
{
	register WINDOW *wp;		/* scanning pointer to windows */
	register KEYTAB *ktp;		/* pointer into the command table */
	register NBIND *nptr;		/* pointer into the name binding table */
	register BUFFER *bp;		/* buffer to put binding list into */
	int cpos;			/* current position to use in outseq */
	int cmark;			/* current mark */
	unsigned char outseq[80];	/* output buffer for keystroke sequence */

	/* split the current window to make room for the binding list */

	if (splitwind(FALSE, 1) == FALSE)
		return(FALSE);

	/* and get a buffer for it */

	bp = bfind(TEXT21, TRUE, 0);
/*		   "Binding list" */

	if (bp == NULL || bclear(bp) == FALSE)
	{
		mlwrite(TEXT22);
/*			"Can not display binding list" */
		return(FALSE);
	}

	/* let us know this is in progress */

	mlwrite(TEXT23);
/*		"[Building binding list]" */

	/* disconect the current buffer */

	if (--curbp->b_nwnd == 0)		/* Last use.		*/
	{
		curbp->b_dotp  = curwp->w_dotp;
		curbp->b_doto  = curwp->w_doto;

		for (cmark = 0; cmark < NMARKS; cmark++)
		{
			curbp->b_markp[cmark] = curwp->w_markp[cmark];
			curbp->b_marko[cmark] = curwp->w_marko[cmark];
		}
		curbp->b_fcol  = curwp->w_fcol;
	}

	/* connect the current window to this buffer */

	curbp = bp;		/* make this buffer current in current window */
	bp->b_mode = 0; 	/* no modes active in binding list */
	bp->b_nwnd++;		/* mark us as more in use */
	wp = curwp;
	wp->w_bufp = bp;
	wp->w_linep = bp->b_linep;
	wp->w_flag = WFHARD|WFFORCE;
	wp->w_dotp = bp->b_dotp;
	wp->w_doto = bp->b_doto;

	for (cmark = 0; cmark < NMARKS; cmark++)
	{
		wp->w_markp[cmark] = NULL;
		wp->w_marko[cmark] = 0;
	}

	/* build the contents of this window, inserting it line by line */

	nptr = &names[0];

	while (nptr->n_func != NULL)
	{
		/* add in the command name */

		strcpy(outseq, nptr->n_name);
		cpos = strlen(outseq);

#if	APROP
		/* if we are executing an apropos command..... */

		if (type == FALSE &&
		    /* and current string doesn't include the search string */
		    strinc(outseq, mstring) == FALSE)
			goto fail;
#endif
		/* search down any keys bound to this */

		ktp = &keytab[0];

		while (ktp->k_type != BINDNUL)
		{
			if (ktp->k_type == BINDFNC &&
			    ktp->k_ptr.fp == nptr->n_func)
			{
				/* padd out some spaces */

				while (cpos < 25)
					outseq[cpos++] = ' ';

				/* add in the command sequence */

				cmdstr(ktp->k_code, &outseq[cpos]);
				strcat(outseq, "\r");

				/* and add it as a line into the buffer */

				if (linstr(outseq) != TRUE)
					return(FALSE);

				cpos = 0;	/* and clear the line */
			}
			++ktp;
		}

		/* if no key was bound, we need to dump it anyway */

		if (cpos > 0)
		{
			outseq[cpos++] = '\r';
			outseq[cpos] = 0;

			if (linstr(outseq) != TRUE)
				return(FALSE);
		}

fail:		/* and on to the next name */

		++nptr;
	}

	/* add a blank line between the key and macro lists */

	lnewline();

	/* scan all buffers looking for macroes and their bindings */

	bp = bheadp;

	while (bp)
	{
		/* is this buffer a macro? */

		if (bp->b_bname[0] != '[')
			goto bfail;

		/* add in the command name */

		strcpy(outseq, bp->b_bname);
		cpos = strlen(outseq);

#if	APROP
		/* if we are executing an apropos command..... */

		if (type == FALSE &&
		    /* and current string doesn't include the search string */
		    strinc(outseq, mstring) == FALSE)
			goto bfail;
#endif
		/* search down any keys bound to this macro */

		ktp = &keytab[0];

		while (ktp->k_ptr.fp != NULL)
		{
			if (ktp->k_type == BINDBUF &&
			    ktp->k_ptr.buf == bp)
			{
				/* padd out some spaces */

				while (cpos < 25)
					outseq[cpos++] = ' ';

				/* add in the command sequence */

				cmdstr(ktp->k_code, &outseq[cpos]);
				strcat(outseq, "\r");

				/* and add it as a line into the buffer */

				if (linstr(outseq) != TRUE)
					return(FALSE);

				cpos = 0;	/* and clear the line */
			}
			++ktp;
		}

		/* if no key was bound, we need to dump it anyway */

		if (cpos > 0)
		{
			outseq[cpos++] = '\r';
			outseq[cpos] = 0;

			if (linstr(outseq) != TRUE)
				return(FALSE);
		}

bfail:		/* and on to the next buffer */

		bp = bp->b_bufp;
	}

	curwp->w_bufp->b_mode |= MDVIEW;	/* put this buffer view mode */
	curbp->b_flag &= ~BFCHG;		/* don't flag this as a change */
	wp->w_dotp = lforw(curbp->b_linep);	/* back to the beginning */
	wp->w_doto = 0;
	upmode();
	mlwrite("");   /* clear the mode line */
	return(TRUE);
}

#if	APROP

PASCAL NEAR strinc(source, sub) /* does source include sub? */
unsigned char *source;	/* string to search in */
unsigned char *sub;	/* substring to look for */
{
	unsigned char *sp;	/* ptr into source */
	unsigned char *nxtsp;	/* next ptr into source */
	unsigned char *tp;	/* ptr into substring */

	/* for each character in the source string */

	sp = source;

	while (*sp)
	{
		tp = sub;
		nxtsp = sp;

		/* is the substring here? */

		while (*tp)
		{
			if (*nxtsp++ != *tp)
				break;
			else
			     tp++;
		}

		/* yes, return a success */

		if (*tp == 0)
			return(TRUE);

		/* no, onward */

		sp++;
	}
	return(FALSE);
}
#endif

/* get a command key sequence from the keyboard */

unsigned int PASCAL NEAR getckey(mflag)
int mflag;	/* going for a meta sequence? */
{
	register unsigned int c;	/* character fetched */
	unsigned char tok[NSTRING];	/* command incoming */

	/* check to see if we are executing a command line */

	if (clexec)
	{
		macarg(tok);	/* get the next token */
		return(stock(tok));
	}

	/* or the normal way */

	if (mflag)
		c = getkey();
	else
		c = getcmd();
	return(c);
}

/* execute the startup file */

PASCAL NEAR startup(sfname)
unsigned char *sfname;		/* name of startup file (null if default) */
{
	unsigned char *fname;	/* resulting file name to execute */

	/* look up the startup file */

	if (*sfname != 0)
		fname = flook(sfname, TRUE);
	else
#if SHARED
	{
		strcpy(tname, pathname[0]);
		fname = flook(tname, TRUE	}
#else
		fname = flook(pathname[0], TRUE);
#endif

	/* if it isn't around, don't sweat it */

	if (fname == NULL)
		return(TRUE);

	/* otherwise, execute the sucker */

	return(dofile(fname));
}

/*	Look up the existance of a file along the normal or PATH
	environment variable. Look first in the HOME directory if
	asked and possible
*/

unsigned char *PASCAL NEAR flook(fname, hflag)
unsigned char *fname;	/* base file name to search for */
int hflag;		/* Look in the HOME environment variable first? */
{
	register unsigned char *home;		/* path to home directory */
	register unsigned char *path;		/* environmental PATH variable */
	register unsigned char *sp;		/* pointer into path spec */
	register int i; 			/* index */
	static unsigned char fspec[NFILEN];	/* full path spec to search */
	unsigned char *getenv();

	/* if we have an absolute path.. check only there! */

	sp = fname;

	while (*sp)
	{
#if	ATARI
		if (*sp == ':' || *sp == DIRSEPCHAR)
#else
		if (*sp == ':' || *sp == '\\' || *sp == '/')
#endif
		{
			if (ffropen(fname) == FIOSUC)
			{
				ffclose();
				return(fname);
			}
			else
				return(NULL);
		}
		++sp;
	}

#if	ENVFUNC

	if (hflag)
	{
#if WMCS
		home = getenv("SYS$HOME");
#else
		home = getenv("HOME");
#endif
		if (home != NULL)
		{
			/* build home dir file spec */

			strcpy(fspec, home);
#if WMCS
			strcat(fspec,fname);
#else
			strcat(fspec, DIRSEPSTR);
			strcat(fspec, fname);
#endif
			/* and try it out */

			if (ffropen(fspec) == FIOSUC)
			{
				ffclose();
				return(fspec);
			}
		}
	}
#endif

	/* always try the current directory first */

	if (ffropen(fname) == FIOSUC)
	{
		ffclose();
		return(fname);
	}

#if	ENVFUNC
	/* get the PATH variable */
#if WMCS
	path = getenv("OPT$PATH");
#else
	path = getenv("PATH");
#endif
	if (path != NULL)
	{
		while (*path)
		{
			/* build next possible file spec */

			sp = fspec;
#if	ST520 & MWC
			while (*path && (*path != PATHCHR) && (*path != ','))
#else
			while (*path && (*path != PATHCHR))
#endif
				*sp++ = *path++;

			/* add a terminating dir separator if we need it */

			if (*(sp-1) != DIRSEPCHAR)
				*sp++ = DIRSEPCHAR;
			*sp = 0;

			strcat(fspec, fname);

			/* and try it out */

			if (ffropen(fspec) == FIOSUC)
			{
				ffclose();
				return(fspec);
			}

#if	ST520 & MWC
			if ((*path == PATHCHR) || (*path == ','))
#else
			if (*path == PATHCHR)
#endif
				++path;
		}
	}
#endif

	/* look it up via the old table method */

	for (i=2; i < NPNAMES; i++)
	{
		strcpy(fspec, pathname[i]);
		strcat(fspec, fname);

		/* and try it out */

		if (ffropen(fspec) == FIOSUC)
		{
			ffclose();
			return(fspec);
		}
	}

	return(NULL);	/* no such luck */
}

PASCAL NEAR cmdstr(c, seq) /* change a key command to a string we can print out */
int c;			/* sequence to translate */
unsigned char *seq;	/* destination string for sequence */
{
	unsigned char *ptr;	/* pointer into current position in sequence */

	ptr = seq;

	/* apply ^X sequence if needed */

	if (c & CTLX)
	{
		*ptr++ = '^';
		*ptr++ = 'X';
	}

	/* apply ALT key sequence if needed */

	if (c & ALTD)
	{
		*ptr++ = 'A';
		*ptr++ = '-';
	}

	/* apply Shifted sequence if needed */

	if (c & SHFT)
	{
		*ptr++ = 'S';
		*ptr++ = '-';
	}

	/* apply MOUS sequence if needed */

	if (c & MOUS)
	{
		*ptr++ = 'M';
		*ptr++ = 'S';
	}

	/* apply meta sequence if needed */

	if (c & META)
	{
		*ptr++ = 'M';
		*ptr++ = '-';
	}

	/* apply SPEC sequence if needed */

	if (c & SPEC)
	{
		*ptr++ = 'F';
		*ptr++ = 'N';
	}

	/* apply control sequence if needed */

	if (c & CTRL)
	{
	       *ptr++ = '^';
	}

	c = c & 255;	/* strip the prefixes */

	/* and output the final sequence */

	*ptr++ = c;
	*ptr = 0;      /* terminate the string */
}

/*	This function looks a key binding up in the binding table	*/

KEYTAB *getbind(c)
int c;	/* key to find what is bound to it */
{
	register KEYTAB *ktp;

	/* scan through the binding table, looking for the key's entry */

	ktp = &keytab[0];

	while (ktp->k_type != BINDNUL)
	{
		if (ktp->k_code == c)
			return(ktp);
		++ktp;
	}

	/* no such binding */

	return((KEYTAB *)NULL);
}

/* getfname:	This function takes a ptr to KEYTAB entry and gets the name
		associated with it
*/

unsigned char *PASCAL NEAR getfname(key)
KEYTAB *key;	/* key binding to return a name of */
{
	int (PASCAL NEAR *func)(); /* ptr to the requested function */
	register NBIND *nptr;	/* pointer into the name binding table */
	register BUFFER *bp;	/* ptr to buffer to test */
	register BUFFER *kbuf;	/* ptr to requested buffer */

	/* if this isn't a valid key, it has no name */

	if (key == NULL)
		return(NULL);

	/* skim through the binding table, looking for a match */

	if (key->k_type == BINDFNC)
	{
		func = key->k_ptr.fp;
		nptr = &names[0];

		while (nptr->n_func != NULL)
		{
			if (nptr->n_func == func)
				return(nptr->n_name);
			++nptr;
		}
		return(NULL);
	}

	/* skim through the buffer list looking for a match */

	kbuf = key->k_ptr.buf;
	bp = bheadp;

	while (bp)
	{
		if (bp == kbuf)
			return(bp->b_bname);

		bp = bp->b_bufp;
	}
	return(NULL);
}

/* fncmatch:	match fname to a function in the names table and return
		any match or NULL if none */

int (PASCAL NEAR *PASCAL NEAR fncmatch(fname))()
unsigned char *fname;	/* name to attempt to match */
{
#if	BINARY
	register int nval;     /* value of matched name */

	nval = binary(fname, namval, numfunc);

	if (nval == -1)
		return(NULL);
	else
		return(names[nval].n_func);

#else
	register NBIND *ffp;	/* pointer to entry in name binding table */

	/* scan through the table, returning any match */

	ffp = &names[0];

	while (ffp->n_func != NULL)
	{
		if (strcmp(fname, ffp->n_name) == 0)
			return(ffp->n_func);

		++ffp;
	}
	return(NULL);
#endif
}

#if	BINARY

unsigned char *PASCAL NEAR namval(index)
int index;	/* index of name to fetch out of the name table */
{
	return(names[index].n_name);
}
#endif

/*	stock() 	String key name TO Command Key

	A key binding consists of one or more prefix functions followed by
	a keystroke.  Allowable prefixes must be in the following order:

	^X	preceeding control-X
	A-	simeltaneous ALT key (on PCs mainly)
	S-	shifted function key
	MS	mouse generated keystroke
	M-	Preceding META key
	FN	function key
	^	control key

	Meta and ^X prefix of lower case letters are converted to upper
	case.  Real control characters are automatically converted to
	the ^A form.
*/

unsigned int PASCAL NEAR stock(keyname)
unsigned char *keyname;	/* name of key to translate to Command key form */
{
	register unsigned int c;	/* key sequence to return */

	/* parse it up */

	c = 0;

	/* Do ^X prefix */

	if(*keyname == '^' && *(keyname+1) == 'X')
	{
		if(*(keyname+2) != 0)  /* Key is not bare ^X */
		{
		    c |= CTLX;
		    keyname += 2;
		}
	}

	/* and the ALT key prefix */

	if (*keyname == 'A' && *(keyname+1) == '-')
	{
		c |= ALTD;
		keyname += 2;
	}

	/* and the SHIFTED prefix */

	if (*keyname == 'S' && *(keyname+1) == '-')
	{
		c |= SHFT;
		keyname += 2;
	}

	/* and the mouse (MOUS) prefix */

	if (*keyname == 'M' && *(keyname+1) == 'S')
	{
		c |= MOUS;
		keyname += 2;
	}

	/* then the META prefix */

	if (*keyname == 'M' && *(keyname+1) == '-')
	{
		c |= META;
		keyname += 2;
	}

	/* next the function prefix */

	if (*keyname == 'F' && *(keyname+1) == 'N')
	{
		c |= SPEC;
		keyname += 2;
	}

	/* a control char?  (Always upper case) */

	if (*keyname == '^' && *(keyname+1) != 0)
	{
		c |= CTRL;
		++keyname;
		uppercase(keyname);
	}

	/* A literal control character? (Boo, hiss) */

	if (*keyname < 32)
	{
		c |= CTRL;
		*keyname += '@';
	}

	/* make sure we are not lower case if used with ^X or M- */

	if(!(c & (MOUS|SPEC|ALTD|SHFT)))	/* If not a special key */
		if( c & (CTLX|META))		/* If is a prefix */
			uppercase(keyname);	/* Then make sure it's upper case */

	/* the final sequence... */

	c |= *keyname;
	return(c);
}

unsigned char *PASCAL NEAR transbind(skey)	/* string key name to binding name.... */
unsigned char *skey;	/* name of key to get binding for */
{
	unsigned char *bindname;

	bindname = getfname(getbind(stock(skey)));

	if (bindname == NULL)
		bindname = errorm;

	return(bindname);
}

int PASCAL NEAR execkey(key, f, n)	/* execute a function bound to a key */
KEYTAB *key;	/* key to execute */
int f, n;	/* agruments to C function */
{
	register int status;	/* error return */

	if (key->k_type == BINDFNC)
		return((*(key->k_ptr.fp))(f, n));

	if (key->k_type == BINDBUF)
	{
		while (n--)
		{
			status = dobuf(key->k_ptr.buf);

			if (status != TRUE)
				return(status);
		}
	}
	return(TRUE);
}

/* set a KEYTAB to the given name of the given type */

setkey(key, type, name)
KEYTAB *key;		/* ptr to key to set */
short type;		/* type of binding */
unsigned char *name;	/* name of function or buffer */
{
	key->k_type = type;

	if (type == BINDFNC)
		key->k_ptr.fp = fncmatch(name);

	else if (type == BINDBUF)
		/* not quite yet... */;
}
