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