1 /* $NetBSD: key.c,v 1.22 2009/02/21 23:31:56 christos Exp $ */
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #if !defined(lint) && !defined(SCCSID)
38 static char sccsid
[] = "@(#)key.c 8.1 (Berkeley) 6/4/93";
40 __RCSID("$NetBSD: key.c,v 1.22 2009/02/21 23:31:56 christos Exp $");
42 #endif /* not lint && not SCCSID */
45 * key.c: This module contains the procedures for maintaining
46 * the extended-key map.
48 * An extended-key (key) is a sequence of keystrokes introduced
49 * with a sequence introducer and consisting of an arbitrary
50 * number of characters. This module maintains a map (the el->el_key.map)
51 * to convert these extended-key sequences into input strs
52 * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
55 * If key is a substr of some other keys, then the longer
56 * keys are lost!! That is, if the keys "abcd" and "abcef"
57 * are in el->el_key.map, adding the key "abc" will cause the first two
58 * definitions to be lost.
62 * 1) It is not possible to have one key that is a
71 * The Nodes of the el->el_key.map. The el->el_key.map is a linked list
72 * of these node elements
75 Char ch
; /* single character of key */
76 int type
; /* node type */
77 key_value_t val
; /* command code or pointer to str, */
78 /* if this is a leaf */
79 struct key_node_t
*next
; /* ptr to next char of this key */
80 struct key_node_t
*sibling
; /* ptr to another key with same prefix*/
83 private int node_trav(EditLine
*, key_node_t
*, Char
*,
85 private int node__try(EditLine
*, key_node_t
*, const Char
*,
87 private key_node_t
*node__get(Int
);
88 private void node__free(key_node_t
*);
89 private void node__put(EditLine
*, key_node_t
*);
90 private int node__delete(EditLine
*, key_node_t
**, const Char
*);
91 private int node_lookup(EditLine
*, const Char
*, key_node_t
*,
93 private int node_enum(EditLine
*, key_node_t
*, size_t);
95 #define KEY_BUFSIZ EL_BUFSIZ
99 * Initialize the key maps
102 key_init(EditLine
*el
)
105 el
->el_key
.buf
= el_malloc(KEY_BUFSIZ
* sizeof(*el
->el_key
.buf
));
106 if (el
->el_key
.buf
== NULL
)
108 el
->el_key
.map
= NULL
;
117 key_end(EditLine
*el
)
120 el_free((ptr_t
) el
->el_key
.buf
);
121 el
->el_key
.buf
= NULL
;
122 node__free(el
->el_key
.map
);
127 * Associate cmd with a key value
129 protected key_value_t
*
130 key_map_cmd(EditLine
*el
, int cmd
)
133 el
->el_key
.val
.cmd
= (el_action_t
) cmd
;
134 return (&el
->el_key
.val
);
139 * Associate str with a key value
141 protected key_value_t
*
142 key_map_str(EditLine
*el
, Char
*str
)
145 el
->el_key
.val
.str
= str
;
146 return (&el
->el_key
.val
);
151 * Takes all nodes on el->el_key.map and puts them on free list. Then
152 * initializes el->el_key.map with arrow keys
153 * [Always bind the ansi arrow keys?]
156 key_reset(EditLine
*el
)
159 node__put(el
, el
->el_key
.map
);
160 el
->el_key
.map
= NULL
;
166 * Calls the recursive function with entry point el->el_key.map
167 * Looks up *ch in map and then reads characters until a
168 * complete match is found or a mismatch occurs. Returns the
169 * type of the match found (XK_STR, XK_CMD, or XK_EXE).
170 * Returns NULL in val.str and XK_STR for no match.
171 * The last character read is returned in *ch.
174 key_get(EditLine
*el
, Char
*ch
, key_value_t
*val
)
177 return (node_trav(el
, el
->el_key
.map
, ch
, val
));
182 * Adds key to the el->el_key.map and associates the value in val with it.
183 * If key is already is in el->el_key.map, the new code is applied to the
184 * existing key. Ntype specifies if code is a command, an
185 * out str or a unix command.
188 key_add(EditLine
*el
, const Char
*key
, key_value_t
*val
, int ntype
)
191 if (key
[0] == '\0') {
192 (void) fprintf(el
->el_errfile
,
193 "key_add: Null extended-key not allowed.\n");
196 if (ntype
== XK_CMD
&& val
->cmd
== ED_SEQUENCE_LEAD_IN
) {
197 (void) fprintf(el
->el_errfile
,
198 "key_add: sequence-lead-in command not allowed\n");
201 if (el
->el_key
.map
== NULL
)
202 /* tree is initially empty. Set up new node to match key[0] */
203 el
->el_key
.map
= node__get(key
[0]);
204 /* it is properly initialized */
206 /* Now recurse through el->el_key.map */
207 (void) node__try(el
, el
->el_key
.map
, key
, val
, ntype
);
216 key_clear(EditLine
*el
, el_action_t
*map
, const Char
*in
)
219 if (*in
> N_KEYS
) /* can't be in the map */
222 if ((map
[(unsigned char)*in
] == ED_SEQUENCE_LEAD_IN
) &&
223 ((map
== el
->el_map
.key
&&
224 el
->el_map
.alt
[(unsigned char)*in
] != ED_SEQUENCE_LEAD_IN
) ||
225 (map
== el
->el_map
.alt
&&
226 el
->el_map
.key
[(unsigned char)*in
] != ED_SEQUENCE_LEAD_IN
)))
227 (void) key_delete(el
, in
);
232 * Delete the key and all longer keys staring with key, if
236 key_delete(EditLine
*el
, const Char
*key
)
239 if (key
[0] == '\0') {
240 (void) fprintf(el
->el_errfile
,
241 "key_delete: Null extended-key not allowed.\n");
244 if (el
->el_key
.map
== NULL
)
247 (void) node__delete(el
, &el
->el_key
.map
, key
);
253 * Print the binding associated with key key.
254 * Print entire el->el_key.map if null
257 key_print(EditLine
*el
, const Char
*key
)
260 /* do nothing if el->el_key.map is empty and null key specified */
261 if (el
->el_key
.map
== NULL
&& *key
== 0)
264 el
->el_key
.buf
[0] = '"';
265 if (node_lookup(el
, key
, el
->el_key
.map
, 1) <= -1)
266 /* key is not bound */
267 (void) fprintf(el
->el_errfile
, "Unbound extended key \"" FSTR
"\"\n",
274 * recursively traverses node in tree until match or mismatch is
275 * found. May read in more characters.
278 node_trav(EditLine
*el
, key_node_t
*ptr
, Char
*ch
, key_value_t
*val
)
281 if (ptr
->ch
== *ch
) {
284 /* key not complete so get next char */
285 if (FUN(el
,getc
)(el
, ch
) != 1) {/* if EOF or error */
286 val
->cmd
= ED_END_OF_FILE
;
288 /* PWP: Pretend we just read an end-of-file */
290 return (node_trav(el
, ptr
->next
, ch
, val
));
293 if (ptr
->type
!= XK_CMD
)
298 /* no match found here */
300 /* try next sibling */
301 return (node_trav(el
, ptr
->sibling
, ch
, val
));
303 /* no next sibling -- mismatch */
312 * Find a node that matches *str or allocate a new one
315 node__try(EditLine
*el
, key_node_t
*ptr
, const Char
*str
, key_value_t
*val
, int ntype
)
318 if (ptr
->ch
!= *str
) {
321 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
322 if (xm
->sibling
->ch
== *str
)
324 if (xm
->sibling
== NULL
)
325 xm
->sibling
= node__get(*str
); /* setup new node */
328 if (*++str
== '\0') {
330 if (ptr
->next
!= NULL
) {
331 node__put(el
, ptr
->next
);
332 /* lose longer keys with this prefix */
342 el_free((ptr_t
) ptr
->val
.str
);
345 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n",
350 switch (ptr
->type
= ntype
) {
356 if ((ptr
->val
.str
= Strdup(val
->str
)) == NULL
)
360 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ntype
));
364 /* still more chars to go */
365 if (ptr
->next
== NULL
)
366 ptr
->next
= node__get(*str
); /* setup new node */
367 (void) node__try(el
, ptr
->next
, str
, val
, ntype
);
374 * Delete node that matches str
377 node__delete(EditLine
*el
, key_node_t
**inptr
, const Char
*str
)
380 key_node_t
*prev_ptr
= NULL
;
384 if (ptr
->ch
!= *str
) {
387 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
388 if (xm
->sibling
->ch
== *str
)
390 if (xm
->sibling
== NULL
)
395 if (*++str
== '\0') {
397 if (prev_ptr
== NULL
)
398 *inptr
= ptr
->sibling
;
400 prev_ptr
->sibling
= ptr
->sibling
;
404 } else if (ptr
->next
!= NULL
&&
405 node__delete(el
, &ptr
->next
, str
) == 1) {
406 if (ptr
->next
!= NULL
)
408 if (prev_ptr
== NULL
)
409 *inptr
= ptr
->sibling
;
411 prev_ptr
->sibling
= ptr
->sibling
;
422 * Puts a tree of nodes onto free list using free(3).
425 node__put(EditLine
*el
, key_node_t
*ptr
)
430 if (ptr
->next
!= NULL
) {
431 node__put(el
, ptr
->next
);
434 node__put(el
, ptr
->sibling
);
442 if (ptr
->val
.str
!= NULL
)
443 el_free((ptr_t
) ptr
->val
.str
);
446 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ptr
->type
));
449 el_free((ptr_t
) ptr
);
454 * Returns pointer to a key_node_t for ch.
461 ptr
= (key_node_t
*) el_malloc((size_t) sizeof(key_node_t
));
473 node__free(key_node_t
*k
)
477 node__free(k
->sibling
);
483 * look for the str starting at node ptr.
487 node_lookup(EditLine
*el
, const Char
*str
, key_node_t
*ptr
, size_t cnt
)
492 return (-1); /* cannot have null ptr */
494 if (!str
|| *str
== 0) {
495 /* no more chars in str. node_enum from here. */
496 (void) node_enum(el
, ptr
, cnt
);
499 /* If match put this char into el->el_key.buf. Recurse */
500 if (ptr
->ch
== *str
) {
502 used
= ct_visual_char(el
->el_key
.buf
+ cnt
,
503 KEY_BUFSIZ
- cnt
, ptr
->ch
);
505 return (-1); /* ran out of buffer space */
506 if (ptr
->next
!= NULL
)
507 /* not yet at leaf */
508 return (node_lookup(el
, str
+ 1, ptr
->next
,
511 /* next node is null so key should be complete */
513 el
->el_key
.buf
[cnt
+ used
] = '"';
514 el
->el_key
.buf
[cnt
+ used
+ 1] = '\0';
515 key_kprint(el
, el
->el_key
.buf
,
516 &ptr
->val
, ptr
->type
);
520 /* mismatch -- str still has chars */
523 /* no match found try sibling */
525 return (node_lookup(el
, str
, ptr
->sibling
,
535 * Traverse the node printing the characters it is bound in buffer
538 node_enum(EditLine
*el
, key_node_t
*ptr
, size_t cnt
)
542 if (cnt
>= KEY_BUFSIZ
- 5) { /* buffer too small */
543 el
->el_key
.buf
[++cnt
] = '"';
544 el
->el_key
.buf
[++cnt
] = '\0';
545 (void) fprintf(el
->el_errfile
,
546 "Some extended keys too long for internal print buffer");
547 (void) fprintf(el
->el_errfile
, " \"" FSTR
"...\"\n", el
->el_key
.buf
);
552 (void) fprintf(el
->el_errfile
,
553 "node_enum: BUG!! Null ptr passed\n!");
557 /* put this char at end of str */
558 used
= ct_visual_char(el
->el_key
.buf
+ cnt
, KEY_BUFSIZ
- cnt
, ptr
->ch
);
559 if (ptr
->next
== NULL
) {
560 /* print this key and function */
561 el
->el_key
.buf
[cnt
+ used
] = '"';
562 el
->el_key
.buf
[cnt
+ used
+ 1] = '\0';
563 key_kprint(el
, el
->el_key
.buf
, &ptr
->val
, ptr
->type
);
565 (void) node_enum(el
, ptr
->next
, cnt
+ used
);
567 /* go to sibling if there is one */
569 (void) node_enum(el
, ptr
->sibling
, cnt
);
575 * Print the specified key and its associated
576 * function specified by val
579 key_kprint(EditLine
*el
, const Char
*key
, key_value_t
*val
, int ntype
)
582 char unparsbuf
[EL_BUFSIZ
];
583 static const char fmt
[] = "%-15s-> %s\n";
589 (void) key__decode_str(val
->str
, unparsbuf
,
591 ntype
== XK_STR
? "\"\"" : "[]");
592 (void) fprintf(el
->el_outfile
, fmt
,
593 ct_encode_string(key
, &el
->el_scratch
), unparsbuf
);
596 for (fp
= el
->el_map
.help
; fp
->name
; fp
++)
597 if (val
->cmd
== fp
->func
) {
598 ct_wcstombs(unparsbuf
, fp
->name
, sizeof(unparsbuf
));
599 unparsbuf
[sizeof(unparsbuf
) -1] = '\0';
600 (void) fprintf(el
->el_outfile
, fmt
,
601 ct_encode_string(key
, &el
->el_scratch
), unparsbuf
);
605 if (fp
->name
== NULL
)
606 (void) fprintf(el
->el_outfile
,
607 "BUG! Command not found.\n");
612 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ntype
));
616 (void) fprintf(el
->el_outfile
, fmt
, ct_encode_string(key
,
617 &el
->el_scratch
), "no input");
626 /* key__decode_str():
627 * Make a printable version of the ey
630 key__decode_str(const Char
*str
, char *buf
, size_t len
, const char *sep
)
632 char *b
= buf
, *eb
= b
+ len
;
636 if (sep
[0] != '\0') {
644 for (p
= str
; *p
!= 0; p
++) {
645 Char dbuf
[VISUAL_WIDTH_MAX
];
647 ssize_t l
= ct_visual_char(dbuf
, VISUAL_WIDTH_MAX
, *p
);
649 ssize_t n
= ct_encode_char(b
, (size_t)(eb
- b
), *p2
++);
650 if (n
== -1) /* ran out of space */
657 if (sep
[0] != '\0' && sep
[1] != '\0') {
661 if ((size_t)(b
- buf
) >= len
)
663 return (size_t)(b
- buf
);