Sync times() with FreeBSD:
[dragonfly.git] / contrib / tcsh-6 / ed.xmap.c
blob866bc83233a0b0ad45abab97b06272cf665c169a
1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.36 2006/11/29 22:30:09 christos Exp $ */
2 /*
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);
15 * CStr *ch;
16 * XmapVal *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);
25 * CStr *Xkey;
26 * XmapVal *val;
27 * int 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);
35 * CStr *Xkey;
37 * Delete the Xkey and all longer Xkeys staring with Xkey, if
38 * they exists.
40 * Warning:
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.
46 * void ResetXmap();
48 * Removes all entries from Xmap and resets the defaults.
50 * void PrintXkey(Xkey);
51 * CStr *Xkey;
53 * Prints all extended keys prefixed by Xkey and their associated
54 * commands.
56 * Restrictions:
57 * -------------
58 * 1) It is not possible to have one Xkey that is a
59 * substring of another.
61 /*-
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
67 * are met:
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
87 * SUCH DAMAGE.
89 #include "sh.h"
91 RCSID("$tcsh: ed.xmap.c,v 3.36 2006/11/29 22:30:09 christos Exp $")
93 #include "ed.h"
94 #include "ed.defns.h"
96 #ifndef NULL
97 #define NULL 0
98 #endif
100 /* Internal Data types and declarations */
102 /* The Nodes of the Xmap. The Xmap is a linked list of these node
103 * elements
105 typedef struct Xmapnode {
106 Char ch; /* single character of Xkey */
107 int type;
108 XmapVal val; /* command code or pointer to string, if this
109 * is a leaf */
110 struct Xmapnode *next; /* ptr to next char of this Xkey */
111 struct Xmapnode *sibling; /* ptr to another Xkey with same prefix */
112 } XmapNode;
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 *,
124 const XmapNode *);
125 static void Enumerate (struct Strbuf *, const XmapNode *);
126 static void unparsech (struct Strbuf *, Char);
129 XmapVal *
130 XmapCmd(int cmd)
132 static XmapVal xm;
133 xm.cmd = (KEYCMD) cmd;
134 return &xm;
137 XmapVal *
138 XmapStr(CStr *str)
140 static XmapVal xm;
141 xm.str.len = str->len;
142 xm.str.buf = str->buf;
143 return &xm;
146 /* ResetXmap():
147 * Takes all nodes on Xmap and puts them on free list. Then
148 * initializes Xmap with arrow keys
150 void
151 ResetXmap(void)
153 PutFreeNode(Xmap);
154 Xmap = NULL;
156 DefaultArrowKeys();
157 return;
161 /* GetXkey():
162 * Calls the recursive function with entry point Xmap
165 GetXkey(CStr *ch, XmapVal *val)
167 return (TraverseMap(Xmap, ch, val));
170 /* TraverseMap():
171 * recursively traverses node in tree until match or mismatch is
172 * found. May read in more characters.
174 static int
175 TraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val)
177 Char tch;
179 if (ptr->ch == *(ch->buf)) {
180 /* match found */
181 if (ptr->next) {
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 */
187 *(ch->buf) = tch;
188 return (TraverseMap(ptr->next, ch, val));
190 else {
191 *val = ptr->val;
192 if (ptr->type != XK_CMD)
193 *(ch->buf) = '\0';
194 return ptr->type;
197 else {
198 /* no match found here */
199 if (ptr->sibling) {
200 /* try next sibling */
201 return (TraverseMap(ptr->sibling, ch, val));
203 else {
204 /* no next sibling -- mismatch */
205 val->str.buf = NULL;
206 val->str.len = 0;
207 return XK_STR;
212 void
213 AddXkey(const CStr *Xkey, XmapVal *val, int ntype)
215 CStr cs;
216 cs.buf = Xkey->buf;
217 cs.len = Xkey->len;
218 if (Xkey->len == 0) {
219 xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
220 return;
223 if (ntype == XK_CMD && val->cmd == F_XKEY) {
224 xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
225 return;
228 if (Xmap == NULL)
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);
234 return;
237 static int
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)) {
244 XmapNode *xm;
246 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
247 if (xm->sibling->ch == *(str->buf))
248 break;
249 if (xm->sibling == NULL)
250 xm->sibling = GetFreeNode(str); /* setup new node */
251 ptr = xm->sibling;
254 str->buf++;
255 str->len--;
256 if (str->len == 0) {
257 size_t len;
259 /* we're there */
260 if (ptr->next != NULL) {
261 PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */
262 ptr->next = NULL;
265 switch (ptr->type) {
266 case XK_STR:
267 case XK_EXE:
268 xfree(ptr->val.str.buf);
269 ptr->val.str.len = 0;
270 break;
271 case XK_NOD:
272 case XK_CMD:
273 break;
274 default:
275 abort();
276 break;
279 switch (ptr->type = ntype) {
280 case XK_CMD:
281 ptr->val = *val;
282 break;
283 case XK_STR:
284 case XK_EXE:
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);
289 break;
290 default:
291 abort();
292 break;
295 else {
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);
301 return (0);
304 void
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)
317 CStr s;
319 s = *Xkey;
320 if (s.len == 0) {
321 xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
322 return (-1);
325 if (Xmap == NULL)
326 return (0);
328 (void) TryDeleteNode(&Xmap, &s);
329 return (0);
332 /* Destroys str */
333 static int
334 TryDeleteNode(XmapNode **inptr, CStr *str)
336 XmapNode *ptr;
338 ptr = *inptr;
340 * Find a node that matches *string or allocate a new one
342 if (ptr->ch != *(str->buf)) {
343 XmapNode *xm;
345 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
346 if (xm->sibling->ch == *(str->buf))
347 break;
348 if (xm->sibling == NULL)
349 return (0);
350 inptr = &xm->sibling;
351 ptr = xm->sibling;
354 str->buf++;
355 str->len--;
357 if (str->len == 0) {
358 /* we're there */
359 *inptr = ptr->sibling;
360 ptr->sibling = NULL;
361 PutFreeNode(ptr);
362 return (1);
364 else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
365 if (ptr->next != NULL)
366 return (0);
367 *inptr = ptr->sibling;
368 ptr->sibling = NULL;
369 PutFreeNode(ptr);
370 return (1);
372 else {
373 return (0);
377 /* PutFreeNode():
378 * Puts a tree of nodes onto free list using free(3).
380 static void
381 PutFreeNode(XmapNode *ptr)
383 if (ptr == NULL)
384 return;
386 if (ptr->next != NULL) {
387 PutFreeNode(ptr->next);
388 ptr->next = NULL;
391 PutFreeNode(ptr->sibling);
393 switch (ptr->type) {
394 case XK_CMD:
395 case XK_NOD:
396 break;
397 case XK_EXE:
398 case XK_STR:
399 xfree(ptr->val.str.buf);
400 break;
401 default:
402 abort();
403 break;
405 xfree(ptr);
409 /* GetFreeNode():
410 * Returns pointer to an XmapNode for ch.
412 static XmapNode *
413 GetFreeNode(CStr *ch)
415 XmapNode *ptr;
417 ptr = xmalloc(sizeof(XmapNode));
418 ptr->ch = ch->buf[0];
419 ptr->type = XK_NOD;
420 ptr->val.str.buf = NULL;
421 ptr->val.str.len = 0;
422 ptr->next = NULL;
423 ptr->sibling = NULL;
424 return (ptr);
428 /* PrintXKey():
429 * Print the binding associated with Xkey key.
430 * Print entire Xmap if null
432 void
433 PrintXkey(const CStr *key)
435 struct Strbuf buf = Strbuf_INIT;
436 CStr cs;
438 if (key) {
439 cs.buf = key->buf;
440 cs.len = key->len;
442 else {
443 cs.buf = STRNULL;
444 cs.len = 0;
446 /* do nothing if Xmap is empty and null key specified */
447 if (Xmap == NULL && cs.len == 0)
448 return;
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);
455 cleanup_until(&buf);
458 /* Lookup():
459 * look for the string starting at node ptr.
460 * Print if last node
462 static int
463 Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
465 if (ptr == NULL)
466 return (-1); /* cannot have null ptr */
468 if (str->len == 0) {
469 /* no more chars in string. Enumerate from here. */
470 Enumerate(buf, ptr);
471 return (0);
473 else {
474 /* If match put this char into buf. Recurse */
475 if (ptr->ch == *(str->buf)) {
476 /* match found */
477 unparsech(buf, ptr->ch);
478 if (ptr->next != NULL) {
479 /* not yet at leaf */
480 CStr tstr;
481 tstr.buf = str->buf + 1;
482 tstr.len = str->len - 1;
483 return (Lookup(buf, &tstr, ptr->next));
485 else {
486 /* next node is null so key should be complete */
487 if (str->len == 1) {
488 Strbuf_append1(buf, '"');
489 Strbuf_terminate(buf);
490 printOne(buf->s, &ptr->val, ptr->type);
491 return (0);
493 else
494 return (-1);/* mismatch -- string still has chars */
497 else {
498 /* no match found try sibling */
499 if (ptr->sibling)
500 return (Lookup(buf, str, ptr->sibling));
501 else
502 return (-1);
507 static void
508 Enumerate(struct Strbuf *buf, const XmapNode *ptr)
510 size_t old_len;
512 if (ptr == NULL) {
513 #ifdef DEBUG_EDIT
514 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
515 #endif
516 return;
519 old_len = buf->len;
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);
527 else
528 Enumerate(buf, ptr->next);
530 /* go to sibling if there is one */
531 if (ptr->sibling) {
532 buf->len = old_len;
533 Enumerate(buf, ptr->sibling);
538 /* PrintOne():
539 * Print the specified key and its associated
540 * function specified by val
542 void
543 printOne(const Char *key, const XmapVal *val, int ntype)
545 struct KeyFuncs *fp;
546 static const char *fmt = "%s\n";
548 xprintf("%-15S-> ", key);
549 if (val != NULL)
550 switch (ntype) {
551 case XK_STR:
552 case XK_EXE: {
553 unsigned char *p;
555 p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
556 cleanup_push(p, xfree);
557 xprintf(fmt, p);
558 cleanup_until(p);
559 break;
561 case XK_CMD:
562 for (fp = FuncNames; fp->name; fp++)
563 if (val->cmd == fp->func)
564 xprintf(fmt, fp->name);
565 break;
566 default:
567 abort();
568 break;
570 else
571 xprintf(fmt, CGETS(9, 7, "no input"));
574 static void
575 unparsech(struct Strbuf *buf, Char ch)
577 if (ch == 0) {
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, '?');
585 else
586 #ifdef IS_ASCII
587 Strbuf_append1(buf, ch | 0100);
588 #else
589 Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
590 #endif
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);
601 else {
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');
609 eChar
610 parseescape(const Char **ptr)
612 const Char *p;
613 Char c;
615 p = *ptr;
617 if ((p[1] & CHAR) == 0) {
618 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
619 return CHAR_ERR;
621 if ((*p & CHAR) == '\\') {
622 p++;
623 switch (*p & CHAR) {
624 case 'a':
625 c = CTL_ESC('\007'); /* Bell */
626 break;
627 case 'b':
628 c = CTL_ESC('\010'); /* Backspace */
629 break;
630 case 'e':
631 c = CTL_ESC('\033'); /* Escape */
632 break;
633 case 'f':
634 c = CTL_ESC('\014'); /* Form Feed */
635 break;
636 case 'n':
637 c = CTL_ESC('\012'); /* New Line */
638 break;
639 case 'r':
640 c = CTL_ESC('\015'); /* Carriage Return */
641 break;
642 case 't':
643 c = CTL_ESC('\011'); /* Horizontal Tab */
644 break;
645 case 'v':
646 c = CTL_ESC('\013'); /* Vertical Tab */
647 break;
648 case '\\':
649 c = '\\';
650 break;
651 case '0':
652 case '1':
653 case '2':
654 case '3':
655 case '4':
656 case '5':
657 case '6':
658 case '7':
660 int cnt, val;
661 Char ch;
663 for (cnt = 0, val = 0; cnt < 3; cnt++) {
664 ch = *p++ & CHAR;
665 if (ch < '0' || ch > '7') {
666 p--;
667 break;
669 val = (val << 3) | (ch - '0');
671 if ((val & ~0xff) != 0) {
672 xprintf(CGETS(9, 9,
673 "Octal constant does not fit in a char.\n"));
674 return 0;
676 #ifndef IS_ASCII
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/*)*/);
680 #endif
681 c = (Char) val;
682 --p;
684 break;
685 default:
686 c = *p;
687 break;
690 else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
691 strchr("@^_?\\|[{]}", p[1] & CHAR))) {
692 p++;
693 #ifdef IS_ASCII
694 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
695 #else
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 /*)*/);
700 #endif
702 else
703 c = *p;
704 *ptr = p;
705 return (c);
709 unsigned char *
710 unparsestring(const CStr *str, const Char *sep)
712 unsigned char *buf, *b;
713 Char p;
714 int l;
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));
718 b = buf;
719 if (sep[0])
720 #ifndef WINNT_NATIVE
721 *b++ = sep[0];
722 #else /* WINNT_NATIVE */
723 *b++ = CHAR & sep[0];
724 #endif /* !WINNT_NATIVE */
726 for (l = 0; l < str->len; l++) {
727 p = str->buf[l];
728 if (Iscntrl(p)) {
729 *b++ = '^';
730 if (p == CTL_ESC('\177'))
731 *b++ = '?';
732 else
733 #ifdef IS_ASCII
734 *b++ = (unsigned char) (p | 0100);
735 #else
736 *b++ = _toebcdic[_toascii[p]|0100];
737 #endif
739 else if (p == '^' || p == '\\') {
740 *b++ = '\\';
741 *b++ = (unsigned char) p;
743 else if (p == ' ' || (Isprint(p) && !Isspace(p)))
744 b += one_wctomb((char *)b, p & CHAR);
745 else {
746 *b++ = '\\';
747 *b++ = ((p >> 6) & 7) + '0';
748 *b++ = ((p >> 3) & 7) + '0';
749 *b++ = (p & 7) + '0';
752 if (sep[0] && sep[1])
753 #ifndef WINNT_NATIVE
754 *b++ = sep[1];
755 #else /* WINNT_NATIVE */
756 *b++ = CHAR & sep[1];
757 #endif /* !WINNT_NATIVE */
758 *b++ = 0;
759 return buf; /* should check for overflow */