1 /* $Header: /src/pub/tcsh/ed.xmap.c,v 3.25 2002/03/08 17:36:45 christos Exp $ */
3 * ed.xmap.c: This module contains the procedures for maintaining
4 * the extended-key map.
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.
14 * int GetXkey(ch,val);
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.
24 * void AddXkey(Xkey, val, ntype);
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.
34 * int DeleteXkey(Xkey);
37 * Delete the Xkey and all longer Xkeys staring with Xkey, if
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.
48 * Removes all entries from Xmap and resets the defaults.
50 * void PrintXkey(Xkey);
53 * Prints all extended keys prefixed by Xkey and their associated
58 * 1) It is not possible to have one Xkey that is a
59 * substring of another.
62 * Copyright (c) 1980, 1991 The Regents of the University of California.
63 * All rights reserved.
65 * Redistribution and use in source and binary forms, with or without
66 * modification, are permitted provided that the following conditions
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.
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
91 RCSID("$Id: ed.xmap.c,v 3.25 2002/03/08 17:36:45 christos Exp $")
100 /* Internal Data types and declarations */
102 /* The Nodes of the Xmap. The Xmap is a linked list of these node
105 typedef struct Xmapnode {
106 Char ch; /* single character of Xkey */
108 XmapVal val; /* command code or pointer to string, if this
110 struct Xmapnode *next; /* ptr to next char of this Xkey */
111 struct Xmapnode *sibling; /* ptr to another Xkey with same prefix */
114 static XmapNode *Xmap = NULL; /* the current Xmap */
115 #define MAXXKEY 100 /* max length of a Xkey for print putposes */
116 static Char printbuf[MAXXKEY]; /* buffer for printing */
119 /* Some declarations of procedures */
120 static int TraverseMap __P((XmapNode *, CStr *, XmapVal *));
121 static int TryNode __P((XmapNode *, CStr *, XmapVal *, int));
122 static XmapNode *GetFreeNode __P((CStr *));
123 static void PutFreeNode __P((XmapNode *));
124 static int TryDeleteNode __P((XmapNode **, CStr *));
125 static int Lookup __P((CStr *, XmapNode *, int));
126 static int Enumerate __P((XmapNode *, int));
127 static int unparsech __P((int, Char *));
135 xm.cmd = (KEYCMD) cmd;
144 xm.str.len = str->len;
145 xm.str.buf = str->buf;
150 * Takes all nodes on Xmap and puts them on free list. Then
151 * initializes Xmap with arrow keys
165 * Calls the recursive function with entry point Xmap
172 return (TraverseMap(Xmap, ch, val));
176 * recursively traverses node in tree until match or mismatch is
177 * found. May read in more characters.
180 TraverseMap(ptr, ch, val)
187 if (ptr->ch == *(ch->buf)) {
190 /* Xkey not complete so get next char */
191 if (GetNextChar(&tch) != 1) { /* if EOF or error */
192 val->cmd = F_SEND_EOF;
193 return XK_CMD;/* PWP: Pretend we just read an end-of-file */
196 return (TraverseMap(ptr->next, ch, val));
200 if (ptr->type != XK_CMD)
206 /* no match found here */
208 /* try next sibling */
209 return (TraverseMap(ptr->sibling, ch, val));
212 /* no next sibling -- mismatch */
221 AddXkey(Xkey, val, ntype)
229 if (Xkey->len == 0) {
230 xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
234 if (ntype == XK_CMD && val->cmd == F_XKEY) {
235 xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
240 /* tree is initially empty. Set up new node to match Xkey[0] */
241 Xmap = GetFreeNode(&cs); /* it is properly initialized */
243 /* Now recurse through Xmap */
244 (void) TryNode(Xmap, &cs, val, ntype);
249 TryNode(ptr, str, val, ntype)
256 * Find a node that matches *string or allocate a new one
258 if (ptr->ch != *(str->buf)) {
261 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
262 if (xm->sibling->ch == *(str->buf))
264 if (xm->sibling == NULL)
265 xm->sibling = GetFreeNode(str); /* setup new node */
273 if (ptr->next != NULL) {
274 PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */
281 if (ptr->val.str.buf != NULL)
282 xfree((ptr_t) ptr->val.str.buf);
283 ptr->val.str.len = 0;
293 switch (ptr->type = ntype) {
299 ptr->val.str.len = (val->str.len + 1) * sizeof(Char);
300 ptr->val.str.buf = (Char *) xmalloc((size_t) ptr->val.str.len);
301 (void) memmove((ptr_t) ptr->val.str.buf, (ptr_t) val->str.buf,
302 (size_t) ptr->val.str.len);
303 ptr->val.str.len = val->str.len;
311 /* still more chars to go */
312 if (ptr->next == NULL)
313 ptr->next = GetFreeNode(str); /* setup new node */
314 (void) TryNode(ptr->next, str, val, ntype);
324 unsigned char c = (unsigned char) *(in->buf);
325 if ((map[c] == F_XKEY) &&
326 ((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
327 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
328 (void) DeleteXkey(in);
335 if (Xkey->len == 0) {
336 xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
343 (void) TryDeleteNode(&Xmap, Xkey);
348 TryDeleteNode(inptr, str)
353 XmapNode *prev_ptr = NULL;
357 * Find a node that matches *string or allocate a new one
359 if (ptr->ch != *(str->buf)) {
362 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
363 if (xm->sibling->ch == *(str->buf))
365 if (xm->sibling == NULL)
376 if (prev_ptr == NULL)
377 *inptr = ptr->sibling;
379 prev_ptr->sibling = ptr->sibling;
384 else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
385 if (ptr->next != NULL)
387 if (prev_ptr == NULL)
388 *inptr = ptr->sibling;
390 prev_ptr->sibling = ptr->sibling;
401 * Puts a tree of nodes onto free list using free(3).
410 if (ptr->next != NULL) {
411 PutFreeNode(ptr->next);
415 PutFreeNode(ptr->sibling);
423 if (ptr->val.str.buf != NULL)
424 xfree((ptr_t) ptr->val.str.buf);
435 * Returns pointer to an XmapNode for ch.
443 ptr = (XmapNode *) xmalloc((size_t) sizeof(XmapNode));
444 ptr->ch = ch->buf[0];
446 ptr->val.str.buf = NULL;
447 ptr->val.str.len = 0;
455 * Print the binding associated with Xkey key.
456 * Print entire Xmap if null
472 /* do nothing if Xmap is empty and null key specified */
473 if (Xmap == NULL && cs.len == 0)
477 if (Lookup(&cs, Xmap, 1) <= -1)
478 /* key is not bound */
479 xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
484 * look for the string starting at node ptr.
488 Lookup(str, ptr, cnt)
496 return (-1); /* cannot have null ptr */
499 /* no more chars in string. Enumerate from here. */
500 (void) Enumerate(ptr, cnt);
504 /* If match put this char into printbuf. Recurse */
505 if (ptr->ch == *(str->buf)) {
507 ncnt = unparsech(cnt, &ptr->ch);
508 if (ptr->next != NULL) {
509 /* not yet at leaf */
511 tstr.buf = str->buf + 1;
512 tstr.len = str->len - 1;
513 return (Lookup(&tstr, ptr->next, ncnt + 1));
516 /* next node is null so key should be complete */
519 printbuf[ncnt + 1] = '"';
520 printbuf[ncnt + 2] = '\0';
523 (void) printOne(&pb, &ptr->val, ptr->type);
527 return (-1);/* mismatch -- string still has chars */
531 /* no match found try sibling */
533 return (Lookup(str, ptr->sibling, cnt));
547 if (cnt >= MAXXKEY - 5) { /* buffer too small */
548 printbuf[++cnt] = '"';
549 printbuf[++cnt] = '\0';
551 "Some extended keys too long for internal print buffer"));
552 xprintf(" \"%S...\"\n", printbuf);
558 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
563 ncnt = unparsech(cnt, &ptr->ch); /* put this char at end of string */
564 if (ptr->next == NULL) {
566 /* print this Xkey and function */
567 printbuf[++ncnt] = '"';
568 printbuf[++ncnt] = '\0';
571 (void) printOne(&pb, &ptr->val, ptr->type);
574 (void) Enumerate(ptr->next, ncnt + 1);
576 /* go to sibling if there is one */
578 (void) Enumerate(ptr->sibling, cnt);
584 * Print the specified key and its associated
585 * function specified by val
588 printOne(key, val, ntype)
594 unsigned char unparsbuf[200];
595 static char *fmt = "%s\n";
597 xprintf("%-15S-> ", key->buf);
602 xprintf(fmt, unparsestring(&val->str, unparsbuf,
603 ntype == XK_STR ? STRQQ : STRBB));
606 for (fp = FuncNames; fp->name; fp++)
607 if (val->cmd == fp->func)
608 xprintf(fmt, fp->name);
615 xprintf(fmt, key, CGETS(9, 7, "no input"));
625 printbuf[cnt++] = '^';
632 printbuf[cnt++] = '^';
633 if (*ch == CTL_ESC('\177'))
636 printbuf[cnt] = *ch | 0100;
638 if (*ch == CTL_ESC('\177'))
640 printbuf[cnt++] = '^';
643 else if (Isupper(_toebcdic[_toascii[*ch]|0100])
644 || strchr("@[\\]^_", _toebcdic[_toascii[*ch]|0100]) != NULL)
646 printbuf[cnt++] = '^';
647 printbuf[cnt] = _toebcdic[_toascii[*ch]|0100];
651 printbuf[cnt++] = '\\';
652 printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
653 printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
654 printbuf[cnt] = (*ch & 7) + '0';
658 else if (*ch == '^') {
659 printbuf[cnt++] = '\\';
662 else if (*ch == '\\') {
663 printbuf[cnt++] = '\\';
664 printbuf[cnt] = '\\';
666 else if (*ch == ' ' || (Isprint(*ch) && !Isspace(*ch))) {
670 printbuf[cnt++] = '\\';
671 printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
672 printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
673 printbuf[cnt] = (*ch & 7) + '0';
687 if ((p[1] & CHAR) == 0) {
688 xprintf(CGETS(9, 8, "Something must follow: %c\n"), *p);
691 if ((*p & CHAR) == '\\') {
695 c = CTL_ESC('\007'); /* Bell */
698 c = CTL_ESC('\010'); /* Backspace */
701 c = CTL_ESC('\033'); /* Escape */
704 c = CTL_ESC('\014'); /* Form Feed */
707 c = CTL_ESC('\012'); /* New Line */
710 c = CTL_ESC('\015'); /* Carriage Return */
713 c = CTL_ESC('\011'); /* Horizontal Tab */
716 c = CTL_ESC('\013'); /* Vertical Tab */
730 register int cnt, val, ch;
732 for (cnt = 0, val = 0; cnt < 3; cnt++) {
734 if (ch < '0' || ch > '7') {
738 val = (val << 3) | (ch - '0');
740 if ((val & 0xffffff00) != 0) {
742 "Octal constant does not fit in a char.\n"));
746 if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
747 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
748 "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
759 else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
760 strchr("@^_?\\|[{]}", p[1] & CHAR))) {
763 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
765 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
766 if (adrof(STRwarnebcdic))
767 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
768 "Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
779 unparsestring(str, buf, sep)
792 #else /* WINNT_NATIVE */
793 *b++ = CHAR & sep[0];
794 #endif /* !WINNT_NATIVE */
796 for (l = 0; l < str->len; l++) {
801 if (p == CTL_ESC('\177'))
804 *b++ = (unsigned char) (p | 0100);
806 if (_toascii[p] == '\177' || Isupper(_toebcdic[_toascii[p]|0100])
807 || strchr("@[\\]^_", _toebcdic[_toascii[p]|0100]) != NULL)
810 *b++ = (_toascii[p] == '\177') ? '?' : _toebcdic[_toascii[p]|0100];
815 *b++ = ((p >> 6) & 7) + '0';
816 *b++ = ((p >> 3) & 7) + '0';
817 *b++ = (p & 7) + '0';
821 else if (p == '^' || p == '\\') {
823 *b++ = (unsigned char) p;
825 else if (p == ' ' || (Isprint(p) && !Isspace(p))) {
826 *b++ = (unsigned char) p;
830 *b++ = ((p >> 6) & 7) + '0';
831 *b++ = ((p >> 3) & 7) + '0';
832 *b++ = (p & 7) + '0';
835 if (sep[0] && sep[1])
838 #else /* WINNT_NATIVE */
839 *b++ = CHAR & sep[1];
840 #endif /* !WINNT_NATIVE */
842 return buf; /* should check for overflow */