Import tcsh-6.17.00
[dragonfly.git] / contrib / tcsh-6 / ed.xmap.c
CommitLineData
57e3f2b5 1/* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $ */
7d8fb588
MS
2/*
3 * ed.xmap.c: This module contains the procedures for maintaining
4 * the extended-key map.
5 *
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.
13 *
14 * int GetXkey(ch,val);
15 * CStr *ch;
16 * XmapVal *val;
17 *
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.
23 *
24 * void AddXkey(Xkey, val, ntype);
25 * CStr *Xkey;
26 * XmapVal *val;
27 * int ntype;
28 *
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.
33 *
34 * int DeleteXkey(Xkey);
35 * CStr *Xkey;
36 *
37 * Delete the Xkey and all longer Xkeys staring with Xkey, if
38 * they exists.
39 *
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.
45 *
46 * void ResetXmap();
47 *
48 * Removes all entries from Xmap and resets the defaults.
49 *
50 * void PrintXkey(Xkey);
51 * CStr *Xkey;
52 *
53 * Prints all extended keys prefixed by Xkey and their associated
54 * commands.
55 *
56 * Restrictions:
57 * -------------
58 * 1) It is not possible to have one Xkey that is a
59 * substring of another.
60 */
61/*-
62 * Copyright (c) 1980, 1991 The Regents of the University of California.
63 * All rights reserved.
64 *
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.
76 *
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.
88 */
89#include "sh.h"
90
57e3f2b5 91RCSID("$tcsh: ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $")
7d8fb588
MS
92
93#include "ed.h"
94#include "ed.defns.h"
95
96#ifndef NULL
97#define NULL 0
98#endif
99
100/* Internal Data types and declarations */
101
102/* The Nodes of the Xmap. The Xmap is a linked list of these node
103 * elements
104 */
105typedef 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;
113
114static XmapNode *Xmap = NULL; /* the current Xmap */
115
116
117/* Some declarations of procedures */
118static int TraverseMap (XmapNode *, CStr *, XmapVal *);
119static int TryNode (XmapNode *, CStr *, XmapVal *, int);
120static XmapNode *GetFreeNode (CStr *);
121static void PutFreeNode (XmapNode *);
122static int TryDeleteNode (XmapNode **, CStr *);
123static int Lookup (struct Strbuf *, const CStr *,
124 const XmapNode *);
125static void Enumerate (struct Strbuf *, const XmapNode *);
126static void unparsech (struct Strbuf *, Char);
127
128
129XmapVal *
130XmapCmd(int cmd)
131{
132 static XmapVal xm;
133 xm.cmd = (KEYCMD) cmd;
134 return &xm;
135}
136
137XmapVal *
138XmapStr(CStr *str)
139{
140 static XmapVal xm;
141 xm.str.len = str->len;
142 xm.str.buf = str->buf;
143 return &xm;
144}
145
146/* ResetXmap():
147 * Takes all nodes on Xmap and puts them on free list. Then
148 * initializes Xmap with arrow keys
149 */
150void
151ResetXmap(void)
152{
153 PutFreeNode(Xmap);
154 Xmap = NULL;
155
156 DefaultArrowKeys();
157 return;
158}
159
160
161/* GetXkey():
162 * Calls the recursive function with entry point Xmap
163 */
164int
165GetXkey(CStr *ch, XmapVal *val)
166{
167 return (TraverseMap(Xmap, ch, val));
168}
169
170/* TraverseMap():
171 * recursively traverses node in tree until match or mismatch is
172 * found. May read in more characters.
173 */
174static int
175TraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val)
176{
177 Char tch;
178
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 */
186 }
187 *(ch->buf) = tch;
188 return (TraverseMap(ptr->next, ch, val));
189 }
190 else {
191 *val = ptr->val;
192 if (ptr->type != XK_CMD)
193 *(ch->buf) = '\0';
194 return ptr->type;
195 }
196 }
197 else {
198 /* no match found here */
199 if (ptr->sibling) {
200 /* try next sibling */
201 return (TraverseMap(ptr->sibling, ch, val));
202 }
203 else {
204 /* no next sibling -- mismatch */
205 val->str.buf = NULL;
206 val->str.len = 0;
207 return XK_STR;
208 }
209 }
210}
211
212void
213AddXkey(const CStr *Xkey, XmapVal *val, int ntype)
214{
215 CStr cs;
216 cs.buf = Xkey->buf;
217 cs.len = Xkey->len;
218 if (Xkey->len == 0) {
57e3f2b5 219 xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
7d8fb588
MS
220 return;
221 }
222
223 if (ntype == XK_CMD && val->cmd == F_XKEY) {
57e3f2b5
SS
224 xprintf("%s",
225 CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
7d8fb588
MS
226 return;
227 }
228
229 if (Xmap == NULL)
230 /* tree is initially empty. Set up new node to match Xkey[0] */
231 Xmap = GetFreeNode(&cs); /* it is properly initialized */
232
233 /* Now recurse through Xmap */
234 (void) TryNode(Xmap, &cs, val, ntype);
235 return;
236}
237
238static int
239TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
240{
241 /*
242 * Find a node that matches *string or allocate a new one
243 */
244 if (ptr->ch != *(str->buf)) {
245 XmapNode *xm;
246
247 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
248 if (xm->sibling->ch == *(str->buf))
249 break;
250 if (xm->sibling == NULL)
251 xm->sibling = GetFreeNode(str); /* setup new node */
252 ptr = xm->sibling;
253 }
254
255 str->buf++;
256 str->len--;
257 if (str->len == 0) {
258 size_t len;
259
260 /* we're there */
261 if (ptr->next != NULL) {
262 PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */
263 ptr->next = NULL;
264 }
265
266 switch (ptr->type) {
267 case XK_STR:
268 case XK_EXE:
269 xfree(ptr->val.str.buf);
270 ptr->val.str.len = 0;
271 break;
272 case XK_NOD:
273 case XK_CMD:
274 break;
275 default:
276 abort();
277 break;
278 }
279
280 switch (ptr->type = ntype) {
281 case XK_CMD:
282 ptr->val = *val;
283 break;
284 case XK_STR:
285 case XK_EXE:
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);
290 break;
291 default:
292 abort();
293 break;
294 }
295 }
296 else {
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);
301 }
302 return (0);
303}
304
305void
306ClearXkey(KEYCMD *map, const CStr *in)
307{
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);
313}
314
315int
316DeleteXkey(const CStr *Xkey)
317{
318 CStr s;
319
320 s = *Xkey;
321 if (s.len == 0) {
57e3f2b5
SS
322 xprintf("%s",
323 CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
7d8fb588
MS
324 return (-1);
325 }
326
327 if (Xmap == NULL)
328 return (0);
329
330 (void) TryDeleteNode(&Xmap, &s);
331 return (0);
332}
333
334/* Destroys str */
335static int
336TryDeleteNode(XmapNode **inptr, CStr *str)
337{
338 XmapNode *ptr;
339
340 ptr = *inptr;
341 /*
342 * Find a node that matches *string or allocate a new one
343 */
344 if (ptr->ch != *(str->buf)) {
345 XmapNode *xm;
346
347 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
348 if (xm->sibling->ch == *(str->buf))
349 break;
350 if (xm->sibling == NULL)
351 return (0);
352 inptr = &xm->sibling;
353 ptr = xm->sibling;
354 }
355
356 str->buf++;
357 str->len--;
358
359 if (str->len == 0) {
360 /* we're there */
361 *inptr = ptr->sibling;
362 ptr->sibling = NULL;
363 PutFreeNode(ptr);
364 return (1);
365 }
366 else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
367 if (ptr->next != NULL)
368 return (0);
369 *inptr = ptr->sibling;
370 ptr->sibling = NULL;
371 PutFreeNode(ptr);
372 return (1);
373 }
374 else {
375 return (0);
376 }
377}
378
379/* PutFreeNode():
380 * Puts a tree of nodes onto free list using free(3).
381 */
382static void
383PutFreeNode(XmapNode *ptr)
384{
385 if (ptr == NULL)
386 return;
387
388 if (ptr->next != NULL) {
389 PutFreeNode(ptr->next);
390 ptr->next = NULL;
391 }
392
393 PutFreeNode(ptr->sibling);
394
395 switch (ptr->type) {
396 case XK_CMD:
397 case XK_NOD:
398 break;
399 case XK_EXE:
400 case XK_STR:
401 xfree(ptr->val.str.buf);
402 break;
403 default:
404 abort();
405 break;
406 }
407 xfree(ptr);
408}
409
410
411/* GetFreeNode():
412 * Returns pointer to an XmapNode for ch.
413 */
414static XmapNode *
415GetFreeNode(CStr *ch)
416{
417 XmapNode *ptr;
418
419 ptr = xmalloc(sizeof(XmapNode));
420 ptr->ch = ch->buf[0];
421 ptr->type = XK_NOD;
422 ptr->val.str.buf = NULL;
423 ptr->val.str.len = 0;
424 ptr->next = NULL;
425 ptr->sibling = NULL;
426 return (ptr);
427}
428
429
430/* PrintXKey():
431 * Print the binding associated with Xkey key.
432 * Print entire Xmap if null
433 */
434void
435PrintXkey(const CStr *key)
436{
437 struct Strbuf buf = Strbuf_INIT;
438 CStr cs;
439
440 if (key) {
441 cs.buf = key->buf;
442 cs.len = key->len;
443 }
444 else {
445 cs.buf = STRNULL;
446 cs.len = 0;
447 }
448 /* do nothing if Xmap is empty and null key specified */
449 if (Xmap == NULL && cs.len == 0)
450 return;
451
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);
457 cleanup_until(&buf);
458}
459
460/* Lookup():
461 * look for the string starting at node ptr.
462 * Print if last node
463 */
464static int
465Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
466{
467 if (ptr == NULL)
468 return (-1); /* cannot have null ptr */
469
470 if (str->len == 0) {
471 /* no more chars in string. Enumerate from here. */
472 Enumerate(buf, ptr);
473 return (0);
474 }
475 else {
476 /* If match put this char into buf. Recurse */
477 if (ptr->ch == *(str->buf)) {
478 /* match found */
479 unparsech(buf, ptr->ch);
480 if (ptr->next != NULL) {
481 /* not yet at leaf */
482 CStr tstr;
483 tstr.buf = str->buf + 1;
484 tstr.len = str->len - 1;
485 return (Lookup(buf, &tstr, ptr->next));
486 }
487 else {
488 /* next node is null so key should be complete */
489 if (str->len == 1) {
490 Strbuf_append1(buf, '"');
491 Strbuf_terminate(buf);
492 printOne(buf->s, &ptr->val, ptr->type);
493 return (0);
494 }
495 else
496 return (-1);/* mismatch -- string still has chars */
497 }
498 }
499 else {
500 /* no match found try sibling */
501 if (ptr->sibling)
502 return (Lookup(buf, str, ptr->sibling));
503 else
504 return (-1);
505 }
506 }
507}
508
509static void
510Enumerate(struct Strbuf *buf, const XmapNode *ptr)
511{
512 size_t old_len;
513
514 if (ptr == NULL) {
515#ifdef DEBUG_EDIT
516 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
517#endif
518 return;
519 }
520
521 old_len = buf->len;
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);
528 }
529 else
530 Enumerate(buf, ptr->next);
531
532 /* go to sibling if there is one */
533 if (ptr->sibling) {
534 buf->len = old_len;
535 Enumerate(buf, ptr->sibling);
536 }
537}
538
539
540/* PrintOne():
541 * Print the specified key and its associated
542 * function specified by val
543 */
544void
545printOne(const Char *key, const XmapVal *val, int ntype)
546{
547 struct KeyFuncs *fp;
548 static const char *fmt = "%s\n";
549
550 xprintf("%-15S-> ", key);
551 if (val != NULL)
552 switch (ntype) {
553 case XK_STR:
554 case XK_EXE: {
555 unsigned char *p;
556
557 p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
558 cleanup_push(p, xfree);
559 xprintf(fmt, p);
560 cleanup_until(p);
561 break;
562 }
563 case XK_CMD:
564 for (fp = FuncNames; fp->name; fp++)
565 if (val->cmd == fp->func)
566 xprintf(fmt, fp->name);
567 break;
568 default:
569 abort();
570 break;
571 }
572 else
573 xprintf(fmt, CGETS(9, 7, "no input"));
574}
575
576static void
577unparsech(struct Strbuf *buf, Char ch)
578{
579 if (ch == 0) {
580 Strbuf_append1(buf, '^');
581 Strbuf_append1(buf, '@');
582 }
583 else if (Iscntrl(ch)) {
584 Strbuf_append1(buf, '^');
585 if (ch == CTL_ESC('\177'))
586 Strbuf_append1(buf, '?');
587 else
588#ifdef IS_ASCII
589 Strbuf_append1(buf, ch | 0100);
590#else
591 Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
592#endif
593 }
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);
602 }
603 else {
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');
608 }
609}
610
611eChar
612parseescape(const Char **ptr)
613{
614 const Char *p;
615 Char c;
616
617 p = *ptr;
618
619 if ((p[1] & CHAR) == 0) {
620 xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
621 return CHAR_ERR;
622 }
623 if ((*p & CHAR) == '\\') {
624 p++;
625 switch (*p & CHAR) {
626 case 'a':
627 c = CTL_ESC('\007'); /* Bell */
628 break;
629 case 'b':
630 c = CTL_ESC('\010'); /* Backspace */
631 break;
632 case 'e':
633 c = CTL_ESC('\033'); /* Escape */
634 break;
635 case 'f':
636 c = CTL_ESC('\014'); /* Form Feed */
637 break;
638 case 'n':
639 c = CTL_ESC('\012'); /* New Line */
640 break;
641 case 'r':
642 c = CTL_ESC('\015'); /* Carriage Return */
643 break;
644 case 't':
645 c = CTL_ESC('\011'); /* Horizontal Tab */
646 break;
647 case 'v':
648 c = CTL_ESC('\013'); /* Vertical Tab */
649 break;
650 case '\\':
651 c = '\\';
652 break;
653 case '0':
654 case '1':
655 case '2':
656 case '3':
657 case '4':
658 case '5':
659 case '6':
660 case '7':
661 {
662 int cnt, val;
663 Char ch;
664
665 for (cnt = 0, val = 0; cnt < 3; cnt++) {
666 ch = *p++ & CHAR;
667 if (ch < '0' || ch > '7') {
668 p--;
669 break;
670 }
671 val = (val << 3) | (ch - '0');
672 }
673 if ((val & ~0xff) != 0) {
57e3f2b5 674 xprintf("%s", CGETS(9, 9,
7d8fb588
MS
675 "Octal constant does not fit in a char.\n"));
676 return 0;
677 }
678#ifndef IS_ASCII
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/*)*/);
682#endif
683 c = (Char) val;
684 --p;
685 }
686 break;
687 default:
688 c = *p;
689 break;
690 }
691 }
692 else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
693 strchr("@^_?\\|[{]}", p[1] & CHAR))) {
694 p++;
695#ifdef IS_ASCII
696 c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
697#else
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 /*)*/);
702#endif
703 }
704 else
705 c = *p;
706 *ptr = p;
707 return (c);
708}
709
710
711unsigned char *
712unparsestring(const CStr *str, const Char *sep)
713{
714 unsigned char *buf, *b;
715 Char p;
716 int l;
717
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));
720 b = buf;
721 if (sep[0])
722#ifndef WINNT_NATIVE
723 *b++ = sep[0];
724#else /* WINNT_NATIVE */
725 *b++ = CHAR & sep[0];
726#endif /* !WINNT_NATIVE */
727
728 for (l = 0; l < str->len; l++) {
729 p = str->buf[l];
730 if (Iscntrl(p)) {
731 *b++ = '^';
732 if (p == CTL_ESC('\177'))
733 *b++ = '?';
734 else
735#ifdef IS_ASCII
736 *b++ = (unsigned char) (p | 0100);
737#else
738 *b++ = _toebcdic[_toascii[p]|0100];
739#endif
740 }
741 else if (p == '^' || p == '\\') {
742 *b++ = '\\';
743 *b++ = (unsigned char) p;
744 }
745 else if (p == ' ' || (Isprint(p) && !Isspace(p)))
746 b += one_wctomb((char *)b, p & CHAR);
747 else {
748 *b++ = '\\';
749 *b++ = ((p >> 6) & 7) + '0';
750 *b++ = ((p >> 3) & 7) + '0';
751 *b++ = (p & 7) + '0';
752 }
753 }
754 if (sep[0] && sep[1])
755#ifndef WINNT_NATIVE
756 *b++ = sep[1];
757#else /* WINNT_NATIVE */
758 *b++ = CHAR & sep[1];
759#endif /* !WINNT_NATIVE */
760 *b++ = 0;
761 return buf; /* should check for overflow */
762}