/* infomap.c -- keymaps for Info.

   Copyright 1993-2023 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

   Originally written by Brian Fox. */

#include "info.h"
#include "doc.h"
#include "funs.h"
#include "session.h"
#include "terminal.h"
#include "variables.h"

void keymap_bind_keyseq (Keymap map, int *keyseq, KEYMAP_ENTRY *keyentry);

/* Return a new Keymap. */
Keymap
keymap_make_keymap (void)
{
  int i;
  Keymap keymap;

  keymap = (Keymap)xmalloc (KEYMAP_SIZE * sizeof (KEYMAP_ENTRY));

  for (i = 0; i < KEYMAP_SIZE; i++)
    {
      keymap[i].type = ISFUNC;
      keymap[i].value.function = NULL;
    }

  return keymap;
}

/* Record KEYSEQ, a sequence of keys terminated by 0, in the linked list of
   FUNCTION_KEYSEQ hanging off FUNCTION.  Label it with ROOTMAP so we know
   whether the key sequence is for main operation or for the echo area. */
static void
add_function_keyseq (InfoCommand *function, int *keyseq, Keymap rootmap)
{
  FUNCTION_KEYSEQ *ks, *k;
  int len;

  if (function == NULL ||
      function == InfoCmd (info_do_lowercase_version))
    return;

  /* If there is already a key sequence recorded for this key map,
     don't do anything. */
  for (k = function->keys; k; k = k->next)
    if (k->map == rootmap)
      return;

  ks = xmalloc (sizeof (FUNCTION_KEYSEQ));
  ks->next = function->keys;
  ks->map = rootmap;
  for (len = 0; keyseq[len]; len++);
  ks->keyseq = xmalloc ((len + 1) * sizeof (int));
  memcpy (ks->keyseq, keyseq, (len + 1) * sizeof (int));

  function->keys = ks;
}

/* Bind key sequence.  Don't override already bound key sequences. */
void
keymap_bind_keyseq (Keymap map, int *keyseq, KEYMAP_ENTRY *keyentry)
{
  Keymap m = map;
  int *s = keyseq;
  int c;

  if (!s || *s == 0)
    return;

  while ((c = *s++) != '\0')
    {
      switch (m[c].type)
        {
        case ISFUNC:
          if (m[c].value.function)
            return; /* There is a function here already. */

          if (*s != '\0')
            {
              m[c].type = ISKMAP;
              m[c].value.keymap = keymap_make_keymap ();
            }
          break;

        case ISKMAP:
          if (*s == '\0')
            return; /* The key sequence we were asked to bind is an initial
                       subsequence of an already-bound sequence. */
          break;
        }
      if (*s != '\0')
        {
          m = m[c].value.keymap;
        }
      else
        {
          add_function_keyseq (keyentry->value.function, keyseq, map);
          m[c] = *keyentry;
        }
    }

  return;
}


/* Initialize the standard info keymaps. */

Keymap info_keymap = NULL;
Keymap echo_area_keymap = NULL;

/* Make sure that we don't have too many command codes defined. */

#if A_NCOMMANDS > A_MAX_COMMAND + 1
#error "too many commands defined"
#endif

/* Initialize the keymaps from the .info keymap file. */

#define NUL     '\0'

