Upgrade Texinfo from 4.8 to 4.13 on the vendor branch
[dragonfly.git] / contrib / texinfo / info / infomap.c
1 /* infomap.c -- keymaps for Info.
2    $Id: infomap.c,v 1.21 2008/06/11 09:55:42 gray Exp $
3
4    Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2007, 2008
5    Free Software Foundation, Inc.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20    Written by Brian Fox (bfox@ai.mit.edu). */
21
22 #include "info.h"
23 #include "infomap.h"
24 #include "funs.h"
25 #include "terminal.h"
26
27 #if defined(INFOKEY)
28 #include "infokey.h"
29 #include "variables.h"
30 #endif /* INFOKEY */
31
32 static int keymap_bind_keyseq (Keymap map, const char *keyseq,
33     KEYMAP_ENTRY *keyentry);
34
35 /* Return a new keymap which has all the uppercase letters mapped to run
36    the function info_do_lowercase_version (). */
37 Keymap
38 keymap_make_keymap (void)
39 {
40   int i;
41   Keymap keymap;
42
43   keymap = (Keymap)xmalloc (256 * sizeof (KEYMAP_ENTRY));
44
45   for (i = 0; i < 256; i++)
46     {
47       keymap[i].type = ISFUNC;
48       keymap[i].function = NULL;
49     }
50
51   for (i = 'A'; i < ('Z' + 1); i++)
52     {
53       keymap[i].type = ISFUNC;
54 #if defined(INFOKEY)
55       keymap[Meta(i)].type = ISFUNC;
56       keymap[Meta(i)].function =
57 #endif /* INFOKEY */
58       keymap[i].function = InfoCmd(info_do_lowercase_version);
59     }
60
61   return keymap;
62 }
63
64 #if defined(INFOKEY)
65 static FUNCTION_KEYSEQ *
66 find_function_keyseq (Keymap map, int c, Keymap rootmap)
67 {
68   FUNCTION_KEYSEQ *k;
69
70   if (map[c].type != ISFUNC)
71     abort();
72   if (map[c].function == NULL)
73     return NULL;
74   for (k = map[c].function->keys; k; k = k->next)
75     {
76       const unsigned char *p;
77       Keymap m = rootmap;
78       if (k->map != rootmap)
79         continue;
80       for (p = (unsigned char *) k->keyseq; *p && m[*p].type == ISKMAP; p++)
81         m = (Keymap)m[*p].function;
82       if (*p != c || p[1])
83         continue;
84       if (m[*p].type != ISFUNC)
85         abort ();
86       break;
87     }
88   return k;
89 }
90
91 static void
92 add_function_keyseq (InfoCommand *function,
93     const char *keyseq, Keymap rootmap)
94 {
95   FUNCTION_KEYSEQ *ks;
96
97   if (function == NULL ||
98       function == InfoCmd(info_do_lowercase_version) ||
99       function == InfoCmd(ea_insert))
100     return;
101   ks = xmalloc (sizeof(FUNCTION_KEYSEQ));
102   ks->next = function->keys;
103   ks->map = rootmap;
104   ks->keyseq = xstrdup(keyseq);
105   function->keys = ks;
106 }
107
108 static void
109 remove_function_keyseq (InfoCommand *function,
110     const char *keyseq, Keymap rootmap)
111 {
112
113   FUNCTION_KEYSEQ *k, *kp;
114
115   if (function == NULL ||
116       function == InfoCmd(info_do_lowercase_version) ||
117       function == InfoCmd(ea_insert))
118     return;
119   for (kp = NULL, k = function->keys; k; kp = k, k = k->next)
120     if (k->map == rootmap && strcmp(k->keyseq, keyseq) == 0)
121       break;
122   if (!k)
123     abort ();
124   if (kp)
125     kp->next = k->next;
126   else
127     function->keys = k->next;
128 }
129 #endif /* INFOKEY */
130
131 /* Return a new keymap which is a copy of MAP. */
132 Keymap
133 keymap_copy_keymap (Keymap map, Keymap rootmap, Keymap newroot)
134 {
135   int i;
136   Keymap keymap;
137 #if defined(INFOKEY)
138   FUNCTION_KEYSEQ *ks;
139 #endif /* INFOKEY */
140
141   keymap = keymap_make_keymap ();
142   if (!newroot)
143     newroot = keymap;
144
145   for (i = 0; i < 256; i++)
146     {
147       keymap[i].type = map[i].type;
148       switch (map[i].type)
149         {
150         case ISFUNC:
151           keymap[i].function = map[i].function;
152 #if defined(INFOKEY)
153           ks = find_function_keyseq (map, i, rootmap);
154           if (ks)
155             add_function_keyseq(map[i].function, ks->keyseq, newroot);
156 #endif /* INFOKEY */
157           break;
158         case ISKMAP:
159           keymap[i].function = (InfoCommand *)keymap_copy_keymap
160             ((Keymap)map[i].function, rootmap, NULL);
161           break;
162         }
163     }
164   return keymap;
165 }
166
167 /* Free the keymap and its descendants. */
168 void
169 keymap_discard_keymap (Keymap map, Keymap rootmap)
170 {
171   int i;
172
173   if (!map)
174     return;
175   if (!rootmap)
176     rootmap = map;
177
178   for (i = 0; i < 256; i++)
179     {
180 #if defined(INFOKEY)
181       FUNCTION_KEYSEQ *ks;
182 #endif /* INFOKEY */
183       switch (map[i].type)
184         {
185         case ISFUNC:
186 #if defined(INFOKEY)
187           ks = find_function_keyseq(map, i, rootmap);
188           if (ks)
189             remove_function_keyseq (map[i].function, ks->keyseq, rootmap);
190 #endif /* INFOKEY */
191           break;
192
193         case ISKMAP:
194           keymap_discard_keymap ((Keymap)map[i].function, rootmap);
195           break;
196
197         }
198     }
199   free(map);
200 }
201
202 /* Conditionally bind key sequence. */
203 static int
204 keymap_bind_keyseq (Keymap map,
205     const char *keyseq, KEYMAP_ENTRY *keyentry)
206 {
207   Keymap m = map;
208   const unsigned char *s = (unsigned char *) keyseq;
209   int c;
210
211   if (s == NULL || *s == '\0') return 0;
212
213   while ((c = *s++) != '\0')
214     {
215 #if defined(INFOKEY)
216       FUNCTION_KEYSEQ *ks;
217 #endif /* INFOKEY */
218       switch (m[c].type)
219         {
220         case ISFUNC:
221 #if defined(INFOKEY)
222           ks = find_function_keyseq(m, c, map);
223           if (ks)
224             remove_function_keyseq (m[c].function, ks->keyseq, map);
225 #else /* !INFOKEY */
226           if (!(m[c].function == NULL || (
227                 m != map &&
228                 m[c].function == InfoCmd(info_do_lowercase_version))
229               ))
230             return 0;
231 #endif /* !INFOKEY */
232
233           if (*s != '\0')
234             {
235               m[c].type = ISKMAP;
236               /* Here we are casting the Keymap pointer returned from
237                  keymap_make_keymap to an InfoCommand pointer.  Ugh.
238                  This makes the `function' structure garbage
239                  if it's actually interpreted as an InfoCommand.
240                  Should really be using a union, and taking steps to
241                  avoid the possible error.  */
242               m[c].function = (InfoCommand *)keymap_make_keymap ();
243             }
244           break;
245
246         case ISKMAP:
247 #if defined(INFOKEY)
248           if (*s == '\0')
249             keymap_discard_keymap ((Keymap)m[c].function, map);
250 #else /* !INFOKEY */
251           if (*s == '\0')
252             return 0;
253 #endif
254           break;
255         }
256       if (*s != '\0')
257         {
258           m = (Keymap)m[c].function;
259         }
260       else
261         {
262 #if defined(INFOKEY)
263           add_function_keyseq (keyentry->function, keyseq, map);
264 #endif /* INFOKEY */
265           m[c] = *keyentry;
266         }
267     }
268
269   return 1;
270 }
271
272 \f
273 /* Initialize the standard info keymaps. */
274
275 Keymap info_keymap = NULL;
276 Keymap echo_area_keymap = NULL;
277
278 /* Make sure that we don't have too many command codes defined. */
279
280 #if A_NCOMMANDS > A_MAX_COMMAND + 1
281 #error "too many commands defined"
282 #endif
283
284 /* Initialize the keymaps from the .info keymap file. */
285
286 #define NUL     '\0'
287
288 static unsigned char default_emacs_like_info_keys[] =
289 {
290         0,      /* suppress-default-keybindings flag */
291         TAB, NUL,                       A_info_move_to_next_xref,
292         LFD, NUL,                       A_info_select_reference_this_line,
293         RET, NUL,                       A_info_select_reference_this_line,
294         CONTROL('a'), NUL,              A_info_beginning_of_line,
295         CONTROL('b'), NUL,              A_info_backward_char,
296         CONTROL('e'), NUL,              A_info_end_of_line,
297         CONTROL('f'), NUL,              A_info_forward_char,
298         CONTROL('h'), NUL,              A_info_get_help_window,
299         CONTROL('l'), NUL,              A_info_redraw_display,
300         CONTROL('n'), NUL,              A_info_next_line,
301         CONTROL('p'), NUL,              A_info_prev_line,
302         CONTROL('r'), NUL,              A_isearch_backward,
303         CONTROL('s'), NUL,              A_isearch_forward,
304         CONTROL('u'), NUL,              A_info_universal_argument,
305         CONTROL('v'), NUL,              A_info_scroll_forward_page_only,
306         ',', NUL,                       A_info_next_index_match,
307         '/', NUL,                       A_info_search,
308         '0', NUL,                       A_info_last_menu_item,
309         '1', NUL,                       A_info_menu_digit,
310         '2', NUL,                       A_info_menu_digit,
311         '3', NUL,                       A_info_menu_digit,
312         '4', NUL,                       A_info_menu_digit,
313         '5', NUL,                       A_info_menu_digit,
314         '6', NUL,                       A_info_menu_digit,
315         '7', NUL,                       A_info_menu_digit,
316         '8', NUL,                       A_info_menu_digit,
317         '9', NUL,                       A_info_menu_digit,
318         '<', NUL,                       A_info_first_node,
319         '>', NUL,                       A_info_last_node,
320         '?', NUL,                       A_info_get_help_window,
321         '[', NUL,                       A_info_global_prev_node,
322         ']', NUL,                       A_info_global_next_node,
323         'b', NUL,                       A_info_beginning_of_node,
324         'd', NUL,                       A_info_dir_node,
325         'e', NUL,                       A_info_end_of_node,
326         'f', NUL,                       A_info_xref_item,
327         'g', NUL,                       A_info_goto_node,
328         'G', NUL,                       A_info_menu_sequence,
329         'h', NUL,                       A_info_get_help_window,
330         'H', NUL,                       A_info_get_info_help_node,
331         'i', NUL,                       A_info_index_search,
332         'l', NUL,                       A_info_history_node,
333         'm', NUL,                       A_info_menu_item,
334         'n', NUL,                       A_info_next_node,
335         'O', NUL,                       A_info_goto_invocation_node,
336         'p', NUL,                       A_info_prev_node,
337         'r', NUL,                       A_info_xref_item,
338         'R', NUL,                       A_info_toggle_regexp,
339         's', NUL,                       A_info_search,
340         'S', NUL,                       A_info_search_case_sensitively,
341         't', NUL,                       A_info_top_node,
342         'u', NUL,                       A_info_up_node,
343         ESC, '0', NUL,                  A_info_add_digit_to_numeric_arg,
344         ESC, '1', NUL,                  A_info_add_digit_to_numeric_arg,
345         ESC, '2', NUL,                  A_info_add_digit_to_numeric_arg,
346         ESC, '3', NUL,                  A_info_add_digit_to_numeric_arg,
347         ESC, '4', NUL,                  A_info_add_digit_to_numeric_arg,
348         ESC, '5', NUL,                  A_info_add_digit_to_numeric_arg,
349         ESC, '6', NUL,                  A_info_add_digit_to_numeric_arg,
350         ESC, '7', NUL,                  A_info_add_digit_to_numeric_arg,
351         ESC, '8', NUL,                  A_info_add_digit_to_numeric_arg,
352         ESC, '9', NUL,                  A_info_add_digit_to_numeric_arg,
353         ESC, '-', NUL,                  A_info_add_digit_to_numeric_arg,
354         ESC, CONTROL('f'), NUL,         A_info_show_footnotes,
355         ESC, CONTROL('g'), NUL,         A_info_abort_key,
356         ESC, TAB, NUL,                  A_info_move_to_prev_xref,
357         ESC, CONTROL('v'), NUL,         A_info_scroll_other_window,
358         ESC, '<', NUL,                  A_info_beginning_of_node,
359         ESC, '>', NUL,                  A_info_end_of_node,
360         ESC, 'b', NUL,                  A_info_backward_word,
361         ESC, 'f', NUL,                  A_info_forward_word,
362         ESC, 'r', NUL,                  A_info_move_to_window_line,
363         ESC, 'v', NUL,                  A_info_scroll_backward_page_only,
364         Meta('0'), NUL,                 A_info_add_digit_to_numeric_arg,
365         Meta('1'), NUL,                 A_info_add_digit_to_numeric_arg,
366         Meta('2'), NUL,                 A_info_add_digit_to_numeric_arg,
367         Meta('3'), NUL,                 A_info_add_digit_to_numeric_arg,
368         Meta('4'), NUL,                 A_info_add_digit_to_numeric_arg,
369         Meta('5'), NUL,                 A_info_add_digit_to_numeric_arg,
370         Meta('6'), NUL,                 A_info_add_digit_to_numeric_arg,
371         Meta('7'), NUL,                 A_info_add_digit_to_numeric_arg,
372         Meta('8'), NUL,                 A_info_add_digit_to_numeric_arg,
373         Meta('9'), NUL,                 A_info_add_digit_to_numeric_arg,
374         Meta('-'), NUL,                 A_info_add_digit_to_numeric_arg,
375         Meta(CONTROL('f')), NUL,        A_info_show_footnotes,
376         Meta(CONTROL('g')), NUL,        A_info_abort_key,
377         Meta(TAB), NUL,                 A_info_move_to_prev_xref,
378         Meta(CONTROL('v')), NUL,        A_info_scroll_other_window,
379         Meta('<'), NUL,                 A_info_beginning_of_node,
380         Meta('>'), NUL,                 A_info_end_of_node,
381         Meta('b'), NUL,                 A_info_backward_word,
382         Meta('f'), NUL,                 A_info_forward_word,
383         Meta('r'), NUL,                 A_info_move_to_window_line,
384         Meta('v'), NUL,                 A_info_scroll_backward_page_only,
385 #if defined (NAMED_FUNCTIONS)
386         ESC, 'x', NUL,                  A_info_execute_command,
387         Meta('x'), NUL,                 A_info_execute_command,
388 #endif /* NAMED_FUNCTIONS */
389
390         CONTROL('x'), CONTROL('b'), NUL,        A_list_visited_nodes,
391         CONTROL('x'), CONTROL('c'), NUL,        A_info_quit,
392         CONTROL('x'), CONTROL('f'), NUL,        A_info_view_file,
393         CONTROL('x'), CONTROL('g'), NUL,        A_info_abort_key,
394         CONTROL('x'), CONTROL('v'), NUL,        A_info_view_file,
395         CONTROL('x'), '0', NUL,         A_info_delete_window,
396         CONTROL('x'), '1', NUL,         A_info_keep_one_window,
397         CONTROL('x'), '2', NUL,         A_info_split_window,
398         CONTROL('x'), '^', NUL,         A_info_grow_window,
399         CONTROL('x'), 'b', NUL,         A_select_visited_node,
400         CONTROL('x'), 'k', NUL,         A_info_kill_node,
401         CONTROL('x'), 'n', NUL,         A_info_search_next,
402         CONTROL('x'), 'N', NUL,         A_info_search_previous,
403         CONTROL('x'), 'o', NUL,         A_info_next_window,
404         CONTROL('x'), 't', NUL,         A_info_tile_windows,
405         CONTROL('x'), 'w', NUL,         A_info_toggle_wrap,
406
407 /*      Arrow key bindings for info keymaps.  It seems that some
408         terminals do not match their termcap entries, so it's best to just
409         define everything with both of the usual prefixes.  */
410
411         SK_ESCAPE, SK_PAGE_UP, NUL,             A_info_scroll_backward,
412         SK_ESCAPE, SK_PAGE_DOWN, NUL,           A_info_scroll_forward,
413         '\033', 'O', 'A', NUL,                  A_info_prev_line,
414         '\033', '[', 'A', NUL,                  A_info_prev_line,
415         '\033', 'O', 'B', NUL,                  A_info_next_line,
416         '\033', '[', 'B', NUL,                  A_info_next_line,
417         SK_ESCAPE, SK_RIGHT_ARROW, NUL,         A_info_forward_char,
418         '\033', 'O', 'C', NUL,                  A_info_forward_char,
419         '\033', '[', 'C', NUL,                  A_info_forward_char,
420         SK_ESCAPE, SK_LEFT_ARROW, NUL,          A_info_backward_char,
421         '\033', 'O', 'D', NUL,                  A_info_backward_char,
422         '\033', '[', 'D', NUL,                  A_info_backward_char,
423         SK_ESCAPE, SK_HOME, NUL,                A_info_beginning_of_node,
424         SK_ESCAPE, SK_END, NUL,                 A_info_end_of_node,
425         SK_ESCAPE, SK_DELETE, NUL,              A_info_scroll_backward,
426
427         ESC, SK_ESCAPE, SK_PAGE_UP, NUL,        A_info_scroll_other_window_backward,
428         ESC, SK_ESCAPE, SK_PAGE_DOWN, NUL,      A_info_scroll_other_window,
429         ESC, SK_ESCAPE, SK_UP_ARROW, NUL,       A_info_prev_line,
430         ESC, '\033', 'O', 'A', NUL,             A_info_prev_line,
431         ESC, '\033', '[', 'A', NUL,             A_info_prev_line,
432         ESC, SK_ESCAPE, SK_DOWN_ARROW, NUL,     A_info_next_line,
433         ESC, '\033', 'O', 'B', NUL,             A_info_next_line,
434         ESC, '\033', '[', 'B', NUL,             A_info_next_line,
435         ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL,    A_info_forward_word,
436         ESC, '\033', 'O', 'C', NUL,             A_info_forward_word,
437         ESC, '\033', '[', 'C', NUL,             A_info_forward_word,
438         ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL,     A_info_backward_word,
439         ESC, '\033', 'O', 'D', NUL,             A_info_backward_word,
440         ESC, '\033', '[', 'D', NUL,             A_info_backward_word,
441
442         /* We want help to report q, not C-x C-c, etc.  */
443         'q', NUL,                       A_info_quit,
444         'x', NUL,                       A_info_delete_window,
445         SPC, NUL,                       A_info_scroll_forward,
446         DEL, NUL,                       A_info_scroll_backward,
447         '{', NUL,                       A_info_search_previous,
448         '}', NUL,                       A_info_search_next,
449         CONTROL('g'), NUL,              A_info_abort_key,
450         SK_ESCAPE, SK_UP_ARROW, NUL,    A_info_prev_line,
451         SK_ESCAPE, SK_DOWN_ARROW, NUL,  A_info_next_line,
452 };
453
454 \f
455 static unsigned char default_emacs_like_ea_keys[] =
456 {
457         0,      /* suppress-default-keybindings flag */
458         ESC, '0', NUL,                  A_info_add_digit_to_numeric_arg,
459         ESC, '1', NUL,                  A_info_add_digit_to_numeric_arg,
460         ESC, '2', NUL,                  A_info_add_digit_to_numeric_arg,
461         ESC, '3', NUL,                  A_info_add_digit_to_numeric_arg,
462         ESC, '4', NUL,                  A_info_add_digit_to_numeric_arg,
463         ESC, '5', NUL,                  A_info_add_digit_to_numeric_arg,
464         ESC, '6', NUL,                  A_info_add_digit_to_numeric_arg,
465         ESC, '7', NUL,                  A_info_add_digit_to_numeric_arg,
466         ESC, '8', NUL,                  A_info_add_digit_to_numeric_arg,
467         ESC, '9', NUL,                  A_info_add_digit_to_numeric_arg,
468         ESC, '-', NUL,                  A_info_add_digit_to_numeric_arg,
469         Meta('0'), NUL,                 A_info_add_digit_to_numeric_arg,
470         Meta('1'), NUL,                 A_info_add_digit_to_numeric_arg,
471         Meta('2'), NUL,                 A_info_add_digit_to_numeric_arg,
472         Meta('3'), NUL,                 A_info_add_digit_to_numeric_arg,
473         Meta('4'), NUL,                 A_info_add_digit_to_numeric_arg,
474         Meta('5'), NUL,                 A_info_add_digit_to_numeric_arg,
475         Meta('6'), NUL,                 A_info_add_digit_to_numeric_arg,
476         Meta('7'), NUL,                 A_info_add_digit_to_numeric_arg,
477         Meta('8'), NUL,                 A_info_add_digit_to_numeric_arg,
478         Meta('9'), NUL,                 A_info_add_digit_to_numeric_arg,
479         Meta('-'), NUL,                 A_info_add_digit_to_numeric_arg,
480         ESC, CONTROL('g'), NUL,         A_ea_abort,
481         ESC, CONTROL('v'), NUL,         A_ea_scroll_completions_window,
482         ESC, 'b', NUL,                  A_ea_backward_word,
483         ESC, 'd', NUL,                  A_ea_kill_word,
484         ESC, 'f', NUL,                  A_ea_forward_word,
485         ESC, 'y', NUL,                  A_ea_yank_pop,
486         ESC, '?', NUL,                  A_ea_possible_completions,
487         ESC, TAB, NUL,                  A_ea_tab_insert,
488         ESC, DEL, NUL,                  A_ea_backward_kill_word,
489         Meta(CONTROL('g')), NUL,        A_ea_abort,
490         Meta(CONTROL('v')), NUL,        A_ea_scroll_completions_window,
491         Meta('b'), NUL,                 A_ea_backward_word,
492         Meta('d'), NUL,                 A_ea_kill_word,
493         Meta('f'), NUL,                 A_ea_forward_word,
494         Meta('y'), NUL,                 A_ea_yank_pop,
495         Meta('?'), NUL,                 A_ea_possible_completions,
496         Meta(TAB), NUL,                 A_ea_tab_insert,
497         Meta(DEL), NUL,                 A_ea_backward_kill_word,
498         CONTROL('a'), NUL,              A_ea_beg_of_line,
499         CONTROL('b'), NUL,              A_ea_backward,
500         CONTROL('d'), NUL,              A_ea_delete,
501         CONTROL('e'), NUL,              A_ea_end_of_line,
502         CONTROL('f'), NUL,              A_ea_forward,
503         CONTROL('g'), NUL,              A_ea_abort,
504         CONTROL('h'), NUL,              A_ea_rubout,
505 /*      CONTROL('k') */
506         SK_ESCAPE, SK_LITERAL, NUL,     A_ea_kill_line,
507         CONTROL('l'), NUL,              A_info_redraw_display,
508         CONTROL('q'), NUL,              A_ea_quoted_insert,
509         CONTROL('t'), NUL,              A_ea_transpose_chars,
510         CONTROL('u'), NUL,              A_info_universal_argument,
511         CONTROL('y'), NUL,              A_ea_yank,
512         LFD, NUL,                       A_ea_newline,
513         RET, NUL,                       A_ea_newline,
514         SPC, NUL,                       A_ea_complete,
515         TAB, NUL,                       A_ea_complete,
516         '?', NUL,                       A_ea_possible_completions,
517 #ifdef __MSDOS__
518         /* PC users will lynch me if I don't give them their usual DEL
519            effect...  */
520         DEL, NUL,                       A_ea_delete,
521 #else
522         DEL, NUL,                       A_ea_rubout,
523 #endif
524 #if defined (NAMED_FUNCTIONS)
525   /*    ESC, 'x', NUL,                  A_info_execute_command, */
526   /*    Meta('x'), NUL,                 A_info_execute_command, */
527 #endif /* NAMED_FUNCTIONS */
528         CONTROL('x'), 'o', NUL,         A_info_next_window,
529         CONTROL('x'), DEL, NUL,         A_ea_backward_kill_line,
530
531 /*      Arrow key bindings for echo area keymaps.  It seems that some
532         terminals do not match their termcap entries, so it's best to just
533         define everything with both of the usual prefixes.  */
534
535         SK_ESCAPE, SK_RIGHT_ARROW, NUL,         A_ea_forward,
536         '\033', 'O', 'C', NUL,                  A_ea_forward,
537         '\033', '[', 'C', NUL,                  A_ea_forward,
538         SK_ESCAPE, SK_LEFT_ARROW, NUL,          A_ea_backward,
539         '\033', 'O', 'D', NUL,                  A_ea_backward,
540         '\033', '[', 'D', NUL,                  A_ea_backward,
541         ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL,    A_ea_forward_word,
542         ESC, '\033', 'O', 'C', NUL,             A_ea_forward_word,
543         ESC, '\033', '[', 'C', NUL,             A_ea_forward_word,
544         ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL,     A_ea_backward_word,
545         ESC, '\033', 'O', 'D', NUL,             A_ea_backward_word,
546         ESC, '\033', '[', 'D', NUL,             A_ea_backward_word,
547 #ifdef __MSDOS__
548         SK_ESCAPE, SK_DELETE, NUL,              A_ea_delete,
549 #else
550         SK_ESCAPE, SK_DELETE, NUL,              A_ea_rubout,
551 #endif
552         SK_ESCAPE, SK_HOME, NUL,                A_ea_beg_of_line,
553         SK_ESCAPE, SK_END, NUL,                 A_ea_end_of_line,
554         ESC, SK_ESCAPE, SK_DELETE, NUL,         A_ea_backward_kill_word,
555         CONTROL('x'), SK_ESCAPE, SK_DELETE, NUL,A_ea_backward_kill_line,
556 };
557
558 \f
559 static unsigned char default_vi_like_info_keys[] =
560 {
561         0,      /* suppress-default-keybindings flag */
562         '0', NUL,                       A_info_add_digit_to_numeric_arg,
563         '1', NUL,                       A_info_add_digit_to_numeric_arg,
564         '2', NUL,                       A_info_add_digit_to_numeric_arg,
565         '3', NUL,                       A_info_add_digit_to_numeric_arg,
566         '4', NUL,                       A_info_add_digit_to_numeric_arg,
567         '5', NUL,                       A_info_add_digit_to_numeric_arg,
568         '6', NUL,                       A_info_add_digit_to_numeric_arg,
569         '7', NUL,                       A_info_add_digit_to_numeric_arg,
570         '8', NUL,                       A_info_add_digit_to_numeric_arg,
571         '9', NUL,                       A_info_add_digit_to_numeric_arg,
572         '-', NUL,                       A_info_add_digit_to_numeric_arg,
573         TAB, NUL,                       A_info_move_to_next_xref,
574         LFD, NUL,                       A_info_down_line,
575         RET, NUL,                       A_info_down_line,
576         CONTROL('a'), NUL,              A_info_beginning_of_line,
577         CONTROL('b'), NUL,              A_info_scroll_backward_page_only,
578         CONTROL('d'), NUL,              A_info_scroll_half_screen_down,
579         CONTROL('e'), NUL,              A_info_down_line,
580         CONTROL('f'), NUL,              A_info_scroll_forward_page_only,
581         CONTROL('k'), NUL,              A_info_up_line,
582         CONTROL('l'), NUL,              A_info_redraw_display,
583         CONTROL('n'), NUL,              A_info_down_line,
584         CONTROL('p'), NUL,              A_info_up_line,
585         CONTROL('r'), NUL,              A_info_redraw_display,
586         CONTROL('s'), NUL,              A_isearch_forward,
587         CONTROL('u'), NUL,              A_info_scroll_half_screen_up,
588         CONTROL('v'), NUL,              A_info_scroll_forward_page_only,
589         CONTROL('y'), NUL,              A_info_up_line,
590         ',', NUL,                       A_info_next_index_match,
591         '/', NUL,                       A_info_search,
592         ESC, '0', NUL,                  A_info_last_menu_item,
593         ESC, '1', NUL,                  A_info_menu_digit,
594         ESC, '2', NUL,                  A_info_menu_digit,
595         ESC, '3', NUL,                  A_info_menu_digit,
596         ESC, '4', NUL,                  A_info_menu_digit,
597         ESC, '5', NUL,                  A_info_menu_digit,
598         ESC, '6', NUL,                  A_info_menu_digit,
599         ESC, '7', NUL,                  A_info_menu_digit,
600         ESC, '8', NUL,                  A_info_menu_digit,
601         ESC, '9', NUL,                  A_info_menu_digit,
602         Meta('0'), NUL,                 A_info_last_menu_item,
603         Meta('1'), NUL,                 A_info_menu_digit,
604         Meta('2'), NUL,                 A_info_menu_digit,
605         Meta('3'), NUL,                 A_info_menu_digit,
606         Meta('4'), NUL,                 A_info_menu_digit,
607         Meta('5'), NUL,                 A_info_menu_digit,
608         Meta('6'), NUL,                 A_info_menu_digit,
609         Meta('7'), NUL,                 A_info_menu_digit,
610         Meta('8'), NUL,                 A_info_menu_digit,
611         Meta('9'), NUL,                 A_info_menu_digit,
612         '<', NUL,                       A_info_first_node,
613         '>', NUL,                       A_info_last_node,
614         '?', NUL,                       A_info_search_backward,
615         '[', NUL,                       A_info_global_prev_node,
616         ']', NUL,                       A_info_global_next_node,
617         '\'', NUL,                      A_info_history_node,
618         'b', NUL,                       A_info_scroll_backward,
619         'd', NUL,                       A_info_scroll_half_screen_down,
620         'e', NUL,                       A_info_down_line,
621         'E', NUL,                       A_info_view_file,
622         ':', 'e', NUL,                  A_info_view_file,
623         'f', NUL,                       A_info_scroll_forward_page_only,
624         'F', NUL,                       A_info_scroll_forward_page_only,
625         'g', NUL,                       A_info_first_node,
626         'G', NUL,                       A_info_last_node,
627         'h', NUL,                       A_info_get_help_window,
628         'H', NUL,                       A_info_get_help_window,
629         'i', NUL,                       A_info_index_search,
630         'I', NUL,                       A_info_goto_invocation_node,
631         'j', NUL,                       A_info_next_line,
632         'k', NUL,                       A_info_prev_line,
633         'l', NUL,                       A_info_history_node,
634         'm', NUL,                       A_info_menu_item,
635         'n', NUL,                       A_info_search_next,
636         'N', NUL,                       A_info_search_previous,
637         'O', NUL,                       A_info_goto_invocation_node,
638         'p', NUL,                       A_info_prev_node,
639         'Q', NUL,                       A_info_quit,
640         ':', 'q', NUL,                  A_info_quit,
641         ':', 'Q', NUL,                  A_info_quit,
642         'Z', 'Z', NUL,                  A_info_quit,
643         'r', NUL,                       A_info_redraw_display,
644         'R', NUL,                       A_info_toggle_regexp,
645         's', NUL,                       A_info_search,
646         'S', NUL,                       A_info_search_case_sensitively,
647         't', NUL,                       A_info_top_node,
648         'u', NUL,                       A_info_scroll_half_screen_up,
649         'w', NUL,                       A_info_scroll_backward_page_only_set_window,
650         'y', NUL,                       A_info_up_line,
651         'z', NUL,                       A_info_scroll_forward_page_only_set_window,
652         ESC, CONTROL('f'), NUL,         A_info_show_footnotes,
653         ESC, CONTROL('g'), NUL,         A_info_abort_key,
654         ESC, TAB, NUL,                  A_info_move_to_prev_xref,
655         ESC, SPC, NUL,                  A_info_scroll_forward_page_only,
656         ESC, CONTROL('v'), NUL,         A_info_scroll_other_window,
657         ESC, '<', NUL,                  A_info_beginning_of_node,
658         ESC, '>', NUL,                  A_info_end_of_node,
659         ESC, '/', NUL,                  A_info_search,
660         ESC, '?', NUL,                  A_info_search_backward,
661         ESC, 'b', NUL,                  A_info_beginning_of_node,
662         ESC, 'd', NUL,                  A_info_dir_node,
663         ESC, 'e', NUL,                  A_info_end_of_node,
664         ESC, 'f', NUL,                  A_info_xref_item,
665         ESC, 'g', NUL,                  A_info_select_reference_this_line,
666         ESC, 'h', NUL,                  A_info_get_info_help_node,
667         ESC, 'm', NUL,                  A_info_menu_item,
668         ESC, 'n', NUL,                  A_info_search,
669         ESC, 'N', NUL,                  A_info_search_backward,
670         ESC, 'r', NUL,                  A_isearch_backward,
671         ESC, 's', NUL,                  A_isearch_forward,
672         ESC, 't', NUL,                  A_info_top_node,
673         ESC, 'v', NUL,                  A_info_scroll_backward_page_only,
674 #if defined (NAMED_FUNCTIONS)
675         ESC, 'x', NUL,                  A_info_execute_command,
676         Meta('x'), NUL,                 A_info_execute_command,
677 #endif /* NAMED_FUNCTIONS */
678         ESC, DEL, NUL,                  A_info_scroll_other_window_backward,
679         CONTROL('x'), CONTROL('b'), NUL,        A_list_visited_nodes,
680         CONTROL('x'), CONTROL('c'), NUL,        A_info_quit,
681         CONTROL('x'), CONTROL('f'), NUL,        A_info_view_file,
682         CONTROL('x'), CONTROL('g'), NUL,        A_info_abort_key,
683         CONTROL('x'), CONTROL('v'), NUL,        A_info_view_file,
684         CONTROL('x'), LFD, NUL,         A_info_select_reference_this_line,
685         CONTROL('x'), RET, NUL,         A_info_select_reference_this_line,
686         CONTROL('x'), '0', NUL,         A_info_delete_window,
687         CONTROL('x'), '1', NUL,         A_info_keep_one_window,
688         CONTROL('x'), '2', NUL,         A_info_split_window,
689         CONTROL('x'), '^', NUL,         A_info_grow_window,
690         CONTROL('x'), 'b', NUL,         A_select_visited_node,
691         CONTROL('x'), 'g', NUL,         A_info_goto_node,
692         CONTROL('x'), 'i', NUL,         A_info_index_search,
693         CONTROL('x'), 'I', NUL,         A_info_goto_invocation_node,
694         CONTROL('x'), 'k', NUL,         A_info_kill_node,
695         CONTROL('x'), 'n', NUL,         A_info_next_node,
696         CONTROL('x'), 'o', NUL,         A_info_next_window,
697         CONTROL('x'), 'O', NUL,         A_info_goto_invocation_node,
698         CONTROL('x'), 'p', NUL,         A_info_prev_node,
699         CONTROL('x'), 'r', NUL,         A_info_xref_item,
700         CONTROL('x'), 't', NUL,         A_info_tile_windows,
701         CONTROL('x'), 'u', NUL,         A_info_up_node,
702         CONTROL('x'), 'w', NUL,         A_info_toggle_wrap,
703         CONTROL('x'), ',', NUL,         A_info_next_index_match,
704
705 /*      Arrow key bindings for info keymaps.  It seems that some
706         terminals do not match their termcap entries, so it's best to just
707         define everything with both of the usual prefixes.  */
708
709         SK_ESCAPE, SK_PAGE_UP, NUL,             A_info_scroll_backward,
710         SK_ESCAPE, SK_PAGE_DOWN, NUL,           A_info_scroll_forward,
711         '\033', 'O', 'A', NUL,                  A_info_up_line,
712         '\033', '[', 'A', NUL,                  A_info_up_line,
713         '\033', 'O', 'B', NUL,                  A_info_down_line,
714         '\033', '[', 'B', NUL,                  A_info_down_line,
715         SK_ESCAPE, SK_RIGHT_ARROW, NUL,         A_info_scroll_forward_page_only,
716         '\033', 'O', 'C', NUL,                  A_info_scroll_forward_page_only,
717         '\033', '[', 'C', NUL,                  A_info_scroll_forward_page_only,
718         SK_ESCAPE, SK_LEFT_ARROW, NUL,          A_info_scroll_backward_page_only,
719         '\033', 'O', 'D', NUL,                  A_info_scroll_backward_page_only,
720         '\033', '[', 'D', NUL,                  A_info_scroll_backward_page_only,
721         SK_ESCAPE, SK_HOME, NUL,                A_info_beginning_of_node,
722         SK_ESCAPE, SK_END, NUL,                 A_info_end_of_node,
723         ESC, SK_ESCAPE, SK_PAGE_DOWN, NUL,      A_info_scroll_other_window,
724         ESC, SK_ESCAPE, SK_PAGE_UP, NUL,        A_info_scroll_other_window_backward,
725         ESC, SK_ESCAPE, SK_DELETE, NUL,         A_info_scroll_other_window_backward,
726         ESC, SK_ESCAPE, SK_UP_ARROW, NUL,       A_info_prev_node,
727         ESC, '\033', 'O', 'A', NUL,             A_info_prev_node,
728         ESC, '\033', '[', 'A', NUL,             A_info_prev_node,
729         ESC, SK_ESCAPE, SK_DOWN_ARROW, NUL,     A_info_next_node,
730         ESC, '\033', 'O', 'B', NUL,             A_info_next_node,
731         ESC, '\033', '[', 'B', NUL,             A_info_next_node,
732         ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL,    A_info_xref_item,
733         ESC, '\033', 'O', 'C', NUL,             A_info_xref_item,
734         ESC, '\033', '[', 'C', NUL,             A_info_xref_item,
735         ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL,     A_info_beginning_of_node,
736         ESC, '\033', 'O', 'D', NUL,             A_info_beginning_of_node,
737         ESC, '\033', '[', 'D', NUL,             A_info_beginning_of_node,
738         CONTROL('x'), SK_ESCAPE, SK_DELETE, NUL,A_ea_backward_kill_line,
739
740         /* We want help to report q, not C-x C-c, etc.  */
741         'q', NUL,                       A_info_quit,
742         'x', NUL,                       A_info_delete_window,
743         SPC, NUL,                       A_info_scroll_forward,
744         DEL, NUL,                       A_info_scroll_backward,
745         '{', NUL,                       A_info_search_previous,
746         '}', NUL,                       A_info_search_next,
747         CONTROL('g'), NUL,              A_info_abort_key,
748         SK_ESCAPE, SK_UP_ARROW, NUL,    A_info_up_line,
749         SK_ESCAPE, SK_DOWN_ARROW, NUL,  A_info_down_line,
750 };
751
752 \f
753 static unsigned char default_vi_like_ea_keys[] =
754 {
755         0,      /* suppress-default-keybindings flag */
756         ESC, '1', NUL,                  A_info_add_digit_to_numeric_arg,
757         ESC, '2', NUL,                  A_info_add_digit_to_numeric_arg,
758         ESC, '3', NUL,                  A_info_add_digit_to_numeric_arg,
759         ESC, '4', NUL,                  A_info_add_digit_to_numeric_arg,
760         ESC, '5', NUL,                  A_info_add_digit_to_numeric_arg,
761         ESC, '6', NUL,                  A_info_add_digit_to_numeric_arg,
762         ESC, '7', NUL,                  A_info_add_digit_to_numeric_arg,
763         ESC, '8', NUL,                  A_info_add_digit_to_numeric_arg,
764         ESC, '9', NUL,                  A_info_add_digit_to_numeric_arg,
765         ESC, '-', NUL,                  A_info_add_digit_to_numeric_arg,
766         Meta('1'), NUL,                 A_info_add_digit_to_numeric_arg,
767         Meta('2'), NUL,                 A_info_add_digit_to_numeric_arg,
768         Meta('3'), NUL,                 A_info_add_digit_to_numeric_arg,
769         Meta('4'), NUL,                 A_info_add_digit_to_numeric_arg,
770         Meta('5'), NUL,                 A_info_add_digit_to_numeric_arg,
771         Meta('6'), NUL,                 A_info_add_digit_to_numeric_arg,
772         Meta('7'), NUL,                 A_info_add_digit_to_numeric_arg,
773         Meta('8'), NUL,                 A_info_add_digit_to_numeric_arg,
774         Meta('9'), NUL,                 A_info_add_digit_to_numeric_arg,
775         Meta('-'), NUL,                 A_info_add_digit_to_numeric_arg,
776         ESC, CONTROL('g'), NUL,         A_ea_abort,
777         ESC, CONTROL('h'), NUL,         A_ea_backward_kill_word,
778         ESC, CONTROL('v'), NUL,         A_ea_scroll_completions_window,
779         ESC, '0', NUL,                  A_ea_beg_of_line,
780         ESC, '$', NUL,                  A_ea_end_of_line,
781         ESC, 'b', NUL,                  A_ea_backward_word,
782         ESC, 'd', NUL,                  A_ea_kill_word,
783         ESC, 'f', NUL,                  A_ea_forward_word,
784         ESC, 'h', NUL,                  A_ea_forward,
785         ESC, 'l', NUL,                  A_ea_backward,
786         ESC, 'w', NUL,                  A_ea_forward_word,
787         ESC, 'x', NUL,                  A_ea_delete,
788         ESC, 'X', NUL,                  A_ea_kill_word,
789         ESC, 'y', NUL,                  A_ea_yank_pop,
790         ESC, '?', NUL,                  A_ea_possible_completions,
791         ESC, TAB, NUL,                  A_ea_tab_insert,
792         ESC, DEL, NUL,                  A_ea_kill_word,
793         Meta(CONTROL('g')), NUL,        A_ea_abort,
794         Meta(CONTROL('h')), NUL,        A_ea_backward_kill_word,
795         Meta(CONTROL('v')), NUL,        A_ea_scroll_completions_window,
796         Meta('0'), NUL,                 A_ea_beg_of_line,
797         Meta('$'), NUL,                 A_ea_end_of_line,
798         Meta('b'), NUL,                 A_ea_backward_word,
799         Meta('d'), NUL,                 A_ea_kill_word,
800         Meta('f'), NUL,                 A_ea_forward_word,
801         Meta('h'), NUL,                 A_ea_forward,
802         Meta('l'), NUL,                 A_ea_backward,
803         Meta('w'), NUL,                 A_ea_forward_word,
804         Meta('x'), NUL,                 A_ea_delete,
805         Meta('X'), NUL,                 A_ea_kill_word,
806         Meta('y'), NUL,                 A_ea_yank_pop,
807         Meta('?'), NUL,                 A_ea_possible_completions,
808         Meta(TAB), NUL,                 A_ea_tab_insert,
809         Meta(DEL), NUL,                 A_ea_kill_word,
810         CONTROL('a'), NUL,              A_ea_beg_of_line,
811         CONTROL('b'), NUL,              A_ea_backward,
812         CONTROL('d'), NUL,              A_ea_delete,
813         CONTROL('e'), NUL,              A_ea_end_of_line,
814         CONTROL('f'), NUL,              A_ea_forward,
815         CONTROL('g'), NUL,              A_ea_abort,
816         CONTROL('h'), NUL,              A_ea_rubout,
817 /*      CONTROL('k') */
818         SK_ESCAPE, SK_LITERAL, NUL,     A_ea_kill_line,
819         CONTROL('l'), NUL,              A_info_redraw_display,
820         CONTROL('q'), NUL,              A_ea_quoted_insert,
821         CONTROL('t'), NUL,              A_ea_transpose_chars,
822         CONTROL('u'), NUL,              A_ea_abort,
823         CONTROL('v'), NUL,              A_ea_quoted_insert,
824         CONTROL('y'), NUL,              A_ea_yank,
825         LFD, NUL,                       A_ea_newline,
826         RET, NUL,                       A_ea_newline,
827         SPC, NUL,                       A_ea_complete,
828         TAB, NUL,                       A_ea_complete,
829         '?', NUL,                       A_ea_possible_completions,
830 #ifdef __MSDOS__
831         /* PC users will lynch me if I don't give them their usual DEL
832            effect...  */
833         DEL, NUL,                       A_ea_delete,
834 #else
835         DEL, NUL,                       A_ea_rubout,
836 #endif
837         CONTROL('x'), 'o', NUL,         A_info_next_window,
838         CONTROL('x'), DEL, NUL,         A_ea_backward_kill_line,
839
840   /* Arrow key bindings for echo area keymaps.  It seems that some
841      terminals do not match their termcap entries, so it's best to just
842      define everything with both of the usual prefixes.  */
843
844         SK_ESCAPE, SK_RIGHT_ARROW, NUL,         A_ea_forward,
845         '\033', 'O', 'C', NUL,                  A_ea_forward,
846         '\033', '[', 'C', NUL,                  A_ea_forward,
847         SK_ESCAPE, SK_LEFT_ARROW, NUL,          A_ea_backward,
848         '\033', 'O', 'D', NUL,                  A_ea_backward,
849         '\033', '[', 'D', NUL,                  A_ea_backward,
850         SK_ESCAPE, SK_HOME, NUL,                A_ea_beg_of_line,
851         SK_ESCAPE, SK_END, NUL,                 A_ea_end_of_line,
852 #ifdef __MSDOS__
853         SK_ESCAPE, SK_DELETE, NUL,              A_ea_delete,
854 #else
855         SK_DELETE, SK_DELETE, NUL,              A_ea_rubout,
856 #endif
857         ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL,    A_ea_forward_word,
858         ESC, '\033', 'O', 'C', NUL,             A_ea_forward_word,
859         ESC, '\033', '[', 'C', NUL,             A_ea_forward_word,
860         ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL,     A_ea_backward_word,
861         ESC, '\033', 'O', 'D', NUL,             A_ea_backward_word,
862         ESC, '\033', '[', 'D', NUL,             A_ea_backward_word,
863         ESC, SK_ESCAPE, SK_DELETE, NUL,         A_ea_kill_word,
864         CONTROL('x'), SK_ESCAPE, SK_DELETE, NUL,A_ea_backward_kill_line,
865 };
866
867 \f
868 static unsigned char *user_info_keys;
869 static unsigned int user_info_keys_len;
870 static unsigned char *user_ea_keys;
871 static unsigned int user_ea_keys_len;
872 static unsigned char *user_vars;
873 static unsigned int user_vars_len;
874
875 /*
876  * Return the size of a file, or 0 if the size can't be determined.
877  */
878 static unsigned long
879 filesize (int f)
880 {
881         long pos = lseek(f, 0L, SEEK_CUR);
882         long sz = -1L;
883         if (pos != -1L)
884         {
885                 sz = lseek(f, 0L, SEEK_END);
886                 lseek(f, pos, SEEK_SET);
887         }
888         return sz == -1L ? 0L : sz;
889 }
890
891 /* Get an integer from a infokey file.
892    Integers are stored as two bytes, low order first, in radix INFOKEY_RADIX.
893  */
894 static int
895 getint (unsigned char **sp)
896 {
897         int n;
898
899         if ( !((*sp)[0] < INFOKEY_RADIX && (*sp)[1] < INFOKEY_RADIX) )
900                 return -1;
901         n = (*sp)[0] + (*sp)[1] * INFOKEY_RADIX;
902         *sp += 2;
903         return n;
904 }
905
906
907 /* Fetch the contents of the standard infokey file "$HOME/.info".  Return
908    true if ok, false if not.  */
909 static int
910 fetch_user_maps (void)
911 {
912         char *filename = NULL;
913         char *homedir;
914         int f;
915         unsigned char *buf;
916         unsigned long len;
917         long nread;
918         unsigned char *p;
919         int n;
920
921         /* Find and open file. */
922         if ((filename = getenv("INFOKEY")) != NULL)
923                 filename = xstrdup(filename);
924         else if ((homedir = getenv("HOME")) != NULL)
925         {
926                 filename = xmalloc(strlen(homedir) + 2 + strlen(INFOKEY_FILE));
927                 strcpy(filename, homedir);
928                 strcat(filename, "/");
929                 strcat(filename, INFOKEY_FILE);
930         }
931 #ifdef __MSDOS__
932         /* Poor baby, she doesn't have a HOME...  */
933         else
934                 filename = xstrdup(INFOKEY_FILE); /* try current directory */
935 #endif
936         if (filename == NULL || (f = open(filename, O_RDONLY)) == (-1))
937         {
938                 if (filename && errno != ENOENT)
939                 {
940                         info_error(filesys_error_string(filename, errno),
941                             NULL, NULL);
942                         free(filename);
943                 }
944                 return 0;
945         }
946         SET_BINARY (f);
947
948         /* Ensure that the file is a reasonable size. */
949         len = filesize(f);
950         if (len < INFOKEY_NMAGIC + 2 || len > 100 * 1024)
951         {
952                 /* Bad file (a valid file must have at least 9 chars, and
953                    more than 100 KB is a problem). */
954                 if (len < INFOKEY_NMAGIC + 2)
955                         info_error(_("Ignoring invalid infokey file `%s' - too small"),
956                                    filename, NULL);
957                 else
958                         info_error(_("Ignoring invalid infokey file `%s' - too big"),
959                                    filename, NULL);
960                 close(f);
961                 free(filename);
962                 return 0;
963         }
964
965         /* Read the file into a buffer. */
966         buf = xmalloc((int)len);
967         nread = read(f, buf, (unsigned int) len);
968         close(f);
969         if ((unsigned int) nread != len)
970         {
971                 info_error(_("Error reading infokey file `%s' - short read"),
972                     filename, NULL);
973                 free(buf);
974                 free(filename);
975                 return 0;
976         }
977
978         /* Check the header, trailer, and version of the file to increase
979            our confidence that the contents are valid.  */
980         if (    buf[0] != INFOKEY_MAGIC_S0
981                 || buf[1] != INFOKEY_MAGIC_S1
982                 || buf[2] != INFOKEY_MAGIC_S2
983                 || buf[3] != INFOKEY_MAGIC_S3
984                 || buf[len - 4] != INFOKEY_MAGIC_E0
985                 || buf[len - 3] != INFOKEY_MAGIC_E1
986                 || buf[len - 2] != INFOKEY_MAGIC_E2
987                 || buf[len - 1] != INFOKEY_MAGIC_E3
988         )
989         {
990                 info_error(_("Invalid infokey file `%s' (bad magic numbers) -- run infokey to update it"),
991                     filename, NULL);
992                 free(filename);
993                 return 0;
994         }
995         if (len < INFOKEY_NMAGIC + strlen(VERSION) + 1
996             || strcmp(VERSION, (char *) (buf + 4)) != 0)
997         {
998                 info_error
999                   (_("Your infokey file `%s' is out of date -- run infokey to update it"),
1000                     filename, NULL);
1001                 free(filename);
1002                 return 0;
1003         }
1004
1005         /* Extract the pieces.  */
1006         for (p = buf + 4 + strlen(VERSION) + 1;
1007              (unsigned int) (p - buf) < len - 4;
1008              p += n)
1009         {
1010                 int s = *p++;
1011
1012                 n = getint(&p);
1013                 if (n < 0 || (unsigned int) n > len - 4 - (p - buf))
1014                 {
1015                         info_error(_("Invalid infokey file `%s' (bad section length) -- run infokey to update it"),
1016                             filename, NULL);
1017                         free(filename);
1018                         return 0;
1019                 }
1020
1021                 switch (s)
1022                 {
1023                 case INFOKEY_SECTION_INFO:
1024                         user_info_keys = p;
1025                         user_info_keys_len = n;
1026                         break;
1027                 case INFOKEY_SECTION_EA:
1028                         user_ea_keys = p;
1029                         user_ea_keys_len = n;
1030                         break;
1031                 case INFOKEY_SECTION_VAR:
1032                         user_vars = p;
1033                         user_vars_len = n;
1034                         break;
1035                 default:
1036                         info_error(_("Invalid infokey file `%s' (bad section code) -- run infokey to update it"),
1037                             filename, NULL);
1038                         free(filename);
1039                         return 0;
1040                 }
1041         }
1042
1043         free(filename);
1044         return 1;
1045 }
1046
1047 /* Decode special key sequences from the infokey file.  Return zero
1048    if the key sequence includes special keys which the terminal
1049    doesn't define.
1050  */
1051 static int
1052 decode_keys(unsigned char *src, unsigned int slen,
1053     unsigned char *dst, unsigned int dlen)
1054 {
1055         unsigned char *s = src;
1056         unsigned char *d = dst;
1057
1058 #define To_dst(c) do { \
1059   if ((unsigned int) (d - dst) < dlen) *d++ = (c); \
1060 } while (0)
1061
1062         while ((unsigned int) (s - src) < slen)
1063         {
1064                 unsigned char c = ISMETA(*s) ? UNMETA(*s) : *s;
1065
1066                 if (c == SK_ESCAPE)
1067                 {
1068                         char *t;
1069                         static char lit[] = { SK_ESCAPE, NUL };
1070
1071                         switch ((unsigned int) (s + 1 - src) < slen ? s[1] : '\0')
1072                         {
1073                         case SK_RIGHT_ARROW:    t = term_kr; break;
1074                         case SK_LEFT_ARROW:     t = term_kl; break;
1075                         case SK_UP_ARROW:       t = term_ku; break;
1076                         case SK_DOWN_ARROW:     t = term_kd; break;
1077                         case SK_PAGE_UP:        t = term_kP; break;
1078                         case SK_PAGE_DOWN:      t = term_kN; break;
1079                         case SK_HOME:           t = term_kh; break;
1080                         case SK_END:            t = term_ke; break;
1081                         case SK_DELETE:         t = term_kx; break;
1082                         case SK_INSERT:         t = term_ki; break;
1083                         case SK_LITERAL:
1084                         default:                t = lit; break;
1085                         }
1086                         if (t == NULL)
1087                                 return 0;
1088                         while (*t)
1089                                 To_dst(ISMETA(*s) ? Meta(*t++) : *t++);
1090                         s += 2;
1091                 }
1092                 else
1093                 {
1094                         if (ISMETA(*s))
1095                                 To_dst(Meta(*s++));
1096                         else
1097                                 To_dst(*s++);
1098                 }
1099         }
1100
1101         To_dst('\0');
1102
1103         return 1;
1104
1105 #undef To_dst
1106
1107 }
1108
1109 /* Convert an infokey file section to keymap bindings.  Return false if
1110    the default bindings are to be suppressed.  */
1111 static int
1112 section_to_keymaps(Keymap map, unsigned char *table, unsigned int len)
1113 {
1114         int stop;
1115         unsigned char *p;
1116         unsigned char *seq = NULL;
1117         unsigned int seqlen = 0;
1118         enum { getseq, gotseq, getaction } state = getseq;
1119
1120         stop = len > 0 ? table[0] : 0;
1121
1122         for (p = table + 1; (unsigned int) (p - table) < len; p++)
1123         {
1124                 switch (state)
1125                 {
1126                 case getseq:
1127                         if (*p)
1128                         {
1129                                 seq = p;
1130                                 state = gotseq;
1131                         }
1132                         break;
1133
1134                 case gotseq:
1135                         if (!*p)
1136                         {
1137                                 seqlen = p - seq;
1138                                 state = getaction;
1139                         }
1140                         break;
1141
1142                 case getaction:
1143                         {
1144                                 unsigned int action = *p;
1145                                 unsigned char keyseq[256];
1146                                 KEYMAP_ENTRY ke;
1147
1148                                 state = getseq;
1149                                 /* If decode_keys returns zero, it
1150                                    means that seq includes keys which
1151                                    the terminal doesn't support, like
1152                                    PageDown.  In that case, don't bind
1153                                    the key sequence.  */
1154                                 if (decode_keys(seq, seqlen, keyseq,
1155                                                 sizeof keyseq))
1156                                 {
1157                                         keyseq[sizeof keyseq - 1] = '\0';
1158                                         ke.type = ISFUNC;
1159                                         ke.function =
1160                                           action < A_NCOMMANDS
1161                                           ? &function_doc_array[action]
1162                                           : NULL;
1163                                         keymap_bind_keyseq(map,
1164                                             (const char *) keyseq, &ke);
1165                                 }
1166                         }
1167                         break;
1168                 }
1169         }
1170         if (state != getseq)
1171                 info_error(_("Bad data in infokey file -- some key bindings ignored"),
1172                     NULL, NULL);
1173         return !stop;
1174 }
1175
1176 /* Convert an infokey file section to variable settings.
1177  */
1178 static void
1179 section_to_vars(unsigned char *table, unsigned int len)
1180 {
1181         enum { getvar, gotvar, getval, gotval } state = getvar;
1182         unsigned char *var = NULL;
1183         unsigned char *val = NULL;
1184         unsigned char *p;
1185
1186         for (p = table; (unsigned int) (p - table) < len; p++)
1187           {
1188             switch (state)
1189               {
1190               case getvar:
1191                 if (*p)
1192                   {
1193                     var = p;
1194                     state = gotvar;
1195                   }
1196                 break;
1197
1198               case gotvar:
1199                 if (!*p)
1200                   state = getval;
1201                 break;
1202
1203               case getval:
1204                 if (*p)
1205                   {
1206                     val = p;
1207                     state = gotval;
1208                   }
1209                 break;
1210
1211               case gotval:
1212                 if (!*p)
1213                   {
1214                     set_variable_to_value((char *) var, (char *) val);
1215                     state = getvar;
1216                   }
1217                 break;
1218               }
1219           }
1220       if (state != getvar)
1221         info_error(_("Bad data in infokey file -- some var settings ignored"),
1222             NULL, NULL);
1223 }
1224
1225 void
1226 initialize_info_keymaps (void)
1227 {
1228   int i;
1229   int suppress_info_default_bindings = 0;
1230   int suppress_ea_default_bindings = 0;
1231
1232   if (!info_keymap)
1233     {
1234       info_keymap = keymap_make_keymap ();
1235       echo_area_keymap = keymap_make_keymap ();
1236     }
1237
1238   /* Bind the echo area insert routines. */
1239   for (i = 0; i < 256; i++)
1240     if (isprint (i))
1241       echo_area_keymap[i].function = InfoCmd(ea_insert);
1242
1243   /* Get user-defined keys and variables.  */
1244   if (fetch_user_maps())
1245     {
1246       if (user_info_keys_len && user_info_keys[0])
1247         suppress_info_default_bindings = 1;
1248       if (user_ea_keys_len && user_ea_keys[0])
1249         suppress_ea_default_bindings = 1;
1250     }
1251
1252   /* Apply the default bindings, unless the user says to suppress
1253      them.  */
1254   if (vi_keys_p)
1255     {
1256       if (!suppress_info_default_bindings)
1257         section_to_keymaps(info_keymap, default_vi_like_info_keys,
1258                            sizeof(default_vi_like_info_keys));
1259       if (!suppress_ea_default_bindings)
1260           section_to_keymaps(echo_area_keymap, default_vi_like_ea_keys,
1261                              sizeof(default_vi_like_ea_keys));
1262     }
1263   else
1264     {
1265       if (!suppress_info_default_bindings)
1266         section_to_keymaps(info_keymap, default_emacs_like_info_keys,
1267                            sizeof(default_emacs_like_info_keys));
1268       if (!suppress_ea_default_bindings)
1269           section_to_keymaps(echo_area_keymap, default_emacs_like_ea_keys,
1270                              sizeof(default_emacs_like_ea_keys));
1271     }
1272
1273   /* If the user specified custom bindings, apply them on top of the
1274      default ones.  */
1275   if (user_info_keys_len)
1276     section_to_keymaps(info_keymap, user_info_keys, user_info_keys_len);
1277
1278   if (user_ea_keys_len)
1279     section_to_keymaps(echo_area_keymap, user_ea_keys, user_ea_keys_len);
1280
1281   if (user_vars_len)
1282     section_to_vars(user_vars, user_vars_len);
1283 }
1284
1285 /* vim: set sw=2 cino={1s>2sn-s^-se-s: */