/* Help functions for MicroGnuEmacs 3 */

#include "no_help.h"

#ifndef NO_HELP

#include "def.h"
#include "buffer.h"
#include "window.h"
#include "kbd.h"
#include "key.h"
#include "no_macro.h"

#ifndef	NO_MACRO
#include "macro.h"
#endif

#ifdef ANSI
#include <string.h>
#endif

static int showall 
PROTO((char *ind, struct keymap * map));

extern int      rescan();
static VOID bindfound 
PROTO((VOID));
static VOID findbind 
PROTO((struct function *, char *, struct keymap *));

/*
 * MAXKEYLEN is the offset into the line after key names in
 * describe-bindings. Default is 32; if you have keynames longer than 32
 * chars, set it to something longer than the longest of them (a multiple of
 * 8 is suggested). 
 */
#ifndef	MAXKEYLEN
#define	MAXKEYLEN	32
#endif

/*
 * Read a key from the keyboard, and look it up in the keymap.  Display the
 * name of the function currently bound to the key. 
 */
/* ARGSUSED */
desckey(f, n)
{
	register struct keymap *curmap;
	register struct function *funct;
	register char  *pep;
	char            prompt[80];
	int             c;
	int             m;
	int             i;

	(VOID) strcpy(prompt, "Describe key briefly: ");
	pep = prompt + strlen(prompt);
	key.k_count = 0;
	m = curbp->b_nmodes;
	curmap = curbp->b_modes[m]->p_map;
	for (;;) {
		for (;;) {
			ewprintf("%s", prompt);
			pep[-1] = ' ';
			pep = keyname(pep, key.k_chars[key.k_count++] = c = getkey(DISSCR));
			if ((funct = doscan(curmap, c))->f_type != F_PREFIX)
				break;
			*pep++ = '-';
			*pep = '\0';
			curmap = (struct keymap *) funct->f_pref;
		}
		if (funct->f_funcp != rescan
#ifndef	NO_MACRO
		    || funct->f_type == F_MACRO
#endif
			)
			break;
		if (ISUPPER(key.k_chars[key.k_count - 1])) {
			funct = doscan(curmap, TOLOWER(key.k_chars[key.k_count - 1]));
			if (funct->f_type == F_PREFIX) {
				*pep++ = '-';
				*pep = '\0';
				curmap = (struct keymap *) funct->f_pref;
				continue;
			}
			if (funct->f_funcp != rescan
#ifndef	NO_MACRO
			    || funct->f_type == F_MACRO
#endif
				)
				break;
		}
nextmode:
		if (--m < 0)
			break;
		curmap = curbp->b_modes[m]->p_map;
		for (i = 0; i < key.k_count; i++) {
			funct = doscan(curmap, key.k_chars[i]);
			if (funct->f_type != F_PREFIX) {
				if (i == key.k_count - 1
				    && (funct->f_funcp != rescan
#ifndef	NO_MACRO
					|| funct->f_type == F_MACRO
#endif
					))
					goto found;
				funct = NULL;
				goto nextmode;
			}
			curmap = (struct keymap *) funct->f_pref;
		}
		*pep++ = '-';
		*pep = '\0';
	}
found:
#ifndef NO_MACRO
	if (funct->f_type == F_MACRO)
		ewprintf("%k runs the macro %s",
			 ((struct macro *) funct->f_macro)->m_name);
	else
#endif
	if (funct->f_funcp == rescan || funct == NULL)
		ewprintf("%k is not bound to any function");
	else if ((pep = function_name(funct->f_funcp)) != NULL)
		ewprintf("%k runs the command %s", pep);
	else
		ewprintf("%k is bound to an unnamed function");
	epresf = TRUE;
	return TRUE;
}

/*
 * This function creates a table, listing all of the command keys and their
 * current bindings, and stores the table in the *help* pop-up buffer.  This
 * lets MicroGnuEMACS produce it's own wall chart. 
 */
static struct buffer *bp;
static char     buf[80];	/* used by showall and findbind */

