/*	This file is for functions having to do with key bindings,
	descriptions, help commands, and command line execution.

	written 11-feb-86 by Daniel Lawrence
								*/

#include	<stdio.h>
#include	"estruct.h"
#include	"edef.h"
#include	"epath.h"

deskey(f, n)	/* describe the command for a certain key */
{
	register int c;		/* command character to describe */
	register char *ptr;	/* string pointer to scan output strings */
	register KEYTAB *ktp;	/* pointer into the command table */
	register int found;	/* matched command flag */
	register NBIND *nptr;	/* pointer into the name binding table */
	char outseq[80];	/* output buffer for command sequence */

	/* prompt the user to type us a key to describe */
	mlwrite(": describe-key ");

	/* get the command sequence to describe */
	c = getckey();			/* get a command sequence */

	/* change it to something we can print as well */
	cmdstr(c, &outseq[0]);

	/* and dump it out */
	ptr = &outseq[0];
	while (*ptr)
		(*term.t_putchar)(*ptr++);
	(*term.t_putchar)(' ');		/* space it out */

	/* find the right ->function */
	ktp = &keytab[0];
	found = FALSE;
	while (ktp->k_fp != NULL) {
		if (ktp->k_code == c) {
			found = TRUE;
			break;
		}
		++ktp;
	}

	if (!found)
		strcpy(outseq,"Not Bound");
	else {
		/* match it against the name binding table */
		nptr = &names[0];
		strcpy(outseq,"[Bad binding]");
		while (nptr->n_func != NULL) {
			if (nptr->n_func == ktp->k_fp) {
				strcpy(outseq, nptr->n_name);
				break;
			}
			++nptr;
		}
	}

	/* output the command sequence */
	ptr = &outseq[0];
	while (*ptr)
		(*term.t_putchar)(*ptr++);
}

cmdstr(c, seq)	/* change a key command to a string we can print out */

int c;		/* sequence to translate */
char *seq;	/* destination string for sequence */

