xref: /illumos-gate/usr/src/lib/libtecla/common/keytab.c (revision 1da57d551424de5a9d469760be7c4b4d4f10a755)
1 /*
2  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3  *
4  * All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, and/or sell copies of the Software, and to permit persons
11  * to whom the Software is furnished to do so, provided that the above
12  * copyright notice(s) and this permission notice appear in all copies of
13  * the Software and that both the above copyright notice(s) and this
14  * permission notice appear in supporting documentation.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25  *
26  * Except as contained in this notice, the name of a copyright holder
27  * shall not be used in advertising or otherwise to promote the sale, use
28  * or other dealings in this Software without prior written authorization
29  * of the copyright holder.
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <errno.h>
37 
38 #include "keytab.h"
39 #include "strngmem.h"
40 #include "getline.h"
41 #include "errmsg.h"
42 #include "hash.h"
43 
44 /*
45  * When allocating or reallocating the key-binding table, how
46  * many entries should be added?
47  */
48 #define KT_TABLE_INC 100
49 
50 /*
51  * Define the size of the hash table that is used to associate action
52  * names with action functions. This should be a prime number.
53  */
54 #define KT_HASH_SIZE 113
55 
56 /*
57  * Define a binary-symbol-table object.
58  */
59 struct KeyTab {
60   ErrMsg *err;            /* Information about the last error */
61   int size;               /* The allocated dimension of table[] */
62   int nkey;               /* The current number of members in the table */
63   KeySym *table;          /* The table of lexically sorted key sequences */
64   HashTable *actions;     /* The hash table of actions */
65   StringMem *smem;        /* Memory for allocating strings */
66 };
67 
68 static int _kt_extend_table(KeyTab *kt);
69 static int _kt_parse_keybinding_string(const char *keyseq,
70 				       char *binary, int *nc);
71 static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2);
72 static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn,
73 			      void *data);
74 static char _kt_backslash_escape(const char *string, const char **endp);
75 static int _kt_is_emacs_meta(const char *string);
76 static int _kt_is_emacs_ctrl(const char *string);
77 static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq,
78 					int nc, int *first, int *last);
79 
80 /*.......................................................................
81  * Create a new key-binding symbol table.
82  *
83  * Output:
84  *  return  KeyTab *  The new object, or NULL on error.
85  */
_new_KeyTab(void)86 KeyTab *_new_KeyTab(void)
87 {
88   KeyTab *kt;  /* The object to be returned */
89 /*
90  * Allocate the container.
91  */
92   kt = (KeyTab *) malloc(sizeof(KeyTab));
93   if(!kt) {
94     errno = ENOMEM;
95     return NULL;
96   };
97 /*
98  * Before attempting any operation that might fail, initialize the
99  * container at least up to the point at which it can safely be passed
100  * to del_KeyTab().
101  */
102   kt->err = NULL;
103   kt->size = KT_TABLE_INC;
104   kt->nkey = 0;
105   kt->table = NULL;
106   kt->actions = NULL;
107   kt->smem = NULL;
108 /*
109  * Allocate a place to record error messages.
110  */
111   kt->err = _new_ErrMsg();
112   if(!kt->err)
113     return _del_KeyTab(kt);
114 /*
115  * Allocate the table.
116  */
117   kt->table = (KeySym *) malloc(sizeof(kt->table[0]) * kt->size);
118   if(!kt->table) {
119     errno = ENOMEM;
120     return _del_KeyTab(kt);
121   };
122 /*
123  * Allocate a hash table of actions.
124  */
125   kt->actions = _new_HashTable(NULL, KT_HASH_SIZE, IGNORE_CASE, NULL, 0);
126   if(!kt->actions)
127     return _del_KeyTab(kt);
128 /*
129  * Allocate a string allocation object. This allows allocation of
130  * small strings without fragmenting the heap.
131  */
132   kt->smem = _new_StringMem(KT_TABLE_INC);
133   if(!kt->smem)
134     return _del_KeyTab(kt);
135   return kt;
136 }
137 
138 /*.......................................................................
139  * Delete a KeyTab object.
140  *
141  * Input:
142  *  kt   KeyTab *  The object to be deleted.
143  * Output:
144  *  return KeyTab *  The deleted object (always NULL).
145  */
_del_KeyTab(KeyTab * kt)146 KeyTab *_del_KeyTab(KeyTab *kt)
147 {
148   if(kt) {
149     if(kt->table)
150       free(kt->table);
151     kt->actions = _del_HashTable(kt->actions);
152     kt->smem = _del_StringMem(kt->smem, 1);
153     kt->err = _del_ErrMsg(kt->err);
154     free(kt);
155   };
156   return NULL;
157 }
158 
159 /*.......................................................................
160  * Increase the size of the table to accomodate more keys.
161  *
162  * Input:
163  *  kt       KeyTab *  The table to be extended.
164  * Output:
165  *  return      int    0 - OK.
166  *                     1 - Error.
167  */
_kt_extend_table(KeyTab * kt)168 static int _kt_extend_table(KeyTab *kt)
169 {
170 /*
171  * Attempt to increase the size of the table.
172  */
173   KeySym *newtab = (KeySym *) realloc(kt->table, sizeof(kt->table[0]) *
174 				      (kt->size + KT_TABLE_INC));
175 /*
176  * Failed?
177  */
178   if(!newtab) {
179     _err_record_msg(kt->err, "Can't extend keybinding table", END_ERR_MSG);
180     errno = ENOMEM;
181     return 1;
182   };
183 /*
184  * Install the resized table.
185  */
186   kt->table = newtab;
187   kt->size += KT_TABLE_INC;
188   return 0;
189 }
190 
191 /*.......................................................................
192  * Add, update or remove a keybinding to the table.
193  *
194  * Input:
195  *  kt           KeyTab *  The table to add the binding to.
196  *  binder     KtBinder    The source of the binding.
197  *  keyseq   const char *  The key-sequence to bind.
198  *  action         char *  The action to associate with the key sequence, or
199  *                         NULL to remove the action associated with the
200  *                         key sequence.
201  * Output:
202  *  return          int    0 - OK.
203  *                         1 - Error.
204  */
_kt_set_keybinding(KeyTab * kt,KtBinder binder,const char * keyseq,const char * action)205 int _kt_set_keybinding(KeyTab *kt, KtBinder binder, const char *keyseq,
206 		       const char *action)
207 {
208   KtKeyFn *keyfn; /* The action function */
209   void *data;     /* The callback data of the action function */
210 /*
211  * Check arguments.
212  */
213   if(kt==NULL || !keyseq) {
214     errno = EINVAL;
215     if(kt)
216       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
217     return 1;
218   };
219 /*
220  * Lookup the function that implements the specified action.
221  */
222   if(!action) {
223     keyfn = 0;
224     data = NULL;
225   } else {
226     Symbol *sym = _find_HashSymbol(kt->actions, action);
227     if(!sym) {
228       _err_record_msg(kt->err, "Unknown key-binding action: ", action,
229 		      END_ERR_MSG);
230       errno = EINVAL;
231       return 1;
232     };
233     keyfn = (KtKeyFn *) sym->fn;
234     data = sym->data;
235   };
236 /*
237  * Record the action in the table.
238  */
239   return _kt_set_keyfn(kt, binder, keyseq, keyfn, data);
240 }
241 
242 /*.......................................................................
243  * Add, update or remove a keybinding to the table, specifying an action
244  * function directly.
245  *
246  * Input:
247  *  kt       KeyTab *  The table to add the binding to.
248  *  binder KtBinder    The source of the binding.
249  *  keyseq     char *  The key-sequence to bind.
250  *  keyfn   KtKeyFn *  The action function, or NULL to remove any existing
251  *                     action function.
252  *  data       void *  A pointer to anonymous data to be passed to keyfn
253  *                     whenever it is called.
254  * Output:
255  *  return     int    0 - OK.
256  *                    1 - Error.
257  */
_kt_set_keyfn(KeyTab * kt,KtBinder binder,const char * keyseq,KtKeyFn * keyfn,void * data)258 int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq,
259 		  KtKeyFn *keyfn, void *data)
260 {
261   const char *kptr;  /* A pointer into keyseq[] */
262   char *binary;      /* The binary version of keyseq[] */
263   int nc;            /* The number of characters in binary[] */
264   int first,last;    /* The first and last entries in the table which */
265                      /*  minimally match. */
266   int size;          /* The size to allocate for the binary string */
267   int i;
268 /*
269  * Check arguments.
270  */
271   if(kt==NULL || !keyseq) {
272     errno = EINVAL;
273     if(kt)
274       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
275     return 1;
276   };
277 /*
278  * Work out a pessimistic estimate of how much space will be needed
279  * for the binary copy of the string, noting that binary meta characters
280  * embedded in the input string get split into two characters.
281  */
282   for(size=0,kptr = keyseq; *kptr; kptr++)
283     size += IS_META_CHAR(*kptr) ? 2 : 1;
284 /*
285  * Allocate a string that has the length of keyseq[].
286  */
287   binary = _new_StringMemString(kt->smem, size + 1);
288   if(!binary) {
289     errno = ENOMEM;
290     _err_record_msg(kt->err, "Insufficient memory to record key sequence",
291 		    END_ERR_MSG);
292     return 1;
293   };
294 /*
295  * Convert control and octal character specifications to binary characters.
296  */
297   if(_kt_parse_keybinding_string(keyseq, binary, &nc)) {
298     binary = _del_StringMemString(kt->smem, binary);
299     return 1;
300   };
301 /*
302  * Lookup the position in the table at which to insert the binding.
303  */
304   switch(_kt_locate_keybinding(kt, binary, nc, &first, &last)) {
305 /*
306  * If an exact match for the key-sequence is already in the table,
307  * simply replace its binding function (or delete the entry if
308  * the new binding is 0).
309  */
310   case KT_EXACT_MATCH:
311     if(keyfn) {
312       _kt_assign_action(kt->table + first, binder, keyfn, data);
313     } else {
314       _del_StringMemString(kt->smem, kt->table[first].keyseq);
315       memmove(kt->table + first, kt->table + first + 1,
316 	      (kt->nkey - first - 1) * sizeof(kt->table[0]));
317       kt->nkey--;
318     };
319     binary = _del_StringMemString(kt->smem, binary);
320     break;
321 /*
322  * If an ambiguous match has been found and we are installing a
323  * callback, then our new key-sequence would hide all of the ambiguous
324  * matches, so we shouldn't allow it.
325  */
326   case KT_AMBIG_MATCH:
327     if(keyfn) {
328       _err_record_msg(kt->err, "Can't bind \"", keyseq,
329 		      "\", because it is a prefix of another binding",
330 		      END_ERR_MSG);
331       binary = _del_StringMemString(kt->smem, binary);
332       errno = EPERM;
333       return 1;
334     };
335     break;
336 /*
337  * If the entry doesn't exist, create it.
338  */
339   case KT_NO_MATCH:
340 /*
341  * Add a new binding?
342  */
343     if(keyfn) {
344       KeySym *sym;
345 /*
346  * We will need a new entry, extend the table if needed.
347  */
348       if(kt->nkey + 1 > kt->size) {
349 	if(_kt_extend_table(kt)) {
350 	  binary = _del_StringMemString(kt->smem, binary);
351 	  return 1;
352 	};
353       };
354 /*
355  * Make space to insert the new key-sequence before 'last'.
356  */
357       if(last < kt->nkey) {
358 	memmove(kt->table + last + 1, kt->table + last,
359 		(kt->nkey - last) * sizeof(kt->table[0]));
360       };
361 /*
362  * Insert the new binding in the vacated position.
363  */
364       sym = kt->table + last;
365       sym->keyseq = binary;
366       sym->nc = nc;
367       for(i=0; i<KTB_NBIND; i++) {
368 	KtAction *action = sym->actions + i;
369 	action->fn = 0;
370 	action->data = NULL;
371       };
372       sym->binder = -1;
373       _kt_assign_action(sym, binder, keyfn, data);
374       kt->nkey++;
375     };
376     break;
377   case KT_BAD_MATCH:
378     binary = _del_StringMemString(kt->smem, binary);
379     return 1;
380     break;
381   };
382   return 0;
383 }
384 
385 /*.......................................................................
386  * Perform a min-match lookup of a key-binding.
387  *
388  * Input:
389  *  kt          KeyTab *   The keybinding table to lookup in.
390  *  binary_keyseq char *   The binary key-sequence to lookup.
391  *  nc             int     the number of characters in keyseq[].
392  * Input/Output:
393  *  first,last     int *   If there is an ambiguous or exact match, the indexes
394  *                         of the first and last symbols that minimally match
395  *                         will be assigned to *first and *last respectively.
396  *                         If there is no match, then first and last will
397  *                         bracket the location where the symbol should be
398  *                         inserted.
399  * Output:
400  *  return  KtKeyMatch     One of the following enumerators:
401  *                          KT_EXACT_MATCH - An exact match was found.
402  *                          KT_AMBIG_MATCH - An ambiguous match was found.
403  *                          KT_NO_MATCH    - No match was found.
404  *                          KT_BAD_MATCH   - An error occurred while searching.
405  */
_kt_locate_keybinding(KeyTab * kt,const char * binary_keyseq,int nc,int * first,int * last)406 static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq,
407 					int nc, int *first, int *last)
408 {
409   int mid;     /* The index at which to bisect the table */
410   int bot;     /* The lowest index of the table not searched yet */
411   int top;     /* The highest index of the table not searched yet */
412   int test;    /* The return value of strcmp() */
413 /*
414  * Perform a binary search for the key-sequence.
415  */
416   bot = 0;
417   top = kt->nkey - 1;
418   while(top >= bot) {
419     mid = (top + bot)/2;
420     test = _kt_compare_strings(kt->table[mid].keyseq, kt->table[mid].nc,
421 			   binary_keyseq, nc);
422     if(test > 0)
423       top = mid - 1;
424     else if(test < 0)
425       bot = mid + 1;
426     else {
427       *first = *last = mid;
428       return KT_EXACT_MATCH;
429     };
430   };
431 /*
432  * An exact match wasn't found, but top is the index just below the
433  * index where a match would be found, and bot is the index just above
434  * where the match ought to be found.
435  */
436   *first = top;
437   *last = bot;
438 /*
439  * See if any ambiguous matches exist, and if so make *first and *last
440  * refer to the first and last matches.
441  */
442   if(*last < kt->nkey && kt->table[*last].nc > nc &&
443      _kt_compare_strings(kt->table[*last].keyseq, nc, binary_keyseq, nc)==0) {
444     *first = *last;
445     while(*last+1 < kt->nkey && kt->table[*last+1].nc > nc &&
446 	  _kt_compare_strings(kt->table[*last+1].keyseq, nc, binary_keyseq, nc)==0)
447       (*last)++;
448     return KT_AMBIG_MATCH;
449   };
450 /*
451  * No match.
452  */
453   return KT_NO_MATCH;
454 }
455 
456 /*.......................................................................
457  * Lookup the sub-array of key-bindings who's key-sequences minimally
458  * match a given key-sequence.
459  *
460  * Input:
461  *  kt          KeyTab *   The keybinding table to lookup in.
462  *  binary_keyseq char *   The binary key-sequence to lookup.
463  *  nc             int     the number of characters in keyseq[].
464  * Input/Output:
465  *  matches     KeySym **  The array of minimally matching symbols
466  *                         can be found in (*matches)[0..nmatch-1], unless
467  *                         no match was found, in which case *matches will
468  *                         be set to NULL.
469  *  nmatch         int     The number of ambiguously matching symbols. This
470  *                         will be 0 if there is no match, 1 for an exact
471  *                         match, and a number greater than 1 for an ambiguous
472  *                         match.
473  * Output:
474  *  return  KtKeyMatch     One of the following enumerators:
475  *                          KT_EXACT_MATCH - An exact match was found.
476  *                          KT_AMBIG_MATCH - An ambiguous match was found.
477  *                          KT_NO_MATCH    - No match was found.
478  *                          KT_BAD_MATCH   - An error occurred while searching.
479  */
_kt_lookup_keybinding(KeyTab * kt,const char * binary_keyseq,int nc,KeySym ** matches,int * nmatch)480 KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq,
481 				 int nc, KeySym **matches, int *nmatch)
482 {
483   KtKeyMatch status;  /* The return status */
484   int first,last;     /* The indexes of the first and last matching entry */
485                       /* in the symbol table. */
486 /*
487  * Check the arguments.
488  */
489   if(!kt || !binary_keyseq || !matches || !nmatch || nc < 0) {
490     errno = EINVAL;
491     if(kt)
492       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
493     return KT_BAD_MATCH;
494   };
495 /*
496  * Lookup the indexes of the binding-table entries that bracket the
497  * target key-sequence.
498  */
499   status = _kt_locate_keybinding(kt, binary_keyseq, nc, &first, &last);
500 /*
501  * Translate the indexes into the corresponding subarray of matching
502  * table entries.
503  */
504   switch(status) {
505   case KT_EXACT_MATCH:
506   case KT_AMBIG_MATCH:
507     *matches = kt->table + first;
508     *nmatch = last - first + 1;
509     break;
510   default:
511     *matches = NULL;
512     *nmatch = 0;
513     break;
514   };
515   return status;
516 }
517 
518 /*.......................................................................
519  * Convert a keybinding string into a uniq binary representation.
520  *
521  * Control characters can be given directly in their binary form,
522  * expressed as either ^ or C-, followed by the character, expressed in
523  * octal, like \129 or via C-style backslash escapes, with the addition
524  * of '\E' to denote the escape key. Similarly, meta characters can be
525  * given directly in binary or expressed as M- followed by the character.
526  * Meta characters are recorded as two characters in the binary output
527  * string, the first being the escape key, and the second being the key
528  * that was modified by the meta key. This means that binding to
529  * \EA or ^[A or M-A are all equivalent.
530  *
531  * Input:
532  *  keyseq   char *  The key sequence being added.
533  * Input/Output:
534  *  binary   char *  The binary version of the key sequence will be
535  *                   assigned to binary[], which must have at least
536  *                   as many characters as keyseq[] plus the number
537  *                   of embedded binary meta characters.
538  *  nc        int *  The number of characters assigned to binary[]
539  *                   will be recorded in *nc.
540  * Output:
541  *  return    int    0 - OK.
542  *                   1 - Error.
543  */
_kt_parse_keybinding_string(const char * keyseq,char * binary,int * nc)544 static int _kt_parse_keybinding_string(const char *keyseq, char *binary,
545 				       int *nc)
546 {
547   const char *iptr = keyseq;   /* Pointer into keyseq[] */
548   char *optr = binary;         /* Pointer into binary[] */
549   char c;                      /* An intermediate character */
550 /*
551  * Parse the input characters until they are exhausted or the
552  * output string becomes full.
553  */
554   while(*iptr) {
555 /*
556  * Check for special characters.
557  */
558     switch(*iptr) {
559     case '^':        /* A control character specification */
560 /*
561  * Convert the caret expression into the corresponding control
562  * character unless no character follows the caret, in which case
563  * record a literal caret.
564  */
565       if(iptr[1]) {
566 /*
567  * Get the next, possibly escaped, character.
568  */
569 	if(iptr[1] == '\\') {
570 	  c = _kt_backslash_escape(iptr+2, &iptr);
571 	} else {
572 	  c = iptr[1];
573 	  iptr += 2;
574 	};
575 /*
576  * Convert the character to a control character.
577  */
578 	*optr++ = MAKE_CTRL(c);
579       } else {
580 	*optr++ = *iptr++;
581       };
582       break;
583 /*
584  * A backslash-escaped character?
585  */
586     case '\\':
587 /*
588  * Convert the escape sequence to a binary character.
589  */
590       *optr++ = _kt_backslash_escape(iptr+1, &iptr);
591       break;
592 /*
593  * Possibly an emacs-style meta character?
594  */
595     case 'M':
596       if(_kt_is_emacs_meta(iptr)) {
597 	*optr++ = GL_ESC_CHAR;
598 	iptr += 2;
599       } else {
600 	*optr++ = *iptr++;
601       };
602       break;
603 /*
604  * Possibly an emacs-style control character specification?
605  */
606     case 'C':
607       if(_kt_is_emacs_ctrl(iptr)) {
608 	*optr++ = MAKE_CTRL(iptr[2]);
609 	iptr += 3;
610       } else {
611 	*optr++ = *iptr++;
612       };
613       break;
614     default:
615 
616 /*
617  * Convert embedded meta characters into an escape character followed
618  * by the meta-unmodified character.
619  */
620       if(IS_META_CHAR(*iptr)) {
621 	*optr++ = GL_ESC_CHAR;
622 	*optr++ = META_TO_CHAR(*iptr);
623 	iptr++;
624 /*
625  * To allow keysequences that start with printable characters to
626  * be distinguished from the cursor-key keywords, prepend a backslash
627  * to the former. This same operation is performed in gl_interpret_char()
628  * before looking up a keysequence that starts with a printable character.
629  */
630       } else if(iptr==keyseq && !IS_CTRL_CHAR(*iptr) &&
631 		strcmp(keyseq, "up") != 0 && strcmp(keyseq, "down") != 0 &&
632 		strcmp(keyseq, "left") != 0 && strcmp(keyseq, "right") != 0) {
633 	*optr++ = '\\';
634 	*optr++ = *iptr++;
635       } else {
636 	*optr++ = *iptr++;
637       };
638     };
639   };
640 /*
641  * How many characters were placed in the output array?
642  */
643   *nc = optr - binary;
644   return 0;
645 }
646 
647 /*.......................................................................
648  * Add, remove or modify an action.
649  *
650  * Input:
651  *  kt     KeyTab *  The key-binding table.
652  *  action   char *  The name of the action.
653  *  fn    KtKeyFn *  The function that implements the action, or NULL
654  *                   to remove an existing action.
655  *  data     void *  A pointer to arbitrary callback data to pass to the
656  *                   action function whenever it is called.
657  * Output:
658  *  return    int    0 - OK.
659  *                   1 - Error.
660  */
_kt_set_action(KeyTab * kt,const char * action,KtKeyFn * fn,void * data)661 int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn, void *data)
662 {
663   Symbol *sym;   /* The symbol table entry of the action */
664 /*
665  * Check the arguments.
666  */
667   if(!kt || !action) {
668     errno = EINVAL;
669     if(kt)
670       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
671     return 1;
672   };
673 /*
674  * If no function was provided, delete an existing action.
675  */
676   if(!fn) {
677     sym = _del_HashSymbol(kt->actions, action);
678     return 0;
679   };
680 /*
681  * If the action already exists, replace its action function.
682  */
683   sym = _find_HashSymbol(kt->actions, action);
684   if(sym) {
685     sym->fn = (void (*)(void))fn;
686     sym->data = data;
687     return 0;
688   };
689 /*
690  * Add a new action.
691  */
692   if(!_new_HashSymbol(kt->actions, action, 0, (void (*)(void))fn, data, 0)) {
693     _err_record_msg(kt->err, "Insufficient memory to record key-binding action",
694 		    END_ERR_MSG);
695     return 1;
696   };
697   return 0;
698 }
699 
700 /*.......................................................................
701  * Compare two strings of specified length which may contain embedded
702  * ascii NUL's.
703  *
704  * Input:
705  *  s1       char *  The first of the strings to be compared.
706  *  n1        int    The length of the string in s1.
707  *  s2       char *  The second of the strings to be compared.
708  *  n2        int    The length of the string in s2.
709  * Output:
710  *  return    int    < 0 if(s1 < s2)
711  *                     0 if(s1 == s2)
712  *                   > 0 if(s1 > s2)
713  */
_kt_compare_strings(const char * s1,int n1,const char * s2,int n2)714 static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2)
715 {
716   int i;
717 /*
718  * Find the first character where the two strings differ.
719  */
720   for(i=0; i<n1 && i<n2 && s1[i]==s2[i]; i++)
721     ;
722 /*
723  * Did we hit the end of either string before finding a difference?
724  */
725   if(i==n1 || i==n2) {
726     if(n1 == n2)
727       return 0;
728     else if(n1==i)
729       return -1;
730     else
731       return 1;
732   };
733 /*
734  * Compare the two characters that differed to determine which
735  * string is greatest.
736  */
737   return s1[i] - s2[i];
738 }
739 
740 /*.......................................................................
741  * Assign a given action function to a binding table entry.
742  *
743  * Input:
744  *  sym       KeySym *  The binding table entry to be modified.
745  *  binder  KtBinder    The source of the binding.
746  *  keyfn    KtKeyFn *  The action function.
747  *  data        void *  A pointer to arbitrary callback data to pass to
748  *                      the action function whenever it is called.
749  */
_kt_assign_action(KeySym * sym,KtBinder binder,KtKeyFn * keyfn,void * data)750 static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn,
751 			      void *data)
752 {
753   KtAction *action;   /* An action function/data pair */
754   int i;
755 /*
756  * Unknown binding source?
757  */
758   if(binder < 0 || binder >= KTB_NBIND)
759     return;
760 /*
761  * Record the action according to its source.
762  */
763   action = sym->actions + binder;
764   action->fn = keyfn;
765   action->data = data;
766 /*
767  * Find the highest priority binding source that has supplied an
768  * action. Note that the actions[] array is ordered in order of
769  * descreasing priority, so the first entry that contains a function
770  * is the one to use.
771  */
772   for(i=0; i<KTB_NBIND && !sym->actions[i].fn; i++)
773     ;
774 /*
775  * Record the index of this action for use during lookups.
776  */
777   sym->binder = i < KTB_NBIND ? i : -1;
778   return;
779 }
780 
781 /*.......................................................................
782  * Remove all key bindings that came from a specified source.
783  *
784  * Input:
785  *  kt        KeyTab *  The table of key bindings.
786  *  binder  KtBinder    The source of the bindings to be cleared.
787  */
_kt_clear_bindings(KeyTab * kt,KtBinder binder)788 void _kt_clear_bindings(KeyTab *kt, KtBinder binder)
789 {
790   int oldkey;   /* The index of a key in the original binding table */
791   int newkey;   /* The index of a key in the updated binding table */
792 /*
793  * If there is no table, then no bindings exist to be deleted.
794  */
795   if(!kt)
796     return;
797 /*
798  * Clear bindings of the given source.
799  */
800   for(oldkey=0; oldkey<kt->nkey; oldkey++)
801     _kt_assign_action(kt->table + oldkey, binder, 0, NULL);
802 /*
803  * Delete entries that now don't have a binding from any source.
804  */
805   newkey = 0;
806   for(oldkey=0; oldkey<kt->nkey; oldkey++) {
807     KeySym *sym = kt->table + oldkey;
808     if(sym->binder < 0) {
809       _del_StringMemString(kt->smem, sym->keyseq);
810     } else {
811       if(oldkey != newkey)
812 	kt->table[newkey] = *sym;
813       newkey++;
814     };
815   };
816 /*
817  * Record the number of keys that were kept.
818  */
819   kt->nkey = newkey;
820   return;
821 }
822 
823 /*.......................................................................
824  * Translate a backslash escape sequence to a binary character.
825  *
826  * Input:
827  *  string  const char *   The characters that follow the backslash.
828  * Input/Output:
829  *  endp    const char **  If endp!=NULL, on return *endp will be made to
830  *                         point to the character in string[] which follows
831  *                         the escape sequence.
832  * Output:
833  *  return        char     The binary character.
834  */
_kt_backslash_escape(const char * string,const char ** endp)835 static char _kt_backslash_escape(const char *string, const char **endp)
836 {
837   char c;  /* The output character */
838 /*
839  * Is the backslash followed by one or more octal digits?
840  */
841   switch(*string) {
842   case '0': case '1': case '2': case '3':
843   case '4': case '5': case '6': case '7':
844     c = strtol(string, (char **)&string, 8);
845     break;
846   case 'a':
847     c = '\a';
848     string++;
849     break;
850   case 'b':
851     c = '\b';
852     string++;
853     break;
854   case 'e': case 'E': /* Escape */
855     c = GL_ESC_CHAR;
856     string++;
857     break;
858   case 'f':
859     c = '\f';
860     string++;
861     break;
862   case 'n':
863     c = '\n';
864     string++;
865     break;
866   case 'r':
867     c = '\r';
868     string++;
869     break;
870   case 't':
871     c = '\t';
872     string++;
873     break;
874   case 'v':
875     c = '\v';
876     string++;
877     break;
878   case '\0':
879     c = '\\';
880     break;
881   default:
882     c = *string++;
883     break;
884   };
885 /*
886  * Report the character which follows the escape sequence.
887  */
888   if(endp)
889     *endp = string;
890   return c;
891 }
892 
893 /*.......................................................................
894  * Return non-zero if the next two characters are M- and a third character
895  * follows. Otherwise return 0.
896  *
897  * Input:
898  *  string   const char *  The sub-string to scan.
899  * Output:
900  *  return          int    1 - The next two characters are M- and these
901  *                             are followed by at least one character.
902  *                         0 - The next two characters aren't M- or no
903  *                             character follows a M- pair.
904  */
_kt_is_emacs_meta(const char * string)905 static int _kt_is_emacs_meta(const char *string)
906 {
907   return *string++ == 'M' && *string++ == '-' && *string;
908 }
909 
910 /*.......................................................................
911  * Return non-zero if the next two characters are C- and a third character
912  * follows. Otherwise return 0.
913  *
914  * Input:
915  *  string   const char *  The sub-string to scan.
916  * Output:
917  *  return          int    1 - The next two characters are C- and these
918  *                             are followed by at least one character.
919  *                         0 - The next two characters aren't C- or no
920  *                             character follows a C- pair.
921  */
_kt_is_emacs_ctrl(const char * string)922 static int _kt_is_emacs_ctrl(const char *string)
923 {
924   return *string++ == 'C' && *string++ == '-' && *string;
925 }
926 
927 /*.......................................................................
928  * Merge an array of bindings with existing bindings.
929  *
930  * Input:
931  *  kt                    KeyTab *  The table of key bindings.
932  *  binder              KtBinder    The source of the bindings.
933  *  bindings  const KtKeyBinding *  The array of bindings.
934  *  n                        int    The number of bindings in bindings[].
935  * Output:
936  *  return                   int    0 - OK.
937  *                                  1 - Error.
938  */
_kt_add_bindings(KeyTab * kt,KtBinder binder,const KtKeyBinding * bindings,unsigned n)939 int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings,
940 		     unsigned n)
941 {
942   int i;
943 /*
944  * Check the arguments.
945  */
946   if(!kt || !bindings) {
947     errno = EINVAL;
948     if(kt)
949       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
950     return 1;
951   };
952 /*
953  * Install the array of bindings.
954  */
955   for(i=0; i<n; i++) {
956     if(_kt_set_keybinding(kt, binder, bindings[i].keyseq, bindings[i].action))
957       return 1;
958   };
959   return 0;
960 }
961 
962 /*.......................................................................
963  * Lookup the function that implements a given action.
964  *
965  * Input:
966  *  kt          KeyTab *  The table of key bindings.
967  *  action  const char *  The name of the action to look up.
968  * Input/Output:
969  *  fn         KtKeyFn ** If the action is found, the function that
970  *                        implements it will be assigned to *fn. Note
971  *                        that fn can be NULL.
972  *  data          void ** If the action is found, the callback data
973  *                        associated with the action function, will be
974  *                        assigned to *data. Note that data can be NULL.
975  * Output:
976  *  return         int    0 - OK.
977  *                        1 - Action not found.
978  */
_kt_lookup_action(KeyTab * kt,const char * action,KtKeyFn ** fn,void ** data)979 int _kt_lookup_action(KeyTab *kt, const char *action,
980 		      KtKeyFn **fn, void **data)
981 {
982   Symbol *sym;   /* The symbol table entry of the action */
983 /*
984  * Check the arguments.
985  */
986   if(!kt || !action) {
987     errno = EINVAL;
988     if(kt)
989       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
990     return 1;
991   };
992 /*
993  * Lookup the symbol table entry of the action.
994  */
995   sym = _find_HashSymbol(kt->actions, action);
996   if(!sym)
997     return 1;
998 /*
999  * Return the function and ccallback data associated with the action.
1000  */
1001   if(fn)
1002     *fn = (KtKeyFn *) sym->fn;
1003   if(data)
1004     *data = sym->data;
1005   return 0;
1006 }
1007 
1008 /*.......................................................................
1009  * Return extra information (ie. in addition to that provided by errno)
1010  * about the last error to occur in any of the public functions of this
1011  * module.
1012  *
1013  * Input:
1014  *  kt          KeyTab *  The table of key bindings.
1015  * Output:
1016  *  return  const char *  A pointer to the internal buffer in which
1017  *                        the error message is temporarily stored.
1018  */
_kt_last_error(KeyTab * kt)1019 const char *_kt_last_error(KeyTab *kt)
1020 {
1021   return kt ? _err_get_msg(kt->err) : "NULL KeyTab argument";
1022 }
1023