/*
*   Extended (M-X) commands.
*/
#include    "def.h"

extern    char    MSG_not_now[];
extern    char    MSG_func[];
extern    char    MSG_unk_func[];
extern    char    MSG_cmd_t_ex[];
extern    char    MSG_unk_ext[];
extern    char    MSG_d_b[];
extern    char    MSG_unbd[];
extern    char    MSG_bnd_to[];
extern    char    MSG_ins_self[];
extern    char    MSG_bnd_file[];
extern    char    MSG_bld_wall[];
extern    char    MSG_wall_head[];
extern    char    MSG_beavrc[];
extern    char    MSG_null[];


#ifdef CUSTOMIZE 

char *flook();

static char *bindnm =
{
	0
};                              /* file name for customized key bindings */
#endif 

/*
* This function modifies the keyboard
* binding table, by adjusting the entries in the
* big "bindings" array. Most of the grief deals with the
* prompting for additional arguments. This code does not
* work right if there is a keyboard macro floating around.
* Should be fixed.
*/
bool bindtokey ()
{

	register int    s;
	register    SYMBOL * sp;
	register int    c;
	char    xname[NXNAME];
#ifdef CUSTOMIZE
	char    xname2[NXNAME];
#endif 

	if (kbdmip != NULL || kbdmop != NULL)
	{
		writ_echo (MSG_not_now);
		return (FALSE);
	}

	if ((s = eread (MSG_func, xname, NXNAME, EFAUTO, NULL)) != TRUE)
		return (s);
	if ((sp = symlookup (xname)) == NULL)
	{
		writ_echo (MSG_unk_func);
		return (FALSE);
	}

#ifdef CUSTOMIZE
	strcpy (xname2, xname);
#endif 
	eputc (' ');
	eputc ('K');
	eputc ('e');
	eputc ('y');
	eputc (':');
	eputc (' ');
	ttflush ();
	c = getkey ();              /* Read key.        */
	keyname (xname, c);         /* Display keyname. */
	eputs (xname);
	ttflush ();
	if (binding[c] != NULL)     /* Unbind old, and  */
		--binding[c] -> s_nkey;
	binding[c] = sp;            /* rebind new.      */
	++sp -> s_nkey;
	sp -> s_modify |= SBOUND;	/* flag as altered key binding */

	return (TRUE);
}


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

	register    SYMBOL * sp;
	register char   s;
	char    xname[NXNAME];

	if ((s = eread (MSG_cmd_t_ex, xname, NXNAME, EFNEW | EFAUTO, NULL)) != TRUE)
		return (s);
	if ((sp = symlookup (xname)) != NULL)
		return ((*sp -> s_funcp) (f, n, KRANDOM));
	writ_echo (MSG_unk_ext);
	return (ABORT);
}


/*
* Read a key from the keyboard, and look it
* up in the binding table. Display the name of the function
* currently bound to the key. Say that the key is not bound
* if it is indeed not bound, or if the type is not a
* "builtin". This is a bit of overkill, because this is the
* only kind of function there is.
*/
bool help ()
{
	register    SYMBOL * sp;
	register int    c;
	char    b[20];
	char    buf[80];

	writ_echo (MSG_d_b);

	c = getkey ();
	keyname (b, c);
	if ((sp = binding[c]) == NULL)
	{
		sprintf (buf, MSG_unbd, b);
		writ_echo (buf);
	}
	else
	{
		sprintf (buf, MSG_bnd_to, b, sp -> s_name);
		writ_echo (buf);
	}
	return (TRUE);
}

/*
*   Sort the lines in the buffer.
*/
void    sort_buf (b_ptr, cnt)
BUFFER  *b_ptr;
int     cnt;
{
	LINE    *lp1, *lp2;
	bool    no_swap;
	int     loop1, loop2;

	for (loop1 = cnt; loop1 > 0; loop1--)
	{
		no_swap = TRUE;
		lp1 = b_ptr -> b_linep -> l_fp; /* point to first line */
		lp2 = lp1 -> l_fp;      /* point to next line */
		for (loop2 = 0; loop2 <= loop1; loop2++)
		{
			/* compare strings and swap if necessary */
			if (0 < strcmp (&lp1 -> l_text[HFUNCCOL], &lp2 -> l_text[HFUNCCOL]))
			{
				lp1 -> l_bp -> l_fp = lp2;  /* get pointer to first string */
				lp2 -> l_fp -> l_bp = lp1;  /* make it point to second string */

				lp1 -> l_fp = lp2 -> l_fp;
				lp2 -> l_bp = lp1 -> l_bp;

				lp1 -> l_bp = lp2;
				lp2 -> l_fp = lp1;

				lp2 -> l_file_offset = lp1 -> l_file_offset;
				lp1 -> l_file_offset = lp2 -> l_file_offset + lp2 -> l_used;

				no_swap = FALSE;
			}
			else
			{
				/* if no swap then advance both pointers */
				lp1 = lp2;
			}
			lp2 = lp1 -> l_fp;
		}
		/* quick exit if sort is finished sooner than expected */
		if (no_swap)
		{
			return;
		}
	}
}

