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 38 static 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$"); 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 */ 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(Int); 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 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 */ 319 private int 320 node__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 */ 382 private int 383 node__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 */ 430 private void 431 node__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 */ 462 private keymacro_node_t * 463 node__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 478 private void 479 node__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 */ 492 private int 493 node_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 */ 544 private int 545 node_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 */ 587 protected void 588 keymacro_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 */ 638 protected size_t 639 keymacro__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 } 665 add_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