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