diff options
Diffstat (limited to 'libtecla-1.6.3/keytab.c')
-rw-r--r-- | libtecla-1.6.3/keytab.c | 1022 |
1 files changed, 1022 insertions, 0 deletions
diff --git a/libtecla-1.6.3/keytab.c b/libtecla-1.6.3/keytab.c new file mode 100644 index 0000000..eacef8a --- /dev/null +++ b/libtecla-1.6.3/keytab.c @@ -0,0 +1,1022 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "keytab.h" +#include "strngmem.h" +#include "getline.h" +#include "errmsg.h" +#include "hash.h" + +/* + * When allocating or reallocating the key-binding table, how + * many entries should be added? + */ +#define KT_TABLE_INC 100 + +/* + * Define the size of the hash table that is used to associate action + * names with action functions. This should be a prime number. + */ +#define KT_HASH_SIZE 113 + +/* + * Define a binary-symbol-table object. + */ +struct KeyTab { + ErrMsg *err; /* Information about the last error */ + int size; /* The allocated dimension of table[] */ + int nkey; /* The current number of members in the table */ + KeySym *table; /* The table of lexically sorted key sequences */ + HashTable *actions; /* The hash table of actions */ + StringMem *smem; /* Memory for allocating strings */ +}; + +static int _kt_extend_table(KeyTab *kt); +static int _kt_parse_keybinding_string(const char *keyseq, + char *binary, int *nc); +static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2); +static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn, + void *data); +static char _kt_backslash_escape(const char *string, const char **endp); +static int _kt_is_emacs_meta(const char *string); +static int _kt_is_emacs_ctrl(const char *string); +static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq, + int nc, int *first, int *last); + +/*....................................................................... + * Create a new key-binding symbol table. + * + * Output: + * return KeyTab * The new object, or NULL on error. + */ +KeyTab *_new_KeyTab(void) +{ + KeyTab *kt; /* The object to be returned */ +/* + * Allocate the container. + */ + kt = (KeyTab *) malloc(sizeof(KeyTab)); + if(!kt) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_KeyTab(). + */ + kt->err = NULL; + kt->size = KT_TABLE_INC; + kt->nkey = 0; + kt->table = NULL; + kt->actions = NULL; + kt->smem = NULL; +/* + * Allocate a place to record error messages. + */ + kt->err = _new_ErrMsg(); + if(!kt->err) + return _del_KeyTab(kt); +/* + * Allocate the table. + */ + kt->table = (KeySym *) malloc(sizeof(kt->table[0]) * kt->size); + if(!kt->table) { + errno = ENOMEM; + return _del_KeyTab(kt); + }; +/* + * Allocate a hash table of actions. + */ + kt->actions = _new_HashTable(NULL, KT_HASH_SIZE, IGNORE_CASE, NULL, 0); + if(!kt->actions) + return _del_KeyTab(kt); +/* + * Allocate a string allocation object. This allows allocation of + * small strings without fragmenting the heap. + */ + kt->smem = _new_StringMem(KT_TABLE_INC); + if(!kt->smem) + return _del_KeyTab(kt); + return kt; +} + +/*....................................................................... + * Delete a KeyTab object. + * + * Input: + * kt KeyTab * The object to be deleted. + * Output: + * return KeyTab * The deleted object (always NULL). + */ +KeyTab *_del_KeyTab(KeyTab *kt) +{ + if(kt) { + if(kt->table) + free(kt->table); + kt->actions = _del_HashTable(kt->actions); + kt->smem = _del_StringMem(kt->smem, 1); + kt->err = _del_ErrMsg(kt->err); + free(kt); + }; + return NULL; +} + +/*....................................................................... + * Increase the size of the table to accomodate more keys. + * + * Input: + * kt KeyTab * The table to be extended. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _kt_extend_table(KeyTab *kt) +{ +/* + * Attempt to increase the size of the table. + */ + KeySym *newtab = (KeySym *) realloc(kt->table, sizeof(kt->table[0]) * + (kt->size + KT_TABLE_INC)); +/* + * Failed? + */ + if(!newtab) { + _err_record_msg(kt->err, "Can't extend keybinding table", END_ERR_MSG); + errno = ENOMEM; + return 1; + }; +/* + * Install the resized table. + */ + kt->table = newtab; + kt->size += KT_TABLE_INC; + return 0; +} + +/*....................................................................... + * Add, update or remove a keybinding to the table. + * + * Input: + * kt KeyTab * The table to add the binding to. + * binder KtBinder The source of the binding. + * keyseq const char * The key-sequence to bind. + * action char * The action to associate with the key sequence, or + * NULL to remove the action associated with the + * key sequence. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_set_keybinding(KeyTab *kt, KtBinder binder, const char *keyseq, + const char *action) +{ + KtKeyFn *keyfn; /* The action function */ + void *data; /* The callback data of the action function */ +/* + * Check arguments. + */ + if(kt==NULL || !keyseq) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Lookup the function that implements the specified action. + */ + if(!action) { + keyfn = 0; + data = NULL; + } else { + Symbol *sym = _find_HashSymbol(kt->actions, action); + if(!sym) { + _err_record_msg(kt->err, "Unknown key-binding action: ", action, + END_ERR_MSG); + errno = EINVAL; + return 1; + }; + keyfn = (KtKeyFn *) sym->fn; + data = sym->data; + }; +/* + * Record the action in the table. + */ + return _kt_set_keyfn(kt, binder, keyseq, keyfn, data); +} + +/*....................................................................... + * Add, update or remove a keybinding to the table, specifying an action + * function directly. + * + * Input: + * kt KeyTab * The table to add the binding to. + * binder KtBinder The source of the binding. + * keyseq char * The key-sequence to bind. + * keyfn KtKeyFn * The action function, or NULL to remove any existing + * action function. + * data void * A pointer to anonymous data to be passed to keyfn + * whenever it is called. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq, + KtKeyFn *keyfn, void *data) +{ + const char *kptr; /* A pointer into keyseq[] */ + char *binary; /* The binary version of keyseq[] */ + int nc; /* The number of characters in binary[] */ + int first,last; /* The first and last entries in the table which */ + /* minimally match. */ + int size; /* The size to allocate for the binary string */ + int i; +/* + * Check arguments. + */ + if(kt==NULL || !keyseq) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Work out a pessimistic estimate of how much space will be needed + * for the binary copy of the string, noting that binary meta characters + * embedded in the input string get split into two characters. + */ + for(size=0,kptr = keyseq; *kptr; kptr++) + size += IS_META_CHAR(*kptr) ? 2 : 1; +/* + * Allocate a string that has the length of keyseq[]. + */ + binary = _new_StringMemString(kt->smem, size + 1); + if(!binary) { + errno = ENOMEM; + _err_record_msg(kt->err, "Insufficient memory to record key sequence", + END_ERR_MSG); + return 1; + }; +/* + * Convert control and octal character specifications to binary characters. + */ + if(_kt_parse_keybinding_string(keyseq, binary, &nc)) { + binary = _del_StringMemString(kt->smem, binary); + return 1; + }; +/* + * Lookup the position in the table at which to insert the binding. + */ + switch(_kt_locate_keybinding(kt, binary, nc, &first, &last)) { +/* + * If an exact match for the key-sequence is already in the table, + * simply replace its binding function (or delete the entry if + * the new binding is 0). + */ + case KT_EXACT_MATCH: + if(keyfn) { + _kt_assign_action(kt->table + first, binder, keyfn, data); + } else { + _del_StringMemString(kt->smem, kt->table[first].keyseq); + memmove(kt->table + first, kt->table + first + 1, + (kt->nkey - first - 1) * sizeof(kt->table[0])); + kt->nkey--; + }; + binary = _del_StringMemString(kt->smem, binary); + break; +/* + * If an ambiguous match has been found and we are installing a + * callback, then our new key-sequence would hide all of the ambiguous + * matches, so we shouldn't allow it. + */ + case KT_AMBIG_MATCH: + if(keyfn) { + _err_record_msg(kt->err, "Can't bind \"", keyseq, + "\", because it is a prefix of another binding", + END_ERR_MSG); + binary = _del_StringMemString(kt->smem, binary); + errno = EPERM; + return 1; + }; + break; +/* + * If the entry doesn't exist, create it. + */ + case KT_NO_MATCH: +/* + * Add a new binding? + */ + if(keyfn) { + KeySym *sym; +/* + * We will need a new entry, extend the table if needed. + */ + if(kt->nkey + 1 > kt->size) { + if(_kt_extend_table(kt)) { + binary = _del_StringMemString(kt->smem, binary); + return 1; + }; + }; +/* + * Make space to insert the new key-sequence before 'last'. + */ + if(last < kt->nkey) { + memmove(kt->table + last + 1, kt->table + last, + (kt->nkey - last) * sizeof(kt->table[0])); + }; +/* + * Insert the new binding in the vacated position. + */ + sym = kt->table + last; + sym->keyseq = binary; + sym->nc = nc; + for(i=0; i<KTB_NBIND; i++) { + KtAction *action = sym->actions + i; + action->fn = 0; + action->data = NULL; + }; + sym->binder = -1; + _kt_assign_action(sym, binder, keyfn, data); + kt->nkey++; + }; + break; + case KT_BAD_MATCH: + binary = _del_StringMemString(kt->smem, binary); + return 1; + break; + }; + return 0; +} + +/*....................................................................... + * Perform a min-match lookup of a key-binding. + * + * Input: + * kt KeyTab * The keybinding table to lookup in. + * binary_keyseq char * The binary key-sequence to lookup. + * nc int the number of characters in keyseq[]. + * Input/Output: + * first,last int * If there is an ambiguous or exact match, the indexes + * of the first and last symbols that minimally match + * will be assigned to *first and *last respectively. + * If there is no match, then first and last will + * bracket the location where the symbol should be + * inserted. + * Output: + * return KtKeyMatch One of the following enumerators: + * KT_EXACT_MATCH - An exact match was found. + * KT_AMBIG_MATCH - An ambiguous match was found. + * KT_NO_MATCH - No match was found. + * KT_BAD_MATCH - An error occurred while searching. + */ +static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq, + int nc, int *first, int *last) +{ + int mid; /* The index at which to bisect the table */ + int bot; /* The lowest index of the table not searched yet */ + int top; /* The highest index of the table not searched yet */ + int test; /* The return value of strcmp() */ +/* + * Perform a binary search for the key-sequence. + */ + bot = 0; + top = kt->nkey - 1; + while(top >= bot) { + mid = (top + bot)/2; + test = _kt_compare_strings(kt->table[mid].keyseq, kt->table[mid].nc, + binary_keyseq, nc); + if(test > 0) + top = mid - 1; + else if(test < 0) + bot = mid + 1; + else { + *first = *last = mid; + return KT_EXACT_MATCH; + }; + }; +/* + * An exact match wasn't found, but top is the index just below the + * index where a match would be found, and bot is the index just above + * where the match ought to be found. + */ + *first = top; + *last = bot; +/* + * See if any ambiguous matches exist, and if so make *first and *last + * refer to the first and last matches. + */ + if(*last < kt->nkey && kt->table[*last].nc > nc && + _kt_compare_strings(kt->table[*last].keyseq, nc, binary_keyseq, nc)==0) { + *first = *last; + while(*last+1 < kt->nkey && kt->table[*last+1].nc > nc && + _kt_compare_strings(kt->table[*last+1].keyseq, nc, binary_keyseq, nc)==0) + (*last)++; + return KT_AMBIG_MATCH; + }; +/* + * No match. + */ + return KT_NO_MATCH; +} + +/*....................................................................... + * Lookup the sub-array of key-bindings who's key-sequences minimally + * match a given key-sequence. + * + * Input: + * kt KeyTab * The keybinding table to lookup in. + * binary_keyseq char * The binary key-sequence to lookup. + * nc int the number of characters in keyseq[]. + * Input/Output: + * matches KeySym ** The array of minimally matching symbols + * can be found in (*matches)[0..nmatch-1], unless + * no match was found, in which case *matches will + * be set to NULL. + * nmatch int The number of ambiguously matching symbols. This + * will be 0 if there is no match, 1 for an exact + * match, and a number greater than 1 for an ambiguous + * match. + * Output: + * return KtKeyMatch One of the following enumerators: + * KT_EXACT_MATCH - An exact match was found. + * KT_AMBIG_MATCH - An ambiguous match was found. + * KT_NO_MATCH - No match was found. + * KT_BAD_MATCH - An error occurred while searching. + */ +KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq, + int nc, KeySym **matches, int *nmatch) +{ + KtKeyMatch status; /* The return status */ + int first,last; /* The indexes of the first and last matching entry */ + /* in the symbol table. */ +/* + * Check the arguments. + */ + if(!kt || !binary_keyseq || !matches || !nmatch || nc < 0) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return KT_BAD_MATCH; + }; +/* + * Lookup the indexes of the binding-table entries that bracket the + * target key-sequence. + */ + status = _kt_locate_keybinding(kt, binary_keyseq, nc, &first, &last); +/* + * Translate the indexes into the corresponding subarray of matching + * table entries. + */ + switch(status) { + case KT_EXACT_MATCH: + case KT_AMBIG_MATCH: + *matches = kt->table + first; + *nmatch = last - first + 1; + break; + default: + *matches = NULL; + *nmatch = 0; + break; + }; + return status; +} + +/*....................................................................... + * Convert a keybinding string into a uniq binary representation. + * + * Control characters can be given directly in their binary form, + * expressed as either ^ or C-, followed by the character, expressed in + * octal, like \129 or via C-style backslash escapes, with the addition + * of '\E' to denote the escape key. Similarly, meta characters can be + * given directly in binary or expressed as M- followed by the character. + * Meta characters are recorded as two characters in the binary output + * string, the first being the escape key, and the second being the key + * that was modified by the meta key. This means that binding to + * \EA or ^[A or M-A are all equivalent. + * + * Input: + * keyseq char * The key sequence being added. + * Input/Output: + * binary char * The binary version of the key sequence will be + * assigned to binary[], which must have at least + * as many characters as keyseq[] plus the number + * of embedded binary meta characters. + * nc int * The number of characters assigned to binary[] + * will be recorded in *nc. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _kt_parse_keybinding_string(const char *keyseq, char *binary, + int *nc) +{ + const char *iptr = keyseq; /* Pointer into keyseq[] */ + char *optr = binary; /* Pointer into binary[] */ + char c; /* An intermediate character */ +/* + * Parse the input characters until they are exhausted or the + * output string becomes full. + */ + while(*iptr) { +/* + * Check for special characters. + */ + switch(*iptr) { + case '^': /* A control character specification */ +/* + * Convert the caret expression into the corresponding control + * character unless no character follows the caret, in which case + * record a literal caret. + */ + if(iptr[1]) { +/* + * Get the next, possibly escaped, character. + */ + if(iptr[1] == '\\') { + c = _kt_backslash_escape(iptr+2, &iptr); + } else { + c = iptr[1]; + iptr += 2; + }; +/* + * Convert the character to a control character. + */ + *optr++ = MAKE_CTRL(c); + } else { + *optr++ = *iptr++; + }; + break; +/* + * A backslash-escaped character? + */ + case '\\': +/* + * Convert the escape sequence to a binary character. + */ + *optr++ = _kt_backslash_escape(iptr+1, &iptr); + break; +/* + * Possibly an emacs-style meta character? + */ + case 'M': + if(_kt_is_emacs_meta(iptr)) { + *optr++ = GL_ESC_CHAR; + iptr += 2; + } else { + *optr++ = *iptr++; + }; + break; +/* + * Possibly an emacs-style control character specification? + */ + case 'C': + if(_kt_is_emacs_ctrl(iptr)) { + *optr++ = MAKE_CTRL(iptr[2]); + iptr += 3; + } else { + *optr++ = *iptr++; + }; + break; + default: + +/* + * Convert embedded meta characters into an escape character followed + * by the meta-unmodified character. + */ + if(IS_META_CHAR(*iptr)) { + *optr++ = GL_ESC_CHAR; + *optr++ = META_TO_CHAR(*iptr); + iptr++; +/* + * To allow keysequences that start with printable characters to + * be distinguished from the cursor-key keywords, prepend a backslash + * to the former. This same operation is performed in gl_interpret_char() + * before looking up a keysequence that starts with a printable character. + */ + } else if(iptr==keyseq && !IS_CTRL_CHAR(*iptr) && + strcmp(keyseq, "up") != 0 && strcmp(keyseq, "down") != 0 && + strcmp(keyseq, "left") != 0 && strcmp(keyseq, "right") != 0) { + *optr++ = '\\'; + *optr++ = *iptr++; + } else { + *optr++ = *iptr++; + }; + }; + }; +/* + * How many characters were placed in the output array? + */ + *nc = optr - binary; + return 0; +} + +/*....................................................................... + * Add, remove or modify an action. + * + * Input: + * kt KeyTab * The key-binding table. + * action char * The name of the action. + * fn KtKeyFn * The function that implements the action, or NULL + * to remove an existing action. + * data void * A pointer to arbitrary callback data to pass to the + * action function whenever it is called. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn, void *data) +{ + Symbol *sym; /* The symbol table entry of the action */ +/* + * Check the arguments. + */ + if(!kt || !action) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * If no function was provided, delete an existing action. + */ + if(!fn) { + sym = _del_HashSymbol(kt->actions, action); + return 0; + }; +/* + * If the action already exists, replace its action function. + */ + sym = _find_HashSymbol(kt->actions, action); + if(sym) { + sym->fn = (void (*)(void))fn; + sym->data = data; + return 0; + }; +/* + * Add a new action. + */ + if(!_new_HashSymbol(kt->actions, action, 0, (void (*)(void))fn, data, 0)) { + _err_record_msg(kt->err, "Insufficient memory to record key-binding action", + END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * Compare two strings of specified length which may contain embedded + * ascii NUL's. + * + * Input: + * s1 char * The first of the strings to be compared. + * n1 int The length of the string in s1. + * s2 char * The second of the strings to be compared. + * n2 int The length of the string in s2. + * Output: + * return int < 0 if(s1 < s2) + * 0 if(s1 == s2) + * > 0 if(s1 > s2) + */ +static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2) +{ + int i; +/* + * Find the first character where the two strings differ. + */ + for(i=0; i<n1 && i<n2 && s1[i]==s2[i]; i++) + ; +/* + * Did we hit the end of either string before finding a difference? + */ + if(i==n1 || i==n2) { + if(n1 == n2) + return 0; + else if(n1==i) + return -1; + else + return 1; + }; +/* + * Compare the two characters that differed to determine which + * string is greatest. + */ + return s1[i] - s2[i]; +} + +/*....................................................................... + * Assign a given action function to a binding table entry. + * + * Input: + * sym KeySym * The binding table entry to be modified. + * binder KtBinder The source of the binding. + * keyfn KtKeyFn * The action function. + * data void * A pointer to arbitrary callback data to pass to + * the action function whenever it is called. + */ +static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn, + void *data) +{ + KtAction *action; /* An action function/data pair */ + int i; +/* + * Unknown binding source? + */ + if(binder < 0 || binder >= KTB_NBIND) + return; +/* + * Record the action according to its source. + */ + action = sym->actions + binder; + action->fn = keyfn; + action->data = data; +/* + * Find the highest priority binding source that has supplied an + * action. Note that the actions[] array is ordered in order of + * descreasing priority, so the first entry that contains a function + * is the one to use. + */ + for(i=0; i<KTB_NBIND && !sym->actions[i].fn; i++) + ; +/* + * Record the index of this action for use during lookups. + */ + sym->binder = i < KTB_NBIND ? i : -1; + return; +} + +/*....................................................................... + * Remove all key bindings that came from a specified source. + * + * Input: + * kt KeyTab * The table of key bindings. + * binder KtBinder The source of the bindings to be cleared. + */ +void _kt_clear_bindings(KeyTab *kt, KtBinder binder) +{ + int oldkey; /* The index of a key in the original binding table */ + int newkey; /* The index of a key in the updated binding table */ +/* + * If there is no table, then no bindings exist to be deleted. + */ + if(!kt) + return; +/* + * Clear bindings of the given source. + */ + for(oldkey=0; oldkey<kt->nkey; oldkey++) + _kt_assign_action(kt->table + oldkey, binder, 0, NULL); +/* + * Delete entries that now don't have a binding from any source. + */ + newkey = 0; + for(oldkey=0; oldkey<kt->nkey; oldkey++) { + KeySym *sym = kt->table + oldkey; + if(sym->binder < 0) { + _del_StringMemString(kt->smem, sym->keyseq); + } else { + if(oldkey != newkey) + kt->table[newkey] = *sym; + newkey++; + }; + }; +/* + * Record the number of keys that were kept. + */ + kt->nkey = newkey; + return; +} + +/*....................................................................... + * Translate a backslash escape sequence to a binary character. + * + * Input: + * string const char * The characters that follow the backslash. + * Input/Output: + * endp const char ** If endp!=NULL, on return *endp will be made to + * point to the character in string[] which follows + * the escape sequence. + * Output: + * return char The binary character. + */ +static char _kt_backslash_escape(const char *string, const char **endp) +{ + char c; /* The output character */ +/* + * Is the backslash followed by one or more octal digits? + */ + switch(*string) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = strtol(string, (char **)&string, 8); + break; + case 'a': + c = '\a'; + string++; + break; + case 'b': + c = '\b'; + string++; + break; + case 'e': case 'E': /* Escape */ + c = GL_ESC_CHAR; + string++; + break; + case 'f': + c = '\f'; + string++; + break; + case 'n': + c = '\n'; + string++; + break; + case 'r': + c = '\r'; + string++; + break; + case 't': + c = '\t'; + string++; + break; + case 'v': + c = '\v'; + string++; + break; + case '\0': + c = '\\'; + break; + default: + c = *string++; + break; + }; +/* + * Report the character which follows the escape sequence. + */ + if(endp) + *endp = string; + return c; +} + +/*....................................................................... + * Return non-zero if the next two characters are M- and a third character + * follows. Otherwise return 0. + * + * Input: + * string const char * The sub-string to scan. + * Output: + * return int 1 - The next two characters are M- and these + * are followed by at least one character. + * 0 - The next two characters aren't M- or no + * character follows a M- pair. + */ +static int _kt_is_emacs_meta(const char *string) +{ + return *string++ == 'M' && *string++ == '-' && *string; +} + +/*....................................................................... + * Return non-zero if the next two characters are C- and a third character + * follows. Otherwise return 0. + * + * Input: + * string const char * The sub-string to scan. + * Output: + * return int 1 - The next two characters are C- and these + * are followed by at least one character. + * 0 - The next two characters aren't C- or no + * character follows a C- pair. + */ +static int _kt_is_emacs_ctrl(const char *string) +{ + return *string++ == 'C' && *string++ == '-' && *string; +} + +/*....................................................................... + * Merge an array of bindings with existing bindings. + * + * Input: + * kt KeyTab * The table of key bindings. + * binder KtBinder The source of the bindings. + * bindings const KtKeyBinding * The array of bindings. + * n int The number of bindings in bindings[]. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings, + unsigned n) +{ + int i; +/* + * Check the arguments. + */ + if(!kt || !bindings) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Install the array of bindings. + */ + for(i=0; i<n; i++) { + if(_kt_set_keybinding(kt, binder, bindings[i].keyseq, bindings[i].action)) + return 1; + }; + return 0; +} + +/*....................................................................... + * Lookup the function that implements a given action. + * + * Input: + * kt KeyTab * The table of key bindings. + * action const char * The name of the action to look up. + * Input/Output: + * fn KtKeyFn ** If the action is found, the function that + * implements it will be assigned to *fn. Note + * that fn can be NULL. + * data void ** If the action is found, the callback data + * associated with the action function, will be + * assigned to *data. Note that data can be NULL. + * Output: + * return int 0 - OK. + * 1 - Action not found. + */ +int _kt_lookup_action(KeyTab *kt, const char *action, + KtKeyFn **fn, void **data) +{ + Symbol *sym; /* The symbol table entry of the action */ +/* + * Check the arguments. + */ + if(!kt || !action) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Lookup the symbol table entry of the action. + */ + sym = _find_HashSymbol(kt->actions, action); + if(!sym) + return 1; +/* + * Return the function and ccallback data associated with the action. + */ + if(fn) + *fn = (KtKeyFn *) sym->fn; + if(data) + *data = sym->data; + return 0; +} + +/*....................................................................... + * Return extra information (ie. in addition to that provided by errno) + * about the last error to occur in any of the public functions of this + * module. + * + * Input: + * kt KeyTab * The table of key bindings. + * Output: + * return const char * A pointer to the internal buffer in which + * the error message is temporarily stored. + */ +const char *_kt_last_error(KeyTab *kt) +{ + return kt ? _err_get_msg(kt->err) : "NULL KeyTab argument"; +} |