1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.36 2006/11/29 22:30:09 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("$tcsh: ed.xmap.c,v 3.36 2006/11/29 22:30:09 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 */
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
*,
125 static void Enumerate (struct Strbuf
*, const XmapNode
*);
126 static void unparsech (struct Strbuf
*, Char
);
133 xm
.cmd
= (KEYCMD
) cmd
;
141 xm
.str
.len
= str
->len
;
142 xm
.str
.buf
= str
->buf
;
147 * Takes all nodes on Xmap and puts them on free list. Then
148 * initializes Xmap with arrow keys
162 * Calls the recursive function with entry point Xmap
165 GetXkey(CStr
*ch
, XmapVal
*val
)
167 return (TraverseMap(Xmap
, ch
, val
));
171 * recursively traverses node in tree until match or mismatch is
172 * found. May read in more characters.
175 TraverseMap(XmapNode
*ptr
, CStr
*ch
, XmapVal
*val
)
179 if (ptr
->ch
== *(ch
->buf
)) {
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 */
188 return (TraverseMap(ptr
->next
, ch
, val
));
192 if (ptr
->type
!= XK_CMD
)
198 /* no match found here */
200 /* try next sibling */
201 return (TraverseMap(ptr
->sibling
, ch
, val
));
204 /* no next sibling -- mismatch */
213 AddXkey(const CStr
*Xkey
, XmapVal
*val
, int ntype
)
218 if (Xkey
->len
== 0) {
219 xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
223 if (ntype
== XK_CMD
&& val
->cmd
== F_XKEY
) {
224 xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
229 /* tree is initially empty. Set up new node to match Xkey[0] */
230 Xmap
= GetFreeNode(&cs
); /* it is properly initialized */
232 /* Now recurse through Xmap */
233 (void) TryNode(Xmap
, &cs
, val
, ntype
);
238 TryNode(XmapNode
*ptr
, CStr
*str
, XmapVal
*val
, int ntype
)
241 * Find a node that matches *string or allocate a new one
243 if (ptr
->ch
!= *(str
->buf
)) {
246 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
247 if (xm
->sibling
->ch
== *(str
->buf
))
249 if (xm
->sibling
== NULL
)
250 xm
->sibling
= GetFreeNode(str
); /* setup new node */
260 if (ptr
->next
!= NULL
) {
261 PutFreeNode(ptr
->next
); /* lose longer Xkeys with this prefix */
268 xfree(ptr
->val
.str
.buf
);
269 ptr
->val
.str
.len
= 0;
279 switch (ptr
->type
= ntype
) {
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
);
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
);
305 ClearXkey(KEYCMD
*map
, const CStr
*in
)
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
);
315 DeleteXkey(const CStr
*Xkey
)
321 xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
328 (void) TryDeleteNode(&Xmap
, &s
);
334 TryDeleteNode(XmapNode
**inptr
, CStr
*str
)
340 * Find a node that matches *string or allocate a new one
342 if (ptr
->ch
!= *(str
->buf
)) {
345 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
346 if (xm
->sibling
->ch
== *(str
->buf
))
348 if (xm
->sibling
== NULL
)
350 inptr
= &xm
->sibling
;
359 *inptr
= ptr
->sibling
;
364 else if (ptr
->next
!= NULL
&& TryDeleteNode(&ptr
->next
, str
) == 1) {
365 if (ptr
->next
!= NULL
)
367 *inptr
= ptr
->sibling
;
378 * Puts a tree of nodes onto free list using free(3).
381 PutFreeNode(XmapNode
*ptr
)
386 if (ptr
->next
!= NULL
) {
387 PutFreeNode(ptr
->next
);
391 PutFreeNode(ptr
->sibling
);
399 xfree(ptr
->val
.str
.buf
);
410 * Returns pointer to an XmapNode for ch.
413 GetFreeNode(CStr
*ch
)
417 ptr
= xmalloc(sizeof(XmapNode
));
418 ptr
->ch
= ch
->buf
[0];
420 ptr
->val
.str
.buf
= NULL
;
421 ptr
->val
.str
.len
= 0;
429 * Print the binding associated with Xkey key.
430 * Print entire Xmap if null
433 PrintXkey(const CStr
*key
)
435 struct Strbuf buf
= Strbuf_INIT
;
446 /* do nothing if Xmap is empty and null key specified */
447 if (Xmap
== NULL
&& cs
.len
== 0)
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
);
459 * look for the string starting at node ptr.
463 Lookup(struct Strbuf
*buf
, const CStr
*str
, const XmapNode
*ptr
)
466 return (-1); /* cannot have null ptr */
469 /* no more chars in string. Enumerate from here. */
474 /* If match put this char into buf. Recurse */
475 if (ptr
->ch
== *(str
->buf
)) {
477 unparsech(buf
, ptr
->ch
);
478 if (ptr
->next
!= NULL
) {
479 /* not yet at leaf */
481 tstr
.buf
= str
->buf
+ 1;
482 tstr
.len
= str
->len
- 1;
483 return (Lookup(buf
, &tstr
, ptr
->next
));
486 /* next node is null so key should be complete */
488 Strbuf_append1(buf
, '"');
489 Strbuf_terminate(buf
);
490 printOne(buf
->s
, &ptr
->val
, ptr
->type
);
494 return (-1);/* mismatch -- string still has chars */
498 /* no match found try sibling */
500 return (Lookup(buf
, str
, ptr
->sibling
));
508 Enumerate(struct Strbuf
*buf
, const XmapNode
*ptr
)
514 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
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
);
528 Enumerate(buf
, ptr
->next
);
530 /* go to sibling if there is one */
533 Enumerate(buf
, ptr
->sibling
);
539 * Print the specified key and its associated
540 * function specified by val
543 printOne(const Char
*key
, const XmapVal
*val
, int ntype
)
546 static const char *fmt
= "%s\n";
548 xprintf("%-15S-> ", key
);
555 p
= unparsestring(&val
->str
, ntype
== XK_STR
? STRQQ
: STRBB
);
556 cleanup_push(p
, xfree
);
562 for (fp
= FuncNames
; fp
->name
; fp
++)
563 if (val
->cmd
== fp
->func
)
564 xprintf(fmt
, fp
->name
);
571 xprintf(fmt
, CGETS(9, 7, "no input"));
575 unparsech(struct Strbuf
*buf
, Char ch
)
578 Strbuf_append1(buf
, '^');
579 Strbuf_append1(buf
, '@');
581 else if (Iscntrl(ch
)) {
582 Strbuf_append1(buf
, '^');
583 if (ch
== CTL_ESC('\177'))
584 Strbuf_append1(buf
, '?');
587 Strbuf_append1(buf
, ch
| 0100);
589 Strbuf_append1(buf
, _toebcdic
[_toascii
[ch
]|0100]);
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
);
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');
610 parseescape(const Char
**ptr
)
617 if ((p
[1] & CHAR
) == 0) {
618 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p
);
621 if ((*p
& CHAR
) == '\\') {
625 c
= CTL_ESC('\007'); /* Bell */
628 c
= CTL_ESC('\010'); /* Backspace */
631 c
= CTL_ESC('\033'); /* Escape */
634 c
= CTL_ESC('\014'); /* Form Feed */
637 c
= CTL_ESC('\012'); /* New Line */
640 c
= CTL_ESC('\015'); /* Carriage Return */
643 c
= CTL_ESC('\011'); /* Horizontal Tab */
646 c
= CTL_ESC('\013'); /* Vertical Tab */
663 for (cnt
= 0, val
= 0; cnt
< 3; cnt
++) {
665 if (ch
< '0' || ch
> '7') {
669 val
= (val
<< 3) | (ch
- '0');
671 if ((val
& ~0xff) != 0) {
673 "Octal constant does not fit in a char.\n"));
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
/*)*/);
690 else if ((*p
& CHAR
) == '^' && (Isalpha(p
[1] & CHAR
) ||
691 strchr("@^_?\\|[{]}", p
[1] & CHAR
))) {
694 c
= ((*p
& CHAR
) == '?') ? CTL_ESC('\177') : ((*p
& CHAR
) & 0237);
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
/*)*/);
710 unparsestring(const CStr
*str
, const Char
*sep
)
712 unsigned char *buf
, *b
;
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
));
722 #else /* WINNT_NATIVE */
723 *b
++ = CHAR
& sep
[0];
724 #endif /* !WINNT_NATIVE */
726 for (l
= 0; l
< str
->len
; l
++) {
730 if (p
== CTL_ESC('\177'))
734 *b
++ = (unsigned char) (p
| 0100);
736 *b
++ = _toebcdic
[_toascii
[p
]|0100];
739 else if (p
== '^' || p
== '\\') {
741 *b
++ = (unsigned char) p
;
743 else if (p
== ' ' || (Isprint(p
) && !Isspace(p
)))
744 b
+= one_wctomb((char *)b
, p
& CHAR
);
747 *b
++ = ((p
>> 6) & 7) + '0';
748 *b
++ = ((p
>> 3) & 7) + '0';
749 *b
++ = (p
& 7) + '0';
752 if (sep
[0] && sep
[1])
755 #else /* WINNT_NATIVE */
756 *b
++ = CHAR
& sep
[1];
757 #endif /* !WINNT_NATIVE */
759 return buf
; /* should check for overflow */