keymacro.c revision 1.19 1 1.19 christos /* $NetBSD: keymacro.c,v 1.19 2016/04/11 18:56:31 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.19 christos __RCSID("$NetBSD: keymacro.c,v 1.19 2016/04/11 18:56:31 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.18 christos wchar_t 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.19 christos static int node_trav(EditLine *, keymacro_node_t *, wchar_t *,
85 1.1 christos keymacro_value_t *);
86 1.19 christos static int node__try(EditLine *, keymacro_node_t *,
87 1.18 christos const wchar_t *, keymacro_value_t *, int);
88 1.19 christos static keymacro_node_t *node__get(wint_t);
89 1.19 christos static void node__free(keymacro_node_t *);
90 1.19 christos static void node__put(EditLine *, keymacro_node_t *);
91 1.19 christos static int node__delete(EditLine *, keymacro_node_t **,
92 1.18 christos const wchar_t *);
93 1.19 christos static int node_lookup(EditLine *, const wchar_t *,
94 1.2 christos keymacro_node_t *, size_t);
95 1.19 christos static 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.18 christos keymacro_map_str(EditLine *el, wchar_t *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.18 christos keymacro_get(EditLine *el, wchar_t *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.18 christos keymacro_add(EditLine *el, const wchar_t *key, keymacro_value_t *val,
192 1.18 christos int ntype)
193 1.1 christos {
194 1.1 christos
195 1.1 christos if (key[0] == '\0') {
196 1.1 christos (void) fprintf(el->el_errfile,
197 1.1 christos "keymacro_add: Null extended-key not allowed.\n");
198 1.1 christos return;
199 1.1 christos }
200 1.1 christos if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
201 1.1 christos (void) fprintf(el->el_errfile,
202 1.1 christos "keymacro_add: sequence-lead-in command not allowed\n");
203 1.1 christos return;
204 1.1 christos }
205 1.1 christos if (el->el_keymacro.map == NULL)
206 1.1 christos /* tree is initially empty. Set up new node to match key[0] */
207 1.1 christos el->el_keymacro.map = node__get(key[0]);
208 1.1 christos /* it is properly initialized */
209 1.1 christos
210 1.1 christos /* Now recurse through el->el_keymacro.map */
211 1.1 christos (void) node__try(el, el->el_keymacro.map, key, val, ntype);
212 1.1 christos return;
213 1.1 christos }
214 1.1 christos
215 1.1 christos
216 1.1 christos /* keymacro_clear():
217 1.1 christos *
218 1.1 christos */
219 1.1 christos protected void
220 1.18 christos keymacro_clear(EditLine *el, el_action_t *map, const wchar_t *in)
221 1.1 christos {
222 1.1 christos if (*in > N_KEYS) /* can't be in the map */
223 1.1 christos return;
224 1.1 christos if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
225 1.1 christos ((map == el->el_map.key &&
226 1.1 christos el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
227 1.1 christos (map == el->el_map.alt &&
228 1.1 christos el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
229 1.1 christos (void) keymacro_delete(el, in);
230 1.1 christos }
231 1.1 christos
232 1.1 christos
233 1.1 christos /* keymacro_delete():
234 1.1 christos * Delete the key and all longer keys staring with key, if
235 1.1 christos * they exists.
236 1.1 christos */
237 1.1 christos protected int
238 1.18 christos keymacro_delete(EditLine *el, const wchar_t *key)
239 1.1 christos {
240 1.1 christos
241 1.1 christos if (key[0] == '\0') {
242 1.1 christos (void) fprintf(el->el_errfile,
243 1.1 christos "keymacro_delete: Null extended-key not allowed.\n");
244 1.5 christos return -1;
245 1.1 christos }
246 1.1 christos if (el->el_keymacro.map == NULL)
247 1.5 christos return 0;
248 1.1 christos
249 1.1 christos (void) node__delete(el, &el->el_keymacro.map, key);
250 1.5 christos return 0;
251 1.1 christos }
252 1.1 christos
253 1.1 christos
254 1.1 christos /* keymacro_print():
255 1.1 christos * Print the binding associated with key key.
256 1.1 christos * Print entire el->el_keymacro.map if null
257 1.1 christos */
258 1.1 christos protected void
259 1.18 christos keymacro_print(EditLine *el, const wchar_t *key)
260 1.1 christos {
261 1.1 christos
262 1.1 christos /* do nothing if el->el_keymacro.map is empty and null key specified */
263 1.1 christos if (el->el_keymacro.map == NULL && *key == 0)
264 1.1 christos return;
265 1.1 christos
266 1.1 christos el->el_keymacro.buf[0] = '"';
267 1.6 christos if (node_lookup(el, key, el->el_keymacro.map, (size_t)1) <= -1)
268 1.1 christos /* key is not bound */
269 1.16 christos (void) fprintf(el->el_errfile, "Unbound extended key \"%ls"
270 1.2 christos "\"\n", key);
271 1.1 christos return;
272 1.1 christos }
273 1.1 christos
274 1.1 christos
275 1.1 christos /* node_trav():
276 1.1 christos * recursively traverses node in tree until match or mismatch is
277 1.13 christos * found. May read in more characters.
278 1.1 christos */
279 1.19 christos static int
280 1.18 christos node_trav(EditLine *el, keymacro_node_t *ptr, wchar_t *ch,
281 1.18 christos keymacro_value_t *val)
282 1.1 christos {
283 1.1 christos
284 1.1 christos if (ptr->ch == *ch) {
285 1.1 christos /* match found */
286 1.1 christos if (ptr->next) {
287 1.1 christos /* key not complete so get next char */
288 1.18 christos if (el_wgetc(el, ch) != 1) {/* if EOF or error */
289 1.1 christos val->cmd = ED_END_OF_FILE;
290 1.5 christos return XK_CMD;
291 1.1 christos /* PWP: Pretend we just read an end-of-file */
292 1.1 christos }
293 1.5 christos return node_trav(el, ptr->next, ch, val);
294 1.1 christos } else {
295 1.1 christos *val = ptr->val;
296 1.1 christos if (ptr->type != XK_CMD)
297 1.1 christos *ch = '\0';
298 1.5 christos return ptr->type;
299 1.1 christos }
300 1.1 christos } else {
301 1.1 christos /* no match found here */
302 1.1 christos if (ptr->sibling) {
303 1.1 christos /* try next sibling */
304 1.5 christos return node_trav(el, ptr->sibling, ch, val);
305 1.1 christos } else {
306 1.1 christos /* no next sibling -- mismatch */
307 1.1 christos val->str = NULL;
308 1.5 christos return XK_STR;
309 1.1 christos }
310 1.1 christos }
311 1.1 christos }
312 1.1 christos
313 1.1 christos
314 1.1 christos /* node__try():
315 1.13 christos * Find a node that matches *str or allocate a new one
316 1.1 christos */
317 1.19 christos static int
318 1.18 christos node__try(EditLine *el, keymacro_node_t *ptr, const wchar_t *str,
319 1.2 christos keymacro_value_t *val, int ntype)
320 1.1 christos {
321 1.1 christos
322 1.1 christos if (ptr->ch != *str) {
323 1.1 christos keymacro_node_t *xm;
324 1.1 christos
325 1.1 christos for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
326 1.1 christos if (xm->sibling->ch == *str)
327 1.1 christos break;
328 1.1 christos if (xm->sibling == NULL)
329 1.1 christos xm->sibling = node__get(*str); /* setup new node */
330 1.1 christos ptr = xm->sibling;
331 1.1 christos }
332 1.1 christos if (*++str == '\0') {
333 1.1 christos /* we're there */
334 1.1 christos if (ptr->next != NULL) {
335 1.1 christos node__put(el, ptr->next);
336 1.1 christos /* lose longer keys with this prefix */
337 1.1 christos ptr->next = NULL;
338 1.1 christos }
339 1.1 christos switch (ptr->type) {
340 1.1 christos case XK_CMD:
341 1.1 christos case XK_NOD:
342 1.1 christos break;
343 1.1 christos case XK_STR:
344 1.1 christos case XK_EXE:
345 1.1 christos if (ptr->val.str)
346 1.4 christos el_free(ptr->val.str);
347 1.1 christos break;
348 1.1 christos default:
349 1.1 christos EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
350 1.1 christos ptr->type));
351 1.1 christos break;
352 1.1 christos }
353 1.1 christos
354 1.1 christos switch (ptr->type = ntype) {
355 1.1 christos case XK_CMD:
356 1.1 christos ptr->val = *val;
357 1.1 christos break;
358 1.1 christos case XK_STR:
359 1.1 christos case XK_EXE:
360 1.17 christos if ((ptr->val.str = wcsdup(val->str)) == NULL)
361 1.1 christos return -1;
362 1.1 christos break;
363 1.1 christos default:
364 1.1 christos EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
365 1.1 christos break;
366 1.1 christos }
367 1.1 christos } else {
368 1.1 christos /* still more chars to go */
369 1.1 christos if (ptr->next == NULL)
370 1.1 christos ptr->next = node__get(*str); /* setup new node */
371 1.1 christos (void) node__try(el, ptr->next, str, val, ntype);
372 1.1 christos }
373 1.5 christos return 0;
374 1.1 christos }
375 1.1 christos
376 1.1 christos
377 1.1 christos /* node__delete():
378 1.1 christos * Delete node that matches str
379 1.1 christos */
380 1.19 christos static int
381 1.18 christos node__delete(EditLine *el, keymacro_node_t **inptr, const wchar_t *str)
382 1.1 christos {
383 1.1 christos keymacro_node_t *ptr;
384 1.1 christos keymacro_node_t *prev_ptr = NULL;
385 1.1 christos
386 1.1 christos ptr = *inptr;
387 1.1 christos
388 1.1 christos if (ptr->ch != *str) {
389 1.1 christos keymacro_node_t *xm;
390 1.1 christos
391 1.1 christos for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
392 1.1 christos if (xm->sibling->ch == *str)
393 1.1 christos break;
394 1.1 christos if (xm->sibling == NULL)
395 1.5 christos return 0;
396 1.1 christos prev_ptr = xm;
397 1.1 christos ptr = xm->sibling;
398 1.1 christos }
399 1.1 christos if (*++str == '\0') {
400 1.1 christos /* we're there */
401 1.1 christos if (prev_ptr == NULL)
402 1.1 christos *inptr = ptr->sibling;
403 1.1 christos else
404 1.1 christos prev_ptr->sibling = ptr->sibling;
405 1.1 christos ptr->sibling = NULL;
406 1.1 christos node__put(el, ptr);
407 1.5 christos return 1;
408 1.1 christos } else if (ptr->next != NULL &&
409 1.1 christos node__delete(el, &ptr->next, str) == 1) {
410 1.1 christos if (ptr->next != NULL)
411 1.5 christos return 0;
412 1.1 christos if (prev_ptr == NULL)
413 1.1 christos *inptr = ptr->sibling;
414 1.1 christos else
415 1.1 christos prev_ptr->sibling = ptr->sibling;
416 1.1 christos ptr->sibling = NULL;
417 1.1 christos node__put(el, ptr);
418 1.5 christos return 1;
419 1.1 christos } else {
420 1.5 christos return 0;
421 1.1 christos }
422 1.1 christos }
423 1.1 christos
424 1.1 christos
425 1.1 christos /* node__put():
426 1.1 christos * Puts a tree of nodes onto free list using free(3).
427 1.1 christos */
428 1.19 christos static void
429 1.1 christos node__put(EditLine *el, keymacro_node_t *ptr)
430 1.1 christos {
431 1.1 christos if (ptr == NULL)
432 1.1 christos return;
433 1.1 christos
434 1.1 christos if (ptr->next != NULL) {
435 1.1 christos node__put(el, ptr->next);
436 1.1 christos ptr->next = NULL;
437 1.1 christos }
438 1.1 christos node__put(el, ptr->sibling);
439 1.1 christos
440 1.1 christos switch (ptr->type) {
441 1.1 christos case XK_CMD:
442 1.1 christos case XK_NOD:
443 1.1 christos break;
444 1.1 christos case XK_EXE:
445 1.1 christos case XK_STR:
446 1.1 christos if (ptr->val.str != NULL)
447 1.4 christos el_free(ptr->val.str);
448 1.1 christos break;
449 1.1 christos default:
450 1.1 christos EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
451 1.1 christos break;
452 1.1 christos }
453 1.4 christos el_free(ptr);
454 1.1 christos }
455 1.1 christos
456 1.1 christos
457 1.1 christos /* node__get():
458 1.1 christos * Returns pointer to a keymacro_node_t for ch.
459 1.1 christos */
460 1.19 christos static keymacro_node_t *
461 1.9 christos node__get(wint_t ch)
462 1.1 christos {
463 1.1 christos keymacro_node_t *ptr;
464 1.1 christos
465 1.4 christos ptr = el_malloc(sizeof(*ptr));
466 1.1 christos if (ptr == NULL)
467 1.1 christos return NULL;
468 1.18 christos ptr->ch = ch;
469 1.1 christos ptr->type = XK_NOD;
470 1.1 christos ptr->val.str = NULL;
471 1.1 christos ptr->next = NULL;
472 1.1 christos ptr->sibling = NULL;
473 1.5 christos return ptr;
474 1.1 christos }
475 1.1 christos
476 1.19 christos static void
477 1.1 christos node__free(keymacro_node_t *k)
478 1.1 christos {
479 1.1 christos if (k == NULL)
480 1.1 christos return;
481 1.1 christos node__free(k->sibling);
482 1.1 christos node__free(k->next);
483 1.4 christos el_free(k);
484 1.1 christos }
485 1.1 christos
486 1.1 christos /* node_lookup():
487 1.1 christos * look for the str starting at node ptr.
488 1.1 christos * Print if last node
489 1.1 christos */
490 1.19 christos static int
491 1.18 christos node_lookup(EditLine *el, const wchar_t *str, keymacro_node_t *ptr,
492 1.18 christos size_t cnt)
493 1.1 christos {
494 1.1 christos ssize_t used;
495 1.1 christos
496 1.1 christos if (ptr == NULL)
497 1.5 christos return -1; /* cannot have null ptr */
498 1.1 christos
499 1.1 christos if (!str || *str == 0) {
500 1.1 christos /* no more chars in str. node_enum from here. */
501 1.1 christos (void) node_enum(el, ptr, cnt);
502 1.5 christos return 0;
503 1.1 christos } else {
504 1.1 christos /* If match put this char into el->el_keymacro.buf. Recurse */
505 1.1 christos if (ptr->ch == *str) {
506 1.1 christos /* match found */
507 1.1 christos used = ct_visual_char(el->el_keymacro.buf + cnt,
508 1.1 christos KEY_BUFSIZ - cnt, ptr->ch);
509 1.1 christos if (used == -1)
510 1.5 christos return -1; /* ran out of buffer space */
511 1.1 christos if (ptr->next != NULL)
512 1.1 christos /* not yet at leaf */
513 1.1 christos return (node_lookup(el, str + 1, ptr->next,
514 1.7 christos (size_t)used + cnt));
515 1.1 christos else {
516 1.1 christos /* next node is null so key should be complete */
517 1.1 christos if (str[1] == 0) {
518 1.7 christos size_t px = cnt + (size_t)used;
519 1.2 christos el->el_keymacro.buf[px] = '"';
520 1.2 christos el->el_keymacro.buf[px + 1] = '\0';
521 1.1 christos keymacro_kprint(el, el->el_keymacro.buf,
522 1.1 christos &ptr->val, ptr->type);
523 1.5 christos return 0;
524 1.1 christos } else
525 1.5 christos return -1;
526 1.1 christos /* mismatch -- str still has chars */
527 1.1 christos }
528 1.1 christos } else {
529 1.1 christos /* no match found try sibling */
530 1.1 christos if (ptr->sibling)
531 1.1 christos return (node_lookup(el, str, ptr->sibling,
532 1.1 christos cnt));
533 1.1 christos else
534 1.5 christos return -1;
535 1.1 christos }
536 1.1 christos }
537 1.1 christos }
538 1.1 christos
539 1.1 christos
540 1.1 christos /* node_enum():
541 1.1 christos * Traverse the node printing the characters it is bound in buffer
542 1.1 christos */
543 1.19 christos static int
544 1.1 christos node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt)
545 1.1 christos {
546 1.1 christos ssize_t used;
547 1.1 christos
548 1.1 christos if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */
549 1.1 christos el->el_keymacro.buf[++cnt] = '"';
550 1.1 christos el->el_keymacro.buf[++cnt] = '\0';
551 1.1 christos (void) fprintf(el->el_errfile,
552 1.1 christos "Some extended keys too long for internal print buffer");
553 1.16 christos (void) fprintf(el->el_errfile, " \"%ls...\"\n",
554 1.2 christos el->el_keymacro.buf);
555 1.5 christos return 0;
556 1.1 christos }
557 1.1 christos if (ptr == NULL) {
558 1.1 christos #ifdef DEBUG_EDIT
559 1.1 christos (void) fprintf(el->el_errfile,
560 1.1 christos "node_enum: BUG!! Null ptr passed\n!");
561 1.1 christos #endif
562 1.5 christos return -1;
563 1.1 christos }
564 1.1 christos /* put this char at end of str */
565 1.2 christos used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt,
566 1.2 christos ptr->ch);
567 1.1 christos if (ptr->next == NULL) {
568 1.1 christos /* print this key and function */
569 1.7 christos el->el_keymacro.buf[cnt + (size_t)used ] = '"';
570 1.7 christos el->el_keymacro.buf[cnt + (size_t)used + 1] = '\0';
571 1.1 christos keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type);
572 1.1 christos } else
573 1.7 christos (void) node_enum(el, ptr->next, cnt + (size_t)used);
574 1.1 christos
575 1.1 christos /* go to sibling if there is one */
576 1.1 christos if (ptr->sibling)
577 1.1 christos (void) node_enum(el, ptr->sibling, cnt);
578 1.5 christos return 0;
579 1.1 christos }
580 1.1 christos
581 1.1 christos
582 1.1 christos /* keymacro_kprint():
583 1.1 christos * Print the specified key and its associated
584 1.1 christos * function specified by val
585 1.1 christos */
586 1.1 christos protected void
587 1.18 christos keymacro_kprint(EditLine *el, const wchar_t *key, keymacro_value_t *val,
588 1.18 christos 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.16 christos 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.18 christos keymacro__decode_str(const wchar_t *str, char *buf, size_t len,
640 1.18 christos const char *sep)
641 1.1 christos {
642 1.1 christos char *b = buf, *eb = b + len;
643 1.18 christos const wchar_t *p;
644 1.1 christos
645 1.1 christos b = buf;
646 1.1 christos if (sep[0] != '\0') {
647 1.1 christos ADDC(sep[0]);
648 1.1 christos }
649 1.1 christos if (*str == '\0') {
650 1.1 christos ADDC('^');
651 1.1 christos ADDC('@');
652 1.1 christos goto add_endsep;
653 1.1 christos }
654 1.1 christos for (p = str; *p != 0; p++) {
655 1.18 christos wchar_t dbuf[VISUAL_WIDTH_MAX];
656 1.18 christos wchar_t *p2 = dbuf;
657 1.1 christos ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p);
658 1.1 christos while (l-- > 0) {
659 1.1 christos ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++);
660 1.1 christos if (n == -1) /* ran out of space */
661 1.1 christos goto add_endsep;
662 1.1 christos else
663 1.1 christos b += n;
664 1.1 christos }
665 1.1 christos }
666 1.1 christos add_endsep:
667 1.1 christos if (sep[0] != '\0' && sep[1] != '\0') {
668 1.1 christos ADDC(sep[1]);
669 1.1 christos }
670 1.1 christos ADDC('\0');
671 1.1 christos if ((size_t)(b - buf) >= len)
672 1.1 christos buf[len - 1] = '\0';
673 1.1 christos return (size_t)(b - buf);
674 1.1 christos }
675