1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.37 2009/06/25 21:15:37 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.37 2009/06/25 21:15:37 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("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
223 if (ntype
== XK_CMD
&& val
->cmd
== F_XKEY
) {
225 CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
230 /* tree is initially empty. Set up new node to match Xkey[0] */
231 Xmap
= GetFreeNode(&cs
); /* it is properly initialized */
233 /* Now recurse through Xmap */
234 (void) TryNode(Xmap
, &cs
, val
, ntype
);
239 TryNode(XmapNode
*ptr
, CStr
*str
, XmapVal
*val
, int ntype
)
242 * Find a node that matches *string or allocate a new one
244 if (ptr
->ch
!= *(str
->buf
)) {
247 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
248 if (xm
->sibling
->ch
== *(str
->buf
))
250 if (xm
->sibling
== NULL
)
251 xm
->sibling
= GetFreeNode(str
); /* setup new node */
261 if (ptr
->next
!= NULL
) {
262 PutFreeNode(ptr
->next
); /* lose longer Xkeys with this prefix */
269 xfree(ptr
->val
.str
.buf
);
270 ptr
->val
.str
.len
= 0;
280 switch (ptr
->type
= ntype
) {
286 ptr
->val
.str
.len
= val
->str
.len
;
287 len
= (val
->str
.len
+ 1) * sizeof(*ptr
->val
.str
.buf
);
288 ptr
->val
.str
.buf
= xmalloc(len
);
289 (void) memcpy(ptr
->val
.str
.buf
, val
->str
.buf
, len
);
297 /* still more chars to go */
298 if (ptr
->next
== NULL
)
299 ptr
->next
= GetFreeNode(str
); /* setup new node */
300 (void) TryNode(ptr
->next
, str
, val
, ntype
);
306 ClearXkey(KEYCMD
*map
, const CStr
*in
)
308 unsigned char c
= (unsigned char) *(in
->buf
);
309 if ((map
[c
] == F_XKEY
) &&
310 ((map
== CcKeyMap
&& CcAltMap
[c
] != F_XKEY
) ||
311 (map
== CcAltMap
&& CcKeyMap
[c
] != F_XKEY
)))
312 (void) DeleteXkey(in
);
316 DeleteXkey(const CStr
*Xkey
)
323 CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
330 (void) TryDeleteNode(&Xmap
, &s
);
336 TryDeleteNode(XmapNode
**inptr
, CStr
*str
)
342 * Find a node that matches *string or allocate a new one
344 if (ptr
->ch
!= *(str
->buf
)) {
347 for (xm
= ptr
; xm
->sibling
!= NULL
; xm
= xm
->sibling
)
348 if (xm
->sibling
->ch
== *(str
->buf
))
350 if (xm
->sibling
== NULL
)
352 inptr
= &xm
->sibling
;
361 *inptr
= ptr
->sibling
;
366 else if (ptr
->next
!= NULL
&& TryDeleteNode(&ptr
->next
, str
) == 1) {
367 if (ptr
->next
!= NULL
)
369 *inptr
= ptr
->sibling
;
380 * Puts a tree of nodes onto free list using free(3).
383 PutFreeNode(XmapNode
*ptr
)
388 if (ptr
->next
!= NULL
) {
389 PutFreeNode(ptr
->next
);
393 PutFreeNode(ptr
->sibling
);
401 xfree(ptr
->val
.str
.buf
);
412 * Returns pointer to an XmapNode for ch.
415 GetFreeNode(CStr
*ch
)
419 ptr
= xmalloc(sizeof(XmapNode
));
420 ptr
->ch
= ch
->buf
[0];
422 ptr
->val
.str
.buf
= NULL
;
423 ptr
->val
.str
.len
= 0;
431 * Print the binding associated with Xkey key.
432 * Print entire Xmap if null
435 PrintXkey(const CStr
*key
)
437 struct Strbuf buf
= Strbuf_INIT
;
448 /* do nothing if Xmap is empty and null key specified */
449 if (Xmap
== NULL
&& cs
.len
== 0)
452 Strbuf_append1(&buf
, '"');
453 cleanup_push(&buf
, Strbuf_cleanup
);
454 if (Lookup(&buf
, &cs
, Xmap
) <= -1)
455 /* key is not bound */
456 xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs
.buf
);
461 * look for the string starting at node ptr.
465 Lookup(struct Strbuf
*buf
, const CStr
*str
, const XmapNode
*ptr
)
468 return (-1); /* cannot have null ptr */
471 /* no more chars in string. Enumerate from here. */
476 /* If match put this char into buf. Recurse */
477 if (ptr
->ch
== *(str
->buf
)) {
479 unparsech(buf
, ptr
->ch
);
480 if (ptr
->next
!= NULL
) {
481 /* not yet at leaf */
483 tstr
.buf
= str
->buf
+ 1;
484 tstr
.len
= str
->len
- 1;
485 return (Lookup(buf
, &tstr
, ptr
->next
));
488 /* next node is null so key should be complete */
490 Strbuf_append1(buf
, '"');
491 Strbuf_terminate(buf
);
492 printOne(buf
->s
, &ptr
->val
, ptr
->type
);
496 return (-1);/* mismatch -- string still has chars */
500 /* no match found try sibling */
502 return (Lookup(buf
, str
, ptr
->sibling
));
510 Enumerate(struct Strbuf
*buf
, const XmapNode
*ptr
)
516 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
522 unparsech(buf
, ptr
->ch
); /* put this char at end of string */
523 if (ptr
->next
== NULL
) {
524 /* print this Xkey and function */
525 Strbuf_append1(buf
, '"');
526 Strbuf_terminate(buf
);
527 printOne(buf
->s
, &ptr
->val
, ptr
->type
);
530 Enumerate(buf
, ptr
->next
);
532 /* go to sibling if there is one */
535 Enumerate(buf
, ptr
->sibling
);
541 * Print the specified key and its associated
542 * function specified by val
545 printOne(const Char
*key
, const XmapVal
*val
, int ntype
)
548 static const char *fmt
= "%s\n";
550 xprintf("%-15S-> ", key
);
557 p
= unparsestring(&val
->str
, ntype
== XK_STR
? STRQQ
: STRBB
);
558 cleanup_push(p
, xfree
);
564 for (fp
= FuncNames
; fp
->name
; fp
++)
565 if (val
->cmd
== fp
->func
)
566 xprintf(fmt
, fp
->name
);
573 xprintf(fmt
, CGETS(9, 7, "no input"));
577 unparsech(struct Strbuf
*buf
, Char ch
)
580 Strbuf_append1(buf
, '^');
581 Strbuf_append1(buf
, '@');
583 else if (Iscntrl(ch
)) {
584 Strbuf_append1(buf
, '^');
585 if (ch
== CTL_ESC('\177'))
586 Strbuf_append1(buf
, '?');
589 Strbuf_append1(buf
, ch
| 0100);
591 Strbuf_append1(buf
, _toebcdic
[_toascii
[ch
]|0100]);
594 else if (ch
== '^') {
595 Strbuf_append1(buf
, '\\');
596 Strbuf_append1(buf
, '^');
597 } else if (ch
== '\\') {
598 Strbuf_append1(buf
, '\\');
599 Strbuf_append1(buf
, '\\');
600 } else if (ch
== ' ' || (Isprint(ch
) && !Isspace(ch
))) {
601 Strbuf_append1(buf
, ch
);
604 Strbuf_append1(buf
, '\\');
605 Strbuf_append1(buf
, ((ch
>> 6) & 7) + '0');
606 Strbuf_append1(buf
, ((ch
>> 3) & 7) + '0');
607 Strbuf_append1(buf
, (ch
& 7) + '0');
612 parseescape(const Char
**ptr
)
619 if ((p
[1] & CHAR
) == 0) {
620 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p
);
623 if ((*p
& CHAR
) == '\\') {
627 c
= CTL_ESC('\007'); /* Bell */
630 c
= CTL_ESC('\010'); /* Backspace */
633 c
= CTL_ESC('\033'); /* Escape */
636 c
= CTL_ESC('\014'); /* Form Feed */
639 c
= CTL_ESC('\012'); /* New Line */
642 c
= CTL_ESC('\015'); /* Carriage Return */
645 c
= CTL_ESC('\011'); /* Horizontal Tab */
648 c
= CTL_ESC('\013'); /* Vertical Tab */
665 for (cnt
= 0, val
= 0; cnt
< 3; cnt
++) {
667 if (ch
< '0' || ch
> '7') {
671 val
= (val
<< 3) | (ch
- '0');
673 if ((val
& ~0xff) != 0) {
674 xprintf("%s", CGETS(9, 9,
675 "Octal constant does not fit in a char.\n"));
679 if (CTL_ESC(val
) != val
&& adrof(STRwarnebcdic
))
680 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
681 "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val
/*)*/);
692 else if ((*p
& CHAR
) == '^' && (Isalpha(p
[1] & CHAR
) ||
693 strchr("@^_?\\|[{]}", p
[1] & CHAR
))) {
696 c
= ((*p
& CHAR
) == '?') ? CTL_ESC('\177') : ((*p
& CHAR
) & 0237);
698 c
= ((*p
& CHAR
) == '?') ? CTL_ESC('\177') : _toebcdic
[_toascii
[*p
& CHAR
] & 0237];
699 if (adrof(STRwarnebcdic
))
700 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
701 "Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p
& CHAR
/*)*/);
712 unparsestring(const CStr
*str
, const Char
*sep
)
714 unsigned char *buf
, *b
;
718 /* Worst-case is "\uuu" or result of wctomb() for each char from str */
719 buf
= xmalloc((str
->len
+ 1) * max(4, MB_LEN_MAX
));
724 #else /* WINNT_NATIVE */
725 *b
++ = CHAR
& sep
[0];
726 #endif /* !WINNT_NATIVE */
728 for (l
= 0; l
< str
->len
; l
++) {
732 if (p
== CTL_ESC('\177'))
736 *b
++ = (unsigned char) (p
| 0100);
738 *b
++ = _toebcdic
[_toascii
[p
]|0100];
741 else if (p
== '^' || p
== '\\') {
743 *b
++ = (unsigned char) p
;
745 else if (p
== ' ' || (Isprint(p
) && !Isspace(p
)))
746 b
+= one_wctomb((char *)b
, p
& CHAR
);
749 *b
++ = ((p
>> 6) & 7) + '0';
750 *b
++ = ((p
>> 3) & 7) + '0';
751 *b
++ = (p
& 7) + '0';
754 if (sep
[0] && sep
[1])
757 #else /* WINNT_NATIVE */
758 *b
++ = CHAR
& sep
[1];
759 #endif /* !WINNT_NATIVE */
761 return buf
; /* should check for overflow */