]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/dialog/dlg_keys.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / contrib / dialog / dlg_keys.c
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
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
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
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
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
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
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 *
489 skip_white(char *s)
490 {
491     while (*s != '\0' && isspace(UCH(*s)))
492         ++s;
493     return s;
494 }
495
496 static char *
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 *
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
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 *
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
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 *
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
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
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
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
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
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
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 */