1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. 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 8.1 (Berkeley) 06/04/93";
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
key_init(el)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
key_end(el)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 *
key_map_cmd(el,cmd)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 *
key_map_str(el,str)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
key_reset(el)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
key_get(el,ch,val)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
key_add(el,key,val,ntype)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
key_clear(el,map,in)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
key_delete(el,key)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
key_print(el,key)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
node_trav(el,ptr,ch,val)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
node__try(ptr,str,val,ntype)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) {
326 case XK_CMD:
327 case XK_NOD:
328 break;
329 case XK_STR:
330 case XK_EXE:
331 if (ptr->val.str)
332 el_free((ptr_t) ptr->val.str);
333 break;
334 default:
335 abort();
336 break;
337 }
338
339 switch (ptr->type = ntype) {
340 case XK_CMD:
341 ptr->val = *val;
342 break;
343 case XK_STR:
344 case XK_EXE:
345 ptr->val.str = strdup(val->str);
346 break;
347 default:
348 abort();
349 break;
350 }
351 }
352 else {
353 /* still more chars to go */
354 if (ptr->next == NULL)
355 ptr->next = node__get(*str); /* setup new node */
356 (void) node__try(ptr->next, str, val, ntype);
357 }
358 return 0;
359 }
360
361
362 /* node__delete():
363 * Delete node that matches str
364 */
365 private int
node__delete(inptr,str)366 node__delete(inptr, str)
367 key_node_t **inptr;
368 char *str;
369 {
370 key_node_t *ptr;
371 key_node_t *prev_ptr = NULL;
372
373 ptr = *inptr;
374
375 if (ptr->ch != *str) {
376 key_node_t *xm;
377
378 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
379 if (xm->sibling->ch == *str)
380 break;
381 if (xm->sibling == NULL)
382 return 0;
383 prev_ptr = xm;
384 ptr = xm->sibling;
385 }
386
387 if (*++str == '\0') {
388 /* we're there */
389 if (prev_ptr == NULL)
390 *inptr = ptr->sibling;
391 else
392 prev_ptr->sibling = ptr->sibling;
393 ptr->sibling = NULL;
394 node__put(ptr);
395 return 1;
396 }
397 else if (ptr->next != NULL && node__delete(&ptr->next, str) == 1) {
398 if (ptr->next != NULL)
399 return 0;
400 if (prev_ptr == NULL)
401 *inptr = ptr->sibling;
402 else
403 prev_ptr->sibling = ptr->sibling;
404 ptr->sibling = NULL;
405 node__put(ptr);
406 return 1;
407 }
408 else {
409 return 0;
410 }
411 }
412
413 /* node__put():
414 * Puts a tree of nodes onto free list using free(3).
415 */
416 private void
node__put(ptr)417 node__put(ptr)
418 key_node_t *ptr;
419 {
420 if (ptr == NULL)
421 return;
422
423 if (ptr->next != NULL) {
424 node__put(ptr->next);
425 ptr->next = NULL;
426 }
427
428 node__put(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 abort();
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 */
450 private key_node_t *
node__get(ch)451 node__get(ch)
452 int ch;
453 {
454 key_node_t *ptr;
455
456 ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
457 ptr->ch = ch;
458 ptr->type = XK_NOD;
459 ptr->val.str = NULL;
460 ptr->next = NULL;
461 ptr->sibling = NULL;
462 return ptr;
463 }
464
465
466
467 /* node_lookup():
468 * look for the str starting at node ptr.
469 * Print if last node
470 */
471 private int
node_lookup(el,str,ptr,cnt)472 node_lookup(el, str, ptr, cnt)
473 EditLine *el;
474 char *str;
475 key_node_t *ptr;
476 int cnt;
477 {
478 int ncnt;
479
480 if (ptr == NULL)
481 return -1; /* cannot have null ptr */
482
483 if (*str == 0) {
484 /* no more chars in str. node_enum from here. */
485 (void) node_enum(el, ptr, cnt);
486 return 0;
487 }
488 else {
489 /* If match put this char into el->el_key.buf. Recurse */
490 if (ptr->ch == *str) {
491 /* match found */
492 ncnt = key__decode_char(el->el_key.buf, cnt,
493 (unsigned char) ptr->ch);
494 if (ptr->next != NULL)
495 /* not yet at leaf */
496 return node_lookup(el, str + 1, ptr->next, ncnt + 1);
497 else {
498 /* next node is null so key should be complete */
499 if (str[1] == 0) {
500 el->el_key.buf[ncnt + 1] = '"';
501 el->el_key.buf[ncnt + 2] = '\0';
502 key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
503 return 0;
504 }
505 else
506 return -1;/* mismatch -- str still has chars */
507 }
508 }
509 else {
510 /* no match found try sibling */
511 if (ptr->sibling)
512 return node_lookup(el, str, ptr->sibling, cnt);
513 else
514 return -1;
515 }
516 }
517 }
518
519
520 /* node_enum():
521 * Traverse the node printing the characters it is bound in buffer
522 */
523 private int
node_enum(el,ptr,cnt)524 node_enum(el, ptr, cnt)
525 EditLine *el;
526 key_node_t *ptr;
527 int cnt;
528 {
529 int ncnt;
530
531 if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */
532 el->el_key.buf[++cnt] = '"';
533 el->el_key.buf[++cnt] = '\0';
534 (void) fprintf(el->el_errfile,
535 "Some extended keys too long for internal print buffer");
536 (void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf);
537 return 0;
538 }
539
540 if (ptr == NULL) {
541 #ifdef DEBUG_EDIT
542 (void) fprintf(el->el_errfile, "node_enum: BUG!! Null ptr passed\n!");
543 #endif
544 return -1;
545 }
546
547 /* put this char at end of str */
548 ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch);
549 if (ptr->next == NULL) {
550 /* print this key and function */
551 el->el_key.buf[ncnt + 1] = '"';
552 el->el_key.buf[ncnt + 2] = '\0';
553 key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
554 }
555 else
556 (void) node_enum(el, ptr->next, ncnt + 1);
557
558 /* go to sibling if there is one */
559 if (ptr->sibling)
560 (void) node_enum(el, ptr->sibling, cnt);
561 return 0;
562 }
563
564
565 /* key_kprint():
566 * Print the specified key and its associated
567 * function specified by val
568 */
569 protected void
key_kprint(el,key,val,ntype)570 key_kprint(el, key, val, ntype)
571 EditLine *el;
572 char *key;
573 key_value_t *val;
574 int ntype;
575 {
576 el_bindings_t *fp;
577 char unparsbuf[EL_BUFSIZ];
578 static char *fmt = "%-15s-> %s\n";
579
580 if (val != NULL)
581 switch (ntype) {
582 case XK_STR:
583 case XK_EXE:
584 (void) fprintf(el->el_errfile, fmt, key,
585 key__decode_str(val->str, unparsbuf,
586 ntype == XK_STR ? "\"\"" : "[]"));
587 break;
588 case XK_CMD:
589 for (fp = el->el_map.help; fp->name; fp++)
590 if (val->cmd == fp->func) {
591 (void) fprintf(el->el_errfile, fmt, key, fp->name);
592 break;
593 }
594 #ifdef DEBUG_KEY
595 if (fp->name == NULL)
596 (void) fprintf(el->el_errfile, "BUG! Command not found.\n");
597 #endif
598
599 break;
600 default:
601 abort();
602 break;
603 }
604 else
605 (void) fprintf(el->el_errfile, fmt, key, "no input");
606 }
607
608
609 /* key__decode_char():
610 * Put a printable form of char in buf.
611 */
612 private int
key__decode_char(buf,cnt,ch)613 key__decode_char(buf, cnt, ch)
614 char *buf;
615 int cnt, ch;
616 {
617 if (ch == 0) {
618 buf[cnt++] = '^';
619 buf[cnt] = '@';
620 return cnt;
621 }
622
623 if (iscntrl(ch)) {
624 buf[cnt++] = '^';
625 if (ch == '\177')
626 buf[cnt] = '?';
627 else
628 buf[cnt] = ch | 0100;
629 }
630 else if (ch == '^') {
631 buf[cnt++] = '\\';
632 buf[cnt] = '^';
633 }
634 else if (ch == '\\') {
635 buf[cnt++] = '\\';
636 buf[cnt] = '\\';
637 }
638 else if (ch == ' ' || (isprint(ch) && !isspace(ch))) {
639 buf[cnt] = ch;
640 }
641 else {
642 buf[cnt++] = '\\';
643 buf[cnt++] = ((ch >> 6) & 7) + '0';
644 buf[cnt++] = ((ch >> 3) & 7) + '0';
645 buf[cnt] = (ch & 7) + '0';
646 }
647 return cnt;
648 }
649
650 /* key__decode_str():
651 * Make a printable version of the ey
652 */
653 protected char *
key__decode_str(str,buf,sep)654 key__decode_str(str, buf, sep)
655 char *str;
656 char *buf;
657 char *sep;
658 {
659 char *b, *p;
660
661 b = buf;
662 if (sep[0] != '\0')
663 *b++ = sep[0];
664 if (*str == 0) {
665 *b++ = '^';
666 *b++ = '@';
667 if (sep[0] != '\0' && sep[1] != '\0')
668 *b++ = sep[1];
669 *b++ = 0;
670 return buf;
671 }
672
673 for (p = str; *p != 0; p++) {
674 if (iscntrl((unsigned char) *p)) {
675 *b++ = '^';
676 if (*p == '\177')
677 *b++ = '?';
678 else
679 *b++ = *p | 0100;
680 }
681 else if (*p == '^' || *p == '\\') {
682 *b++ = '\\';
683 *b++ = *p;
684 }
685 else if (*p == ' ' || (isprint((unsigned char) *p) &&
686 !isspace((unsigned char) *p))) {
687 *b++ = *p;
688 }
689 else {
690 *b++ = '\\';
691 *b++ = ((*p >> 6) & 7) + '0';
692 *b++ = ((*p >> 3) & 7) + '0';
693 *b++ = (*p & 7) + '0';
694 }
695 }
696 if (sep[0] != '\0' && sep[1] != '\0')
697 *b++ = sep[1];
698 *b++ = 0;
699 return buf; /* should check for overflow */
700 }
701