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