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 */