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