Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libedit / key.c
1 /*-
2  * Copyright (c) 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Christos Zoulas of Cornell University.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #if !defined(lint) && !defined(SCCSID)
38 static char sccsid[] = "@(#)key.c       8.1 (Berkeley) 6/4/93";
39 #endif /* not lint && not SCCSID */
40
41 /*
42  * key.c: This module contains the procedures for maintaining
43  *        the extended-key map.
44  *
45  *      An extended-key (key) is a sequence of keystrokes introduced
46  *      with an sequence introducer and consisting of an arbitrary
47  *      number of characters.  This module maintains a map (the el->el_key.map)
48  *      to convert these extended-key sequences into input strs
49  *      (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
50  *
51  *      Warning:
52  *        If key is a substr of some other keys, then the longer
53  *        keys are lost!!  That is, if the keys "abcd" and "abcef"
54  *        are in el->el_key.map, adding the key "abc" will cause the first two
55  *        definitions to be lost.
56  *
57  *      Restrictions:
58  *      -------------
59  *      1) It is not possible to have one key that is a
60  *         substr of another.
61  */
62 #include "sys.h"
63 #include <string.h>
64 #include <stdlib.h>
65
66 #include "el.h"
67
68 /*
69  * The Nodes of the el->el_key.map.  The el->el_key.map is a linked list
70  * of these node elements
71  */
72 struct key_node_t {
73     char        ch;             /* single character of key              */
74     int         type;           /* node type                            */
75     key_value_t val;            /* command code or pointer to str,      */
76                                 /* if this is a leaf                    */
77     struct key_node_t *next;    /* ptr to next char of this key         */
78     struct key_node_t *sibling; /* ptr to another key with same prefix  */
79 };
80
81 private int            node_trav        __P((EditLine *, key_node_t *, char *,
82                                              key_value_t *));
83 private int            node__try        __P((key_node_t *, char *,
84                                              key_value_t *, int));
85 private key_node_t    *node__get        __P((int));
86 private void           node__put        __P((key_node_t *));
87 private int            node__delete     __P((key_node_t **, char *));
88 private int            node_lookup      __P((EditLine *, char *, key_node_t *,
89                                              int));
90 private int            node_enum        __P((EditLine *, key_node_t *, int));
91 private int            key__decode_char __P((char *, int, int));
92
93 #define KEY_BUFSIZ      EL_BUFSIZ
94
95
96 /* key_init():
97  *      Initialize the key maps
98  */
99 protected int
100 key_init(el)
101     EditLine *el;
102 {
103     el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ);
104     el->el_key.map = NULL;
105     key_reset(el);
106     return 0;
107 }
108
109
110 /* key_end():
111  *      Free the key maps
112  */
113 protected void
114 key_end(el)
115     EditLine *el;
116 {
117     el_free((ptr_t) el->el_key.buf);
118     el->el_key.buf = NULL;
119     /* XXX: provide a function to clear the keys */
120     el->el_key.map = NULL;
121 }
122
123
124 /* key_map_cmd():
125  *      Associate cmd with a key value
126  */
127 protected key_value_t *
128 key_map_cmd(el, cmd)
129     EditLine *el;
130     int cmd;
131 {
132     el->el_key.val.cmd = (el_action_t) cmd;
133     return &el->el_key.val;
134 }
135
136
137 /* key_map_str():
138  *      Associate str with a key value
139  */
140 protected key_value_t *
141 key_map_str(el, str)
142     EditLine *el;
143     char  *str;
144 {
145     el->el_key.val.str = str;
146     return &el->el_key.val;
147 }
148
149
150 /* key_reset():
151  *      Takes all nodes on el->el_key.map and puts them on free list.  Then
152  *      initializes el->el_key.map with arrow keys
153  *      [Always bind the ansi arrow keys?]
154  */
155 protected void
156 key_reset(el)
157     EditLine *el;
158 {
159     node__put(el->el_key.map);
160     el->el_key.map = NULL;
161     return;
162 }
163
164
165 /* key_get():
166  *      Calls the recursive function with entry point el->el_key.map
167  *      Looks up *ch in map and then reads characters until a
168  *      complete match is found or a mismatch occurs. Returns the
169  *      type of the match found (XK_STR, XK_CMD, or XK_EXE).
170  *      Returns NULL in val.str and XK_STR for no match.
171  *      The last character read is returned in *ch.
172  */
173 protected int
174 key_get(el, ch, val)
175     EditLine    *el;
176     char        *ch;
177     key_value_t *val;
178 {
179     return node_trav(el, el->el_key.map, ch, val);
180 }
181
182
183
184 /* key_add():
185  *      Adds key to the el->el_key.map and associates the value in val with it.
186  *      If key is already is in el->el_key.map, the new code is applied to the
187  *      existing key. Ntype specifies if code is a command, an
188  *      out str or a unix command.
189  */
190 protected void
191 key_add(el, key, val, ntype)
192     EditLine    *el;
193     char        *key;
194     key_value_t *val;
195     int          ntype;
196 {
197     if (key[0] == '\0') {
198         (void) fprintf(el->el_errfile,
199                        "key_add: Null extended-key not allowed.\n");
200         return;
201     }
202
203     if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
204         (void) fprintf(el->el_errfile,
205                        "key_add: sequence-lead-in command not allowed\n");
206         return;
207     }
208
209     if (el->el_key.map == NULL)
210         /* tree is initially empty.  Set up new node to match key[0] */
211         el->el_key.map = node__get(key[0]);     /* it is properly initialized */
212
213     /* Now recurse through el->el_key.map */
214     (void) node__try(el->el_key.map, key, val, ntype);
215     return;
216 }
217
218
219 /* key_clear():
220  *
221  */
222 protected void
223 key_clear(el, map, in)
224     EditLine *el;
225     el_action_t *map;
226     char   *in;
227 {
228     if ((map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN) &&
229         ((map == el->el_map.key &&
230           el->el_map.alt[(unsigned char) *in] != ED_SEQUENCE_LEAD_IN) ||
231          (map == el->el_map.alt &&
232           el->el_map.key[(unsigned char) *in] != ED_SEQUENCE_LEAD_IN)))
233         (void) key_delete(el, in);
234 }
235
236
237 /* key_delete():
238  *      Delete the key and all longer keys staring with key, if
239  *      they exists.
240  */
241 protected int
242 key_delete(el, key)
243     EditLine *el;
244     char   *key;
245 {
246     if (key[0] == '\0') {
247         (void) fprintf(el->el_errfile,
248                        "key_delete: Null extended-key not allowed.\n");
249         return -1;
250     }
251
252     if (el->el_key.map == NULL)
253         return 0;
254
255     (void) node__delete(&el->el_key.map, key);
256     return 0;
257 }
258
259
260 /* key_print():
261  *      Print the binding associated with key key.
262  *      Print entire el->el_key.map if null
263  */
264 protected void
265 key_print(el, key)
266     EditLine *el;
267     char   *key;
268 {
269     /* do nothing if el->el_key.map is empty and null key specified */
270     if (el->el_key.map == NULL && *key == 0)
271         return;
272
273     el->el_key.buf[0] =  '"';
274     if (node_lookup(el, key, el->el_key.map, 1) <= -1)
275         /* key is not bound */
276         (void) fprintf(el->el_errfile, "Unbound extended key \"%s\"\n", key);
277     return;
278 }
279
280
281 /* node_trav():
282  *      recursively traverses node in tree until match or mismatch is
283  *      found.  May read in more characters.
284  */
285 private int
286 node_trav(el, ptr, ch, val)
287     EditLine     *el;
288     key_node_t   *ptr;
289     char         *ch;
290     key_value_t  *val;
291 {
292     if (ptr->ch == *ch) {
293         /* match found */
294         if (ptr->next) {
295             /* key not complete so get next char */
296             if (el_getc(el, ch) != 1) { /* if EOF or error */
297                 val->cmd = ED_END_OF_FILE;
298                 return XK_CMD;/* PWP: Pretend we just read an end-of-file */
299             }
300             return node_trav(el, ptr->next, ch, val);
301         }
302         else {
303             *val = ptr->val;
304             if (ptr->type != XK_CMD)
305                 *ch = '\0';
306             return ptr->type;
307         }
308     }
309     else {
310         /* no match found here */
311         if (ptr->sibling) {
312             /* try next sibling */
313             return node_trav(el, ptr->sibling, ch, val);
314         }
315         else {
316             /* no next sibling -- mismatch */
317             val->str = NULL;
318             return XK_STR;
319         }
320     }
321 }
322
323
324 /* node__try():
325  *      Find a node that matches *str or allocate a new one
326  */
327 private int
328 node__try(ptr, str, val, ntype)
329     key_node_t   *ptr;
330     char         *str;
331     key_value_t  *val;
332     int           ntype;
333 {
334     if (ptr->ch != *str) {
335         key_node_t *xm;
336
337         for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
338             if (xm->sibling->ch == *str)
339                 break;
340         if (xm->sibling == NULL)
341             xm->sibling = node__get(*str);      /* setup new node */
342         ptr = xm->sibling;
343     }
344
345     if (*++str == '\0') {
346         /* we're there */
347         if (ptr->next != NULL) {
348             node__put(ptr->next);       /* lose longer keys with this prefix */
349             ptr->next = NULL;
350         }
351         switch (ptr->type) {
352         case XK_CMD:
353         case XK_NOD:
354             break;
355         case XK_STR:
356         case XK_EXE:
357             if (ptr->val.str)
358                 el_free((ptr_t) ptr->val.str);
359             break;
360         default:
361             abort();
362             break;
363         }
364
365         switch (ptr->type = ntype) {
366         case XK_CMD:
367             ptr->val = *val;
368             break;
369         case XK_STR:
370         case XK_EXE:
371             ptr->val.str = strdup(val->str);
372             break;
373         default:
374             abort();
375             break;
376         }
377     }
378     else {
379         /* still more chars to go */
380         if (ptr->next == NULL)
381             ptr->next = node__get(*str);        /* setup new node */
382         (void) node__try(ptr->next, str, val, ntype);
383     }
384     return 0;
385 }
386
387
388 /* node__delete():
389  *      Delete node that matches str
390  */
391 private int
392 node__delete(inptr, str)
393     key_node_t **inptr;
394     char   *str;
395 {
396     key_node_t *ptr;
397     key_node_t *prev_ptr = NULL;
398
399     ptr = *inptr;
400
401     if (ptr->ch != *str) {
402         key_node_t *xm;
403
404         for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
405             if (xm->sibling->ch == *str)
406                 break;
407         if (xm->sibling == NULL)
408             return 0;
409         prev_ptr = xm;
410         ptr = xm->sibling;
411     }
412
413     if (*++str == '\0') {
414         /* we're there */
415         if (prev_ptr == NULL)
416             *inptr = ptr->sibling;
417         else
418             prev_ptr->sibling = ptr->sibling;
419         ptr->sibling = NULL;
420         node__put(ptr);
421         return 1;
422     }
423     else if (ptr->next != NULL && node__delete(&ptr->next, str) == 1) {
424         if (ptr->next != NULL)
425             return 0;
426         if (prev_ptr == NULL)
427             *inptr = ptr->sibling;
428         else
429             prev_ptr->sibling = ptr->sibling;
430         ptr->sibling = NULL;
431         node__put(ptr);
432         return 1;
433     }
434     else {
435         return 0;
436     }
437 }
438
439 /* node__put():
440  *      Puts a tree of nodes onto free list using free(3).
441  */
442 private void
443 node__put(ptr)
444     key_node_t *ptr;
445 {
446     if (ptr == NULL)
447         return;
448
449     if (ptr->next != NULL) {
450         node__put(ptr->next);
451         ptr->next = NULL;
452     }
453
454     node__put(ptr->sibling);
455
456     switch (ptr->type) {
457     case XK_CMD:
458     case XK_NOD:
459         break;
460     case XK_EXE:
461     case XK_STR:
462         if (ptr->val.str != NULL)
463             el_free((ptr_t) ptr->val.str);
464         break;
465     default:
466         abort();
467         break;
468     }
469     el_free((ptr_t) ptr);
470 }
471
472
473 /* node__get():
474  *      Returns pointer to an key_node_t for ch.
475  */
476 private key_node_t *
477 node__get(ch)
478     int    ch;
479 {
480     key_node_t *ptr;
481
482     ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
483     ptr->ch = ch;
484     ptr->type = XK_NOD;
485     ptr->val.str = NULL;
486     ptr->next = NULL;
487     ptr->sibling = NULL;
488     return ptr;
489 }
490
491
492
493 /* node_lookup():
494  *      look for the str starting at node ptr.
495  *      Print if last node
496  */
497 private int
498 node_lookup(el, str, ptr, cnt)
499     EditLine   *el;
500     char       *str;
501     key_node_t *ptr;
502     int         cnt;
503 {
504     int     ncnt;
505
506     if (ptr == NULL)
507         return -1;              /* cannot have null ptr */
508
509     if (*str == 0) {
510         /* no more chars in str.  node_enum from here. */
511         (void) node_enum(el, ptr, cnt);
512         return 0;
513     }
514     else {
515         /* If match put this char into el->el_key.buf.  Recurse */
516         if (ptr->ch == *str) {
517             /* match found */
518             ncnt = key__decode_char(el->el_key.buf, cnt,
519                                     (unsigned char) ptr->ch);
520             if (ptr->next != NULL)
521                 /* not yet at leaf */
522                 return node_lookup(el, str + 1, ptr->next, ncnt + 1);
523             else {
524                 /* next node is null so key should be complete */
525                 if (str[1] == 0) {
526                     el->el_key.buf[ncnt + 1] = '"';
527                     el->el_key.buf[ncnt + 2] = '\0';
528                     key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
529                     return 0;
530                 }
531                 else
532                     return -1;/* mismatch -- str still has chars */
533             }
534         }
535         else {
536             /* no match found try sibling */
537             if (ptr->sibling)
538                 return node_lookup(el, str, ptr->sibling, cnt);
539             else
540                 return -1;
541         }
542     }
543 }
544
545
546 /* node_enum():
547  *      Traverse the node printing the characters it is bound in buffer
548  */
549 private int
550 node_enum(el, ptr, cnt)
551     EditLine *el;
552     key_node_t *ptr;
553     int     cnt;
554 {
555     int     ncnt;
556
557     if (cnt >= KEY_BUFSIZ - 5) {        /* buffer too small */
558         el->el_key.buf[++cnt] = '"';
559         el->el_key.buf[++cnt] = '\0';
560         (void) fprintf(el->el_errfile,
561                     "Some extended keys too long for internal print buffer");
562         (void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf);
563         return 0;
564     }
565
566     if (ptr == NULL) {
567 #ifdef DEBUG_EDIT
568         (void) fprintf(el->el_errfile, "node_enum: BUG!! Null ptr passed\n!");
569 #endif
570         return -1;
571     }
572
573     /* put this char at end of str */
574     ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch);
575     if (ptr->next == NULL) {
576         /* print this key and function */
577         el->el_key.buf[ncnt + 1] = '"';
578         el->el_key.buf[ncnt + 2] = '\0';
579         key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
580     }
581     else
582         (void) node_enum(el, ptr->next, ncnt + 1);
583
584     /* go to sibling if there is one */
585     if (ptr->sibling)
586         (void) node_enum(el, ptr->sibling, cnt);
587     return 0;
588 }
589
590
591 /* key_kprint():
592  *      Print the specified key and its associated
593  *      function specified by val
594  */
595 protected void
596 key_kprint(el, key, val, ntype)
597     EditLine      *el;
598     char          *key;
599     key_value_t   *val;
600     int            ntype;
601 {
602     el_bindings_t *fp;
603     char unparsbuf[EL_BUFSIZ];
604     static char *fmt = "%-15s->  %s\n";
605
606     if (val != NULL)
607         switch (ntype) {
608         case XK_STR:
609         case XK_EXE:
610             (void) fprintf(el->el_errfile, fmt, key,
611                            key__decode_str(val->str, unparsbuf,
612                                               ntype == XK_STR ? "\"\"" : "[]"));
613             break;
614         case XK_CMD:
615             for (fp = el->el_map.help; fp->name; fp++)
616                 if (val->cmd == fp->func) {
617                     (void) fprintf(el->el_errfile, fmt, key, fp->name);
618                     break;
619                 }
620 #ifdef DEBUG_KEY
621             if (fp->name == NULL)
622                 (void) fprintf(el->el_errfile, "BUG! Command not found.\n");
623 #endif
624
625             break;
626         default:
627             abort();
628             break;
629         }
630     else
631         (void) fprintf(el->el_errfile, fmt, key, "no input");
632 }
633
634
635 /* key__decode_char():
636  *      Put a printable form of char in buf.
637  */
638 private int
639 key__decode_char(buf, cnt, ch)
640     char *buf;
641     int   cnt, ch;
642 {
643     ch = (unsigned char)ch;
644
645     if (ch == 0) {
646         buf[cnt++] = '^';
647         buf[cnt] = '@';
648         return cnt;
649     }
650
651     if (iscntrl(ch)) {
652         buf[cnt++] = '^';
653         if (ch == 0177)
654             buf[cnt] = '?';
655         else
656             buf[cnt] = toascii(ch) | 0100;
657     }
658     else if (ch == '^') {
659         buf[cnt++] = '\\';
660         buf[cnt] = '^';
661     }
662     else if (ch == '\\') {
663         buf[cnt++] = '\\';
664         buf[cnt] = '\\';
665     }
666     else if (ch == ' ' || (isprint(ch) && !isspace(ch))) {
667         buf[cnt] = ch;
668     }
669     else {
670         buf[cnt++] = '\\';
671         buf[cnt++] = ((ch >> 6) & 7) + '0';
672         buf[cnt++] = ((ch >> 3) & 7) + '0';
673         buf[cnt] = (ch & 7) + '0';
674     }
675     return cnt;
676 }
677
678 /* key__decode_str():
679  *      Make a printable version of the ey
680  */
681 protected char *
682 key__decode_str(str, buf, sep)
683     char   *str;
684     char   *buf;
685     char   *sep;
686 {
687     char *b, *p;
688
689     b = buf;
690     if (sep[0] != '\0')
691         *b++ = sep[0];
692     if (*str == 0) {
693         *b++ = '^';
694         *b++ = '@';
695         if (sep[0] != '\0' && sep[1] != '\0')
696             *b++ = sep[1];
697         *b++ = 0;
698         return buf;
699     }
700
701     for (p = str; *p != 0; p++) {
702         if (iscntrl((unsigned char) *p)) {
703             *b++ = '^';
704             if (*p == '\177')
705                 *b++ = '?';
706             else
707                 *b++ = toascii(*p) | 0100;
708         }
709         else if (*p == '^' || *p == '\\') {
710             *b++ = '\\';
711             *b++ = *p;
712         }
713         else if (*p == ' ' || (isprint((unsigned char) *p) &&
714                                !isspace((unsigned char) *p))) {
715             *b++ = *p;
716         }
717         else {
718             *b++ = '\\';
719             *b++ = ((*p >> 6) & 7) + '0';
720             *b++ = ((*p >> 3) & 7) + '0';
721             *b++ = (*p & 7) + '0';
722         }
723     }
724     if (sep[0] != '\0' && sep[1] != '\0')
725         *b++ = sep[1];
726     *b++ = 0;
727     return buf;                 /* should check for overflow */
728 }