static int default_emacs_like_info_keys[] =
{
  /* Favoured command bindings come first.  We want help to
     report q, not C-x C-c, etc.  */
  'H', NUL,                       A_info_get_help_window,
  'q', NUL,                       A_info_quit,
  KEY_UP_ARROW, NUL,              A_info_prev_line,
  KEY_DOWN_ARROW, NUL,            A_info_next_line,
  KEY_PAGE_UP, NUL,             A_info_scroll_backward,
  KEY_PAGE_DOWN, NUL,           A_info_scroll_forward,
  KEY_HOME, NUL,                  A_info_beginning_of_node,
  KEY_END, NUL,                   A_info_end_of_node,
  '{', NUL,                       A_info_search_previous,
  '}', NUL,                       A_info_search_next,
  CONTROL('g'), NUL,              A_info_abort_key,
  RET, NUL,                       A_info_select_reference_this_line,

  TAB, NUL,                       A_info_move_to_next_xref,
  LFD, NUL,                       A_info_select_reference_this_line,
  CONTROL('a'), NUL,              A_info_beginning_of_line,
  CONTROL('b'), NUL,              A_info_backward_char,
  CONTROL('e'), NUL,              A_info_end_of_line,
  CONTROL('f'), NUL,              A_info_forward_char,
  CONTROL('h'), NUL,              A_info_scroll_backward,
  CONTROL('l'), NUL,              A_info_redraw_display,
  CONTROL('n'), NUL,              A_info_next_line,
  CONTROL('p'), NUL,              A_info_prev_line,
  CONTROL('r'), NUL,              A_isearch_backward,
  CONTROL('s'), NUL,              A_isearch_forward,
  CONTROL('u'), NUL,              A_info_universal_argument,
  CONTROL('v'), NUL,              A_info_scroll_forward_page_only,
  SPC, NUL,                       A_info_scroll_forward,
  ',', NUL,                       A_info_next_index_match,
  '/', NUL,                       A_info_search,
  '0', NUL,                       A_info_last_menu_item,
  '1', NUL,                       A_info_menu_digit,
  '2', NUL,                       A_info_menu_digit,
  '3', NUL,                       A_info_menu_digit,
  '4', NUL,                       A_info_menu_digit,
  '5', NUL,                       A_info_menu_digit,
  '6', NUL,                       A_info_menu_digit,
  '7', NUL,                       A_info_menu_digit,
  '8', NUL,                       A_info_menu_digit,
  '9', NUL,                       A_info_menu_digit,
  '<', NUL,                       A_info_first_node,
  '=', NUL,                       A_info_display_file_info,
  '>', NUL,                       A_info_last_node,
  '?', NUL,                       A_info_search_backward,
  '[', NUL,                       A_info_global_prev_node,
  ']', NUL,                       A_info_global_next_node,
  'b', NUL,                       A_info_beginning_of_node,
  'd', NUL,                       A_info_dir_node,
  'e', NUL,                       A_info_end_of_node,
  'f', NUL,                       A_info_xref_item,
  'g', NUL,                       A_info_goto_node,
  'G', NUL,                       A_info_menu_sequence,
  'h', NUL,                       A_info_get_info_help_node,
  'i', NUL,                       A_info_index_search,
  'I', NUL,                       A_info_virtual_index,
  'l', NUL,                       A_info_history_node,
  'm', NUL,                       A_info_menu_item,
  'n', NUL,                       A_info_next_node,
  'O', NUL,                       A_info_goto_invocation_node,
  'p', NUL,                       A_info_prev_node,
  'r', NUL,                       A_info_xref_item,
  'R', NUL,                       A_info_toggle_regexp,
  's', NUL,                       A_info_search,
  'S', NUL,                       A_info_search_case_sensitively,
  't', NUL,                       A_info_top_node,
  'u', NUL,                       A_info_up_node,
  'x', NUL,                       A_info_delete_window,
  KEYMAP_META('0'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('1'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('2'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('3'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('4'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('5'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('6'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('7'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('8'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('9'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('-'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META(CONTROL('f')), NUL,        A_info_show_footnotes,
  KEYMAP_META(CONTROL('g')), NUL,        A_info_abort_key,
  KEYMAP_META(TAB), NUL,                 A_info_move_to_prev_xref,
  KEYMAP_META(CONTROL('v')), NUL,        A_info_scroll_other_window,
  KEYMAP_META('<'), NUL,                 A_info_beginning_of_node,
  KEYMAP_META('>'), NUL,                 A_info_end_of_node,
  KEYMAP_META('b'), NUL,                 A_info_backward_word,
  KEYMAP_META('f'), NUL,                 A_info_forward_word,
  KEYMAP_META('r'), NUL,                 A_info_move_to_window_line,
  KEYMAP_META('v'), NUL,                 A_info_scroll_backward_page_only,
  KEYMAP_META('x'), NUL,                 A_info_execute_command,
  KEYMAP_META('/'), NUL,                 A_info_tree_search,
  KEYMAP_META('}'), NUL,                 A_info_tree_search_next,
  KEYMAP_META('{'), NUL,                 A_info_tree_search_previous,

  CONTROL('x'), CONTROL('b'), NUL,        A_list_visited_nodes,
  CONTROL('x'), CONTROL('c'), NUL,        A_info_quit,
  CONTROL('x'), CONTROL('f'), NUL,        A_info_view_file,
  CONTROL('x'), CONTROL('g'), NUL,        A_info_abort_key,
  CONTROL('x'), CONTROL('v'), NUL,        A_info_view_file,
  CONTROL('x'), '0', NUL,         A_info_delete_window,
  CONTROL('x'), '1', NUL,         A_info_keep_one_window,
  CONTROL('x'), '2', NUL,         A_info_split_window,
  CONTROL('x'), '^', NUL,         A_info_grow_window,
  CONTROL('x'), 'b', NUL,         A_select_visited_node,
  CONTROL('x'), 'f', NUL,         A_info_all_files,
  CONTROL('x'), 'n', NUL,         A_info_search_next,
  CONTROL('x'), 'N', NUL,         A_info_search_previous,
  CONTROL('x'), 'o', NUL,         A_info_next_window,
  CONTROL('x'), 't', NUL,         A_info_tile_windows,
  CONTROL('x'), 'w', NUL,         A_info_toggle_wrap,

  KEY_RIGHT_ARROW, NUL,         A_info_forward_char,
  KEY_LEFT_ARROW, NUL,          A_info_backward_char,
  KEY_DELETE, NUL,              A_info_scroll_backward,
  
  ESC, KEY_PAGE_UP, NUL,        A_info_scroll_other_window_backward,
  ESC, KEY_PAGE_DOWN, NUL,      A_info_scroll_other_window,
  ESC, KEY_UP_ARROW, NUL,       A_info_prev_line,
  ESC, KEY_DOWN_ARROW, NUL,     A_info_next_line,
  ESC, KEY_RIGHT_ARROW, NUL,    A_info_forward_word,
  ESC, KEY_LEFT_ARROW, NUL,     A_info_backward_word,
  KEY_BACK_TAB, NUL,            A_info_move_to_prev_xref,
  
};


static int default_emacs_like_ea_keys[] =
{
  KEYMAP_META('0'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('1'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('2'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('3'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('4'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('5'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('6'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('7'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('8'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('9'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('-'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META(CONTROL('g')), NUL,        A_ea_abort,
  KEYMAP_META(CONTROL('v')), NUL,        A_ea_scroll_completions_window,
  KEYMAP_META('b'), NUL,                 A_ea_backward_word,
  KEYMAP_META('d'), NUL,                 A_ea_kill_word,
  KEYMAP_META('f'), NUL,                 A_ea_forward_word,
  KEYMAP_META('y'), NUL,                 A_ea_yank_pop,
  KEYMAP_META('?'), NUL,                 A_ea_possible_completions,
  KEYMAP_META(TAB), NUL,                 A_ea_tab_insert,
  KEYMAP_META(KEY_DELETE), NUL,                 A_ea_backward_kill_word,
  CONTROL('a'), NUL,              A_ea_beg_of_line,
  CONTROL('b'), NUL,              A_ea_backward,
  CONTROL('d'), NUL,              A_ea_delete,
  CONTROL('e'), NUL,              A_ea_end_of_line,
  CONTROL('f'), NUL,              A_ea_forward,
  CONTROL('g'), NUL,              A_ea_abort,
  ESC, NUL,                       A_ea_abort,
  CONTROL('h'), NUL,              A_ea_rubout,
  CONTROL('k'), NUL,              A_ea_kill_line,
  CONTROL('l'), NUL,              A_info_redraw_display,
  CONTROL('q'), NUL,              A_ea_quoted_insert,
  CONTROL('t'), NUL,              A_ea_transpose_chars,
  CONTROL('u'), NUL,              A_info_universal_argument,
  CONTROL('y'), NUL,              A_ea_yank,
  LFD, NUL,                       A_ea_newline,
  RET, NUL,                       A_ea_newline,
  TAB, NUL,                       A_ea_complete,
#ifdef __MSDOS__
  /* PC users will lynch me if I don't give them their usual DEL
     effect...  */
  KEY_DELETE, NUL,                       A_ea_delete,
#else
  KEY_DELETE, NUL,                       A_ea_rubout,
#endif
  CONTROL('x'), KEY_DELETE, NUL,         A_ea_backward_kill_line,

  KEY_RIGHT_ARROW, NUL,           A_ea_forward,
  KEY_LEFT_ARROW, NUL,            A_ea_backward,
  KEY_HOME, NUL,                 A_ea_beg_of_line,
  KEY_END, NUL,                  A_ea_end_of_line,
};


static int default_vi_like_info_keys[] =
{
  /* We want help to report q, not C-x C-c, etc.  */
  'q', NUL,                       A_info_quit,
  'x', NUL,                       A_info_delete_window,
  SPC, NUL,                       A_info_scroll_forward,
  '{', NUL,                       A_info_search_previous,
  '}', NUL,                       A_info_search_next,
  KEY_UP_ARROW, NUL,    A_info_up_line,
  KEY_DOWN_ARROW, NUL,  A_info_down_line,

  '0', NUL,                       A_info_add_digit_to_numeric_arg,
  '1', NUL,                       A_info_add_digit_to_numeric_arg,
  '2', NUL,                       A_info_add_digit_to_numeric_arg,
  '3', NUL,                       A_info_add_digit_to_numeric_arg,
  '4', NUL,                       A_info_add_digit_to_numeric_arg,
  '5', NUL,                       A_info_add_digit_to_numeric_arg,
  '6', NUL,                       A_info_add_digit_to_numeric_arg,
  '7', NUL,                       A_info_add_digit_to_numeric_arg,
  '8', NUL,                       A_info_add_digit_to_numeric_arg,
  '9', NUL,                       A_info_add_digit_to_numeric_arg,
  '-', NUL,                       A_info_add_digit_to_numeric_arg,
  TAB, NUL,                       A_info_move_to_next_xref,
  LFD, NUL,                       A_info_down_line,
  RET, NUL,                       A_info_down_line,
  CONTROL('a'), NUL,              A_info_beginning_of_line,
  CONTROL('b'), NUL,              A_info_scroll_backward_page_only,
  CONTROL('c'), NUL,              A_info_abort_key,
  CONTROL('d'), NUL,              A_info_scroll_half_screen_down,
  CONTROL('e'), NUL,              A_info_down_line,
  CONTROL('f'), NUL,              A_info_scroll_forward_page_only,
  CONTROL('g'), NUL,              A_info_display_file_info,
  CONTROL('k'), NUL,              A_info_up_line,
  CONTROL('l'), NUL,              A_info_redraw_display,
  CONTROL('n'), NUL,              A_info_down_line,
  CONTROL('p'), NUL,              A_info_up_line,
  CONTROL('r'), NUL,              A_info_redraw_display,
  CONTROL('s'), NUL,              A_isearch_forward,
  CONTROL('u'), NUL,              A_info_scroll_half_screen_up,
  CONTROL('v'), NUL,              A_info_scroll_forward_page_only,
  CONTROL('y'), NUL,              A_info_up_line,
  ',', NUL,                       A_info_next_index_match,
  '/', NUL,                       A_info_search,
  KEYMAP_META('0'), NUL,                 A_info_last_menu_item,
  KEYMAP_META('1'), NUL,                 A_info_menu_digit,
  KEYMAP_META('2'), NUL,                 A_info_menu_digit,
  KEYMAP_META('3'), NUL,                 A_info_menu_digit,
  KEYMAP_META('4'), NUL,                 A_info_menu_digit,
  KEYMAP_META('5'), NUL,                 A_info_menu_digit,
  KEYMAP_META('6'), NUL,                 A_info_menu_digit,
  KEYMAP_META('7'), NUL,                 A_info_menu_digit,
  KEYMAP_META('8'), NUL,                 A_info_menu_digit,
  KEYMAP_META('9'), NUL,                 A_info_menu_digit,
  '<', NUL,                       A_info_first_node,
  '>', NUL,                       A_info_last_node,
  '?', NUL,                       A_info_search_backward,
  '[', NUL,                       A_info_global_prev_node,
  ']', NUL,                       A_info_global_next_node,
  '\'', NUL,                      A_info_history_node,
  'b', NUL,                       A_info_scroll_backward_page_only,
  'd', NUL,                       A_info_scroll_half_screen_down,
  'e', NUL,                       A_info_down_line,
  'E', NUL,                       A_info_view_file,
  ':', 'e', NUL,                  A_info_view_file,
  'f', NUL,                       A_info_scroll_forward_page_only,
  'F', NUL,                       A_info_scroll_forward_page_only,
  'g', NUL,                       A_info_first_node,
  'G', NUL,                       A_info_last_node,
  'h', NUL,                       A_info_get_help_window,
  'H', NUL,                       A_info_get_help_window,
  'i', NUL,                       A_info_index_search,
  'I', NUL,                       A_info_goto_invocation_node,
  'j', NUL,                       A_info_next_line,
  'k', NUL,                       A_info_prev_line,
  'l', NUL,                       A_info_history_node,
  'm', NUL,                       A_info_menu_item,
  'n', NUL,                       A_info_search_next,
  ':', 'a', NUL,                  A_info_all_files,
  'N', NUL,                       A_info_search_previous,
  'O', NUL,                       A_info_goto_invocation_node,
  'p', NUL,                       A_info_prev_node,
  'Q', NUL,                       A_info_quit,
  ':', 'q', NUL,                  A_info_quit,
  ':', 'Q', NUL,                  A_info_quit,
  'Z', 'Z', NUL,                  A_info_quit,
  'r', NUL,                       A_info_redraw_display,
  'R', NUL,                       A_info_toggle_regexp,
  's', NUL,                       A_info_search,
  'S', NUL,                       A_info_search_case_sensitively,
  't', NUL,                       A_info_top_node,
  'u', NUL,                       A_info_scroll_half_screen_up,
  'w', NUL,                       A_info_scroll_backward_page_only_set_window,
  'y', NUL,                       A_info_up_line,
  'z', NUL,                       A_info_scroll_forward_page_only_set_window,
  KEYMAP_META(CONTROL('f')), NUL,         A_info_show_footnotes,
  KEYMAP_META(CONTROL('g')), NUL,         A_info_abort_key,
  KEYMAP_META(TAB), NUL,                  A_info_move_to_prev_xref,
  KEYMAP_META(SPC), NUL,                  A_info_scroll_forward_page_only,
  KEYMAP_META(CONTROL('v')), NUL,         A_info_scroll_other_window,
  KEYMAP_META('<'), NUL,                  A_info_beginning_of_node,
  KEYMAP_META('>'), NUL,                  A_info_end_of_node,
  KEYMAP_META('/'), NUL,                  A_info_search,
  KEYMAP_META('?'), NUL,                  A_info_search_backward,
  KEYMAP_META('b'), NUL,                  A_info_beginning_of_node,
  KEYMAP_META('d'), NUL,                  A_info_dir_node,
  KEYMAP_META('e'), NUL,                  A_info_end_of_node,
  KEYMAP_META('f'), NUL,                  A_info_xref_item,
  KEYMAP_META('g'), NUL,                  A_info_select_reference_this_line,
  KEYMAP_META('h'), NUL,                  A_info_get_info_help_node,
  KEYMAP_META('I'), NUL,                  A_info_virtual_index,
  KEYMAP_META('m'), NUL,                  A_info_menu_item,
  KEYMAP_META('n'), NUL,                  A_info_search,
  KEYMAP_META('N'), NUL,                  A_info_search_backward,
  KEYMAP_META('r'), NUL,                  A_isearch_backward,
  KEYMAP_META('s'), NUL,                  A_isearch_forward,
  KEYMAP_META('t'), NUL,                  A_info_top_node,
  KEYMAP_META('v'), NUL,                  A_info_scroll_backward_page_only,
  KEYMAP_META('x'), NUL,                  A_info_execute_command,
  CONTROL('x'), CONTROL('b'), NUL,        A_list_visited_nodes,
  CONTROL('x'), CONTROL('c'), NUL,        A_info_quit,
  CONTROL('x'), CONTROL('f'), NUL,        A_info_view_file,
  CONTROL('x'), CONTROL('g'), NUL,        A_info_abort_key,
  CONTROL('x'), CONTROL('v'), NUL,        A_info_view_file,
  CONTROL('x'), LFD, NUL,         A_info_select_reference_this_line,
  CONTROL('x'), RET, NUL,         A_info_select_reference_this_line,
  CONTROL('x'), '0', NUL,         A_info_delete_window,
  CONTROL('x'), '1', NUL,         A_info_keep_one_window,
  CONTROL('x'), '2', NUL,         A_info_split_window,
  CONTROL('x'), '^', NUL,         A_info_grow_window,
  CONTROL('x'), 'b', NUL,         A_select_visited_node,
  CONTROL('x'), 'g', NUL,         A_info_goto_node,
  CONTROL('x'), 'i', NUL,         A_info_index_search,
  CONTROL('x'), 'I', NUL,         A_info_goto_invocation_node,
  CONTROL('x'), 'n', NUL,         A_info_next_node,
  CONTROL('x'), 'o', NUL,         A_info_next_window,
  CONTROL('x'), 'O', NUL,         A_info_goto_invocation_node,
  CONTROL('x'), 'p', NUL,         A_info_prev_node,
  CONTROL('x'), 'r', NUL,         A_info_xref_item,
  CONTROL('x'), 't', NUL,         A_info_tile_windows,
  CONTROL('x'), 'u', NUL,         A_info_up_node,
  CONTROL('x'), 'w', NUL,         A_info_toggle_wrap,
  CONTROL('x'), ',', NUL,         A_info_next_index_match,

  KEY_PAGE_UP, NUL,             A_info_scroll_backward,
  KEY_PAGE_DOWN, NUL,           A_info_scroll_forward,
  KEY_DELETE, NUL,              A_info_scroll_backward,
  KEY_RIGHT_ARROW, NUL,         A_info_scroll_forward_page_only,
  KEY_LEFT_ARROW, NUL,          A_info_scroll_backward_page_only,
  KEY_HOME, NUL,                A_info_beginning_of_node,
  KEY_END, NUL,                 A_info_end_of_node,
  ESC, KEY_PAGE_DOWN, NUL,      A_info_scroll_other_window,
  ESC, KEY_PAGE_UP, NUL,        A_info_scroll_other_window_backward,
  ESC, KEY_DELETE, NUL,         A_info_scroll_other_window_backward,
  ESC, KEY_UP_ARROW, NUL,       A_info_prev_node,
  ESC, KEY_DOWN_ARROW, NUL,     A_info_next_node,
  ESC, KEY_RIGHT_ARROW, NUL,    A_info_xref_item,
  ESC, KEY_LEFT_ARROW, NUL,     A_info_beginning_of_node,
  CONTROL('x'), KEY_DELETE, NUL, A_ea_backward_kill_line,
  KEY_BACK_TAB, NUL,            A_info_move_to_prev_xref,
  
};


static int default_vi_like_ea_keys[] =
{
  KEYMAP_META('1'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('2'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('3'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('4'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('5'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('6'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('7'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('8'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('9'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META('-'), NUL,                 A_info_add_digit_to_numeric_arg,
  KEYMAP_META(CONTROL('g')), NUL,        A_ea_abort,
  KEYMAP_META(CONTROL('h')), NUL,        A_ea_backward_kill_word,
  KEYMAP_META(CONTROL('v')), NUL,        A_ea_scroll_completions_window,
  KEYMAP_META('0'), NUL,                 A_ea_beg_of_line,
  KEYMAP_META('$'), NUL,                 A_ea_end_of_line,
  KEYMAP_META('b'), NUL,                 A_ea_backward_word,
  KEYMAP_META('d'), NUL,                 A_ea_kill_word,
  KEYMAP_META('f'), NUL,                 A_ea_forward_word,
  KEYMAP_META('h'), NUL,                 A_ea_backward,
  KEYMAP_META('l'), NUL,                 A_ea_forward,
  KEYMAP_META('w'), NUL,                 A_ea_forward_word,
  KEYMAP_META('x'), NUL,                 A_ea_delete,
  KEYMAP_META('X'), NUL,                 A_ea_kill_word,
  KEYMAP_META('y'), NUL,                 A_ea_yank_pop,
  KEYMAP_META('?'), NUL,                 A_ea_possible_completions,
  KEYMAP_META(TAB), NUL,                 A_ea_tab_insert,
  KEYMAP_META(DEL), NUL,                 A_ea_kill_word,
  CONTROL('a'), NUL,              A_ea_beg_of_line,
  CONTROL('b'), NUL,              A_ea_backward,
  CONTROL('d'), NUL,              A_ea_delete,
  CONTROL('e'), NUL,              A_ea_end_of_line,
  CONTROL('f'), NUL,              A_ea_forward,
  CONTROL('g'), NUL,              A_ea_abort,
  ESC, NUL,                       A_ea_abort,
  CONTROL('h'), NUL,              A_ea_rubout,
  CONTROL('k'), NUL,              A_ea_kill_line,
  CONTROL('l'), NUL,              A_info_redraw_display,
  CONTROL('q'), NUL,              A_ea_quoted_insert,
  CONTROL('t'), NUL,              A_ea_transpose_chars,
  CONTROL('u'), NUL,              A_ea_abort,
  CONTROL('v'), NUL,              A_ea_quoted_insert,
  CONTROL('y'), NUL,              A_ea_yank,
  LFD, NUL,                       A_ea_newline,
  RET, NUL,                       A_ea_newline,
  TAB, NUL,                       A_ea_complete,
  
  KEY_RIGHT_ARROW, NUL,         A_ea_forward,
  KEY_LEFT_ARROW, NUL,          A_ea_backward,
  KEY_HOME, NUL,                A_ea_beg_of_line,
  KEY_END, NUL,                 A_ea_end_of_line,
#ifdef __MSDOS__
  KEY_DELETE, NUL,              A_ea_delete,
#else
  KEY_DELETE, NUL,              A_ea_rubout,
#endif
  CONTROL('x'), KEY_DELETE, NUL,A_ea_backward_kill_line,
};


/* Whether to suppress the default key bindings. */
static int sup_info, sup_ea;

/* Fetch the contents of the init file at INIT_FILE, or the standard
   infokey file "$HOME/.infokey".  Return non-zero if an init file was
   loaded and read. */
static int
fetch_user_maps (char *init_file)
{
  char *filename = NULL;
  char *homedir;
  FILE *inf;

  /* In infokey.c */
  int compile (FILE *fp, const char *filename, int *, int *);

  /* Find and open file. */
  if (init_file)
    filename = xstrdup (init_file);
  else if ((homedir = getenv ("HOME")) != NULL
#ifdef __MINGW32__
	    || (homedir = getenv ("USERPROFILE")) != NULL
#endif
	  )
    {
      filename = xmalloc (strlen (homedir) + 2 + strlen (INFOKEY_FILE));
      strcpy (filename, homedir);
      strcat (filename, "/");
      strcat (filename, INFOKEY_FILE);
    }
#if defined(__MSDOS__) || defined(__MINGW32__)
  /* Poor baby, she doesn't have a HOME...  */
  else
    filename = xstrdup (INFOKEY_FILE); /* try current directory */
#endif
  inf = fopen (filename, "r");
  if (!inf)
    {
      free (filename);
      if (init_file)
        info_error (_("could not open init file %s"), init_file);
      return 0;
    }

  compile (inf, filename, &sup_info, &sup_ea);

  free (filename);
  return 1;
}


/* Set key bindings in MAP from TABLE, which is of length LEN. */
static void
section_to_keymaps (Keymap map, int *table, unsigned int len)
{
  int k;
  Keymap esc_map;

  int *p;
  int *seq;
  enum { getseq, gotseq, getaction } state = getseq;
  
  for (p = table; (unsigned int) (p - table) < len; p++)
    {
      switch (state)
	{
	case getseq:
	  if (*p)
	    {
	      seq = p;
	      state = gotseq;
	    }
	  break;
	  
	case gotseq:
	  if (!*p)
            state = getaction;
	  break;
	  
	case getaction:
	  {
	    unsigned int action = *p;
	    KEYMAP_ENTRY ke;
	    
	    state = getseq;

            ke.type = ISFUNC;
            ke.value.function = action < A_NCOMMANDS ?
                                &function_doc_array[action]
                                : NULL;
            keymap_bind_keyseq (map, seq, &ke);
	  }
	  break;
	}
    }
  if (state != getseq)
    abort ();

  /* Go through map and bind ESC x to the same function as M-x if it is not 
     bound already. */
  if (!map[ESC].value.function)
    {
      map[ESC].type = ISKMAP;
      map[ESC].value.keymap = keymap_make_keymap ();
    }

  if (map[ESC].type != ISKMAP)
    return; /* ESC is bound to a command. */

  esc_map = map[ESC].value.keymap;
  for (k = 1; k < KEYMAP_META_BASE; k++)
    {
      if (map[k + KEYMAP_META_BASE].type == ISFUNC
          && esc_map[k].value.function == 0)
        {
          esc_map[k].type = ISFUNC;
          esc_map[k].value.function = map[k + KEYMAP_META_BASE].value.function;
        }
    }
  return;
}

/* Read key bindings and variable settings from INIT_FILE.  If INIT_FILE
   is null, look for the init file in the default location. */
void
read_init_file (char *init_file)
{
  int *info_keys, *ea_keys; /* Pointers to keymap tables. */
  long info_keys_len, ea_keys_len; /* Sizes of keymap tables. */

  int i;

  if (!info_keymap)
    {
      info_keymap = keymap_make_keymap ();
      echo_area_keymap = keymap_make_keymap ();
    }

  if (!vi_keys_p)
    {
      info_keys = default_emacs_like_info_keys;
      info_keys_len = sizeof (default_emacs_like_info_keys)/sizeof (int);
      ea_keys = default_emacs_like_ea_keys;
      ea_keys_len = sizeof (default_emacs_like_ea_keys)/sizeof (int);
    }
  else
    {
      info_keys = default_vi_like_info_keys;
      info_keys_len = sizeof (default_vi_like_info_keys)/sizeof(int);
      ea_keys = default_vi_like_ea_keys;
      ea_keys_len = sizeof (default_vi_like_ea_keys)/sizeof(int);
    }

  /* Get user-defined keys and variables.  */
  if (fetch_user_maps (init_file))
    {
      if (sup_info)
        info_keys = 0; /* Suppress default bindings. */
      if (sup_ea)
        ea_keys = 0;
    }

  /* Apply the default bindings, unless the user says to suppress
     them. */
  if (info_keys)
    section_to_keymaps (info_keymap, info_keys, info_keys_len);
  if (ea_keys)
    section_to_keymaps (echo_area_keymap, ea_keys, ea_keys_len);

  for (i = 'A'; i < ('Z' + 1); i++)
    {
      if (!info_keymap[i].value.function)
        {
          info_keymap[i].type = ISFUNC;
          info_keymap[i].value.function = InfoCmd (info_do_lowercase_version);
        }

      if (!info_keymap[KEYMAP_META(i)].value.function)
        {
          info_keymap[KEYMAP_META(i)].type = ISFUNC;
          info_keymap[KEYMAP_META(i)].value.function
            = InfoCmd (info_do_lowercase_version);
        }
    }
}

/* vim: set sw=2 cino={1s>2sn-s^-se-s: */
