Vendor import of tcsh 6.15.00
[dragonfly.git] / contrib / tcsh-6 / ed.xmap.c
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.
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
91 RCSID("$tcsh: ed.xmap.c,v 3.36 2006/11/29 22:30:09 christos Exp $")
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  */
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;
113
114 static XmapNode *Xmap = NULL;   /* the current Xmap */
115
116
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);
127
128
129 XmapVal *
130 XmapCmd(int cmd)
131 {
132     static XmapVal xm;
133     xm.cmd = (KEYCMD) cmd;
134     return &xm;
135 }
136
137 XmapVal *
138 XmapStr(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  */
150 void
151 ResetXmap(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  */
164 int
165 GetXkey(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  */
174 static int
175 TraverseMap(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
212 void
213 AddXkey(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) {
219         xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
220         return;
221     }
222
223     if (ntype == XK_CMD && val->cmd == F_XKEY) {
224         xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
225         return;
226     }
227
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 */
231
232     /* Now recurse through Xmap */
233     (void) TryNode(Xmap, &cs, val, ntype);      
234     return;
235 }
236
237 static int
238 TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
239 {
240     /*
241      * Find a node that matches *string or allocate a new one
242      */
243     if (ptr->ch != *(str->buf)) {
244         XmapNode *xm;
245
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;
252     }
253
254     str->buf++;
255     str->len--;
256     if (str->len == 0) {
257         size_t len;
258
259         /* we're there */
260         if (ptr->next != NULL) {
261             PutFreeNode(ptr->next);     /* lose longer Xkeys with this prefix */
262             ptr->next = NULL;
263         }
264
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;
277         }
278
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;
293         }
294     }
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);
300     }
301     return (0);
302 }
303
304 void
305 ClearXkey(KEYCMD *map, const CStr *in)
306 {
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);
312 }
313
314 int
315 DeleteXkey(const CStr *Xkey)
316 {
317     CStr s;
318
319     s = *Xkey;
320     if (s.len == 0) {
321         xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
322         return (-1);
323     }
324
325     if (Xmap == NULL)
326         return (0);
327
328     (void) TryDeleteNode(&Xmap, &s);
329     return (0);
330 }
331
332 /* Destroys str */
333 static int
334 TryDeleteNode(XmapNode **inptr, CStr *str)
335 {
336     XmapNode *ptr;
337
338     ptr = *inptr;
339     /*
340      * Find a node that matches *string or allocate a new one
341      */
342     if (ptr->ch != *(str->buf)) {
343         XmapNode *xm;
344
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;
352     }
353
354     str->buf++;
355     str->len--;
356
357     if (str->len == 0) {
358         /* we're there */
359         *inptr = ptr->sibling;
360         ptr->sibling = NULL;
361         PutFreeNode(ptr);
362         return (1);
363     }
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);
371     }
372     else {
373         return (0);
374     }
375 }
376
377 /* PutFreeNode():
378  *      Puts a tree of nodes onto free list using free(3).
379  */
380 static void
381 PutFreeNode(XmapNode *ptr)
382 {
383     if (ptr == NULL)
384         return;
385
386     if (ptr->next != NULL) {
387         PutFreeNode(ptr->next);
388         ptr->next = NULL;
389     }
390
391     PutFreeNode(ptr->sibling);
392
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;
404     }
405     xfree(ptr);
406 }
407
408
409 /* GetFreeNode():
410  *      Returns pointer to an XmapNode for ch.
411  */
412 static XmapNode *
413 GetFreeNode(CStr *ch)
414 {
415     XmapNode *ptr;
416
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);
425 }
426  
427
428 /* PrintXKey():
429  *      Print the binding associated with Xkey key.
430  *      Print entire Xmap if null
431  */
432 void
433 PrintXkey(const CStr *key)
434 {
435     struct Strbuf buf = Strbuf_INIT;
436     CStr cs;
437
438     if (key) {
439         cs.buf = key->buf;
440         cs.len = key->len;
441     }
442     else {
443         cs.buf = STRNULL;
444         cs.len = 0;
445     }
446     /* do nothing if Xmap is empty and null key specified */
447     if (Xmap == NULL && cs.len == 0)
448         return;
449
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);
456 }
457
458 /* Lookup():
459  *      look for the string starting at node ptr.
460  *      Print if last node
461  */
462 static int
463 Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
464 {
465     if (ptr == NULL)
466         return (-1);            /* cannot have null ptr */
467
468     if (str->len == 0) {
469         /* no more chars in string.  Enumerate from here. */
470         Enumerate(buf, ptr);
471         return (0);
472     }
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));
484             }
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);
492                 }
493                 else
494                     return (-1);/* mismatch -- string still has chars */
495             }
496         }
497         else {
498             /* no match found try sibling */
499             if (ptr->sibling)
500                 return (Lookup(buf, str, ptr->sibling));
501             else
502                 return (-1);
503         }
504     }
505 }
506
507 static void
508 Enumerate(struct Strbuf *buf, const XmapNode *ptr)
509 {
510     size_t old_len;
511
512     if (ptr == NULL) {
513 #ifdef DEBUG_EDIT
514         xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
515 #endif
516         return;
517     }
518
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);
526     }
527     else
528         Enumerate(buf, ptr->next);
529
530     /* go to sibling if there is one */
531     if (ptr->sibling) {
532         buf->len = old_len;
533         Enumerate(buf, ptr->sibling);
534     }
535 }
536
537
538 /* PrintOne():
539  *      Print the specified key and its associated
540  *      function specified by val
541  */
542 void
543 printOne(const Char *key, const XmapVal *val, int ntype)
544 {
545     struct KeyFuncs *fp;
546     static const char *fmt = "%s\n";
547
548     xprintf("%-15S-> ", key);
549     if (val != NULL)
550         switch (ntype) {
551         case XK_STR:
552         case XK_EXE: {
553             unsigned char *p;
554
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;
560         }
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;
569         }
570     else
571         xprintf(fmt, CGETS(9, 7, "no input"));
572 }
573
574 static void
575 unparsech(struct Strbuf *buf, Char ch)
576 {
577     if (ch == 0) {
578         Strbuf_append1(buf, '^');
579         Strbuf_append1(buf, '@');
580     }
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
591     }
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);
600     }
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');
606     }
607 }
608
609 eChar
610 parseescape(const Char **ptr)
611 {
612     const Char *p;
613     Char c;
614
615     p = *ptr;
616
617     if ((p[1] & CHAR) == 0) {
618         xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
619         return CHAR_ERR;
620     }
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':
659             {
660                 int cnt, val;
661                 Char ch;
662
663                 for (cnt = 0, val = 0; cnt < 3; cnt++) {
664                     ch = *p++ & CHAR;
665                     if (ch < '0' || ch > '7') {
666                         p--;
667                         break;
668                     }
669                     val = (val << 3) | (ch - '0');
670                 }
671                 if ((val & ~0xff) != 0) {
672                     xprintf(CGETS(9, 9,
673                             "Octal constant does not fit in a char.\n"));
674                     return 0;
675                 }
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;
683             }
684             break;
685         default:
686             c = *p;
687             break;
688         }
689     }
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
701     }
702     else
703         c = *p;
704     *ptr = p;
705     return (c);
706 }
707
708
709 unsigned char *
710 unparsestring(const CStr *str, const Char *sep)
711 {
712     unsigned char *buf, *b;
713     Char   p;
714     int l;
715
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 */
725
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
738         }
739         else if (p == '^' || p == '\\') {
740             *b++ = '\\';
741             *b++ = (unsigned char) p;
742         }
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';
750         }
751     }
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 */
760 }