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