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