keymacro.c revision 302408
1/* $NetBSD: keymacro.c,v 1.7 2011/08/16 16:25:15 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 38static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93"; 39#else 40__RCSID("$NetBSD: keymacro.c,v 1.7 2011/08/16 16:25:15 christos Exp $"); 41#endif 42#endif /* not lint && not SCCSID */ 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: stable/11/lib/libedit/keymacro.c 296435 2016-03-06 21:32:54Z pfg $"); 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 <string.h> 69#include <stdlib.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 */ 77struct 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 86private int node_trav(EditLine *, keymacro_node_t *, Char *, 87 keymacro_value_t *); 88private int node__try(EditLine *, keymacro_node_t *, const Char *, 89 keymacro_value_t *, int); 90private keymacro_node_t *node__get(Int); 91private void node__free(keymacro_node_t *); 92private void node__put(EditLine *, keymacro_node_t *); 93private int node__delete(EditLine *, keymacro_node_t **, 94 const Char *); 95private int node_lookup(EditLine *, const Char *, 96 keymacro_node_t *, size_t); 97private 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 */ 105protected int 106keymacro_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 */ 121protected void 122keymacro_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 */ 134protected keymacro_value_t * 135keymacro_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 */ 146protected keymacro_value_t * 147keymacro_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 */ 160protected void 161keymacro_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 */ 178protected int 179keymacro_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 */ 192protected void 193keymacro_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 */ 220protected void 221keymacro_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 */ 240protected int 241keymacro_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 */ 261protected void 262keymacro_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 */ 282private int 283node_trav(EditLine *el, keymacro_node_t *ptr, Char *ch, 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 (FUN(el,getc)(el, ch) != 1) {/* if EOF or error */ 291 val->cmd = ED_END_OF_FILE; 292 return XK_CMD; 293 /* PWP: Pretend we just read an end-of-file */ 294 } 295 return node_trav(el, ptr->next, ch, val); 296 } else { 297 *val = ptr->val; 298 if (ptr->type != XK_CMD) 299 *ch = '\0'; 300 return ptr->type; 301 } 302 } else { 303 /* no match found here */ 304 if (ptr->sibling) { 305 /* try next sibling */ 306 return node_trav(el, ptr->sibling, ch, val); 307 } else { 308 /* no next sibling -- mismatch */ 309 val->str = NULL; 310 return XK_STR; 311 } 312 } 313} 314 315 316/* node__try(): 317 * Find a node that matches *str or allocate a new one 318 */ 319private int 320node__try(EditLine *el, keymacro_node_t *ptr, const Char *str, 321 keymacro_value_t *val, int ntype) 322{ 323 324 if (ptr->ch != *str) { 325 keymacro_node_t *xm; 326 327 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 328 if (xm->sibling->ch == *str) 329 break; 330 if (xm->sibling == NULL) 331 xm->sibling = node__get(*str); /* setup new node */ 332 ptr = xm->sibling; 333 } 334 if (*++str == '\0') { 335 /* we're there */ 336 if (ptr->next != NULL) { 337 node__put(el, ptr->next); 338 /* lose longer keys with this prefix */ 339 ptr->next = NULL; 340 } 341 switch (ptr->type) { 342 case XK_CMD: 343 case XK_NOD: 344 break; 345 case XK_STR: 346 case XK_EXE: 347 if (ptr->val.str) 348 el_free(ptr->val.str); 349 break; 350 default: 351 EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", 352 ptr->type)); 353 break; 354 } 355 356 switch (ptr->type = ntype) { 357 case XK_CMD: 358 ptr->val = *val; 359 break; 360 case XK_STR: 361 case XK_EXE: 362 if ((ptr->val.str = Strdup(val->str)) == NULL) 363 return -1; 364 break; 365 default: 366 EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); 367 break; 368 } 369 } else { 370 /* still more chars to go */ 371 if (ptr->next == NULL) 372 ptr->next = node__get(*str); /* setup new node */ 373 (void) node__try(el, ptr->next, str, val, ntype); 374 } 375 return 0; 376} 377 378 379/* node__delete(): 380 * Delete node that matches str 381 */ 382private int 383node__delete(EditLine *el, keymacro_node_t **inptr, const Char *str) 384{ 385 keymacro_node_t *ptr; 386 keymacro_node_t *prev_ptr = NULL; 387 388 ptr = *inptr; 389 390 if (ptr->ch != *str) { 391 keymacro_node_t *xm; 392 393 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 394 if (xm->sibling->ch == *str) 395 break; 396 if (xm->sibling == NULL) 397 return 0; 398 prev_ptr = xm; 399 ptr = xm->sibling; 400 } 401 if (*++str == '\0') { 402 /* we're there */ 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 if (ptr->next != NULL && 411 node__delete(el, &ptr->next, str) == 1) { 412 if (ptr->next != NULL) 413 return 0; 414 if (prev_ptr == NULL) 415 *inptr = ptr->sibling; 416 else 417 prev_ptr->sibling = ptr->sibling; 418 ptr->sibling = NULL; 419 node__put(el, ptr); 420 return 1; 421 } else { 422 return 0; 423 } 424} 425 426 427/* node__put(): 428 * Puts a tree of nodes onto free list using free(3). 429 */ 430private void 431node__put(EditLine *el, keymacro_node_t *ptr) 432{ 433 if (ptr == NULL) 434 return; 435 436 if (ptr->next != NULL) { 437 node__put(el, ptr->next); 438 ptr->next = NULL; 439 } 440 node__put(el, ptr->sibling); 441 442 switch (ptr->type) { 443 case XK_CMD: 444 case XK_NOD: 445 break; 446 case XK_EXE: 447 case XK_STR: 448 if (ptr->val.str != NULL) 449 el_free(ptr->val.str); 450 break; 451 default: 452 EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type)); 453 break; 454 } 455 el_free(ptr); 456} 457 458 459/* node__get(): 460 * Returns pointer to a keymacro_node_t for ch. 461 */ 462private keymacro_node_t * 463node__get(Int ch) 464{ 465 keymacro_node_t *ptr; 466 467 ptr = el_malloc(sizeof(*ptr)); 468 if (ptr == NULL) 469 return NULL; 470 ptr->ch = ch; 471 ptr->type = XK_NOD; 472 ptr->val.str = NULL; 473 ptr->next = NULL; 474 ptr->sibling = NULL; 475 return ptr; 476} 477 478private void 479node__free(keymacro_node_t *k) 480{ 481 if (k == NULL) 482 return; 483 node__free(k->sibling); 484 node__free(k->next); 485 el_free(k); 486} 487 488/* node_lookup(): 489 * look for the str starting at node ptr. 490 * Print if last node 491 */ 492private int 493node_lookup(EditLine *el, const Char *str, keymacro_node_t *ptr, size_t cnt) 494{ 495 ssize_t used; 496 497 if (ptr == NULL) 498 return -1; /* cannot have null ptr */ 499 500 if (!str || *str == 0) { 501 /* no more chars in str. node_enum from here. */ 502 (void) node_enum(el, ptr, cnt); 503 return 0; 504 } else { 505 /* If match put this char into el->el_keymacro.buf. Recurse */ 506 if (ptr->ch == *str) { 507 /* match found */ 508 used = ct_visual_char(el->el_keymacro.buf + cnt, 509 KEY_BUFSIZ - cnt, ptr->ch); 510 if (used == -1) 511 return -1; /* ran out of buffer space */ 512 if (ptr->next != NULL) 513 /* not yet at leaf */ 514 return (node_lookup(el, str + 1, ptr->next, 515 (size_t)used + cnt)); 516 else { 517 /* next node is null so key should be complete */ 518 if (str[1] == 0) { 519 size_t px = cnt + (size_t)used; 520 el->el_keymacro.buf[px] = '"'; 521 el->el_keymacro.buf[px + 1] = '\0'; 522 keymacro_kprint(el, el->el_keymacro.buf, 523 &ptr->val, ptr->type); 524 return 0; 525 } else 526 return -1; 527 /* mismatch -- str still has chars */ 528 } 529 } else { 530 /* no match found try sibling */ 531 if (ptr->sibling) 532 return (node_lookup(el, str, ptr->sibling, 533 cnt)); 534 else 535 return -1; 536 } 537 } 538} 539 540 541/* node_enum(): 542 * Traverse the node printing the characters it is bound in buffer 543 */ 544private int 545node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt) 546{ 547 ssize_t used; 548 549 if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */ 550 el->el_keymacro.buf[++cnt] = '"'; 551 el->el_keymacro.buf[++cnt] = '\0'; 552 (void) fprintf(el->el_errfile, 553 "Some extended keys too long for internal print buffer"); 554 (void) fprintf(el->el_errfile, " \"" FSTR "...\"\n", 555 el->el_keymacro.buf); 556 return 0; 557 } 558 if (ptr == NULL) { 559#ifdef DEBUG_EDIT 560 (void) fprintf(el->el_errfile, 561 "node_enum: BUG!! Null ptr passed\n!"); 562#endif 563 return -1; 564 } 565 /* put this char at end of str */ 566 used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt, 567 ptr->ch); 568 if (ptr->next == NULL) { 569 /* print this key and function */ 570 el->el_keymacro.buf[cnt + (size_t)used ] = '"'; 571 el->el_keymacro.buf[cnt + (size_t)used + 1] = '\0'; 572 keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type); 573 } else 574 (void) node_enum(el, ptr->next, cnt + (size_t)used); 575 576 /* go to sibling if there is one */ 577 if (ptr->sibling) 578 (void) node_enum(el, ptr->sibling, cnt); 579 return 0; 580} 581 582 583/* keymacro_kprint(): 584 * Print the specified key and its associated 585 * function specified by val 586 */ 587protected void 588keymacro_kprint(EditLine *el, const Char *key, keymacro_value_t *val, int ntype) 589{ 590 el_bindings_t *fp; 591 char unparsbuf[EL_BUFSIZ]; 592 static const char fmt[] = "%-15s-> %s\n"; 593 594 if (val != NULL) 595 switch (ntype) { 596 case XK_STR: 597 case XK_EXE: 598 (void) keymacro__decode_str(val->str, unparsbuf, 599 sizeof(unparsbuf), 600 ntype == XK_STR ? "\"\"" : "[]"); 601 (void) fprintf(el->el_outfile, fmt, 602 ct_encode_string(key, &el->el_scratch), unparsbuf); 603 break; 604 case XK_CMD: 605 for (fp = el->el_map.help; fp->name; fp++) 606 if (val->cmd == fp->func) { 607 ct_wcstombs(unparsbuf, fp->name, sizeof(unparsbuf)); 608 unparsbuf[sizeof(unparsbuf) -1] = '\0'; 609 (void) fprintf(el->el_outfile, fmt, 610 ct_encode_string(key, &el->el_scratch), unparsbuf); 611 break; 612 } 613#ifdef DEBUG_KEY 614 if (fp->name == NULL) 615 (void) fprintf(el->el_outfile, 616 "BUG! Command not found.\n"); 617#endif 618 619 break; 620 default: 621 EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); 622 break; 623 } 624 else 625 (void) fprintf(el->el_outfile, fmt, ct_encode_string(key, 626 &el->el_scratch), "no input"); 627} 628 629 630#define ADDC(c) \ 631 if (b < eb) \ 632 *b++ = c; \ 633 else \ 634 b++ 635/* keymacro__decode_str(): 636 * Make a printable version of the ey 637 */ 638protected size_t 639keymacro__decode_str(const Char *str, char *buf, size_t len, const char *sep) 640{ 641 char *b = buf, *eb = b + len; 642 const Char *p; 643 644 b = buf; 645 if (sep[0] != '\0') { 646 ADDC(sep[0]); 647 } 648 if (*str == '\0') { 649 ADDC('^'); 650 ADDC('@'); 651 goto add_endsep; 652 } 653 for (p = str; *p != 0; p++) { 654 Char dbuf[VISUAL_WIDTH_MAX]; 655 Char *p2 = dbuf; 656 ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p); 657 while (l-- > 0) { 658 ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++); 659 if (n == -1) /* ran out of space */ 660 goto add_endsep; 661 else 662 b += n; 663 } 664 } 665add_endsep: 666 if (sep[0] != '\0' && sep[1] != '\0') { 667 ADDC(sep[1]); 668 } 669 ADDC('\0'); 670 if ((size_t)(b - buf) >= len) 671 buf[len - 1] = '\0'; 672 return (size_t)(b - buf); 673} 674