dhcpcd: update README.DRAGONFLY
[dragonfly.git] / contrib / dialog / dlg_keys.c
blobb4fde73eb1df99ba024e3e34eb2af5178b6d8659
1 /*
2 * $Id: dlg_keys.c,v 1.62 2022/04/14 22:08:43 tom Exp $
4 * dlg_keys.c -- runtime binding support for dialog
6 * Copyright 2006-2020,2022 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
24 #include <dlg_internals.h>
25 #include <dlg_keys.h>
27 #define LIST_BINDINGS struct _list_bindings
29 #define CHR_BACKSLASH '\\'
30 #define IsOctal(ch) ((ch) >= '0' && (ch) <= '7')
32 LIST_BINDINGS {
33 LIST_BINDINGS *link;
34 WINDOW *win; /* window on which widget gets input */
35 const char *name; /* widget name */
36 bool buttons; /* true only for dlg_register_buttons() */
37 DLG_KEYS_BINDING *binding; /* list of bindings */
40 #define WILDNAME "*"
41 static LIST_BINDINGS *all_bindings;
42 static const DLG_KEYS_BINDING end_keys_binding = END_KEYS_BINDING;
45 * For a given named widget's window, associate a binding table.
47 void
48 dlg_register_window(WINDOW *win, const char *name, DLG_KEYS_BINDING * binding)
50 LIST_BINDINGS *p, *q;
52 for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
53 if (p->win == win && !strcmp(p->name, name)) {
54 p->binding = binding;
55 return;
58 /* add built-in bindings at the end of the list (see compare_bindings). */
59 if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
60 p->win = win;
61 p->name = name;
62 p->binding = binding;
63 if (q != 0) {
64 q->link = p;
65 } else {
66 all_bindings = p;
69 #if defined(HAVE_DLG_TRACE) && defined(HAVE_RC_FILE)
71 * Trace the binding information assigned to this window. For most widgets
72 * there is only one binding table. forms have two, so the trace will be
73 * longer. Since compiled-in bindings are only visible when the widget is
74 * registered, there is no other way to see what bindings are available,
75 * than by running dialog and tracing it.
77 DLG_TRACE(("# dlg_register_window %s\n", name));
78 dlg_dump_keys(dialog_state.trace_output);
79 dlg_dump_window_keys(dialog_state.trace_output, win);
80 DLG_TRACE(("# ...done dlg_register_window %s\n", name));
81 #endif
85 * A few CHR_xxx symbols in the default bindings are provided to fill in for
86 * incomplete terminal descriptions, or for consistency.
88 * A terminal description normally has an appropriate setting for kbs, which
89 * dialog assumes is the same as the curses value for KEY_BACKSPACE. But just
90 * in case, dialog supplies both.
92 * Also, while a terminal description may have KEY_DC, that normally is not the
93 * same as the shifted-backspace key provided with rxvt/xterm and imitators of
94 * those. Accommodate that by checking the return value of erasechar().
96 * The killchar() function's return value need not correspond to any of the
97 * KEY_xxx symbols. Just map CHR_KILL to that.
99 static int
100 actual_curses_key(DLG_KEYS_BINDING * p)
102 int result = p->curses_key;
103 int checks;
105 switch (p->curses_key) {
106 case CHR_KILL:
107 if ((checks = killchar()) > 0) {
108 result = checks;
110 break;
111 case CHR_BACKSPACE:
112 if ((checks = erasechar()) > 0) {
113 result = checks;
115 break;
116 case CHR_DELETE:
117 if ((checks = erasechar()) > 0 &&
118 (checks == result)) {
119 result = CHR_BACKSPACE;
121 break;
123 return result;
127 * Unlike dlg_lookup_key(), this looks for either widget-builtin or rc-file
128 * definitions, depending on whether 'win' is null.
130 static int
131 key_is_bound(WINDOW *win, const char *name, int curses_key, int function_key)
133 LIST_BINDINGS *p;
135 for (p = all_bindings; p != 0; p = p->link) {
136 if (p->win == win && !dlg_strcmp(p->name, name)) {
137 int n;
138 for (n = 0; p->binding[n].is_function_key >= 0; ++n) {
139 if (actual_curses_key(&(p->binding[n])) == curses_key
140 && p->binding[n].is_function_key == function_key) {
141 return TRUE;
146 return FALSE;
150 * Call this function after dlg_register_window(), for the list of button
151 * labels associated with the widget.
153 * Ensure that dlg_lookup_key() will not accidentally translate a key that
154 * we would like to use for a button abbreviation to some other key, e.g.,
155 * h/j/k/l for navigation into a cursor key. Do this by binding the key
156 * to itself.
158 * See dlg_char_to_button().
160 void
161 dlg_register_buttons(WINDOW *win, const char *name, const char **buttons)
163 int n;
164 LIST_BINDINGS *p;
165 DLG_KEYS_BINDING *q;
167 if (buttons == 0)
168 return;
170 for (n = 0; buttons[n] != 0; ++n) {
171 int curses_key = dlg_button_to_char(buttons[n]);
173 /* ignore binding if there is no key to bind */
174 if (curses_key < 0)
175 continue;
177 /* ignore multibyte characters */
178 if (curses_key >= KEY_MIN)
179 continue;
181 /* if it is not bound in the widget, skip it (no conflicts) */
182 if (!key_is_bound(win, name, curses_key, FALSE))
183 continue;
185 #ifdef HAVE_RC_FILE
186 /* if it is bound in the rc-file, skip it */
187 if (key_is_bound(0, name, curses_key, FALSE))
188 continue;
189 #endif
191 if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
192 if ((q = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0) {
193 q[0].is_function_key = 0;
194 q[0].curses_key = curses_key;
195 q[0].dialog_key = curses_key;
196 q[1] = end_keys_binding;
198 p->win = win;
199 p->name = name;
200 p->buttons = TRUE;
201 p->binding = q;
203 /* put these at the beginning, to override the widget's table */
204 p->link = all_bindings;
205 all_bindings = p;
206 } else {
207 free(p);
214 * Remove the bindings for a given window.
216 void
217 dlg_unregister_window(WINDOW *win)
219 LIST_BINDINGS *p, *q;
221 for (p = all_bindings, q = 0; p != 0; p = p->link) {
222 if (p->win == win) {
223 if (q != 0) {
224 q->link = p->link;
225 } else {
226 all_bindings = p->link;
228 /* the user-defined and buttons-bindings all are length=1 */
229 if (p->binding[1].is_function_key < 0)
230 free(p->binding);
231 free(p);
232 dlg_unregister_window(win);
233 break;
235 q = p;
240 * Call this after wgetch(), using the same window pointer and passing
241 * the curses-key.
243 * If there is no binding associated with the widget, it simply returns
244 * the given curses-key.
246 * Parameters:
247 * win is the window on which the wgetch() was done.
248 * curses_key is the value returned by wgetch().
249 * fkey in/out (on input, it is nonzero if curses_key is a function key,
250 * and on output, it is nonzero if the result is a function key).
253 dlg_lookup_key(WINDOW *win, int curses_key, int *fkey)
255 LIST_BINDINGS *p;
256 DLG_KEYS_BINDING *q;
259 * Ignore mouse clicks, since they are already encoded properly.
261 #ifdef KEY_MOUSE
262 if (*fkey != 0 && curses_key == KEY_MOUSE) {
264 } else
265 #endif
267 * Ignore resize events, since they are already encoded properly.
269 #ifdef KEY_RESIZE
270 if (*fkey != 0 && curses_key == KEY_RESIZE) {
272 } else
273 #endif
274 if (*fkey == 0 || curses_key < KEY_MAX) {
275 const char *name = WILDNAME;
276 if (win != 0) {
277 for (p = all_bindings; p != 0; p = p->link) {
278 if (p->win == win) {
279 name = p->name;
280 break;
284 for (p = all_bindings; p != 0; p = p->link) {
285 if (p->win == win ||
286 (p->win == 0 &&
287 (!strcmp(p->name, name) || !strcmp(p->name, WILDNAME)))) {
288 int function_key = (*fkey != 0);
289 for (q = p->binding; q->is_function_key >= 0; ++q) {
290 if (p->buttons
291 && !function_key
292 && actual_curses_key(q) == (int) dlg_toupper(curses_key)) {
293 *fkey = 0;
294 return q->dialog_key;
296 if (actual_curses_key(q) == curses_key
297 && q->is_function_key == function_key) {
298 *fkey = q->dialog_key;
299 return *fkey;
305 return curses_key;
309 * Test a dialog internal keycode to see if it corresponds to one of the push
310 * buttons on the widget such as "OK".
312 * This is only useful if there are user-defined key bindings, since there are
313 * no built-in bindings that map directly to DLGK_OK, etc.
315 * See also dlg_ok_buttoncode().
318 dlg_result_key(int dialog_key, int fkey GCC_UNUSED, int *resultp)
320 int done = FALSE;
322 DLG_TRACE(("# dlg_result_key(dialog_key=%d, fkey=%d)\n", dialog_key, fkey));
323 #ifdef KEY_RESIZE
324 if (dialog_state.had_resize) {
325 if (dialog_key == ERR) {
326 dialog_key = 0;
327 } else {
328 dialog_state.had_resize = FALSE;
330 } else if (fkey && dialog_key == KEY_RESIZE) {
331 dialog_state.had_resize = TRUE;
333 #endif
334 #ifdef HAVE_RC_FILE
335 if (fkey) {
336 switch ((DLG_KEYS_ENUM) dialog_key) {
337 case DLGK_OK:
338 if (!dialog_vars.nook) {
339 *resultp = DLG_EXIT_OK;
340 done = TRUE;
342 break;
343 case DLGK_CANCEL:
344 if (!dialog_vars.nocancel) {
345 *resultp = DLG_EXIT_CANCEL;
346 done = TRUE;
348 break;
349 case DLGK_EXTRA:
350 if (dialog_vars.extra_button) {
351 *resultp = DLG_EXIT_EXTRA;
352 done = TRUE;
354 break;
355 case DLGK_HELP:
356 if (dialog_vars.help_button) {
357 *resultp = DLG_EXIT_HELP;
358 done = TRUE;
360 break;
361 case DLGK_ESC:
362 *resultp = DLG_EXIT_ESC;
363 done = TRUE;
364 break;
365 default:
366 break;
368 } else
369 #endif
370 if (dialog_key == ESC) {
371 *resultp = DLG_EXIT_ESC;
372 done = TRUE;
373 } else if (dialog_key == ERR) {
374 *resultp = DLG_EXIT_ERROR;
375 done = TRUE;
378 return done;
382 * If a key was bound to one of the button-codes in dlg_result_key(), fake
383 * a button-value and an "Enter" key to cause the calling widget to return
384 * the corresponding status.
386 * See dlg_ok_buttoncode(), which maps settings for ok/extra/help and button
387 * number into exit-code.
390 dlg_button_key(int exit_code, int *button, int *dialog_key, int *fkey)
392 int changed = FALSE;
393 switch (exit_code) {
394 case DLG_EXIT_OK:
395 if (!dialog_vars.nook) {
396 *button = 0;
397 changed = TRUE;
399 break;
400 case DLG_EXIT_EXTRA:
401 if (dialog_vars.extra_button) {
402 *button = dialog_vars.nook ? 0 : 1;
403 changed = TRUE;
405 break;
406 case DLG_EXIT_CANCEL:
407 if (!dialog_vars.nocancel) {
408 *button = dialog_vars.nook ? 1 : 2;
409 changed = TRUE;
411 break;
412 case DLG_EXIT_HELP:
413 if (dialog_vars.help_button) {
414 int cancel = dialog_vars.nocancel ? 0 : 1;
415 int extra = dialog_vars.extra_button ? 1 : 0;
416 int okay = dialog_vars.nook ? 0 : 1;
417 *button = okay + extra + cancel;
418 changed = TRUE;
420 break;
422 if (changed) {
423 DLG_TRACE(("# dlg_button_key(%d:%s) button %d\n",
424 exit_code, dlg_exitcode2s(exit_code), *button));
425 *dialog_key = *fkey = DLGK_ENTER;
427 return changed;
431 dlg_ok_button_key(int exit_code, int *button, int *dialog_key, int *fkey)
433 int result;
434 DIALOG_VARS save;
436 dlg_save_vars(&save);
437 dialog_vars.nocancel = TRUE;
439 result = dlg_button_key(exit_code, button, dialog_key, fkey);
441 dlg_restore_vars(&save);
442 return result;
445 #ifdef HAVE_RC_FILE
446 typedef struct {
447 const char *name;
448 int code;
449 } CODENAME;
451 #define ASCII_NAME(name,code) { #name, code }
452 #define CURSES_NAME(upper) { #upper, KEY_ ## upper }
453 #define COUNT_CURSES TableSize(curses_names)
454 static const CODENAME curses_names[] =
456 ASCII_NAME(ESC, '\033'),
457 ASCII_NAME(CR, '\r'),
458 ASCII_NAME(LF, '\n'),
459 ASCII_NAME(FF, '\f'),
460 ASCII_NAME(TAB, '\t'),
461 ASCII_NAME(DEL, '\177'),
463 CURSES_NAME(DOWN),
464 CURSES_NAME(UP),
465 CURSES_NAME(LEFT),
466 CURSES_NAME(RIGHT),
467 CURSES_NAME(HOME),
468 CURSES_NAME(BACKSPACE),
469 CURSES_NAME(F0),
470 CURSES_NAME(DL),
471 CURSES_NAME(IL),
472 CURSES_NAME(DC),
473 CURSES_NAME(IC),
474 CURSES_NAME(EIC),
475 CURSES_NAME(CLEAR),
476 CURSES_NAME(EOS),
477 CURSES_NAME(EOL),
478 CURSES_NAME(SF),
479 CURSES_NAME(SR),
480 CURSES_NAME(NPAGE),
481 CURSES_NAME(PPAGE),
482 CURSES_NAME(STAB),
483 CURSES_NAME(CTAB),
484 CURSES_NAME(CATAB),
485 CURSES_NAME(ENTER),
486 CURSES_NAME(PRINT),
487 CURSES_NAME(LL),
488 CURSES_NAME(A1),
489 CURSES_NAME(A3),
490 CURSES_NAME(B2),
491 CURSES_NAME(C1),
492 CURSES_NAME(C3),
493 CURSES_NAME(BTAB),
494 CURSES_NAME(BEG),
495 CURSES_NAME(CANCEL),
496 CURSES_NAME(CLOSE),
497 CURSES_NAME(COMMAND),
498 CURSES_NAME(COPY),
499 CURSES_NAME(CREATE),
500 CURSES_NAME(END),
501 CURSES_NAME(EXIT),
502 CURSES_NAME(FIND),
503 CURSES_NAME(HELP),
504 CURSES_NAME(MARK),
505 CURSES_NAME(MESSAGE),
506 CURSES_NAME(MOVE),
507 CURSES_NAME(NEXT),
508 CURSES_NAME(OPEN),
509 CURSES_NAME(OPTIONS),
510 CURSES_NAME(PREVIOUS),
511 CURSES_NAME(REDO),
512 CURSES_NAME(REFERENCE),
513 CURSES_NAME(REFRESH),
514 CURSES_NAME(REPLACE),
515 CURSES_NAME(RESTART),
516 CURSES_NAME(RESUME),
517 CURSES_NAME(SAVE),
518 CURSES_NAME(SBEG),
519 CURSES_NAME(SCANCEL),
520 CURSES_NAME(SCOMMAND),
521 CURSES_NAME(SCOPY),
522 CURSES_NAME(SCREATE),
523 CURSES_NAME(SDC),
524 CURSES_NAME(SDL),
525 CURSES_NAME(SELECT),
526 CURSES_NAME(SEND),
527 CURSES_NAME(SEOL),
528 CURSES_NAME(SEXIT),
529 CURSES_NAME(SFIND),
530 CURSES_NAME(SHELP),
531 CURSES_NAME(SHOME),
532 CURSES_NAME(SIC),
533 CURSES_NAME(SLEFT),
534 CURSES_NAME(SMESSAGE),
535 CURSES_NAME(SMOVE),
536 CURSES_NAME(SNEXT),
537 CURSES_NAME(SOPTIONS),
538 CURSES_NAME(SPREVIOUS),
539 CURSES_NAME(SPRINT),
540 CURSES_NAME(SREDO),
541 CURSES_NAME(SREPLACE),
542 CURSES_NAME(SRIGHT),
543 CURSES_NAME(SRSUME),
544 CURSES_NAME(SSAVE),
545 CURSES_NAME(SSUSPEND),
546 CURSES_NAME(SUNDO),
547 CURSES_NAME(SUSPEND),
548 CURSES_NAME(UNDO),
551 #define DIALOG_NAME(upper) { #upper, DLGK_ ## upper }
552 #define COUNT_DIALOG TableSize(dialog_names)
553 static const CODENAME dialog_names[] =
555 DIALOG_NAME(OK),
556 DIALOG_NAME(CANCEL),
557 DIALOG_NAME(EXTRA),
558 DIALOG_NAME(HELP),
559 DIALOG_NAME(ESC),
560 DIALOG_NAME(PAGE_FIRST),
561 DIALOG_NAME(PAGE_LAST),
562 DIALOG_NAME(PAGE_NEXT),
563 DIALOG_NAME(PAGE_PREV),
564 DIALOG_NAME(ITEM_FIRST),
565 DIALOG_NAME(ITEM_LAST),
566 DIALOG_NAME(ITEM_NEXT),
567 DIALOG_NAME(ITEM_PREV),
568 DIALOG_NAME(FIELD_FIRST),
569 DIALOG_NAME(FIELD_LAST),
570 DIALOG_NAME(FIELD_NEXT),
571 DIALOG_NAME(FIELD_PREV),
572 DIALOG_NAME(FORM_FIRST),
573 DIALOG_NAME(FORM_LAST),
574 DIALOG_NAME(FORM_NEXT),
575 DIALOG_NAME(FORM_PREV),
576 DIALOG_NAME(GRID_UP),
577 DIALOG_NAME(GRID_DOWN),
578 DIALOG_NAME(GRID_LEFT),
579 DIALOG_NAME(GRID_RIGHT),
580 DIALOG_NAME(DELETE_LEFT),
581 DIALOG_NAME(DELETE_RIGHT),
582 DIALOG_NAME(DELETE_ALL),
583 DIALOG_NAME(ENTER),
584 DIALOG_NAME(BEGIN),
585 DIALOG_NAME(FINAL),
586 DIALOG_NAME(SELECT),
587 DIALOG_NAME(HELPFILE),
588 DIALOG_NAME(TRACE),
589 DIALOG_NAME(TOGGLE),
590 DIALOG_NAME(LEAVE)
593 #define MAP2(letter,actual) { letter, actual }
595 static const struct {
596 int letter;
597 int actual;
598 } escaped_letters[] = {
600 MAP2('a', DLG_CTRL('G')),
601 MAP2('b', DLG_CTRL('H')),
602 MAP2('f', DLG_CTRL('L')),
603 MAP2('n', DLG_CTRL('J')),
604 MAP2('r', DLG_CTRL('M')),
605 MAP2('s', CHR_SPACE),
606 MAP2('t', DLG_CTRL('I')),
607 MAP2('\\', '\\'),
610 #undef MAP2
612 static char *
613 skip_white(char *s)
615 while (*s != '\0' && isspace(UCH(*s)))
616 ++s;
617 return s;
620 static char *
621 skip_black(char *s)
623 while (*s != '\0' && !isspace(UCH(*s)))
624 ++s;
625 return s;
629 * Find a user-defined binding, given the curses key code.
631 static DLG_KEYS_BINDING *
632 find_binding(char *widget, int curses_key)
634 LIST_BINDINGS *p;
635 DLG_KEYS_BINDING *result = 0;
637 for (p = all_bindings; p != 0; p = p->link) {
638 if (p->win == 0
639 && !dlg_strcmp(p->name, widget)
640 && actual_curses_key(p->binding) == curses_key) {
641 result = p->binding;
642 break;
645 return result;
649 * Built-in bindings have a nonzero "win" member, and the associated binding
650 * table can have more than one entry. We keep those last, since lookups will
651 * find the user-defined bindings first and use those.
653 * Sort "*" (all-widgets) entries past named widgets, since those are less
654 * specific.
656 static int
657 compare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b)
659 int result = 0;
660 if (a->win == b->win) {
661 if (!strcmp(a->name, b->name)) {
662 result = actual_curses_key(a->binding) - actual_curses_key(b->binding);
663 } else if (!strcmp(b->name, WILDNAME)) {
664 result = -1;
665 } else if (!strcmp(a->name, WILDNAME)) {
666 result = 1;
667 } else {
668 result = dlg_strcmp(a->name, b->name);
670 } else if (b->win) {
671 result = -1;
672 } else {
673 result = 1;
675 return result;
679 * Find a user-defined binding, given the curses key code. If it does not
680 * exist, create a new one, inserting it into the linked list, keeping it
681 * sorted to simplify lookups for user-defined bindings that can override
682 * the built-in bindings.
684 static DLG_KEYS_BINDING *
685 make_binding(char *widget, int curses_key, int is_function, int dialog_key)
687 LIST_BINDINGS *entry = 0;
688 DLG_KEYS_BINDING *data = 0;
689 char *name;
690 DLG_KEYS_BINDING *result = find_binding(widget, curses_key);
692 if (result == 0
693 && (entry = dlg_calloc(LIST_BINDINGS, 1)) != 0
694 && (data = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0
695 && (name = dlg_strclone(widget)) != 0) {
696 LIST_BINDINGS *p, *q;
698 entry->name = name;
699 entry->binding = data;
701 data[0].is_function_key = is_function;
702 data[0].curses_key = curses_key;
703 data[0].dialog_key = dialog_key;
705 data[1] = end_keys_binding;
707 for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
708 if (compare_bindings(entry, p) < 0) {
709 break;
712 if (q != 0) {
713 q->link = entry;
714 } else {
715 all_bindings = entry;
717 if (p != 0) {
718 entry->link = p;
720 result = data;
721 } else if (entry != 0) {
722 free(entry);
723 if (data)
724 free(data);
727 return result;
730 static int
731 decode_escaped(char **string)
733 int result = 0;
735 if (IsOctal(**string)) {
736 int limit = 3;
737 while (limit-- > 0 && IsOctal(**string)) {
738 int ch = (**string);
739 *string += 1;
740 result = (result << 3) | (ch - '0');
742 } else {
743 unsigned n;
745 for (n = 0; n < TableSize(escaped_letters); ++n) {
746 if (**string == escaped_letters[n].letter) {
747 *string += 1;
748 result = escaped_letters[n].actual;
749 break;
753 return result;
756 static char *
757 encode_escaped(int value)
759 static char result[80];
760 unsigned n;
761 bool found = FALSE;
762 for (n = 0; n < TableSize(escaped_letters); ++n) {
763 if (value == escaped_letters[n].actual) {
764 found = TRUE;
765 sprintf(result, "%c", escaped_letters[n].letter);
766 break;
769 if (!found) {
770 sprintf(result, "%03o", value & 0xff);
772 return result;
776 * Parse the parameters of the "bindkey" configuration-file entry. This
777 * expects widget name which may be "*", followed by curses key definition and
778 * then dialog key definition.
780 * The curses key "should" be one of the names (ignoring case) from
781 * curses_names[], but may also be a single control character (prefix "^" or
782 * "~" depending on whether it is C0 or C1), or an escaped single character.
783 * Binding a printable character with dialog is possible but not useful.
785 * The dialog key must be one of the names from dialog_names[].
788 dlg_parse_bindkey(char *params)
790 char *p = skip_white(params);
791 int result = FALSE;
792 char *widget;
793 int curses_key;
794 int dialog_key;
796 curses_key = -1;
797 dialog_key = -1;
798 widget = p;
800 p = skip_black(p);
801 if (p != widget && *p != '\0') {
802 char *q;
803 unsigned xx;
804 bool escaped = FALSE;
805 int modified = 0;
806 int is_function = FALSE;
808 *p++ = '\0';
809 p = skip_white(p);
810 q = p;
811 while (*p != '\0' && curses_key < 0) {
812 if (escaped) {
813 escaped = FALSE;
814 curses_key = decode_escaped(&p);
815 } else if (*p == CHR_BACKSLASH) {
816 escaped = TRUE;
817 } else if (modified) {
818 if (*p == '?') {
819 curses_key = ((modified == '^')
820 ? 127
821 : 255);
822 } else {
823 curses_key = ((modified == '^')
824 ? (*p & 0x1f)
825 : ((*p & 0x1f) | 0x80));
827 } else if (*p == '^') {
828 modified = *p;
829 } else if (*p == '~') {
830 modified = *p;
831 } else if (isspace(UCH(*p))) {
832 break;
834 ++p;
836 if (!isspace(UCH(*p))) {
838 } else {
839 *p++ = '\0';
840 if (curses_key < 0) {
841 char fprefix[2];
842 char check[2];
843 int keynumber;
844 if (sscanf(q, "%1[Ff]%d%c", fprefix, &keynumber, check) == 2) {
845 curses_key = KEY_F(keynumber);
846 is_function = TRUE;
847 } else {
848 for (xx = 0; xx < COUNT_CURSES; ++xx) {
849 if (!dlg_strcmp(curses_names[xx].name, q)) {
850 curses_key = curses_names[xx].code;
851 is_function = (curses_key >= KEY_MIN);
852 break;
858 q = skip_white(p);
859 p = skip_black(q);
860 if (p != q) {
861 for (xx = 0; xx < COUNT_DIALOG; ++xx) {
862 if (!dlg_strcmp(dialog_names[xx].name, q)) {
863 dialog_key = dialog_names[xx].code;
864 break;
868 if (*widget != '\0'
869 && curses_key >= 0
870 && dialog_key >= 0
871 && make_binding(widget, curses_key, is_function, dialog_key) != 0) {
872 result = TRUE;
875 return result;
878 static void
879 dump_curses_key(FILE *fp, int curses_key)
881 if (curses_key > KEY_MIN) {
882 unsigned n;
883 bool found = FALSE;
884 for (n = 0; n < COUNT_CURSES; ++n) {
885 if (curses_names[n].code == curses_key) {
886 fprintf(fp, "%s", curses_names[n].name);
887 found = TRUE;
888 break;
891 if (!found) {
892 #ifdef KEY_MOUSE
893 if (is_DLGK_MOUSE(curses_key)) {
894 fprintf(fp, "MOUSE-");
895 dump_curses_key(fp, curses_key - M_EVENT);
896 } else
897 #endif
898 if (curses_key >= KEY_F(0)) {
899 fprintf(fp, "F%d", curses_key - KEY_F(0));
900 } else {
901 fprintf(fp, "curses%d", curses_key);
904 } else if (curses_key >= 0 && curses_key < 32) {
905 fprintf(fp, "^%c", curses_key + 64);
906 } else if (curses_key == 127) {
907 fprintf(fp, "^?");
908 } else if (curses_key >= 128 && curses_key < 160) {
909 fprintf(fp, "~%c", curses_key - 64);
910 } else if (curses_key == 255) {
911 fprintf(fp, "~?");
912 } else if (curses_key > 32 &&
913 curses_key < 127 &&
914 curses_key != CHR_BACKSLASH) {
915 fprintf(fp, "%c", curses_key);
916 } else {
917 fprintf(fp, "%c%s", CHR_BACKSLASH, encode_escaped(curses_key));
921 static void
922 dump_dialog_key(FILE *fp, int dialog_key)
924 unsigned n;
925 bool found = FALSE;
926 for (n = 0; n < COUNT_DIALOG; ++n) {
927 if (dialog_names[n].code == dialog_key) {
928 fputs(dialog_names[n].name, fp);
929 found = TRUE;
930 break;
933 if (!found) {
934 fprintf(fp, "dialog%d", dialog_key);
938 static void
939 dump_one_binding(FILE *fp,
940 WINDOW *win,
941 const char *widget,
942 DLG_KEYS_BINDING * binding)
944 int actual;
945 int fkey = (actual_curses_key(binding) > 255);
947 fprintf(fp, "bindkey %s ", widget);
948 dump_curses_key(fp, actual_curses_key(binding));
949 fputc(' ', fp);
950 dump_dialog_key(fp, binding->dialog_key);
951 actual = dlg_lookup_key(win, actual_curses_key(binding), &fkey);
952 #ifdef KEY_MOUSE
953 if (is_DLGK_MOUSE(actual_curses_key(binding)) && is_DLGK_MOUSE(actual)) {
954 ; /* EMPTY */
955 } else
956 #endif
957 if (actual != binding->dialog_key) {
958 fprintf(fp, "\t# overridden by ");
959 dump_dialog_key(fp, actual);
961 fputc('\n', fp);
965 * Dump bindings for the given window. If it is a null, then this dumps the
966 * initial bindings which were loaded from the rc-file that are used as
967 * overall defaults.
969 void
970 dlg_dump_window_keys(FILE *fp, WINDOW *win)
972 if (fp != 0) {
973 LIST_BINDINGS *p;
974 DLG_KEYS_BINDING *q;
975 const char *last = "";
977 for (p = all_bindings; p != 0; p = p->link) {
978 if (p->win == win) {
979 if (dlg_strcmp(last, p->name)) {
980 fprintf(fp, "# key bindings for %s widgets%s\n",
981 !strcmp(p->name, WILDNAME) ? "all" : p->name,
982 win == 0 ? " (user-defined)" : "");
983 last = p->name;
985 for (q = p->binding; q->is_function_key >= 0; ++q) {
986 dump_one_binding(fp, win, p->name, q);
994 * Dump all of the bindings which are not specific to a given widget, i.e.,
995 * the "win" member is null.
997 void
998 dlg_dump_keys(FILE *fp)
1000 if (fp != 0) {
1001 LIST_BINDINGS *p;
1002 unsigned count = 0;
1004 for (p = all_bindings; p != 0; p = p->link) {
1005 if (p->win == 0) {
1006 ++count;
1009 if (count != 0) {
1010 dlg_dump_window_keys(fp, 0);
1014 #endif /* HAVE_RC_FILE */