/* ARGSUSED */
wallchart(f, n)
{
	int             m;
	static char     locbind[80] = "Local keybindings for mode ";

	if ((bp = bfind("*help*", TRUE)) == NULL)
		return FALSE;
	bp->b_flag &= ~BFCHG;	/* don't ask, just	 */
	if (bclear(bp) != TRUE)
		return FALSE;	/* clear it out.	 */
	for (m = curbp->b_nmodes; m > 0; m--) {
		(VOID) strcat(locbind, curbp->b_modes[m]->p_name);
		(VOID) strcat(locbind, ":");
		if ((addline(bp, locbind) == FALSE) ||
		    (showall(buf, curbp->b_modes[m]->p_map) == FALSE) ||
		    (addline(bp, "") == FALSE))
			return FALSE;
	}
	if ((addline(bp, "Global bindings:") == FALSE) ||
	    (showall(buf, map_table[0].p_map) == FALSE))
		return FALSE;
	return popbuftop(bp);
}

static int
showall(ind, map)
	char           *ind;
	struct keymap  *map;
{
	register struct map_element *ele;
	register int    i;
	struct function *functp;
	char           *cp;
	char           *cp2;
	int             last;

	if (addline(bp, "") == FALSE)
		return FALSE;
	last = -1;
	for (ele = &map->map_element[0];
	     ele < &map->map_element[map->map_num];
	     ele++) {
		/*
		 * Currently, map_default _must_ be a CFUNCT. May change
		 * later 
		 */
		if (map->map_default->f_funcp != rescan && ++last < ele->k_base) {
			cp = keyname(ind, last);
			if (last < ele->k_base - 1) {
				(VOID) strcpy(cp, " .. ");
				cp = keyname(cp + 4, ele->k_base - 1);
			}
			do {
				*cp++ = ' ';
			} while (cp < &buf[MAXKEYLEN]);
			(VOID) strcpy(cp, function_name(map->map_default->f_funcp));
			if (addline(bp, buf) == FALSE)
				return FALSE;
		}
		last = ele->k_num;
		for (i = ele->k_base; i <= last; i++) {
			functp = &(ele->k_entry[i - ele->k_base]);
			cp2 = NULL;
			if (functp->f_type == F_PREFIX)
				cp2 = map_name((struct keymap *) functp->f_pref);
#ifndef	NO_MACRO
			else if (functp->f_type == F_MACRO)
				cp2 = ((struct macro *) functp->f_macro)->m_name;
#endif
			else if (functp->f_funcp != rescan)
				cp2 = function_name(functp->f_funcp);
			if (cp2 != NULL) {
				cp = keyname(ind, i);
				do {
					*cp++ = ' ';
				} while (cp < &buf[MAXKEYLEN]);
				*cp = '\0';
				(VOID) strcpy(cp, cp2);
				if (addline(bp, buf) == FALSE)
					return FALSE;
			}
		}
	}

	for (ele = &map->map_element[0];
	     ele < &map->map_element[map->map_num];
	     ele++) {
		for (i = ele->k_base; i <= ele->k_num; i++) {
			if (ele->k_entry[i - ele->k_base].f_type == F_PREFIX) {
				cp = keyname(ind, i);
				*cp++ = ' ';
				if (showall(cp, (struct keymap *) ele->k_entry[i - ele->k_base].f_pref) == FALSE)
					return FALSE;
			}
		}
	}
	return TRUE;
}

help_help(f, n)
	int             f, n;
{
	struct keymap  *kp;
	struct function *funct;

	if ((kp = name_map("help")) == NULL)
		return FALSE;
	ewprintf("a b c: ");
	epresf = ERASE;
	do {
		funct = doscan(kp, getkey(DISSCR));
	} while (funct->f_type != F_CFUNCT || funct->f_funcp == help_help);
	return (*funct->f_funcp) (f, n);
}

static char     buf2[128];
static char    *buf2p;

