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