1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.36 2006/11/29 22:30:09 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.36 2006/11/29 22:30:09 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(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(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n")); 225 return; 226 } 227 228 if (Xmap == NULL) 229 /* tree is initially empty. Set up new node to match Xkey[0] */ 230 Xmap = GetFreeNode(&cs); /* it is properly initialized */ 231 232 /* Now recurse through Xmap */ 233 (void) TryNode(Xmap, &cs, val, ntype); 234 return; 235 } 236 237 static int 238 TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype) 239 { 240 /* 241 * Find a node that matches *string or allocate a new one 242 */ 243 if (ptr->ch != *(str->buf)) { 244 XmapNode *xm; 245 246 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 247 if (xm->sibling->ch == *(str->buf)) 248 break; 249 if (xm->sibling == NULL) 250 xm->sibling = GetFreeNode(str); /* setup new node */ 251 ptr = xm->sibling; 252 } 253 254 str->buf++; 255 str->len--; 256 if (str->len == 0) { 257 size_t len; 258 259 /* we're there */ 260 if (ptr->next != NULL) { 261 PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */ 262 ptr->next = NULL; 263 } 264 265 switch (ptr->type) { 266 case XK_STR: 267 case XK_EXE: 268 xfree(ptr->val.str.buf); 269 ptr->val.str.len = 0; 270 break; 271 case XK_NOD: 272 case XK_CMD: 273 break; 274 default: 275 abort(); 276 break; 277 } 278 279 switch (ptr->type = ntype) { 280 case XK_CMD: 281 ptr->val = *val; 282 break; 283 case XK_STR: 284 case XK_EXE: 285 ptr->val.str.len = val->str.len; 286 len = (val->str.len + 1) * sizeof(*ptr->val.str.buf); 287 ptr->val.str.buf = xmalloc(len); 288 (void) memcpy(ptr->val.str.buf, val->str.buf, len); 289 break; 290 default: 291 abort(); 292 break; 293 } 294 } 295 else { 296 /* still more chars to go */ 297 if (ptr->next == NULL) 298 ptr->next = GetFreeNode(str); /* setup new node */ 299 (void) TryNode(ptr->next, str, val, ntype); 300 } 301 return (0); 302 } 303 304 void 305 ClearXkey(KEYCMD *map, const CStr *in) 306 { 307 unsigned char c = (unsigned char) *(in->buf); 308 if ((map[c] == F_XKEY) && 309 ((map == CcKeyMap && CcAltMap[c] != F_XKEY) || 310 (map == CcAltMap && CcKeyMap[c] != F_XKEY))) 311 (void) DeleteXkey(in); 312 } 313 314 int 315 DeleteXkey(const CStr *Xkey) 316 { 317 CStr s; 318 319 s = *Xkey; 320 if (s.len == 0) { 321 xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n")); 322 return (-1); 323 } 324 325 if (Xmap == NULL) 326 return (0); 327 328 (void) TryDeleteNode(&Xmap, &s); 329 return (0); 330 } 331 332 /* Destroys str */ 333 static int 334 TryDeleteNode(XmapNode **inptr, CStr *str) 335 { 336 XmapNode *ptr; 337 338 ptr = *inptr; 339 /* 340 * Find a node that matches *string or allocate a new one 341 */ 342 if (ptr->ch != *(str->buf)) { 343 XmapNode *xm; 344 345 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 346 if (xm->sibling->ch == *(str->buf)) 347 break; 348 if (xm->sibling == NULL) 349 return (0); 350 inptr = &xm->sibling; 351 ptr = xm->sibling; 352 } 353 354 str->buf++; 355 str->len--; 356 357 if (str->len == 0) { 358 /* we're there */ 359 *inptr = ptr->sibling; 360 ptr->sibling = NULL; 361 PutFreeNode(ptr); 362 return (1); 363 } 364 else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) { 365 if (ptr->next != NULL) 366 return (0); 367 *inptr = ptr->sibling; 368 ptr->sibling = NULL; 369 PutFreeNode(ptr); 370 return (1); 371 } 372 else { 373 return (0); 374 } 375 } 376 377 /* PutFreeNode(): 378 * Puts a tree of nodes onto free list using free(3). 379 */ 380 static void 381 PutFreeNode(XmapNode *ptr) 382 { 383 if (ptr == NULL) 384 return; 385 386 if (ptr->next != NULL) { 387 PutFreeNode(ptr->next); 388 ptr->next = NULL; 389 } 390 391 PutFreeNode(ptr->sibling); 392 393 switch (ptr->type) { 394 case XK_CMD: 395 case XK_NOD: 396 break; 397 case XK_EXE: 398 case XK_STR: 399 xfree(ptr->val.str.buf); 400 break; 401 default: 402 abort(); 403 break; 404 } 405 xfree(ptr); 406 } 407 408 409 /* GetFreeNode(): 410 * Returns pointer to an XmapNode for ch. 411 */ 412 static XmapNode * 413 GetFreeNode(CStr *ch) 414 { 415 XmapNode *ptr; 416 417 ptr = xmalloc(sizeof(XmapNode)); 418 ptr->ch = ch->buf[0]; 419 ptr->type = XK_NOD; 420 ptr->val.str.buf = NULL; 421 ptr->val.str.len = 0; 422 ptr->next = NULL; 423 ptr->sibling = NULL; 424 return (ptr); 425 } 426 427 428 /* PrintXKey(): 429 * Print the binding associated with Xkey key. 430 * Print entire Xmap if null 431 */ 432 void 433 PrintXkey(const CStr *key) 434 { 435 struct Strbuf buf = Strbuf_INIT; 436 CStr cs; 437 438 if (key) { 439 cs.buf = key->buf; 440 cs.len = key->len; 441 } 442 else { 443 cs.buf = STRNULL; 444 cs.len = 0; 445 } 446 /* do nothing if Xmap is empty and null key specified */ 447 if (Xmap == NULL && cs.len == 0) 448 return; 449 450 Strbuf_append1(&buf, '"'); 451 cleanup_push(&buf, Strbuf_cleanup); 452 if (Lookup(&buf, &cs, Xmap) <= -1) 453 /* key is not bound */ 454 xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf); 455 cleanup_until(&buf); 456 } 457 458 /* Lookup(): 459 * look for the string starting at node ptr. 460 * Print if last node 461 */ 462 static int 463 Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr) 464 { 465 if (ptr == NULL) 466 return (-1); /* cannot have null ptr */ 467 468 if (str->len == 0) { 469 /* no more chars in string. Enumerate from here. */ 470 Enumerate(buf, ptr); 471 return (0); 472 } 473 else { 474 /* If match put this char into buf. Recurse */ 475 if (ptr->ch == *(str->buf)) { 476 /* match found */ 477 unparsech(buf, ptr->ch); 478 if (ptr->next != NULL) { 479 /* not yet at leaf */ 480 CStr tstr; 481 tstr.buf = str->buf + 1; 482 tstr.len = str->len - 1; 483 return (Lookup(buf, &tstr, ptr->next)); 484 } 485 else { 486 /* next node is null so key should be complete */ 487 if (str->len == 1) { 488 Strbuf_append1(buf, '"'); 489 Strbuf_terminate(buf); 490 printOne(buf->s, &ptr->val, ptr->type); 491 return (0); 492 } 493 else 494 return (-1);/* mismatch -- string still has chars */ 495 } 496 } 497 else { 498 /* no match found try sibling */ 499 if (ptr->sibling) 500 return (Lookup(buf, str, ptr->sibling)); 501 else 502 return (-1); 503 } 504 } 505 } 506 507 static void 508 Enumerate(struct Strbuf *buf, const XmapNode *ptr) 509 { 510 size_t old_len; 511 512 if (ptr == NULL) { 513 #ifdef DEBUG_EDIT 514 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!")); 515 #endif 516 return; 517 } 518 519 old_len = buf->len; 520 unparsech(buf, ptr->ch); /* put this char at end of string */ 521 if (ptr->next == NULL) { 522 /* print this Xkey and function */ 523 Strbuf_append1(buf, '"'); 524 Strbuf_terminate(buf); 525 printOne(buf->s, &ptr->val, ptr->type); 526 } 527 else 528 Enumerate(buf, ptr->next); 529 530 /* go to sibling if there is one */ 531 if (ptr->sibling) { 532 buf->len = old_len; 533 Enumerate(buf, ptr->sibling); 534 } 535 } 536 537 538 /* PrintOne(): 539 * Print the specified key and its associated 540 * function specified by val 541 */ 542 void 543 printOne(const Char *key, const XmapVal *val, int ntype) 544 { 545 struct KeyFuncs *fp; 546 static const char *fmt = "%s\n"; 547 548 xprintf("%-15S-> ", key); 549 if (val != NULL) 550 switch (ntype) { 551 case XK_STR: 552 case XK_EXE: { 553 unsigned char *p; 554 555 p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB); 556 cleanup_push(p, xfree); 557 xprintf(fmt, p); 558 cleanup_until(p); 559 break; 560 } 561 case XK_CMD: 562 for (fp = FuncNames; fp->name; fp++) 563 if (val->cmd == fp->func) 564 xprintf(fmt, fp->name); 565 break; 566 default: 567 abort(); 568 break; 569 } 570 else 571 xprintf(fmt, CGETS(9, 7, "no input")); 572 } 573 574 static void 575 unparsech(struct Strbuf *buf, Char ch) 576 { 577 if (ch == 0) { 578 Strbuf_append1(buf, '^'); 579 Strbuf_append1(buf, '@'); 580 } 581 else if (Iscntrl(ch)) { 582 Strbuf_append1(buf, '^'); 583 if (ch == CTL_ESC('\177')) 584 Strbuf_append1(buf, '?'); 585 else 586 #ifdef IS_ASCII 587 Strbuf_append1(buf, ch | 0100); 588 #else 589 Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]); 590 #endif 591 } 592 else if (ch == '^') { 593 Strbuf_append1(buf, '\\'); 594 Strbuf_append1(buf, '^'); 595 } else if (ch == '\\') { 596 Strbuf_append1(buf, '\\'); 597 Strbuf_append1(buf, '\\'); 598 } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) { 599 Strbuf_append1(buf, ch); 600 } 601 else { 602 Strbuf_append1(buf, '\\'); 603 Strbuf_append1(buf, ((ch >> 6) & 7) + '0'); 604 Strbuf_append1(buf, ((ch >> 3) & 7) + '0'); 605 Strbuf_append1(buf, (ch & 7) + '0'); 606 } 607 } 608 609 eChar 610 parseescape(const Char **ptr) 611 { 612 const Char *p; 613 Char c; 614 615 p = *ptr; 616 617 if ((p[1] & CHAR) == 0) { 618 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p); 619 return CHAR_ERR; 620 } 621 if ((*p & CHAR) == '\\') { 622 p++; 623 switch (*p & CHAR) { 624 case 'a': 625 c = CTL_ESC('\007'); /* Bell */ 626 break; 627 case 'b': 628 c = CTL_ESC('\010'); /* Backspace */ 629 break; 630 case 'e': 631 c = CTL_ESC('\033'); /* Escape */ 632 break; 633 case 'f': 634 c = CTL_ESC('\014'); /* Form Feed */ 635 break; 636 case 'n': 637 c = CTL_ESC('\012'); /* New Line */ 638 break; 639 case 'r': 640 c = CTL_ESC('\015'); /* Carriage Return */ 641 break; 642 case 't': 643 c = CTL_ESC('\011'); /* Horizontal Tab */ 644 break; 645 case 'v': 646 c = CTL_ESC('\013'); /* Vertical Tab */ 647 break; 648 case '\\': 649 c = '\\'; 650 break; 651 case '0': 652 case '1': 653 case '2': 654 case '3': 655 case '4': 656 case '5': 657 case '6': 658 case '7': 659 { 660 int cnt, val; 661 Char ch; 662 663 for (cnt = 0, val = 0; cnt < 3; cnt++) { 664 ch = *p++ & CHAR; 665 if (ch < '0' || ch > '7') { 666 p--; 667 break; 668 } 669 val = (val << 3) | (ch - '0'); 670 } 671 if ((val & ~0xff) != 0) { 672 xprintf(CGETS(9, 9, 673 "Octal constant does not fit in a char.\n")); 674 return 0; 675 } 676 #ifndef IS_ASCII 677 if (CTL_ESC(val) != val && adrof(STRwarnebcdic)) 678 xprintf(/*CGETS(9, 9, no NLS-String yet!*/ 679 "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/); 680 #endif 681 c = (Char) val; 682 --p; 683 } 684 break; 685 default: 686 c = *p; 687 break; 688 } 689 } 690 else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) || 691 strchr("@^_?\\|[{]}", p[1] & CHAR))) { 692 p++; 693 #ifdef IS_ASCII 694 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237); 695 #else 696 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237]; 697 if (adrof(STRwarnebcdic)) 698 xprintf(/*CGETS(9, 9, no NLS-String yet!*/ 699 "Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/); 700 #endif 701 } 702 else 703 c = *p; 704 *ptr = p; 705 return (c); 706 } 707 708 709 unsigned char * 710 unparsestring(const CStr *str, const Char *sep) 711 { 712 unsigned char *buf, *b; 713 Char p; 714 int l; 715 716 /* Worst-case is "\uuu" or result of wctomb() for each char from str */ 717 buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX)); 718 b = buf; 719 if (sep[0]) 720 #ifndef WINNT_NATIVE 721 *b++ = sep[0]; 722 #else /* WINNT_NATIVE */ 723 *b++ = CHAR & sep[0]; 724 #endif /* !WINNT_NATIVE */ 725 726 for (l = 0; l < str->len; l++) { 727 p = str->buf[l]; 728 if (Iscntrl(p)) { 729 *b++ = '^'; 730 if (p == CTL_ESC('\177')) 731 *b++ = '?'; 732 else 733 #ifdef IS_ASCII 734 *b++ = (unsigned char) (p | 0100); 735 #else 736 *b++ = _toebcdic[_toascii[p]|0100]; 737 #endif 738 } 739 else if (p == '^' || p == '\\') { 740 *b++ = '\\'; 741 *b++ = (unsigned char) p; 742 } 743 else if (p == ' ' || (Isprint(p) && !Isspace(p))) 744 b += one_wctomb((char *)b, p & CHAR); 745 else { 746 *b++ = '\\'; 747 *b++ = ((p >> 6) & 7) + '0'; 748 *b++ = ((p >> 3) & 7) + '0'; 749 *b++ = (p & 7) + '0'; 750 } 751 } 752 if (sep[0] && sep[1]) 753 #ifndef WINNT_NATIVE 754 *b++ = sep[1]; 755 #else /* WINNT_NATIVE */ 756 *b++ = CHAR & sep[1]; 757 #endif /* !WINNT_NATIVE */ 758 *b++ = 0; 759 return buf; /* should check for overflow */ 760 } 761