1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $ */ 2 /* 3 * ed.xmap.c: This module contains the procedures for maintaining 4 * the extended-key map. 5 * 6 * An extended-key (Xkey) is a sequence of keystrokes 7 * introduced with an sequence introducer and consisting 8 * of an arbitrary number of characters. This module maintains 9 * a map (the Xmap) to convert these extended-key sequences 10 * into input strings (XK_STR), editor functions (XK_CMD), or 11 * unix commands (XK_EXE). It contains the 12 * following externally visible functions. 13 * 14 * int GetXkey(ch,val); 15 * CStr *ch; 16 * XmapVal *val; 17 * 18 * Looks up *ch in map and then reads characters until a 19 * complete match is found or a mismatch occurs. Returns the 20 * type of the match found (XK_STR, XK_CMD, or XK_EXE). 21 * Returns NULL in val.str and XK_STR for no match. 22 * The last character read is returned in *ch. 23 * 24 * void AddXkey(Xkey, val, ntype); 25 * CStr *Xkey; 26 * XmapVal *val; 27 * int ntype; 28 * 29 * Adds Xkey to the Xmap and associates the value in val with it. 30 * If Xkey is already is in Xmap, the new code is applied to the 31 * existing Xkey. Ntype specifies if code is a command, an 32 * out string or a unix command. 33 * 34 * int DeleteXkey(Xkey); 35 * CStr *Xkey; 36 * 37 * Delete the Xkey and all longer Xkeys staring with Xkey, if 38 * they exists. 39 * 40 * Warning: 41 * If Xkey is a substring of some other Xkeys, then the longer 42 * Xkeys are lost!! That is, if the Xkeys "abcd" and "abcef" 43 * are in Xmap, adding the key "abc" will cause the first two 44 * definitions to be lost. 45 * 46 * void ResetXmap(); 47 * 48 * Removes all entries from Xmap and resets the defaults. 49 * 50 * void PrintXkey(Xkey); 51 * CStr *Xkey; 52 * 53 * Prints all extended keys prefixed by Xkey and their associated 54 * commands. 55 * 56 * Restrictions: 57 * ------------- 58 * 1) It is not possible to have one Xkey that is a 59 * substring of another. 60 */ 61 /*- 62 * Copyright (c) 1980, 1991 The Regents of the University of California. 63 * All rights reserved. 64 * 65 * Redistribution and use in source and binary forms, with or without 66 * modification, are permitted provided that the following conditions 67 * are met: 68 * 1. Redistributions of source code must retain the above copyright 69 * notice, this list of conditions and the following disclaimer. 70 * 2. Redistributions in binary form must reproduce the above copyright 71 * notice, this list of conditions and the following disclaimer in the 72 * documentation and/or other materials provided with the distribution. 73 * 3. Neither the name of the University nor the names of its contributors 74 * may be used to endorse or promote products derived from this software 75 * without specific prior written permission. 76 * 77 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 80 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 87 * SUCH DAMAGE. 88 */ 89 #include "sh.h" 90 91 RCSID("$tcsh: ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $") 92 93 #include "ed.h" 94 #include "ed.defns.h" 95 96 #ifndef NULL 97 #define NULL 0 98 #endif 99 100 /* Internal Data types and declarations */ 101 102 /* The Nodes of the Xmap. The Xmap is a linked list of these node 103 * elements 104 */ 105 typedef struct Xmapnode { 106 Char ch; /* single character of Xkey */ 107 int type; 108 XmapVal val; /* command code or pointer to string, if this 109 * is a leaf */ 110 struct Xmapnode *next; /* ptr to next char of this Xkey */ 111 struct Xmapnode *sibling; /* ptr to another Xkey with same prefix */ 112 } XmapNode; 113 114 static XmapNode *Xmap = NULL; /* the current Xmap */ 115 116 117 /* Some declarations of procedures */ 118 static int TraverseMap (XmapNode *, CStr *, XmapVal *); 119 static int TryNode (XmapNode *, CStr *, XmapVal *, int); 120 static XmapNode *GetFreeNode (CStr *); 121 static void PutFreeNode (XmapNode *); 122 static int TryDeleteNode (XmapNode **, CStr *); 123 static int Lookup (struct Strbuf *, const CStr *, 124 const XmapNode *); 125 static void Enumerate (struct Strbuf *, const XmapNode *); 126 static void unparsech (struct Strbuf *, Char); 127 128 129 XmapVal * 130 XmapCmd(int cmd) 131 { 132 static XmapVal xm; 133 xm.cmd = (KEYCMD) cmd; 134 return &xm; 135 } 136 137 XmapVal * 138 XmapStr(CStr *str) 139 { 140 static XmapVal xm; 141 xm.str.len = str->len; 142 xm.str.buf = str->buf; 143 return &xm; 144 } 145 146 /* ResetXmap(): 147 * Takes all nodes on Xmap and puts them on free list. Then 148 * initializes Xmap with arrow keys 149 */ 150 void 151 ResetXmap(void) 152 { 153 PutFreeNode(Xmap); 154 Xmap = NULL; 155 156 DefaultArrowKeys(); 157 return; 158 } 159 160 161 /* GetXkey(): 162 * Calls the recursive function with entry point Xmap 163 */ 164 int 165 GetXkey(CStr *ch, XmapVal *val) 166 { 167 return (TraverseMap(Xmap, ch, val)); 168 } 169 170 /* TraverseMap(): 171 * recursively traverses node in tree until match or mismatch is 172 * found. May read in more characters. 173 */ 174 static int 175 TraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val) 176 { 177 Char tch; 178 179 if (ptr->ch == *(ch->buf)) { 180 /* match found */ 181 if (ptr->next) { 182 /* Xkey not complete so get next char */ 183 if (GetNextChar(&tch) != 1) { /* if EOF or error */ 184 val->cmd = F_SEND_EOF; 185 return XK_CMD;/* PWP: Pretend we just read an end-of-file */ 186 } 187 *(ch->buf) = tch; 188 return (TraverseMap(ptr->next, ch, val)); 189 } 190 else { 191 *val = ptr->val; 192 if (ptr->type != XK_CMD) 193 *(ch->buf) = '\0'; 194 return ptr->type; 195 } 196 } 197 else { 198 /* no match found here */ 199 if (ptr->sibling) { 200 /* try next sibling */ 201 return (TraverseMap(ptr->sibling, ch, val)); 202 } 203 else { 204 /* no next sibling -- mismatch */ 205 val->str.buf = NULL; 206 val->str.len = 0; 207 return XK_STR; 208 } 209 } 210 } 211 212 void 213 AddXkey(const CStr *Xkey, XmapVal *val, int ntype) 214 { 215 CStr cs; 216 cs.buf = Xkey->buf; 217 cs.len = Xkey->len; 218 if (Xkey->len == 0) { 219 xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n")); 220 return; 221 } 222 223 if (ntype == XK_CMD && val->cmd == F_XKEY) { 224 xprintf("%s", 225 CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n")); 226 return; 227 } 228 229 if (Xmap == NULL) 230 /* tree is initially empty. Set up new node to match Xkey[0] */ 231 Xmap = GetFreeNode(&cs); /* it is properly initialized */ 232 233 /* Now recurse through Xmap */ 234 (void) TryNode(Xmap, &cs, val, ntype); 235 return; 236 } 237 238 static int 239 TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype) 240 { 241 /* 242 * Find a node that matches *string or allocate a new one 243 */ 244 if (ptr->ch != *(str->buf)) { 245 XmapNode *xm; 246 247 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 248 if (xm->sibling->ch == *(str->buf)) 249 break; 250 if (xm->sibling == NULL) 251 xm->sibling = GetFreeNode(str); /* setup new node */ 252 ptr = xm->sibling; 253 } 254 255 str->buf++; 256 str->len--; 257 if (str->len == 0) { 258 size_t len; 259 260 /* we're there */ 261 if (ptr->next != NULL) { 262 PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */ 263 ptr->next = NULL; 264 } 265 266 switch (ptr->type) { 267 case XK_STR: 268 case XK_EXE: 269 xfree(ptr->val.str.buf); 270 ptr->val.str.len = 0; 271 break; 272 case XK_NOD: 273 case XK_CMD: 274 break; 275 default: 276 abort(); 277 break; 278 } 279 280 switch (ptr->type = ntype) { 281 case XK_CMD: 282 ptr->val = *val; 283 break; 284 case XK_STR: 285 case XK_EXE: 286 ptr->val.str.len = val->str.len; 287 len = (val->str.len + 1) * sizeof(*ptr->val.str.buf); 288 ptr->val.str.buf = xmalloc(len); 289 (void) memcpy(ptr->val.str.buf, val->str.buf, len); 290 break; 291 default: 292 abort(); 293 break; 294 } 295 } 296 else { 297 /* still more chars to go */ 298 if (ptr->next == NULL) 299 ptr->next = GetFreeNode(str); /* setup new node */ 300 (void) TryNode(ptr->next, str, val, ntype); 301 } 302 return (0); 303 } 304 305 void 306 ClearXkey(KEYCMD *map, const CStr *in) 307 { 308 unsigned char c = (unsigned char) *(in->buf); 309 if ((map[c] == F_XKEY) && 310 ((map == CcKeyMap && CcAltMap[c] != F_XKEY) || 311 (map == CcAltMap && CcKeyMap[c] != F_XKEY))) 312 (void) DeleteXkey(in); 313 } 314 315 int 316 DeleteXkey(const CStr *Xkey) 317 { 318 CStr s; 319 320 s = *Xkey; 321 if (s.len == 0) { 322 xprintf("%s", 323 CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n")); 324 return (-1); 325 } 326 327 if (Xmap == NULL) 328 return (0); 329 330 (void) TryDeleteNode(&Xmap, &s); 331 return (0); 332 } 333 334 /* Destroys str */ 335 static int 336 TryDeleteNode(XmapNode **inptr, CStr *str) 337 { 338 XmapNode *ptr; 339 340 ptr = *inptr; 341 /* 342 * Find a node that matches *string or allocate a new one 343 */ 344 if (ptr->ch != *(str->buf)) { 345 XmapNode *xm; 346 347 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 348 if (xm->sibling->ch == *(str->buf)) 349 break; 350 if (xm->sibling == NULL) 351 return (0); 352 inptr = &xm->sibling; 353 ptr = xm->sibling; 354 } 355 356 str->buf++; 357 str->len--; 358 359 if (str->len == 0) { 360 /* we're there */ 361 *inptr = ptr->sibling; 362 ptr->sibling = NULL; 363 PutFreeNode(ptr); 364 return (1); 365 } 366 else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) { 367 if (ptr->next != NULL) 368 return (0); 369 *inptr = ptr->sibling; 370 ptr->sibling = NULL; 371 PutFreeNode(ptr); 372 return (1); 373 } 374 else { 375 return (0); 376 } 377 } 378 379 /* PutFreeNode(): 380 * Puts a tree of nodes onto free list using free(3). 381 */ 382 static void 383 PutFreeNode(XmapNode *ptr) 384 { 385 if (ptr == NULL) 386 return; 387 388 if (ptr->next != NULL) { 389 PutFreeNode(ptr->next); 390 ptr->next = NULL; 391 } 392 393 PutFreeNode(ptr->sibling); 394 395 switch (ptr->type) { 396 case XK_CMD: 397 case XK_NOD: 398 break; 399 case XK_EXE: 400 case XK_STR: 401 xfree(ptr->val.str.buf); 402 break; 403 default: 404 abort(); 405 break; 406 } 407 xfree(ptr); 408 } 409 410 411 /* GetFreeNode(): 412 * Returns pointer to an XmapNode for ch. 413 */ 414 static XmapNode * 415 GetFreeNode(CStr *ch) 416 { 417 XmapNode *ptr; 418 419 ptr = xmalloc(sizeof(XmapNode)); 420 ptr->ch = ch->buf[0]; 421 ptr->type = XK_NOD; 422 ptr->val.str.buf = NULL; 423 ptr->val.str.len = 0; 424 ptr->next = NULL; 425 ptr->sibling = NULL; 426 return (ptr); 427 } 428 429 430 /* PrintXKey(): 431 * Print the binding associated with Xkey key. 432 * Print entire Xmap if null 433 */ 434 void 435 PrintXkey(const CStr *key) 436 { 437 struct Strbuf buf = Strbuf_INIT; 438 CStr cs; 439 440 if (key) { 441 cs.buf = key->buf; 442 cs.len = key->len; 443 } 444 else { 445 cs.buf = STRNULL; 446 cs.len = 0; 447 } 448 /* do nothing if Xmap is empty and null key specified */ 449 if (Xmap == NULL && cs.len == 0) 450 return; 451 452 Strbuf_append1(&buf, '"'); 453 cleanup_push(&buf, Strbuf_cleanup); 454 if (Lookup(&buf, &cs, Xmap) <= -1) 455 /* key is not bound */ 456 xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf); 457 cleanup_until(&buf); 458 } 459 460 /* Lookup(): 461 * look for the string starting at node ptr. 462 * Print if last node 463 */ 464 static int 465 Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr) 466 { 467 if (ptr == NULL) 468 return (-1); /* cannot have null ptr */ 469 470 if (str->len == 0) { 471 /* no more chars in string. Enumerate from here. */ 472 Enumerate(buf, ptr); 473 return (0); 474 } 475 else { 476 /* If match put this char into buf. Recurse */ 477 if (ptr->ch == *(str->buf)) { 478 /* match found */ 479 unparsech(buf, ptr->ch); 480 if (ptr->next != NULL) { 481 /* not yet at leaf */ 482 CStr tstr; 483 tstr.buf = str->buf + 1; 484 tstr.len = str->len - 1; 485 return (Lookup(buf, &tstr, ptr->next)); 486 } 487 else { 488 /* next node is null so key should be complete */ 489 if (str->len == 1) { 490 Strbuf_append1(buf, '"'); 491 Strbuf_terminate(buf); 492 printOne(buf->s, &ptr->val, ptr->type); 493 return (0); 494 } 495 else 496 return (-1);/* mismatch -- string still has chars */ 497 } 498 } 499 else { 500 /* no match found try sibling */ 501 if (ptr->sibling) 502 return (Lookup(buf, str, ptr->sibling)); 503 else 504 return (-1); 505 } 506 } 507 } 508 509 static void 510 Enumerate(struct Strbuf *buf, const XmapNode *ptr) 511 { 512 size_t old_len; 513 514 if (ptr == NULL) { 515 #ifdef DEBUG_EDIT 516 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!")); 517 #endif 518 return; 519 } 520 521 old_len = buf->len; 522 unparsech(buf, ptr->ch); /* put this char at end of string */ 523 if (ptr->next == NULL) { 524 /* print this Xkey and function */ 525 Strbuf_append1(buf, '"'); 526 Strbuf_terminate(buf); 527 printOne(buf->s, &ptr->val, ptr->type); 528 } 529 else 530 Enumerate(buf, ptr->next); 531 532 /* go to sibling if there is one */ 533 if (ptr->sibling) { 534 buf->len = old_len; 535 Enumerate(buf, ptr->sibling); 536 } 537 } 538 539 540 /* PrintOne(): 541 * Print the specified key and its associated 542 * function specified by val 543 */ 544 void 545 printOne(const Char *key, const XmapVal *val, int ntype) 546 { 547 struct KeyFuncs *fp; 548 static const char *fmt = "%s\n"; 549 550 xprintf("%-15S-> ", key); 551 if (val != NULL) 552 switch (ntype) { 553 case XK_STR: 554 case XK_EXE: { 555 unsigned char *p; 556 557 p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB); 558 cleanup_push(p, xfree); 559 xprintf(fmt, p); 560 cleanup_until(p); 561 break; 562 } 563 case XK_CMD: 564 for (fp = FuncNames; fp->name; fp++) 565 if (val->cmd == fp->func) 566 xprintf(fmt, fp->name); 567 break; 568 default: 569 abort(); 570 break; 571 } 572 else 573 xprintf(fmt, CGETS(9, 7, "no input")); 574 } 575 576 static void 577 unparsech(struct Strbuf *buf, Char ch) 578 { 579 if (ch == 0) { 580 Strbuf_append1(buf, '^'); 581 Strbuf_append1(buf, '@'); 582 } 583 else if (Iscntrl(ch)) { 584 Strbuf_append1(buf, '^'); 585 if (ch == CTL_ESC('\177')) 586 Strbuf_append1(buf, '?'); 587 else 588 #ifdef IS_ASCII 589 Strbuf_append1(buf, ch | 0100); 590 #else 591 Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]); 592 #endif 593 } 594 else if (ch == '^') { 595 Strbuf_append1(buf, '\\'); 596 Strbuf_append1(buf, '^'); 597 } else if (ch == '\\') { 598 Strbuf_append1(buf, '\\'); 599 Strbuf_append1(buf, '\\'); 600 } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) { 601 Strbuf_append1(buf, ch); 602 } 603 else { 604 Strbuf_append1(buf, '\\'); 605 Strbuf_append1(buf, ((ch >> 6) & 7) + '0'); 606 Strbuf_append1(buf, ((ch >> 3) & 7) + '0'); 607 Strbuf_append1(buf, (ch & 7) + '0'); 608 } 609 } 610 611 eChar 612 parseescape(const Char **ptr) 613 { 614 const Char *p; 615 Char c; 616 617 p = *ptr; 618 619 if ((p[1] & CHAR) == 0) { 620 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p); 621 return CHAR_ERR; 622 } 623 if ((*p & CHAR) == '\\') { 624 p++; 625 switch (*p & CHAR) { 626 case 'a': 627 c = CTL_ESC('\007'); /* Bell */ 628 break; 629 case 'b': 630 c = CTL_ESC('\010'); /* Backspace */ 631 break; 632 case 'e': 633 c = CTL_ESC('\033'); /* Escape */ 634 break; 635 case 'f': 636 c = CTL_ESC('\014'); /* Form Feed */ 637 break; 638 case 'n': 639 c = CTL_ESC('\012'); /* New Line */ 640 break; 641 case 'r': 642 c = CTL_ESC('\015'); /* Carriage Return */ 643 break; 644 case 't': 645 c = CTL_ESC('\011'); /* Horizontal Tab */ 646 break; 647 case 'v': 648 c = CTL_ESC('\013'); /* Vertical Tab */ 649 break; 650 case '\\': 651 c = '\\'; 652 break; 653 case '0': 654 case '1': 655 case '2': 656 case '3': 657 case '4': 658 case '5': 659 case '6': 660 case '7': 661 { 662 int cnt, val; 663 Char ch; 664 665 for (cnt = 0, val = 0; cnt < 3; cnt++) { 666 ch = *p++ & CHAR; 667 if (ch < '0' || ch > '7') { 668 p--; 669 break; 670 } 671 val = (val << 3) | (ch - '0'); 672 } 673 if ((val & ~0xff) != 0) { 674 xprintf("%s", CGETS(9, 9, 675 "Octal constant does not fit in a char.\n")); 676 return 0; 677 } 678 #ifndef IS_ASCII 679 if (CTL_ESC(val) != val && adrof(STRwarnebcdic)) 680 xprintf(/*CGETS(9, 9, no NLS-String yet!*/ 681 "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/); 682 #endif 683 c = (Char) val; 684 --p; 685 } 686 break; 687 default: 688 c = *p; 689 break; 690 } 691 } 692 else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) || 693 strchr("@^_?\\|[{]}", p[1] & CHAR))) { 694 p++; 695 #ifdef IS_ASCII 696 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237); 697 #else 698 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237]; 699 if (adrof(STRwarnebcdic)) 700 xprintf(/*CGETS(9, 9, no NLS-String yet!*/ 701 "Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/); 702 #endif 703 } 704 else 705 c = *p; 706 *ptr = p; 707 return (c); 708 } 709 710 711 unsigned char * 712 unparsestring(const CStr *str, const Char *sep) 713 { 714 unsigned char *buf, *b; 715 Char p; 716 int l; 717 718 /* Worst-case is "\uuu" or result of wctomb() for each char from str */ 719 buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX)); 720 b = buf; 721 if (sep[0]) 722 #ifndef WINNT_NATIVE 723 *b++ = sep[0]; 724 #else /* WINNT_NATIVE */ 725 *b++ = CHAR & sep[0]; 726 #endif /* !WINNT_NATIVE */ 727 728 for (l = 0; l < str->len; l++) { 729 p = str->buf[l]; 730 if (Iscntrl(p)) { 731 *b++ = '^'; 732 if (p == CTL_ESC('\177')) 733 *b++ = '?'; 734 else 735 #ifdef IS_ASCII 736 *b++ = (unsigned char) (p | 0100); 737 #else 738 *b++ = _toebcdic[_toascii[p]|0100]; 739 #endif 740 } 741 else if (p == '^' || p == '\\') { 742 *b++ = '\\'; 743 *b++ = (unsigned char) p; 744 } 745 else if (p == ' ' || (Isprint(p) && !Isspace(p))) 746 b += one_wctomb((char *)b, p & CHAR); 747 else { 748 *b++ = '\\'; 749 *b++ = ((p >> 6) & 7) + '0'; 750 *b++ = ((p >> 3) & 7) + '0'; 751 *b++ = (p & 7) + '0'; 752 } 753 } 754 if (sep[0] && sep[1]) 755 #ifndef WINNT_NATIVE 756 *b++ = sep[1]; 757 #else /* WINNT_NATIVE */ 758 *b++ = CHAR & sep[1]; 759 #endif /* !WINNT_NATIVE */ 760 *b++ = 0; 761 return buf; /* should check for overflow */ 762 } 763