{
	char *ptr;	/* pointer into current position in sequence */

	ptr = seq;

	/* apply meta sequence if needed */
	if (c & META) {
		*ptr++ = 'M';
		*ptr++ = '-';
	}

	/* apply ^X sequence if needed */
	if (c & CTLX) {
		*ptr++ = '^';
		*ptr++ = 'X';
	}

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

help(f, n)	/* give me some help!!!!
		   bring up a fake buffer and read the help file
		   into it with view mode			*/
{
	register int status;	/* status of I/O operations */
	register WINDOW *wp;	/* scnaning pointer to windows */
	register int i;		/* index into help file names */
	char fname[NSTRING];	/* buffer to construct file name in */

	/* search through the list of help files */
	for (i=2; i < NPNAMES; i++) {
		strcpy(fname, pathname[i]);
		strcat(fname, pathname[1]);
		status = ffropen(fname);
		if (status == FIOSUC)
			break;
	}

	if (status == FIOFNF) {
		mlwrite("[Help file is not online]");
		return(FALSE);
	}
	ffclose();	/* close the file to prepare for to read it in */

	/* split the current window to make room for the help stuff */
	if (splitwind(FALSE, 1) == FALSE)
			return(FALSE);

	/* and read the stuff in */
	if (getfile(fname, FALSE) == FALSE)
		return(FALSE);

	/* make this window in VIEW mode, update all mode lines */
	curwp->w_bufp->b_mode |= MDVIEW;
	wp = wheadp;
	while (wp != NULL) {
		wp->w_flag |= WFMODE;
		wp = wp->w_wndp;
	}
	return(TRUE);
}

int (*fncmatch(fname))() /* match fname to a function in the names table
			    and return any match or NULL if none		*/

char *fname;	/* name to attempt to match */

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

/* bindtokey:	add a new key to the key binding table
*/

bindtokey(f, n)

int f, n;	/* command arguments [IGNORED] */

{
	register int c;		/* command key to bind */
	register (*kfunc)();	/* ptr to the requexted function to bind to */
	register char *ptr;	/* ptr to dump out input key string */
	register KEYTAB *ktp;	/* pointer into the command table */
	register int found;	/* matched command flag */
	char outseq[80];	/* output buffer for keystroke sequence */
	int (*getname())();

	/* prompt the user to type in a key to bind */
	mlwrite(": bind-to-key ");

	/* get the function name to bind it to */
	kfunc = getname();
	if (kfunc == NULL) {
		mlwrite("[No such function]");
		return(FALSE);
	}
	(*term.t_putchar)(' ');		/* space it out */
	(*term.t_flush)();

	/* get the command sequence to bind */
	c = getckey();			/* get a command sequence */

	/* change it to something we can print as well */
	cmdstr(c, &outseq[0]);

	/* and dump it out */
	ptr = &outseq[0];
	while (*ptr)
		(*term.t_putchar)(*ptr++);

	/* search the table to see if it exists */
	ktp = &keytab[0];
	found = FALSE;
	while (ktp->k_fp != NULL) {
		if (ktp->k_code == c) {
			found = TRUE;
			break;
		}
		++ktp;
	}

	if (found) {	/* it exists, just change it then */
		ktp->k_fp = kfunc;
	} else {	/* otherwise we need to add it to the end */
		/* if we run out of binding room, bitch */
		if (ktp >= &keytab[NBINDS]) {
			mlwrite("Binding table FULL!");
			return(FALSE);
		}

		ktp->k_code = c;	/* add keycode */
		ktp->k_fp = kfunc;	/* and the function pointer */
		++ktp;			/* and make sure the next is null */
		ktp->k_code = 0;
		ktp->k_fp = NULL;
	}

	return(TRUE);
}

/* unbindkey:	delete a key from the key binding table
*/

unbindkey(f, n)

int f, n;	/* command arguments [IGNORED] */

{
	register int c;		/* command key to unbind */
	register char *ptr;	/* ptr to dump out input key string */
	register KEYTAB *ktp;	/* pointer into the command table */
	register KEYTAB *sktp;	/* saved pointer into the command table */
	register int found;	/* matched command flag */
	char outseq[80];	/* output buffer for keystroke sequence */

	/* prompt the user to type in a key to unbind */
	mlwrite(": unbind-key ");

	/* get the command sequence to unbind */
	c = getckey();			/* get a command sequence */

	/* change it to something we can print as well */
	cmdstr(c, &outseq[0]);

	/* and dump it out */
	ptr = &outseq[0];
	while (*ptr)
		(*term.t_putchar)(*ptr++);

	/* search the table to see if the key exists */
	ktp = &keytab[0];
	found = FALSE;
	while (ktp->k_fp != NULL) {
		if (ktp->k_code == c) {
			found = TRUE;
			break;
		}
		++ktp;
	}

	/* if it isn't bound, bitch */
	if (!found) {
		mlwrite("[Key not bound]");
		return(FALSE);
	}

	/* save the pointer and scan to the end of the table */
	sktp = ktp;
	while (ktp->k_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_fp   = ktp->k_fp;

	/* null out the last one */
	ktp->k_code = 0;
	ktp->k_fp = NULL;
	return(TRUE);
}

/* namedcmd:	execute a named command even if it is not bound
*/

namedcmd(f, n)

int f, n;	/* command arguments [passed through to command executed] */

{
	register (*kfunc)();	/* ptr to the requexted function to bind to */
	int (*getname())();

	/* prompt the user to type a named command */
	mlwrite(": ");

	/* and now get the function name to execute */
	kfunc = getname();
	if (kfunc == NULL) {
		mlwrite("[No such function]");
		return(FALSE);
	}

	/* and then execute the command */
	return((*kfunc)(f, n));
}

desbind(f, n)	/* describe bindings
		   bring up a fake buffer and list the key bindings
		   into it with view mode			*/
{
	register WINDOW *wp;	/* scnaning 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 */
	char *strp;		/* pointer int string to send */
	int cpos;		/* current position to use in outseq */
	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("Binding list", TRUE, 0);
	if (bp == NULL || bclear(bp) == FALSE) {
		mlwrite("Can not display binding list");
		return(FALSE);
	}

	/* let us know this is in progress */
	mlwrite("[Building buffer list]");

	/* disconect the current buffer */
        if (--curbp->b_nwnd == 0) {             /* Last use.            */
                curbp->b_dotp  = curwp->w_dotp;
                curbp->b_doto  = curwp->w_doto;
                curbp->b_markp = curwp->w_markp;
                curbp->b_marko = curwp->w_marko;
        }

	/* 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|WFHARD;
	wp->w_dotp = bp->b_dotp;
	wp->w_doto = bp->b_doto;
	wp->w_markp = NULL;
	wp->w_marko = 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);
		
		/* search down any keys bound to this */
		ktp = &keytab[0];
		while (ktp->k_fp != NULL) {
			if (ktp->k_fp == nptr->n_func) {
				/* padd out some spaces */
				while (cpos < 25)
					outseq[cpos++] = ' ';

				/* add in the command sequence */
				cmdstr(ktp->k_code, &outseq[cpos]);
				while (outseq[cpos] != 0)
					++cpos;

				/* and add it as a line into the buffer */
				strp = &outseq[0];
				while (*strp != 0)
					linsert(1, *strp++);
				lnewline();

				cpos = 0;	/* and clear the line */
			}
			++ktp;
		}

		/* if no key was bound, we need to dump it anyway */
		if (cpos > 0) {
			outseq[cpos] = 0;
			strp = &outseq[0];
			while (*strp != 0)
				linsert(1, *strp++);
			lnewline();
		}

		/* and on to the next name */
		++nptr;
	}

	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(bp->b_linep);/* back to the begining */
	wp->w_doto = 0;
	wp = wheadp;			/* and update ALL mode lines */
	while (wp != NULL) {
		wp->w_flag |= WFMODE;
		wp = wp->w_wndp;
	}
	mlwrite("");	/* clear the mode line */
	return(TRUE);
}

/*	execcmd:	Execute a command line command to be typed in
			by the user					*/

execcmd(f, n)

int f, n;	/* default Flag and Numeric argument */

{
	register int status;		/* status return */
	char cmdstr[NSTRING];		/* string holding command to execute */

	/* get the line wanted */
	if ((status = mlreply(": ", cmdstr, NSTRING)) != TRUE)
		return(status);

	return(docmd(cmdstr));
}

/*	docmd:	take a passed string as a command line and translate
		it to be executed as a command. This function will be
		used by execute-command-line and by all source and
		startup files.

	format of the command line is:

		{# arg} <command-name> {<argument string(s)>}
*/

docmd(cline)

char *cline;	/* command line to execute */

{
	register char *cp;	/* pointer to current position in command */
	register char *tp;	/* pointer to current position in token */
	register int f;		/* default argument flag */
	register int n;		/* numeric repeat value */
	register int sign;	/* sign of numeric argument */
	register int (*fnc)();	/* function to execute */
	register int status;	/* return status of function */
	register int oldcle;	/* old contents of clexec flag */
	char token[NSTRING];	/* next token off of command line */
	int (*fncmatch())();
	char *gettok();

	/* first set up the default command values */
	f = FALSE;
	n = 1;

	cp = cline;		/* start at the begining of the line */
	cp = gettok(cp, token);	/* and grab the first token */

	/* check for and process numeric leadin argument */
	if ((token[0] >= '0' && token[0] <= '9') || token[0] == '-') {
		f = TRUE;
		n = 0;
		tp = &token[0];

		/* check for a sign! */
		sign = 1;
		if (*tp == '-') {
			++tp;
			sign = -1;
		}

		/* calc up the digits in the token string */
		while(*tp) {
			if (*tp >= '0' && *tp <= '9')
				n = n * 10 + *tp - '0';
			++tp;
		}
		n *= sign;	/* adjust for the sign */

		/* and now get the command to execute */
		cp = gettok(cp, token);		/* grab the next token */
	}

	/* and match the token to see if it exists */
	if ((fnc = fncmatch(token)) == NULL) {
		mlwrite("[No such Function]");
		return(FALSE);
	}
	
	/* save the arguments and go execute the command */
	strcpy(sarg, cp);		/* save the rest */
	oldcle = clexec;		/* save old clexec flag */
	clexec = TRUE;			/* in cline execution */
	status = (*fnc)(f, n);		/* call the function */
	clexec = oldcle;		/* restore clexec flag */
	return(status);
}

/* gettok:	chop a token off a string
		return a pointer past the token
*/

char *gettok(src, tok)

char *src, *tok;	/* source string, destination token */

{
	/* first scan past any whitespace in the source string */
	while (*src == ' ' || *src == '\t')
		++src;

	/* if quoted, go till next quote */
	if (*src == '"') {
		++src;		/* past the quote */
		while (*src != 0 && *src != '"')
			*tok++ = *src++;
		++src;		/* past the last quote */
		*tok = 0;	/* terminate token and return */
		return(src);
	}

	/* copy until we find the end or whitespace */
	while (*src != 0 && *src != ' ' && *src != '\t')
		*tok++ = *src++;

	/* terminate tok and return */
	*tok = 0;
	return(src);
}

/* nxtarg:	grab the next token out of sarg, return it, and
		chop it of sarg					*/

nxtarg(tok)

char *tok;	/* buffer to put token into */

{
	char *newsarg;	/* pointer to new begining of sarg */
	char *gettok();

	newsarg = gettok(sarg, tok);	/* grab the token */
	strcpy(sarg, newsarg);		/* and chop it of sarg */
	return(TRUE);
}

getckey()	/* get a command key sequence from the keyboard	*/

{
	register int c;		/* character fetched */
	register char *tp;	/* pointer into the token */
	char tok[NSTRING];	/* command incoming */

	/* check to see if we are executing a command line */
	if (clexec) {
		nxtarg(tok);	/* get the next token */

		/* parse it up */
		tp = &tok[0];
		c = 0;

		/* first, the META prefix */
		if (*tp == 'M' && *(tp+1) == '-') {
			c = META;
			tp += 2;
		}

		/* next the function prefix */
		if (*tp == 'F' && *(tp+1) == 'N') {
			c |= SPEC;
			tp += 2;
		}

		/* control-x as well... */
		if (*tp == '^' && *(tp+1) == 'X') {
			c |= CTLX;
			tp += 2;
		}

		/* a control char? */
		if (*tp == '^' & *(tp+1) != 0) {
			c |= CTRL;
			++tp;
		}

		/* make sure we are not lower case */
		if (c >= 'a' && c <= 'z')
			c -= 32;

		/* the final sequence... */
		c |= *tp;

		return(c);
	}

	/* or the normal way */
	c = getkey();			/* get a command sequence */
	if (c == (CTRL|'X'))		/* get control-x sequence */
		c = CTLX | getctl();
	return(c);
}

/*	execbuf:	Execute the contents of a named buffer	*/

execbuf(f, n)

int f, n;	/* default flag and numeric arg */

{
        register BUFFER *bp;		/* ptr to buffer to execute */
        register int status;		/* status return */
        char bufn[NBUFN];		/* name of buffer to execute */

	/* find out what buffer the user wants to execute */
        if ((status = mlreply("Execute buffer: ", bufn, NBUFN)) != TRUE)
                return(status);

	/* find the pointer to that buffer */
        if ((bp=bfind(bufn, TRUE, 0)) == NULL)
                return(FALSE);

	/* and now execute it as asked */
	while (n-- > 0)
		if ((status = dobuf(bp)) != TRUE)
			return(status);
	return(TRUE);
}

/*	dobuf:	execute the contents of the buffer pointed to
		by the passed BP				*/

dobuf(bp)

BUFFER *bp;	/* buffer to execute */

{
        register int status;		/* status return */
	register LINE *lp;		/* pointer to line to execute */
	register LINE *hlp;		/* pointer to line header */
	register int linlen;		/* length of line to execute */
	register WINDOW *wp;		/* ptr to windows to scan */
	char eline[NSTRING];		/* text of line to execute */

	/* starting at the beginning of the buffer */
	hlp = bp->b_linep;
	lp = hlp->l_fp;
	while (lp != hlp) {
		/* calculate the line length and make a local copy */
		linlen = lp->l_used;
		if (linlen > NSTRING - 1)
			linlen = NSTRING - 1;
		strncpy(eline, lp->l_text, linlen);
		eline[linlen] = 0;	/* make sure it ends */

		/* if it is not a comment, execute it */
		if (eline[0] != ';' && eline[0] != 0) {
			status = docmd(eline);
			if (status != TRUE) {	/* a command error */
				/* look if buffer is showing */
				wp = wheadp;
				while (wp != NULL) {
					if (wp->w_bufp == bp) {
						/* and point it */
						wp->w_dotp = lp;
						wp->w_doto = 0;
						wp->w_flag |= WFHARD;
					}
					wp = wp->w_wndp;
				}
				/* in any case set the buffer . */
				bp->b_dotp = lp;
				bp->b_doto = 0;
				return(status);
			}
		}
		lp = lp->l_fp;		/* on to the next line */
	}
        return(TRUE);
}

execfile(f, n)	/* execute a series of commands in a file
*/

int f, n;	/* default flag and numeric arg to pass on to file */

{
	register int status;	/* return status of name query */
	char *fname[NSTRING];	/* name of file to execute */

	if ((status = mlreply("File to execute: ", fname, NSTRING -1)) != TRUE)
		return(status);

	/* otherwise, execute it */
	while (n-- > 0)
		if ((status=dofile(fname)) != TRUE)
			return(status);

	return(TRUE);
}

/*	dofile:	yank a file into a buffer and execute it
		if there are no errors, delete the buffer on exit */

dofile(fname)

char *fname;	/* file name to execute */

{
	register BUFFER *bp;	/* buffer to place file to exeute */
	register BUFFER *cb;	/* temp to hold current buf while we read */
	register int status;	/* results of various calls */
	char bname[NBUFN];	/* name of buffer */

	makename(bname, fname);		/* derive the name of the buffer */
	if ((bp = bfind(bname, TRUE, 0)) == NULL) /* get the needed buffer */
		return(FALSE);

	bp->b_mode = MDVIEW;	/* mark the buffer as read only */
	cb = curbp;		/* save the old buffer */
	curbp = bp;		/* make this one current */
	/* and try to read in the file to execute */
	if ((status = readin(fname, FALSE)) != TRUE) {
		curbp = cb;	/* restore the current buffer */
		return(status);
	}

	/* go execute it! */
	curbp = cb;		/* restore the current buffer */
	if ((status = dobuf(bp)) != TRUE)
		return(status);

	/* if not displayed, remove the now unneeded buffer and exit */
	if (bp->b_nwnd == 0)
		zotbuf(bp);
	return(TRUE);
}


/* execute the startup file */

startup()

{
	register int status;	/* status of I/O operations */
	register int i;		/* index into help file names */
	char fname[NSTRING];	/* buffer to construct file name in */

#if	(MSDOS & LATTICE) | V7
	char *homedir;		/* pointer to your home directory */
	char *getenv();
	
	/* get the HOME from the environment */
	if ((homedir = getenv("HOME")) != NULL) {
		/* build the file name */
		strcpy(fname, homedir);
		strcat(fname, "/");
		strcat(fname, pathname[0]);

		/* and test it */
		status = ffropen(fname);
		if (status == FIOSUC) {
			ffclose();
			return(dofile(fname));
		}
	}
#endif

	/* search through the list of startup files */
	for (i=2; i < NPNAMES; i++) {
		strcpy(fname, pathname[i]);
		strcat(fname, pathname[0]);
		status = ffropen(fname);
		if (status == FIOSUC)
			break;
	}

	/* if it isn't around, don't sweat it */
	if (status == FIOFNF)
		return(TRUE);

	ffclose();	/* close the file to prepare for to read it in */

	return(dofile(fname));
}