/*
* This function creates a table, listing all
* of the command keys and their current bindings, and stores
* the table in the standard pop-op buffer (the one used by the
* directory list command, the buffer list command, etc.). This
* lets the editor produce it's own wall chart. The bindings to
* "ins-self" are only displayed if there is an argument.
*/
char    wallchart (f, n, k)
{

	register char   s;
	register int    key, i, j;
	register    SYMBOL * sp;
	register char  *cp1;
	register char  *cp2;
	char    buf[64];
	WINDOW  *wp;

	if ((s = bclear (blistp)) != TRUE)/* Clear it out.    */
		return (s);
	i = 0;
	(void) strcpy (blistp -> b_fname, MSG_null);
	blistp -> b_flag = BFVIEW;
	blistp -> b_type = BTHELP;
	writ_echo (MSG_bld_wall);
	sprintf (buf, MSG_wall_head);
	if (addline (buf) == FALSE)
		return(FALSE);
	for (key = 0; key < NKEYS; ++key)
	{
		/* For all keys.    */
		sp = binding[key];
		if (sp != NULL &&
		    (f != FALSE || strcmp (sp -> s_name, MSG_ins_self) != 0))
		{
			cp1 = &buf[0];
			while (cp1 < &buf[HFUNCCOL])/* Goto column 3.  */
				*cp1++ = ' ';
			if ((sp -> s_modify & SBOUND) == 0)	/* comment out default binding */
				buf[0] = '#';
			cp2 = sp -> s_name; /* Add function name.   */
			while (*cp1++ = *cp2++)
				;
			cp1--;
			while (cp1 < &buf[HKEY])/* Goto column 32.  */
				*cp1++ = ' ';
			keyname (&buf[HKEY], key);
			cp1 = &buf[strlen(buf)];
			while (cp1 < &buf[HKEYCODE])/* Goto column 50.  */
				*cp1++ = ' ';
			sprintf (&buf[HKEYCODE], "%4X", key);
			if (addline (buf) == FALSE)
				break; /* lets go with what we have */
			i++;
		}
	}

	/* list unbound functions lest they get lost */
	for (j = 0; j < NSHASH; j++)
	{
		sp = symbol[j];
		while (sp != NULL)
		{
			if (sp -> s_nkey == 0)
			{
				cp1 = &buf[0];
				while (cp1 < &buf[HFUNCCOL])/* Goto column 3.  */
					*cp1++ = ' ';
				buf[0] = '#';
				cp2 = sp -> s_name; /* Add function name.   */
				while (*cp1++ = *cp2++)
					;
				cp1--;
				while (cp1 < &buf[HENDCOL])
					*cp1++ = ' ';
				*cp1 = 0;
				i++;
				if (addline (buf) == FALSE)
					break; /* lets go with what we have */
			}
			sp = sp -> s_symp;
		}
	}
	sort_buf (blistp, i);      /* sort buffer lines */
	popblist ();
	writ_echo (MSG_null);
	/* make new window the current window */
	wp = wheadp;
	while (wp != NULL)
	{
		if (wp -> w_bufp == blistp)
		{
			curwp = wp;
			curbp = wp -> w_bufp;
			return (TRUE);
		}
		wp = wp -> w_wndp;
	}
	return (TRUE);
}

/* check for BEAVFIL and read it in if found
* - also, set local file variable for bindtokey for saving new defs
* (this is some what of a hack as it only handles 'bindtokey' changes at 
* this time - also local file io !!!)
*/
void check_extend (sfname)

char *sfname;	/* name of startup file (null if default) */

