xref: /netbsd-src/lib/libedit/keymacro.c (revision 2cf0b469b9d048aeccdc6451da18d68a11366991)
1*2cf0b469Srillig /*	$NetBSD: keymacro.c,v 1.25 2025/01/03 00:40:08 rillig Exp $	*/
2d47f9584Schristos 
3d47f9584Schristos /*-
4d47f9584Schristos  * Copyright (c) 1992, 1993
5d47f9584Schristos  *	The Regents of the University of California.  All rights reserved.
6d47f9584Schristos  *
7d47f9584Schristos  * This code is derived from software contributed to Berkeley by
8d47f9584Schristos  * Christos Zoulas of Cornell University.
9d47f9584Schristos  *
10d47f9584Schristos  * Redistribution and use in source and binary forms, with or without
11d47f9584Schristos  * modification, are permitted provided that the following conditions
12d47f9584Schristos  * are met:
13d47f9584Schristos  * 1. Redistributions of source code must retain the above copyright
14d47f9584Schristos  *    notice, this list of conditions and the following disclaimer.
15d47f9584Schristos  * 2. Redistributions in binary form must reproduce the above copyright
16d47f9584Schristos  *    notice, this list of conditions and the following disclaimer in the
17d47f9584Schristos  *    documentation and/or other materials provided with the distribution.
18d47f9584Schristos  * 3. Neither the name of the University nor the names of its contributors
19d47f9584Schristos  *    may be used to endorse or promote products derived from this software
20d47f9584Schristos  *    without specific prior written permission.
21d47f9584Schristos  *
22d47f9584Schristos  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23d47f9584Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24d47f9584Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25d47f9584Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26d47f9584Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27d47f9584Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28d47f9584Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29d47f9584Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30d47f9584Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31d47f9584Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32d47f9584Schristos  * SUCH DAMAGE.
33d47f9584Schristos  */
34d47f9584Schristos 
35d47f9584Schristos #include "config.h"
36d47f9584Schristos #if !defined(lint) && !defined(SCCSID)
37d47f9584Schristos #if 0
387da8639eSchristos static char sccsid[] = "@(#)key.c	8.1 (Berkeley) 6/4/93";
39d47f9584Schristos #else
40*2cf0b469Srillig __RCSID("$NetBSD: keymacro.c,v 1.25 2025/01/03 00:40:08 rillig Exp $");
41d47f9584Schristos #endif
42d47f9584Schristos #endif /* not lint && not SCCSID */
43d47f9584Schristos 
44d47f9584Schristos /*
4502479cacSchristos  * keymacro.c: This module contains the procedures for maintaining
46d47f9584Schristos  *	       the extended-key map.
47d47f9584Schristos  *
48d47f9584Schristos  *      An extended-key (key) is a sequence of keystrokes introduced
49d47f9584Schristos  *	with a sequence introducer and consisting of an arbitrary
5002479cacSchristos  *	number of characters.  This module maintains a map (the
5102479cacSchristos  *	el->el_keymacro.map)
52d47f9584Schristos  *	to convert these extended-key sequences into input strs
53985c823cSchristos  *	(XK_STR) or editor functions (XK_CMD).
54d47f9584Schristos  *
55d47f9584Schristos  *      Warning:
56d47f9584Schristos  *	  If key is a substr of some other keys, then the longer
57d47f9584Schristos  *	  keys are lost!!  That is, if the keys "abcd" and "abcef"
5802479cacSchristos  *	  are in el->el_keymacro.map, adding the key "abc" will cause
5902479cacSchristos  *	  the first two definitions to be lost.
60d47f9584Schristos  *
61d47f9584Schristos  *      Restrictions:
62d47f9584Schristos  *      -------------
63d47f9584Schristos  *      1) It is not possible to have one key that is a
64d47f9584Schristos  *	   substr of another.
65d47f9584Schristos  */
66d47f9584Schristos #include <stdlib.h>
6722383670Schristos #include <string.h>
68d47f9584Schristos 
69d47f9584Schristos #include "el.h"
704fc1f47dSchristos #include "fcns.h"
71d47f9584Schristos 
72d47f9584Schristos /*
7302479cacSchristos  * The Nodes of the el->el_keymacro.map.  The el->el_keymacro.map is a
7402479cacSchristos  * linked list of these node elements
75d47f9584Schristos  */
76d47f9584Schristos struct keymacro_node_t {
770594af80Schristos 	wchar_t		 ch;		/* single character of key	 */
78d47f9584Schristos 	int		 type;		/* node type			 */
79d47f9584Schristos 	keymacro_value_t val;		/* command code or pointer to str,  */
80d47f9584Schristos 					/* if this is a leaf		 */
81d47f9584Schristos 	struct keymacro_node_t *next;	/* ptr to next char of this key  */
82d47f9584Schristos 	struct keymacro_node_t *sibling;/* ptr to another key with same prefix*/
83d47f9584Schristos };
84d47f9584Schristos 
85469d44f8Schristos static int		 node_trav(EditLine *, keymacro_node_t *, wchar_t *,
86d47f9584Schristos     keymacro_value_t *);
87469d44f8Schristos static int		 node__try(EditLine *, keymacro_node_t *,
880594af80Schristos     const wchar_t *, keymacro_value_t *, int);
89469d44f8Schristos static keymacro_node_t	*node__get(wint_t);
90469d44f8Schristos static void		 node__free(keymacro_node_t *);
91469d44f8Schristos static void		 node__put(EditLine *, keymacro_node_t *);
92469d44f8Schristos static int		 node__delete(EditLine *, keymacro_node_t **,
930594af80Schristos     const wchar_t *);
94469d44f8Schristos static int		 node_lookup(EditLine *, const wchar_t *,
9502479cacSchristos     keymacro_node_t *, size_t);
96469d44f8Schristos static int		 node_enum(EditLine *, keymacro_node_t *, size_t);
97d47f9584Schristos 
98d47f9584Schristos #define	KEY_BUFSIZ	EL_BUFSIZ
99d47f9584Schristos 
100d47f9584Schristos 
101d47f9584Schristos /* keymacro_init():
102d47f9584Schristos  *	Initialize the key maps
103d47f9584Schristos  */
104a2d6b270Schristos libedit_private int
105d47f9584Schristos keymacro_init(EditLine *el)
106d47f9584Schristos {
107d47f9584Schristos 
108113f06a3Schristos 	el->el_keymacro.buf = el_calloc(KEY_BUFSIZ,
10902479cacSchristos 	    sizeof(*el->el_keymacro.buf));
110d47f9584Schristos 	if (el->el_keymacro.buf == NULL)
111b71bed95Schristos 		return -1;
112d47f9584Schristos 	el->el_keymacro.map = NULL;
113d47f9584Schristos 	keymacro_reset(el);
114b71bed95Schristos 	return 0;
115d47f9584Schristos }
116d47f9584Schristos 
117d47f9584Schristos /* keymacro_end():
118d47f9584Schristos  *	Free the key maps
119d47f9584Schristos  */
120a2d6b270Schristos libedit_private void
121d47f9584Schristos keymacro_end(EditLine *el)
122d47f9584Schristos {
123d47f9584Schristos 
124a13cd756Schristos 	el_free(el->el_keymacro.buf);
125d47f9584Schristos 	el->el_keymacro.buf = NULL;
126d47f9584Schristos 	node__free(el->el_keymacro.map);
127d47f9584Schristos }
128d47f9584Schristos 
129d47f9584Schristos 
130d47f9584Schristos /* keymacro_map_cmd():
131d47f9584Schristos  *	Associate cmd with a key value
132d47f9584Schristos  */
133a2d6b270Schristos libedit_private keymacro_value_t *
134d47f9584Schristos keymacro_map_cmd(EditLine *el, int cmd)
135d47f9584Schristos {
136d47f9584Schristos 
137d47f9584Schristos 	el->el_keymacro.val.cmd = (el_action_t) cmd;
138b71bed95Schristos 	return &el->el_keymacro.val;
139d47f9584Schristos }
140d47f9584Schristos 
141d47f9584Schristos 
142d47f9584Schristos /* keymacro_map_str():
143d47f9584Schristos  *	Associate str with a key value
144d47f9584Schristos  */
145a2d6b270Schristos libedit_private keymacro_value_t *
1460594af80Schristos keymacro_map_str(EditLine *el, wchar_t *str)
147d47f9584Schristos {
148d47f9584Schristos 
149d47f9584Schristos 	el->el_keymacro.val.str = str;
150b71bed95Schristos 	return &el->el_keymacro.val;
151d47f9584Schristos }
152d47f9584Schristos 
153d47f9584Schristos 
154d47f9584Schristos /* keymacro_reset():
15502479cacSchristos  *	Takes all nodes on el->el_keymacro.map and puts them on free list.
15602479cacSchristos  *	Then initializes el->el_keymacro.map with arrow keys
157d47f9584Schristos  *	[Always bind the ansi arrow keys?]
158d47f9584Schristos  */
159a2d6b270Schristos libedit_private void
160d47f9584Schristos keymacro_reset(EditLine *el)
161d47f9584Schristos {
162d47f9584Schristos 
163d47f9584Schristos 	node__put(el, el->el_keymacro.map);
164d47f9584Schristos 	el->el_keymacro.map = NULL;
165d47f9584Schristos 	return;
166d47f9584Schristos }
167d47f9584Schristos 
168d47f9584Schristos 
169d47f9584Schristos /* keymacro_get():
170d47f9584Schristos  *	Calls the recursive function with entry point el->el_keymacro.map
171d47f9584Schristos  *      Looks up *ch in map and then reads characters until a
172d47f9584Schristos  *      complete match is found or a mismatch occurs. Returns the
173985c823cSchristos  *      type of the match found (XK_STR or XK_CMD).
174d47f9584Schristos  *      Returns NULL in val.str and XK_STR for no match.
1750a374fd7Schristos  *      Returns XK_NOD for end of file or read error.
176d47f9584Schristos  *      The last character read is returned in *ch.
177d47f9584Schristos  */
178a2d6b270Schristos libedit_private int
1790594af80Schristos keymacro_get(EditLine *el, wchar_t *ch, keymacro_value_t *val)
180d47f9584Schristos {
181d47f9584Schristos 
182b71bed95Schristos 	return node_trav(el, el->el_keymacro.map, ch, val);
183d47f9584Schristos }
184d47f9584Schristos 
185d47f9584Schristos 
186d47f9584Schristos /* keymacro_add():
18702479cacSchristos  *      Adds key to the el->el_keymacro.map and associates the value in
18802479cacSchristos  *	val with it. If key is already is in el->el_keymacro.map, the new
18902479cacSchristos  *	code is applied to the existing key. Ntype specifies if code is a
19002479cacSchristos  *	command, an out str or a unix command.
191d47f9584Schristos  */
192a2d6b270Schristos libedit_private void
1930594af80Schristos keymacro_add(EditLine *el, const wchar_t *key, keymacro_value_t *val,
1940594af80Schristos     int ntype)
195d47f9584Schristos {
196d47f9584Schristos 
197d47f9584Schristos 	if (key[0] == '\0') {
198d47f9584Schristos 		(void) fprintf(el->el_errfile,
199d47f9584Schristos 		    "keymacro_add: Null extended-key not allowed.\n");
200d47f9584Schristos 		return;
201d47f9584Schristos 	}
202d47f9584Schristos 	if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
203d47f9584Schristos 		(void) fprintf(el->el_errfile,
204d47f9584Schristos 		    "keymacro_add: sequence-lead-in command not allowed\n");
205d47f9584Schristos 		return;
206d47f9584Schristos 	}
207d47f9584Schristos 	if (el->el_keymacro.map == NULL)
208d47f9584Schristos 		/* tree is initially empty.  Set up new node to match key[0] */
209d47f9584Schristos 		el->el_keymacro.map = node__get(key[0]);
210d47f9584Schristos 			/* it is properly initialized */
211d47f9584Schristos 
212d47f9584Schristos 	/* Now recurse through el->el_keymacro.map */
213d47f9584Schristos 	(void) node__try(el, el->el_keymacro.map, key, val, ntype);
214d47f9584Schristos 	return;
215d47f9584Schristos }
216d47f9584Schristos 
217d47f9584Schristos 
218d47f9584Schristos /* keymacro_clear():
219d47f9584Schristos  *
220d47f9584Schristos  */
221a2d6b270Schristos libedit_private void
2220594af80Schristos keymacro_clear(EditLine *el, el_action_t *map, const wchar_t *in)
223d47f9584Schristos {
224d47f9584Schristos         if (*in > N_KEYS) /* can't be in the map */
225d47f9584Schristos                 return;
226d47f9584Schristos 	if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
227d47f9584Schristos 	    ((map == el->el_map.key &&
228d47f9584Schristos 	    el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
229d47f9584Schristos 	    (map == el->el_map.alt &&
230d47f9584Schristos 	    el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
231d47f9584Schristos 		(void) keymacro_delete(el, in);
232d47f9584Schristos }
233d47f9584Schristos 
234d47f9584Schristos 
235d47f9584Schristos /* keymacro_delete():
236d47f9584Schristos  *      Delete the key and all longer keys staring with key, if
237d47f9584Schristos  *      they exists.
238d47f9584Schristos  */
239a2d6b270Schristos libedit_private int
2400594af80Schristos keymacro_delete(EditLine *el, const wchar_t *key)
241d47f9584Schristos {
242d47f9584Schristos 
243d47f9584Schristos 	if (key[0] == '\0') {
244d47f9584Schristos 		(void) fprintf(el->el_errfile,
245d47f9584Schristos 		    "keymacro_delete: Null extended-key not allowed.\n");
246b71bed95Schristos 		return -1;
247d47f9584Schristos 	}
248d47f9584Schristos 	if (el->el_keymacro.map == NULL)
249b71bed95Schristos 		return 0;
250d47f9584Schristos 
251d47f9584Schristos 	(void) node__delete(el, &el->el_keymacro.map, key);
252b71bed95Schristos 	return 0;
253d47f9584Schristos }
254d47f9584Schristos 
255d47f9584Schristos 
256d47f9584Schristos /* keymacro_print():
257d47f9584Schristos  *	Print the binding associated with key key.
258d47f9584Schristos  *	Print entire el->el_keymacro.map if null
259d47f9584Schristos  */
260a2d6b270Schristos libedit_private void
2610594af80Schristos keymacro_print(EditLine *el, const wchar_t *key)
262d47f9584Schristos {
263d47f9584Schristos 
264d47f9584Schristos 	/* do nothing if el->el_keymacro.map is empty and null key specified */
265d47f9584Schristos 	if (el->el_keymacro.map == NULL && *key == 0)
266d47f9584Schristos 		return;
267d47f9584Schristos 
268d47f9584Schristos 	el->el_keymacro.buf[0] = '"';
269c11bd863Schristos 	if (node_lookup(el, key, el->el_keymacro.map, (size_t)1) <= -1)
270d47f9584Schristos 		/* key is not bound */
271fcf85103Schristos 		(void) fprintf(el->el_errfile, "Unbound extended key \"%ls"
27202479cacSchristos 		    "\"\n", key);
273d47f9584Schristos 	return;
274d47f9584Schristos }
275d47f9584Schristos 
276d47f9584Schristos 
277d47f9584Schristos /* node_trav():
278d47f9584Schristos  *	recursively traverses node in tree until match or mismatch is
279d47f9584Schristos  *	found.  May read in more characters.
280d47f9584Schristos  */
281469d44f8Schristos static int
2820594af80Schristos node_trav(EditLine *el, keymacro_node_t *ptr, wchar_t *ch,
2830594af80Schristos     keymacro_value_t *val)
284d47f9584Schristos {
285d47f9584Schristos 
286d47f9584Schristos 	if (ptr->ch == *ch) {
287d47f9584Schristos 		/* match found */
288d47f9584Schristos 		if (ptr->next) {
289d47f9584Schristos 			/* key not complete so get next char */
2900a374fd7Schristos 			if (el_wgetc(el, ch) != 1)
2910a374fd7Schristos 				return XK_NOD;
292b71bed95Schristos 			return node_trav(el, ptr->next, ch, val);
293d47f9584Schristos 		} else {
294d47f9584Schristos 			*val = ptr->val;
295d47f9584Schristos 			if (ptr->type != XK_CMD)
296d47f9584Schristos 				*ch = '\0';
297b71bed95Schristos 			return ptr->type;
298d47f9584Schristos 		}
299d47f9584Schristos 	} else {
300d47f9584Schristos 		/* no match found here */
301d47f9584Schristos 		if (ptr->sibling) {
302d47f9584Schristos 			/* try next sibling */
303b71bed95Schristos 			return node_trav(el, ptr->sibling, ch, val);
304d47f9584Schristos 		} else {
305d47f9584Schristos 			/* no next sibling -- mismatch */
306d47f9584Schristos 			val->str = NULL;
307b71bed95Schristos 			return XK_STR;
308d47f9584Schristos 		}
309d47f9584Schristos 	}
310d47f9584Schristos }
311d47f9584Schristos 
312d47f9584Schristos 
313d47f9584Schristos /* node__try():
314d47f9584Schristos  *	Find a node that matches *str or allocate a new one
315d47f9584Schristos  */
316469d44f8Schristos static int
3170594af80Schristos node__try(EditLine *el, keymacro_node_t *ptr, const wchar_t *str,
31802479cacSchristos     keymacro_value_t *val, int ntype)
319d47f9584Schristos {
320d47f9584Schristos 
321d47f9584Schristos 	if (ptr->ch != *str) {
322d47f9584Schristos 		keymacro_node_t *xm;
323d47f9584Schristos 
324d47f9584Schristos 		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
325d47f9584Schristos 			if (xm->sibling->ch == *str)
326d47f9584Schristos 				break;
327d47f9584Schristos 		if (xm->sibling == NULL)
328d47f9584Schristos 			xm->sibling = node__get(*str);	/* setup new node */
329d47f9584Schristos 		ptr = xm->sibling;
330d47f9584Schristos 	}
331d47f9584Schristos 	if (*++str == '\0') {
332d47f9584Schristos 		/* we're there */
333d47f9584Schristos 		if (ptr->next != NULL) {
334d47f9584Schristos 			node__put(el, ptr->next);
335d47f9584Schristos 				/* lose longer keys with this prefix */
336d47f9584Schristos 			ptr->next = NULL;
337d47f9584Schristos 		}
338d47f9584Schristos 		switch (ptr->type) {
339d47f9584Schristos 		case XK_CMD:
340d47f9584Schristos 		case XK_NOD:
341d47f9584Schristos 			break;
342d47f9584Schristos 		case XK_STR:
343d47f9584Schristos 			if (ptr->val.str)
344a13cd756Schristos 				el_free(ptr->val.str);
345d47f9584Schristos 			break;
346d47f9584Schristos 		default:
347d47f9584Schristos 			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
348d47f9584Schristos 			    ptr->type));
349d47f9584Schristos 		}
350d47f9584Schristos 
351d47f9584Schristos 		switch (ptr->type = ntype) {
352d47f9584Schristos 		case XK_CMD:
353d47f9584Schristos 			ptr->val = *val;
354d47f9584Schristos 			break;
355d47f9584Schristos 		case XK_STR:
3560aefc7f9Schristos 			if ((ptr->val.str = wcsdup(val->str)) == NULL)
357d47f9584Schristos 				return -1;
358d47f9584Schristos 			break;
359d47f9584Schristos 		default:
360d47f9584Schristos 			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
361d47f9584Schristos 		}
362d47f9584Schristos 	} else {
363d47f9584Schristos 		/* still more chars to go */
364d47f9584Schristos 		if (ptr->next == NULL)
365d47f9584Schristos 			ptr->next = node__get(*str);	/* setup new node */
366d47f9584Schristos 		(void) node__try(el, ptr->next, str, val, ntype);
367d47f9584Schristos 	}
368b71bed95Schristos 	return 0;
369d47f9584Schristos }
370d47f9584Schristos 
371d47f9584Schristos 
372d47f9584Schristos /* node__delete():
373d47f9584Schristos  *	Delete node that matches str
374d47f9584Schristos  */
375469d44f8Schristos static int
3760594af80Schristos node__delete(EditLine *el, keymacro_node_t **inptr, const wchar_t *str)
377d47f9584Schristos {
378d47f9584Schristos 	keymacro_node_t *ptr;
379d47f9584Schristos 	keymacro_node_t *prev_ptr = NULL;
380d47f9584Schristos 
381d47f9584Schristos 	ptr = *inptr;
382d47f9584Schristos 
383d47f9584Schristos 	if (ptr->ch != *str) {
384d47f9584Schristos 		keymacro_node_t *xm;
385d47f9584Schristos 
386d47f9584Schristos 		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
387d47f9584Schristos 			if (xm->sibling->ch == *str)
388d47f9584Schristos 				break;
389d47f9584Schristos 		if (xm->sibling == NULL)
390b71bed95Schristos 			return 0;
391d47f9584Schristos 		prev_ptr = xm;
392d47f9584Schristos 		ptr = xm->sibling;
393d47f9584Schristos 	}
394d47f9584Schristos 	if (*++str == '\0') {
395d47f9584Schristos 		/* we're there */
396d47f9584Schristos 		if (prev_ptr == NULL)
397d47f9584Schristos 			*inptr = ptr->sibling;
398d47f9584Schristos 		else
399d47f9584Schristos 			prev_ptr->sibling = ptr->sibling;
400d47f9584Schristos 		ptr->sibling = NULL;
401d47f9584Schristos 		node__put(el, ptr);
402b71bed95Schristos 		return 1;
403d47f9584Schristos 	} else if (ptr->next != NULL &&
404d47f9584Schristos 	    node__delete(el, &ptr->next, str) == 1) {
405d47f9584Schristos 		if (ptr->next != NULL)
406b71bed95Schristos 			return 0;
407d47f9584Schristos 		if (prev_ptr == NULL)
408d47f9584Schristos 			*inptr = ptr->sibling;
409d47f9584Schristos 		else
410d47f9584Schristos 			prev_ptr->sibling = ptr->sibling;
411d47f9584Schristos 		ptr->sibling = NULL;
412d47f9584Schristos 		node__put(el, ptr);
413b71bed95Schristos 		return 1;
414d47f9584Schristos 	} else {
415b71bed95Schristos 		return 0;
416d47f9584Schristos 	}
417d47f9584Schristos }
418d47f9584Schristos 
419d47f9584Schristos 
420d47f9584Schristos /* node__put():
421d47f9584Schristos  *	Puts a tree of nodes onto free list using free(3).
422d47f9584Schristos  */
423469d44f8Schristos static void
424d47f9584Schristos node__put(EditLine *el, keymacro_node_t *ptr)
425d47f9584Schristos {
426d47f9584Schristos 	if (ptr == NULL)
427d47f9584Schristos 		return;
428d47f9584Schristos 
429d47f9584Schristos 	if (ptr->next != NULL) {
430d47f9584Schristos 		node__put(el, ptr->next);
431d47f9584Schristos 		ptr->next = NULL;
432d47f9584Schristos 	}
433d47f9584Schristos 	node__put(el, ptr->sibling);
434d47f9584Schristos 
435d47f9584Schristos 	switch (ptr->type) {
436d47f9584Schristos 	case XK_CMD:
437d47f9584Schristos 	case XK_NOD:
438d47f9584Schristos 		break;
439d47f9584Schristos 	case XK_STR:
440d47f9584Schristos 		if (ptr->val.str != NULL)
441a13cd756Schristos 			el_free(ptr->val.str);
442d47f9584Schristos 		break;
443d47f9584Schristos 	default:
444d47f9584Schristos 		EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
445d47f9584Schristos 	}
446a13cd756Schristos 	el_free(ptr);
447d47f9584Schristos }
448d47f9584Schristos 
449d47f9584Schristos 
450d47f9584Schristos /* node__get():
451d47f9584Schristos  *	Returns pointer to a keymacro_node_t for ch.
452d47f9584Schristos  */
453469d44f8Schristos static keymacro_node_t *
454f54e4f97Schristos node__get(wint_t ch)
455d47f9584Schristos {
456d47f9584Schristos 	keymacro_node_t *ptr;
457d47f9584Schristos 
458a13cd756Schristos 	ptr = el_malloc(sizeof(*ptr));
459d47f9584Schristos 	if (ptr == NULL)
460d47f9584Schristos 		return NULL;
4610594af80Schristos 	ptr->ch = ch;
462d47f9584Schristos 	ptr->type = XK_NOD;
463d47f9584Schristos 	ptr->val.str = NULL;
464d47f9584Schristos 	ptr->next = NULL;
465d47f9584Schristos 	ptr->sibling = NULL;
466b71bed95Schristos 	return ptr;
467d47f9584Schristos }
468d47f9584Schristos 
469469d44f8Schristos static void
470d47f9584Schristos node__free(keymacro_node_t *k)
471d47f9584Schristos {
472d47f9584Schristos 	if (k == NULL)
473d47f9584Schristos 		return;
474d47f9584Schristos 	node__free(k->sibling);
475d47f9584Schristos 	node__free(k->next);
476a13cd756Schristos 	el_free(k);
477d47f9584Schristos }
478d47f9584Schristos 
479d47f9584Schristos /* node_lookup():
480d47f9584Schristos  *	look for the str starting at node ptr.
481d47f9584Schristos  *	Print if last node
482d47f9584Schristos  */
483469d44f8Schristos static int
4840594af80Schristos node_lookup(EditLine *el, const wchar_t *str, keymacro_node_t *ptr,
4850594af80Schristos     size_t cnt)
486d47f9584Schristos {
487d47f9584Schristos 	ssize_t used;
488d47f9584Schristos 
489d47f9584Schristos 	if (ptr == NULL)
490b71bed95Schristos 		return -1;	/* cannot have null ptr */
491d47f9584Schristos 
492d47f9584Schristos 	if (!str || *str == 0) {
493d47f9584Schristos 		/* no more chars in str.  node_enum from here. */
494d47f9584Schristos 		(void) node_enum(el, ptr, cnt);
495b71bed95Schristos 		return 0;
496d47f9584Schristos 	} else {
497d47f9584Schristos 		/* If match put this char into el->el_keymacro.buf.  Recurse */
498d47f9584Schristos 		if (ptr->ch == *str) {
499d47f9584Schristos 			/* match found */
500d47f9584Schristos 			used = ct_visual_char(el->el_keymacro.buf + cnt,
501d47f9584Schristos 			    KEY_BUFSIZ - cnt, ptr->ch);
502d47f9584Schristos 			if (used == -1)
503b71bed95Schristos 				return -1; /* ran out of buffer space */
504d47f9584Schristos 			if (ptr->next != NULL)
505d47f9584Schristos 				/* not yet at leaf */
506d47f9584Schristos 				return (node_lookup(el, str + 1, ptr->next,
5073d802cf5Schristos 				    (size_t)used + cnt));
508d47f9584Schristos 			else {
509d47f9584Schristos 			    /* next node is null so key should be complete */
510d47f9584Schristos 				if (str[1] == 0) {
5113d802cf5Schristos 					size_t px = cnt + (size_t)used;
51202479cacSchristos 					el->el_keymacro.buf[px] = '"';
51302479cacSchristos 					el->el_keymacro.buf[px + 1] = '\0';
514d47f9584Schristos 					keymacro_kprint(el, el->el_keymacro.buf,
515d47f9584Schristos 					    &ptr->val, ptr->type);
516b71bed95Schristos 					return 0;
517d47f9584Schristos 				} else
518b71bed95Schristos 					return -1;
519d47f9584Schristos 					/* mismatch -- str still has chars */
520d47f9584Schristos 			}
521d47f9584Schristos 		} else {
522d47f9584Schristos 			/* no match found try sibling */
523d47f9584Schristos 			if (ptr->sibling)
524d47f9584Schristos 				return (node_lookup(el, str, ptr->sibling,
525d47f9584Schristos 				    cnt));
526d47f9584Schristos 			else
527b71bed95Schristos 				return -1;
528d47f9584Schristos 		}
529d47f9584Schristos 	}
530d47f9584Schristos }
531d47f9584Schristos 
532d47f9584Schristos 
533d47f9584Schristos /* node_enum():
534d47f9584Schristos  *	Traverse the node printing the characters it is bound in buffer
535d47f9584Schristos  */
536469d44f8Schristos static int
537d47f9584Schristos node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt)
538d47f9584Schristos {
539d47f9584Schristos         ssize_t used;
540d47f9584Schristos 
541d47f9584Schristos 	if (cnt >= KEY_BUFSIZ - 5) {	/* buffer too small */
542d47f9584Schristos 		el->el_keymacro.buf[++cnt] = '"';
543d47f9584Schristos 		el->el_keymacro.buf[++cnt] = '\0';
544d47f9584Schristos 		(void) fprintf(el->el_errfile,
545d47f9584Schristos 		    "Some extended keys too long for internal print buffer");
546fcf85103Schristos 		(void) fprintf(el->el_errfile, " \"%ls...\"\n",
54702479cacSchristos 		    el->el_keymacro.buf);
548b71bed95Schristos 		return 0;
549d47f9584Schristos 	}
550d47f9584Schristos 	if (ptr == NULL) {
551d47f9584Schristos #ifdef DEBUG_EDIT
552d47f9584Schristos 		(void) fprintf(el->el_errfile,
553d47f9584Schristos 		    "node_enum: BUG!! Null ptr passed\n!");
554d47f9584Schristos #endif
555b71bed95Schristos 		return -1;
556d47f9584Schristos 	}
557d47f9584Schristos 	/* put this char at end of str */
55802479cacSchristos         used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt,
55902479cacSchristos 	    ptr->ch);
560d47f9584Schristos 	if (ptr->next == NULL) {
561d47f9584Schristos 		/* print this key and function */
5623d802cf5Schristos 		el->el_keymacro.buf[cnt + (size_t)used   ] = '"';
5633d802cf5Schristos 		el->el_keymacro.buf[cnt + (size_t)used + 1] = '\0';
564d47f9584Schristos 		keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type);
565d47f9584Schristos 	} else
5663d802cf5Schristos 		(void) node_enum(el, ptr->next, cnt + (size_t)used);
567d47f9584Schristos 
568d47f9584Schristos 	/* go to sibling if there is one */
569d47f9584Schristos 	if (ptr->sibling)
570d47f9584Schristos 		(void) node_enum(el, ptr->sibling, cnt);
571b71bed95Schristos 	return 0;
572d47f9584Schristos }
573d47f9584Schristos 
574d47f9584Schristos 
575d47f9584Schristos /* keymacro_kprint():
576d47f9584Schristos  *	Print the specified key and its associated
577d47f9584Schristos  *	function specified by val
578d47f9584Schristos  */
579a2d6b270Schristos libedit_private void
5800594af80Schristos keymacro_kprint(EditLine *el, const wchar_t *key, keymacro_value_t *val,
5810594af80Schristos     int ntype)
582d47f9584Schristos {
583d47f9584Schristos 	el_bindings_t *fp;
584d47f9584Schristos 	char unparsbuf[EL_BUFSIZ];
585d47f9584Schristos 	static const char fmt[] = "%-15s->  %s\n";
586d47f9584Schristos 
587d47f9584Schristos 	if (val != NULL)
588d47f9584Schristos 		switch (ntype) {
589d47f9584Schristos 		case XK_STR:
590d47f9584Schristos 			(void) keymacro__decode_str(val->str, unparsbuf,
591d47f9584Schristos 			    sizeof(unparsbuf),
592d47f9584Schristos 			    ntype == XK_STR ? "\"\"" : "[]");
593d47f9584Schristos 			(void) fprintf(el->el_outfile, fmt,
594d47f9584Schristos 			    ct_encode_string(key, &el->el_scratch), unparsbuf);
595d47f9584Schristos 			break;
596d47f9584Schristos 		case XK_CMD:
597d47f9584Schristos 			for (fp = el->el_map.help; fp->name; fp++)
598d47f9584Schristos 				if (val->cmd == fp->func) {
599fcf85103Schristos                     wcstombs(unparsbuf, fp->name, sizeof(unparsbuf));
600d47f9584Schristos                     unparsbuf[sizeof(unparsbuf) -1] = '\0';
601d47f9584Schristos 					(void) fprintf(el->el_outfile, fmt,
602d47f9584Schristos                         ct_encode_string(key, &el->el_scratch), unparsbuf);
603d47f9584Schristos 					break;
604d47f9584Schristos 				}
605d47f9584Schristos #ifdef DEBUG_KEY
606d47f9584Schristos 			if (fp->name == NULL)
607d47f9584Schristos 				(void) fprintf(el->el_outfile,
608d47f9584Schristos 				    "BUG! Command not found.\n");
609d47f9584Schristos #endif
610d47f9584Schristos 
611d47f9584Schristos 			break;
612d47f9584Schristos 		default:
613d47f9584Schristos 			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
614d47f9584Schristos 		}
615d47f9584Schristos 	else
616d47f9584Schristos 		(void) fprintf(el->el_outfile, fmt, ct_encode_string(key,
617d47f9584Schristos 		    &el->el_scratch), "no input");
618d47f9584Schristos }
619d47f9584Schristos 
620d47f9584Schristos 
621d47f9584Schristos #define ADDC(c) \
622d47f9584Schristos 	if (b < eb) \
623d47f9584Schristos 		*b++ = c; \
624d47f9584Schristos 	else \
625d47f9584Schristos 		b++
626d47f9584Schristos /* keymacro__decode_str():
627d47f9584Schristos  *	Make a printable version of the ey
628d47f9584Schristos  */
629a2d6b270Schristos libedit_private size_t
6300594af80Schristos keymacro__decode_str(const wchar_t *str, char *buf, size_t len,
6310594af80Schristos     const char *sep)
632d47f9584Schristos {
633d47f9584Schristos 	char *b = buf, *eb = b + len;
6340594af80Schristos 	const wchar_t *p;
635d47f9584Schristos 
636d47f9584Schristos 	b = buf;
637d47f9584Schristos 	if (sep[0] != '\0') {
638d47f9584Schristos 		ADDC(sep[0]);
639d47f9584Schristos 	}
640d47f9584Schristos 	if (*str == '\0') {
641d47f9584Schristos 		ADDC('^');
642d47f9584Schristos 		ADDC('@');
643d47f9584Schristos 		goto add_endsep;
644d47f9584Schristos 	}
645d47f9584Schristos 	for (p = str; *p != 0; p++) {
6460594af80Schristos 		wchar_t dbuf[VISUAL_WIDTH_MAX];
6470594af80Schristos 		wchar_t *p2 = dbuf;
648d47f9584Schristos 		ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p);
649d47f9584Schristos 		while (l-- > 0) {
650d47f9584Schristos 			ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++);
651d47f9584Schristos 			if (n == -1) /* ran out of space */
652d47f9584Schristos 				goto add_endsep;
653d47f9584Schristos 			else
654d47f9584Schristos 				b += n;
655d47f9584Schristos 		}
656d47f9584Schristos 	}
657d47f9584Schristos add_endsep:
658d47f9584Schristos 	if (sep[0] != '\0' && sep[1] != '\0') {
659d47f9584Schristos 		ADDC(sep[1]);
660d47f9584Schristos 	}
661d47f9584Schristos 	ADDC('\0');
662d47f9584Schristos 	if ((size_t)(b - buf) >= len)
663d47f9584Schristos 	    buf[len - 1] = '\0';
664d47f9584Schristos 	return (size_t)(b - buf);
665d47f9584Schristos }
666