2 * ed.xmap.c: This module contains the procedures for maintaining
3 * the extended-key map.
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.
13 * int GetXkey(ch,val);
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.
23 * void AddXkey(Xkey, val, ntype);
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.
33 * int DeleteXkey(Xkey);
36 * Delete the Xkey and all longer Xkeys staring with Xkey, if
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.
47 * Removes all entries from Xmap and resets the defaults.
49 * void PrintXkey(Xkey);
52 * Prints all extended keys prefixed by Xkey and their associated
57 * 1) It is not possible to have one Xkey that is a
58 * substring of another.
61 * Copyright (c) 1980, 1991 The Regents of the University of California.
62 * All rights reserved.
64 * Redistribution and use in source and binary forms, with or without
65 * modification, are permitted provided that the following conditions
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.
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
96 /* Internal Data types and declarations */
98 /* The Nodes of the Xmap. The Xmap is a linked list of these node
101 typedef struct Xmapnode
{
102 Char ch
; /* single character of Xkey */
104 XmapVal val
; /* command code or pointer to string, if this
106 struct Xmapnode
*next
; /* ptr to next char of this Xkey */
107 struct Xmapnode
*sibling
; /* ptr to another Xkey with same prefix */
110 static XmapNode
*Xmap
= NULL
; /* the current Xmap */
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
*,
121 static void Enumerate (struct Strbuf
*, const XmapNode
*);
122 static void unparsech (struct Strbuf
*, Char
);
129 xm
.cmd
= (KEYCMD
) cmd
;
137 xm
.str
.len
= str
->len
;
138 xm
.str
.buf
= str
->buf
;
143 * Takes all nodes on Xmap and puts them on free list. Then
144 * initializes Xmap with arrow keys
158 * Calls the recursive function with entry point Xmap
161 GetXkey(CStr
*ch
, XmapVal
*val
)
163 return (TraverseMap(Xmap
, ch
, val
));
167 * recursively traverses node in tree until match or mismatch is
168 * found. May read in more characters.
171 TraverseMap(XmapNode
*ptr
, CStr
*ch
, XmapVal
*val
)
175 if (ptr
->ch
== *(ch
->buf
)) {
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 */
184 return (TraverseMap(ptr
->next
, ch
, val
));
188 if (ptr
->type
!= XK_CMD
)
194 /* no match found here */
196 /* try next sibling */
197 return (TraverseMap(ptr
->sibling
, ch
, val
));
200 /* no next sibling -- mismatch */
209 AddXkey(const CStr
*Xkey
, XmapVal
*val
, int ntype
)
214 if (Xkey
->len
== 0) {
215 xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
219 if (ntype
== XK_CMD
&& val
->cmd
== F_XKEY
) {
221 CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
226 /* tree is initially empty. Set up new node to match Xkey[0] */
227 Xmap
= GetFreeNode(&cs
); /* it is properly initialized */
229 /* Now recurse through Xmap */
230 (void) TryNode(Xmap
, &cs
, val
, ntype
);
235 TryNode(XmapNode
*ptr
, CStr
*str
, XmapVal
*val
, int ntype
)
238 * Find a node that matches *string or allocate a new one
240 if (ptr
->ch
!= *(str
->buf
)) {
243 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
244 if (xm
->sibling
->ch
== *(str
->buf
))
246 if (xm
->sibling
== NULL
)
247 xm
->sibling
= GetFreeNode(str
); /* setup new node */
257 if (ptr
->next
!= NULL
) {
258 PutFreeNode(ptr
->next
); /* lose longer Xkeys with this prefix */
265 xfree(ptr
->val
.str
.buf
);
266 ptr
->val
.str
.len
= 0;
276 switch (ptr
->type
= ntype
) {
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
);
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
);
302 ClearXkey(KEYCMD
*map
, const CStr
*in
)
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
);
312 DeleteXkey(const CStr
*Xkey
)
319 CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
326 (void) TryDeleteNode(&Xmap
, &s
);
332 TryDeleteNode(XmapNode
**inptr
, CStr
*str
)
338 * Find a node that matches *string or allocate a new one
340 if (ptr
->ch
!= *(str
->buf
)) {
343 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
344 if (xm
->sibling
->ch
== *(str
->buf
))
346 if (xm
->sibling
== NULL
)
348 inptr
= &xm
->sibling
;
357 *inptr
= ptr
->sibling
;
362 else if (ptr
->next
!= NULL
&& TryDeleteNode(&ptr
->next
, str
) == 1) {
363 if (ptr
->next
!= NULL
)
365 *inptr
= ptr
->sibling
;
376 * Puts a tree of nodes onto free list using free(3).
379 PutFreeNode(XmapNode
*ptr
)
384 if (ptr
->next
!= NULL
) {
385 PutFreeNode(ptr
->next
);
389 PutFreeNode(ptr
->sibling
);
397 xfree(ptr
->val
.str
.buf
);
408 * Returns pointer to an XmapNode for ch.
411 GetFreeNode(CStr
*ch
)
415 ptr
= xmalloc(sizeof(XmapNode
));
416 ptr
->ch
= ch
->buf
[0];
418 ptr
->val
.str
.buf
= NULL
;
419 ptr
->val
.str
.len
= 0;
427 * Print the binding associated with Xkey key.
428 * Print entire Xmap if null
431 PrintXkey(const CStr
*key
)
433 struct Strbuf buf
= Strbuf_INIT
;
444 /* do nothing if Xmap is empty and null key specified */
445 if (Xmap
== NULL
&& cs
.len
== 0)
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
);
457 * look for the string starting at node ptr.
461 Lookup(struct Strbuf
*buf
, const CStr
*str
, const XmapNode
*ptr
)
464 return (-1); /* cannot have null ptr */
467 /* no more chars in string. Enumerate from here. */
472 /* If match put this char into buf. Recurse */
473 if (ptr
->ch
== *(str
->buf
)) {
475 unparsech(buf
, ptr
->ch
);
476 if (ptr
->next
!= NULL
) {
477 /* not yet at leaf */
479 tstr
.buf
= str
->buf
+ 1;
480 tstr
.len
= str
->len
- 1;
481 return (Lookup(buf
, &tstr
, ptr
->next
));
484 /* next node is null so key should be complete */
486 Strbuf_append1(buf
, '"');
487 Strbuf_terminate(buf
);
488 printOne(buf
->s
, &ptr
->val
, ptr
->type
);
492 return (-1);/* mismatch -- string still has chars */
496 /* no match found try sibling */
498 return (Lookup(buf
, str
, ptr
->sibling
));
506 Enumerate(struct Strbuf
*buf
, const XmapNode
*ptr
)
512 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
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
);
526 Enumerate(buf
, ptr
->next
);
528 /* go to sibling if there is one */
531 Enumerate(buf
, ptr
->sibling
);
537 * Print the specified key and its associated
538 * function specified by val
541 printOne(const Char
*key
, const XmapVal
*val
, int ntype
)
544 static const char *fmt
= "%s\n";
546 xprintf("%-15" TCSH_S
"-> ", key
);
553 p
= unparsestring(&val
->str
, ntype
== XK_STR
? STRQQ
: STRBB
);
554 cleanup_push(p
, xfree
);
560 for (fp
= FuncNames
; fp
->name
; fp
++)
561 if (val
->cmd
== fp
->func
)
562 xprintf(fmt
, fp
->name
);
569 xprintf(fmt
, CGETS(9, 7, "no input"));
573 unparsech(struct Strbuf
*buf
, Char ch
)
576 Strbuf_append1(buf
, '^');
577 Strbuf_append1(buf
, '@');
579 else if (Iscntrl(ch
)) {
580 Strbuf_append1(buf
, '^');
581 if (ch
== CTL_ESC('\177'))
582 Strbuf_append1(buf
, '?');
585 Strbuf_append1(buf
, ch
| 0100);
587 Strbuf_append1(buf
, _toebcdic
[_toascii
[ch
]|0100]);
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
);
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');
608 parse_hex_range(const Char
**cp
, size_t l
)
616 for (; (c
= (**cp
& CHAR
)) != '\0' && ui
< l
&& Isxdigit(c
); (*cp
)++, ui
++) {
617 Char x
= Isdigit(c
) ? '0' : ((Isupper(c
) ? 'A' : 'a') - 10);
626 return _toebcdic
[r
& 0xff];
633 * The e parameter is false when we are doing echo and true when we are
634 * using it to parse other escaped strings. For echo, we don't print errors
635 * and we don't unescape backslashes that we don't understand. Perhaps we
636 * always not unescape backslashes we don't understand...
639 parseescape(const Char
**ptr
, int e
)
646 if ((p
[1] & CHAR
) == 0) {
648 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p
);
651 if ((*p
& CHAR
) == '\\') {
655 c
= CTL_ESC('\007'); /* Bell */
658 c
= CTL_ESC('\010'); /* Backspace */
662 if ((*p
& CHAR
) == '\\') {
664 if ((*p
& CHAR
) != '\\') {
667 xprintf(/*CGETS(9, 9, */
668 "Invalid escape sequence: %s%c\n"/*)*/, "\\c\\", (char)*p
);
671 c
= (*p
& CHAR
) & 0237;
672 } else if ((Isalpha(*p
& CHAR
) || strchr("@^_?\\|[{]}", *p
& CHAR
))) {
673 /* XXX: Duplicate code from ^ below */
675 c
= ((*p
& CHAR
) == '?') ? CTL_ESC('\177') : ((*p
& CHAR
) & 0237);
677 c
= ((*p
& CHAR
) == '?') ? CTL_ESC('\177') : _toebcdic
[_toascii
[*p
& CHAR
] & 0237];
678 if (adrof(STRwarnebcdic
))
679 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
680 "Warning: Control character %s%c may be interpreted differently in EBCDIC.\n", "\\c", *p
& CHAR
/*)*/);
682 } else { /* backward compat */
688 c
= CTL_ESC('\033'); /* Escape */
691 c
= CTL_ESC('\014'); /* Form Feed */
694 c
= CTL_ESC('\012'); /* New Line */
697 c
= CTL_ESC('\015'); /* Carriage Return */
700 c
= CTL_ESC('\011'); /* Horizontal Tab */
703 c
= CTL_ESC('\013'); /* Vertical Tab */
710 if ((*p
& CHAR
) == '{' && Isxdigit(*(p
+ 1) & CHAR
)) { /* \x{20ac} */
711 const Char
*q
= p
- 2;
713 c
= parse_hex_range(&p
, 8);
714 if ((p
[1] & CHAR
) != '}') {
716 xprintf("%s", /* CGETS(9, 9, */
717 "Missing closing brace");
723 } else if (Isxdigit(*p
& CHAR
)) { /* \x9f */
724 c
= parse_hex_range(&p
, 2);
725 } else { /* backward compat */
733 size_t limit
= (*p
& CHAR
) == 'u' ? 4 : 8;
735 if (Isxdigit(*p
& CHAR
)) { /* \u20ac or \U000020ac */
736 c
= parse_hex_range(&p
, limit
);
737 } else { /* backward compat */
755 for (cnt
= 0, val
= 0; cnt
< 3; cnt
++) {
757 if (ch
< '0' || ch
> '7') {
761 val
= (val
<< 3) | (ch
- '0');
763 if ((val
& ~0xff) != 0) {
765 xprintf("%s", CGETS(9, 9,
766 "Octal constant does not fit in a char.\n"));
771 if (CTL_ESC(val
) != val
&& adrof(STRwarnebcdic
))
772 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
773 "Warning: Octal constant \\%3.3o is interpreted as "
774 "EBCDIC value.\n", val
/*)*/);
787 else if ((*p
& CHAR
) == '^' && (Isalpha(p
[1] & CHAR
) ||
788 strchr("@^_?\\|[{]}", p
[1] & CHAR
))) {
791 c
= ((*p
& CHAR
) == '?') ? CTL_ESC('\177') : ((*p
& CHAR
) & 0237);
793 c
= ((*p
& CHAR
) == '?') ? CTL_ESC('\177') : _toebcdic
[_toascii
[*p
& CHAR
] & 0237];
794 if (adrof(STRwarnebcdic
))
795 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
796 "Warning: Control character %s%c may be interpreted differently in EBCDIC.\n", "^", *p
& CHAR
/*)*/);
807 unparsestring(const CStr
*str
, const Char
*sep
)
809 unsigned char *buf
, *b
;
813 /* Worst-case is "\uuu" or result of wctomb() for each char from str */
814 buf
= xmalloc((str
->len
+ 1) * max(4, MB_LEN_MAX
));
819 #else /* WINNT_NATIVE */
820 *b
++ = CHAR
& sep
[0];
821 #endif /* !WINNT_NATIVE */
823 for (l
= 0; l
< str
->len
; l
++) {
827 if (p
== CTL_ESC('\177'))
831 *b
++ = (unsigned char) (p
| 0100);
833 *b
++ = _toebcdic
[_toascii
[p
]|0100];
836 else if (p
== '^' || p
== '\\') {
838 *b
++ = (unsigned char) p
;
840 else if (p
== ' ' || (Isprint(p
) && !Isspace(p
)))
841 b
+= one_wctomb((char *)b
, p
);
844 *b
++ = ((p
>> 6) & 7) + '0';
845 *b
++ = ((p
>> 3) & 7) + '0';
846 *b
++ = (p
& 7) + '0';
849 if (sep
[0] && sep
[1])
852 #else /* WINNT_NATIVE */
853 *b
++ = CHAR
& sep
[1];
854 #endif /* !WINNT_NATIVE */
856 return buf
; /* should check for overflow */