{
	char *fname;	/* resulting file name to execute */
	char	rc_name[NFILEN];	/* fixed up name of rc file */
	char	*term;
	char *getenv();
	register    SYMBOL * sp;
	char    funcname[NXNAME + 1];
	char    keybind[NXNAME + 1];
	int     keyval;
	FILE * bindf;

	/* look up the startup file */
	if ((sfname != NULL) && (*sfname != 0))
		fname = flook(sfname, TRUE);
	else
	{
#ifdef UNIX
		/* hidden file under unix */
		strcpy (&rc_name[0], ".");
		strcpy (&rc_name[1], MSG_beavrc);

		if ((term = getenv("TERM")) != 0)
		{
			strcpy (&rc_name[strlen(rc_name)], ".");
			strcpy (&rc_name[strlen(rc_name)], term);
		}
		fname = flook(rc_name, TRUE);
		/* if fixed up name is not there then check original */
		if (fname == NULL)
		{
			/* hidden file under unix */
			strcpy (&rc_name[0], ".");
			strcpy (&rc_name[1], MSG_beavrc);
			fname = flook(rc_name, TRUE);
		}
#else
		strcpy (rc_name, MSG_beavrc);
		fname = flook(rc_name, TRUE);
#ifdef AMIGA
		/* look for .beavrc in the current directory */
		if(!fname) {
			rc_name[0] = '.';
			strcpy (&rc_name[1], MSG_beavrc);
			fname = flook(rc_name, TRUE);
		}
		/* look for .beavrc in S: */
		if(!fname) {
			/* Have a look in startup directory */
			rc_name[0] = 'S';
			rc_name[1] = ':';
			rc_name[2] = '.';
			strcpy (&rc_name[3], MSG_beavrc);
			fname = flook(rc_name, TRUE);
		}
#endif /* AMIGA */
#endif
	}
	/* if it isn't around, don't sweat it */
	if (fname == NULL)
		return;

	if (bindf = fopen(fname, "r"))
	{
		char	buffr[80];
		char	*buffp;

		buffp = buffr;
		while (fread (buffp++, sizeof(char), 1, bindf) == 1)
		{
			/* scanf is unhappy with commas */
			if (buffp[-1] == ',')
				buffp[-1] = '-';

			/* did we get a whole line */
			if (buffp[-1] == '\n')
			{
				*buffp = 0;	/* terminate line */
				buffp = buffr;
				sscanf (buffr, "%s %s %x", funcname, keybind, &keyval);
				if ((buffr[0] == '#') || (keyval == 0))
					continue;
				if (sp = symlookup (funcname))
				{
					if (binding[keyval] != NULL)/* Unbind old, and  */
						--binding[keyval] -> s_nkey;
					binding[keyval] = sp;/* rebind new.      */
					++sp -> s_nkey;
					sp -> s_modify |= SBOUND;	/* flag as altered key binding */
				}
			}
		}
		fclose (bindf);
	}
}

/*	Look up the existance of a file along the normal or PATH
	environment variable. Look first in the HOME directory if
	asked and possible
*/

char *flook(fname, hflag)

char *fname;	/* base file name to search for */
int hflag;	/* Look in the HOME environment variable first? */

{
	register char *home;	/* path to home directory */
	register char *path;	/* environmental PATH variable */
	register char *sp;	/* pointer into path spec */
	register int i;		/* index */
	static char fspec[NFILEN * 2];	/* full path spec to search */
	char *getenv();
	FILE * bindf;

	if (hflag) {
		home = getenv("HOME");
		if (home != NULL) {
			/* build home dir file spec */
			strcpy(fspec, home);
			if (fspec[strlen(fspec) - 1] != '/')
				strcat(fspec, "/");
			strcat(fspec, fname);

			/* and try it out */
			if (bindf = fopen(fspec, "r"))
			{
				fclose(bindf);
				return(fspec);
			}
		}
	}

	/* always try the current directory first */
	if (bindf = fopen(fname, "r"))
	{
		fclose(bindf);
		return(fname);
	}

	/* get the PATH variable */
	path = getenv("PATH");
	if (path != NULL)
		while (*path) {

			/* build next possible file spec */
			sp = fspec;
			while (*path && (*path != PATHCHR))
				*sp++ = *path++;

			/* add a terminating dir separator if we need it */
			if (sp[-1] != SEPCHAR)
				*sp++ = SEPCHAR;

			*sp = 0;
			strcat(fspec, fname);

			/* and try it out */
			if (bindf = fopen(fspec, "r"))
			{
				fclose(bindf);
				return(fspec);
			}

			if (*path == PATHCHR)
				++path;
		}

	return(NULL);	/* no such luck */
}


/* interactive method for loading binding file
* (uses above routine, obviously)
*/
char    load_extend ()
{

#ifdef CUSTOMIZE
	register char   s;
	char    fname[NFILEN];

	if ((s = ereply (MSG_bnd_file, fname, NFILEN, NULL)) != TRUE)
		return (s);
	check_extend (fname);
	writ_echo (okmsg);
#endif
	return (TRUE);
}

int     find_keyval (name)
char   *name;
{
	SYMBOL * sp;
	int     key;

	for (key = 0; key < NKEYS; ++key)
	{
		/* For all keys.    */
		sp = binding[key];
		if (sp != NULL && (strcmp (sp -> s_name, name) == 0))
			return (key);
	}
	return (0);
}
