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