1/*	$OpenBSD: help.c,v 1.37 2023/03/08 04:43:11 guenther Exp $	*/
2
3/* This file is in the public domain. */
4
5/*
6 * Help functions for Mg 2
7 */
8
9#include <sys/queue.h>
10#include <signal.h>
11#include <stdio.h>
12#include <string.h>
13
14#include "def.h"
15#include "funmap.h"
16#include "kbd.h"
17#include "key.h"
18#include "macro.h"
19
20static int	showall(struct buffer *, KEYMAP *, char *);
21static int	findbind(KEYMAP *, PF, char *, size_t);
22
23/*
24 * Read a key from the keyboard, and look it up in the keymap.
25 * Display the name of the function currently bound to the key.
26 */
27int
28desckey(int f, int n)
29{
30	KEYMAP	*curmap;
31	PF	 funct;
32	int	 c, m, i, num;
33	char	*pep;
34	char	 dprompt[80];
35
36	if (inmacro)
37		return (TRUE);	/* ignore inside keyboard macro */
38
39	num = strlcpy(dprompt, "Describe key briefly: ", sizeof(dprompt));
40	if (num >= sizeof(dprompt))
41		num = sizeof(dprompt) - 1;
42	pep = dprompt + num;
43	key.k_count = 0;
44	m = curbp->b_nmodes;
45	curmap = curbp->b_modes[m]->p_map;
46	for (;;) {
47		for (;;) {
48			ewprintf("%s", dprompt);
49			pep[-1] = ' ';
50			pep = getkeyname(pep, sizeof(dprompt) - (pep - dprompt),
51			    key.k_chars[key.k_count++] = c = getkey(FALSE));
52			if ((funct = doscan(curmap, c, &curmap)) != NULL)
53				break;
54			*pep++ = '-';
55			*pep = '\0';
56		}
57		if (funct != rescan)
58			break;
59		if (ISUPPER(key.k_chars[key.k_count - 1])) {
60			funct = doscan(curmap,
61			    TOLOWER(key.k_chars[key.k_count - 1]), &curmap);
62			if (funct == NULL) {
63				*pep++ = '-';
64				*pep = '\0';
65				continue;
66			}
67			if (funct != rescan)
68				break;
69		}
70nextmode:
71		if (--m < 0)
72			break;
73		curmap = curbp->b_modes[m]->p_map;
74		for (i = 0; i < key.k_count; i++) {
75			funct = doscan(curmap, key.k_chars[i], &curmap);
76			if (funct != NULL) {
77				if (i == key.k_count - 1 && funct != rescan)
78					goto found;
79				funct = rescan;
80				goto nextmode;
81			}
82		}
83		*pep++ = '-';
84		*pep = '\0';
85	}
86found:
87	if (funct == rescan || funct == selfinsert)
88		ewprintf("%k is not bound to any function");
89	else if ((pep = (char *)function_name(funct)) != NULL)
90		ewprintf("%k runs the command %s", pep);
91	else
92		ewprintf("%k is bound to an unnamed function");
93	return (TRUE);
94}
95
96/*
97 * This function creates a table, listing all of the command
98 * keys and their current bindings, and stores the table in the
99 * *help* pop-up buffer.  This lets Mg produce its own wall chart.
100 */
101int
102wallchart(int f, int n)
103{
104	int		 m;
105	struct buffer		*bp;
106
107	bp = bfind("*help*", TRUE);
108	if (bclear(bp) != TRUE)
109		/* clear it out */
110		return (FALSE);
111	bp->b_flag |= BFREADONLY;
112	for (m = curbp->b_nmodes; m > 0; m--) {
113		if ((addlinef(bp, "Local keybindings for mode %s:",
114				curbp->b_modes[m]->p_name) == FALSE) ||
115		    (showall(bp, curbp->b_modes[m]->p_map, "") == FALSE) ||
116		    (addline(bp, "") == FALSE))
117			return (FALSE);
118	}
119	if ((addline(bp, "Global bindings:") == FALSE) ||
120	    (showall(bp, fundamental_map, "") == FALSE))
121		return (FALSE);
122	return (popbuftop(bp, WNONE));
123}
124
125static int
126showall(struct buffer *bp, KEYMAP *map, char *prefix)
127{
128	KEYMAP	*newmap;
129	char	 buf[80], keybuf[16];
130	PF	 fun;
131	int	 c;
132
133	if (addline(bp, "") == FALSE)
134		return (FALSE);
135
136	/* XXX - 256 ? */
137	for (c = 0; c < 256; c++) {
138		fun = doscan(map, c, &newmap);
139		if (fun == rescan || fun == selfinsert)
140			continue;
141		getkeyname(buf, sizeof(buf), c);
142		(void)snprintf(keybuf, sizeof(keybuf), "%s%s ", prefix, buf);
143		if (fun == NULL) {
144			if (showall(bp, newmap, keybuf) == FALSE)
145				return (FALSE);
146		} else {
147			if (addlinef(bp, "%-16s%s", keybuf,
148				    function_name(fun)) == FALSE)
149				return (FALSE);
150		}
151	}
152	return (TRUE);
153}
154
155int
156help_help(int f, int n)
157{
158	KEYMAP	*kp;
159	PF	 funct;
160
161	if ((kp = name_map("help")) == NULL)
162		return (FALSE);
163	ewprintf("a b c: ");
164	do {
165		funct = doscan(kp, getkey(FALSE), NULL);
166	} while (funct == NULL || funct == help_help);
167
168	if (macrodef && macrocount < MAXMACRO)
169		macro[macrocount - 1].m_funct = funct;
170
171	return ((*funct)(f, n));
172}
173
174int
175apropos_command(int f, int n)
176{
177	struct buffer		*bp;
178	struct list		*fnames, *el;
179	char		 string[32];
180
181	if (eread("apropos: ", string, sizeof(string), EFNUL | EFNEW) == NULL)
182		return (ABORT);
183	/* FALSE means we got a 0 character string, which is fine */
184	bp = bfind("*help*", TRUE);
185	if (bclear(bp) == FALSE)
186		return (FALSE);
187
188	fnames = complete_function_list("");
189	for (el = fnames; el != NULL; el = el->l_next) {
190		char buf[32];
191
192		if (strstr(el->l_name, string) == NULL)
193			continue;
194
195		buf[0] = '\0';
196		findbind(fundamental_map, name_function(el->l_name),
197		    buf, sizeof(buf));
198
199		if (addlinef(bp, "%-32s%s", el->l_name,  buf) == FALSE) {
200			free_file_list(fnames);
201			return (FALSE);
202		}
203	}
204	free_file_list(fnames);
205	return (popbuftop(bp, WNONE));
206}
207
208static int
209findbind(KEYMAP *map, PF fun, char *buf, size_t len)
210{
211	KEYMAP	*newmap;
212	PF	 nfun;
213	char	 buf2[16], keybuf[16];
214	int	 c;
215
216	/* XXX - 256 ? */
217	for (c = 0; c < 256; c++) {
218		nfun = doscan(map, c, &newmap);
219		if (nfun == fun) {
220			getkeyname(buf, len, c);
221			return (TRUE);
222		}
223		if (nfun == NULL) {
224			if (findbind(newmap, fun, buf2, sizeof(buf2)) == TRUE) {
225				getkeyname(keybuf, sizeof(keybuf), c);
226				(void)snprintf(buf, len, "%s %s", keybuf, buf2);
227				return (TRUE);
228			}
229		}
230	}
231	return (FALSE);
232}
233