1: /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/ed.xmap.c,v 3.0 1991/07/04 21:49:28 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 or editor functions. It contains the 11: * following externally visible functions. 12: * 13: * int GetXkey(ch,code); 14: * Char *ch; 15: * Char **code; 16: * 17: * Looks up *ch in map and then reads characters until a 18: * complete match is found or a mismatch occurs. Returns 1 19: * for command and 0 for a string. Returns NULL in code for 20: * no match and 0 as value. The last character read is returned 21: * in *ch. 22: * 23: * void AddXkey(Xkey, code); 24: * Char *Xkey; 25: * Char * code; 26: * 27: * void AddXKeyCmd(Xkey, CmdCode); 28: * Char *Xkey; 29: * Char CmdCode; 30: * 31: * Adds Xkey to the Xmap and associates the code with it. If 32: * Xkey is already is in Xmap, the new code is applied to the 33: * existing Xkey. 34: * 35: * int DeleteXkey(Xkey); 36: * Char *Xkey; 37: * 38: * Delete the Xkey and all longer Xkeys staring with Xkey, if 39: * they exists. 40: * 41: * Warning: 42: * If Xkey is a substring of some other Xkeys, then the longer 43: * Xkeys are lost!! That is, if the Xkeys "abcd" and "abcef" 44: * are in Xmap, adding the key "abc" will cause the first two 45: * definitions to be lost. 46: * 47: * void ResetXmap(); 48: * 49: * Removes all entries from Xmap and resets the defaults. 50: * 51: * void PrintXkey(Xkey); 52: * Char *Xkey; 53: * 54: * Prints all extended keys prefixed by Xkey and their associated 55: * commands. 56: * 57: * Restrictions: 58: * ------------- 59: * 1) It is not possible to have one Xkey that is a 60: * substring of another. 61: */ 62: /*- 63: * Copyright (c) 1980, 1991 The Regents of the University of California. 64: * All rights reserved. 65: * 66: * Redistribution and use in source and binary forms, with or without 67: * modification, are permitted provided that the following conditions 68: * are met: 69: * 1. Redistributions of source code must retain the above copyright 70: * notice, this list of conditions and the following disclaimer. 71: * 2. Redistributions in binary form must reproduce the above copyright 72: * notice, this list of conditions and the following disclaimer in the 73: * documentation and/or other materials provided with the distribution. 74: * 3. All advertising materials mentioning features or use of this software 75: * must display the following acknowledgement: 76: * This product includes software developed by the University of 77: * California, Berkeley and its contributors. 78: * 4. Neither the name of the University nor the names of its contributors 79: * may be used to endorse or promote products derived from this software 80: * without specific prior written permission. 81: * 82: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 83: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 84: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 85: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 86: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 87: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 88: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 89: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 90: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 91: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 92: * SUCH DAMAGE. 93: */ 94: #include "config.h" 95: #if !defined(lint) && !defined(pdp11) 96: static char *rcsid() 97: { return "$Id: ed.xmap.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; } 98: #endif 99: 100: #include "sh.h" 101: #include "ed.h" 102: #include "ed.defns.h" 103: 104: #ifndef NULL 105: #define NULL 0 106: #endif 107: 108: /* Internal Data types and declarations */ 109: 110: /* The Nodes of the Xmap. The Xmap is a linked list of these node 111: * elements 112: */ 113: typedef struct Xmapnode { 114: Char ch; /* single character of Xkey */ 115: Char *code; /* command code or pointer to string, if this 116: * is a leaf */ 117: struct Xmapnode *next; /* ptr to next char of this Xkey */ 118: struct Xmapnode *sibling; /* ptr to another Xkey with same prefix */ 119: } XmapNode; 120: 121: static XmapNode *Xmap = NULL; /* the current Xmap */ 122: 123: static Char CurCode; 124: static XmapNode CurCmd = {0, &CurCode, NULL, NULL}; 125: 126: /* Some declarations of procedures */ 127: static int TraverseMap __P((XmapNode *, Char *, Char **)); 128: static int TryNode __P((XmapNode *, Char *, Char *, int)); 129: static XmapNode *GetFreeNode __P((int)); 130: static void PutFreeNode __P((XmapNode *)); 131: static int TryDeleteNode __P((XmapNode **, Char *)); 132: 133: 134: /* ResetXmap(): 135: * Takes all nodes on Xmap and puts them on free list. Then 136: * initializes Xmap with arrow keys 137: */ 138: void 139: ResetXmap(vi) 140: int vi; 141: { 142: static Char strA[] = {033, '[', 'A', '\0'}; 143: static Char strB[] = {033, '[', 'B', '\0'}; 144: static Char strC[] = {033, '[', 'C', '\0'}; 145: static Char strD[] = {033, '[', 'D', '\0'}; 146: static Char stOA[] = {033, 'O', 'A', '\0'}; 147: static Char stOB[] = {033, 'O', 'B', '\0'}; 148: static Char stOC[] = {033, 'O', 'C', '\0'}; 149: static Char stOD[] = {033, 'O', 'D', '\0'}; 150: 151: PutFreeNode(Xmap); 152: Xmap = NULL; 153: AddXKeyCmd(strA, F_UP_HIST); 154: AddXKeyCmd(strB, F_DOWN_HIST); 155: AddXKeyCmd(strC, F_CHARFWD); 156: AddXKeyCmd(strD, F_CHARBACK); 157: AddXKeyCmd(stOA, F_UP_HIST); 158: AddXKeyCmd(stOB, F_DOWN_HIST); 159: AddXKeyCmd(stOC, F_CHARFWD); 160: AddXKeyCmd(stOD, F_CHARBACK); 161: if (vi) { 162: AddXKeyCmd(&strA[1], F_UP_HIST); 163: AddXKeyCmd(&strB[1], F_DOWN_HIST); 164: AddXKeyCmd(&strC[1], F_CHARFWD); 165: AddXKeyCmd(&strD[1], F_CHARBACK); 166: AddXKeyCmd(&stOA[1], F_UP_HIST); 167: AddXKeyCmd(&stOB[1], F_DOWN_HIST); 168: AddXKeyCmd(&stOC[1], F_CHARFWD); 169: AddXKeyCmd(&stOD[1], F_CHARBACK); 170: } 171: return; 172: } 173: 174: 175: /* GetXkey(): 176: * Calls the recursive function with entry point Xmap 177: */ 178: int 179: GetXkey(ch, code) 180: Char *ch; 181: Char **code; 182: { 183: return (TraverseMap(Xmap, ch, code)); 184: } 185: 186: /* TraverseMap(): 187: * recursively traverses node in tree until match or mismatch is 188: * found. May read in more characters. 189: */ 190: static int 191: TraverseMap(ptr, ch, code) 192: XmapNode *ptr; 193: Char *ch; 194: Char **code; 195: { 196: Char tch; 197: 198: if (ptr->ch == *ch) { 199: /* match found */ 200: if (ptr->next) { 201: if (ptr->next != &CurCmd) { 202: /* Xkey not complete so get next char */ 203: if (G_N_Char(&tch) != 1) { /* if EOF or error */ 204: *ch = 0; 205: CurCmd.code[0] = F_SEND_EOF; 206: *code = CurCmd.code; 207: return 1; /* PWP: Pretend we just read an end-of-file */ 208: } 209: *ch = tch; 210: return (TraverseMap(ptr->next, ch, code)); 211: } 212: else { 213: CurCmd.code[0] = (Char) ptr->code; 214: *code = CurCmd.code; 215: return 1; 216: } 217: } 218: else { 219: /* next is null so this is leaf node and a string */ 220: *ch = 0; 221: *code = ptr->code; 222: return 0; 223: } 224: } 225: else { 226: /* no match found here */ 227: if (ptr->sibling) { 228: /* try next sibling */ 229: return (TraverseMap(ptr->sibling, ch, code)); 230: } 231: else { 232: /* no next sibling -- mismatch */ 233: *code = NULL; 234: return 0; 235: } 236: } 237: } 238: 239: void 240: AddXkey(Xkey, code) 241: Char *Xkey; 242: Char *code; 243: { 244: if (Xkey[0] == '\0') { 245: xprintf("AddXkey: Null extended-key not allowed.\n"); 246: return; 247: } 248: 249: if (Xmap == NULL) 250: /* tree is initially empty. Set up new node to match Xkey[0] */ 251: Xmap = GetFreeNode(Xkey[0]); /* it is properly initialized */ 252: 253: /* Now recurse through Xmap */ 254: (void) TryNode(Xmap, Xkey, code, 1); /* string */ 255: return; 256: } 257: 258: void 259: AddXKeyCmd(Xkey, CmdCode) 260: Char *Xkey; 261: int CmdCode; 262: { 263: /* Gould does not like casts... */ 264: unsigned int comp_r_bug; 265: 266: if (Xkey[0] == '\0') { 267: xprintf("AddXKeyCmd: Null extended-key not allowed.\n"); 268: return; 269: } 270: if (CmdCode == F_XKEY) { 271: xprintf("AddXKeyCmd: sequence-lead-in command not allowed\n"); 272: return; 273: } 274: 275: if (Xmap == NULL) 276: /* tree is initially empty. Set up new node to match Xkey[0] */ 277: Xmap = GetFreeNode(Xkey[0]); /* it is properly initialized */ 278: 279: /* Now recurse through Xmap */ 280: comp_r_bug = (unsigned int) CmdCode; 281: (void) TryNode(Xmap, Xkey, (Char *) comp_r_bug, 0); /* command */ 282: return; 283: } 284: 285: 286: static int 287: TryNode(ptr, string, code, IsString) 288: XmapNode *ptr; 289: Char *string; 290: Char *code; 291: int IsString; 292: { 293: /* 294: * Find a node that matches *string or allocate a new one 295: */ 296: if (ptr->ch != *string) { 297: XmapNode *xm; 298: 299: for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 300: if (xm->sibling->ch == *string) 301: break; 302: if (xm->sibling == NULL) 303: xm->sibling = GetFreeNode(*string); /* setup new node */ 304: ptr = xm->sibling; 305: } 306: 307: if (*++string == '\0') { 308: /* we're there */ 309: if (ptr->next != NULL && ptr->next != &CurCmd) { 310: PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */ 311: ptr->next = NULL; 312: } 313: if (ptr->next == NULL && ptr->code) 314: xfree((ptr_t) ptr->code); 315: if (IsString) { 316: ptr->next = NULL; 317: ptr->code = Strsave(code); 318: } 319: else { 320: ptr->next = &CurCmd; 321: ptr->code = code; 322: } 323: } 324: else { 325: /* still more chars to go */ 326: /* 327: * christos: We need to allocate a new XmapNode also if the next 328: * XmapNode is the CurCmd, cause the previous XmapNode definition was 329: * only one char long! 330: */ 331: if (ptr->next == NULL || ptr->next == &CurCmd) 332: ptr->next = GetFreeNode(*string); /* setup new node */ 333: (void) TryNode(ptr->next, string, code, IsString); 334: } 335: return (0); 336: } 337: 338: void 339: ClearXkey(map, in) 340: KEYCMD *map; 341: Char *in; 342: { 343: if ((map[(unsigned char) *in] == F_XKEY) && 344: ((map == CcKeyMap && CcAltMap[(unsigned char) *in] != F_XKEY) || 345: (map == CcAltMap && CcKeyMap[(unsigned char) *in] != F_XKEY))) 346: (void) DeleteXkey(in); 347: } 348: 349: int 350: DeleteXkey(Xkey) 351: Char *Xkey; 352: { 353: if (Xkey[0] == '\0') { 354: xprintf("DeleteXkey: Null extended-key not allowed.\n"); 355: return (-1); 356: } 357: 358: if (Xmap == NULL) 359: return (0); 360: 361: (void) TryDeleteNode(&Xmap, Xkey); 362: return (0); 363: } 364: 365: static int 366: TryDeleteNode(inptr, string) 367: XmapNode **inptr; 368: Char *string; 369: { 370: XmapNode *ptr; 371: XmapNode *prev_ptr = NULL; 372: 373: ptr = *inptr; 374: /* 375: * Find a node that matches *string or allocate a new one 376: */ 377: if (ptr->ch != *string) { 378: XmapNode *xm; 379: 380: for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) 381: if (xm->sibling->ch == *string) 382: break; 383: if (xm->sibling == NULL) 384: return (0); 385: prev_ptr = xm; 386: ptr = xm->sibling; 387: } 388: 389: if (*++string == '\0') { 390: /* we're there */ 391: if (prev_ptr == NULL) 392: *inptr = ptr->sibling; 393: else 394: prev_ptr->sibling = ptr->sibling; 395: ptr->sibling = NULL; 396: PutFreeNode(ptr); 397: return (1); 398: } 399: else if (ptr->next != NULL && TryDeleteNode(&ptr->next, string) == 1) { 400: if (ptr->next != NULL) 401: return (0); 402: if (prev_ptr == NULL) 403: *inptr = ptr->sibling; 404: else 405: prev_ptr->sibling = ptr->sibling; 406: ptr->sibling = NULL; 407: PutFreeNode(ptr); 408: return (1); 409: } 410: else { 411: return (0); 412: } 413: } 414: 415: 416: 417: 418: 419: /* PutFreeNode(): 420: * Puts a tree of nodes onto free list using free(3). 421: */ 422: static void 423: PutFreeNode(ptr) 424: XmapNode *ptr; 425: { 426: if (ptr == NULL) 427: return; 428: 429: if (ptr->next && ptr->next != &CurCmd) 430: PutFreeNode(ptr->next); 431: PutFreeNode(ptr->sibling); 432: if (ptr->next == NULL && ptr->code) 433: xfree((ptr_t) ptr->code); 434: xfree((ptr_t) ptr); 435: } 436: 437: 438: /* GetFreeNode(): 439: * Returns pointer to an XmapNode for ch. 440: */ 441: static XmapNode * 442: GetFreeNode(ch) 443: int ch; 444: { 445: XmapNode *ptr; 446: 447: ptr = (XmapNode *) xmalloc((size_t) sizeof(XmapNode)); 448: ptr->ch = ch; 449: ptr->code = NULL; 450: ptr->next = NULL; 451: ptr->sibling = NULL; 452: return (ptr); 453: } 454: 455: /* Now The Print Routine */ 456: #define maxXkey 100 /* max length of a Xkey for print putposes */ 457: static Char printbuf[maxXkey]; /* buffer for printing */ 458: 459: static int Lookup(); 460: static int Enumerate(); 461: static int printOne(); 462: static int u_p_ch(); 463: extern unsigned char *u_p_string(); 464: 465: /* PrintXKey(): 466: * Print the binding associated with Xkey key. 467: * Print entire Xmap if null 468: */ 469: void 470: PrintXkey(key) 471: Char *key; 472: { 473: /* do nothing if Xmap is empty and null key specified */ 474: if (Xmap == NULL && *key == 0) 475: return; 476: 477: printbuf[0] = '"'; 478: if (Lookup(key, Xmap, 1) <= -1) 479: /* key is not bound */ 480: xprintf("Unbound extended key \"%s\"\n", short2str(key)); 481: return; 482: } 483: 484: /* Lookup(): 485: * look for the string starting at node ptr. 486: * Print if last node 487: */ 488: static int 489: Lookup(string, ptr, cnt) 490: int cnt; 491: Char *string; 492: XmapNode *ptr; 493: { 494: int ncnt; 495: 496: if (ptr == NULL) 497: return (-1); /* cannot have null ptr */ 498: 499: if (*string == 0) { 500: /* no more chars in string. Enumerate from here. */ 501: (void) Enumerate(ptr, cnt); 502: return (0); 503: } 504: else { 505: /* If match put this char into printbuf. Recurse */ 506: if (ptr->ch == *string) { 507: /* match found */ 508: ncnt = u_p_ch(cnt, ptr->ch); 509: if (ptr->next && ptr->next != &CurCmd) 510: /* not yet at leaf */ 511: return (Lookup(string + 1, ptr->next, ncnt + 1)); 512: else { 513: /* next node is null or &CurCmd so key should be complete */ 514: if (string[1] == 0) { 515: printbuf[ncnt + 1] = '"'; 516: printbuf[ncnt + 2] = '\0'; 517: (void) printOne(printbuf, ptr->code, ptr->next == NULL); 518: return (0); 519: } 520: else 521: return (-1);/* mismatch -- string still has chars */ 522: } 523: } 524: else { 525: /* no match found try sibling */ 526: if (ptr->sibling) 527: return (Lookup(string, ptr->sibling, cnt)); 528: else 529: return (-1); 530: } 531: } 532: } 533: 534: static int 535: Enumerate(ptr, cnt) 536: int cnt; 537: XmapNode *ptr; 538: { 539: int ncnt; 540: 541: if (cnt >= maxXkey - 5) { /* buffer too small */ 542: printbuf[++cnt] = '"'; 543: printbuf[++cnt] = '\0'; 544: xprintf("Some extended keys too long for internal print buffer"); 545: xprintf(" \"%s...\"\n", short2str(printbuf)); 546: return (0); 547: } 548: 549: if (ptr == NULL) { 550: #ifdef DEBUG_EDIT 551: xprintf("Enumerate: BUG!! Null ptr passed\n!"); 552: #endif 553: return (-1); 554: } 555: 556: ncnt = u_p_ch(cnt, ptr->ch); /* put this char at end of string */ 557: if (ptr->next == NULL || ptr->next == &CurCmd) { 558: /* print this Xkey and function */ 559: printbuf[ncnt + 1] = '"'; 560: printbuf[ncnt + 2] = '\0'; 561: (void) printOne(printbuf, ptr->code, ptr->next == NULL); 562: } 563: else 564: (void) Enumerate(ptr->next, ncnt + 1); 565: 566: /* go to sibling if there is one */ 567: if (ptr->sibling) 568: (void) Enumerate(ptr->sibling, cnt); 569: return (0); 570: } 571: 572: 573: /* PrintOne(): 574: * Print the specified key and its associated 575: * function specified by code 576: */ 577: static int 578: printOne(key, code, prstring) 579: Char *key; 580: Char *code; 581: int prstring; 582: { 583: struct KeyFuncs *fp; 584: unsigned char unparsbuf[200]; 585: static char *fmt = "%-15s-> %s\n"; 586: 587: if (code) { 588: if (prstring) 589: xprintf(fmt, short2str(key), u_p_string(code, unparsbuf)); 590: else { 591: for (fp = FuncNames; fp->name; fp++) { 592: if ((int) code == fp->func) 593: xprintf(fmt, short2str(key), fp->name); 594: } 595: } 596: } 597: else 598: xprintf(fmt, short2str(key), "no input"); 599: return (0); 600: } 601: 602: static int 603: u_p_ch(cnt, ch) 604: int cnt; 605: Char ch; 606: { 607: if (ch == 0) { 608: printbuf[cnt++] = '^'; 609: printbuf[cnt] = '@'; 610: return cnt; 611: } 612: 613: if (Iscntrl(ch)) { 614: printbuf[cnt++] = '^'; 615: if (ch == '\177') 616: printbuf[cnt] = '?'; 617: else 618: printbuf[cnt] = ch | 0100; 619: } 620: else if (ch == '^') { 621: printbuf[cnt++] = '\\'; 622: printbuf[cnt] = '^'; 623: } 624: else if (ch == '\\') { 625: printbuf[cnt++] = '\\'; 626: printbuf[cnt] = '\\'; 627: } 628: else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) { 629: printbuf[cnt] = ch; 630: } 631: else { 632: printbuf[cnt++] = '\\'; 633: printbuf[cnt++] = ((ch >> 6) & 7) + '0'; 634: printbuf[cnt++] = ((ch >> 3) & 7) + '0'; 635: printbuf[cnt] = (ch & 7) + '0'; 636: } 637: return cnt; 638: }