1 /* $NetBSD: keymacro.c,v 1.7 2011/08/16 16:25:15 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: keymacro.c,v 1.7 2011/08/16 16:25:15 christos Exp $");
42 #endif /* not lint && not SCCSID */
45 * keymacro.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
51 * el->el_keymacro.map)
52 * to convert these extended-key sequences into input strs
53 * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
56 * If key is a substr of some other keys, then the longer
57 * keys are lost!! That is, if the keys "abcd" and "abcef"
58 * are in el->el_keymacro.map, adding the key "abc" will cause
59 * the first two definitions to be lost.
63 * 1) It is not possible to have one key that is a
72 * The Nodes of the el->el_keymacro.map. The el->el_keymacro.map is a
73 * linked list of these node elements
75 struct keymacro_node_t
{
76 Char ch
; /* single character of key */
77 int type
; /* node type */
78 keymacro_value_t val
; /* command code or pointer to str, */
79 /* if this is a leaf */
80 struct keymacro_node_t
*next
; /* ptr to next char of this key */
81 struct keymacro_node_t
*sibling
;/* ptr to another key with same prefix*/
84 private int node_trav(EditLine
*, keymacro_node_t
*, Char
*,
86 private int node__try(EditLine
*, keymacro_node_t
*, const Char
*,
87 keymacro_value_t
*, int);
88 private keymacro_node_t
*node__get(Int
);
89 private void node__free(keymacro_node_t
*);
90 private void node__put(EditLine
*, keymacro_node_t
*);
91 private int node__delete(EditLine
*, keymacro_node_t
**,
93 private int node_lookup(EditLine
*, const Char
*,
94 keymacro_node_t
*, size_t);
95 private int node_enum(EditLine
*, keymacro_node_t
*, size_t);
97 #define KEY_BUFSIZ EL_BUFSIZ
101 * Initialize the key maps
104 keymacro_init(EditLine
*el
)
107 el
->el_keymacro
.buf
= el_malloc(KEY_BUFSIZ
*
108 sizeof(*el
->el_keymacro
.buf
));
109 if (el
->el_keymacro
.buf
== NULL
)
111 el
->el_keymacro
.map
= NULL
;
120 keymacro_end(EditLine
*el
)
123 el_free(el
->el_keymacro
.buf
);
124 el
->el_keymacro
.buf
= NULL
;
125 node__free(el
->el_keymacro
.map
);
129 /* keymacro_map_cmd():
130 * Associate cmd with a key value
132 protected keymacro_value_t
*
133 keymacro_map_cmd(EditLine
*el
, int cmd
)
136 el
->el_keymacro
.val
.cmd
= (el_action_t
) cmd
;
137 return &el
->el_keymacro
.val
;
141 /* keymacro_map_str():
142 * Associate str with a key value
144 protected keymacro_value_t
*
145 keymacro_map_str(EditLine
*el
, Char
*str
)
148 el
->el_keymacro
.val
.str
= str
;
149 return &el
->el_keymacro
.val
;
154 * Takes all nodes on el->el_keymacro.map and puts them on free list.
155 * Then initializes el->el_keymacro.map with arrow keys
156 * [Always bind the ansi arrow keys?]
159 keymacro_reset(EditLine
*el
)
162 node__put(el
, el
->el_keymacro
.map
);
163 el
->el_keymacro
.map
= NULL
;
169 * Calls the recursive function with entry point el->el_keymacro.map
170 * Looks up *ch in map and then reads characters until a
171 * complete match is found or a mismatch occurs. Returns the
172 * type of the match found (XK_STR, XK_CMD, or XK_EXE).
173 * Returns NULL in val.str and XK_STR for no match.
174 * The last character read is returned in *ch.
177 keymacro_get(EditLine
*el
, Char
*ch
, keymacro_value_t
*val
)
180 return node_trav(el
, el
->el_keymacro
.map
, ch
, val
);
185 * Adds key to the el->el_keymacro.map and associates the value in
186 * val with it. If key is already is in el->el_keymacro.map, the new
187 * code is applied to the existing key. Ntype specifies if code is a
188 * command, an out str or a unix command.
191 keymacro_add(EditLine
*el
, const Char
*key
, keymacro_value_t
*val
, int ntype
)
194 if (key
[0] == '\0') {
195 (void) fprintf(el
->el_errfile
,
196 "keymacro_add: Null extended-key not allowed.\n");
199 if (ntype
== XK_CMD
&& val
->cmd
== ED_SEQUENCE_LEAD_IN
) {
200 (void) fprintf(el
->el_errfile
,
201 "keymacro_add: sequence-lead-in command not allowed\n");
204 if (el
->el_keymacro
.map
== NULL
)
205 /* tree is initially empty. Set up new node to match key[0] */
206 el
->el_keymacro
.map
= node__get(key
[0]);
207 /* it is properly initialized */
209 /* Now recurse through el->el_keymacro.map */
210 (void) node__try(el
, el
->el_keymacro
.map
, key
, val
, ntype
);
219 keymacro_clear(EditLine
*el
, el_action_t
*map
, const Char
*in
)
222 if (*in
> N_KEYS
) /* can't be in the map */
225 if ((map
[(unsigned char)*in
] == ED_SEQUENCE_LEAD_IN
) &&
226 ((map
== el
->el_map
.key
&&
227 el
->el_map
.alt
[(unsigned char)*in
] != ED_SEQUENCE_LEAD_IN
) ||
228 (map
== el
->el_map
.alt
&&
229 el
->el_map
.key
[(unsigned char)*in
] != ED_SEQUENCE_LEAD_IN
)))
230 (void) keymacro_delete(el
, in
);
234 /* keymacro_delete():
235 * Delete the key and all longer keys staring with key, if
239 keymacro_delete(EditLine
*el
, const Char
*key
)
242 if (key
[0] == '\0') {
243 (void) fprintf(el
->el_errfile
,
244 "keymacro_delete: Null extended-key not allowed.\n");
247 if (el
->el_keymacro
.map
== NULL
)
250 (void) node__delete(el
, &el
->el_keymacro
.map
, key
);
256 * Print the binding associated with key key.
257 * Print entire el->el_keymacro.map if null
260 keymacro_print(EditLine
*el
, const Char
*key
)
263 /* do nothing if el->el_keymacro.map is empty and null key specified */
264 if (el
->el_keymacro
.map
== NULL
&& *key
== 0)
267 el
->el_keymacro
.buf
[0] = '"';
268 if (node_lookup(el
, key
, el
->el_keymacro
.map
, (size_t)1) <= -1)
269 /* key is not bound */
270 (void) fprintf(el
->el_errfile
, "Unbound extended key \"" FSTR
277 * recursively traverses node in tree until match or mismatch is
278 * found. May read in more characters.
281 node_trav(EditLine
*el
, keymacro_node_t
*ptr
, Char
*ch
, keymacro_value_t
*val
)
284 if (ptr
->ch
== *ch
) {
287 /* key not complete so get next char */
288 if (FUN(el
,getc
)(el
, ch
) != 1) {/* if EOF or error */
289 val
->cmd
= ED_END_OF_FILE
;
291 /* PWP: Pretend we just read an end-of-file */
293 return node_trav(el
, ptr
->next
, ch
, val
);
296 if (ptr
->type
!= XK_CMD
)
301 /* no match found here */
303 /* try next sibling */
304 return node_trav(el
, ptr
->sibling
, ch
, val
);
306 /* no next sibling -- mismatch */
315 * Find a node that matches *str or allocate a new one
318 node__try(EditLine
*el
, keymacro_node_t
*ptr
, const Char
*str
,
319 keymacro_value_t
*val
, int ntype
)
322 if (ptr
->ch
!= *str
) {
325 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
326 if (xm
->sibling
->ch
== *str
)
328 if (xm
->sibling
== NULL
)
329 xm
->sibling
= node__get(*str
); /* setup new node */
332 if (*++str
== '\0') {
334 if (ptr
->next
!= NULL
) {
335 node__put(el
, ptr
->next
);
336 /* lose longer keys with this prefix */
346 el_free(ptr
->val
.str
);
349 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n",
354 switch (ptr
->type
= ntype
) {
360 if ((ptr
->val
.str
= Strdup(val
->str
)) == NULL
)
364 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ntype
));
368 /* still more chars to go */
369 if (ptr
->next
== NULL
)
370 ptr
->next
= node__get(*str
); /* setup new node */
371 (void) node__try(el
, ptr
->next
, str
, val
, ntype
);
378 * Delete node that matches str
381 node__delete(EditLine
*el
, keymacro_node_t
**inptr
, const Char
*str
)
383 keymacro_node_t
*ptr
;
384 keymacro_node_t
*prev_ptr
= NULL
;
388 if (ptr
->ch
!= *str
) {
391 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
392 if (xm
->sibling
->ch
== *str
)
394 if (xm
->sibling
== NULL
)
399 if (*++str
== '\0') {
401 if (prev_ptr
== NULL
)
402 *inptr
= ptr
->sibling
;
404 prev_ptr
->sibling
= ptr
->sibling
;
408 } else if (ptr
->next
!= NULL
&&
409 node__delete(el
, &ptr
->next
, str
) == 1) {
410 if (ptr
->next
!= NULL
)
412 if (prev_ptr
== NULL
)
413 *inptr
= ptr
->sibling
;
415 prev_ptr
->sibling
= ptr
->sibling
;
426 * Puts a tree of nodes onto free list using free(3).
429 node__put(EditLine
*el
, keymacro_node_t
*ptr
)
434 if (ptr
->next
!= NULL
) {
435 node__put(el
, ptr
->next
);
438 node__put(el
, ptr
->sibling
);
446 if (ptr
->val
.str
!= NULL
)
447 el_free(ptr
->val
.str
);
450 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ptr
->type
));
458 * Returns pointer to a keymacro_node_t for ch.
460 private keymacro_node_t
*
463 keymacro_node_t
*ptr
;
465 ptr
= el_malloc(sizeof(*ptr
));
477 node__free(keymacro_node_t
*k
)
481 node__free(k
->sibling
);
487 * look for the str starting at node ptr.
491 node_lookup(EditLine
*el
, const Char
*str
, keymacro_node_t
*ptr
, size_t cnt
)
496 return -1; /* cannot have null ptr */
498 if (!str
|| *str
== 0) {
499 /* no more chars in str. node_enum from here. */
500 (void) node_enum(el
, ptr
, cnt
);
503 /* If match put this char into el->el_keymacro.buf. Recurse */
504 if (ptr
->ch
== *str
) {
506 used
= ct_visual_char(el
->el_keymacro
.buf
+ cnt
,
507 KEY_BUFSIZ
- cnt
, ptr
->ch
);
509 return -1; /* ran out of buffer space */
510 if (ptr
->next
!= NULL
)
511 /* not yet at leaf */
512 return (node_lookup(el
, str
+ 1, ptr
->next
,
513 (size_t)used
+ cnt
));
515 /* next node is null so key should be complete */
517 size_t px
= cnt
+ (size_t)used
;
518 el
->el_keymacro
.buf
[px
] = '"';
519 el
->el_keymacro
.buf
[px
+ 1] = '\0';
520 keymacro_kprint(el
, el
->el_keymacro
.buf
,
521 &ptr
->val
, ptr
->type
);
525 /* mismatch -- str still has chars */
528 /* no match found try sibling */
530 return (node_lookup(el
, str
, ptr
->sibling
,
540 * Traverse the node printing the characters it is bound in buffer
543 node_enum(EditLine
*el
, keymacro_node_t
*ptr
, size_t cnt
)
547 if (cnt
>= KEY_BUFSIZ
- 5) { /* buffer too small */
548 el
->el_keymacro
.buf
[++cnt
] = '"';
549 el
->el_keymacro
.buf
[++cnt
] = '\0';
550 (void) fprintf(el
->el_errfile
,
551 "Some extended keys too long for internal print buffer");
552 (void) fprintf(el
->el_errfile
, " \"" FSTR
"...\"\n",
553 el
->el_keymacro
.buf
);
558 (void) fprintf(el
->el_errfile
,
559 "node_enum: BUG!! Null ptr passed\n!");
563 /* put this char at end of str */
564 used
= ct_visual_char(el
->el_keymacro
.buf
+ cnt
, KEY_BUFSIZ
- cnt
,
566 if (ptr
->next
== NULL
) {
567 /* print this key and function */
568 el
->el_keymacro
.buf
[cnt
+ (size_t)used
] = '"';
569 el
->el_keymacro
.buf
[cnt
+ (size_t)used
+ 1] = '\0';
570 keymacro_kprint(el
, el
->el_keymacro
.buf
, &ptr
->val
, ptr
->type
);
572 (void) node_enum(el
, ptr
->next
, cnt
+ (size_t)used
);
574 /* go to sibling if there is one */
576 (void) node_enum(el
, ptr
->sibling
, cnt
);
581 /* keymacro_kprint():
582 * Print the specified key and its associated
583 * function specified by val
586 keymacro_kprint(EditLine
*el
, const Char
*key
, keymacro_value_t
*val
, int ntype
)
589 char unparsbuf
[EL_BUFSIZ
];
590 static const char fmt
[] = "%-15s-> %s\n";
596 (void) keymacro__decode_str(val
->str
, unparsbuf
,
598 ntype
== XK_STR
? "\"\"" : "[]");
599 (void) fprintf(el
->el_outfile
, fmt
,
600 ct_encode_string(key
, &el
->el_scratch
), unparsbuf
);
603 for (fp
= el
->el_map
.help
; fp
->name
; fp
++)
604 if (val
->cmd
== fp
->func
) {
605 ct_wcstombs(unparsbuf
, fp
->name
, sizeof(unparsbuf
));
606 unparsbuf
[sizeof(unparsbuf
) -1] = '\0';
607 (void) fprintf(el
->el_outfile
, fmt
,
608 ct_encode_string(key
, &el
->el_scratch
), unparsbuf
);
612 if (fp
->name
== NULL
)
613 (void) fprintf(el
->el_outfile
,
614 "BUG! Command not found.\n");
619 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ntype
));
623 (void) fprintf(el
->el_outfile
, fmt
, ct_encode_string(key
,
624 &el
->el_scratch
), "no input");
633 /* keymacro__decode_str():
634 * Make a printable version of the ey
637 keymacro__decode_str(const Char
*str
, char *buf
, size_t len
, const char *sep
)
639 char *b
= buf
, *eb
= b
+ len
;
643 if (sep
[0] != '\0') {
651 for (p
= str
; *p
!= 0; p
++) {
652 Char dbuf
[VISUAL_WIDTH_MAX
];
654 ssize_t l
= ct_visual_char(dbuf
, VISUAL_WIDTH_MAX
, *p
);
656 ssize_t n
= ct_encode_char(b
, (size_t)(eb
- b
), *p2
++);
657 if (n
== -1) /* ran out of space */
664 if (sep
[0] != '\0' && sep
[1] != '\0') {
668 if ((size_t)(b
- buf
) >= len
)
670 return (size_t)(b
- buf
);