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