1/*	$NetBSD: key.c,v 1.5 2005/06/09 16:48:58 lukem Exp $	*/
2/*	from	NetBSD: key.c,v 1.15 2003/10/18 23:48:42 christos Exp	*/
3
4/*-
5 * Copyright (c) 1992, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Christos Zoulas of Cornell University.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "tnftp.h"
37#include "sys.h"
38
39/*
40 * key.c: This module contains the procedures for maintaining
41 *	  the extended-key map.
42 *
43 *      An extended-key (key) is a sequence of keystrokes introduced
44 *	with an sequence introducer and consisting of an arbitrary
45 *	number of characters.  This module maintains a map (the el->el_key.map)
46 *	to convert these extended-key sequences into input strs
47 *	(XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
48 *
49 *      Warning:
50 *	  If key is a substr of some other keys, then the longer
51 *	  keys are lost!!  That is, if the keys "abcd" and "abcef"
52 *	  are in el->el_key.map, adding the key "abc" will cause the first two
53 *	  definitions to be lost.
54 *
55 *      Restrictions:
56 *      -------------
57 *      1) It is not possible to have one key that is a
58 *	   substr of another.
59 */
60#include <string.h>
61#include <stdlib.h>
62
63#include "el.h"
64
65/*
66 * The Nodes of the el->el_key.map.  The el->el_key.map is a linked list
67 * of these node elements
68 */
69struct key_node_t {
70	char		ch;		/* single character of key 	 */
71	int		type;		/* node type			 */
72	key_value_t	val;		/* command code or pointer to str,  */
73					/* if this is a leaf 		 */
74	struct key_node_t *next;	/* ptr to next char of this key  */
75	struct key_node_t *sibling;	/* ptr to another key with same prefix*/
76};
77
78private int		 node_trav(EditLine *, key_node_t *, char *,
79    key_value_t *);
80private int		 node__try(EditLine *, key_node_t *, const char *,
81    key_value_t *, int);
82private key_node_t	*node__get(int);
83private void		 node__put(EditLine *, key_node_t *);
84private int		 node__delete(EditLine *, key_node_t **, const char *);
85private int		 node_lookup(EditLine *, const char *, key_node_t *,
86    int);
87private int		 node_enum(EditLine *, key_node_t *, int);
88private int		 key__decode_char(char *, int, int);
89
90#define	KEY_BUFSIZ	EL_BUFSIZ
91
92
93/* key_init():
94 *	Initialize the key maps
95 */
96protected int
97key_init(EditLine *el)
98{
99
100	el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ);
101	if (el->el_key.buf == NULL)
102		return (-1);
103	el->el_key.map = NULL;
104	key_reset(el);
105	return (0);
106}
107
108
109/* key_end():
110 *	Free the key maps
111 */
112protected void
113key_end(EditLine *el)
114{
115
116	el_free((ptr_t) el->el_key.buf);
117	el->el_key.buf = NULL;
118	/* XXX: provide a function to clear the keys */
119	el->el_key.map = NULL;
120}
121
122
123/* key_map_cmd():
124 *	Associate cmd with a key value
125 */
126protected key_value_t *
127key_map_cmd(EditLine *el, int cmd)
128{
129
130	el->el_key.val.cmd = (el_action_t) cmd;
131	return (&el->el_key.val);
132}
133
134
135/* key_map_str():
136 *	Associate str with a key value
137 */
138protected key_value_t *
139key_map_str(EditLine *el, char *str)
140{
141
142	el->el_key.val.str = str;
143	return (&el->el_key.val);
144}
145
146
147/* key_reset():
148 *	Takes all nodes on el->el_key.map and puts them on free list.  Then
149 *	initializes el->el_key.map with arrow keys
150 *	[Always bind the ansi arrow keys?]
151 */
152protected void
153key_reset(EditLine *el)
154{
155
156	node__put(el, el->el_key.map);
157	el->el_key.map = NULL;
158	return;
159}
160
161
162/* key_get():
163 *	Calls the recursive function with entry point el->el_key.map
164 *      Looks up *ch in map and then reads characters until a
165 *      complete match is found or a mismatch occurs. Returns the
166 *      type of the match found (XK_STR, XK_CMD, or XK_EXE).
167 *      Returns NULL in val.str and XK_STR for no match.
168 *      The last character read is returned in *ch.
169 */
170protected int
171key_get(EditLine *el, char *ch, key_value_t *val)
172{
173
174	return (node_trav(el, el->el_key.map, ch, val));
175}
176
177
178/* key_add():
179 *      Adds key to the el->el_key.map and associates the value in val with it.
180 *      If key is already is in el->el_key.map, the new code is applied to the
181 *      existing key. Ntype specifies if code is a command, an
182 *      out str or a unix command.
183 */
184protected void
185key_add(EditLine *el, const char *key, key_value_t *val, int ntype)
186{
187
188	if (key[0] == '\0') {
189		(void) fprintf(el->el_errfile,
190		    "key_add: Null extended-key not allowed.\n");
191		return;
192	}
193	if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
194		(void) fprintf(el->el_errfile,
195		    "key_add: sequence-lead-in command not allowed\n");
196		return;
197	}
198	if (el->el_key.map == NULL)
199		/* tree is initially empty.  Set up new node to match key[0] */
200		el->el_key.map = node__get(key[0]);
201			/* it is properly initialized */
202
203	/* Now recurse through el->el_key.map */
204	(void) node__try(el, el->el_key.map, key, val, ntype);
205	return;
206}
207
208
209/* key_clear():
210 *
211 */
212protected void
213key_clear(EditLine *el, el_action_t *map, const char *in)
214{
215
216	if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
217	    ((map == el->el_map.key &&
218	    el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
219	    (map == el->el_map.alt &&
220	    el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
221		(void) key_delete(el, in);
222}
223
224
225/* key_delete():
226 *      Delete the key and all longer keys staring with key, if
227 *      they exists.
228 */
229protected int
230key_delete(EditLine *el, const char *key)
231{
232
233	if (key[0] == '\0') {
234		(void) fprintf(el->el_errfile,
235		    "key_delete: Null extended-key not allowed.\n");
236		return (-1);
237	}
238	if (el->el_key.map == NULL)
239		return (0);
240
241	(void) node__delete(el, &el->el_key.map, key);
242	return (0);
243}
244
245
246/* key_print():
247 *	Print the binding associated with key key.
248 *	Print entire el->el_key.map if null
249 */
250protected void
251key_print(EditLine *el, const char *key)
252{
253
254	/* do nothing if el->el_key.map is empty and null key specified */
255	if (el->el_key.map == NULL && *key == 0)
256		return;
257
258	el->el_key.buf[0] = '"';
259	if (node_lookup(el, key, el->el_key.map, 1) <= -1)
260		/* key is not bound */
261		(void) fprintf(el->el_errfile, "Unbound extended key \"%s\"\n",
262		    key);
263	return;
264}
265
266
267/* node_trav():
268 *	recursively traverses node in tree until match or mismatch is
269 * 	found.  May read in more characters.
270 */
271private int
272node_trav(EditLine *el, key_node_t *ptr, char *ch, key_value_t *val)
273{
274
275	if (ptr->ch == *ch) {
276		/* match found */
277		if (ptr->next) {
278			/* key not complete so get next char */
279			if (el_getc(el, ch) != 1) {	/* if EOF or error */
280				val->cmd = ED_END_OF_FILE;
281				return (XK_CMD);
282				/* PWP: Pretend we just read an end-of-file */
283			}
284			return (node_trav(el, ptr->next, ch, val));
285		} else {
286			*val = ptr->val;
287			if (ptr->type != XK_CMD)
288				*ch = '\0';
289			return (ptr->type);
290		}
291	} else {
292		/* no match found here */
293		if (ptr->sibling) {
294			/* try next sibling */
295			return (node_trav(el, ptr->sibling, ch, val));
296		} else {
297			/* no next sibling -- mismatch */
298			val->str = NULL;
299			return (XK_STR);
300		}
301	}
302}
303
304
305/* node__try():
306 * 	Find a node that matches *str or allocate a new one
307 */
308private int
309node__try(EditLine *el, key_node_t *ptr, const char *str, key_value_t *val, int ntype)
310{
311
312	if (ptr->ch != *str) {
313		key_node_t *xm;
314
315		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
316			if (xm->sibling->ch == *str)
317				break;
318		if (xm->sibling == NULL)
319			xm->sibling = node__get(*str);	/* setup new node */
320		ptr = xm->sibling;
321	}
322	if (*++str == '\0') {
323		/* we're there */
324		if (ptr->next != NULL) {
325			node__put(el, ptr->next);
326				/* lose longer keys with this prefix */
327			ptr->next = NULL;
328		}
329		switch (ptr->type) {
330		case XK_CMD:
331		case XK_NOD:
332			break;
333		case XK_STR:
334		case XK_EXE:
335			if (ptr->val.str)
336				el_free((ptr_t) ptr->val.str);
337			break;
338		default:
339			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
340			    ptr->type));
341			break;
342		}
343
344		switch (ptr->type = ntype) {
345		case XK_CMD:
346			ptr->val = *val;
347			break;
348		case XK_STR:
349		case XK_EXE:
350			if ((ptr->val.str = el_strdup(val->str)) == NULL)
351				return -1;
352			break;
353		default:
354			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
355			break;
356		}
357	} else {
358		/* still more chars to go */
359		if (ptr->next == NULL)
360			ptr->next = node__get(*str);	/* setup new node */
361		(void) node__try(el, ptr->next, str, val, ntype);
362	}
363	return (0);
364}
365
366
367/* node__delete():
368 *	Delete node that matches str
369 */
370private int
371node__delete(EditLine *el, key_node_t **inptr, const char *str)
372{
373	key_node_t *ptr;
374	key_node_t *prev_ptr = NULL;
375
376	ptr = *inptr;
377
378	if (ptr->ch != *str) {
379		key_node_t *xm;
380
381		for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
382			if (xm->sibling->ch == *str)
383				break;
384		if (xm->sibling == NULL)
385			return (0);
386		prev_ptr = xm;
387		ptr = xm->sibling;
388	}
389	if (*++str == '\0') {
390		/* we're there */
391		if (prev_ptr == NULL)
392			*inptr = ptr->sibling;
393		else
394			prev_ptr->sibling = ptr->sibling;
395		ptr->sibling = NULL;
396		node__put(el, ptr);
397		return (1);
398	} else if (ptr->next != NULL &&
399	    node__delete(el, &ptr->next, str) == 1) {
400		if (ptr->next != NULL)
401			return (0);
402		if (prev_ptr == NULL)
403			*inptr = ptr->sibling;
404		else
405			prev_ptr->sibling = ptr->sibling;
406		ptr->sibling = NULL;
407		node__put(el, ptr);
408		return (1);
409	} else {
410		return (0);
411	}
412}
413
414
415/* node__put():
416 *	Puts a tree of nodes onto free list using free(3).
417 */
418private void
419node__put(EditLine *el, key_node_t *ptr)
420{
421	if (ptr == NULL)
422		return;
423
424	if (ptr->next != NULL) {
425		node__put(el, ptr->next);
426		ptr->next = NULL;
427	}
428	node__put(el, ptr->sibling);
429
430	switch (ptr->type) {
431	case XK_CMD:
432	case XK_NOD:
433		break;
434	case XK_EXE:
435	case XK_STR:
436		if (ptr->val.str != NULL)
437			el_free((ptr_t) ptr->val.str);
438		break;
439	default:
440		EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
441		break;
442	}
443	el_free((ptr_t) ptr);
444}
445
446
447/* node__get():
448 *	Returns pointer to an key_node_t for ch.
449 */
450private key_node_t *
451node__get(int ch)
452{
453	key_node_t *ptr;
454
455	ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
456	if (ptr == NULL)
457		return NULL;
458	ptr->ch = ch;
459	ptr->type = XK_NOD;
460	ptr->val.str = NULL;
461	ptr->next = NULL;
462	ptr->sibling = NULL;
463	return (ptr);
464}
465
466
467
468/* node_lookup():
469 *	look for the str starting at node ptr.
470 *	Print if last node
471 */
472private int
473node_lookup(EditLine *el, const char *str, key_node_t *ptr, int cnt)
474{
475	int ncnt;
476
477	if (ptr == NULL)
478		return (-1);	/* cannot have null ptr */
479
480	if (*str == 0) {
481		/* no more chars in str.  node_enum from here. */
482		(void) node_enum(el, ptr, cnt);
483		return (0);
484	} else {
485		/* If match put this char into el->el_key.buf.  Recurse */
486		if (ptr->ch == *str) {
487			/* match found */
488			ncnt = key__decode_char(el->el_key.buf, cnt,
489			    (unsigned char) ptr->ch);
490			if (ptr->next != NULL)
491				/* not yet at leaf */
492				return (node_lookup(el, str + 1, ptr->next,
493				    ncnt + 1));
494			else {
495			    /* next node is null so key should be complete */
496				if (str[1] == 0) {
497					el->el_key.buf[ncnt + 1] = '"';
498					el->el_key.buf[ncnt + 2] = '\0';
499					key_kprint(el, el->el_key.buf,
500					    &ptr->val, ptr->type);
501					return (0);
502				} else
503					return (-1);
504					/* mismatch -- str still has chars */
505			}
506		} else {
507			/* no match found try sibling */
508			if (ptr->sibling)
509				return (node_lookup(el, str, ptr->sibling,
510				    cnt));
511			else
512				return (-1);
513		}
514	}
515}
516
517
518/* node_enum():
519 *	Traverse the node printing the characters it is bound in buffer
520 */
521private int
522node_enum(EditLine *el, key_node_t *ptr, int cnt)
523{
524	int ncnt;
525
526	if (cnt >= KEY_BUFSIZ - 5) {	/* buffer too small */
527		el->el_key.buf[++cnt] = '"';
528		el->el_key.buf[++cnt] = '\0';
529		(void) fprintf(el->el_errfile,
530		    "Some extended keys too long for internal print buffer");
531		(void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf);
532		return (0);
533	}
534	if (ptr == NULL) {
535#ifdef DEBUG_EDIT
536		(void) fprintf(el->el_errfile,
537		    "node_enum: BUG!! Null ptr passed\n!");
538#endif
539		return (-1);
540	}
541	/* put this char at end of str */
542	ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch);
543	if (ptr->next == NULL) {
544		/* print this key and function */
545		el->el_key.buf[ncnt + 1] = '"';
546		el->el_key.buf[ncnt + 2] = '\0';
547		key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
548	} else
549		(void) node_enum(el, ptr->next, ncnt + 1);
550
551	/* go to sibling if there is one */
552	if (ptr->sibling)
553		(void) node_enum(el, ptr->sibling, cnt);
554	return (0);
555}
556
557
558/* key_kprint():
559 *	Print the specified key and its associated
560 *	function specified by val
561 */
562protected void
563key_kprint(EditLine *el, const char *key, key_value_t *val, int ntype)
564{
565	el_bindings_t *fp;
566	char unparsbuf[EL_BUFSIZ];
567	static const char fmt[] = "%-15s->  %s\n";
568
569	if (val != NULL)
570		switch (ntype) {
571		case XK_STR:
572		case XK_EXE:
573			(void) fprintf(el->el_outfile, fmt, key,
574			    key__decode_str(val->str, unparsbuf,
575				ntype == XK_STR ? "\"\"" : "[]"));
576			break;
577		case XK_CMD:
578			for (fp = el->el_map.help; fp->name; fp++)
579				if (val->cmd == fp->func) {
580					(void) fprintf(el->el_outfile, fmt,
581					    key, fp->name);
582					break;
583				}
584#ifdef DEBUG_KEY
585			if (fp->name == NULL)
586				(void) fprintf(el->el_outfile,
587				    "BUG! Command not found.\n");
588#endif
589
590			break;
591		default:
592			EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
593			break;
594		}
595	else
596		(void) fprintf(el->el_outfile, fmt, key, "no input");
597}
598
599
600/* key__decode_char():
601 *	Put a printable form of char in buf.
602 */
603private int
604key__decode_char(char *buf, int cnt, int ch)
605{
606	if (ch == 0) {
607		buf[cnt++] = '^';
608		buf[cnt] = '@';
609		return (cnt);
610	}
611	if (iscntrl(ch)) {
612		buf[cnt++] = '^';
613		if (ch == '\177')
614			buf[cnt] = '?';
615		else
616			buf[cnt] = ch | 0100;
617	} else if (ch == '^') {
618		buf[cnt++] = '\\';
619		buf[cnt] = '^';
620	} else if (ch == '\\') {
621		buf[cnt++] = '\\';
622		buf[cnt] = '\\';
623	} else if (ch == ' ' || (isprint(ch) && !isspace(ch))) {
624		buf[cnt] = ch;
625	} else {
626		buf[cnt++] = '\\';
627		buf[cnt++] = (((unsigned int) ch >> 6) & 7) + '0';
628		buf[cnt++] = (((unsigned int) ch >> 3) & 7) + '0';
629		buf[cnt] = (ch & 7) + '0';
630	}
631	return (cnt);
632}
633
634
635/* key__decode_str():
636 *	Make a printable version of the ey
637 */
638protected char *
639key__decode_str(const char *str, char *buf, const char *sep)
640{
641	char *b;
642	const char *p;
643
644	b = buf;
645	if (sep[0] != '\0')
646		*b++ = sep[0];
647	if (*str == 0) {
648		*b++ = '^';
649		*b++ = '@';
650		if (sep[0] != '\0' && sep[1] != '\0')
651			*b++ = sep[1];
652		*b++ = 0;
653		return (buf);
654	}
655	for (p = str; *p != 0; p++) {
656		if (iscntrl((unsigned char) *p)) {
657			*b++ = '^';
658			if (*p == '\177')
659				*b++ = '?';
660			else
661				*b++ = *p | 0100;
662		} else if (*p == '^' || *p == '\\') {
663			*b++ = '\\';
664			*b++ = *p;
665		} else if (*p == ' ' || (isprint((unsigned char) *p) &&
666			!isspace((unsigned char) *p))) {
667			*b++ = *p;
668		} else {
669			*b++ = '\\';
670			*b++ = (((unsigned int) *p >> 6) & 7) + '0';
671			*b++ = (((unsigned int) *p >> 3) & 7) + '0';
672			*b++ = (*p & 7) + '0';
673		}
674	}
675	if (sep[0] != '\0' && sep[1] != '\0')
676		*b++ = sep[1];
677	*b++ = 0;
678	return (buf);		/* should check for overflow */
679}
680