xref: /dragonfly/contrib/dialog/dlg_keys.c (revision 3851e4b8)
1 /*
2  *  $Id: dlg_keys.c,v 1.35 2014/01/12 18:21:52 tom Exp $
3  *
4  *  dlg_keys.c -- runtime binding support for dialog
5  *
6  *  Copyright 2006-2011,2014 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 ||
234 		(p->win == 0 &&
235 		 (!strcmp(p->name, name) || !strcmp(p->name, WILDNAME)))) {
236 		int function_key = (*fkey != 0);
237 		for (q = p->binding; q->is_function_key >= 0; ++q) {
238 		    if (p->buttons
239 			&& !function_key
240 			&& q->curses_key == (int) dlg_toupper(curses_key)) {
241 			*fkey = 0;
242 			return q->dialog_key;
243 		    }
244 		    if (q->curses_key == curses_key
245 			&& q->is_function_key == function_key) {
246 			*fkey = q->dialog_key;
247 			return *fkey;
248 		    }
249 		}
250 	    }
251 	}
252     }
253     return curses_key;
254 }
255 
256 /*
257  * Test a dialog internal keycode to see if it corresponds to one of the push
258  * buttons on the widget such as "OK".
259  *
260  * This is only useful if there are user-defined key bindings, since there are
261  * no built-in bindings that map directly to DLGK_OK, etc.
262  *
263  * See also dlg_ok_buttoncode().
264  */
265 int
266 dlg_result_key(int dialog_key, int fkey GCC_UNUSED, int *resultp)
267 {
268     int done = FALSE;
269 
270 #ifdef HAVE_RC_FILE
271     if (fkey) {
272 	switch ((DLG_KEYS_ENUM) dialog_key) {
273 	case DLGK_OK:
274 	    *resultp = DLG_EXIT_OK;
275 	    done = TRUE;
276 	    break;
277 	case DLGK_CANCEL:
278 	    if (!dialog_vars.nocancel) {
279 		*resultp = DLG_EXIT_CANCEL;
280 		done = TRUE;
281 	    }
282 	    break;
283 	case DLGK_EXTRA:
284 	    if (dialog_vars.extra_button) {
285 		*resultp = DLG_EXIT_EXTRA;
286 		done = TRUE;
287 	    }
288 	    break;
289 	case DLGK_HELP:
290 	    if (dialog_vars.help_button) {
291 		*resultp = DLG_EXIT_HELP;
292 		done = TRUE;
293 	    }
294 	    break;
295 	case DLGK_ESC:
296 	    *resultp = DLG_EXIT_ESC;
297 	    done = TRUE;
298 	    break;
299 	default:
300 	    break;
301 	}
302     } else
303 #endif
304     if (dialog_key == ESC) {
305 	*resultp = DLG_EXIT_ESC;
306 	done = TRUE;
307     } else if (dialog_key == ERR) {
308 	*resultp = DLG_EXIT_ERROR;
309 	done = TRUE;
310     }
311 
312     return done;
313 }
314 
315 #ifdef HAVE_RC_FILE
316 typedef struct {
317     const char *name;
318     int code;
319 } CODENAME;
320 
321 #define ASCII_NAME(name,code)  { #name, code }
322 #define CURSES_NAME(upper) { #upper, KEY_ ## upper }
323 #define COUNT_CURSES  sizeof(curses_names)/sizeof(curses_names[0])
324 static const CODENAME curses_names[] =
325 {
326     ASCII_NAME(ESC, '\033'),
327     ASCII_NAME(CR, '\r'),
328     ASCII_NAME(LF, '\n'),
329     ASCII_NAME(FF, '\f'),
330     ASCII_NAME(TAB, '\t'),
331     ASCII_NAME(DEL, '\177'),
332 
333     CURSES_NAME(DOWN),
334     CURSES_NAME(UP),
335     CURSES_NAME(LEFT),
336     CURSES_NAME(RIGHT),
337     CURSES_NAME(HOME),
338     CURSES_NAME(BACKSPACE),
339     CURSES_NAME(F0),
340     CURSES_NAME(DL),
341     CURSES_NAME(IL),
342     CURSES_NAME(DC),
343     CURSES_NAME(IC),
344     CURSES_NAME(EIC),
345     CURSES_NAME(CLEAR),
346     CURSES_NAME(EOS),
347     CURSES_NAME(EOL),
348     CURSES_NAME(SF),
349     CURSES_NAME(SR),
350     CURSES_NAME(NPAGE),
351     CURSES_NAME(PPAGE),
352     CURSES_NAME(STAB),
353     CURSES_NAME(CTAB),
354     CURSES_NAME(CATAB),
355     CURSES_NAME(ENTER),
356     CURSES_NAME(PRINT),
357     CURSES_NAME(LL),
358     CURSES_NAME(A1),
359     CURSES_NAME(A3),
360     CURSES_NAME(B2),
361     CURSES_NAME(C1),
362     CURSES_NAME(C3),
363     CURSES_NAME(BTAB),
364     CURSES_NAME(BEG),
365     CURSES_NAME(CANCEL),
366     CURSES_NAME(CLOSE),
367     CURSES_NAME(COMMAND),
368     CURSES_NAME(COPY),
369     CURSES_NAME(CREATE),
370     CURSES_NAME(END),
371     CURSES_NAME(EXIT),
372     CURSES_NAME(FIND),
373     CURSES_NAME(HELP),
374     CURSES_NAME(MARK),
375     CURSES_NAME(MESSAGE),
376     CURSES_NAME(MOVE),
377     CURSES_NAME(NEXT),
378     CURSES_NAME(OPEN),
379     CURSES_NAME(OPTIONS),
380     CURSES_NAME(PREVIOUS),
381     CURSES_NAME(REDO),
382     CURSES_NAME(REFERENCE),
383     CURSES_NAME(REFRESH),
384     CURSES_NAME(REPLACE),
385     CURSES_NAME(RESTART),
386     CURSES_NAME(RESUME),
387     CURSES_NAME(SAVE),
388     CURSES_NAME(SBEG),
389     CURSES_NAME(SCANCEL),
390     CURSES_NAME(SCOMMAND),
391     CURSES_NAME(SCOPY),
392     CURSES_NAME(SCREATE),
393     CURSES_NAME(SDC),
394     CURSES_NAME(SDL),
395     CURSES_NAME(SELECT),
396     CURSES_NAME(SEND),
397     CURSES_NAME(SEOL),
398     CURSES_NAME(SEXIT),
399     CURSES_NAME(SFIND),
400     CURSES_NAME(SHELP),
401     CURSES_NAME(SHOME),
402     CURSES_NAME(SIC),
403     CURSES_NAME(SLEFT),
404     CURSES_NAME(SMESSAGE),
405     CURSES_NAME(SMOVE),
406     CURSES_NAME(SNEXT),
407     CURSES_NAME(SOPTIONS),
408     CURSES_NAME(SPREVIOUS),
409     CURSES_NAME(SPRINT),
410     CURSES_NAME(SREDO),
411     CURSES_NAME(SREPLACE),
412     CURSES_NAME(SRIGHT),
413     CURSES_NAME(SRSUME),
414     CURSES_NAME(SSAVE),
415     CURSES_NAME(SSUSPEND),
416     CURSES_NAME(SUNDO),
417     CURSES_NAME(SUSPEND),
418     CURSES_NAME(UNDO),
419 };
420 
421 #define DIALOG_NAME(upper) { #upper, DLGK_ ## upper }
422 #define COUNT_DIALOG  sizeof(dialog_names)/sizeof(dialog_names[0])
423 static const CODENAME dialog_names[] =
424 {
425     DIALOG_NAME(OK),
426     DIALOG_NAME(CANCEL),
427     DIALOG_NAME(EXTRA),
428     DIALOG_NAME(HELP),
429     DIALOG_NAME(ESC),
430     DIALOG_NAME(PAGE_FIRST),
431     DIALOG_NAME(PAGE_LAST),
432     DIALOG_NAME(PAGE_NEXT),
433     DIALOG_NAME(PAGE_PREV),
434     DIALOG_NAME(ITEM_FIRST),
435     DIALOG_NAME(ITEM_LAST),
436     DIALOG_NAME(ITEM_NEXT),
437     DIALOG_NAME(ITEM_PREV),
438     DIALOG_NAME(FIELD_FIRST),
439     DIALOG_NAME(FIELD_LAST),
440     DIALOG_NAME(FIELD_NEXT),
441     DIALOG_NAME(FIELD_PREV),
442     DIALOG_NAME(FORM_FIRST),
443     DIALOG_NAME(FORM_LAST),
444     DIALOG_NAME(FORM_NEXT),
445     DIALOG_NAME(FORM_PREV),
446     DIALOG_NAME(GRID_UP),
447     DIALOG_NAME(GRID_DOWN),
448     DIALOG_NAME(GRID_LEFT),
449     DIALOG_NAME(GRID_RIGHT),
450     DIALOG_NAME(DELETE_LEFT),
451     DIALOG_NAME(DELETE_RIGHT),
452     DIALOG_NAME(DELETE_ALL),
453     DIALOG_NAME(ENTER),
454     DIALOG_NAME(BEGIN),
455     DIALOG_NAME(FINAL),
456     DIALOG_NAME(SELECT),
457     DIALOG_NAME(HELPFILE),
458     DIALOG_NAME(TRACE)
459 };
460 
461 static char *
462 skip_white(char *s)
463 {
464     while (*s != '\0' && isspace(UCH(*s)))
465 	++s;
466     return s;
467 }
468 
469 static char *
470 skip_black(char *s)
471 {
472     while (*s != '\0' && !isspace(UCH(*s)))
473 	++s;
474     return s;
475 }
476 
477 /*
478  * Find a user-defined binding, given the curses key code.
479  */
480 static DLG_KEYS_BINDING *
481 find_binding(char *widget, int curses_key)
482 {
483     LIST_BINDINGS *p;
484     DLG_KEYS_BINDING *result = 0;
485 
486     for (p = all_bindings; p != 0; p = p->link) {
487 	if (p->win == 0
488 	    && !dlg_strcmp(p->name, widget)
489 	    && p->binding->curses_key == curses_key) {
490 	    result = p->binding;
491 	    break;
492 	}
493     }
494     return result;
495 }
496 
497 /*
498  * Built-in bindings have a nonzero "win" member, and the associated binding
499  * table can have more than one entry.  We keep those last, since lookups will
500  * find the user-defined bindings first and use those.
501  *
502  * Sort "*" (all-widgets) entries past named widgets, since those are less
503  * specific.
504  */
505 static int
506 compare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b)
507 {
508     int result = 0;
509     if (a->win == b->win) {
510 	if (!strcmp(a->name, b->name)) {
511 	    result = a->binding[0].curses_key - b->binding[0].curses_key;
512 	} else if (!strcmp(b->name, WILDNAME)) {
513 	    result = -1;
514 	} else if (!strcmp(a->name, WILDNAME)) {
515 	    result = 1;
516 	} else {
517 	    result = dlg_strcmp(a->name, b->name);
518 	}
519     } else if (b->win) {
520 	result = -1;
521     } else {
522 	result = 1;
523     }
524     return result;
525 }
526 
527 /*
528  * Find a user-defined binding, given the curses key code.  If it does not
529  * exist, create a new one, inserting it into the linked list, keeping it
530  * sorted to simplify lookups for user-defined bindings that can override
531  * the built-in bindings.
532  */
533 static DLG_KEYS_BINDING *
534 make_binding(char *widget, int curses_key, int is_function, int dialog_key)
535 {
536     LIST_BINDINGS *entry = 0;
537     DLG_KEYS_BINDING *data = 0;
538     char *name;
539     LIST_BINDINGS *p, *q;
540     DLG_KEYS_BINDING *result = find_binding(widget, curses_key);
541 
542     if (result == 0
543 	&& (entry = dlg_calloc(LIST_BINDINGS, 1)) != 0
544 	&& (data = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0
545 	&& (name = dlg_strclone(widget)) != 0) {
546 
547 	entry->name = name;
548 	entry->binding = data;
549 
550 	data[0].is_function_key = is_function;
551 	data[0].curses_key = curses_key;
552 	data[0].dialog_key = dialog_key;
553 
554 	data[1] = end_keys_binding;
555 
556 	for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
557 	    if (compare_bindings(entry, p) < 0) {
558 		break;
559 	    }
560 	}
561 	if (q != 0) {
562 	    q->link = entry;
563 	} else {
564 	    all_bindings = entry;
565 	}
566 	if (p != 0) {
567 	    entry->link = p;
568 	}
569 	result = data;
570     } else if (entry != 0) {
571 	free(entry);
572 	if (data)
573 	    free(data);
574     }
575 
576     return result;
577 }
578 
579 /*
580  * Parse the parameters of the "bindkeys" configuration-file entry.  This
581  * expects widget name which may be "*", followed by curses key definition and
582  * then dialog key definition.
583  *
584  * The curses key "should" be one of the names (ignoring case) from
585  * curses_names[], but may also be a single control character (prefix "^" or
586  * "~" depending on whether it is C0 or C1), or an escaped single character.
587  * Binding a printable character with dialog is possible but not useful.
588  *
589  * The dialog key must be one of the names from dialog_names[].
590  */
591 int
592 dlg_parse_bindkey(char *params)
593 {
594     char *p = skip_white(params);
595     char *q;
596     bool escaped = FALSE;
597     int modified = 0;
598     int result = FALSE;
599     unsigned xx;
600     char *widget;
601     int is_function = FALSE;
602     int curses_key;
603     int dialog_key;
604 
605     curses_key = -1;
606     dialog_key = -1;
607     widget = p;
608 
609     p = skip_black(p);
610     if (p != widget && *p != '\0') {
611 	*p++ = '\0';
612 	p = skip_white(p);
613 	q = p;
614 	while (*p != '\0' && curses_key < 0) {
615 	    if (escaped) {
616 		escaped = FALSE;
617 		curses_key = *p;
618 	    } else if (*p == '\\') {
619 		escaped = TRUE;
620 	    } else if (modified) {
621 		if (*p == '?') {
622 		    curses_key = ((modified == '^')
623 				  ? 127
624 				  : 255);
625 		} else {
626 		    curses_key = ((modified == '^')
627 				  ? (*p & 0x1f)
628 				  : ((*p & 0x1f) | 0x80));
629 		}
630 	    } else if (*p == '^') {
631 		modified = *p;
632 	    } else if (*p == '~') {
633 		modified = *p;
634 	    } else if (isspace(UCH(*p))) {
635 		break;
636 	    }
637 	    ++p;
638 	}
639 	if (!isspace(UCH(*p))) {
640 	    ;
641 	} else {
642 	    *p++ = '\0';
643 	    if (curses_key < 0) {
644 		char fprefix[2];
645 		char check[2];
646 		int keynumber;
647 		if (sscanf(q, "%[Ff]%d%c", fprefix, &keynumber, check) == 2) {
648 		    curses_key = KEY_F(keynumber);
649 		    is_function = TRUE;
650 		} else {
651 		    for (xx = 0; xx < COUNT_CURSES; ++xx) {
652 			if (!dlg_strcmp(curses_names[xx].name, q)) {
653 			    curses_key = curses_names[xx].code;
654 			    is_function = (curses_key >= KEY_MIN);
655 			    break;
656 			}
657 		    }
658 		}
659 	    }
660 	}
661 	q = skip_white(p);
662 	p = skip_black(q);
663 	if (p != q) {
664 	    for (xx = 0; xx < COUNT_DIALOG; ++xx) {
665 		if (!dlg_strcmp(dialog_names[xx].name, q)) {
666 		    dialog_key = dialog_names[xx].code;
667 		    break;
668 		}
669 	    }
670 	}
671 	if (*widget != '\0'
672 	    && curses_key >= 0
673 	    && dialog_key >= 0
674 	    && make_binding(widget, curses_key, is_function, dialog_key) != 0) {
675 	    result = TRUE;
676 	}
677     }
678     return result;
679 }
680 
681 static void
682 dump_curses_key(FILE *fp, int curses_key)
683 {
684     if (curses_key > KEY_MIN) {
685 	unsigned n;
686 	bool found = FALSE;
687 	for (n = 0; n < COUNT_CURSES; ++n) {
688 	    if (curses_names[n].code == curses_key) {
689 		fprintf(fp, "%s", curses_names[n].name);
690 		found = TRUE;
691 		break;
692 	    }
693 	}
694 	if (!found) {
695 	    if (curses_key >= KEY_F(0)) {
696 		fprintf(fp, "F%d", curses_key - KEY_F(0));
697 	    } else {
698 		fprintf(fp, "curses%d", curses_key);
699 	    }
700 	}
701     } else if (curses_key >= 0 && curses_key < 32) {
702 	fprintf(fp, "^%c", curses_key + 64);
703     } else if (curses_key == 127) {
704 	fprintf(fp, "^?");
705     } else if (curses_key >= 128 && curses_key < 160) {
706 	fprintf(fp, "~%c", curses_key - 64);
707     } else if (curses_key == 255) {
708 	fprintf(fp, "~?");
709     } else {
710 	fprintf(fp, "\\%c", curses_key);
711     }
712 }
713 
714 static void
715 dump_dialog_key(FILE *fp, int dialog_key)
716 {
717     unsigned n;
718     bool found = FALSE;
719     for (n = 0; n < COUNT_DIALOG; ++n) {
720 	if (dialog_names[n].code == dialog_key) {
721 	    fputs(dialog_names[n].name, fp);
722 	    found = TRUE;
723 	    break;
724 	}
725     }
726     if (!found) {
727 	fprintf(fp, "dialog%d", dialog_key);
728     }
729 }
730 
731 static void
732 dump_one_binding(FILE *fp, const char *widget, DLG_KEYS_BINDING * binding)
733 {
734     fprintf(fp, "bindkey %s ", widget);
735     dump_curses_key(fp, binding->curses_key);
736     fputc(' ', fp);
737     dump_dialog_key(fp, binding->dialog_key);
738     fputc('\n', fp);
739 }
740 
741 /*
742  * Dump bindings for the given window.  If it is a null, then this dumps the
743  * initial bindings which were loaded from the rc-file that are used as
744  * overall defaults.
745  */
746 void
747 dlg_dump_window_keys(FILE *fp, WINDOW *win)
748 {
749     if (fp != 0) {
750 	LIST_BINDINGS *p;
751 	DLG_KEYS_BINDING *q;
752 	const char *last = "";
753 
754 	for (p = all_bindings; p != 0; p = p->link) {
755 	    if (p->win == win) {
756 		if (dlg_strcmp(last, p->name)) {
757 		    fprintf(fp, "\n# key bindings for %s widgets\n",
758 			    !strcmp(p->name, WILDNAME) ? "all" : p->name);
759 		    last = p->name;
760 		}
761 		for (q = p->binding; q->is_function_key >= 0; ++q) {
762 		    dump_one_binding(fp, p->name, q);
763 		}
764 	    }
765 	}
766     }
767 }
768 
769 /*
770  * Dump all of the bindings which are not specific to a given widget, i.e.,
771  * the "win" member is null.
772  */
773 void
774 dlg_dump_keys(FILE *fp)
775 {
776     if (fp != 0) {
777 	LIST_BINDINGS *p;
778 	unsigned count = 0;
779 
780 	for (p = all_bindings; p != 0; p = p->link) {
781 	    if (p->win == 0) {
782 		++count;
783 	    }
784 	}
785 	if (count != 0) {
786 	    dlg_dump_window_keys(fp, 0);
787 	}
788     }
789 }
790 #endif /* HAVE_RC_FILE */
791