text: /*	$OpenBSD: help.c,v 1.35 2015/03/19 21:22:15 bcallah Exp $	*/
  
  /* This file is in the public domain. */
  
  /*
   * Help functions for Mg 2
   */
  
  #include <sys/queue.h>
  #include <signal.h>
  #include <stdio.h>
  #include <string.h>
  
  #include "def.h"
  #include "funmap.h"
  #include "kbd.h"
  #include "key.h"
  #include "macro.h"
  
  static int	showall(struct buffer *, KEYMAP *, char *);
  static int	findbind(KEYMAP *, PF, char *, size_t);
  
  /*
   * 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 */
  int
  desckey(int f, int n)
  {
  	KEYMAP	*curmap;
  	PF	 funct;
  	int	 c, m, i, num;
  	char	*pep;
  	char	 dprompt[80];
  
  	if (inmacro)
  		return (TRUE);	/* ignore inside keyboard macro */
  
  	num = strlcpy(dprompt, "Describe key briefly: ", sizeof(dprompt));
  	if (num >= sizeof(dprompt))
  		num = sizeof(dprompt) - 1;
  	pep = dprompt + num;
  	key.k_count = 0;
  	m = curbp->b_nmodes;
  	curmap = curbp->b_modes[m]->p_map;
  	for (;;) {
  		for (;;) {
  			ewprintf("%s", dprompt);
  			pep[-1] = ' ';
  			pep = getkeyname(pep, sizeof(dprompt) - (pep - dprompt),
  			    key.k_chars[key.k_count++] = c = getkey(FALSE));
  			if ((funct = doscan(curmap, c, &curmap)) != NULL)
  				break;
  			*pep++ = '-';
  			*pep = '\0';
  		}
  		if (funct != rescan)
  			break;
  		if (ISUPPER(key.k_chars[key.k_count - 1])) {
  			funct = doscan(curmap,
  			    TOLOWER(key.k_chars[key.k_count - 1]), &curmap);
  			if (funct == NULL) {
  				*pep++ = '-';
  				*pep = '\0';
  				continue;
  			}
  			if (funct != rescan)
  				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], &curmap);
  			if (funct != NULL) {
  				if (i == key.k_count - 1 && funct != rescan)
  					goto found;
  				funct = rescan;
  				goto nextmode;
  			}
  		}
  		*pep++ = '-';
  		*pep = '\0';
  	}
  found:
  	if (funct == rescan || funct == selfinsert)
  		ewprintf("%k is not bound to any function");
  	else if ((pep = (char *)function_name(funct)) != NULL)
  		ewprintf("%k runs the command %s", pep);
  	else
  		ewprintf("%k is bound to an unnamed function");
  	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 Mg produce it's own wall chart.
   */
  /* ARGSUSED */
  int
  wallchart(int f, int n)
  {
  	int		 m;
  	struct buffer		*bp;
  
  	bp = bfind("*help*", TRUE);
  	if (bclear(bp) != TRUE)
  		/* clear it out */
  		return (FALSE);
  	bp->b_flag |= BFREADONLY;
  	for (m = curbp->b_nmodes; m > 0; m--) {
  		if ((addlinef(bp, "Local keybindings for mode %s:",
  				curbp->b_modes[m]->p_name) == FALSE) ||
  		    (showall(bp, curbp->b_modes[m]->p_map, "") == FALSE) ||
  		    (addline(bp, "") == FALSE))
  			return (FALSE);
  	}
  	if ((addline(bp, "Global bindings:") == FALSE) ||
  	    (showall(bp, fundamental_map, "") == FALSE))
  		return (FALSE);
  	return (popbuftop(bp, WNONE));
  }
  
  static int
  showall(struct buffer *bp, KEYMAP *map, char *prefix)
  {
  	KEYMAP	*newmap;
  	char	 buf[80], keybuf[16];
  	PF	 fun;
  	int	 c;
  
  	if (addline(bp, "") == FALSE)
  		return (FALSE);
  
  	/* XXX - 256 ? */
  	for (c = 0; c < 256; c++) {
  		fun = doscan(map, c, &newmap);
  		if (fun == rescan || fun == selfinsert)
  			continue;
  		getkeyname(buf, sizeof(buf), c);
  		(void)snprintf(keybuf, sizeof(keybuf), "%s%s ", prefix, buf);
  		if (fun == NULL) {
  			if (showall(bp, newmap, keybuf) == FALSE)
  				return (FALSE);
  		} else {
  			if (addlinef(bp, "%-16s%s", keybuf,
  				    function_name(fun)) == FALSE)
  				return (FALSE);
  		}
  	}
  	return (TRUE);
  }
  
  int
  help_help(int f, int n)
  {
  	KEYMAP	*kp;
  	PF	 funct;
  
  	if ((kp = name_map("help")) == NULL)
  		return (FALSE);
  	ewprintf("a b c: ");
  	do {
  		funct = doscan(kp, getkey(FALSE), NULL);
  	} while (funct == NULL || funct == help_help);
  
  	if (macrodef && macrocount < MAXMACRO)
  		macro[macrocount - 1].m_funct = funct;
  
  	return ((*funct)(f, n));
  }
  
  /* ARGSUSED */
  int
  apropos_command(int f, int n)
  {
  	struct buffer		*bp;
  	struct list		*fnames, *el;
  	char		 string[32];
  
  	if (eread("apropos: ", string, sizeof(string), EFNUL | EFNEW) == NULL)
  		return (ABORT);
  	/* FALSE means we got a 0 character string, which is fine */
  	bp = bfind("*help*", TRUE);
  	if (bclear(bp) == FALSE)
  		return (FALSE);
  
  	fnames = complete_function_list("");
  	for (el = fnames; el != NULL; el = el->l_next) {
  		char buf[32];
  
  		if (strstr(el->l_name, string) == NULL)
  			continue;
  
  		buf[0] = '\0';
  		findbind(fundamental_map, name_function(el->l_name),
  		    buf, sizeof(buf));
  
  		if (addlinef(bp, "%-32s%s", el->l_name,  buf) == FALSE) {
  			free_file_list(fnames);
  			return (FALSE);
  		}
  	}
  	free_file_list(fnames);
  	return (popbuftop(bp, WNONE));
  }
  
  static int
  findbind(KEYMAP *map, PF fun, char *buf, size_t len)
  {
  	KEYMAP	*newmap;
  	PF	 nfun;
  	char	 buf2[16], keybuf[16];
  	int	 c;
  
  	/* XXX - 256 ? */
  	for (c = 0; c < 256; c++) {
  		nfun = doscan(map, c, &newmap);
  		if (nfun == fun) {
  			getkeyname(buf, len, c);
  			return (TRUE);
  		}
  		if (nfun == NULL) {
  			if (findbind(newmap, fun, buf2, sizeof(buf2)) == TRUE) {
  				getkeyname(keybuf, sizeof(keybuf), c);
  				(void)snprintf(buf, len, "%s %s", keybuf, buf2);
  				return (TRUE);
  			}
  		}
  	}
  	return (FALSE);
  }
  