root/src/noitedit/key.c

Revision 52b8bc4e1ecc74b21f4db46822bfe9c609fb818a, 16.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

refs #283

  • 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 #include <errno.h>
73 #include <assert.h>
74
75 #include "noitedit/el.h"
76
77 /*
78  * The Nodes of the el->el_key.map.  The el->el_key.map is a linked list
79  * of these node elements
80  */
81 struct key_node_t {
82         char            ch;             /* single character of key       */
83         int             type;           /* node type                     */
84         key_value_t     val;            /* command code or pointer to str,  */
85                                         /* if this is a leaf             */
86         struct key_node_t *next;        /* ptr to next char of this key  */
87         struct key_node_t *sibling;     /* ptr to another key with same prefix*/
88 };
89
90 private int              node_trav(EditLine *, key_node_t *, char *,
91     key_value_t *);
92 private int              node__try(EditLine *, key_node_t *, const char *,
93     key_value_t *, int);
94 private key_node_t      *node__get(int);
95 private void             node__put(EditLine *, key_node_t *);
96 private int              node__delete(EditLine *, key_node_t **, char *);
97 private int              node_lookup(EditLine *, char *, key_node_t *, int);
98 private int              node_enum(EditLine *, key_node_t *, int);
99 private int              key__decode_char(char *, int, int);
100
101 #define KEY_BUFSIZ      EL_BUFSIZ
102
103
104 /* key_init():
105  *      Initialize the key maps
106  */
107 protected int
108 key_init(EditLine *el)
109 {
110
111         el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ);
112         if (el->el_key.buf == NULL)
113                 return (-1);
114         el->el_key.map = NULL;
115         key_reset(el);
116         return (0);
117 }
118
119
120 /* key_end():
121  *      Free the key maps
122  */
123 protected void
124 key_end(EditLine *el)
125 {
126
127         el_free((ptr_t) el->el_key.buf);
128         el->el_key.buf = NULL;
129         /* XXX: provide a function to clear the keys */
130         el->el_key.map = NULL;
131 }
132
133
134 /* key_map_cmd():
135  *      Associate cmd with a key value
136  */
137 protected key_value_t *
138 key_map_cmd(EditLine *el, int cmd)
139 {
140
141         el->el_key.val.cmd = (el_action_t) cmd;
142         return (&el->el_key.val);
143 }
144
145
146 /* key_map_str():
147  *      Associate str with a key value
148  */
149 protected key_value_t *
150 key_map_str(EditLine *el, char *str)
151 {
152
153         el->el_key.val.str = str;
154         return (&el->el_key.val);
155 }
156
157
158 /* key_reset():
159  *      Takes all nodes on el->el_key.map and puts them on free list.  Then
160  *      initializes el->el_key.map with arrow keys
161  *      [Always bind the ansi arrow keys?]
162  */
163 protected void
164 key_reset(EditLine *el)
165 {
166
167         node__put(el, el->el_key.map);
168         el->el_key.map = NULL;
169         return;
170 }
171
172
173 /* key_get():
174  *      Calls the recursive function with entry point el->el_key.map
175  *      Looks up *ch in map and then reads characters until a
176  *      complete match is found or a mismatch occurs. Returns the
177  *      type of the match found (XK_STR, XK_CMD, or XK_EXE).
178  *      Returns NULL in val.str and XK_STR for no match.
179  *      The last character read is returned in *ch.
180  */
181 protected int
182 key_get(EditLine *el, char *ch, key_value_t *val)
183 {
184 #ifdef DEBUG_KEY
185         (void) el->el_err_printf(el, "key_get (%p [%02x]) [%02x]\r\n",
186                                  el->el_keystate, el->el_keystate ? el->el_keystate->ch : 0, *ch);
187 #endif
188         return (node_trav(el, el->el_keystate ? el->el_keystate : el->el_key.map, ch, val));
189 }
190
191
192 /* key_add():
193  *      Adds key to the el->el_key.map and associates the value in val with it.
194  *      If key is already is in el->el_key.map, the new code is applied to the
195  *      existing key. Ntype specifies if code is a command, an
196  *      out str or a unix command.
197  */
198 protected void
199 key_add(EditLine *el, const char *key, key_value_t *val, int ntype)
200 {
201
202         if (key[0] == '\0') {
203                 (void) el->el_err_printf(el,
204                     "key_add: Null extended-key not allowed.\r\n");
205                 return;
206         }
207         if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
208                 (void) el->el_err_printf(el,
209                     "key_add: sequence-lead-in command not allowed\r\n");
210                 return;
211         }
212         if (el->el_key.map == NULL)
213                 /* tree is initially empty.  Set up new node to match key[0] */
214                 el->el_key.map = node__get(key[0]);
215                         /* it is properly initialized */
216
217         /* Now recurse through el->el_key.map */
218         (void) node__try(el, el->el_key.map, key, val, ntype);
219         return;
220 }
221
222
223 /* key_clear():
224  *
225  */
226 protected void
227 key_clear(EditLine *el, el_action_t *map, char *in)
228 {
229
230         if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
231             ((map == el->el_map.key &&
232             el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
233             (map == el->el_map.alt &&
234             el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
235                 (void) key_delete(el, in);
236 }
237
238
239 /* key_delete():
240  *      Delete the key and all longer keys staring with key, if
241  *      they exists.
242  */
243 protected int
244 key_delete(EditLine *el, char *key)
245 {
246
247         if (key[0] == '\0') {
248                 (void) el->el_err_printf(el,
249                     "key_delete: Null extended-key not allowed.\r\n");
250                 return (-1);
251         }
252         if (el->el_key.map == NULL)
253                 return (0);
254
255         (void) node__delete(el, &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(EditLine *el, char *key)
266 {
267
268         /* do nothing if el->el_key.map is empty and null key specified */
269         if (el->el_key.map == NULL && *key == 0)
270                 return;
271
272         el->el_key.buf[0] = '"';
273         if (node_lookup(el, key, el->el_key.map, 1) <= -1)
274                 /* key is not bound */
275                 (void) el->el_err_printf(el, "Unbound extended key \"%s\"\r\n",
276                     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(EditLine *el, key_node_t *ptr, char *ch, key_value_t *val)
287 {
288         int meta = 0;
289         if(el->el_keystate && (unsigned char)el->el_keystate->ch == 0x1b) {
290                 /* A previous character exists and it is 0x1b (ESC) */
291                 meta = 1;
292         }
293         if (ptr->ch == *ch) {
294                 /* match found */
295                 if (ptr->next) {
296                         /* key not complete so get next char */
297                         if (el_getc(el, ch) != 1) {     /* if EOF or error */
298                                 if(errno == EAGAIN) {
299                                         el->el_keystate = ptr;
300                                         val->cmd = ED_UNASSIGNED;
301                                         return (XK_CMD);
302                                 }
303                                 val->cmd = ED_END_OF_FILE;
304                                 return (XK_CMD);
305                                 /* PWP: Pretend we just read an end-of-file */
306                         }
307                         el->el_keystate = NULL;
308                         return (node_trav(el, ptr->next, ch, val));
309                 } else {
310                         *val = ptr->val;
311                         if (ptr->type != XK_CMD)
312                                 *ch = '\0';
313                         el->el_keystate = NULL;
314                         return (ptr->type);
315                 }
316         } else {
317                 /* no match found here */
318                 if (ptr->sibling) {
319                         /* try next sibling */
320                         return (node_trav(el, ptr->sibling, ch, val));
321                 } else {
322                         /* no next sibling -- mismatch */
323                         val->str = NULL;
324                         el->el_keystate = NULL;
325                         if(meta && el->el_map.type == MAP_EMACS) {
326                                 *ch |= 0200;
327                                 val->cmd = el->el_map.emacs[(unsigned char) *ch];
328                                 return XK_CMD;
329                         }
330                         return (XK_STR);
331                 }
332         }
333 }
334
335
336 /* node__try():
337  *      Find a node that matches *str or allocate a new one
338  */
339 private int
340 node__try(EditLine *el, key_node_t *ptr, const char *str, key_value_t *val, int ntype)
341 {
342
343         if (ptr->ch != *str) {
344                 key_node_t *xm;
345
346                 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
347                         if (xm->sibling->ch == *str)
348                                 break;
349                 if (xm->sibling == NULL)
350                         xm->sibling = node__get(*str);  /* setup new node */
351                 ptr = xm->sibling;
352         }
353         if (*++str == '\0') {
354                 /* we're there */
355                 if (ptr->next != NULL) {
356                         node__put(el, ptr->next);
357                                 /* lose longer keys with this prefix */
358                         ptr->next = NULL;
359                 }
360                 switch (ptr->type) {
361                 case XK_CMD:
362                 case XK_NOD:
363                         break;
364                 case XK_STR:
365                 case XK_EXE:
366                         if (ptr->val.str)
367                                 el_free((ptr_t) ptr->val.str);
368                         break;
369                 default:
370                         EL_ABORT((el, "Bad XK_ type %d\n",
371                             ptr->type));
372                         break;
373                 }
374
375                 switch (ptr->type = ntype) {
376                 case XK_CMD:
377                         ptr->val = *val;
378                         break;
379                 case XK_STR:
380                 case XK_EXE:
381                         ptr->val.str = strdup(val->str);
382                         break;
383                 default:
384                         EL_ABORT((el, "Bad XK_ type %d\n", ntype));
385                         break;
386                 }
387         } else {
388                 /* still more chars to go */
389                 if (ptr->next == NULL)
390                         ptr->next = node__get(*str);    /* setup new node */
391                 (void) node__try(el, ptr->next, str, val, ntype);
392         }
393         return (0);
394 }
395
396
397 /* node__delete():
398  *      Delete node that matches str
399  */
400 private int
401 node__delete(EditLine *el, key_node_t **inptr, char *str)
402 {
403         key_node_t *ptr;
404         key_node_t *prev_ptr = NULL;
405
406         ptr = *inptr;
407
408         if (ptr->ch != *str) {
409                 key_node_t *xm;
410
411                 for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
412                         if (xm->sibling->ch == *str)
413                                 break;
414                 if (xm->sibling == NULL)
415                         return (0);
416                 prev_ptr = xm;
417                 ptr = xm->sibling;
418         }
419         if (*++str == '\0') {
420                 /* we're there */
421                 if (prev_ptr == NULL)
422                         *inptr = ptr->sibling;
423                 else
424                         prev_ptr->sibling = ptr->sibling;
425                 ptr->sibling = NULL;
426                 node__put(el, ptr);
427                 return (1);
428         } else if (ptr->next != NULL &&
429             node__delete(el, &ptr->next, str) == 1) {
430                 if (ptr->next != NULL)
431                         return (0);
432                 if (prev_ptr == NULL)
433                         *inptr = ptr->sibling;
434                 else
435                         prev_ptr->sibling = ptr->sibling;
436                 ptr->sibling = NULL;
437                 node__put(el, ptr);
438                 return (1);
439         } else {
440                 return (0);
441         }
442 }
443
444
445 /* node__put():
446  *      Puts a tree of nodes onto free list using free(3).
447  */
448 private void
449 node__put(EditLine *el, key_node_t *ptr)
450 {
451         if (ptr == NULL)
452                 return;
453
454         if (ptr->next != NULL) {
455                 node__put(el, ptr->next);
456                 ptr->next = NULL;
457         }
458         node__put(el, ptr->sibling);
459
460         switch (ptr->type) {
461         case XK_CMD:
462         case XK_NOD:
463                 break;
464         case XK_EXE:
465         case XK_STR:
466                 if (ptr->val.str != NULL)
467                         el_free((ptr_t) ptr->val.str);
468                 break;
469         default:
470                 EL_ABORT((el, "Bad XK_ type %d\n", ptr->type));
471                 break;
472         }
473         el_free((ptr_t) ptr);
474 }
475
476
477 /* node__get():
478  *      Returns pointer to an key_node_t for ch.
479  */
480 private key_node_t *
481 node__get(int ch)
482 {
483         key_node_t *ptr;
484
485         ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
486         if (ptr == NULL)
487                 return NULL;
488         ptr->ch = ch;
489         ptr->type = XK_NOD;
490         ptr->val.str = NULL;
491         ptr->next = NULL;
492         ptr->sibling = NULL;
493         return (ptr);
494 }
495
496
497
498 /* node_lookup():
499  *      look for the str starting at node ptr.
500  *      Print if last node
501  */
502 private int
503 node_lookup(EditLine *el, char *str, key_node_t *ptr, int cnt)
504 {
505         int ncnt;
506
507         if (ptr == NULL)
508                 return (-1);    /* cannot have null ptr */
509
510         if (*str == 0) {
511                 /* no more chars in str.  node_enum from here. */
512                 (void) node_enum(el, ptr, cnt);
513                 return (0);
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,
523                                     ncnt + 1));
524                         else {
525                             /* next node is null so key should be complete */
526                                 if (str[1] == 0) {
527                                         el->el_key.buf[ncnt + 1] = '"';
528                                         el->el_key.buf[ncnt + 2] = '\0';
529                                         key_kprint(el, el->el_key.buf,
530                                             &ptr->val, ptr->type);
531                                         return (0);
532                                 } else
533                                         return (-1);
534                                         /* mismatch -- str still has chars */
535                         }
536                 } else {
537                         /* no match found try sibling */
538                         if (ptr->sibling)
539                                 return (node_lookup(el, str, ptr->sibling,
540                                     cnt));
541                         else
542                                 return (-1);
543                 }
544         }
545 }
546
547
548 /* node_enum():
549  *      Traverse the node printing the characters it is bound in buffer
550  */
551 private int
552 node_enum(EditLine *el, key_node_t *ptr, int cnt)
553 {
554         int ncnt;
555
556         if (cnt >= KEY_BUFSIZ - 5) {    /* buffer too small */
557                 el->el_key.buf[++cnt] = '"';
558                 el->el_key.buf[++cnt] = '\0';
559                 (void) el->el_err_printf(el,
560                     "Some extended keys too long for internal print buffer");
561                 (void) el->el_err_printf(el, " \"%s...\"\r\n", el->el_key.buf);
562                 return (0);
563         }
564         if (ptr == NULL) {
565 #ifdef DEBUG_EDIT
566                 (void) el->el_err_printf(el,
567                     "node_enum: BUG!! Null ptr passed\r\n!");
568 #endif
569                 return (-1);
570         }
571         /* put this char at end of str */
572         ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch);
573         if (ptr->next == NULL) {
574                 /* print this key and function */
575                 el->el_key.buf[ncnt + 1] = '"';
576                 el->el_key.buf[ncnt + 2] = '\0';
577                 key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
578         } else
579                 (void) node_enum(el, ptr->next, ncnt + 1);
580
581         /* go to sibling if there is one */
582         if (ptr->sibling)
583                 (void) node_enum(el, ptr->sibling, cnt);
584         return (0);
585 }
586
587
588 /* key_kprint():
589  *      Print the specified key and its associated
590  *      function specified by val
591  */
592 protected void
593 key_kprint(EditLine *el, char *key, key_value_t *val, int ntype)
594 {
595         el_bindings_t *fp;
596         char unparsbuf[EL_BUFSIZ];
597         static const char fmt[] = "%-15s->  %s\n";
598
599         if (val != NULL)
600                 switch (ntype) {
601                 case XK_STR:
602                 case XK_EXE:
603                         (void) el->el_std_printf(el, fmt, key,
604                             key__decode_str(val->str, unparsbuf,
605                                 ntype == XK_STR ? "\"\"" : "[]"));
606                         break;
607                 case XK_CMD:
608                         for (fp = el->el_map.help; fp->name; fp++)
609                                 if (val->cmd == fp->func) {
610                                         (void) el->el_std_printf(el, fmt,
611                                             key, fp->name);
612                                         break;
613                                 }
614 #ifdef DEBUG_KEY
615                         if (fp->name == NULL)
616                                 (void) el->el_std_printf(el,
617                                     "BUG! Command not found.\n");
618 #endif
619
620                         break;
621                 default:
622                         EL_ABORT((el, "Bad XK_ type %d\n", ntype));
623                         break;
624                 }
625         else
626                 (void) el->el_std_printf(el, fmt, key, "no input");
627 }
628
629
630 /* key__decode_char():
631  *      Put a printable form of char in buf.
632  */
633 private int
634 key__decode_char(char *buf, int cnt, int ch)
635 {
636         if (ch == 0) {
637                 buf[cnt++] = '^';
638                 buf[cnt] = '@';
639                 return (cnt);
640         }
641         if (iscntrl(ch)) {
642                 buf[cnt++] = '^';
643                 if (ch == '\177')
644                         buf[cnt] = '?';
645                 else
646                         buf[cnt] = ch | 0100;
647         } else if (ch == '^') {
648                 buf[cnt++] = '\\';
649                 buf[cnt] = '^';
650         } else if (ch == '\\') {
651                 buf[cnt++] = '\\';
652                 buf[cnt] = '\\';
653         } else if (ch == ' ' || (isprint(ch) && !isspace(ch))) {
654                 buf[cnt] = ch;
655         } else {
656                 buf[cnt++] = '\\';
657                 buf[cnt++] = (((unsigned int) ch >> 6) & 7) + '0';
658                 buf[cnt++] = (((unsigned int) ch >> 3) & 7) + '0';
659                 buf[cnt] = (ch & 7) + '0';
660         }
661         return (cnt);
662 }
663
664
665 /* key__decode_str():
666  *      Make a printable version of the ey
667  */
668 protected char *
669 key__decode_str(char *str, char *buf, char *sep)
670 {
671         char *b, *p;
672
673         b = buf;
674         if (sep[0] != '\0')
675                 *b++ = sep[0];
676         if (*str == 0) {
677                 *b++ = '^';
678                 *b++ = '@';
679                 if (sep[0] != '\0' && sep[1] != '\0')
680                         *b++ = sep[1];
681                 *b = 0;
682                 return (buf);
683         }
684         for (p = str; *p != 0; p++) {
685                 if (iscntrl((unsigned char) *p)) {
686                         *b++ = '^';
687                         if (*p == '\177')
688                                 *b++ = '?';
689                         else
690                                 *b++ = *p | 0100;
691                 } else if (*p == '^' || *p == '\\') {
692                         *b++ = '\\';
693                         *b++ = *p;
694                 } else if (*p == ' ' || (isprint((unsigned char) *p) &&
695                         !isspace((unsigned char) *p))) {
696                         *b++ = *p;
697                 } else {
698                         *b++ = '\\';
699                         *b++ = (((unsigned int) *p >> 6) & 7) + '0';
700                         *b++ = (((unsigned int) *p >> 3) & 7) + '0';
701                         *b++ = (*p & 7) + '0';
702                 }
703         }
704         if (sep[0] != '\0' && sep[1] != '\0')
705                 *b++ = sep[1];
706         *b = 0;
707         return (buf);           /* should check for overflow */
708 }
Note: See TracBrowser for help on using the browser.