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