1 /*
2 * $Id: dlg_keys.c,v 1.45 2018/05/28 17:27:10 tom Exp $
3 *
4 * dlg_keys.c -- runtime binding support for dialog
5 *
6 * Copyright 2006-2017,2018 Thomas E. Dickey
7 *
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.
11 *
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.
16 *
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.
22 */
23
24 #include <dialog.h>
25 #include <dlg_keys.h>
26
27 #define LIST_BINDINGS struct _list_bindings
28
29 #define CHR_BACKSLASH '\\'
30 #define IsOctal(ch) ((ch) >= '0' && (ch) <= '7')
31 #define TableSize(name) (sizeof(name)/sizeof(name[0]))
32
33 LIST_BINDINGS {
34 LIST_BINDINGS *link;
35 WINDOW *win; /* window on which widget gets input */
36 const char *name; /* widget name */
37 bool buttons; /* true only for dlg_register_buttons() */
38 DLG_KEYS_BINDING *binding; /* list of bindings */
39 };
40
41 #define WILDNAME "*"
42 static LIST_BINDINGS *all_bindings;
43 static const DLG_KEYS_BINDING end_keys_binding = END_KEYS_BINDING;
44
45 /*
46 * For a given named widget's window, associate a binding table.
47 */
48 void
dlg_register_window(WINDOW * win,const char * name,DLG_KEYS_BINDING * binding)49 dlg_register_window(WINDOW *win, const char *name, DLG_KEYS_BINDING * binding)
50 {
51 LIST_BINDINGS *p, *q;
52
53 for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
54 if (p->win == win && !strcmp(p->name, name)) {
55 p->binding = binding;
56 return;
57 }
58 }
59 /* add built-in bindings at the end of the list (see compare_bindings). */
60 if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
61 p->win = win;
62 p->name = name;
63 p->binding = binding;
64 if (q != 0) {
65 q->link = p;
66 } else {
67 all_bindings = p;
68 }
69 }
70 #if defined(HAVE_DLG_TRACE) && defined(HAVE_RC_FILE)
71 /*
72 * Trace the binding information assigned to this window. For most widgets
73 * there is only one binding table. forms have two, so the trace will be
74 * longer. Since compiled-in bindings are only visible when the widget is
75 * registered, there is no other way to see what bindings are available,
76 * than by running dialog and tracing it.
77 */
78 DLG_TRACE(("# dlg_register_window %s\n", name));
79 dlg_dump_keys(dialog_state.trace_output);
80 dlg_dump_window_keys(dialog_state.trace_output, win);
81 DLG_TRACE(("# ...done dlg_register_window %s\n", name));
82 #endif
83 }
84
85 /*
86 * Unlike dlg_lookup_key(), this looks for either widget-builtin or rc-file
87 * definitions, depending on whether 'win' is null.
88 */
89 static int
key_is_bound(WINDOW * win,const char * name,int curses_key,int function_key)90 key_is_bound(WINDOW *win, const char *name, int curses_key, int function_key)
91 {
92 LIST_BINDINGS *p;
93
94 for (p = all_bindings; p != 0; p = p->link) {
95 if (p->win == win && !dlg_strcmp(p->name, name)) {
96 int n;
97 for (n = 0; p->binding[n].is_function_key >= 0; ++n) {
98 if (p->binding[n].curses_key == curses_key
99 && p->binding[n].is_function_key == function_key) {
100 return TRUE;
101 }
102 }
103 }
104 }
105 return FALSE;
106 }
107
108 /*
109 * Call this function after dlg_register_window(), for the list of button
110 * labels associated with the widget.
111 *
112 * Ensure that dlg_lookup_key() will not accidentally translate a key that
113 * we would like to use for a button abbreviation to some other key, e.g.,
114 * h/j/k/l for navigation into a cursor key. Do this by binding the key
115 * to itself.
116 *
117 * See dlg_char_to_button().
118 */
119 void
dlg_register_buttons(WINDOW * win,const char * name,const char ** buttons)120 dlg_register_buttons(WINDOW *win, const char *name, const char **buttons)
121 {
122 int n;
123 LIST_BINDINGS *p;
124 DLG_KEYS_BINDING *q;
125
126 if (buttons == 0)
127 return;
128
129 for (n = 0; buttons[n] != 0; ++n) {
130 int curses_key = dlg_button_to_char(buttons[n]);
131
132 /* ignore multibyte characters */
133 if (curses_key >= KEY_MIN)
134 continue;
135
136 /* if it is not bound in the widget, skip it (no conflicts) */
137 if (!key_is_bound(win, name, curses_key, FALSE))
138 continue;
139
140 #ifdef HAVE_RC_FILE
141 /* if it is bound in the rc-file, skip it */
142 if (key_is_bound(0, name, curses_key, FALSE))
143 continue;
144 #endif
145
146 if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
147 if ((q = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0) {
148 q[0].is_function_key = 0;
149 q[0].curses_key = curses_key;
150 q[0].dialog_key = curses_key;
151 q[1] = end_keys_binding;
152
153 p->win = win;
154 p->name = name;
155 p->buttons = TRUE;
156 p->binding = q;
157
158 /* put these at the beginning, to override the widget's table */
159 p->link = all_bindings;
160 all_bindings = p;
161 } else {
162 free(p);
163 }
164 }
165 }
166 }
167
168 /*
169 * Remove the bindings for a given window.
170 */
171 void
dlg_unregister_window(WINDOW * win)172 dlg_unregister_window(WINDOW *win)
173 {
174 LIST_BINDINGS *p, *q;
175
176 for (p = all_bindings, q = 0; p != 0; p = p->link) {
177 if (p->win == win) {
178 if (q != 0) {
179 q->link = p->link;
180 } else {
181 all_bindings = p->link;
182 }
183 /* the user-defined and buttons-bindings all are length=1 */
184 if (p->binding[1].is_function_key < 0)
185 free(p->binding);
186 free(p);
187 dlg_unregister_window(win);
188 break;
189 }
190 q = p;
191 }
192 }
193
194 /*
195 * Call this after wgetch(), using the same window pointer and passing
196 * the curses-key.
197 *
198 * If there is no binding associated with the widget, it simply returns
199 * the given curses-key.
200 *
201 * Parameters:
202 * win is the window on which the wgetch() was done.
203 * curses_key is the value returned by wgetch().
204 * fkey in/out (on input, it is nonzero if curses_key is a function key,
205 * and on output, it is nonzero if the result is a function key).
206 */
207 int
dlg_lookup_key(WINDOW * win,int curses_key,int * fkey)208 dlg_lookup_key(WINDOW *win, int curses_key, int *fkey)
209 {
210 LIST_BINDINGS *p;
211 DLG_KEYS_BINDING *q;
212
213 /*
214 * Ignore mouse clicks, since they are already encoded properly.
215 */
216 #ifdef KEY_MOUSE
217 if (*fkey != 0 && curses_key == KEY_MOUSE) {
218 ;
219 } else
220 #endif
221 /*
222 * Ignore resize events, since they are already encoded properly.
223 */
224 #ifdef KEY_RESIZE
225 if (*fkey != 0 && curses_key == KEY_RESIZE) {
226 ;
227 } else
228 #endif
229 if (*fkey == 0 || curses_key < KEY_MAX) {
230 const char *name = WILDNAME;
231 if (win != 0) {
232 for (p = all_bindings; p != 0; p = p->link) {
233 if (p->win == win) {
234 name = p->name;
235 break;
236 }
237 }
238 }
239 for (p = all_bindings; p != 0; p = p->link) {
240 if (p->win == win ||
241 (p->win == 0 &&
242 (!strcmp(p->name, name) || !strcmp(p->name, WILDNAME)))) {
243 int function_key = (*fkey != 0);
244 for (q = p->binding; q->is_function_key >= 0; ++q) {
245 if (p->buttons
246 && !function_key
247 && q->curses_key == (int) dlg_toupper(curses_key)) {
248 *fkey = 0;
249 return q->dialog_key;
250 }
251 if (q->curses_key == curses_key
252 && q->is_function_key == function_key) {
253 *fkey = q->dialog_key;
254 return *fkey;
255 }
256 }
257 }
258 }
259 }
260 return curses_key;
261 }
262
263 /*
264 * Test a dialog internal keycode to see if it corresponds to one of the push
265 * buttons on the widget such as "OK".
266 *
267 * This is only useful if there are user-defined key bindings, since there are
268 * no built-in bindings that map directly to DLGK_OK, etc.
269 *
270 * See also dlg_ok_buttoncode().
271 */
272 int
dlg_result_key(int dialog_key,int fkey GCC_UNUSED,int * resultp)273 dlg_result_key(int dialog_key, int fkey GCC_UNUSED, int *resultp)
274 {
275 int done = FALSE;
276
277 #ifdef HAVE_RC_FILE
278 if (fkey) {
279 switch ((DLG_KEYS_ENUM) dialog_key) {
280 case DLGK_OK:
281 *resultp = DLG_EXIT_OK;
282 done = TRUE;
283 break;
284 case DLGK_CANCEL:
285 if (!dialog_vars.nocancel) {
286 *resultp = DLG_EXIT_CANCEL;
287 done = TRUE;
288 }
289 break;
290 case DLGK_EXTRA:
291 if (dialog_vars.extra_button) {
292 *resultp = DLG_EXIT_EXTRA;
293 done = TRUE;
294 }
295 break;
296 case DLGK_HELP:
297 if (dialog_vars.help_button) {
298 *resultp = DLG_EXIT_HELP;
299 done = TRUE;
300 }
301 break;
302 case DLGK_ESC:
303 *resultp = DLG_EXIT_ESC;
304 done = TRUE;
305 break;
306 default:
307 break;
308 }
309 } else
310 #endif
311 if (dialog_key == ESC) {
312 *resultp = DLG_EXIT_ESC;
313 done = TRUE;
314 } else if (dialog_key == ERR) {
315 *resultp = DLG_EXIT_ERROR;
316 done = TRUE;
317 }
318
319 return done;
320 }
321
322 #ifdef HAVE_RC_FILE
323 typedef struct {
324 const char *name;
325 int code;
326 } CODENAME;
327
328 #define ASCII_NAME(name,code) { #name, code }
329 #define CURSES_NAME(upper) { #upper, KEY_ ## upper }
330 #define COUNT_CURSES TableSize(curses_names)
331 static const CODENAME curses_names[] =
332 {
333 ASCII_NAME(ESC, '\033'),
334 ASCII_NAME(CR, '\r'),
335 ASCII_NAME(LF, '\n'),
336 ASCII_NAME(FF, '\f'),
337 ASCII_NAME(TAB, '\t'),
338 ASCII_NAME(DEL, '\177'),
339
340 CURSES_NAME(DOWN),
341 CURSES_NAME(UP),
342 CURSES_NAME(LEFT),
343 CURSES_NAME(RIGHT),
344 CURSES_NAME(HOME),
345 CURSES_NAME(BACKSPACE),
346 CURSES_NAME(F0),
347 CURSES_NAME(DL),
348 CURSES_NAME(IL),
349 CURSES_NAME(DC),
350 CURSES_NAME(IC),
351 CURSES_NAME(EIC),
352 CURSES_NAME(CLEAR),
353 CURSES_NAME(EOS),
354 CURSES_NAME(EOL),
355 CURSES_NAME(SF),
356 CURSES_NAME(SR),
357 CURSES_NAME(NPAGE),
358 CURSES_NAME(PPAGE),
359 CURSES_NAME(STAB),
360 CURSES_NAME(CTAB),
361 CURSES_NAME(CATAB),
362 CURSES_NAME(ENTER),
363 CURSES_NAME(PRINT),
364 CURSES_NAME(LL),
365 CURSES_NAME(A1),
366 CURSES_NAME(A3),
367 CURSES_NAME(B2),
368 CURSES_NAME(C1),
369 CURSES_NAME(C3),
370 CURSES_NAME(BTAB),
371 CURSES_NAME(BEG),
372 CURSES_NAME(CANCEL),
373 CURSES_NAME(CLOSE),
374 CURSES_NAME(COMMAND),
375 CURSES_NAME(COPY),
376 CURSES_NAME(CREATE),
377 CURSES_NAME(END),
378 CURSES_NAME(EXIT),
379 CURSES_NAME(FIND),
380 CURSES_NAME(HELP),
381 CURSES_NAME(MARK),
382 CURSES_NAME(MESSAGE),
383 CURSES_NAME(MOVE),
384 CURSES_NAME(NEXT),
385 CURSES_NAME(OPEN),
386 CURSES_NAME(OPTIONS),
387 CURSES_NAME(PREVIOUS),
388 CURSES_NAME(REDO),
389 CURSES_NAME(REFERENCE),
390 CURSES_NAME(REFRESH),
391 CURSES_NAME(REPLACE),
392 CURSES_NAME(RESTART),
393 CURSES_NAME(RESUME),
394 CURSES_NAME(SAVE),
395 CURSES_NAME(SBEG),
396 CURSES_NAME(SCANCEL),
397 CURSES_NAME(SCOMMAND),
398 CURSES_NAME(SCOPY),
399 CURSES_NAME(SCREATE),
400 CURSES_NAME(SDC),
401 CURSES_NAME(SDL),
402 CURSES_NAME(SELECT),
403 CURSES_NAME(SEND),
404 CURSES_NAME(SEOL),
405 CURSES_NAME(SEXIT),
406 CURSES_NAME(SFIND),
407 CURSES_NAME(SHELP),
408 CURSES_NAME(SHOME),
409 CURSES_NAME(SIC),
410 CURSES_NAME(SLEFT),
411 CURSES_NAME(SMESSAGE),
412 CURSES_NAME(SMOVE),
413 CURSES_NAME(SNEXT),
414 CURSES_NAME(SOPTIONS),
415 CURSES_NAME(SPREVIOUS),
416 CURSES_NAME(SPRINT),
417 CURSES_NAME(SREDO),
418 CURSES_NAME(SREPLACE),
419 CURSES_NAME(SRIGHT),
420 CURSES_NAME(SRSUME),
421 CURSES_NAME(SSAVE),
422 CURSES_NAME(SSUSPEND),
423 CURSES_NAME(SUNDO),
424 CURSES_NAME(SUSPEND),
425 CURSES_NAME(UNDO),
426 };
427
428 #define DIALOG_NAME(upper) { #upper, DLGK_ ## upper }
429 #define COUNT_DIALOG TableSize(dialog_names)
430 static const CODENAME dialog_names[] =
431 {
432 DIALOG_NAME(OK),
433 DIALOG_NAME(CANCEL),
434 DIALOG_NAME(EXTRA),
435 DIALOG_NAME(HELP),
436 DIALOG_NAME(ESC),
437 DIALOG_NAME(PAGE_FIRST),
438 DIALOG_NAME(PAGE_LAST),
439 DIALOG_NAME(PAGE_NEXT),
440 DIALOG_NAME(PAGE_PREV),
441 DIALOG_NAME(ITEM_FIRST),
442 DIALOG_NAME(ITEM_LAST),
443 DIALOG_NAME(ITEM_NEXT),
444 DIALOG_NAME(ITEM_PREV),
445 DIALOG_NAME(FIELD_FIRST),
446 DIALOG_NAME(FIELD_LAST),
447 DIALOG_NAME(FIELD_NEXT),
448 DIALOG_NAME(FIELD_PREV),
449 DIALOG_NAME(FORM_FIRST),
450 DIALOG_NAME(FORM_LAST),
451 DIALOG_NAME(FORM_NEXT),
452 DIALOG_NAME(FORM_PREV),
453 DIALOG_NAME(GRID_UP),
454 DIALOG_NAME(GRID_DOWN),
455 DIALOG_NAME(GRID_LEFT),
456 DIALOG_NAME(GRID_RIGHT),
457 DIALOG_NAME(DELETE_LEFT),
458 DIALOG_NAME(DELETE_RIGHT),
459 DIALOG_NAME(DELETE_ALL),
460 DIALOG_NAME(ENTER),
461 DIALOG_NAME(BEGIN),
462 DIALOG_NAME(FINAL),
463 DIALOG_NAME(SELECT),
464 DIALOG_NAME(HELPFILE),
465 DIALOG_NAME(TRACE),
466 DIALOG_NAME(TOGGLE)
467 };
468
469 #define MAP2(letter,actual) { letter, actual }
470
471 static const struct {
472 int letter;
473 int actual;
474 } escaped_letters[] = {
475
476 MAP2('a', DLG_CTRL('G')),
477 MAP2('b', DLG_CTRL('H')),
478 MAP2('f', DLG_CTRL('L')),
479 MAP2('n', DLG_CTRL('J')),
480 MAP2('r', DLG_CTRL('M')),
481 MAP2('s', CHR_SPACE),
482 MAP2('t', DLG_CTRL('I')),
483 MAP2('\\', '\\'),
484 };
485
486 #undef MAP2
487
488 static char *
skip_white(char * s)489 skip_white(char *s)
490 {
491 while (*s != '\0' && isspace(UCH(*s)))
492 ++s;
493 return s;
494 }
495
496 static char *
skip_black(char * s)497 skip_black(char *s)
498 {
499 while (*s != '\0' && !isspace(UCH(*s)))
500 ++s;
501 return s;
502 }
503
504 /*
505 * Find a user-defined binding, given the curses key code.
506 */
507 static DLG_KEYS_BINDING *
find_binding(char * widget,int curses_key)508 find_binding(char *widget, int curses_key)
509 {
510 LIST_BINDINGS *p;
511 DLG_KEYS_BINDING *result = 0;
512
513 for (p = all_bindings; p != 0; p = p->link) {
514 if (p->win == 0
515 && !dlg_strcmp(p->name, widget)
516 && p->binding->curses_key == curses_key) {
517 result = p->binding;
518 break;
519 }
520 }
521 return result;
522 }
523
524 /*
525 * Built-in bindings have a nonzero "win" member, and the associated binding
526 * table can have more than one entry. We keep those last, since lookups will
527 * find the user-defined bindings first and use those.
528 *
529 * Sort "*" (all-widgets) entries past named widgets, since those are less
530 * specific.
531 */
532 static int
compare_bindings(LIST_BINDINGS * a,LIST_BINDINGS * b)533 compare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b)
534 {
535 int result = 0;
536 if (a->win == b->win) {
537 if (!strcmp(a->name, b->name)) {
538 result = a->binding[0].curses_key - b->binding[0].curses_key;
539 } else if (!strcmp(b->name, WILDNAME)) {
540 result = -1;
541 } else if (!strcmp(a->name, WILDNAME)) {
542 result = 1;
543 } else {
544 result = dlg_strcmp(a->name, b->name);
545 }
546 } else if (b->win) {
547 result = -1;
548 } else {
549 result = 1;
550 }
551 return result;
552 }
553
554 /*
555 * Find a user-defined binding, given the curses key code. If it does not
556 * exist, create a new one, inserting it into the linked list, keeping it
557 * sorted to simplify lookups for user-defined bindings that can override
558 * the built-in bindings.
559 */
560 static DLG_KEYS_BINDING *
make_binding(char * widget,int curses_key,int is_function,int dialog_key)561 make_binding(char *widget, int curses_key, int is_function, int dialog_key)
562 {
563 LIST_BINDINGS *entry = 0;
564 DLG_KEYS_BINDING *data = 0;
565 char *name;
566 LIST_BINDINGS *p, *q;
567 DLG_KEYS_BINDING *result = find_binding(widget, curses_key);
568
569 if (result == 0
570 && (entry = dlg_calloc(LIST_BINDINGS, 1)) != 0
571 && (data = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0
572 && (name = dlg_strclone(widget)) != 0) {
573
574 entry->name = name;
575 entry->binding = data;
576
577 data[0].is_function_key = is_function;
578 data[0].curses_key = curses_key;
579 data[0].dialog_key = dialog_key;
580
581 data[1] = end_keys_binding;
582
583 for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
584 if (compare_bindings(entry, p) < 0) {
585 break;
586 }
587 }
588 if (q != 0) {
589 q->link = entry;
590 } else {
591 all_bindings = entry;
592 }
593 if (p != 0) {
594 entry->link = p;
595 }
596 result = data;
597 } else if (entry != 0) {
598 free(entry);
599 if (data)
600 free(data);
601 }
602
603 return result;
604 }
605
606 static int
decode_escaped(char ** string)607 decode_escaped(char **string)
608 {
609 unsigned n;
610 int result = 0;
611
612 if (IsOctal(**string)) {
613 int limit = 3;
614 while (limit-- > 0 && IsOctal(**string)) {
615 int ch = (**string);
616 *string += 1;
617 result = (result << 3) | (ch - '0');
618 }
619 } else {
620 for (n = 0; n < TableSize(escaped_letters); ++n) {
621 if (**string == escaped_letters[n].letter) {
622 *string += 1;
623 result = escaped_letters[n].actual;
624 break;
625 }
626 }
627 }
628 return result;
629 }
630
631 static char *
encode_escaped(int value)632 encode_escaped(int value)
633 {
634 static char result[80];
635 unsigned n;
636 bool found = FALSE;
637 for (n = 0; n < TableSize(escaped_letters); ++n) {
638 if (value == escaped_letters[n].actual) {
639 found = TRUE;
640 sprintf(result, "%c", escaped_letters[n].letter);
641 break;
642 }
643 }
644 if (!found) {
645 sprintf(result, "%03o", value & 0xff);
646 }
647 return result;
648 }
649
650 /*
651 * Parse the parameters of the "bindkey" configuration-file entry. This
652 * expects widget name which may be "*", followed by curses key definition and
653 * then dialog key definition.
654 *
655 * The curses key "should" be one of the names (ignoring case) from
656 * curses_names[], but may also be a single control character (prefix "^" or
657 * "~" depending on whether it is C0 or C1), or an escaped single character.
658 * Binding a printable character with dialog is possible but not useful.
659 *
660 * The dialog key must be one of the names from dialog_names[].
661 */
662 int
dlg_parse_bindkey(char * params)663 dlg_parse_bindkey(char *params)
664 {
665 char *p = skip_white(params);
666 char *q;
667 bool escaped = FALSE;
668 int modified = 0;
669 int result = FALSE;
670 unsigned xx;
671 char *widget;
672 int is_function = FALSE;
673 int curses_key;
674 int dialog_key;
675
676 curses_key = -1;
677 dialog_key = -1;
678 widget = p;
679
680 p = skip_black(p);
681 if (p != widget && *p != '\0') {
682 *p++ = '\0';
683 p = skip_white(p);
684 q = p;
685 while (*p != '\0' && curses_key < 0) {
686 if (escaped) {
687 escaped = FALSE;
688 curses_key = decode_escaped(&p);
689 } else if (*p == CHR_BACKSLASH) {
690 escaped = TRUE;
691 } else if (modified) {
692 if (*p == '?') {
693 curses_key = ((modified == '^')
694 ? 127
695 : 255);
696 } else {
697 curses_key = ((modified == '^')
698 ? (*p & 0x1f)
699 : ((*p & 0x1f) | 0x80));
700 }
701 } else if (*p == '^') {
702 modified = *p;
703 } else if (*p == '~') {
704 modified = *p;
705 } else if (isspace(UCH(*p))) {
706 break;
707 }
708 ++p;
709 }
710 if (!isspace(UCH(*p))) {
711 ;
712 } else {
713 *p++ = '\0';
714 if (curses_key < 0) {
715 char fprefix[2];
716 char check[2];
717 int keynumber;
718 if (sscanf(q, "%[Ff]%d%c", fprefix, &keynumber, check) == 2) {
719 curses_key = KEY_F(keynumber);
720 is_function = TRUE;
721 } else {
722 for (xx = 0; xx < COUNT_CURSES; ++xx) {
723 if (!dlg_strcmp(curses_names[xx].name, q)) {
724 curses_key = curses_names[xx].code;
725 is_function = (curses_key >= KEY_MIN);
726 break;
727 }
728 }
729 }
730 }
731 }
732 q = skip_white(p);
733 p = skip_black(q);
734 if (p != q) {
735 for (xx = 0; xx < COUNT_DIALOG; ++xx) {
736 if (!dlg_strcmp(dialog_names[xx].name, q)) {
737 dialog_key = dialog_names[xx].code;
738 break;
739 }
740 }
741 }
742 if (*widget != '\0'
743 && curses_key >= 0
744 && dialog_key >= 0
745 && make_binding(widget, curses_key, is_function, dialog_key) != 0) {
746 result = TRUE;
747 }
748 }
749 return result;
750 }
751
752 static void
dump_curses_key(FILE * fp,int curses_key)753 dump_curses_key(FILE *fp, int curses_key)
754 {
755 if (curses_key > KEY_MIN) {
756 unsigned n;
757 bool found = FALSE;
758 for (n = 0; n < COUNT_CURSES; ++n) {
759 if (curses_names[n].code == curses_key) {
760 fprintf(fp, "%s", curses_names[n].name);
761 found = TRUE;
762 break;
763 }
764 }
765 if (!found) {
766 #ifdef KEY_MOUSE
767 if (is_DLGK_MOUSE(curses_key)) {
768 fprintf(fp, "MOUSE-");
769 dump_curses_key(fp, curses_key - M_EVENT);
770 } else
771 #endif
772 if (curses_key >= KEY_F(0)) {
773 fprintf(fp, "F%d", curses_key - KEY_F(0));
774 } else {
775 fprintf(fp, "curses%d", curses_key);
776 }
777 }
778 } else if (curses_key >= 0 && curses_key < 32) {
779 fprintf(fp, "^%c", curses_key + 64);
780 } else if (curses_key == 127) {
781 fprintf(fp, "^?");
782 } else if (curses_key >= 128 && curses_key < 160) {
783 fprintf(fp, "~%c", curses_key - 64);
784 } else if (curses_key == 255) {
785 fprintf(fp, "~?");
786 } else if (curses_key > 32 &&
787 curses_key < 127 &&
788 curses_key != CHR_BACKSLASH) {
789 fprintf(fp, "%c", curses_key);
790 } else {
791 fprintf(fp, "%c%s", CHR_BACKSLASH, encode_escaped(curses_key));
792 }
793 }
794
795 static void
dump_dialog_key(FILE * fp,int dialog_key)796 dump_dialog_key(FILE *fp, int dialog_key)
797 {
798 unsigned n;
799 bool found = FALSE;
800 for (n = 0; n < COUNT_DIALOG; ++n) {
801 if (dialog_names[n].code == dialog_key) {
802 fputs(dialog_names[n].name, fp);
803 found = TRUE;
804 break;
805 }
806 }
807 if (!found) {
808 fprintf(fp, "dialog%d", dialog_key);
809 }
810 }
811
812 static void
dump_one_binding(FILE * fp,WINDOW * win,const char * widget,DLG_KEYS_BINDING * binding)813 dump_one_binding(FILE *fp,
814 WINDOW *win,
815 const char *widget,
816 DLG_KEYS_BINDING * binding)
817 {
818 int actual;
819 int fkey = (binding->curses_key > 255);
820
821 fprintf(fp, "bindkey %s ", widget);
822 dump_curses_key(fp, binding->curses_key);
823 fputc(' ', fp);
824 dump_dialog_key(fp, binding->dialog_key);
825 actual = dlg_lookup_key(win, binding->curses_key, &fkey);
826 #ifdef KEY_MOUSE
827 if (is_DLGK_MOUSE(binding->curses_key) && is_DLGK_MOUSE(actual)) {
828 ; /* EMPTY */
829 } else
830 #endif
831 if (actual != binding->dialog_key) {
832 fprintf(fp, "\t# overridden by ");
833 dump_dialog_key(fp, actual);
834 }
835 fputc('\n', fp);
836 }
837
838 /*
839 * Dump bindings for the given window. If it is a null, then this dumps the
840 * initial bindings which were loaded from the rc-file that are used as
841 * overall defaults.
842 */
843 void
dlg_dump_window_keys(FILE * fp,WINDOW * win)844 dlg_dump_window_keys(FILE *fp, WINDOW *win)
845 {
846 if (fp != 0) {
847 LIST_BINDINGS *p;
848 DLG_KEYS_BINDING *q;
849 const char *last = "";
850
851 for (p = all_bindings; p != 0; p = p->link) {
852 if (p->win == win) {
853 if (dlg_strcmp(last, p->name)) {
854 fprintf(fp, "# key bindings for %s widgets%s\n",
855 !strcmp(p->name, WILDNAME) ? "all" : p->name,
856 win == 0 ? " (user-defined)" : "");
857 last = p->name;
858 }
859 for (q = p->binding; q->is_function_key >= 0; ++q) {
860 dump_one_binding(fp, win, p->name, q);
861 }
862 }
863 }
864 }
865 }
866
867 /*
868 * Dump all of the bindings which are not specific to a given widget, i.e.,
869 * the "win" member is null.
870 */
871 void
dlg_dump_keys(FILE * fp)872 dlg_dump_keys(FILE *fp)
873 {
874 if (fp != 0) {
875 LIST_BINDINGS *p;
876 unsigned count = 0;
877
878 for (p = all_bindings; p != 0; p = p->link) {
879 if (p->win == 0) {
880 ++count;
881 }
882 }
883 if (count != 0) {
884 dlg_dump_window_keys(fp, 0);
885 }
886 }
887 }
888 #endif /* HAVE_RC_FILE */
889