17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*1da57d55SToomas Soome  *
47c478bd9Sstevel@tonic-gate  * All rights reserved.
5*1da57d55SToomas Soome  *
67c478bd9Sstevel@tonic-gate  * Permission is hereby granted, free of charge, to any person obtaining a
77c478bd9Sstevel@tonic-gate  * copy of this software and associated documentation files (the
87c478bd9Sstevel@tonic-gate  * "Software"), to deal in the Software without restriction, including
97c478bd9Sstevel@tonic-gate  * without limitation the rights to use, copy, modify, merge, publish,
107c478bd9Sstevel@tonic-gate  * distribute, and/or sell copies of the Software, and to permit persons
117c478bd9Sstevel@tonic-gate  * to whom the Software is furnished to do so, provided that the above
127c478bd9Sstevel@tonic-gate  * copyright notice(s) and this permission notice appear in all copies of
137c478bd9Sstevel@tonic-gate  * the Software and that both the above copyright notice(s) and this
147c478bd9Sstevel@tonic-gate  * permission notice appear in supporting documentation.
15*1da57d55SToomas Soome  *
167c478bd9Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177c478bd9Sstevel@tonic-gate  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
187c478bd9Sstevel@tonic-gate  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
197c478bd9Sstevel@tonic-gate  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
207c478bd9Sstevel@tonic-gate  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
217c478bd9Sstevel@tonic-gate  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
227c478bd9Sstevel@tonic-gate  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
237c478bd9Sstevel@tonic-gate  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
247c478bd9Sstevel@tonic-gate  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*1da57d55SToomas Soome  *
267c478bd9Sstevel@tonic-gate  * Except as contained in this notice, the name of a copyright holder
277c478bd9Sstevel@tonic-gate  * shall not be used in advertising or otherwise to promote the sale, use
287c478bd9Sstevel@tonic-gate  * or other dealings in this Software without prior written authorization
297c478bd9Sstevel@tonic-gate  * of the copyright holder.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <stdio.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <ctype.h>
367c478bd9Sstevel@tonic-gate #include <errno.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include "keytab.h"
397c478bd9Sstevel@tonic-gate #include "strngmem.h"
407c478bd9Sstevel@tonic-gate #include "getline.h"
417c478bd9Sstevel@tonic-gate #include "errmsg.h"
427c478bd9Sstevel@tonic-gate #include "hash.h"
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate  * When allocating or reallocating the key-binding table, how
467c478bd9Sstevel@tonic-gate  * many entries should be added?
477c478bd9Sstevel@tonic-gate  */
487c478bd9Sstevel@tonic-gate #define KT_TABLE_INC 100
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*
517c478bd9Sstevel@tonic-gate  * Define the size of the hash table that is used to associate action
527c478bd9Sstevel@tonic-gate  * names with action functions. This should be a prime number.
537c478bd9Sstevel@tonic-gate  */
547c478bd9Sstevel@tonic-gate #define KT_HASH_SIZE 113
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate  * Define a binary-symbol-table object.
587c478bd9Sstevel@tonic-gate  */
597c478bd9Sstevel@tonic-gate struct KeyTab {
607c478bd9Sstevel@tonic-gate   ErrMsg *err;            /* Information about the last error */
617c478bd9Sstevel@tonic-gate   int size;               /* The allocated dimension of table[] */
627c478bd9Sstevel@tonic-gate   int nkey;               /* The current number of members in the table */
637c478bd9Sstevel@tonic-gate   KeySym *table;          /* The table of lexically sorted key sequences */
647c478bd9Sstevel@tonic-gate   HashTable *actions;     /* The hash table of actions */
657c478bd9Sstevel@tonic-gate   StringMem *smem;        /* Memory for allocating strings */
667c478bd9Sstevel@tonic-gate };
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate static int _kt_extend_table(KeyTab *kt);
697c478bd9Sstevel@tonic-gate static int _kt_parse_keybinding_string(const char *keyseq,
707c478bd9Sstevel@tonic-gate 				       char *binary, int *nc);
717c478bd9Sstevel@tonic-gate static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2);
727c478bd9Sstevel@tonic-gate static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn,
737c478bd9Sstevel@tonic-gate 			      void *data);
747c478bd9Sstevel@tonic-gate static char _kt_backslash_escape(const char *string, const char **endp);
757c478bd9Sstevel@tonic-gate static int _kt_is_emacs_meta(const char *string);
767c478bd9Sstevel@tonic-gate static int _kt_is_emacs_ctrl(const char *string);
777c478bd9Sstevel@tonic-gate static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq,
787c478bd9Sstevel@tonic-gate 					int nc, int *first, int *last);
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate /*.......................................................................
817c478bd9Sstevel@tonic-gate  * Create a new key-binding symbol table.
827c478bd9Sstevel@tonic-gate  *
837c478bd9Sstevel@tonic-gate  * Output:
847c478bd9Sstevel@tonic-gate  *  return  KeyTab *  The new object, or NULL on error.
857c478bd9Sstevel@tonic-gate  */
_new_KeyTab(void)867c478bd9Sstevel@tonic-gate KeyTab *_new_KeyTab(void)
877c478bd9Sstevel@tonic-gate {
887c478bd9Sstevel@tonic-gate   KeyTab *kt;  /* The object to be returned */
897c478bd9Sstevel@tonic-gate /*
907c478bd9Sstevel@tonic-gate  * Allocate the container.
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate   kt = (KeyTab *) malloc(sizeof(KeyTab));
937c478bd9Sstevel@tonic-gate   if(!kt) {
947c478bd9Sstevel@tonic-gate     errno = ENOMEM;
957c478bd9Sstevel@tonic-gate     return NULL;
967c478bd9Sstevel@tonic-gate   };
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate  * Before attempting any operation that might fail, initialize the
997c478bd9Sstevel@tonic-gate  * container at least up to the point at which it can safely be passed
1007c478bd9Sstevel@tonic-gate  * to del_KeyTab().
1017c478bd9Sstevel@tonic-gate  */
1027c478bd9Sstevel@tonic-gate   kt->err = NULL;
1037c478bd9Sstevel@tonic-gate   kt->size = KT_TABLE_INC;
1047c478bd9Sstevel@tonic-gate   kt->nkey = 0;
1057c478bd9Sstevel@tonic-gate   kt->table = NULL;
1067c478bd9Sstevel@tonic-gate   kt->actions = NULL;
1077c478bd9Sstevel@tonic-gate   kt->smem = NULL;
1087c478bd9Sstevel@tonic-gate /*
1097c478bd9Sstevel@tonic-gate  * Allocate a place to record error messages.
1107c478bd9Sstevel@tonic-gate  */
1117c478bd9Sstevel@tonic-gate   kt->err = _new_ErrMsg();
1127c478bd9Sstevel@tonic-gate   if(!kt->err)
1137c478bd9Sstevel@tonic-gate     return _del_KeyTab(kt);
1147c478bd9Sstevel@tonic-gate /*
1157c478bd9Sstevel@tonic-gate  * Allocate the table.
1167c478bd9Sstevel@tonic-gate  */
1177c478bd9Sstevel@tonic-gate   kt->table = (KeySym *) malloc(sizeof(kt->table[0]) * kt->size);
1187c478bd9Sstevel@tonic-gate   if(!kt->table) {
1197c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1207c478bd9Sstevel@tonic-gate     return _del_KeyTab(kt);
1217c478bd9Sstevel@tonic-gate   };
1227c478bd9Sstevel@tonic-gate /*
1237c478bd9Sstevel@tonic-gate  * Allocate a hash table of actions.
1247c478bd9Sstevel@tonic-gate  */
1257c478bd9Sstevel@tonic-gate   kt->actions = _new_HashTable(NULL, KT_HASH_SIZE, IGNORE_CASE, NULL, 0);
1267c478bd9Sstevel@tonic-gate   if(!kt->actions)
1277c478bd9Sstevel@tonic-gate     return _del_KeyTab(kt);
1287c478bd9Sstevel@tonic-gate /*
1297c478bd9Sstevel@tonic-gate  * Allocate a string allocation object. This allows allocation of
1307c478bd9Sstevel@tonic-gate  * small strings without fragmenting the heap.
1317c478bd9Sstevel@tonic-gate  */
1327c478bd9Sstevel@tonic-gate   kt->smem = _new_StringMem(KT_TABLE_INC);
1337c478bd9Sstevel@tonic-gate   if(!kt->smem)
1347c478bd9Sstevel@tonic-gate     return _del_KeyTab(kt);
1357c478bd9Sstevel@tonic-gate   return kt;
1367c478bd9Sstevel@tonic-gate }
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate /*.......................................................................
1397c478bd9Sstevel@tonic-gate  * Delete a KeyTab object.
1407c478bd9Sstevel@tonic-gate  *
1417c478bd9Sstevel@tonic-gate  * Input:
1427c478bd9Sstevel@tonic-gate  *  kt   KeyTab *  The object to be deleted.
1437c478bd9Sstevel@tonic-gate  * Output:
1447c478bd9Sstevel@tonic-gate  *  return KeyTab *  The deleted object (always NULL).
1457c478bd9Sstevel@tonic-gate  */
_del_KeyTab(KeyTab * kt)1467c478bd9Sstevel@tonic-gate KeyTab *_del_KeyTab(KeyTab *kt)
1477c478bd9Sstevel@tonic-gate {
1487c478bd9Sstevel@tonic-gate   if(kt) {
1497c478bd9Sstevel@tonic-gate     if(kt->table)
1507c478bd9Sstevel@tonic-gate       free(kt->table);
1517c478bd9Sstevel@tonic-gate     kt->actions = _del_HashTable(kt->actions);
1527c478bd9Sstevel@tonic-gate     kt->smem = _del_StringMem(kt->smem, 1);
1537c478bd9Sstevel@tonic-gate     kt->err = _del_ErrMsg(kt->err);
1547c478bd9Sstevel@tonic-gate     free(kt);
1557c478bd9Sstevel@tonic-gate   };
1567c478bd9Sstevel@tonic-gate   return NULL;
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate /*.......................................................................
1607c478bd9Sstevel@tonic-gate  * Increase the size of the table to accomodate more keys.
1617c478bd9Sstevel@tonic-gate  *
1627c478bd9Sstevel@tonic-gate  * Input:
1637c478bd9Sstevel@tonic-gate  *  kt       KeyTab *  The table to be extended.
1647c478bd9Sstevel@tonic-gate  * Output:
1657c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
1667c478bd9Sstevel@tonic-gate  *                     1 - Error.
1677c478bd9Sstevel@tonic-gate  */
_kt_extend_table(KeyTab * kt)1687c478bd9Sstevel@tonic-gate static int _kt_extend_table(KeyTab *kt)
1697c478bd9Sstevel@tonic-gate {
1707c478bd9Sstevel@tonic-gate /*
1717c478bd9Sstevel@tonic-gate  * Attempt to increase the size of the table.
1727c478bd9Sstevel@tonic-gate  */
1737c478bd9Sstevel@tonic-gate   KeySym *newtab = (KeySym *) realloc(kt->table, sizeof(kt->table[0]) *
1747c478bd9Sstevel@tonic-gate 				      (kt->size + KT_TABLE_INC));
1757c478bd9Sstevel@tonic-gate /*
1767c478bd9Sstevel@tonic-gate  * Failed?
1777c478bd9Sstevel@tonic-gate  */
1787c478bd9Sstevel@tonic-gate   if(!newtab) {
1797c478bd9Sstevel@tonic-gate     _err_record_msg(kt->err, "Can't extend keybinding table", END_ERR_MSG);
1807c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1817c478bd9Sstevel@tonic-gate     return 1;
1827c478bd9Sstevel@tonic-gate   };
1837c478bd9Sstevel@tonic-gate /*
1847c478bd9Sstevel@tonic-gate  * Install the resized table.
1857c478bd9Sstevel@tonic-gate  */
1867c478bd9Sstevel@tonic-gate   kt->table = newtab;
1877c478bd9Sstevel@tonic-gate   kt->size += KT_TABLE_INC;
1887c478bd9Sstevel@tonic-gate   return 0;
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate /*.......................................................................
1927c478bd9Sstevel@tonic-gate  * Add, update or remove a keybinding to the table.
1937c478bd9Sstevel@tonic-gate  *
1947c478bd9Sstevel@tonic-gate  * Input:
1957c478bd9Sstevel@tonic-gate  *  kt           KeyTab *  The table to add the binding to.
1967c478bd9Sstevel@tonic-gate  *  binder     KtBinder    The source of the binding.
1977c478bd9Sstevel@tonic-gate  *  keyseq   const char *  The key-sequence to bind.
1987c478bd9Sstevel@tonic-gate  *  action         char *  The action to associate with the key sequence, or
1997c478bd9Sstevel@tonic-gate  *                         NULL to remove the action associated with the
2007c478bd9Sstevel@tonic-gate  *                         key sequence.
2017c478bd9Sstevel@tonic-gate  * Output:
2027c478bd9Sstevel@tonic-gate  *  return          int    0 - OK.
2037c478bd9Sstevel@tonic-gate  *                         1 - Error.
2047c478bd9Sstevel@tonic-gate  */
_kt_set_keybinding(KeyTab * kt,KtBinder binder,const char * keyseq,const char * action)2057c478bd9Sstevel@tonic-gate int _kt_set_keybinding(KeyTab *kt, KtBinder binder, const char *keyseq,
2067c478bd9Sstevel@tonic-gate 		       const char *action)
2077c478bd9Sstevel@tonic-gate {
2087c478bd9Sstevel@tonic-gate   KtKeyFn *keyfn; /* The action function */
2097c478bd9Sstevel@tonic-gate   void *data;     /* The callback data of the action function */
2107c478bd9Sstevel@tonic-gate /*
2117c478bd9Sstevel@tonic-gate  * Check arguments.
2127c478bd9Sstevel@tonic-gate  */
2137c478bd9Sstevel@tonic-gate   if(kt==NULL || !keyseq) {
2147c478bd9Sstevel@tonic-gate     errno = EINVAL;
2157c478bd9Sstevel@tonic-gate     if(kt)
2167c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
2177c478bd9Sstevel@tonic-gate     return 1;
2187c478bd9Sstevel@tonic-gate   };
2197c478bd9Sstevel@tonic-gate /*
2207c478bd9Sstevel@tonic-gate  * Lookup the function that implements the specified action.
2217c478bd9Sstevel@tonic-gate  */
2227c478bd9Sstevel@tonic-gate   if(!action) {
2237c478bd9Sstevel@tonic-gate     keyfn = 0;
2247c478bd9Sstevel@tonic-gate     data = NULL;
2257c478bd9Sstevel@tonic-gate   } else {
2267c478bd9Sstevel@tonic-gate     Symbol *sym = _find_HashSymbol(kt->actions, action);
2277c478bd9Sstevel@tonic-gate     if(!sym) {
2287c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "Unknown key-binding action: ", action,
2297c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
2307c478bd9Sstevel@tonic-gate       errno = EINVAL;
2317c478bd9Sstevel@tonic-gate       return 1;
2327c478bd9Sstevel@tonic-gate     };
2337c478bd9Sstevel@tonic-gate     keyfn = (KtKeyFn *) sym->fn;
2347c478bd9Sstevel@tonic-gate     data = sym->data;
2357c478bd9Sstevel@tonic-gate   };
2367c478bd9Sstevel@tonic-gate /*
2377c478bd9Sstevel@tonic-gate  * Record the action in the table.
2387c478bd9Sstevel@tonic-gate  */
2397c478bd9Sstevel@tonic-gate   return _kt_set_keyfn(kt, binder, keyseq, keyfn, data);
2407c478bd9Sstevel@tonic-gate }
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate /*.......................................................................
2437c478bd9Sstevel@tonic-gate  * Add, update or remove a keybinding to the table, specifying an action
2447c478bd9Sstevel@tonic-gate  * function directly.
2457c478bd9Sstevel@tonic-gate  *
2467c478bd9Sstevel@tonic-gate  * Input:
2477c478bd9Sstevel@tonic-gate  *  kt       KeyTab *  The table to add the binding to.
2487c478bd9Sstevel@tonic-gate  *  binder KtBinder    The source of the binding.
2497c478bd9Sstevel@tonic-gate  *  keyseq     char *  The key-sequence to bind.
2507c478bd9Sstevel@tonic-gate  *  keyfn   KtKeyFn *  The action function, or NULL to remove any existing
2517c478bd9Sstevel@tonic-gate  *                     action function.
2527c478bd9Sstevel@tonic-gate  *  data       void *  A pointer to anonymous data to be passed to keyfn
2537c478bd9Sstevel@tonic-gate  *                     whenever it is called.
2547c478bd9Sstevel@tonic-gate  * Output:
2557c478bd9Sstevel@tonic-gate  *  return     int    0 - OK.
2567c478bd9Sstevel@tonic-gate  *                    1 - Error.
2577c478bd9Sstevel@tonic-gate  */
_kt_set_keyfn(KeyTab * kt,KtBinder binder,const char * keyseq,KtKeyFn * keyfn,void * data)2587c478bd9Sstevel@tonic-gate int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq,
2597c478bd9Sstevel@tonic-gate 		  KtKeyFn *keyfn, void *data)
2607c478bd9Sstevel@tonic-gate {
2617c478bd9Sstevel@tonic-gate   const char *kptr;  /* A pointer into keyseq[] */
2627c478bd9Sstevel@tonic-gate   char *binary;      /* The binary version of keyseq[] */
2637c478bd9Sstevel@tonic-gate   int nc;            /* The number of characters in binary[] */
2647c478bd9Sstevel@tonic-gate   int first,last;    /* The first and last entries in the table which */
2657c478bd9Sstevel@tonic-gate                      /*  minimally match. */
2667c478bd9Sstevel@tonic-gate   int size;          /* The size to allocate for the binary string */
2677c478bd9Sstevel@tonic-gate   int i;
2687c478bd9Sstevel@tonic-gate /*
2697c478bd9Sstevel@tonic-gate  * Check arguments.
2707c478bd9Sstevel@tonic-gate  */
2717c478bd9Sstevel@tonic-gate   if(kt==NULL || !keyseq) {
2727c478bd9Sstevel@tonic-gate     errno = EINVAL;
2737c478bd9Sstevel@tonic-gate     if(kt)
2747c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
2757c478bd9Sstevel@tonic-gate     return 1;
2767c478bd9Sstevel@tonic-gate   };
2777c478bd9Sstevel@tonic-gate /*
2787c478bd9Sstevel@tonic-gate  * Work out a pessimistic estimate of how much space will be needed
2797c478bd9Sstevel@tonic-gate  * for the binary copy of the string, noting that binary meta characters
2807c478bd9Sstevel@tonic-gate  * embedded in the input string get split into two characters.
2817c478bd9Sstevel@tonic-gate  */
2827c478bd9Sstevel@tonic-gate   for(size=0,kptr = keyseq; *kptr; kptr++)
2837c478bd9Sstevel@tonic-gate     size += IS_META_CHAR(*kptr) ? 2 : 1;
2847c478bd9Sstevel@tonic-gate /*
2857c478bd9Sstevel@tonic-gate  * Allocate a string that has the length of keyseq[].
2867c478bd9Sstevel@tonic-gate  */
2877c478bd9Sstevel@tonic-gate   binary = _new_StringMemString(kt->smem, size + 1);
2887c478bd9Sstevel@tonic-gate   if(!binary) {
2897c478bd9Sstevel@tonic-gate     errno = ENOMEM;
2907c478bd9Sstevel@tonic-gate     _err_record_msg(kt->err, "Insufficient memory to record key sequence",
2917c478bd9Sstevel@tonic-gate 		    END_ERR_MSG);
2927c478bd9Sstevel@tonic-gate     return 1;
2937c478bd9Sstevel@tonic-gate   };
2947c478bd9Sstevel@tonic-gate /*
2957c478bd9Sstevel@tonic-gate  * Convert control and octal character specifications to binary characters.
2967c478bd9Sstevel@tonic-gate  */
2977c478bd9Sstevel@tonic-gate   if(_kt_parse_keybinding_string(keyseq, binary, &nc)) {
2987c478bd9Sstevel@tonic-gate     binary = _del_StringMemString(kt->smem, binary);
2997c478bd9Sstevel@tonic-gate     return 1;
3007c478bd9Sstevel@tonic-gate   };
3017c478bd9Sstevel@tonic-gate /*
3027c478bd9Sstevel@tonic-gate  * Lookup the position in the table at which to insert the binding.
3037c478bd9Sstevel@tonic-gate  */
3047c478bd9Sstevel@tonic-gate   switch(_kt_locate_keybinding(kt, binary, nc, &first, &last)) {
3057c478bd9Sstevel@tonic-gate /*
3067c478bd9Sstevel@tonic-gate  * If an exact match for the key-sequence is already in the table,
3077c478bd9Sstevel@tonic-gate  * simply replace its binding function (or delete the entry if
3087c478bd9Sstevel@tonic-gate  * the new binding is 0).
3097c478bd9Sstevel@tonic-gate  */
3107c478bd9Sstevel@tonic-gate   case KT_EXACT_MATCH:
3117c478bd9Sstevel@tonic-gate     if(keyfn) {
3127c478bd9Sstevel@tonic-gate       _kt_assign_action(kt->table + first, binder, keyfn, data);
3137c478bd9Sstevel@tonic-gate     } else {
3147c478bd9Sstevel@tonic-gate       _del_StringMemString(kt->smem, kt->table[first].keyseq);
3157c478bd9Sstevel@tonic-gate       memmove(kt->table + first, kt->table + first + 1,
3167c478bd9Sstevel@tonic-gate 	      (kt->nkey - first - 1) * sizeof(kt->table[0]));
3177c478bd9Sstevel@tonic-gate       kt->nkey--;
3187c478bd9Sstevel@tonic-gate     };
3197c478bd9Sstevel@tonic-gate     binary = _del_StringMemString(kt->smem, binary);
3207c478bd9Sstevel@tonic-gate     break;
3217c478bd9Sstevel@tonic-gate /*
3227c478bd9Sstevel@tonic-gate  * If an ambiguous match has been found and we are installing a
3237c478bd9Sstevel@tonic-gate  * callback, then our new key-sequence would hide all of the ambiguous
3247c478bd9Sstevel@tonic-gate  * matches, so we shouldn't allow it.
3257c478bd9Sstevel@tonic-gate  */
3267c478bd9Sstevel@tonic-gate   case KT_AMBIG_MATCH:
3277c478bd9Sstevel@tonic-gate     if(keyfn) {
3287c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "Can't bind \"", keyseq,
3297c478bd9Sstevel@tonic-gate 		      "\", because it is a prefix of another binding",
3307c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
3317c478bd9Sstevel@tonic-gate       binary = _del_StringMemString(kt->smem, binary);
3327c478bd9Sstevel@tonic-gate       errno = EPERM;
3337c478bd9Sstevel@tonic-gate       return 1;
3347c478bd9Sstevel@tonic-gate     };
3357c478bd9Sstevel@tonic-gate     break;
3367c478bd9Sstevel@tonic-gate /*
3377c478bd9Sstevel@tonic-gate  * If the entry doesn't exist, create it.
3387c478bd9Sstevel@tonic-gate  */
3397c478bd9Sstevel@tonic-gate   case KT_NO_MATCH:
3407c478bd9Sstevel@tonic-gate /*
3417c478bd9Sstevel@tonic-gate  * Add a new binding?
3427c478bd9Sstevel@tonic-gate  */
3437c478bd9Sstevel@tonic-gate     if(keyfn) {
3447c478bd9Sstevel@tonic-gate       KeySym *sym;
3457c478bd9Sstevel@tonic-gate /*
3467c478bd9Sstevel@tonic-gate  * We will need a new entry, extend the table if needed.
3477c478bd9Sstevel@tonic-gate  */
3487c478bd9Sstevel@tonic-gate       if(kt->nkey + 1 > kt->size) {
3497c478bd9Sstevel@tonic-gate 	if(_kt_extend_table(kt)) {
3507c478bd9Sstevel@tonic-gate 	  binary = _del_StringMemString(kt->smem, binary);
3517c478bd9Sstevel@tonic-gate 	  return 1;
3527c478bd9Sstevel@tonic-gate 	};
3537c478bd9Sstevel@tonic-gate       };
3547c478bd9Sstevel@tonic-gate /*
3557c478bd9Sstevel@tonic-gate  * Make space to insert the new key-sequence before 'last'.
3567c478bd9Sstevel@tonic-gate  */
3577c478bd9Sstevel@tonic-gate       if(last < kt->nkey) {
3587c478bd9Sstevel@tonic-gate 	memmove(kt->table + last + 1, kt->table + last,
3597c478bd9Sstevel@tonic-gate 		(kt->nkey - last) * sizeof(kt->table[0]));
3607c478bd9Sstevel@tonic-gate       };
3617c478bd9Sstevel@tonic-gate /*
3627c478bd9Sstevel@tonic-gate  * Insert the new binding in the vacated position.
3637c478bd9Sstevel@tonic-gate  */
3647c478bd9Sstevel@tonic-gate       sym = kt->table + last;
3657c478bd9Sstevel@tonic-gate       sym->keyseq = binary;
3667c478bd9Sstevel@tonic-gate       sym->nc = nc;
3677c478bd9Sstevel@tonic-gate       for(i=0; i<KTB_NBIND; i++) {
3687c478bd9Sstevel@tonic-gate 	KtAction *action = sym->actions + i;
3697c478bd9Sstevel@tonic-gate 	action->fn = 0;
3707c478bd9Sstevel@tonic-gate 	action->data = NULL;
3717c478bd9Sstevel@tonic-gate       };
3727c478bd9Sstevel@tonic-gate       sym->binder = -1;
3737c478bd9Sstevel@tonic-gate       _kt_assign_action(sym, binder, keyfn, data);
3747c478bd9Sstevel@tonic-gate       kt->nkey++;
3757c478bd9Sstevel@tonic-gate     };
3767c478bd9Sstevel@tonic-gate     break;
3777c478bd9Sstevel@tonic-gate   case KT_BAD_MATCH:
3787c478bd9Sstevel@tonic-gate     binary = _del_StringMemString(kt->smem, binary);
3797c478bd9Sstevel@tonic-gate     return 1;
3807c478bd9Sstevel@tonic-gate     break;
3817c478bd9Sstevel@tonic-gate   };
3827c478bd9Sstevel@tonic-gate   return 0;
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate /*.......................................................................
3867c478bd9Sstevel@tonic-gate  * Perform a min-match lookup of a key-binding.
3877c478bd9Sstevel@tonic-gate  *
3887c478bd9Sstevel@tonic-gate  * Input:
3897c478bd9Sstevel@tonic-gate  *  kt          KeyTab *   The keybinding table to lookup in.
3907c478bd9Sstevel@tonic-gate  *  binary_keyseq char *   The binary key-sequence to lookup.
3917c478bd9Sstevel@tonic-gate  *  nc             int     the number of characters in keyseq[].
3927c478bd9Sstevel@tonic-gate  * Input/Output:
3937c478bd9Sstevel@tonic-gate  *  first,last     int *   If there is an ambiguous or exact match, the indexes
3947c478bd9Sstevel@tonic-gate  *                         of the first and last symbols that minimally match
3957c478bd9Sstevel@tonic-gate  *                         will be assigned to *first and *last respectively.
3967c478bd9Sstevel@tonic-gate  *                         If there is no match, then first and last will
3977c478bd9Sstevel@tonic-gate  *                         bracket the location where the symbol should be
3987c478bd9Sstevel@tonic-gate  *                         inserted.
3997c478bd9Sstevel@tonic-gate  * Output:
4007c478bd9Sstevel@tonic-gate  *  return  KtKeyMatch     One of the following enumerators:
4017c478bd9Sstevel@tonic-gate  *                          KT_EXACT_MATCH - An exact match was found.
4027c478bd9Sstevel@tonic-gate  *                          KT_AMBIG_MATCH - An ambiguous match was found.
4037c478bd9Sstevel@tonic-gate  *                          KT_NO_MATCH    - No match was found.
4047c478bd9Sstevel@tonic-gate  *                          KT_BAD_MATCH   - An error occurred while searching.
4057c478bd9Sstevel@tonic-gate  */
_kt_locate_keybinding(KeyTab * kt,const char * binary_keyseq,int nc,int * first,int * last)4067c478bd9Sstevel@tonic-gate static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq,
4077c478bd9Sstevel@tonic-gate 					int nc, int *first, int *last)
4087c478bd9Sstevel@tonic-gate {
4097c478bd9Sstevel@tonic-gate   int mid;     /* The index at which to bisect the table */
4107c478bd9Sstevel@tonic-gate   int bot;     /* The lowest index of the table not searched yet */
4117c478bd9Sstevel@tonic-gate   int top;     /* The highest index of the table not searched yet */
4127c478bd9Sstevel@tonic-gate   int test;    /* The return value of strcmp() */
4137c478bd9Sstevel@tonic-gate /*
4147c478bd9Sstevel@tonic-gate  * Perform a binary search for the key-sequence.
4157c478bd9Sstevel@tonic-gate  */
4167c478bd9Sstevel@tonic-gate   bot = 0;
4177c478bd9Sstevel@tonic-gate   top = kt->nkey - 1;
4187c478bd9Sstevel@tonic-gate   while(top >= bot) {
4197c478bd9Sstevel@tonic-gate     mid = (top + bot)/2;
4207c478bd9Sstevel@tonic-gate     test = _kt_compare_strings(kt->table[mid].keyseq, kt->table[mid].nc,
4217c478bd9Sstevel@tonic-gate 			   binary_keyseq, nc);
4227c478bd9Sstevel@tonic-gate     if(test > 0)
4237c478bd9Sstevel@tonic-gate       top = mid - 1;
4247c478bd9Sstevel@tonic-gate     else if(test < 0)
4257c478bd9Sstevel@tonic-gate       bot = mid + 1;
4267c478bd9Sstevel@tonic-gate     else {
4277c478bd9Sstevel@tonic-gate       *first = *last = mid;
4287c478bd9Sstevel@tonic-gate       return KT_EXACT_MATCH;
4297c478bd9Sstevel@tonic-gate     };
4307c478bd9Sstevel@tonic-gate   };
4317c478bd9Sstevel@tonic-gate /*
4327c478bd9Sstevel@tonic-gate  * An exact match wasn't found, but top is the index just below the
4337c478bd9Sstevel@tonic-gate  * index where a match would be found, and bot is the index just above
4347c478bd9Sstevel@tonic-gate  * where the match ought to be found.
4357c478bd9Sstevel@tonic-gate  */
4367c478bd9Sstevel@tonic-gate   *first = top;
4377c478bd9Sstevel@tonic-gate   *last = bot;
4387c478bd9Sstevel@tonic-gate /*
4397c478bd9Sstevel@tonic-gate  * See if any ambiguous matches exist, and if so make *first and *last
4407c478bd9Sstevel@tonic-gate  * refer to the first and last matches.
4417c478bd9Sstevel@tonic-gate  */
4427c478bd9Sstevel@tonic-gate   if(*last < kt->nkey && kt->table[*last].nc > nc &&
4437c478bd9Sstevel@tonic-gate      _kt_compare_strings(kt->table[*last].keyseq, nc, binary_keyseq, nc)==0) {
4447c478bd9Sstevel@tonic-gate     *first = *last;
4457c478bd9Sstevel@tonic-gate     while(*last+1 < kt->nkey && kt->table[*last+1].nc > nc &&
4467c478bd9Sstevel@tonic-gate 	  _kt_compare_strings(kt->table[*last+1].keyseq, nc, binary_keyseq, nc)==0)
4477c478bd9Sstevel@tonic-gate       (*last)++;
4487c478bd9Sstevel@tonic-gate     return KT_AMBIG_MATCH;
4497c478bd9Sstevel@tonic-gate   };
4507c478bd9Sstevel@tonic-gate /*
4517c478bd9Sstevel@tonic-gate  * No match.
4527c478bd9Sstevel@tonic-gate  */
4537c478bd9Sstevel@tonic-gate   return KT_NO_MATCH;
4547c478bd9Sstevel@tonic-gate }
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate /*.......................................................................
4577c478bd9Sstevel@tonic-gate  * Lookup the sub-array of key-bindings who's key-sequences minimally
4587c478bd9Sstevel@tonic-gate  * match a given key-sequence.
4597c478bd9Sstevel@tonic-gate  *
4607c478bd9Sstevel@tonic-gate  * Input:
4617c478bd9Sstevel@tonic-gate  *  kt          KeyTab *   The keybinding table to lookup in.
4627c478bd9Sstevel@tonic-gate  *  binary_keyseq char *   The binary key-sequence to lookup.
4637c478bd9Sstevel@tonic-gate  *  nc             int     the number of characters in keyseq[].
4647c478bd9Sstevel@tonic-gate  * Input/Output:
4657c478bd9Sstevel@tonic-gate  *  matches     KeySym **  The array of minimally matching symbols
4667c478bd9Sstevel@tonic-gate  *                         can be found in (*matches)[0..nmatch-1], unless
4677c478bd9Sstevel@tonic-gate  *                         no match was found, in which case *matches will
4687c478bd9Sstevel@tonic-gate  *                         be set to NULL.
4697c478bd9Sstevel@tonic-gate  *  nmatch         int     The number of ambiguously matching symbols. This
4707c478bd9Sstevel@tonic-gate  *                         will be 0 if there is no match, 1 for an exact
4717c478bd9Sstevel@tonic-gate  *                         match, and a number greater than 1 for an ambiguous
4727c478bd9Sstevel@tonic-gate  *                         match.
4737c478bd9Sstevel@tonic-gate  * Output:
4747c478bd9Sstevel@tonic-gate  *  return  KtKeyMatch     One of the following enumerators:
4757c478bd9Sstevel@tonic-gate  *                          KT_EXACT_MATCH - An exact match was found.
4767c478bd9Sstevel@tonic-gate  *                          KT_AMBIG_MATCH - An ambiguous match was found.
4777c478bd9Sstevel@tonic-gate  *                          KT_NO_MATCH    - No match was found.
4787c478bd9Sstevel@tonic-gate  *                          KT_BAD_MATCH   - An error occurred while searching.
4797c478bd9Sstevel@tonic-gate  */
_kt_lookup_keybinding(KeyTab * kt,const char * binary_keyseq,int nc,KeySym ** matches,int * nmatch)4807c478bd9Sstevel@tonic-gate KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq,
4817c478bd9Sstevel@tonic-gate 				 int nc, KeySym **matches, int *nmatch)
4827c478bd9Sstevel@tonic-gate {
4837c478bd9Sstevel@tonic-gate   KtKeyMatch status;  /* The return status */
4847c478bd9Sstevel@tonic-gate   int first,last;     /* The indexes of the first and last matching entry */
4857c478bd9Sstevel@tonic-gate                       /* in the symbol table. */
4867c478bd9Sstevel@tonic-gate /*
4877c478bd9Sstevel@tonic-gate  * Check the arguments.
4887c478bd9Sstevel@tonic-gate  */
4897c478bd9Sstevel@tonic-gate   if(!kt || !binary_keyseq || !matches || !nmatch || nc < 0) {
4907c478bd9Sstevel@tonic-gate     errno = EINVAL;
4917c478bd9Sstevel@tonic-gate     if(kt)
4927c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
4937c478bd9Sstevel@tonic-gate     return KT_BAD_MATCH;
4947c478bd9Sstevel@tonic-gate   };
4957c478bd9Sstevel@tonic-gate /*
4967c478bd9Sstevel@tonic-gate  * Lookup the indexes of the binding-table entries that bracket the
4977c478bd9Sstevel@tonic-gate  * target key-sequence.
4987c478bd9Sstevel@tonic-gate  */
4997c478bd9Sstevel@tonic-gate   status = _kt_locate_keybinding(kt, binary_keyseq, nc, &first, &last);
5007c478bd9Sstevel@tonic-gate /*
5017c478bd9Sstevel@tonic-gate  * Translate the indexes into the corresponding subarray of matching
5027c478bd9Sstevel@tonic-gate  * table entries.
5037c478bd9Sstevel@tonic-gate  */
5047c478bd9Sstevel@tonic-gate   switch(status) {
5057c478bd9Sstevel@tonic-gate   case KT_EXACT_MATCH:
5067c478bd9Sstevel@tonic-gate   case KT_AMBIG_MATCH:
5077c478bd9Sstevel@tonic-gate     *matches = kt->table + first;
5087c478bd9Sstevel@tonic-gate     *nmatch = last - first + 1;
5097c478bd9Sstevel@tonic-gate     break;
5107c478bd9Sstevel@tonic-gate   default:
5117c478bd9Sstevel@tonic-gate     *matches = NULL;
5127c478bd9Sstevel@tonic-gate     *nmatch = 0;
5137c478bd9Sstevel@tonic-gate     break;
5147c478bd9Sstevel@tonic-gate   };
5157c478bd9Sstevel@tonic-gate   return status;
5167c478bd9Sstevel@tonic-gate }
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate /*.......................................................................
5197c478bd9Sstevel@tonic-gate  * Convert a keybinding string into a uniq binary representation.
5207c478bd9Sstevel@tonic-gate  *
5217c478bd9Sstevel@tonic-gate  * Control characters can be given directly in their binary form,
5227c478bd9Sstevel@tonic-gate  * expressed as either ^ or C-, followed by the character, expressed in
5237c478bd9Sstevel@tonic-gate  * octal, like \129 or via C-style backslash escapes, with the addition
5247c478bd9Sstevel@tonic-gate  * of '\E' to denote the escape key. Similarly, meta characters can be
5257c478bd9Sstevel@tonic-gate  * given directly in binary or expressed as M- followed by the character.
5267c478bd9Sstevel@tonic-gate  * Meta characters are recorded as two characters in the binary output
5277c478bd9Sstevel@tonic-gate  * string, the first being the escape key, and the second being the key
5287c478bd9Sstevel@tonic-gate  * that was modified by the meta key. This means that binding to
5297c478bd9Sstevel@tonic-gate  * \EA or ^[A or M-A are all equivalent.
5307c478bd9Sstevel@tonic-gate  *
5317c478bd9Sstevel@tonic-gate  * Input:
5327c478bd9Sstevel@tonic-gate  *  keyseq   char *  The key sequence being added.
5337c478bd9Sstevel@tonic-gate  * Input/Output:
5347c478bd9Sstevel@tonic-gate  *  binary   char *  The binary version of the key sequence will be
5357c478bd9Sstevel@tonic-gate  *                   assigned to binary[], which must have at least
5367c478bd9Sstevel@tonic-gate  *                   as many characters as keyseq[] plus the number
5377c478bd9Sstevel@tonic-gate  *                   of embedded binary meta characters.
5387c478bd9Sstevel@tonic-gate  *  nc        int *  The number of characters assigned to binary[]
5397c478bd9Sstevel@tonic-gate  *                   will be recorded in *nc.
5407c478bd9Sstevel@tonic-gate  * Output:
5417c478bd9Sstevel@tonic-gate  *  return    int    0 - OK.
5427c478bd9Sstevel@tonic-gate  *                   1 - Error.
5437c478bd9Sstevel@tonic-gate  */
_kt_parse_keybinding_string(const char * keyseq,char * binary,int * nc)5447c478bd9Sstevel@tonic-gate static int _kt_parse_keybinding_string(const char *keyseq, char *binary,
5457c478bd9Sstevel@tonic-gate 				       int *nc)
5467c478bd9Sstevel@tonic-gate {
5477c478bd9Sstevel@tonic-gate   const char *iptr = keyseq;   /* Pointer into keyseq[] */
5487c478bd9Sstevel@tonic-gate   char *optr = binary;         /* Pointer into binary[] */
5497c478bd9Sstevel@tonic-gate   char c;                      /* An intermediate character */
5507c478bd9Sstevel@tonic-gate /*
5517c478bd9Sstevel@tonic-gate  * Parse the input characters until they are exhausted or the
5527c478bd9Sstevel@tonic-gate  * output string becomes full.
5537c478bd9Sstevel@tonic-gate  */
5547c478bd9Sstevel@tonic-gate   while(*iptr) {
5557c478bd9Sstevel@tonic-gate /*
5567c478bd9Sstevel@tonic-gate  * Check for special characters.
5577c478bd9Sstevel@tonic-gate  */
5587c478bd9Sstevel@tonic-gate     switch(*iptr) {
5597c478bd9Sstevel@tonic-gate     case '^':        /* A control character specification */
5607c478bd9Sstevel@tonic-gate /*
5617c478bd9Sstevel@tonic-gate  * Convert the caret expression into the corresponding control
5627c478bd9Sstevel@tonic-gate  * character unless no character follows the caret, in which case
5637c478bd9Sstevel@tonic-gate  * record a literal caret.
5647c478bd9Sstevel@tonic-gate  */
5657c478bd9Sstevel@tonic-gate       if(iptr[1]) {
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate  * Get the next, possibly escaped, character.
5687c478bd9Sstevel@tonic-gate  */
5697c478bd9Sstevel@tonic-gate 	if(iptr[1] == '\\') {
5707c478bd9Sstevel@tonic-gate 	  c = _kt_backslash_escape(iptr+2, &iptr);
5717c478bd9Sstevel@tonic-gate 	} else {
5727c478bd9Sstevel@tonic-gate 	  c = iptr[1];
5737c478bd9Sstevel@tonic-gate 	  iptr += 2;
5747c478bd9Sstevel@tonic-gate 	};
5757c478bd9Sstevel@tonic-gate /*
5767c478bd9Sstevel@tonic-gate  * Convert the character to a control character.
5777c478bd9Sstevel@tonic-gate  */
5787c478bd9Sstevel@tonic-gate 	*optr++ = MAKE_CTRL(c);
5797c478bd9Sstevel@tonic-gate       } else {
5807c478bd9Sstevel@tonic-gate 	*optr++ = *iptr++;
5817c478bd9Sstevel@tonic-gate       };
5827c478bd9Sstevel@tonic-gate       break;
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate  * A backslash-escaped character?
5857c478bd9Sstevel@tonic-gate  */
5867c478bd9Sstevel@tonic-gate     case '\\':
5877c478bd9Sstevel@tonic-gate /*
5887c478bd9Sstevel@tonic-gate  * Convert the escape sequence to a binary character.
5897c478bd9Sstevel@tonic-gate  */
5907c478bd9Sstevel@tonic-gate       *optr++ = _kt_backslash_escape(iptr+1, &iptr);
5917c478bd9Sstevel@tonic-gate       break;
5927c478bd9Sstevel@tonic-gate /*
5937c478bd9Sstevel@tonic-gate  * Possibly an emacs-style meta character?
5947c478bd9Sstevel@tonic-gate  */
5957c478bd9Sstevel@tonic-gate     case 'M':
5967c478bd9Sstevel@tonic-gate       if(_kt_is_emacs_meta(iptr)) {
5977c478bd9Sstevel@tonic-gate 	*optr++ = GL_ESC_CHAR;
5987c478bd9Sstevel@tonic-gate 	iptr += 2;
5997c478bd9Sstevel@tonic-gate       } else {
6007c478bd9Sstevel@tonic-gate 	*optr++ = *iptr++;
6017c478bd9Sstevel@tonic-gate       };
6027c478bd9Sstevel@tonic-gate       break;
6037c478bd9Sstevel@tonic-gate /*
6047c478bd9Sstevel@tonic-gate  * Possibly an emacs-style control character specification?
6057c478bd9Sstevel@tonic-gate  */
6067c478bd9Sstevel@tonic-gate     case 'C':
6077c478bd9Sstevel@tonic-gate       if(_kt_is_emacs_ctrl(iptr)) {
6087c478bd9Sstevel@tonic-gate 	*optr++ = MAKE_CTRL(iptr[2]);
6097c478bd9Sstevel@tonic-gate 	iptr += 3;
6107c478bd9Sstevel@tonic-gate       } else {
6117c478bd9Sstevel@tonic-gate 	*optr++ = *iptr++;
6127c478bd9Sstevel@tonic-gate       };
6137c478bd9Sstevel@tonic-gate       break;
6147c478bd9Sstevel@tonic-gate     default:
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate /*
6177c478bd9Sstevel@tonic-gate  * Convert embedded meta characters into an escape character followed
6187c478bd9Sstevel@tonic-gate  * by the meta-unmodified character.
6197c478bd9Sstevel@tonic-gate  */
6207c478bd9Sstevel@tonic-gate       if(IS_META_CHAR(*iptr)) {
6217c478bd9Sstevel@tonic-gate 	*optr++ = GL_ESC_CHAR;
6227c478bd9Sstevel@tonic-gate 	*optr++ = META_TO_CHAR(*iptr);
6237c478bd9Sstevel@tonic-gate 	iptr++;
6247c478bd9Sstevel@tonic-gate /*
6257c478bd9Sstevel@tonic-gate  * To allow keysequences that start with printable characters to
6267c478bd9Sstevel@tonic-gate  * be distinguished from the cursor-key keywords, prepend a backslash
6277c478bd9Sstevel@tonic-gate  * to the former. This same operation is performed in gl_interpret_char()
6287c478bd9Sstevel@tonic-gate  * before looking up a keysequence that starts with a printable character.
6297c478bd9Sstevel@tonic-gate  */
6307c478bd9Sstevel@tonic-gate       } else if(iptr==keyseq && !IS_CTRL_CHAR(*iptr) &&
6317c478bd9Sstevel@tonic-gate 		strcmp(keyseq, "up") != 0 && strcmp(keyseq, "down") != 0 &&
6327c478bd9Sstevel@tonic-gate 		strcmp(keyseq, "left") != 0 && strcmp(keyseq, "right") != 0) {
6337c478bd9Sstevel@tonic-gate 	*optr++ = '\\';
6347c478bd9Sstevel@tonic-gate 	*optr++ = *iptr++;
6357c478bd9Sstevel@tonic-gate       } else {
6367c478bd9Sstevel@tonic-gate 	*optr++ = *iptr++;
6377c478bd9Sstevel@tonic-gate       };
6387c478bd9Sstevel@tonic-gate     };
6397c478bd9Sstevel@tonic-gate   };
6407c478bd9Sstevel@tonic-gate /*
6417c478bd9Sstevel@tonic-gate  * How many characters were placed in the output array?
6427c478bd9Sstevel@tonic-gate  */
6437c478bd9Sstevel@tonic-gate   *nc = optr - binary;
6447c478bd9Sstevel@tonic-gate   return 0;
6457c478bd9Sstevel@tonic-gate }
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate /*.......................................................................
6487c478bd9Sstevel@tonic-gate  * Add, remove or modify an action.
6497c478bd9Sstevel@tonic-gate  *
6507c478bd9Sstevel@tonic-gate  * Input:
6517c478bd9Sstevel@tonic-gate  *  kt     KeyTab *  The key-binding table.
6527c478bd9Sstevel@tonic-gate  *  action   char *  The name of the action.
6537c478bd9Sstevel@tonic-gate  *  fn    KtKeyFn *  The function that implements the action, or NULL
6547c478bd9Sstevel@tonic-gate  *                   to remove an existing action.
6557c478bd9Sstevel@tonic-gate  *  data     void *  A pointer to arbitrary callback data to pass to the
6567c478bd9Sstevel@tonic-gate  *                   action function whenever it is called.
6577c478bd9Sstevel@tonic-gate  * Output:
6587c478bd9Sstevel@tonic-gate  *  return    int    0 - OK.
6597c478bd9Sstevel@tonic-gate  *                   1 - Error.
6607c478bd9Sstevel@tonic-gate  */
_kt_set_action(KeyTab * kt,const char * action,KtKeyFn * fn,void * data)6617c478bd9Sstevel@tonic-gate int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn, void *data)
6627c478bd9Sstevel@tonic-gate {
6637c478bd9Sstevel@tonic-gate   Symbol *sym;   /* The symbol table entry of the action */
6647c478bd9Sstevel@tonic-gate /*
6657c478bd9Sstevel@tonic-gate  * Check the arguments.
6667c478bd9Sstevel@tonic-gate  */
6677c478bd9Sstevel@tonic-gate   if(!kt || !action) {
6687c478bd9Sstevel@tonic-gate     errno = EINVAL;
6697c478bd9Sstevel@tonic-gate     if(kt)
6707c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
6717c478bd9Sstevel@tonic-gate     return 1;
6727c478bd9Sstevel@tonic-gate   };
6737c478bd9Sstevel@tonic-gate /*
6747c478bd9Sstevel@tonic-gate  * If no function was provided, delete an existing action.
6757c478bd9Sstevel@tonic-gate  */
6767c478bd9Sstevel@tonic-gate   if(!fn) {
6777c478bd9Sstevel@tonic-gate     sym = _del_HashSymbol(kt->actions, action);
6787c478bd9Sstevel@tonic-gate     return 0;
6797c478bd9Sstevel@tonic-gate   };
6807c478bd9Sstevel@tonic-gate /*
6817c478bd9Sstevel@tonic-gate  * If the action already exists, replace its action function.
6827c478bd9Sstevel@tonic-gate  */
6837c478bd9Sstevel@tonic-gate   sym = _find_HashSymbol(kt->actions, action);
6847c478bd9Sstevel@tonic-gate   if(sym) {
6857c478bd9Sstevel@tonic-gate     sym->fn = (void (*)(void))fn;
6867c478bd9Sstevel@tonic-gate     sym->data = data;
6877c478bd9Sstevel@tonic-gate     return 0;
6887c478bd9Sstevel@tonic-gate   };
6897c478bd9Sstevel@tonic-gate /*
6907c478bd9Sstevel@tonic-gate  * Add a new action.
6917c478bd9Sstevel@tonic-gate  */
6927c478bd9Sstevel@tonic-gate   if(!_new_HashSymbol(kt->actions, action, 0, (void (*)(void))fn, data, 0)) {
6937c478bd9Sstevel@tonic-gate     _err_record_msg(kt->err, "Insufficient memory to record key-binding action",
6947c478bd9Sstevel@tonic-gate 		    END_ERR_MSG);
6957c478bd9Sstevel@tonic-gate     return 1;
6967c478bd9Sstevel@tonic-gate   };
6977c478bd9Sstevel@tonic-gate   return 0;
6987c478bd9Sstevel@tonic-gate }
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate /*.......................................................................
7017c478bd9Sstevel@tonic-gate  * Compare two strings of specified length which may contain embedded
7027c478bd9Sstevel@tonic-gate  * ascii NUL's.
7037c478bd9Sstevel@tonic-gate  *
7047c478bd9Sstevel@tonic-gate  * Input:
7057c478bd9Sstevel@tonic-gate  *  s1       char *  The first of the strings to be compared.
7067c478bd9Sstevel@tonic-gate  *  n1        int    The length of the string in s1.
7077c478bd9Sstevel@tonic-gate  *  s2       char *  The second of the strings to be compared.
7087c478bd9Sstevel@tonic-gate  *  n2        int    The length of the string in s2.
7097c478bd9Sstevel@tonic-gate  * Output:
7107c478bd9Sstevel@tonic-gate  *  return    int    < 0 if(s1 < s2)
7117c478bd9Sstevel@tonic-gate  *                     0 if(s1 == s2)
7127c478bd9Sstevel@tonic-gate  *                   > 0 if(s1 > s2)
7137c478bd9Sstevel@tonic-gate  */
_kt_compare_strings(const char * s1,int n1,const char * s2,int n2)7147c478bd9Sstevel@tonic-gate static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2)
7157c478bd9Sstevel@tonic-gate {
7167c478bd9Sstevel@tonic-gate   int i;
7177c478bd9Sstevel@tonic-gate /*
7187c478bd9Sstevel@tonic-gate  * Find the first character where the two strings differ.
7197c478bd9Sstevel@tonic-gate  */
7207c478bd9Sstevel@tonic-gate   for(i=0; i<n1 && i<n2 && s1[i]==s2[i]; i++)
7217c478bd9Sstevel@tonic-gate     ;
7227c478bd9Sstevel@tonic-gate /*
7237c478bd9Sstevel@tonic-gate  * Did we hit the end of either string before finding a difference?
7247c478bd9Sstevel@tonic-gate  */
7257c478bd9Sstevel@tonic-gate   if(i==n1 || i==n2) {
7267c478bd9Sstevel@tonic-gate     if(n1 == n2)
7277c478bd9Sstevel@tonic-gate       return 0;
7287c478bd9Sstevel@tonic-gate     else if(n1==i)
7297c478bd9Sstevel@tonic-gate       return -1;
7307c478bd9Sstevel@tonic-gate     else
7317c478bd9Sstevel@tonic-gate       return 1;
7327c478bd9Sstevel@tonic-gate   };
7337c478bd9Sstevel@tonic-gate /*
7347c478bd9Sstevel@tonic-gate  * Compare the two characters that differed to determine which
7357c478bd9Sstevel@tonic-gate  * string is greatest.
7367c478bd9Sstevel@tonic-gate  */
7377c478bd9Sstevel@tonic-gate   return s1[i] - s2[i];
7387c478bd9Sstevel@tonic-gate }
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate /*.......................................................................
7417c478bd9Sstevel@tonic-gate  * Assign a given action function to a binding table entry.
7427c478bd9Sstevel@tonic-gate  *
7437c478bd9Sstevel@tonic-gate  * Input:
7447c478bd9Sstevel@tonic-gate  *  sym       KeySym *  The binding table entry to be modified.
7457c478bd9Sstevel@tonic-gate  *  binder  KtBinder    The source of the binding.
7467c478bd9Sstevel@tonic-gate  *  keyfn    KtKeyFn *  The action function.
7477c478bd9Sstevel@tonic-gate  *  data        void *  A pointer to arbitrary callback data to pass to
7487c478bd9Sstevel@tonic-gate  *                      the action function whenever it is called.
7497c478bd9Sstevel@tonic-gate  */
_kt_assign_action(KeySym * sym,KtBinder binder,KtKeyFn * keyfn,void * data)7507c478bd9Sstevel@tonic-gate static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn,
7517c478bd9Sstevel@tonic-gate 			      void *data)
7527c478bd9Sstevel@tonic-gate {
7537c478bd9Sstevel@tonic-gate   KtAction *action;   /* An action function/data pair */
7547c478bd9Sstevel@tonic-gate   int i;
7557c478bd9Sstevel@tonic-gate /*
7567c478bd9Sstevel@tonic-gate  * Unknown binding source?
7577c478bd9Sstevel@tonic-gate  */
7587c478bd9Sstevel@tonic-gate   if(binder < 0 || binder >= KTB_NBIND)
7597c478bd9Sstevel@tonic-gate     return;
7607c478bd9Sstevel@tonic-gate /*
7617c478bd9Sstevel@tonic-gate  * Record the action according to its source.
7627c478bd9Sstevel@tonic-gate  */
7637c478bd9Sstevel@tonic-gate   action = sym->actions + binder;
7647c478bd9Sstevel@tonic-gate   action->fn = keyfn;
7657c478bd9Sstevel@tonic-gate   action->data = data;
7667c478bd9Sstevel@tonic-gate /*
7677c478bd9Sstevel@tonic-gate  * Find the highest priority binding source that has supplied an
7687c478bd9Sstevel@tonic-gate  * action. Note that the actions[] array is ordered in order of
7697c478bd9Sstevel@tonic-gate  * descreasing priority, so the first entry that contains a function
7707c478bd9Sstevel@tonic-gate  * is the one to use.
7717c478bd9Sstevel@tonic-gate  */
7727c478bd9Sstevel@tonic-gate   for(i=0; i<KTB_NBIND && !sym->actions[i].fn; i++)
7737c478bd9Sstevel@tonic-gate     ;
7747c478bd9Sstevel@tonic-gate /*
7757c478bd9Sstevel@tonic-gate  * Record the index of this action for use during lookups.
7767c478bd9Sstevel@tonic-gate  */
7777c478bd9Sstevel@tonic-gate   sym->binder = i < KTB_NBIND ? i : -1;
7787c478bd9Sstevel@tonic-gate   return;
7797c478bd9Sstevel@tonic-gate }
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate /*.......................................................................
7827c478bd9Sstevel@tonic-gate  * Remove all key bindings that came from a specified source.
7837c478bd9Sstevel@tonic-gate  *
7847c478bd9Sstevel@tonic-gate  * Input:
7857c478bd9Sstevel@tonic-gate  *  kt        KeyTab *  The table of key bindings.
7867c478bd9Sstevel@tonic-gate  *  binder  KtBinder    The source of the bindings to be cleared.
7877c478bd9Sstevel@tonic-gate  */
_kt_clear_bindings(KeyTab * kt,KtBinder binder)7887c478bd9Sstevel@tonic-gate void _kt_clear_bindings(KeyTab *kt, KtBinder binder)
7897c478bd9Sstevel@tonic-gate {
7907c478bd9Sstevel@tonic-gate   int oldkey;   /* The index of a key in the original binding table */
7917c478bd9Sstevel@tonic-gate   int newkey;   /* The index of a key in the updated binding table */
7927c478bd9Sstevel@tonic-gate /*
7937c478bd9Sstevel@tonic-gate  * If there is no table, then no bindings exist to be deleted.
7947c478bd9Sstevel@tonic-gate  */
7957c478bd9Sstevel@tonic-gate   if(!kt)
7967c478bd9Sstevel@tonic-gate     return;
7977c478bd9Sstevel@tonic-gate /*
7987c478bd9Sstevel@tonic-gate  * Clear bindings of the given source.
7997c478bd9Sstevel@tonic-gate  */
8007c478bd9Sstevel@tonic-gate   for(oldkey=0; oldkey<kt->nkey; oldkey++)
8017c478bd9Sstevel@tonic-gate     _kt_assign_action(kt->table + oldkey, binder, 0, NULL);
8027c478bd9Sstevel@tonic-gate /*
8037c478bd9Sstevel@tonic-gate  * Delete entries that now don't have a binding from any source.
8047c478bd9Sstevel@tonic-gate  */
8057c478bd9Sstevel@tonic-gate   newkey = 0;
8067c478bd9Sstevel@tonic-gate   for(oldkey=0; oldkey<kt->nkey; oldkey++) {
8077c478bd9Sstevel@tonic-gate     KeySym *sym = kt->table + oldkey;
8087c478bd9Sstevel@tonic-gate     if(sym->binder < 0) {
8097c478bd9Sstevel@tonic-gate       _del_StringMemString(kt->smem, sym->keyseq);
8107c478bd9Sstevel@tonic-gate     } else {
8117c478bd9Sstevel@tonic-gate       if(oldkey != newkey)
8127c478bd9Sstevel@tonic-gate 	kt->table[newkey] = *sym;
8137c478bd9Sstevel@tonic-gate       newkey++;
8147c478bd9Sstevel@tonic-gate     };
8157c478bd9Sstevel@tonic-gate   };
8167c478bd9Sstevel@tonic-gate /*
8177c478bd9Sstevel@tonic-gate  * Record the number of keys that were kept.
8187c478bd9Sstevel@tonic-gate  */
8197c478bd9Sstevel@tonic-gate   kt->nkey = newkey;
8207c478bd9Sstevel@tonic-gate   return;
8217c478bd9Sstevel@tonic-gate }
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate /*.......................................................................
8247c478bd9Sstevel@tonic-gate  * Translate a backslash escape sequence to a binary character.
8257c478bd9Sstevel@tonic-gate  *
8267c478bd9Sstevel@tonic-gate  * Input:
8277c478bd9Sstevel@tonic-gate  *  string  const char *   The characters that follow the backslash.
8287c478bd9Sstevel@tonic-gate  * Input/Output:
8297c478bd9Sstevel@tonic-gate  *  endp    const char **  If endp!=NULL, on return *endp will be made to
8307c478bd9Sstevel@tonic-gate  *                         point to the character in string[] which follows
8317c478bd9Sstevel@tonic-gate  *                         the escape sequence.
8327c478bd9Sstevel@tonic-gate  * Output:
8337c478bd9Sstevel@tonic-gate  *  return        char     The binary character.
8347c478bd9Sstevel@tonic-gate  */
_kt_backslash_escape(const char * string,const char ** endp)8357c478bd9Sstevel@tonic-gate static char _kt_backslash_escape(const char *string, const char **endp)
8367c478bd9Sstevel@tonic-gate {
8377c478bd9Sstevel@tonic-gate   char c;  /* The output character */
8387c478bd9Sstevel@tonic-gate /*
8397c478bd9Sstevel@tonic-gate  * Is the backslash followed by one or more octal digits?
8407c478bd9Sstevel@tonic-gate  */
8417c478bd9Sstevel@tonic-gate   switch(*string) {
8427c478bd9Sstevel@tonic-gate   case '0': case '1': case '2': case '3':
8437c478bd9Sstevel@tonic-gate   case '4': case '5': case '6': case '7':
8447c478bd9Sstevel@tonic-gate     c = strtol(string, (char **)&string, 8);
8457c478bd9Sstevel@tonic-gate     break;
8467c478bd9Sstevel@tonic-gate   case 'a':
8477c478bd9Sstevel@tonic-gate     c = '\a';
8487c478bd9Sstevel@tonic-gate     string++;
8497c478bd9Sstevel@tonic-gate     break;
8507c478bd9Sstevel@tonic-gate   case 'b':
8517c478bd9Sstevel@tonic-gate     c = '\b';
8527c478bd9Sstevel@tonic-gate     string++;
8537c478bd9Sstevel@tonic-gate     break;
8547c478bd9Sstevel@tonic-gate   case 'e': case 'E': /* Escape */
8557c478bd9Sstevel@tonic-gate     c = GL_ESC_CHAR;
8567c478bd9Sstevel@tonic-gate     string++;
8577c478bd9Sstevel@tonic-gate     break;
8587c478bd9Sstevel@tonic-gate   case 'f':
8597c478bd9Sstevel@tonic-gate     c = '\f';
8607c478bd9Sstevel@tonic-gate     string++;
8617c478bd9Sstevel@tonic-gate     break;
8627c478bd9Sstevel@tonic-gate   case 'n':
8637c478bd9Sstevel@tonic-gate     c = '\n';
8647c478bd9Sstevel@tonic-gate     string++;
8657c478bd9Sstevel@tonic-gate     break;
8667c478bd9Sstevel@tonic-gate   case 'r':
8677c478bd9Sstevel@tonic-gate     c = '\r';
8687c478bd9Sstevel@tonic-gate     string++;
8697c478bd9Sstevel@tonic-gate     break;
8707c478bd9Sstevel@tonic-gate   case 't':
8717c478bd9Sstevel@tonic-gate     c = '\t';
8727c478bd9Sstevel@tonic-gate     string++;
8737c478bd9Sstevel@tonic-gate     break;
8747c478bd9Sstevel@tonic-gate   case 'v':
8757c478bd9Sstevel@tonic-gate     c = '\v';
8767c478bd9Sstevel@tonic-gate     string++;
8777c478bd9Sstevel@tonic-gate     break;
8787c478bd9Sstevel@tonic-gate   case '\0':
8797c478bd9Sstevel@tonic-gate     c = '\\';
8807c478bd9Sstevel@tonic-gate     break;
8817c478bd9Sstevel@tonic-gate   default:
8827c478bd9Sstevel@tonic-gate     c = *string++;
8837c478bd9Sstevel@tonic-gate     break;
8847c478bd9Sstevel@tonic-gate   };
8857c478bd9Sstevel@tonic-gate /*
8867c478bd9Sstevel@tonic-gate  * Report the character which follows the escape sequence.
8877c478bd9Sstevel@tonic-gate  */
8887c478bd9Sstevel@tonic-gate   if(endp)
8897c478bd9Sstevel@tonic-gate     *endp = string;
8907c478bd9Sstevel@tonic-gate   return c;
8917c478bd9Sstevel@tonic-gate }
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate /*.......................................................................
8947c478bd9Sstevel@tonic-gate  * Return non-zero if the next two characters are M- and a third character
8957c478bd9Sstevel@tonic-gate  * follows. Otherwise return 0.
8967c478bd9Sstevel@tonic-gate  *
8977c478bd9Sstevel@tonic-gate  * Input:
8987c478bd9Sstevel@tonic-gate  *  string   const char *  The sub-string to scan.
8997c478bd9Sstevel@tonic-gate  * Output:
9007c478bd9Sstevel@tonic-gate  *  return          int    1 - The next two characters are M- and these
9017c478bd9Sstevel@tonic-gate  *                             are followed by at least one character.
9027c478bd9Sstevel@tonic-gate  *                         0 - The next two characters aren't M- or no
9037c478bd9Sstevel@tonic-gate  *                             character follows a M- pair.
9047c478bd9Sstevel@tonic-gate  */
_kt_is_emacs_meta(const char * string)9057c478bd9Sstevel@tonic-gate static int _kt_is_emacs_meta(const char *string)
9067c478bd9Sstevel@tonic-gate {
9077c478bd9Sstevel@tonic-gate   return *string++ == 'M' && *string++ == '-' && *string;
9087c478bd9Sstevel@tonic-gate }
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate /*.......................................................................
9117c478bd9Sstevel@tonic-gate  * Return non-zero if the next two characters are C- and a third character
9127c478bd9Sstevel@tonic-gate  * follows. Otherwise return 0.
9137c478bd9Sstevel@tonic-gate  *
9147c478bd9Sstevel@tonic-gate  * Input:
9157c478bd9Sstevel@tonic-gate  *  string   const char *  The sub-string to scan.
9167c478bd9Sstevel@tonic-gate  * Output:
9177c478bd9Sstevel@tonic-gate  *  return          int    1 - The next two characters are C- and these
9187c478bd9Sstevel@tonic-gate  *                             are followed by at least one character.
9197c478bd9Sstevel@tonic-gate  *                         0 - The next two characters aren't C- or no
9207c478bd9Sstevel@tonic-gate  *                             character follows a C- pair.
9217c478bd9Sstevel@tonic-gate  */
_kt_is_emacs_ctrl(const char * string)9227c478bd9Sstevel@tonic-gate static int _kt_is_emacs_ctrl(const char *string)
9237c478bd9Sstevel@tonic-gate {
9247c478bd9Sstevel@tonic-gate   return *string++ == 'C' && *string++ == '-' && *string;
9257c478bd9Sstevel@tonic-gate }
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate /*.......................................................................
9287c478bd9Sstevel@tonic-gate  * Merge an array of bindings with existing bindings.
9297c478bd9Sstevel@tonic-gate  *
9307c478bd9Sstevel@tonic-gate  * Input:
9317c478bd9Sstevel@tonic-gate  *  kt                    KeyTab *  The table of key bindings.
9327c478bd9Sstevel@tonic-gate  *  binder              KtBinder    The source of the bindings.
9337c478bd9Sstevel@tonic-gate  *  bindings  const KtKeyBinding *  The array of bindings.
9347c478bd9Sstevel@tonic-gate  *  n                        int    The number of bindings in bindings[].
9357c478bd9Sstevel@tonic-gate  * Output:
9367c478bd9Sstevel@tonic-gate  *  return                   int    0 - OK.
9377c478bd9Sstevel@tonic-gate  *                                  1 - Error.
9387c478bd9Sstevel@tonic-gate  */
_kt_add_bindings(KeyTab * kt,KtBinder binder,const KtKeyBinding * bindings,unsigned n)9397c478bd9Sstevel@tonic-gate int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings,
9407c478bd9Sstevel@tonic-gate 		     unsigned n)
9417c478bd9Sstevel@tonic-gate {
9427c478bd9Sstevel@tonic-gate   int i;
9437c478bd9Sstevel@tonic-gate /*
9447c478bd9Sstevel@tonic-gate  * Check the arguments.
9457c478bd9Sstevel@tonic-gate  */
9467c478bd9Sstevel@tonic-gate   if(!kt || !bindings) {
9477c478bd9Sstevel@tonic-gate     errno = EINVAL;
9487c478bd9Sstevel@tonic-gate     if(kt)
9497c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
9507c478bd9Sstevel@tonic-gate     return 1;
9517c478bd9Sstevel@tonic-gate   };
9527c478bd9Sstevel@tonic-gate /*
9537c478bd9Sstevel@tonic-gate  * Install the array of bindings.
9547c478bd9Sstevel@tonic-gate  */
9557c478bd9Sstevel@tonic-gate   for(i=0; i<n; i++) {
9567c478bd9Sstevel@tonic-gate     if(_kt_set_keybinding(kt, binder, bindings[i].keyseq, bindings[i].action))
9577c478bd9Sstevel@tonic-gate       return 1;
9587c478bd9Sstevel@tonic-gate   };
9597c478bd9Sstevel@tonic-gate   return 0;
9607c478bd9Sstevel@tonic-gate }
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate /*.......................................................................
9637c478bd9Sstevel@tonic-gate  * Lookup the function that implements a given action.
9647c478bd9Sstevel@tonic-gate  *
9657c478bd9Sstevel@tonic-gate  * Input:
9667c478bd9Sstevel@tonic-gate  *  kt          KeyTab *  The table of key bindings.
9677c478bd9Sstevel@tonic-gate  *  action  const char *  The name of the action to look up.
9687c478bd9Sstevel@tonic-gate  * Input/Output:
9697c478bd9Sstevel@tonic-gate  *  fn         KtKeyFn ** If the action is found, the function that
9707c478bd9Sstevel@tonic-gate  *                        implements it will be assigned to *fn. Note
9717c478bd9Sstevel@tonic-gate  *                        that fn can be NULL.
9727c478bd9Sstevel@tonic-gate  *  data          void ** If the action is found, the callback data
9737c478bd9Sstevel@tonic-gate  *                        associated with the action function, will be
9747c478bd9Sstevel@tonic-gate  *                        assigned to *data. Note that data can be NULL.
9757c478bd9Sstevel@tonic-gate  * Output:
9767c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
9777c478bd9Sstevel@tonic-gate  *                        1 - Action not found.
9787c478bd9Sstevel@tonic-gate  */
_kt_lookup_action(KeyTab * kt,const char * action,KtKeyFn ** fn,void ** data)9797c478bd9Sstevel@tonic-gate int _kt_lookup_action(KeyTab *kt, const char *action,
9807c478bd9Sstevel@tonic-gate 		      KtKeyFn **fn, void **data)
9817c478bd9Sstevel@tonic-gate {
9827c478bd9Sstevel@tonic-gate   Symbol *sym;   /* The symbol table entry of the action */
9837c478bd9Sstevel@tonic-gate /*
9847c478bd9Sstevel@tonic-gate  * Check the arguments.
9857c478bd9Sstevel@tonic-gate  */
9867c478bd9Sstevel@tonic-gate   if(!kt || !action) {
9877c478bd9Sstevel@tonic-gate     errno = EINVAL;
9887c478bd9Sstevel@tonic-gate     if(kt)
9897c478bd9Sstevel@tonic-gate       _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG);
9907c478bd9Sstevel@tonic-gate     return 1;
9917c478bd9Sstevel@tonic-gate   };
9927c478bd9Sstevel@tonic-gate /*
9937c478bd9Sstevel@tonic-gate  * Lookup the symbol table entry of the action.
9947c478bd9Sstevel@tonic-gate  */
9957c478bd9Sstevel@tonic-gate   sym = _find_HashSymbol(kt->actions, action);
9967c478bd9Sstevel@tonic-gate   if(!sym)
9977c478bd9Sstevel@tonic-gate     return 1;
9987c478bd9Sstevel@tonic-gate /*
9997c478bd9Sstevel@tonic-gate  * Return the function and ccallback data associated with the action.
10007c478bd9Sstevel@tonic-gate  */
10017c478bd9Sstevel@tonic-gate   if(fn)
10027c478bd9Sstevel@tonic-gate     *fn = (KtKeyFn *) sym->fn;
10037c478bd9Sstevel@tonic-gate   if(data)
10047c478bd9Sstevel@tonic-gate     *data = sym->data;
10057c478bd9Sstevel@tonic-gate   return 0;
10067c478bd9Sstevel@tonic-gate }
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate /*.......................................................................
10097c478bd9Sstevel@tonic-gate  * Return extra information (ie. in addition to that provided by errno)
10107c478bd9Sstevel@tonic-gate  * about the last error to occur in any of the public functions of this
10117c478bd9Sstevel@tonic-gate  * module.
10127c478bd9Sstevel@tonic-gate  *
10137c478bd9Sstevel@tonic-gate  * Input:
10147c478bd9Sstevel@tonic-gate  *  kt          KeyTab *  The table of key bindings.
10157c478bd9Sstevel@tonic-gate  * Output:
10167c478bd9Sstevel@tonic-gate  *  return  const char *  A pointer to the internal buffer in which
10177c478bd9Sstevel@tonic-gate  *                        the error message is temporarily stored.
10187c478bd9Sstevel@tonic-gate  */
_kt_last_error(KeyTab * kt)10197c478bd9Sstevel@tonic-gate const char *_kt_last_error(KeyTab *kt)
10207c478bd9Sstevel@tonic-gate {
10217c478bd9Sstevel@tonic-gate   return kt ? _err_get_msg(kt->err) : "NULL KeyTab argument";
10227c478bd9Sstevel@tonic-gate }
1023