11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1992, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Christos Zoulas of Cornell University.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
16148834Sstefanf * 3. Neither the name of the University nor the names of its contributors
171573Srgrimes *    may be used to endorse or promote products derived from this software
181573Srgrimes *    without specific prior written permission.
191573Srgrimes *
201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
3184260Sobrien *
32237448Spfg *	$NetBSD: key.c,v 1.20 2009/02/15 21:55:23 christos Exp $
331573Srgrimes */
341573Srgrimes
351573Srgrimes#if !defined(lint) && !defined(SCCSID)
361573Srgrimesstatic char sccsid[] = "@(#)key.c	8.1 (Berkeley) 6/4/93";
371573Srgrimes#endif /* not lint && not SCCSID */
3884260Sobrien#include <sys/cdefs.h>
3984260Sobrien__FBSDID("$FreeBSD: releng/10.3/lib/libedit/key.c 237448 2012-06-22 18:01:22Z pfg $");
401573Srgrimes
411573Srgrimes/*
421573Srgrimes * key.c: This module contains the procedures for maintaining
431573Srgrimes *	  the extended-key map.
441573Srgrimes *
458870Srgrimes *      An extended-key (key) is a sequence of keystrokes introduced
46108470Sschweikh *	with a sequence introducer and consisting of an arbitrary
478870Srgrimes *	number of characters.  This module maintains a map (the el->el_key.map)
488870Srgrimes *	to convert these extended-key sequences into input strs
491573Srgrimes *	(XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
501573Srgrimes *
511573Srgrimes *      Warning:
521573Srgrimes *	  If key is a substr of some other keys, then the longer
531573Srgrimes *	  keys are lost!!  That is, if the keys "abcd" and "abcef"
541573Srgrimes *	  are in el->el_key.map, adding the key "abc" will cause the first two
551573Srgrimes *	  definitions to be lost.
561573Srgrimes *
571573Srgrimes *      Restrictions:
581573Srgrimes *      -------------
591573Srgrimes *      1) It is not possible to have one key that is a
601573Srgrimes *	   substr of another.
611573Srgrimes */
621573Srgrimes#include <string.h>
631573Srgrimes#include <stdlib.h>
641573Srgrimes
651573Srgrimes#include "el.h"
661573Srgrimes
678870Srgrimes/*
688870Srgrimes * The Nodes of the el->el_key.map.  The el->el_key.map is a linked list
691573Srgrimes * of these node elements
701573Srgrimes */
711573Srgrimesstruct key_node_t {
7284260Sobrien	char		ch;		/* single character of key 	 */
7384260Sobrien	int		type;		/* node type			 */
7484260Sobrien	key_value_t	val;		/* command code or pointer to str,  */
7584260Sobrien					/* if this is a leaf 		 */
7684260Sobrien	struct key_node_t *next;	/* ptr to next char of this key  */
7784260Sobrien	struct key_node_t *sibling;	/* ptr to another key with same prefix*/
781573Srgrimes};
791573Srgrimes
8084260Sobrienprivate int		 node_trav(EditLine *, key_node_t *, char *,
8184260Sobrien    key_value_t *);
8284260Sobrienprivate int		 node__try(EditLine *, key_node_t *, const char *,
8384260Sobrien    key_value_t *, int);
8484260Sobrienprivate key_node_t	*node__get(int);
85148834Sstefanfprivate void		 node__free(key_node_t *);
8684260Sobrienprivate void		 node__put(EditLine *, key_node_t *);
87148834Sstefanfprivate int		 node__delete(EditLine *, key_node_t **, const char *);
88148834Sstefanfprivate int		 node_lookup(EditLine *, const char *, key_node_t *,
89237448Spfg    size_t);
90237448Spfgprivate int		 node_enum(EditLine *, key_node_t *, size_t);
911573Srgrimes
9284260Sobrien#define	KEY_BUFSIZ	EL_BUFSIZ
931573Srgrimes
941573Srgrimes
951573Srgrimes/* key_init():
961573Srgrimes *	Initialize the key maps
971573Srgrimes */
981573Srgrimesprotected int
9984260Sobrienkey_init(EditLine *el)
1001573Srgrimes{
10184260Sobrien
10284260Sobrien	el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ);
10384260Sobrien	if (el->el_key.buf == NULL)
10484260Sobrien		return (-1);
10584260Sobrien	el->el_key.map = NULL;
10684260Sobrien	key_reset(el);
10784260Sobrien	return (0);
1088870Srgrimes}
1091573Srgrimes
1101573Srgrimes/* key_end():
1111573Srgrimes *	Free the key maps
1121573Srgrimes */
1131573Srgrimesprotected void
11484260Sobrienkey_end(EditLine *el)
1151573Srgrimes{
11684260Sobrien
11784260Sobrien	el_free((ptr_t) el->el_key.buf);
11884260Sobrien	el->el_key.buf = NULL;
119148834Sstefanf	node__free(el->el_key.map);
1208870Srgrimes}
1211573Srgrimes
1221573Srgrimes
1231573Srgrimes/* key_map_cmd():
1241573Srgrimes *	Associate cmd with a key value
1251573Srgrimes */
1261573Srgrimesprotected key_value_t *
12784260Sobrienkey_map_cmd(EditLine *el, int cmd)
1281573Srgrimes{
12984260Sobrien
13084260Sobrien	el->el_key.val.cmd = (el_action_t) cmd;
13184260Sobrien	return (&el->el_key.val);
1321573Srgrimes}
1331573Srgrimes
1341573Srgrimes
1351573Srgrimes/* key_map_str():
1361573Srgrimes *	Associate str with a key value
1371573Srgrimes */
1381573Srgrimesprotected key_value_t *
13984260Sobrienkey_map_str(EditLine *el, char *str)
1401573Srgrimes{
14184260Sobrien
14284260Sobrien	el->el_key.val.str = str;
14384260Sobrien	return (&el->el_key.val);
1441573Srgrimes}
1451573Srgrimes
1461573Srgrimes
1471573Srgrimes/* key_reset():
1481573Srgrimes *	Takes all nodes on el->el_key.map and puts them on free list.  Then
1491573Srgrimes *	initializes el->el_key.map with arrow keys
1501573Srgrimes *	[Always bind the ansi arrow keys?]
1511573Srgrimes */
1521573Srgrimesprotected void
15384260Sobrienkey_reset(EditLine *el)
1541573Srgrimes{
15584260Sobrien
15684260Sobrien	node__put(el, el->el_key.map);
15784260Sobrien	el->el_key.map = NULL;
15884260Sobrien	return;
1591573Srgrimes}
1601573Srgrimes
1611573Srgrimes
1621573Srgrimes/* key_get():
1631573Srgrimes *	Calls the recursive function with entry point el->el_key.map
1641573Srgrimes *      Looks up *ch in map and then reads characters until a
1651573Srgrimes *      complete match is found or a mismatch occurs. Returns the
1661573Srgrimes *      type of the match found (XK_STR, XK_CMD, or XK_EXE).
1678870Srgrimes *      Returns NULL in val.str and XK_STR for no match.
1681573Srgrimes *      The last character read is returned in *ch.
1691573Srgrimes */
1701573Srgrimesprotected int
17184260Sobrienkey_get(EditLine *el, char *ch, key_value_t *val)
1721573Srgrimes{
17384260Sobrien
17484260Sobrien	return (node_trav(el, el->el_key.map, ch, val));
1751573Srgrimes}
1761573Srgrimes
1771573Srgrimes
1781573Srgrimes/* key_add():
1791573Srgrimes *      Adds key to the el->el_key.map and associates the value in val with it.
1801573Srgrimes *      If key is already is in el->el_key.map, the new code is applied to the
1811573Srgrimes *      existing key. Ntype specifies if code is a command, an
1821573Srgrimes *      out str or a unix command.
1831573Srgrimes */
1841573Srgrimesprotected void
18584260Sobrienkey_add(EditLine *el, const char *key, key_value_t *val, int ntype)
1861573Srgrimes{
1871573Srgrimes
18884260Sobrien	if (key[0] == '\0') {
18984260Sobrien		(void) fprintf(el->el_errfile,
19084260Sobrien		    "key_add: Null extended-key not allowed.\n");
19184260Sobrien		return;
19284260Sobrien	}
19384260Sobrien	if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
19484260Sobrien		(void) fprintf(el->el_errfile,
19584260Sobrien		    "key_add: sequence-lead-in command not allowed\n");
19684260Sobrien		return;
19784260Sobrien	}
19884260Sobrien	if (el->el_key.map == NULL)
19984260Sobrien		/* tree is initially empty.  Set up new node to match key[0] */
20084260Sobrien		el->el_key.map = node__get(key[0]);
20184260Sobrien			/* it is properly initialized */
20284260Sobrien
20384260Sobrien	/* Now recurse through el->el_key.map */
20484260Sobrien	(void) node__try(el, el->el_key.map, key, val, ntype);
2051573Srgrimes	return;
2061573Srgrimes}
2071573Srgrimes
2081573Srgrimes
2091573Srgrimes/* key_clear():
2101573Srgrimes *
2111573Srgrimes */
2121573Srgrimesprotected void
213148834Sstefanfkey_clear(EditLine *el, el_action_t *map, const char *in)
2141573Srgrimes{
21584260Sobrien
21684260Sobrien	if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
21784260Sobrien	    ((map == el->el_map.key &&
21884260Sobrien	    el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
21984260Sobrien	    (map == el->el_map.alt &&
22084260Sobrien	    el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
22184260Sobrien		(void) key_delete(el, in);
2221573Srgrimes}
2231573Srgrimes
2241573Srgrimes
2251573Srgrimes/* key_delete():
2261573Srgrimes *      Delete the key and all longer keys staring with key, if
2271573Srgrimes *      they exists.
2281573Srgrimes */
2291573Srgrimesprotected int
230148834Sstefanfkey_delete(EditLine *el, const char *key)
2311573Srgrimes{
2321573Srgrimes
23384260Sobrien	if (key[0] == '\0') {
23484260Sobrien		(void) fprintf(el->el_errfile,
23584260Sobrien		    "key_delete: Null extended-key not allowed.\n");
23684260Sobrien		return (-1);
23784260Sobrien	}
23884260Sobrien	if (el->el_key.map == NULL)
23984260Sobrien		return (0);
2401573Srgrimes
24184260Sobrien	(void) node__delete(el, &el->el_key.map, key);
24284260Sobrien	return (0);
2431573Srgrimes}
2441573Srgrimes
2451573Srgrimes
2461573Srgrimes/* key_print():
2471573Srgrimes *	Print the binding associated with key key.
2481573Srgrimes *	Print entire el->el_key.map if null
2491573Srgrimes */
2501573Srgrimesprotected void
251148834Sstefanfkey_print(EditLine *el, const char *key)
2521573Srgrimes{
25384260Sobrien
25484260Sobrien	/* do nothing if el->el_key.map is empty and null key specified */
25584260Sobrien	if (el->el_key.map == NULL && *key == 0)
25684260Sobrien		return;
25784260Sobrien
25884260Sobrien	el->el_key.buf[0] = '"';
25984260Sobrien	if (node_lookup(el, key, el->el_key.map, 1) <= -1)
26084260Sobrien		/* key is not bound */
26184260Sobrien		(void) fprintf(el->el_errfile, "Unbound extended key \"%s\"\n",
26284260Sobrien		    key);
2631573Srgrimes	return;
2641573Srgrimes}
2651573Srgrimes
2661573Srgrimes
2671573Srgrimes/* node_trav():
2681573Srgrimes *	recursively traverses node in tree until match or mismatch is
2691573Srgrimes * 	found.  May read in more characters.
2701573Srgrimes */
2711573Srgrimesprivate int
27284260Sobriennode_trav(EditLine *el, key_node_t *ptr, char *ch, key_value_t *val)
2731573Srgrimes{
27484260Sobrien
27584260Sobrien	if (ptr->ch == *ch) {
27684260Sobrien		/* match found */
27784260Sobrien		if (ptr->next) {
27884260Sobrien			/* key not complete so get next char */
27984260Sobrien			if (el_getc(el, ch) != 1) {	/* if EOF or error */
28084260Sobrien				val->cmd = ED_END_OF_FILE;
28184260Sobrien				return (XK_CMD);
28284260Sobrien				/* PWP: Pretend we just read an end-of-file */
28384260Sobrien			}
28484260Sobrien			return (node_trav(el, ptr->next, ch, val));
28584260Sobrien		} else {
28684260Sobrien			*val = ptr->val;
28784260Sobrien			if (ptr->type != XK_CMD)
28884260Sobrien				*ch = '\0';
28984260Sobrien			return (ptr->type);
29084260Sobrien		}
29184260Sobrien	} else {
29284260Sobrien		/* no match found here */
29384260Sobrien		if (ptr->sibling) {
29484260Sobrien			/* try next sibling */
29584260Sobrien			return (node_trav(el, ptr->sibling, ch, val));
29684260Sobrien		} else {
29784260Sobrien			/* no next sibling -- mismatch */
29884260Sobrien			val->str = NULL;
29984260Sobrien			return (XK_STR);
30084260Sobrien		}
3011573Srgrimes	}
3021573Srgrimes}
3031573Srgrimes
3041573Srgrimes
3051573Srgrimes/* node__try():
3061573Srgrimes * 	Find a node that matches *str or allocate a new one
3071573Srgrimes */
3081573Srgrimesprivate int
30984260Sobriennode__try(EditLine *el, key_node_t *ptr, const char *str, key_value_t *val, int ntype)
3101573Srgrimes{
3111573Srgrimes
31284260Sobrien	if (ptr->ch != *str) {
31384260Sobrien		key_node_t *xm;
3141573Srgrimes
31584260Sobrien		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
31684260Sobrien			if (xm->sibling->ch == *str)
31784260Sobrien				break;
31884260Sobrien		if (xm->sibling == NULL)
31984260Sobrien			xm->sibling = node__get(*str);	/* setup new node */
32084260Sobrien		ptr = xm->sibling;
3211573Srgrimes	}
32284260Sobrien	if (*++str == '\0') {
32384260Sobrien		/* we're there */
32484260Sobrien		if (ptr->next != NULL) {
32584260Sobrien			node__put(el, ptr->next);
32684260Sobrien				/* lose longer keys with this prefix */
32784260Sobrien			ptr->next = NULL;
32884260Sobrien		}
32984260Sobrien		switch (ptr->type) {
33084260Sobrien		case XK_CMD:
33184260Sobrien		case XK_NOD:
33284260Sobrien			break;
33384260Sobrien		case XK_STR:
33484260Sobrien		case XK_EXE:
33584260Sobrien			if (ptr->val.str)
33684260Sobrien				el_free((ptr_t) ptr->val.str);
33784260Sobrien			break;
33884260Sobrien		default:
33984260Sobrien			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
34084260Sobrien			    ptr->type));
34184260Sobrien			break;
34284260Sobrien		}
3431573Srgrimes
34484260Sobrien		switch (ptr->type = ntype) {
34584260Sobrien		case XK_CMD:
34684260Sobrien			ptr->val = *val;
34784260Sobrien			break;
34884260Sobrien		case XK_STR:
34984260Sobrien		case XK_EXE:
350148834Sstefanf			if ((ptr->val.str = el_strdup(val->str)) == NULL)
351148834Sstefanf				return -1;
35284260Sobrien			break;
35384260Sobrien		default:
35484260Sobrien			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
35584260Sobrien			break;
35684260Sobrien		}
35784260Sobrien	} else {
35884260Sobrien		/* still more chars to go */
35984260Sobrien		if (ptr->next == NULL)
36084260Sobrien			ptr->next = node__get(*str);	/* setup new node */
36184260Sobrien		(void) node__try(el, ptr->next, str, val, ntype);
3621573Srgrimes	}
36384260Sobrien	return (0);
3641573Srgrimes}
3651573Srgrimes
3661573Srgrimes
3671573Srgrimes/* node__delete():
3681573Srgrimes *	Delete node that matches str
3691573Srgrimes */
3701573Srgrimesprivate int
371148834Sstefanfnode__delete(EditLine *el, key_node_t **inptr, const char *str)
3721573Srgrimes{
37384260Sobrien	key_node_t *ptr;
37484260Sobrien	key_node_t *prev_ptr = NULL;
3751573Srgrimes
37684260Sobrien	ptr = *inptr;
3771573Srgrimes
37884260Sobrien	if (ptr->ch != *str) {
37984260Sobrien		key_node_t *xm;
3801573Srgrimes
38184260Sobrien		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
38284260Sobrien			if (xm->sibling->ch == *str)
38384260Sobrien				break;
38484260Sobrien		if (xm->sibling == NULL)
38584260Sobrien			return (0);
38684260Sobrien		prev_ptr = xm;
38784260Sobrien		ptr = xm->sibling;
38884260Sobrien	}
38984260Sobrien	if (*++str == '\0') {
39084260Sobrien		/* we're there */
39184260Sobrien		if (prev_ptr == NULL)
39284260Sobrien			*inptr = ptr->sibling;
39384260Sobrien		else
39484260Sobrien			prev_ptr->sibling = ptr->sibling;
39584260Sobrien		ptr->sibling = NULL;
39684260Sobrien		node__put(el, ptr);
39784260Sobrien		return (1);
39884260Sobrien	} else if (ptr->next != NULL &&
39984260Sobrien	    node__delete(el, &ptr->next, str) == 1) {
40084260Sobrien		if (ptr->next != NULL)
40184260Sobrien			return (0);
40284260Sobrien		if (prev_ptr == NULL)
40384260Sobrien			*inptr = ptr->sibling;
40484260Sobrien		else
40584260Sobrien			prev_ptr->sibling = ptr->sibling;
40684260Sobrien		ptr->sibling = NULL;
40784260Sobrien		node__put(el, ptr);
40884260Sobrien		return (1);
40984260Sobrien	} else {
41084260Sobrien		return (0);
41184260Sobrien	}
4121573Srgrimes}
4131573Srgrimes
41484260Sobrien
4151573Srgrimes/* node__put():
4161573Srgrimes *	Puts a tree of nodes onto free list using free(3).
4171573Srgrimes */
4181573Srgrimesprivate void
41984260Sobriennode__put(EditLine *el, key_node_t *ptr)
4201573Srgrimes{
42184260Sobrien	if (ptr == NULL)
42284260Sobrien		return;
4231573Srgrimes
42484260Sobrien	if (ptr->next != NULL) {
42584260Sobrien		node__put(el, ptr->next);
42684260Sobrien		ptr->next = NULL;
42784260Sobrien	}
42884260Sobrien	node__put(el, ptr->sibling);
4291573Srgrimes
43084260Sobrien	switch (ptr->type) {
43184260Sobrien	case XK_CMD:
43284260Sobrien	case XK_NOD:
43384260Sobrien		break;
43484260Sobrien	case XK_EXE:
43584260Sobrien	case XK_STR:
43684260Sobrien		if (ptr->val.str != NULL)
43784260Sobrien			el_free((ptr_t) ptr->val.str);
43884260Sobrien		break;
43984260Sobrien	default:
44084260Sobrien		EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
44184260Sobrien		break;
44284260Sobrien	}
44384260Sobrien	el_free((ptr_t) ptr);
4441573Srgrimes}
4451573Srgrimes
4461573Srgrimes
4471573Srgrimes/* node__get():
448108470Sschweikh *	Returns pointer to a key_node_t for ch.
4491573Srgrimes */
4501573Srgrimesprivate key_node_t *
45184260Sobriennode__get(int ch)
4521573Srgrimes{
45384260Sobrien	key_node_t *ptr;
4541573Srgrimes
45584260Sobrien	ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
45684260Sobrien	if (ptr == NULL)
45784260Sobrien		return NULL;
45884260Sobrien	ptr->ch = ch;
45984260Sobrien	ptr->type = XK_NOD;
46084260Sobrien	ptr->val.str = NULL;
46184260Sobrien	ptr->next = NULL;
46284260Sobrien	ptr->sibling = NULL;
46384260Sobrien	return (ptr);
4641573Srgrimes}
4651573Srgrimes
466148834Sstefanfprivate void
467148834Sstefanfnode__free(key_node_t *k)
468148834Sstefanf{
469148834Sstefanf	if (k == NULL)
470148834Sstefanf		return;
471148834Sstefanf	node__free(k->sibling);
472148834Sstefanf	node__free(k->next);
473148834Sstefanf	el_free((ptr_t) k);
474148834Sstefanf}
4751573Srgrimes
4761573Srgrimes/* node_lookup():
4771573Srgrimes *	look for the str starting at node ptr.
4781573Srgrimes *	Print if last node
4791573Srgrimes */
4801573Srgrimesprivate int
481237448Spfgnode_lookup(EditLine *el, const char *str, key_node_t *ptr, size_t cnt)
4821573Srgrimes{
483237448Spfg	size_t ncnt;
4841573Srgrimes
48584260Sobrien	if (ptr == NULL)
48684260Sobrien		return (-1);	/* cannot have null ptr */
4871573Srgrimes
48884260Sobrien	if (*str == 0) {
48984260Sobrien		/* no more chars in str.  node_enum from here. */
49084260Sobrien		(void) node_enum(el, ptr, cnt);
49184260Sobrien		return (0);
49284260Sobrien	} else {
49384260Sobrien		/* If match put this char into el->el_key.buf.  Recurse */
49484260Sobrien		if (ptr->ch == *str) {
49584260Sobrien			/* match found */
496237448Spfg			ncnt = key__decode_char(el->el_key.buf,
497237448Spfg			    (size_t)KEY_BUFSIZ, cnt,
49884260Sobrien			    (unsigned char) ptr->ch);
49984260Sobrien			if (ptr->next != NULL)
50084260Sobrien				/* not yet at leaf */
50184260Sobrien				return (node_lookup(el, str + 1, ptr->next,
50284260Sobrien				    ncnt + 1));
50384260Sobrien			else {
50484260Sobrien			    /* next node is null so key should be complete */
50584260Sobrien				if (str[1] == 0) {
50684260Sobrien					el->el_key.buf[ncnt + 1] = '"';
50784260Sobrien					el->el_key.buf[ncnt + 2] = '\0';
50884260Sobrien					key_kprint(el, el->el_key.buf,
50984260Sobrien					    &ptr->val, ptr->type);
51084260Sobrien					return (0);
51184260Sobrien				} else
51284260Sobrien					return (-1);
51384260Sobrien					/* mismatch -- str still has chars */
51484260Sobrien			}
51584260Sobrien		} else {
51684260Sobrien			/* no match found try sibling */
51784260Sobrien			if (ptr->sibling)
51884260Sobrien				return (node_lookup(el, str, ptr->sibling,
51984260Sobrien				    cnt));
52084260Sobrien			else
52184260Sobrien				return (-1);
5221573Srgrimes		}
5231573Srgrimes	}
5241573Srgrimes}
5251573Srgrimes
5261573Srgrimes
5271573Srgrimes/* node_enum():
5281573Srgrimes *	Traverse the node printing the characters it is bound in buffer
5291573Srgrimes */
5301573Srgrimesprivate int
531237448Spfgnode_enum(EditLine *el, key_node_t *ptr, size_t cnt)
5321573Srgrimes{
533237448Spfg	size_t ncnt;
5341573Srgrimes
53584260Sobrien	if (cnt >= KEY_BUFSIZ - 5) {	/* buffer too small */
53684260Sobrien		el->el_key.buf[++cnt] = '"';
53784260Sobrien		el->el_key.buf[++cnt] = '\0';
53884260Sobrien		(void) fprintf(el->el_errfile,
5391573Srgrimes		    "Some extended keys too long for internal print buffer");
54084260Sobrien		(void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf);
54184260Sobrien		return (0);
54284260Sobrien	}
54384260Sobrien	if (ptr == NULL) {
5441573Srgrimes#ifdef DEBUG_EDIT
54584260Sobrien		(void) fprintf(el->el_errfile,
54684260Sobrien		    "node_enum: BUG!! Null ptr passed\n!");
5471573Srgrimes#endif
54884260Sobrien		return (-1);
54984260Sobrien	}
55084260Sobrien	/* put this char at end of str */
551237448Spfg	ncnt = key__decode_char(el->el_key.buf, (size_t)KEY_BUFSIZ, cnt,
552167457Sstefanf	    (unsigned char)ptr->ch);
55384260Sobrien	if (ptr->next == NULL) {
55484260Sobrien		/* print this key and function */
55584260Sobrien		el->el_key.buf[ncnt + 1] = '"';
55684260Sobrien		el->el_key.buf[ncnt + 2] = '\0';
55784260Sobrien		key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
55884260Sobrien	} else
55984260Sobrien		(void) node_enum(el, ptr->next, ncnt + 1);
5601573Srgrimes
56184260Sobrien	/* go to sibling if there is one */
56284260Sobrien	if (ptr->sibling)
56384260Sobrien		(void) node_enum(el, ptr->sibling, cnt);
56484260Sobrien	return (0);
5651573Srgrimes}
5661573Srgrimes
5671573Srgrimes
5681573Srgrimes/* key_kprint():
5691573Srgrimes *	Print the specified key and its associated
5701573Srgrimes *	function specified by val
5711573Srgrimes */
5721573Srgrimesprotected void
573148834Sstefanfkey_kprint(EditLine *el, const char *key, key_value_t *val, int ntype)
5741573Srgrimes{
57584260Sobrien	el_bindings_t *fp;
57684260Sobrien	char unparsbuf[EL_BUFSIZ];
57784260Sobrien	static const char fmt[] = "%-15s->  %s\n";
5781573Srgrimes
57984260Sobrien	if (val != NULL)
58084260Sobrien		switch (ntype) {
58184260Sobrien		case XK_STR:
58284260Sobrien		case XK_EXE:
583167457Sstefanf			(void) key__decode_str(val->str, unparsbuf,
584167457Sstefanf			    sizeof(unparsbuf),
585167457Sstefanf			    ntype == XK_STR ? "\"\"" : "[]");
586167457Sstefanf			(void) fprintf(el->el_outfile, fmt, key, unparsbuf);
58784260Sobrien			break;
58884260Sobrien		case XK_CMD:
58984260Sobrien			for (fp = el->el_map.help; fp->name; fp++)
59084260Sobrien				if (val->cmd == fp->func) {
59184260Sobrien					(void) fprintf(el->el_outfile, fmt,
59284260Sobrien					    key, fp->name);
59384260Sobrien					break;
59484260Sobrien				}
5951573Srgrimes#ifdef DEBUG_KEY
59684260Sobrien			if (fp->name == NULL)
59784260Sobrien				(void) fprintf(el->el_outfile,
59884260Sobrien				    "BUG! Command not found.\n");
5991573Srgrimes#endif
6001573Srgrimes
60184260Sobrien			break;
60284260Sobrien		default:
60384260Sobrien			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
60484260Sobrien			break;
60584260Sobrien		}
60684260Sobrien	else
60784260Sobrien		(void) fprintf(el->el_outfile, fmt, key, "no input");
6081573Srgrimes}
6091573Srgrimes
6101573Srgrimes
611167457Sstefanf#define ADDC(c) \
612167457Sstefanf	if (b < eb) \
613167457Sstefanf		*b++ = c; \
614167457Sstefanf	else \
615167457Sstefanf		b++
6161573Srgrimes/* key__decode_char():
6171573Srgrimes *	Put a printable form of char in buf.
6181573Srgrimes */
619237448Spfgprotected size_t
620237448Spfgkey__decode_char(char *buf, size_t cnt, size_t off, int ch)
6211573Srgrimes{
622167457Sstefanf	char *sb = buf + off;
623167457Sstefanf	char *eb = buf + cnt;
624167457Sstefanf	char *b = sb;
625167457Sstefanf
62684260Sobrien	ch = (unsigned char)ch;
62784260Sobrien	if (ch == 0) {
628167457Sstefanf		ADDC('^');
629167457Sstefanf		ADDC('@');
630237448Spfg		return (int)(b - sb);
63184260Sobrien	}
63284260Sobrien	if (iscntrl(ch)) {
633167457Sstefanf		ADDC('^');
634167457Sstefanf		if (ch == '\177')
635167457Sstefanf			ADDC('?');
63684260Sobrien		else
637167457Sstefanf			ADDC(toascii(ch) | 0100);
63884260Sobrien	} else if (ch == '^') {
639167457Sstefanf		ADDC('\\');
640167457Sstefanf		ADDC('^');
64184260Sobrien	} else if (ch == '\\') {
642167457Sstefanf		ADDC('\\');
643167457Sstefanf		ADDC('\\');
64484260Sobrien	} else if (ch == ' ' || (isprint(ch) && !isspace(ch))) {
645167457Sstefanf		ADDC(ch);
64684260Sobrien	} else {
647167457Sstefanf		ADDC('\\');
648167457Sstefanf		ADDC((((unsigned int) ch >> 6) & 7) + '0');
649167457Sstefanf		ADDC((((unsigned int) ch >> 3) & 7) + '0');
650167457Sstefanf		ADDC((ch & 7) + '0');
65184260Sobrien	}
652237448Spfg	return (size_t)(b - sb);
6531573Srgrimes}
6541573Srgrimes
655148834Sstefanf
6561573Srgrimes/* key__decode_str():
6571573Srgrimes *	Make a printable version of the ey
6581573Srgrimes */
659237448Spfgprotected size_t
660237448Spfgkey__decode_str(const char *str, char *buf, size_t len, const char *sep)
6611573Srgrimes{
662167457Sstefanf	char *b = buf, *eb = b + len;
663148834Sstefanf	const char *p;
6641573Srgrimes
66584260Sobrien	b = buf;
666167457Sstefanf	if (sep[0] != '\0') {
667167457Sstefanf		ADDC(sep[0]);
66884260Sobrien	}
669167457Sstefanf	if (*str == '\0') {
670167457Sstefanf		ADDC('^');
671167457Sstefanf		ADDC('@');
672167457Sstefanf		if (sep[0] != '\0' && sep[1] != '\0') {
673167457Sstefanf			ADDC(sep[1]);
674167457Sstefanf		}
675167457Sstefanf		goto done;
676167457Sstefanf	}
67784260Sobrien	for (p = str; *p != 0; p++) {
67884260Sobrien		if (iscntrl((unsigned char) *p)) {
679167457Sstefanf			ADDC('^');
680167457Sstefanf			if (*p == '\177') {
681167457Sstefanf				ADDC('?');
682167457Sstefanf			} else {
683167457Sstefanf				ADDC(toascii(*p) | 0100);
684167457Sstefanf			}
68584260Sobrien		} else if (*p == '^' || *p == '\\') {
686167457Sstefanf			ADDC('\\');
687167457Sstefanf			ADDC(*p);
68884260Sobrien		} else if (*p == ' ' || (isprint((unsigned char) *p) &&
68984260Sobrien			!isspace((unsigned char) *p))) {
690167457Sstefanf			ADDC(*p);
69184260Sobrien		} else {
692167457Sstefanf			ADDC('\\');
693167457Sstefanf			ADDC((((unsigned int) *p >> 6) & 7) + '0');
694167457Sstefanf			ADDC((((unsigned int) *p >> 3) & 7) + '0');
695167457Sstefanf			ADDC((*p & 7) + '0');
69684260Sobrien		}
69784260Sobrien	}
698167457Sstefanf	if (sep[0] != '\0' && sep[1] != '\0') {
699167457Sstefanf		ADDC(sep[1]);
700167457Sstefanf	}
701167457Sstefanfdone:
702167457Sstefanf	ADDC('\0');
703237448Spfg	if ((size_t)(b - buf) >= len)
704167457Sstefanf	    buf[len - 1] = '\0';
705237448Spfg	return (size_t)(b - buf);
7061573Srgrimes}
707