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