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("%-15S-> ", 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 parseescape(const Char **ptr)
615 if ((p[1] & CHAR) == 0) {
616 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
619 if ((*p & CHAR) == '\\') {
623 c = CTL_ESC('\007'); /* Bell */
626 c = CTL_ESC('\010'); /* Backspace */
629 c = CTL_ESC('\033'); /* Escape */
632 c = CTL_ESC('\014'); /* Form Feed */
635 c = CTL_ESC('\012'); /* New Line */
638 c = CTL_ESC('\015'); /* Carriage Return */
641 c = CTL_ESC('\011'); /* Horizontal Tab */
644 c = CTL_ESC('\013'); /* Vertical Tab */
661 for (cnt = 0, val = 0; cnt < 3; cnt++) {
663 if (ch < '0' || ch > '7') {
667 val = (val << 3) | (ch - '0');
669 if ((val & ~0xff) != 0) {
670 xprintf("%s", CGETS(9, 9,
671 "Octal constant does not fit in a char.\n"));
675 if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
676 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
677 "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
688 else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
689 strchr("@^_?\\|[{]}", p[1] & CHAR))) {
692 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
694 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
695 if (adrof(STRwarnebcdic))
696 xprintf(/*CGETS(9, 9, no NLS-String yet!*/
697 "Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
708 unparsestring(const CStr *str, const Char *sep)
710 unsigned char *buf, *b;
714 /* Worst-case is "\uuu" or result of wctomb() for each char from str */
715 buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX));
720 #else /* WINNT_NATIVE */
721 *b++ = CHAR & sep[0];
722 #endif /* !WINNT_NATIVE */
724 for (l = 0; l < str->len; l++) {
728 if (p == CTL_ESC('\177'))
732 *b++ = (unsigned char) (p | 0100);
734 *b++ = _toebcdic[_toascii[p]|0100];
737 else if (p == '^' || p == '\\') {
739 *b++ = (unsigned char) p;
741 else if (p == ' ' || (Isprint(p) && !Isspace(p)))
742 b += one_wctomb((char *)b, p);
745 *b++ = ((p >> 6) & 7) + '0';
746 *b++ = ((p >> 3) & 7) + '0';
747 *b++ = (p & 7) + '0';
750 if (sep[0] && sep[1])
753 #else /* WINNT_NATIVE */
754 *b++ = CHAR & sep[1];
755 #endif /* !WINNT_NATIVE */
757 return buf; /* should check for overflow */