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