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