root/src/noitedit/key.c

Revision 2c4eb5b5fbb052c18d52a989026b447982cc17c3, 16.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

pull header-space into the noitedir dir, this makes it stand apart from libedit more trivially.

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