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