1114472Sru/* infodoc.c -- functions which build documentation nodes.
2146515Sru   $Id: infodoc.c,v 1.8 2004/04/11 17:56:45 karl Exp $
321495Sjmacd
4146515Sru   Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software
5114472Sru   Foundation, Inc.
621495Sjmacd
721495Sjmacd   This program is free software; you can redistribute it and/or modify
821495Sjmacd   it under the terms of the GNU General Public License as published by
921495Sjmacd   the Free Software Foundation; either version 2, or (at your option)
1021495Sjmacd   any later version.
1121495Sjmacd
1221495Sjmacd   This program is distributed in the hope that it will be useful,
1321495Sjmacd   but WITHOUT ANY WARRANTY; without even the implied warranty of
1421495Sjmacd   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1521495Sjmacd   GNU General Public License for more details.
1621495Sjmacd
1721495Sjmacd   You should have received a copy of the GNU General Public License
1821495Sjmacd   along with this program; if not, write to the Free Software
1921495Sjmacd   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2021495Sjmacd
2121495Sjmacd   Written by Brian Fox (bfox@ai.mit.edu). */
2221495Sjmacd
2321495Sjmacd#include "info.h"
2493139Sru#include "funs.h"
2521495Sjmacd
2656160Sru/* HELP_NODE_GETS_REGENERATED is always defined now that keys may get
2756160Sru   rebound, or other changes in the help text may occur.  */
2856160Sru#define HELP_NODE_GETS_REGENERATED 1
2921495Sjmacd
3021495Sjmacd/* The name of the node used in the help window. */
3121495Sjmacdstatic char *info_help_nodename = "*Info Help*";
3221495Sjmacd
3321495Sjmacd/* A node containing printed key bindings and their documentation. */
3421495Sjmacdstatic NODE *internal_info_help_node = (NODE *)NULL;
3521495Sjmacd
3621495Sjmacd/* A pointer to the contents of the help node. */
3721495Sjmacdstatic char *internal_info_help_node_contents = (char *)NULL;
3821495Sjmacd
3993139Sru/* The (more or less) static text which appears in the internal info
40116525Sru   help node.  The actual key bindings are inserted.  Keep the
4193139Sru   underlines (****, etc.) in the same N_ call as  the text lines they
4293139Sru   refer to, so translations can make the number of *'s or -'s match.  */
4393139Sru#if defined(INFOKEY)
4493139Sru
4521495Sjmacdstatic char *info_internal_help_text[] = {
4693139Sru  N_("Basic Commands in Info Windows\n\
4793139Sru******************************\n"),
4856160Sru  "\n",
4993139Sru  N_("\\%-10[quit-help]  Quit this help.\n"),
5093139Sru  N_("\\%-10[quit]  Quit Info altogether.\n"),
5193139Sru  N_("\\%-10[get-info-help-node]  Invoke the Info tutorial.\n"),
5293139Sru  "\n",
5393139Sru  N_("Selecting other nodes:\n\
5493139Sru----------------------\n"),
5593139Sru  N_("\\%-10[next-node]  Move to the \"next\" node of this node.\n"),
5693139Sru  N_("\\%-10[prev-node]  Move to the \"previous\" node of this node.\n"),
5793139Sru  N_("\\%-10[up-node]  Move \"up\" from this node.\n"),
5893139Sru  N_("\\%-10[menu-item]  Pick menu item specified by name.\n\
5993139Sru              Picking a menu item causes another node to be selected.\n"),
6093139Sru  N_("\\%-10[xref-item]  Follow a cross reference.  Reads name of reference.\n"),
6193139Sru  N_("\\%-10[history-node]  Move to the last node seen in this window.\n"),
6293139Sru  N_("\\%-10[move-to-next-xref]  Skip to next hypertext link within this node.\n"),
6393139Sru  N_("\\%-10[move-to-prev-xref]  Skip to previous hypertext link within this node.\n"),
6493139Sru  N_("\\%-10[select-reference-this-line]  Follow the hypertext link under cursor.\n"),
6593139Sru  N_("\\%-10[dir-node]  Move to the `directory' node.  Equivalent to `\\[goto-node] (DIR)'.\n"),
6693139Sru  N_("\\%-10[top-node]  Move to the Top node.  Equivalent to `\\[goto-node] Top'.\n"),
6793139Sru  "\n",
6893139Sru  N_("Moving within a node:\n\
6993139Sru---------------------\n"),
70114472Sru  N_("\\%-10[beginning-of-node]  Go to the beginning of this node.\n"),
71114472Sru  N_("\\%-10[end-of-node]  Go to the end of this node.\n"),
72114472Sru  N_("\\%-10[next-line]  Scroll forward 1 line.\n"),
73114472Sru  N_("\\%-10[prev-line]  Scroll backward 1 line.\n"),
7493139Sru  N_("\\%-10[scroll-forward]  Scroll forward a page.\n"),
7593139Sru  N_("\\%-10[scroll-backward]  Scroll backward a page.\n"),
7693139Sru  "\n",
7793139Sru  N_("Other commands:\n\
7893139Sru---------------\n"),
7993139Sru  N_("\\%-10[menu-digit]  Pick first ... ninth item in node's menu.\n"),
8093139Sru  N_("\\%-10[last-menu-item]  Pick last item in node's menu.\n"),
8193139Sru  N_("\\%-10[index-search]  Search for a specified string in the index entries of this Info\n\
8293139Sru              file, and select the node referenced by the first entry found.\n"),
8393139Sru  N_("\\%-10[goto-node]  Move to node specified by name.\n\
8493139Sru              You may include a filename as well, as in (FILENAME)NODENAME.\n"),
8593139Sru  N_("\\%-10[search]  Search forward for a specified string\n\
8693139Sru              and select the node in which the next occurrence is found.\n"),
8793139Sru  N_("\\%-10[search-backward]  Search backward for a specified string\n\
8893139Sru              and select the node in which the previous occurrence is found.\n"),
8993139Sru  NULL
9093139Sru};
9193139Sru
9293139Sru#else /* !INFOKEY */
9393139Sru
9493139Srustatic char *info_internal_help_text[] = {
9593139Sru  N_("Basic Commands in Info Windows\n\
9693139Sru******************************\n"),
9793139Sru  "\n",
9856160Sru  N_("  %-10s  Quit this help.\n"),
9956160Sru  N_("  %-10s  Quit Info altogether.\n"),
10056160Sru  N_("  %-10s  Invoke the Info tutorial.\n"),
10156160Sru  "\n",
10293139Sru  N_("Selecting other nodes:\n\
10393139Sru----------------------\n",
10456160Sru  N_("  %-10s  Move to the `next' node of this node.\n"),
10556160Sru  N_("  %-10s  Move to the `previous' node of this node.\n"),
10656160Sru  N_("  %-10s  Move `up' from this node.\n"),
10756160Sru  N_("  %-10s  Pick menu item specified by name.\n"),
10856160Sru  N_("              Picking a menu item causes another node to be selected.\n"),
10956160Sru  N_("  %-10s  Follow a cross reference.  Reads name of reference.\n"),
11056160Sru  N_("  %-10s  Move to the last node seen in this window.\n"),
11156160Sru  N_("  %-10s  Skip to next hypertext link within this node.\n"),
11256160Sru  N_("  %-10s  Follow the hypertext link under cursor.\n"),
11356160Sru  N_("  %-10s  Move to the `directory' node.  Equivalent to `g (DIR)'.\n"),
11456160Sru  N_("  %-10s  Move to the Top node.  Equivalent to `g Top'.\n"),
11556160Sru  "\n",
11693139Sru  N_("Moving within a node:\n\
11793139Sru---------------------\n"),
11893139Sru  N_("  %-10s  Scroll forward a page.\n"),
11993139Sru  N_("  %-10s  Scroll backward a page.\n"),
12093139Sru  N_("  %-10s  Go to the beginning of this node.\n"),
12193139Sru  N_("  %-10s  Go to the end of this node.\n"),
12293139Sru  N_("  %-10s  Scroll forward 1 line.\n"),
12393139Sru  N_("  %-10s  Scroll backward 1 line.\n"),
12493139Sru  "\n",
12593139Sru  N_("Other commands:\n\
12693139Sru---------------\n"),
12756160Sru  N_("  %-10s  Pick first ... ninth item in node's menu.\n"),
12856160Sru  N_("  %-10s  Pick last item in node's menu.\n"),
12956160Sru  N_("  %-10s  Search for a specified string in the index entries of this Info\n"),
13056160Sru  N_("              file, and select the node referenced by the first entry found.\n"),
13156160Sru  N_("  %-10s  Move to node specified by name.\n"),
13256160Sru  N_("              You may include a filename as well, as in (FILENAME)NODENAME.\n"),
13393139Sru  N_("  %-10s  Search forward for a specified string,\n"),
13456160Sru  N_("              and select the node in which the next occurrence is found.\n"),
13593139Sru  N_("  %-10s  Search backward for a specified string\n"),
13656160Sru  N_("              and select the node in which the next occurrence is found.\n"),
13742660Smarkm  NULL
13821495Sjmacd};
13921495Sjmacd
14056160Srustatic char *info_help_keys_text[][2] = {
14156160Sru  { "", "" },
14256160Sru  { "", "" },
14356160Sru  { "", "" },
14456160Sru  { "CTRL-x 0", "CTRL-x 0" },
14556160Sru  { "q", "q" },
14656160Sru  { "h", "ESC h" },
14756160Sru  { "", "" },
14856160Sru  { "", "" },
14956160Sru  { "", "" },
15056160Sru  { "SPC", "SPC" },
15156160Sru  { "DEL", "b" },
15256160Sru  { "b", "ESC b" },
15356160Sru  { "e", "ESC e" },
15456160Sru  { "ESC 1 SPC", "RET" },
15556160Sru  { "ESC 1 DEL", "y" },
15656160Sru  { "", "" },
15756160Sru  { "", "" },
15856160Sru  { "", "" },
15956160Sru  { "n", "CTRL-x n" },
16056160Sru  { "p", "CTRL-x p" },
16156160Sru  { "u", "CTRL-x u" },
16256160Sru  { "m", "ESC m" },
16356160Sru  { "", "" },
16456160Sru  { "f", "ESC f" },
16556160Sru  { "l", "l" },
16656160Sru  { "TAB", "TAB" },
16756160Sru  { "RET", "CTRL-x RET" },
16856160Sru  { "d", "ESC d" },
16956160Sru  { "t", "ESC t" },
17056160Sru  { "", "" },
17156160Sru  { "", "" },
17256160Sru  { "", "" },
17356160Sru  { "1-9", "ESC 1-9" },
17456160Sru  { "0", "ESC 0" },
17556160Sru  { "i", "CTRL-x i" },
17656160Sru  { "", "" },
17756160Sru  { "g", "CTRL-x g" },
17856160Sru  { "", "" },
17956160Sru  { "s", "/" },
18056160Sru  { "", "" },
18156160Sru  { "ESC - s", "?" },
18256160Sru  { "", "" },
18356160Sru  NULL
18456160Sru};
18556160Sru
18693139Sru#endif /* !INFOKEY */
18721495Sjmacd
188146515Srustatic char *where_is_internal (Keymap map, InfoCommand *cmd);
18993139Sru
19021495Sjmacdvoid
191146515Srudump_map_to_message_buffer (char *prefix, Keymap map)
19221495Sjmacd{
19321495Sjmacd  register int i;
19493139Sru  unsigned prefix_len = strlen (prefix);
19593139Sru  char *new_prefix = (char *)xmalloc (prefix_len + 2);
19621495Sjmacd
19793139Sru  strncpy (new_prefix, prefix, prefix_len);
19893139Sru  new_prefix[prefix_len + 1] = '\0';
19993139Sru
20021495Sjmacd  for (i = 0; i < 256; i++)
20121495Sjmacd    {
20293139Sru      new_prefix[prefix_len] = i;
20321495Sjmacd      if (map[i].type == ISKMAP)
20442660Smarkm        {
20542660Smarkm          dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function);
20642660Smarkm        }
20721495Sjmacd      else if (map[i].function)
20842660Smarkm        {
20942660Smarkm          register int last;
21042660Smarkm          char *doc, *name;
21121495Sjmacd
21242660Smarkm          doc = function_documentation (map[i].function);
21342660Smarkm          name = function_name (map[i].function);
21421495Sjmacd
21542660Smarkm          if (!*doc)
21642660Smarkm            continue;
21721495Sjmacd
21842660Smarkm          /* Find out if there is a series of identical functions, as in
21942660Smarkm             ea_insert (). */
22042660Smarkm          for (last = i + 1; last < 256; last++)
22142660Smarkm            if ((map[last].type != ISFUNC) ||
22242660Smarkm                (map[last].function != map[i].function))
22342660Smarkm              break;
22421495Sjmacd
22542660Smarkm          if (last - 1 != i)
22642660Smarkm            {
227146515Sru              printf_to_message_buffer ("%s .. ", pretty_keyseq (new_prefix),
228146515Sru                  NULL, NULL);
229114472Sru              new_prefix[prefix_len] = last - 1;
230146515Sru              printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
231146515Sru                  NULL, NULL);
23242660Smarkm              i = last - 1;
23342660Smarkm            }
23442660Smarkm          else
235146515Sru            printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
236146515Sru                NULL, NULL);
23721495Sjmacd
23821495Sjmacd#if defined (NAMED_FUNCTIONS)
23942660Smarkm          /* Print the name of the function, and some padding before the
24042660Smarkm             documentation string is printed. */
24142660Smarkm          {
24242660Smarkm            int length_so_far;
24342660Smarkm            int desired_doc_start = 40; /* Must be multiple of 8. */
24421495Sjmacd
245146515Sru            printf_to_message_buffer ("(%s)", name, NULL, NULL);
24642660Smarkm            length_so_far = message_buffer_length_this_line ();
24721495Sjmacd
248146515Sru            if ((desired_doc_start + strlen (doc))
249146515Sru                >= (unsigned int) the_screen->width)
250146515Sru              printf_to_message_buffer ("\n     ", NULL, NULL, NULL);
25142660Smarkm            else
25242660Smarkm              {
25342660Smarkm                while (length_so_far < desired_doc_start)
25442660Smarkm                  {
255146515Sru                    printf_to_message_buffer ("\t", NULL, NULL, NULL);
25642660Smarkm                    length_so_far += character_width ('\t', length_so_far);
25742660Smarkm                  }
25842660Smarkm              }
25942660Smarkm          }
26021495Sjmacd#endif /* NAMED_FUNCTIONS */
261146515Sru          printf_to_message_buffer ("%s\n", doc, NULL, NULL);
26242660Smarkm        }
26321495Sjmacd    }
26493139Sru  free (new_prefix);
26521495Sjmacd}
26621495Sjmacd
26756160Sru/* How to create internal_info_help_node.  HELP_IS_ONLY_WINDOW_P says
26856160Sru   whether we're going to end up in a second (or more) window of our
26956160Sru   own, or whether there's only one window and we're going to usurp it.
27056160Sru   This determines how to quit the help window.  Maybe we should just
27156160Sru   make q do the right thing in both cases.  */
27256160Sru
27321495Sjmacdstatic void
274146515Srucreate_internal_info_help_node (int help_is_only_window_p)
27521495Sjmacd{
27621495Sjmacd  register int i;
27721495Sjmacd  NODE *node;
27856160Sru  char *contents = NULL;
27993139Sru  char *exec_keys;
28021495Sjmacd
28156160Sru#ifndef HELP_NODE_GETS_REGENERATED
28221495Sjmacd  if (internal_info_help_node_contents)
28321495Sjmacd    contents = internal_info_help_node_contents;
28421495Sjmacd#endif /* !HELP_NODE_GETS_REGENERATED */
28521495Sjmacd
28621495Sjmacd  if (!contents)
28721495Sjmacd    {
28821495Sjmacd      int printed_one_mx = 0;
28921495Sjmacd
29021495Sjmacd      initialize_message_buffer ();
29121495Sjmacd
29221495Sjmacd      for (i = 0; info_internal_help_text[i]; i++)
29356160Sru        {
29493139Sru#ifdef INFOKEY
295146515Sru          printf_to_message_buffer (replace_in_documentation
296146515Sru              ((char *) _(info_internal_help_text[i]), help_is_only_window_p),
297146515Sru              NULL, NULL, NULL);
29893139Sru#else
29956160Sru          /* Don't translate blank lines, gettext outputs the po file
30056160Sru             header in that case.  We want a blank line.  */
30156160Sru          char *msg = *(info_internal_help_text[i])
30256160Sru                      ? _(info_internal_help_text[i])
30356160Sru                      : info_internal_help_text[i];
30456160Sru          char *key = info_help_keys_text[i][vi_keys_p];
305116525Sru
30656160Sru          /* If we have only one window (because the window size was too
30756160Sru             small to split it), CTRL-x 0 doesn't work to `quit' help.  */
30856160Sru          if (STREQ (key, "CTRL-x 0") && help_is_only_window_p)
30956160Sru            key = "l";
31021495Sjmacd
311146515Sru          printf_to_message_buffer (msg, key, NULL, NULL);
31293139Sru#endif /* !INFOKEY */
31356160Sru        }
31456160Sru
315146515Sru      printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
316146515Sru      printf_to_message_buffer ((char *) _("The current search path is:\n"),
317146515Sru          NULL, NULL, NULL);
318146515Sru      printf_to_message_buffer ("  %s\n", infopath, NULL, NULL);
319146515Sru      printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
320146515Sru      printf_to_message_buffer ((char *) _("Commands available in Info windows:\n\n"),
321146515Sru          NULL, NULL, NULL);
32221495Sjmacd      dump_map_to_message_buffer ("", info_keymap);
323146515Sru      printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
324146515Sru      printf_to_message_buffer ((char *) _("Commands available in the echo area:\n\n"),
325146515Sru          NULL, NULL, NULL);
32621495Sjmacd      dump_map_to_message_buffer ("", echo_area_keymap);
32721495Sjmacd
32821495Sjmacd#if defined (NAMED_FUNCTIONS)
32993139Sru      /* Get a list of commands which have no keystroke equivs. */
33093139Sru      exec_keys = where_is (info_keymap, InfoCmd(info_execute_command));
33193139Sru      if (exec_keys)
33293139Sru        exec_keys = xstrdup (exec_keys);
33321495Sjmacd      for (i = 0; function_doc_array[i].func; i++)
33442660Smarkm        {
33593139Sru          InfoCommand *cmd = DocInfoCmd(&function_doc_array[i]);
33621495Sjmacd
337146515Sru          if (InfoFunction(cmd) != (VFunction *) info_do_lowercase_version
338114472Sru              && !where_is_internal (info_keymap, cmd)
33993139Sru              && !where_is_internal (echo_area_keymap, cmd))
34042660Smarkm            {
34142660Smarkm              if (!printed_one_mx)
34242660Smarkm                {
343146515Sru                  printf_to_message_buffer ("---------------------\n\n",
344146515Sru                      NULL, NULL, NULL);
345114472Sru                  if (exec_keys && exec_keys[0])
346114472Sru                      printf_to_message_buffer
347146515Sru                        ((char *) _("The following commands can only be invoked via %s:\n\n"),
348146515Sru                         exec_keys, NULL, NULL);
349114472Sru                  else
350114472Sru                      printf_to_message_buffer
351146515Sru                        ((char *) _("The following commands cannot be invoked at all:\n\n"),
352146515Sru                         NULL, NULL, NULL);
35342660Smarkm                  printed_one_mx = 1;
35442660Smarkm                }
35521495Sjmacd
35642660Smarkm              printf_to_message_buffer
35793139Sru                ("%s %s\n     %s\n",
358114472Sru                 exec_keys,
35942660Smarkm                 function_doc_array[i].func_name,
36056160Sru                 replace_in_documentation (strlen (function_doc_array[i].doc)
361146515Sru                   ? (char *) _(function_doc_array[i].doc) : "", 0)
362114472Sru                );
36356160Sru
36442660Smarkm            }
36542660Smarkm        }
36621495Sjmacd
36721495Sjmacd      if (printed_one_mx)
368146515Sru        printf_to_message_buffer ("\n", NULL, NULL, NULL);
36993139Sru
37093139Sru      maybe_free (exec_keys);
37121495Sjmacd#endif /* NAMED_FUNCTIONS */
37221495Sjmacd
37321495Sjmacd      printf_to_message_buffer
37442660Smarkm        ("%s", replace_in_documentation
375146515Sru         ((char *) _("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"), 0),
376146515Sru         NULL, NULL);
37721495Sjmacd      node = message_buffer_to_node ();
37821495Sjmacd      internal_info_help_node_contents = node->contents;
37921495Sjmacd    }
38021495Sjmacd  else
38121495Sjmacd    {
38221495Sjmacd      /* We already had the right contents, so simply use them. */
38321495Sjmacd      node = build_message_node ("", 0, 0);
38421495Sjmacd      free (node->contents);
38521495Sjmacd      node->contents = contents;
38621495Sjmacd      node->nodelen = 1 + strlen (contents);
38721495Sjmacd    }
38821495Sjmacd
38921495Sjmacd  internal_info_help_node = node;
39021495Sjmacd
39121495Sjmacd  /* Do not GC this node's contents.  It never changes, and we never need
39221495Sjmacd     to delete it once it is made.  If you change some things (such as
39321495Sjmacd     placing information about dynamic variables in the help text) then
39421495Sjmacd     you will need to allow the contents to be gc'd, and you will have to
39521495Sjmacd     arrange to always regenerate the help node. */
39621495Sjmacd#if defined (HELP_NODE_GETS_REGENERATED)
39721495Sjmacd  add_gcable_pointer (internal_info_help_node->contents);
39821495Sjmacd#endif
39921495Sjmacd
40021495Sjmacd  name_internal_node (internal_info_help_node, info_help_nodename);
40121495Sjmacd
40221495Sjmacd  /* Even though this is an internal node, we don't want the window
40321495Sjmacd     system to treat it specially.  So we turn off the internalness
40421495Sjmacd     of it here. */
40521495Sjmacd  internal_info_help_node->flags &= ~N_IsInternal;
40621495Sjmacd}
40721495Sjmacd
40821495Sjmacd/* Return a window which is the window showing help in this Info. */
40956160Sru
41056160Sru/* If the eligible window's height is >= this, split it to make the help
41156160Sru   window.  Otherwise display the help window in the current window.  */
41256160Sru#define HELP_SPLIT_SIZE 24
41356160Sru
41421495Sjmacdstatic WINDOW *
415146515Sruinfo_find_or_create_help_window (void)
41621495Sjmacd{
41756160Sru  int help_is_only_window_p;
41856160Sru  WINDOW *eligible = NULL;
41956160Sru  WINDOW *help_window = get_window_of_node (internal_info_help_node);
42021495Sjmacd
42121495Sjmacd  /* If we couldn't find the help window, then make it. */
42221495Sjmacd  if (!help_window)
42321495Sjmacd    {
42456160Sru      WINDOW *window;
42521495Sjmacd      int max = 0;
42621495Sjmacd
42721495Sjmacd      for (window = windows; window; window = window->next)
42842660Smarkm        {
42942660Smarkm          if (window->height > max)
43042660Smarkm            {
43142660Smarkm              max = window->height;
43242660Smarkm              eligible = window;
43342660Smarkm            }
43442660Smarkm        }
43521495Sjmacd
43621495Sjmacd      if (!eligible)
43756160Sru        return NULL;
43821495Sjmacd    }
43956160Sru#ifndef HELP_NODE_GETS_REGENERATED
44021495Sjmacd  else
44156160Sru    /* help window is static, just return it.  */
44256160Sru    return help_window;
44356160Sru#endif /* not HELP_NODE_GETS_REGENERATED */
44421495Sjmacd
44556160Sru  /* Make sure that we have a node containing the help text.  The
44656160Sru     argument is false if help will be the only window (so l must be used
44756160Sru     to quit help), true if help will be one of several visible windows
44856160Sru     (so CTRL-x 0 must be used to quit help).  */
449146515Sru  help_is_only_window_p = ((help_window && !windows->next)
450146515Sru        || (!help_window && eligible->height < HELP_SPLIT_SIZE));
45156160Sru  create_internal_info_help_node (help_is_only_window_p);
45221495Sjmacd
45321495Sjmacd  /* Either use the existing window to display the help node, or create
45421495Sjmacd     a new window if there was no existing help window. */
45521495Sjmacd  if (!help_window)
45656160Sru    { /* Split the largest window into 2 windows, and show the help text
45742660Smarkm         in that window. */
45856160Sru      if (eligible->height >= HELP_SPLIT_SIZE)
45942660Smarkm        {
46042660Smarkm          active_window = eligible;
46142660Smarkm          help_window = window_make_window (internal_info_help_node);
46242660Smarkm        }
46321495Sjmacd      else
46442660Smarkm        {
46542660Smarkm          set_remembered_pagetop_and_point (active_window);
46642660Smarkm          window_set_node_of_window (active_window, internal_info_help_node);
46742660Smarkm          help_window = active_window;
46842660Smarkm        }
46921495Sjmacd    }
47021495Sjmacd  else
47156160Sru    { /* Case where help node always gets regenerated, and we have an
47242660Smarkm         existing window in which to place the node. */
47321495Sjmacd      if (active_window != help_window)
47442660Smarkm        {
47542660Smarkm          set_remembered_pagetop_and_point (active_window);
47642660Smarkm          active_window = help_window;
47742660Smarkm        }
47821495Sjmacd      window_set_node_of_window (active_window, internal_info_help_node);
47921495Sjmacd    }
48021495Sjmacd  remember_window_and_node (help_window, help_window->node);
48156160Sru  return help_window;
48221495Sjmacd}
48321495Sjmacd
48421495Sjmacd/* Create or move to the help window. */
48542660SmarkmDECLARE_INFO_COMMAND (info_get_help_window, _("Display help message"))
48621495Sjmacd{
48721495Sjmacd  WINDOW *help_window;
48821495Sjmacd
48921495Sjmacd  help_window = info_find_or_create_help_window ();
49021495Sjmacd  if (help_window)
49121495Sjmacd    {
49221495Sjmacd      active_window = help_window;
49321495Sjmacd      active_window->flags |= W_UpdateWindow;
49421495Sjmacd    }
49521495Sjmacd  else
49621495Sjmacd    {
497146515Sru      info_error ((char *) msg_cant_make_help, NULL, NULL);
49821495Sjmacd    }
49921495Sjmacd}
50021495Sjmacd
50121495Sjmacd/* Show the Info help node.  This means that the "info" file is installed
50221495Sjmacd   where it can easily be found on your system. */
50342660SmarkmDECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'"))
50421495Sjmacd{
50521495Sjmacd  NODE *node;
50621495Sjmacd  char *nodename;
50721495Sjmacd
50821495Sjmacd  /* If there is a window on the screen showing the node "(info)Help" or
50921495Sjmacd     the node "(info)Help-Small-Screen", simply select that window. */
51021495Sjmacd  {
51121495Sjmacd    WINDOW *win;
51221495Sjmacd
51321495Sjmacd    for (win = windows; win; win = win->next)
51421495Sjmacd      {
51542660Smarkm        if (win->node && win->node->filename &&
51642660Smarkm            (strcasecmp
51742660Smarkm             (filename_non_directory (win->node->filename), "info") == 0) &&
51842660Smarkm            ((strcmp (win->node->nodename, "Help") == 0) ||
51942660Smarkm             (strcmp (win->node->nodename, "Help-Small-Screen") == 0)))
52042660Smarkm          {
52142660Smarkm            active_window = win;
52242660Smarkm            return;
52342660Smarkm          }
52421495Sjmacd      }
52521495Sjmacd  }
52621495Sjmacd
52721495Sjmacd  /* If the current window is small, show the small screen help. */
52821495Sjmacd  if (active_window->height < 24)
52921495Sjmacd    nodename = "Help-Small-Screen";
53021495Sjmacd  else
53121495Sjmacd    nodename = "Help";
53221495Sjmacd
53321495Sjmacd  /* Try to get the info file for Info. */
53421495Sjmacd  node = info_get_node ("Info", nodename);
53521495Sjmacd
53621495Sjmacd  if (!node)
53721495Sjmacd    {
53821495Sjmacd      if (info_recent_file_error)
539146515Sru        info_error (info_recent_file_error, NULL, NULL);
54021495Sjmacd      else
541146515Sru        info_error ((char *) msg_cant_file_node, "Info", nodename);
54221495Sjmacd    }
54321495Sjmacd  else
54421495Sjmacd    {
54521495Sjmacd      /* If the current window is very large (greater than 45 lines),
54642660Smarkm         then split it and show the help node in another window.
54742660Smarkm         Otherwise, use the current window. */
54821495Sjmacd
54921495Sjmacd      if (active_window->height > 45)
55042660Smarkm        active_window = window_make_window (node);
55121495Sjmacd      else
55242660Smarkm        {
55342660Smarkm          set_remembered_pagetop_and_point (active_window);
55442660Smarkm          window_set_node_of_window (active_window, node);
55542660Smarkm        }
55621495Sjmacd
55721495Sjmacd      remember_window_and_node (active_window, node);
55821495Sjmacd    }
55921495Sjmacd}
56021495Sjmacd
56121495Sjmacd/* **************************************************************** */
56242660Smarkm/*                                                                  */
56342660Smarkm/*                   Groveling Info Keymaps and Docs                */
56442660Smarkm/*                                                                  */
56521495Sjmacd/* **************************************************************** */
56621495Sjmacd
56721495Sjmacd/* Return the documentation associated with the Info command FUNCTION. */
56821495Sjmacdchar *
569146515Srufunction_documentation (InfoCommand *cmd)
57021495Sjmacd{
57193139Sru  char *doc;
57293139Sru
57393139Sru#if defined (INFOKEY)
57493139Sru
57593139Sru  doc = cmd->doc;
57693139Sru
57793139Sru#else /* !INFOKEY */
57893139Sru
57921495Sjmacd  register int i;
58021495Sjmacd
58121495Sjmacd  for (i = 0; function_doc_array[i].func; i++)
58293139Sru    if (InfoFunction(cmd) == function_doc_array[i].func)
58321495Sjmacd      break;
58421495Sjmacd
58593139Sru  doc = function_doc_array[i].func ? function_doc_array[i].doc : "";
58693139Sru
58793139Sru#endif /* !INFOKEY */
58893139Sru
589146515Sru  return replace_in_documentation ((strlen (doc) == 0) ? doc : (char *) _(doc), 0);
59021495Sjmacd}
59121495Sjmacd
59221495Sjmacd#if defined (NAMED_FUNCTIONS)
59321495Sjmacd/* Return the user-visible name of the function associated with the
59421495Sjmacd   Info command FUNCTION. */
59521495Sjmacdchar *
596146515Srufunction_name (InfoCommand *cmd)
59793139Sru{
59893139Sru#if defined (INFOKEY)
59921495Sjmacd
60093139Sru  return cmd->func_name;
60193139Sru
60293139Sru#else /* !INFOKEY */
60393139Sru
60421495Sjmacd  register int i;
60521495Sjmacd
60621495Sjmacd  for (i = 0; function_doc_array[i].func; i++)
60793139Sru    if (InfoFunction(cmd) == function_doc_array[i].func)
60821495Sjmacd      break;
60921495Sjmacd
61021495Sjmacd  return (function_doc_array[i].func_name);
61193139Sru
61293139Sru#endif /* !INFOKEY */
61321495Sjmacd}
61421495Sjmacd
61593139Sru/* Return a pointer to the info command for function NAME. */
61693139SruInfoCommand *
617146515Srunamed_function (char *name)
61821495Sjmacd{
61921495Sjmacd  register int i;
62021495Sjmacd
62121495Sjmacd  for (i = 0; function_doc_array[i].func; i++)
62221495Sjmacd    if (strcmp (function_doc_array[i].func_name, name) == 0)
62321495Sjmacd      break;
62421495Sjmacd
62593139Sru  return (DocInfoCmd(&function_doc_array[i]));
62621495Sjmacd}
62721495Sjmacd#endif /* NAMED_FUNCTIONS */
62821495Sjmacd
62921495Sjmacd/* Return the documentation associated with KEY in MAP. */
63021495Sjmacdchar *
631146515Srukey_documentation (char key, Keymap map)
63221495Sjmacd{
63393139Sru  InfoCommand *function = map[key].function;
63421495Sjmacd
63521495Sjmacd  if (function)
63621495Sjmacd    return (function_documentation (function));
63721495Sjmacd  else
63821495Sjmacd    return ((char *)NULL);
63921495Sjmacd}
64021495Sjmacd
64142660SmarkmDECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
64221495Sjmacd{
64393139Sru  char keys[50];
64421495Sjmacd  unsigned char keystroke;
64593139Sru  char *k = keys;
64621495Sjmacd  Keymap map;
64721495Sjmacd
64893139Sru  *k = '\0';
64921495Sjmacd  map = window->keymap;
65021495Sjmacd
65156160Sru  for (;;)
65221495Sjmacd    {
653146515Sru      message_in_echo_area ((char *) _("Describe key: %s"),
654146515Sru          pretty_keyseq (keys), NULL);
65521495Sjmacd      keystroke = info_get_input_char ();
65621495Sjmacd      unmessage_in_echo_area ();
65721495Sjmacd
65893139Sru#if !defined (INFOKEY)
65956160Sru      if (Meta_p (keystroke))
66042660Smarkm        {
66142660Smarkm          if (map[ESC].type != ISKMAP)
66242660Smarkm            {
66342660Smarkm              window_message_in_echo_area
66456160Sru              (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke)));
66542660Smarkm              return;
66642660Smarkm            }
66721495Sjmacd
668114472Sru          *k++ = '\e';
66942660Smarkm          keystroke = UnMeta (keystroke);
67042660Smarkm          map = (Keymap)map[ESC].function;
67142660Smarkm        }
67293139Sru#endif /* !INFOKEY */
67321495Sjmacd
67493139Sru      /* Add the KEYSTROKE to our list. */
67593139Sru      *k++ = keystroke;
67693139Sru      *k = '\0';
67721495Sjmacd
67893139Sru      if (map[keystroke].function == (InfoCommand *)NULL)
67942660Smarkm        {
680146515Sru          message_in_echo_area ((char *) _("%s is undefined."),
681146515Sru              pretty_keyseq (keys), NULL);
68242660Smarkm          return;
68342660Smarkm        }
68421495Sjmacd      else if (map[keystroke].type == ISKMAP)
68542660Smarkm        {
68642660Smarkm          map = (Keymap)map[keystroke].function;
68742660Smarkm          continue;
68842660Smarkm        }
68921495Sjmacd      else
69042660Smarkm        {
69193139Sru          char *keyname, *message, *fundoc, *funname = "";
69221495Sjmacd
69393139Sru#if defined (INFOKEY)
694114472Sru          /* If the key is bound to do-lowercase-version, but its
695114472Sru             lower-case variant is undefined, say that this key is
696114472Sru             also undefined.  This is especially important for unbound
697114472Sru             edit keys that emit an escape sequence: it's terribly
698114472Sru             confusing to see a message "Home (do-lowercase-version)"
699114472Sru             or some such when Home is unbound.  */
700146515Sru          if (InfoFunction(map[keystroke].function)
701146515Sru              == (VFunction *) info_do_lowercase_version)
702114472Sru            {
703114472Sru              unsigned char lowerkey = Meta_p(keystroke)
704114472Sru                                       ? Meta (tolower (UnMeta (keystroke)))
705114472Sru                                       : tolower (keystroke);
70693139Sru
707114472Sru              if (map[lowerkey].function == (InfoCommand *)NULL)
708114472Sru                {
709146515Sru                  message_in_echo_area ((char *) _("%s is undefined."),
710146515Sru                                        pretty_keyseq (keys), NULL);
711114472Sru                  return;
712114472Sru                }
713114472Sru            }
71493139Sru#endif
71593139Sru
716114472Sru          keyname = pretty_keyseq (keys);
71793139Sru
71821495Sjmacd#if defined (NAMED_FUNCTIONS)
71942660Smarkm          funname = function_name (map[keystroke].function);
72021495Sjmacd#endif /* NAMED_FUNCTIONS */
72121495Sjmacd
72242660Smarkm          fundoc = function_documentation (map[keystroke].function);
72321495Sjmacd
72442660Smarkm          message = (char *)xmalloc
72542660Smarkm            (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
72621495Sjmacd
72721495Sjmacd#if defined (NAMED_FUNCTIONS)
72842660Smarkm          sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
72921495Sjmacd#else
73042660Smarkm          sprintf (message, _("%s is defined to %s."), keyname, fundoc);
73121495Sjmacd#endif /* !NAMED_FUNCTIONS */
73221495Sjmacd
733146515Sru          window_message_in_echo_area ("%s", message, NULL);
73442660Smarkm          free (message);
73542660Smarkm          break;
73642660Smarkm        }
73721495Sjmacd    }
73821495Sjmacd}
73921495Sjmacd
74093139Sru/* Return the pretty printable name of a single character. */
74121495Sjmacdchar *
742146515Srupretty_keyname (unsigned char key)
74321495Sjmacd{
74493139Sru  static char rep_buffer[30];
74521495Sjmacd  char *rep;
74621495Sjmacd
74721495Sjmacd  if (Meta_p (key))
74821495Sjmacd    {
74921495Sjmacd      char temp[20];
75021495Sjmacd
75121495Sjmacd      rep = pretty_keyname (UnMeta (key));
75221495Sjmacd
75393139Sru#if defined (INFOKEY)
75493139Sru      sprintf (temp, "M-%s", rep);
75593139Sru#else /* !INFOKEY */
75621495Sjmacd      sprintf (temp, "ESC %s", rep);
75793139Sru#endif /* !INFOKEY */
75821495Sjmacd      strcpy (rep_buffer, temp);
75921495Sjmacd      rep = rep_buffer;
76021495Sjmacd    }
76121495Sjmacd  else if (Control_p (key))
76221495Sjmacd    {
76321495Sjmacd      switch (key)
76442660Smarkm        {
76542660Smarkm        case '\n': rep = "LFD"; break;
76642660Smarkm        case '\t': rep = "TAB"; break;
76742660Smarkm        case '\r': rep = "RET"; break;
76842660Smarkm        case ESC:  rep = "ESC"; break;
76921495Sjmacd
77042660Smarkm        default:
77142660Smarkm          sprintf (rep_buffer, "C-%c", UnControl (key));
77242660Smarkm          rep = rep_buffer;
77342660Smarkm        }
77421495Sjmacd    }
77521495Sjmacd  else
77621495Sjmacd    {
77721495Sjmacd      switch (key)
77842660Smarkm        {
77942660Smarkm        case ' ': rep = "SPC"; break;
78042660Smarkm        case DEL: rep = "DEL"; break;
78142660Smarkm        default:
78242660Smarkm          rep_buffer[0] = key;
78342660Smarkm          rep_buffer[1] = '\0';
78442660Smarkm          rep = rep_buffer;
78542660Smarkm        }
78621495Sjmacd    }
78721495Sjmacd  return (rep);
78821495Sjmacd}
78921495Sjmacd
79093139Sru/* Return the pretty printable string which represents KEYSEQ. */
79193139Sru
792146515Srustatic void pretty_keyseq_internal (char *keyseq, char *rep);
79393139Sru
79493139Sruchar *
795146515Srupretty_keyseq (char *keyseq)
79693139Sru{
79793139Sru  static char keyseq_rep[200];
79893139Sru
79993139Sru  keyseq_rep[0] = '\0';
80093139Sru  if (*keyseq)
80193139Sru    pretty_keyseq_internal (keyseq, keyseq_rep);
80293139Sru  return (keyseq_rep);
80393139Sru}
80493139Sru
80593139Srustatic void
806146515Srupretty_keyseq_internal (char *keyseq, char *rep)
80793139Sru{
80893139Sru  if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0)
80993139Sru    {
81093139Sru      strcpy(rep, "PgUp");
81193139Sru      keyseq += strlen(term_kP);
81293139Sru    }
81393139Sru  else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0)
81493139Sru    {
81593139Sru      strcpy(rep, "PgDn");
81693139Sru      keyseq += strlen(term_kN);
81793139Sru    }
81893139Sru#if defined(INFOKEY)
81993139Sru  else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0)
82093139Sru    {
82193139Sru      strcpy(rep, "Home");
82293139Sru      keyseq += strlen(term_kh);
82393139Sru    }
82493139Sru  else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0)
82593139Sru    {
82693139Sru      strcpy(rep, "End");
82793139Sru      keyseq += strlen(term_ke);
82893139Sru    }
82993139Sru  else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0)
83093139Sru    {
83193139Sru      strcpy(rep, "INS");
83293139Sru      keyseq += strlen(term_ki);
83393139Sru    }
83493139Sru  else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0)
83593139Sru    {
83693139Sru      strcpy(rep, "DEL");
83793139Sru      keyseq += strlen(term_kx);
83893139Sru    }
83993139Sru#endif /* INFOKEY */
84093139Sru  else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0)
84193139Sru    {
84293139Sru      strcpy(rep, "Up");
84393139Sru      keyseq += strlen(term_ku);
84493139Sru    }
84593139Sru  else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0)
84693139Sru    {
84793139Sru      strcpy(rep, "Down");
84893139Sru      keyseq += strlen(term_kd);
84993139Sru    }
85093139Sru  else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0)
85193139Sru    {
85293139Sru      strcpy(rep, "Left");
85393139Sru      keyseq += strlen(term_kl);
85493139Sru    }
85593139Sru  else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0)
85693139Sru    {
85793139Sru      strcpy(rep, "Right");
85893139Sru      keyseq += strlen(term_kr);
85993139Sru    }
86093139Sru  else
86193139Sru    {
86293139Sru      strcpy (rep, pretty_keyname (keyseq[0]));
86393139Sru      keyseq++;
86493139Sru    }
86593139Sru  if (*keyseq)
86693139Sru    {
86793139Sru      strcat (rep, " ");
86893139Sru      pretty_keyseq_internal (keyseq, rep + strlen(rep));
86993139Sru    }
87093139Sru}
87193139Sru
87293139Sru/* Return a pointer to the last character in s that is found in f. */
87393139Srustatic char *
874146515Srustrrpbrk (const char *s, const char *f)
87593139Sru{
87693139Sru  register const char *e = s + strlen(s);
87793139Sru  register const char *t;
87893139Sru
87993139Sru  while (e-- != s)
88093139Sru    {
88193139Sru      for (t = f; *t; t++)
88293139Sru        if (*e == *t)
883114472Sru          return (char *)e;
88493139Sru    }
88593139Sru  return NULL;
88693139Sru}
88793139Sru
88821495Sjmacd/* Replace the names of functions with the key that invokes them. */
88921495Sjmacdchar *
890146515Srureplace_in_documentation (char *string, int help_is_only_window_p)
89121495Sjmacd{
89293139Sru  unsigned reslen = strlen (string);
89321495Sjmacd  register int i, start, next;
89421495Sjmacd  static char *result = (char *)NULL;
89521495Sjmacd
89621495Sjmacd  maybe_free (result);
89793139Sru  result = (char *)xmalloc (1 + reslen);
89821495Sjmacd
89921495Sjmacd  i = next = start = 0;
90021495Sjmacd
90121495Sjmacd  /* Skip to the beginning of a replaceable function. */
90221495Sjmacd  for (i = start; string[i]; i++)
90321495Sjmacd    {
90493139Sru      int j = i + 1;
90593139Sru
90621495Sjmacd      /* Is this the start of a replaceable function name? */
90793139Sru      if (string[i] == '\\')
908114472Sru        {
909114472Sru          char *fmt = NULL;
910114472Sru          unsigned min = 0;
911114472Sru          unsigned max = 0;
91221495Sjmacd
913114472Sru          if(string[j] == '%')
914114472Sru            {
915114472Sru              if (string[++j] == '-')
916114472Sru                j++;
917114472Sru              if (isdigit(string[j]))
918114472Sru                {
919114472Sru                  min = atoi(string + j);
920114472Sru                  while (isdigit(string[j]))
921114472Sru                    j++;
922114472Sru                  if (string[j] == '.' && isdigit(string[j + 1]))
923114472Sru                    {
924114472Sru                      j += 1;
925114472Sru                      max = atoi(string + j);
926114472Sru                      while (isdigit(string[j]))
927114472Sru                        j++;
928114472Sru                    }
929114472Sru                  fmt = (char *)xmalloc (j - i + 2);
930114472Sru                  strncpy (fmt, string + i + 1, j - i);
931114472Sru                  fmt[j - i - 1] = 's';
932114472Sru                  fmt[j - i] = '\0';
933114472Sru                }
934114472Sru              else
935114472Sru                j = i + 1;
936114472Sru            }
937114472Sru          if (string[j] == '[')
938114472Sru            {
939114472Sru              unsigned arg = 0;
940114472Sru              char *argstr = NULL;
941114472Sru              char *rep_name, *fun_name, *rep;
942114472Sru              InfoCommand *command;
943114472Sru              char *repstr = NULL;
944114472Sru              unsigned replen;
94521495Sjmacd
946114472Sru              /* Copy in the old text. */
947114472Sru              strncpy (result + next, string + start, i - start);
948114472Sru              next += (i - start);
949114472Sru              start = j + 1;
95021495Sjmacd
951114472Sru              /* Look for an optional numeric arg. */
952114472Sru              i = start;
953114472Sru              if (isdigit(string[i])
954114472Sru                  || (string[i] == '-' && isdigit(string[i + 1])) )
955114472Sru                {
956114472Sru                  arg = atoi(string + i);
957114472Sru                  if (string[i] == '-')
958114472Sru                    i++;
959114472Sru                  while (isdigit(string[i]))
960114472Sru                    i++;
961114472Sru                }
962114472Sru              start = i;
96321495Sjmacd
964114472Sru              /* Move to the end of the function name. */
965114472Sru              for (i = start; string[i] && (string[i] != ']'); i++);
96621495Sjmacd
967114472Sru              rep_name = (char *)xmalloc (1 + i - start);
968114472Sru              strncpy (rep_name, string + start, i - start);
969114472Sru              rep_name[i - start] = '\0';
97021495Sjmacd
971114472Sru            /* If we have only one window (because the window size was too
972114472Sru               small to split it), we have to quit help by going back one
973114472Sru               noew in the history list, not deleting the window.  */
974114472Sru              if (strcmp (rep_name, "quit-help") == 0)
975114472Sru                fun_name = help_is_only_window_p ? "history-node"
976114472Sru                                                 : "delete-window";
977114472Sru              else
978114472Sru                fun_name = rep_name;
97921495Sjmacd
980114472Sru              /* Find a key which invokes this function in the info_keymap. */
981114472Sru              command = named_function (fun_name);
98293139Sru
983114472Sru              free (rep_name);
98493139Sru
985114472Sru              /* If the internal documentation string fails, there is a
986114472Sru                 serious problem with the associated command's documentation.
987114472Sru                 We croak so that it can be fixed immediately. */
988114472Sru              if (!command)
989114472Sru                abort ();
99093139Sru
991114472Sru              if (arg)
992114472Sru                {
993114472Sru                  char *argrep, *p;
99493139Sru
995114472Sru                  argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg));
996114472Sru                  p = argrep ? strrpbrk (argrep, "0123456789-") : NULL;
997114472Sru                  if (p)
998114472Sru                    {
999114472Sru                      argstr = (char *)xmalloc (p - argrep + 21);
1000114472Sru                      strncpy (argstr, argrep, p - argrep);
1001114472Sru                      sprintf (argstr + (p - argrep), "%d", arg);
1002114472Sru                    }
1003114472Sru                  else
1004114472Sru                    command = NULL;
1005114472Sru                }
1006114472Sru              rep = command ? where_is (info_keymap, command) : NULL;
1007114472Sru              if (!rep)
1008114472Sru                rep = "N/A";
1009114472Sru              replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1;
1010114472Sru              repstr = (char *)xmalloc (replen);
1011114472Sru              repstr[0] = '\0';
1012114472Sru              if (argstr)
1013114472Sru                {
1014114472Sru                  strcat(repstr, argstr);
1015114472Sru                  strcat(repstr, " ");
1016114472Sru                  free (argstr);
1017114472Sru                }
1018114472Sru              strcat(repstr, rep);
101993139Sru
1020114472Sru              if (fmt)
1021114472Sru                {
1022114472Sru                  if (replen > max)
1023114472Sru                    replen = max;
1024114472Sru                  if (replen < min)
1025114472Sru                    replen = min;
1026114472Sru                }
1027114472Sru              if (next + replen > reslen)
1028114472Sru                {
1029114472Sru                  reslen = next + replen + 1;
1030114472Sru                  result = (char *)xrealloc (result, reslen + 1);
1031114472Sru                }
103293139Sru
1033114472Sru              if (fmt)
1034114472Sru                  sprintf (result + next, fmt, repstr);
1035114472Sru              else
1036114472Sru                  strcpy (result + next, repstr);
103793139Sru
1038114472Sru              next = strlen (result);
1039114472Sru              free (repstr);
104093139Sru
1041114472Sru              start = i;
1042114472Sru              if (string[i])
1043114472Sru                start++;
1044114472Sru            }
104593139Sru
1046114472Sru          maybe_free (fmt);
1047114472Sru        }
104821495Sjmacd    }
104921495Sjmacd  strcpy (result + next, string + start);
105021495Sjmacd  return (result);
105121495Sjmacd}
105221495Sjmacd
105321495Sjmacd/* Return a string of characters which could be typed from the keymap
105421495Sjmacd   MAP to invoke FUNCTION. */
105521495Sjmacdstatic char *where_is_rep = (char *)NULL;
105621495Sjmacdstatic int where_is_rep_index = 0;
105721495Sjmacdstatic int where_is_rep_size = 0;
105821495Sjmacd
105993139Sruchar *
1060146515Sruwhere_is (Keymap map, InfoCommand *cmd)
106121495Sjmacd{
106221495Sjmacd  char *rep;
106321495Sjmacd
106421495Sjmacd  if (!where_is_rep_size)
106521495Sjmacd    where_is_rep = (char *)xmalloc (where_is_rep_size = 100);
106621495Sjmacd  where_is_rep_index = 0;
106721495Sjmacd
106893139Sru  rep = where_is_internal (map, cmd);
106921495Sjmacd
107093139Sru  /* If it couldn't be found, return "M-x Foo" (or equivalent). */
107121495Sjmacd  if (!rep)
107221495Sjmacd    {
107321495Sjmacd      char *name;
107421495Sjmacd
107593139Sru      name = function_name (cmd);
107693139Sru      if (!name)
1077114472Sru        return NULL; /* no such function */
107821495Sjmacd
107993139Sru      rep = where_is_internal (map, InfoCmd(info_execute_command));
108093139Sru      if (!rep)
108193139Sru        return ""; /* function exists but can't be got to by user */
108221495Sjmacd
108393139Sru      sprintf (where_is_rep, "%s %s", rep, name);
108493139Sru
108521495Sjmacd      rep = where_is_rep;
108621495Sjmacd    }
108721495Sjmacd  return (rep);
108821495Sjmacd}
108921495Sjmacd
109093139Sru/* Return the printed rep of the keystrokes that invoke FUNCTION,
109193139Sru   as found in MAP, or NULL. */
109221495Sjmacdstatic char *
1093146515Sruwhere_is_internal (Keymap map, InfoCommand *cmd)
109421495Sjmacd{
109593139Sru#if defined(INFOKEY)
109693139Sru
109793139Sru  register FUNCTION_KEYSEQ *k;
109893139Sru
109993139Sru  for (k = cmd->keys; k; k = k->next)
110093139Sru    if (k->map == map)
110193139Sru      return pretty_keyseq (k->keyseq);
110293139Sru
110393139Sru  return NULL;
110493139Sru
110593139Sru#else /* !INFOKEY */
1106114472Sru  /* There is a bug in that create_internal_info_help_node calls
1107114472Sru     where_is_internal without setting where_is_rep_index to zero.  This
1108114472Sru     was found by Mandrake and reported by Thierry Vignaud
1109114472Sru     <tvignaud@mandrakesoft.com> around April 24, 2002.
1110116525Sru
1111114472Sru     I think the best fix is to make where_is_rep_index another
1112114472Sru     parameter to this recursively-called function, instead of a static
1113114472Sru     variable.  But this [!INFOKEY] branch of the code is not enabled
1114114472Sru     any more, so let's just skip the whole thing.  --karl, 28sep02.  */
111521495Sjmacd  register int i;
111656160Sru
111721495Sjmacd  /* If the function is directly invokable in MAP, return the representation
111821495Sjmacd     of that keystroke. */
111921495Sjmacd  for (i = 0; i < 256; i++)
112093139Sru    if ((map[i].type == ISFUNC) && map[i].function == cmd)
112121495Sjmacd      {
112242660Smarkm        sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
112342660Smarkm        return (where_is_rep);
112421495Sjmacd      }
112521495Sjmacd
112621495Sjmacd  /* Okay, search subsequent maps for this function. */
112721495Sjmacd  for (i = 0; i < 256; i++)
112821495Sjmacd    {
112921495Sjmacd      if (map[i].type == ISKMAP)
113042660Smarkm        {
113142660Smarkm          int saved_index = where_is_rep_index;
113242660Smarkm          char *rep;
113321495Sjmacd
113442660Smarkm          sprintf (where_is_rep + where_is_rep_index, "%s ",
113542660Smarkm                   pretty_keyname (i));
113621495Sjmacd
113742660Smarkm          where_is_rep_index = strlen (where_is_rep);
113893139Sru          rep = where_is_internal ((Keymap)map[i].function, cmd);
113921495Sjmacd
114042660Smarkm          if (rep)
114142660Smarkm            return (where_is_rep);
114221495Sjmacd
114342660Smarkm          where_is_rep_index = saved_index;
114442660Smarkm        }
114521495Sjmacd    }
114621495Sjmacd
114756160Sru  return NULL;
114893139Sru
114993139Sru#endif /* INFOKEY */
115021495Sjmacd}
115121495Sjmacd
115221495SjmacdDECLARE_INFO_COMMAND (info_where_is,
115356160Sru   _("Show what to type to execute a given command"))
115421495Sjmacd{
115521495Sjmacd  char *command_name;
115621495Sjmacd
1157146515Sru  command_name = read_function_name ((char *) _("Where is command: "), window);
115821495Sjmacd
115921495Sjmacd  if (!command_name)
116021495Sjmacd    {
116121495Sjmacd      info_abort_key (active_window, count, key);
116221495Sjmacd      return;
116321495Sjmacd    }
116421495Sjmacd
116521495Sjmacd  if (*command_name)
116621495Sjmacd    {
116793139Sru      InfoCommand *command;
116821495Sjmacd
116993139Sru      command = named_function (command_name);
117021495Sjmacd
117193139Sru      if (command)
117242660Smarkm        {
117342660Smarkm          char *location;
117421495Sjmacd
117593139Sru          location = where_is (active_window->keymap, command);
117621495Sjmacd
117793139Sru          if (!location || !location[0])
117842660Smarkm            {
1179146515Sru              info_error ((char *) _("`%s' is not on any keys"),
1180146515Sru                  command_name, NULL);
118142660Smarkm            }
118242660Smarkm          else
118342660Smarkm            {
118493139Sru              if (strstr (location, function_name (command)))
118542660Smarkm                window_message_in_echo_area
1186146515Sru                  ((char *) _("%s can only be invoked via %s."),
1187146515Sru                   command_name, location);
118842660Smarkm              else
118942660Smarkm                window_message_in_echo_area
1190146515Sru                  ((char *) _("%s can be invoked via %s."),
1191146515Sru                   command_name, location);
119242660Smarkm            }
119342660Smarkm        }
119421495Sjmacd      else
1195146515Sru        info_error ((char *) _("There is no function named `%s'"),
1196146515Sru            command_name, NULL);
119721495Sjmacd    }
119821495Sjmacd
119921495Sjmacd  free (command_name);
120021495Sjmacd}
1201