1 /* $OpenBSD: keymacro.c,v 1.16 2016/05/25 09:23:49 schwarze Exp $ */
2 /* $NetBSD: keymacro.c,v 1.23 2016/05/24 15:00:45 christos Exp $ */
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Christos Zoulas of Cornell University.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
39 * keymacro.c: This module contains the procedures for maintaining
40 * the extended-key map.
42 * An extended-key (key) is a sequence of keystrokes introduced
43 * with a sequence introducer and consisting of an arbitrary
44 * number of characters. This module maintains a map (the
45 * el->el_keymacro.map)
46 * to convert these extended-key sequences into input strs
47 * (XK_STR) or editor functions (XK_CMD).
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_keymacro.map, adding the key "abc" will cause
53 * the first two definitions to be lost.
57 * 1) It is not possible to have one key that is a
67 * The Nodes of the el->el_keymacro.map. The el->el_keymacro.map is a
68 * linked list of these node elements
70 struct keymacro_node_t
{
71 wchar_t ch
; /* single character of key */
72 int type
; /* node type */
73 keymacro_value_t val
; /* command code or pointer to str, */
74 /* if this is a leaf */
75 struct keymacro_node_t
*next
; /* ptr to next char of this key */
76 struct keymacro_node_t
*sibling
;/* ptr to another key with same prefix*/
79 static int node_trav(EditLine
*, keymacro_node_t
*, wchar_t *,
81 static int node__try(EditLine
*, keymacro_node_t
*,
82 const wchar_t *, keymacro_value_t
*, int);
83 static keymacro_node_t
*node__get(wint_t);
84 static void node__free(keymacro_node_t
*);
85 static void node__put(EditLine
*, keymacro_node_t
*);
86 static int node__delete(EditLine
*, keymacro_node_t
**,
88 static int node_lookup(EditLine
*, const wchar_t *,
89 keymacro_node_t
*, size_t);
90 static int node_enum(EditLine
*, keymacro_node_t
*, size_t);
92 #define KEY_BUFSIZ EL_BUFSIZ
96 * Initialize the key maps
99 keymacro_init(EditLine
*el
)
102 el
->el_keymacro
.buf
= reallocarray(NULL
, KEY_BUFSIZ
,
103 sizeof(*el
->el_keymacro
.buf
));
104 if (el
->el_keymacro
.buf
== NULL
)
106 el
->el_keymacro
.map
= NULL
;
115 keymacro_end(EditLine
*el
)
118 free(el
->el_keymacro
.buf
);
119 el
->el_keymacro
.buf
= NULL
;
120 node__free(el
->el_keymacro
.map
);
124 /* keymacro_map_cmd():
125 * Associate cmd with a key value
127 protected keymacro_value_t
*
128 keymacro_map_cmd(EditLine
*el
, int cmd
)
131 el
->el_keymacro
.val
.cmd
= (el_action_t
) cmd
;
132 return &el
->el_keymacro
.val
;
136 /* keymacro_map_str():
137 * Associate str with a key value
139 protected keymacro_value_t
*
140 keymacro_map_str(EditLine
*el
, wchar_t *str
)
143 el
->el_keymacro
.val
.str
= str
;
144 return &el
->el_keymacro
.val
;
149 * Takes all nodes on el->el_keymacro.map and puts them on free list.
150 * Then initializes el->el_keymacro.map with arrow keys
151 * [Always bind the ansi arrow keys?]
154 keymacro_reset(EditLine
*el
)
157 node__put(el
, el
->el_keymacro
.map
);
158 el
->el_keymacro
.map
= NULL
;
164 * Calls the recursive function with entry point el->el_keymacro.map
165 * Looks up *ch in map and then reads characters until a
166 * complete match is found or a mismatch occurs. Returns the
167 * type of the match found (XK_STR or XK_CMD).
168 * Returns NULL in val.str and XK_STR for no match.
169 * Returns XK_NOD for end of file or read error.
170 * The last character read is returned in *ch.
173 keymacro_get(EditLine
*el
, wchar_t *ch
, keymacro_value_t
*val
)
176 return node_trav(el
, el
->el_keymacro
.map
, ch
, val
);
181 * Adds key to the el->el_keymacro.map and associates the value in
182 * val with it. If key is already is in el->el_keymacro.map, the new
183 * code is applied to the existing key. Ntype specifies if code is a
184 * command, an out str or a unix command.
187 keymacro_add(EditLine
*el
, const wchar_t *key
, keymacro_value_t
*val
,
191 if (key
[0] == '\0') {
192 (void) fprintf(el
->el_errfile
,
193 "keymacro_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 "keymacro_add: sequence-lead-in command not allowed\n");
201 if (el
->el_keymacro
.map
== NULL
)
202 /* tree is initially empty. Set up new node to match key[0] */
203 el
->el_keymacro
.map
= node__get(key
[0]);
204 /* it is properly initialized */
206 /* Now recurse through el->el_keymacro.map */
207 (void) node__try(el
, el
->el_keymacro
.map
, key
, val
, ntype
);
216 keymacro_clear(EditLine
*el
, el_action_t
*map
, const wchar_t *in
)
218 if (*in
> N_KEYS
) /* can't be in the map */
220 if ((map
[(unsigned char)*in
] == ED_SEQUENCE_LEAD_IN
) &&
221 ((map
== el
->el_map
.key
&&
222 el
->el_map
.alt
[(unsigned char)*in
] != ED_SEQUENCE_LEAD_IN
) ||
223 (map
== el
->el_map
.alt
&&
224 el
->el_map
.key
[(unsigned char)*in
] != ED_SEQUENCE_LEAD_IN
)))
225 (void) keymacro_delete(el
, in
);
229 /* keymacro_delete():
230 * Delete the key and all longer keys staring with key, if
234 keymacro_delete(EditLine
*el
, const wchar_t *key
)
237 if (key
[0] == '\0') {
238 (void) fprintf(el
->el_errfile
,
239 "keymacro_delete: Null extended-key not allowed.\n");
242 if (el
->el_keymacro
.map
== NULL
)
245 (void) node__delete(el
, &el
->el_keymacro
.map
, key
);
251 * Print the binding associated with key key.
252 * Print entire el->el_keymacro.map if null
255 keymacro_print(EditLine
*el
, const wchar_t *key
)
258 /* do nothing if el->el_keymacro.map is empty and null key specified */
259 if (el
->el_keymacro
.map
== NULL
&& *key
== 0)
262 el
->el_keymacro
.buf
[0] = '"';
263 if (node_lookup(el
, key
, el
->el_keymacro
.map
, 1) <= -1)
264 /* key is not bound */
265 (void) fprintf(el
->el_errfile
, "Unbound extended key \"%ls"
272 * recursively traverses node in tree until match or mismatch is
273 * found. May read in more characters.
276 node_trav(EditLine
*el
, keymacro_node_t
*ptr
, wchar_t *ch
,
277 keymacro_value_t
*val
)
280 if (ptr
->ch
== *ch
) {
283 /* key not complete so get next char */
284 if (el_wgetc(el
, ch
) != 1)
286 return node_trav(el
, ptr
->next
, ch
, val
);
289 if (ptr
->type
!= XK_CMD
)
294 /* no match found here */
296 /* try next sibling */
297 return node_trav(el
, ptr
->sibling
, ch
, val
);
299 /* no next sibling -- mismatch */
308 * Find a node that matches *str or allocate a new one
311 node__try(EditLine
*el
, keymacro_node_t
*ptr
, const wchar_t *str
,
312 keymacro_value_t
*val
, int ntype
)
315 if (ptr
->ch
!= *str
) {
318 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
319 if (xm
->sibling
->ch
== *str
)
321 if (xm
->sibling
== NULL
)
322 xm
->sibling
= node__get(*str
); /* setup new node */
325 if (*++str
== '\0') {
327 if (ptr
->next
!= NULL
) {
328 node__put(el
, ptr
->next
);
329 /* lose longer keys with this prefix */
341 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n",
346 switch (ptr
->type
= ntype
) {
351 if ((ptr
->val
.str
= wcsdup(val
->str
)) == NULL
)
355 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ntype
));
359 /* still more chars to go */
360 if (ptr
->next
== NULL
)
361 ptr
->next
= node__get(*str
); /* setup new node */
362 (void) node__try(el
, ptr
->next
, str
, val
, ntype
);
369 * Delete node that matches str
372 node__delete(EditLine
*el
, keymacro_node_t
**inptr
, const wchar_t *str
)
374 keymacro_node_t
*ptr
;
375 keymacro_node_t
*prev_ptr
= NULL
;
379 if (ptr
->ch
!= *str
) {
382 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
383 if (xm
->sibling
->ch
== *str
)
385 if (xm
->sibling
== NULL
)
390 if (*++str
== '\0') {
392 if (prev_ptr
== NULL
)
393 *inptr
= ptr
->sibling
;
395 prev_ptr
->sibling
= ptr
->sibling
;
399 } else if (ptr
->next
!= NULL
&&
400 node__delete(el
, &ptr
->next
, str
) == 1) {
401 if (ptr
->next
!= NULL
)
403 if (prev_ptr
== NULL
)
404 *inptr
= ptr
->sibling
;
406 prev_ptr
->sibling
= ptr
->sibling
;
417 * Puts a tree of nodes onto free list using free(3).
420 node__put(EditLine
*el
, keymacro_node_t
*ptr
)
425 if (ptr
->next
!= NULL
) {
426 node__put(el
, ptr
->next
);
429 node__put(el
, ptr
->sibling
);
436 if (ptr
->val
.str
!= NULL
)
440 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ptr
->type
));
448 * Returns pointer to a keymacro_node_t for ch.
450 static keymacro_node_t
*
453 keymacro_node_t
*ptr
;
455 ptr
= malloc(sizeof(*ptr
));
467 node__free(keymacro_node_t
*k
)
471 node__free(k
->sibling
);
477 * look for the str starting at node ptr.
481 node_lookup(EditLine
*el
, const wchar_t *str
, keymacro_node_t
*ptr
,
487 return -1; /* cannot have null ptr */
489 if (!str
|| *str
== 0) {
490 /* no more chars in str. node_enum from here. */
491 (void) node_enum(el
, ptr
, cnt
);
494 /* If match put this char into el->el_keymacro.buf. Recurse */
495 if (ptr
->ch
== *str
) {
497 used
= ct_visual_char(el
->el_keymacro
.buf
+ cnt
,
498 KEY_BUFSIZ
- cnt
, ptr
->ch
);
500 return -1; /* ran out of buffer space */
501 if (ptr
->next
!= NULL
)
502 /* not yet at leaf */
503 return (node_lookup(el
, str
+ 1, ptr
->next
,
506 /* next node is null so key should be complete */
508 el
->el_keymacro
.buf
[cnt
+used
] = '"';
509 el
->el_keymacro
.buf
[cnt
+used
+1] = '\0';
510 keymacro_kprint(el
, el
->el_keymacro
.buf
,
511 &ptr
->val
, ptr
->type
);
515 /* mismatch -- str still has chars */
518 /* no match found try sibling */
520 return (node_lookup(el
, str
, ptr
->sibling
,
530 * Traverse the node printing the characters it is bound in buffer
533 node_enum(EditLine
*el
, keymacro_node_t
*ptr
, size_t cnt
)
537 if (cnt
>= KEY_BUFSIZ
- 5) { /* buffer too small */
538 el
->el_keymacro
.buf
[++cnt
] = '"';
539 el
->el_keymacro
.buf
[++cnt
] = '\0';
540 (void) fprintf(el
->el_errfile
,
541 "Some extended keys too long for internal print buffer");
542 (void) fprintf(el
->el_errfile
, " \"%ls...\"\n",
543 el
->el_keymacro
.buf
);
548 (void) fprintf(el
->el_errfile
,
549 "node_enum: BUG!! Null ptr passed\n!");
553 /* put this char at end of str */
554 used
= ct_visual_char(el
->el_keymacro
.buf
+ cnt
, KEY_BUFSIZ
- cnt
,
556 if (ptr
->next
== NULL
) {
557 /* print this key and function */
558 el
->el_keymacro
.buf
[cnt
+ used
] = '"';
559 el
->el_keymacro
.buf
[cnt
+ used
+ 1] = '\0';
560 keymacro_kprint(el
, el
->el_keymacro
.buf
, &ptr
->val
, ptr
->type
);
562 (void) node_enum(el
, ptr
->next
, cnt
+ used
);
564 /* go to sibling if there is one */
566 (void) node_enum(el
, ptr
->sibling
, cnt
);
571 /* keymacro_kprint():
572 * Print the specified key and its associated
573 * function specified by val
576 keymacro_kprint(EditLine
*el
, const wchar_t *key
, keymacro_value_t
*val
,
580 char unparsbuf
[EL_BUFSIZ
];
581 static const char fmt
[] = "%-15s-> %s\n";
586 (void) keymacro__decode_str(val
->str
, unparsbuf
,
588 ntype
== XK_STR
? "\"\"" : "[]");
589 (void) fprintf(el
->el_outfile
, fmt
,
590 ct_encode_string(key
, &el
->el_scratch
), unparsbuf
);
593 for (fp
= el
->el_map
.help
; fp
->name
; fp
++)
594 if (val
->cmd
== fp
->func
) {
595 wcstombs(unparsbuf
, fp
->name
, sizeof(unparsbuf
));
596 unparsbuf
[sizeof(unparsbuf
) -1] = '\0';
597 (void) fprintf(el
->el_outfile
, fmt
,
598 ct_encode_string(key
, &el
->el_scratch
), unparsbuf
);
602 if (fp
->name
== NULL
)
603 (void) fprintf(el
->el_outfile
,
604 "BUG! Command not found.\n");
609 EL_ABORT((el
->el_errfile
, "Bad XK_ type %d\n", ntype
));
613 (void) fprintf(el
->el_outfile
, fmt
, ct_encode_string(key
,
614 &el
->el_scratch
), "no input");
623 /* keymacro__decode_str():
624 * Make a printable version of the ey
627 keymacro__decode_str(const wchar_t *str
, char *buf
, size_t len
,
630 char *b
= buf
, *eb
= b
+ len
;
634 if (sep
[0] != '\0') {
642 for (p
= str
; *p
!= 0; p
++) {
643 wchar_t dbuf
[VISUAL_WIDTH_MAX
];
645 ssize_t l
= ct_visual_char(dbuf
, VISUAL_WIDTH_MAX
, *p
);
647 ssize_t n
= ct_encode_char(b
, (size_t)(eb
- b
), *p2
++);
648 if (n
== -1) /* ran out of space */
655 if (sep
[0] != '\0' && sep
[1] != '\0') {
659 if ((size_t)(b
- buf
) >= len
)
661 return (size_t)(b
- buf
);