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