/* ARGSUSED */
apropos_command(f, n)
	int             f, n;
{
	register char  *cp1;
	register int    slen;
#ifndef	NO_MACRO
	extern struct macro kbdmacro;
	register struct macro *mac;
#endif
	char            string[NMACN];
	struct functnames *fnp;
	struct function tmp;
	struct buffer  *bp;

	if (eread("apropos: ", string, sizeof(string), EFNEW) == ABORT)
		return ABORT;
	/* FALSE means we got a 0 character string, which is fine */
	if ((bp = bfind("*help*", TRUE)) == NULL)
		return FALSE;
	bp->b_flag &= ~BFCHG;
	if (bclear(bp) == FALSE)
		return FALSE;
	tmp.f_type = F_CFUNCT;
	slen = strlen(string);
	for (fnp = &functnames[0]; fnp < &functnames[nfunct]; fnp++) {
		for (cp1 = fnp->n_name; *cp1; cp1++)
			if (strncmp(cp1, string, slen) == 0) {
				(VOID) strcpy(buf2, fnp->n_name);
				buf2p = &buf2[strlen(buf2)];
				tmp.f_funcp = fnp->n_funct;
				findbind(&tmp, buf, map_table[0].p_map);
				if (addline(bp, buf2) == FALSE)
					return FALSE;
				break;
			}
	}
#ifndef	NO_MACRO
	tmp.f_type = F_MACRO;
	for (mac = kbdmacro.m_macp; mac; mac = mac->m_macp) {
		for (cp1 = mac->m_name; *cp1; cp1++) {
			if (strncmp(cp1, string, slen) == 0) {
				(VOID) strcpy(buf2, mac->m_name);
				buf2p = &buf2[strlen(buf2)];
				tmp.f_macro = (int (*) ()) mac;
				findbind(&tmp, buf, map_table[0].p_map);
				if (addline(bp, buf2) == FALSE)
					return FALSE;
				break;
			}
		}
	}
#endif
	return popbuftop(bp);
}

static          VOID
findbind(funct, ind, map)
	struct function *funct;
	char           *ind;
	struct keymap  *map;
{
	register struct map_element *ele;
	register int    i;
	char           *cp;
	int             last;

	last = -1;
	for (ele = &map->map_element[0];
	     ele < &map->map_element[map->map_num];
	     ele++) {
		/*
		 * Cheating - we test equality on the union by testing one of
		 * the elements. They are all pointers, but have different
		 * types. If someone with a processor that cares about this
		 * objects, we'll have to spell it out longhand. Bleah. 
		 */
		if (map->map_default->f_type == funct->f_type
		    && map->map_default->f_funcp == funct->f_funcp && ++last < ele->k_base) {
			cp = keyname(ind, last);
			if (last < ele->k_base - 1) {
				(VOID) strcpy(cp, " .. ");
				(VOID) keyname(cp + 4, ele->k_base - 1);
			}
			bindfound();
		}
		last = ele->k_num;
		for (i = ele->k_base; i <= last; i++) {
			if (ele->k_entry[i - ele->k_base].f_type == funct->f_type
			    && funct->f_funcp == ele->k_entry[i - ele->k_base].f_funcp) {
				(VOID) keyname(ind, i);
				bindfound();
			}
		}
	}
	for (ele = &map->map_element[0];
	     ele < &map->map_element[map->map_num];
	     ele++) {
		for (i = ele->k_base; i <= ele->k_num; i++) {
			if (ele->k_entry[i - ele->k_base].f_type == F_PREFIX) {
				cp = keyname(ind, i);
				*cp++ = ' ';
				findbind(funct, cp, (struct keymap *) ele->k_entry[i - ele->k_base].f_pref);
			}
		}
	}
}

static          VOID
bindfound()
{

	if (buf2p < &buf2[32]) {
		do {
			*buf2p++ = ' ';
		} while (buf2p < &buf2[32]);
	} else {
		*buf2p++ = ',';
		*buf2p++ = ' ';
	}
	(VOID) strcpy(buf2p, buf);
	buf2p += strlen(buf);
}
#else
#include "nullfile.h"
#endif
