Home | History | Annotate | Line # | Download | only in info
infodoc.c revision 1.1
      1 /*	$NetBSD: infodoc.c,v 1.1 2016/01/14 00:11:29 christos Exp $	*/
      2 
      3 /* infodoc.c -- functions which build documentation nodes.
      4    Id: infodoc.c,v 1.8 2004/04/11 17:56:45 karl Exp
      5 
      6    Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software
      7    Foundation, Inc.
      8 
      9    This program is free software; you can redistribute it and/or modify
     10    it under the terms of the GNU General Public License as published by
     11    the Free Software Foundation; either version 2, or (at your option)
     12    any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program; if not, write to the Free Software
     21    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     22 
     23    Written by Brian Fox (bfox (at) ai.mit.edu). */
     24 
     25 #include "info.h"
     26 #include "funs.h"
     27 
     28 /* HELP_NODE_GETS_REGENERATED is always defined now that keys may get
     29    rebound, or other changes in the help text may occur.  */
     30 #define HELP_NODE_GETS_REGENERATED 1
     31 
     32 /* The name of the node used in the help window. */
     33 static char *info_help_nodename = "*Info Help*";
     34 
     35 /* A node containing printed key bindings and their documentation. */
     36 static NODE *internal_info_help_node = (NODE *)NULL;
     37 
     38 /* A pointer to the contents of the help node. */
     39 static char *internal_info_help_node_contents = (char *)NULL;
     40 
     41 /* The (more or less) static text which appears in the internal info
     42    help node.  The actual key bindings are inserted.  Keep the
     43    underlines (****, etc.) in the same N_ call as  the text lines they
     44    refer to, so translations can make the number of *'s or -'s match.  */
     45 #if defined(INFOKEY)
     46 
     47 static char *info_internal_help_text[] = {
     48   N_("Basic Commands in Info Windows\n\
     49 ******************************\n"),
     50   "\n",
     51   N_("\\%-10[quit-help]  Quit this help.\n"),
     52   N_("\\%-10[quit]  Quit Info altogether.\n"),
     53   N_("\\%-10[get-info-help-node]  Invoke the Info tutorial.\n"),
     54   "\n",
     55   N_("Selecting other nodes:\n\
     56 ----------------------\n"),
     57   N_("\\%-10[next-node]  Move to the \"next\" node of this node.\n"),
     58   N_("\\%-10[prev-node]  Move to the \"previous\" node of this node.\n"),
     59   N_("\\%-10[up-node]  Move \"up\" from this node.\n"),
     60   N_("\\%-10[menu-item]  Pick menu item specified by name.\n\
     61               Picking a menu item causes another node to be selected.\n"),
     62   N_("\\%-10[xref-item]  Follow a cross reference.  Reads name of reference.\n"),
     63   N_("\\%-10[history-node]  Move to the last node seen in this window.\n"),
     64   N_("\\%-10[move-to-next-xref]  Skip to next hypertext link within this node.\n"),
     65   N_("\\%-10[move-to-prev-xref]  Skip to previous hypertext link within this node.\n"),
     66   N_("\\%-10[select-reference-this-line]  Follow the hypertext link under cursor.\n"),
     67   N_("\\%-10[dir-node]  Move to the `directory' node.  Equivalent to `\\[goto-node] (DIR)'.\n"),
     68   N_("\\%-10[top-node]  Move to the Top node.  Equivalent to `\\[goto-node] Top'.\n"),
     69   "\n",
     70   N_("Moving within a node:\n\
     71 ---------------------\n"),
     72   N_("\\%-10[beginning-of-node]  Go to the beginning of this node.\n"),
     73   N_("\\%-10[end-of-node]  Go to the end of this node.\n"),
     74   N_("\\%-10[next-line]  Scroll forward 1 line.\n"),
     75   N_("\\%-10[prev-line]  Scroll backward 1 line.\n"),
     76   N_("\\%-10[scroll-forward]  Scroll forward a page.\n"),
     77   N_("\\%-10[scroll-backward]  Scroll backward a page.\n"),
     78   "\n",
     79   N_("Other commands:\n\
     80 ---------------\n"),
     81   N_("\\%-10[menu-digit]  Pick first ... ninth item in node's menu.\n"),
     82   N_("\\%-10[last-menu-item]  Pick last item in node's menu.\n"),
     83   N_("\\%-10[index-search]  Search for a specified string in the index entries of this Info\n\
     84               file, and select the node referenced by the first entry found.\n"),
     85   N_("\\%-10[goto-node]  Move to node specified by name.\n\
     86               You may include a filename as well, as in (FILENAME)NODENAME.\n"),
     87   N_("\\%-10[search]  Search forward for a specified string\n\
     88               and select the node in which the next occurrence is found.\n"),
     89   N_("\\%-10[search-backward]  Search backward for a specified string\n\
     90               and select the node in which the previous occurrence is found.\n"),
     91   NULL
     92 };
     93 
     94 #else /* !INFOKEY */
     95 
     96 static char *info_internal_help_text[] = {
     97   N_("Basic Commands in Info Windows\n\
     98 ******************************\n"),
     99   "\n",
    100   N_("  %-10s  Quit this help.\n"),
    101   N_("  %-10s  Quit Info altogether.\n"),
    102   N_("  %-10s  Invoke the Info tutorial.\n"),
    103   "\n",
    104   N_("Selecting other nodes:\n\
    105 ----------------------\n",
    106   N_("  %-10s  Move to the `next' node of this node.\n"),
    107   N_("  %-10s  Move to the `previous' node of this node.\n"),
    108   N_("  %-10s  Move `up' from this node.\n"),
    109   N_("  %-10s  Pick menu item specified by name.\n"),
    110   N_("              Picking a menu item causes another node to be selected.\n"),
    111   N_("  %-10s  Follow a cross reference.  Reads name of reference.\n"),
    112   N_("  %-10s  Move to the last node seen in this window.\n"),
    113   N_("  %-10s  Skip to next hypertext link within this node.\n"),
    114   N_("  %-10s  Follow the hypertext link under cursor.\n"),
    115   N_("  %-10s  Move to the `directory' node.  Equivalent to `g (DIR)'.\n"),
    116   N_("  %-10s  Move to the Top node.  Equivalent to `g Top'.\n"),
    117   "\n",
    118   N_("Moving within a node:\n\
    119 ---------------------\n"),
    120   N_("  %-10s  Scroll forward a page.\n"),
    121   N_("  %-10s  Scroll backward a page.\n"),
    122   N_("  %-10s  Go to the beginning of this node.\n"),
    123   N_("  %-10s  Go to the end of this node.\n"),
    124   N_("  %-10s  Scroll forward 1 line.\n"),
    125   N_("  %-10s  Scroll backward 1 line.\n"),
    126   "\n",
    127   N_("Other commands:\n\
    128 ---------------\n"),
    129   N_("  %-10s  Pick first ... ninth item in node's menu.\n"),
    130   N_("  %-10s  Pick last item in node's menu.\n"),
    131   N_("  %-10s  Search for a specified string in the index entries of this Info\n"),
    132   N_("              file, and select the node referenced by the first entry found.\n"),
    133   N_("  %-10s  Move to node specified by name.\n"),
    134   N_("              You may include a filename as well, as in (FILENAME)NODENAME.\n"),
    135   N_("  %-10s  Search forward for a specified string,\n"),
    136   N_("              and select the node in which the next occurrence is found.\n"),
    137   N_("  %-10s  Search backward for a specified string\n"),
    138   N_("              and select the node in which the next occurrence is found.\n"),
    139   NULL
    140 };
    141 
    142 static char *info_help_keys_text[][2] = {
    143   { "", "" },
    144   { "", "" },
    145   { "", "" },
    146   { "CTRL-x 0", "CTRL-x 0" },
    147   { "q", "q" },
    148   { "h", "ESC h" },
    149   { "", "" },
    150   { "", "" },
    151   { "", "" },
    152   { "SPC", "SPC" },
    153   { "DEL", "b" },
    154   { "b", "ESC b" },
    155   { "e", "ESC e" },
    156   { "ESC 1 SPC", "RET" },
    157   { "ESC 1 DEL", "y" },
    158   { "", "" },
    159   { "", "" },
    160   { "", "" },
    161   { "n", "CTRL-x n" },
    162   { "p", "CTRL-x p" },
    163   { "u", "CTRL-x u" },
    164   { "m", "ESC m" },
    165   { "", "" },
    166   { "f", "ESC f" },
    167   { "l", "l" },
    168   { "TAB", "TAB" },
    169   { "RET", "CTRL-x RET" },
    170   { "d", "ESC d" },
    171   { "t", "ESC t" },
    172   { "", "" },
    173   { "", "" },
    174   { "", "" },
    175   { "1-9", "ESC 1-9" },
    176   { "0", "ESC 0" },
    177   { "i", "CTRL-x i" },
    178   { "", "" },
    179   { "g", "CTRL-x g" },
    180   { "", "" },
    181   { "s", "/" },
    182   { "", "" },
    183   { "ESC - s", "?" },
    184   { "", "" },
    185   NULL
    186 };
    187 
    188 #endif /* !INFOKEY */
    189 
    190 static char *where_is_internal (Keymap map, InfoCommand *cmd);
    191 
    192 void
    193 dump_map_to_message_buffer (char *prefix, Keymap map)
    194 {
    195   register int i;
    196   unsigned prefix_len = strlen (prefix);
    197   char *new_prefix = (char *)xmalloc (prefix_len + 2);
    198 
    199   strncpy (new_prefix, prefix, prefix_len);
    200   new_prefix[prefix_len + 1] = '\0';
    201 
    202   for (i = 0; i < 256; i++)
    203     {
    204       new_prefix[prefix_len] = i;
    205       if (map[i].type == ISKMAP)
    206         {
    207           dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function);
    208         }
    209       else if (map[i].function)
    210         {
    211           register int last;
    212           char *doc, *name;
    213 
    214           doc = function_documentation (map[i].function);
    215           name = function_name (map[i].function);
    216 
    217           if (!*doc)
    218             continue;
    219 
    220           /* Find out if there is a series of identical functions, as in
    221              ea_insert (). */
    222           for (last = i + 1; last < 256; last++)
    223             if ((map[last].type != ISFUNC) ||
    224                 (map[last].function != map[i].function))
    225               break;
    226 
    227           if (last - 1 != i)
    228             {
    229               printf_to_message_buffer ("%s .. ", pretty_keyseq (new_prefix),
    230                   NULL, NULL);
    231               new_prefix[prefix_len] = last - 1;
    232               printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
    233                   NULL, NULL);
    234               i = last - 1;
    235             }
    236           else
    237             printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
    238                 NULL, NULL);
    239 
    240 #if defined (NAMED_FUNCTIONS)
    241           /* Print the name of the function, and some padding before the
    242              documentation string is printed. */
    243           {
    244             int length_so_far;
    245             int desired_doc_start = 40; /* Must be multiple of 8. */
    246 
    247             printf_to_message_buffer ("(%s)", name, NULL, NULL);
    248             length_so_far = message_buffer_length_this_line ();
    249 
    250             if ((desired_doc_start + strlen (doc))
    251                 >= (unsigned int) the_screen->width)
    252               printf_to_message_buffer ("\n     ", NULL, NULL, NULL);
    253             else
    254               {
    255                 while (length_so_far < desired_doc_start)
    256                   {
    257                     printf_to_message_buffer ("\t", NULL, NULL, NULL);
    258                     length_so_far += character_width ('\t', length_so_far);
    259                   }
    260               }
    261           }
    262 #endif /* NAMED_FUNCTIONS */
    263           printf_to_message_buffer ("%s\n", doc, NULL, NULL);
    264         }
    265     }
    266   free (new_prefix);
    267 }
    268 
    269 /* How to create internal_info_help_node.  HELP_IS_ONLY_WINDOW_P says
    270    whether we're going to end up in a second (or more) window of our
    271    own, or whether there's only one window and we're going to usurp it.
    272    This determines how to quit the help window.  Maybe we should just
    273    make q do the right thing in both cases.  */
    274 
    275 static void
    276 create_internal_info_help_node (int help_is_only_window_p)
    277 {
    278   register int i;
    279   NODE *node;
    280   char *contents = NULL;
    281   char *exec_keys;
    282 
    283 #ifndef HELP_NODE_GETS_REGENERATED
    284   if (internal_info_help_node_contents)
    285     contents = internal_info_help_node_contents;
    286 #endif /* !HELP_NODE_GETS_REGENERATED */
    287 
    288   if (!contents)
    289     {
    290       int printed_one_mx = 0;
    291 
    292       initialize_message_buffer ();
    293 
    294       for (i = 0; info_internal_help_text[i]; i++)
    295         {
    296 #ifdef INFOKEY
    297           printf_to_message_buffer (replace_in_documentation
    298               ((char *) _(info_internal_help_text[i]), help_is_only_window_p),
    299               NULL, NULL, NULL);
    300 #else
    301           /* Don't translate blank lines, gettext outputs the po file
    302              header in that case.  We want a blank line.  */
    303           char *msg = *(info_internal_help_text[i])
    304                       ? _(info_internal_help_text[i])
    305                       : info_internal_help_text[i];
    306           char *key = info_help_keys_text[i][vi_keys_p];
    307 
    308           /* If we have only one window (because the window size was too
    309              small to split it), CTRL-x 0 doesn't work to `quit' help.  */
    310           if (STREQ (key, "CTRL-x 0") && help_is_only_window_p)
    311             key = "l";
    312 
    313           printf_to_message_buffer (msg, key, NULL, NULL);
    314 #endif /* !INFOKEY */
    315         }
    316 
    317       printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
    318       printf_to_message_buffer ((char *) _("The current search path is:\n"),
    319           NULL, NULL, NULL);
    320       printf_to_message_buffer ("  %s\n", infopath, NULL, NULL);
    321       printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
    322       printf_to_message_buffer ((char *) _("Commands available in Info windows:\n\n"),
    323           NULL, NULL, NULL);
    324       dump_map_to_message_buffer ("", info_keymap);
    325       printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
    326       printf_to_message_buffer ((char *) _("Commands available in the echo area:\n\n"),
    327           NULL, NULL, NULL);
    328       dump_map_to_message_buffer ("", echo_area_keymap);
    329 
    330 #if defined (NAMED_FUNCTIONS)
    331       /* Get a list of commands which have no keystroke equivs. */
    332       exec_keys = where_is (info_keymap, InfoCmd(info_execute_command));
    333       if (exec_keys)
    334         exec_keys = xstrdup (exec_keys);
    335       for (i = 0; function_doc_array[i].func; i++)
    336         {
    337           InfoCommand *cmd = DocInfoCmd(&function_doc_array[i]);
    338 
    339           if (InfoFunction(cmd) != (VFunction *) info_do_lowercase_version
    340               && !where_is_internal (info_keymap, cmd)
    341               && !where_is_internal (echo_area_keymap, cmd))
    342             {
    343               if (!printed_one_mx)
    344                 {
    345                   printf_to_message_buffer ("---------------------\n\n",
    346                       NULL, NULL, NULL);
    347                   if (exec_keys && exec_keys[0])
    348                       printf_to_message_buffer
    349                         ((char *) _("The following commands can only be invoked via %s:\n\n"),
    350                          exec_keys, NULL, NULL);
    351                   else
    352                       printf_to_message_buffer
    353                         ((char *) _("The following commands cannot be invoked at all:\n\n"),
    354                          NULL, NULL, NULL);
    355                   printed_one_mx = 1;
    356                 }
    357 
    358               printf_to_message_buffer
    359                 ("%s %s\n     %s\n",
    360                  exec_keys,
    361                  function_doc_array[i].func_name,
    362                  replace_in_documentation (strlen (function_doc_array[i].doc)
    363                    ? (char *) _(function_doc_array[i].doc) : "", 0)
    364                 );
    365 
    366             }
    367         }
    368 
    369       if (printed_one_mx)
    370         printf_to_message_buffer ("\n", NULL, NULL, NULL);
    371 
    372       maybe_free (exec_keys);
    373 #endif /* NAMED_FUNCTIONS */
    374 
    375       printf_to_message_buffer
    376         ("%s", replace_in_documentation
    377          ((char *) _("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"), 0),
    378          NULL, NULL);
    379       node = message_buffer_to_node ();
    380       internal_info_help_node_contents = node->contents;
    381     }
    382   else
    383     {
    384       /* We already had the right contents, so simply use them. */
    385       node = build_message_node ("", 0, 0);
    386       free (node->contents);
    387       node->contents = contents;
    388       node->nodelen = 1 + strlen (contents);
    389     }
    390 
    391   internal_info_help_node = node;
    392 
    393   /* Do not GC this node's contents.  It never changes, and we never need
    394      to delete it once it is made.  If you change some things (such as
    395      placing information about dynamic variables in the help text) then
    396      you will need to allow the contents to be gc'd, and you will have to
    397      arrange to always regenerate the help node. */
    398 #if defined (HELP_NODE_GETS_REGENERATED)
    399   add_gcable_pointer (internal_info_help_node->contents);
    400 #endif
    401 
    402   name_internal_node (internal_info_help_node, info_help_nodename);
    403 
    404   /* Even though this is an internal node, we don't want the window
    405      system to treat it specially.  So we turn off the internalness
    406      of it here. */
    407   internal_info_help_node->flags &= ~N_IsInternal;
    408 }
    409 
    410 /* Return a window which is the window showing help in this Info. */
    411 
    412 /* If the eligible window's height is >= this, split it to make the help
    413    window.  Otherwise display the help window in the current window.  */
    414 #define HELP_SPLIT_SIZE 24
    415 
    416 static WINDOW *
    417 info_find_or_create_help_window (void)
    418 {
    419   int help_is_only_window_p;
    420   WINDOW *eligible = NULL;
    421   WINDOW *help_window = get_window_of_node (internal_info_help_node);
    422 
    423   /* If we couldn't find the help window, then make it. */
    424   if (!help_window)
    425     {
    426       WINDOW *window;
    427       int max = 0;
    428 
    429       for (window = windows; window; window = window->next)
    430         {
    431           if (window->height > max)
    432             {
    433               max = window->height;
    434               eligible = window;
    435             }
    436         }
    437 
    438       if (!eligible)
    439         return NULL;
    440     }
    441 #ifndef HELP_NODE_GETS_REGENERATED
    442   else
    443     /* help window is static, just return it.  */
    444     return help_window;
    445 #endif /* not HELP_NODE_GETS_REGENERATED */
    446 
    447   /* Make sure that we have a node containing the help text.  The
    448      argument is false if help will be the only window (so l must be used
    449      to quit help), true if help will be one of several visible windows
    450      (so CTRL-x 0 must be used to quit help).  */
    451   help_is_only_window_p = ((help_window && !windows->next)
    452         || (!help_window && eligible->height < HELP_SPLIT_SIZE));
    453   create_internal_info_help_node (help_is_only_window_p);
    454 
    455   /* Either use the existing window to display the help node, or create
    456      a new window if there was no existing help window. */
    457   if (!help_window)
    458     { /* Split the largest window into 2 windows, and show the help text
    459          in that window. */
    460       if (eligible->height >= HELP_SPLIT_SIZE)
    461         {
    462           active_window = eligible;
    463           help_window = window_make_window (internal_info_help_node);
    464         }
    465       else
    466         {
    467           set_remembered_pagetop_and_point (active_window);
    468           window_set_node_of_window (active_window, internal_info_help_node);
    469           help_window = active_window;
    470         }
    471     }
    472   else
    473     { /* Case where help node always gets regenerated, and we have an
    474          existing window in which to place the node. */
    475       if (active_window != help_window)
    476         {
    477           set_remembered_pagetop_and_point (active_window);
    478           active_window = help_window;
    479         }
    480       window_set_node_of_window (active_window, internal_info_help_node);
    481     }
    482   remember_window_and_node (help_window, help_window->node);
    483   return help_window;
    484 }
    485 
    486 /* Create or move to the help window. */
    487 DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message"))
    488 {
    489   WINDOW *help_window;
    490 
    491   help_window = info_find_or_create_help_window ();
    492   if (help_window)
    493     {
    494       active_window = help_window;
    495       active_window->flags |= W_UpdateWindow;
    496     }
    497   else
    498     {
    499       info_error ((char *) msg_cant_make_help, NULL, NULL);
    500     }
    501 }
    502 
    503 /* Show the Info help node.  This means that the "info" file is installed
    504    where it can easily be found on your system. */
    505 DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'"))
    506 {
    507   NODE *node;
    508   char *nodename;
    509 
    510   /* If there is a window on the screen showing the node "(info)Help" or
    511      the node "(info)Help-Small-Screen", simply select that window. */
    512   {
    513     WINDOW *win;
    514 
    515     for (win = windows; win; win = win->next)
    516       {
    517         if (win->node && win->node->filename &&
    518             (strcasecmp
    519              (filename_non_directory (win->node->filename), "info") == 0) &&
    520             ((strcmp (win->node->nodename, "Help") == 0) ||
    521              (strcmp (win->node->nodename, "Help-Small-Screen") == 0)))
    522           {
    523             active_window = win;
    524             return;
    525           }
    526       }
    527   }
    528 
    529   /* If the current window is small, show the small screen help. */
    530   if (active_window->height < 24)
    531     nodename = "Help-Small-Screen";
    532   else
    533     nodename = "Help";
    534 
    535   /* Try to get the info file for Info. */
    536   node = info_get_node ("Info", nodename);
    537 
    538   if (!node)
    539     {
    540       if (info_recent_file_error)
    541         info_error (info_recent_file_error, NULL, NULL);
    542       else
    543         info_error ((char *) msg_cant_file_node, "Info", nodename);
    544     }
    545   else
    546     {
    547       /* If the current window is very large (greater than 45 lines),
    548          then split it and show the help node in another window.
    549          Otherwise, use the current window. */
    550 
    551       if (active_window->height > 45)
    552         active_window = window_make_window (node);
    553       else
    554         {
    555           set_remembered_pagetop_and_point (active_window);
    556           window_set_node_of_window (active_window, node);
    557         }
    558 
    559       remember_window_and_node (active_window, node);
    560     }
    561 }
    562 
    563 /* **************************************************************** */
    565 /*                                                                  */
    566 /*                   Groveling Info Keymaps and Docs                */
    567 /*                                                                  */
    568 /* **************************************************************** */
    569 
    570 /* Return the documentation associated with the Info command FUNCTION. */
    571 char *
    572 function_documentation (InfoCommand *cmd)
    573 {
    574   char *doc;
    575 
    576 #if defined (INFOKEY)
    577 
    578   doc = cmd->doc;
    579 
    580 #else /* !INFOKEY */
    581 
    582   register int i;
    583 
    584   for (i = 0; function_doc_array[i].func; i++)
    585     if (InfoFunction(cmd) == function_doc_array[i].func)
    586       break;
    587 
    588   doc = function_doc_array[i].func ? function_doc_array[i].doc : "";
    589 
    590 #endif /* !INFOKEY */
    591 
    592   return replace_in_documentation ((strlen (doc) == 0) ? doc : (char *) _(doc), 0);
    593 }
    594 
    595 #if defined (NAMED_FUNCTIONS)
    596 /* Return the user-visible name of the function associated with the
    597    Info command FUNCTION. */
    598 char *
    599 function_name (InfoCommand *cmd)
    600 {
    601 #if defined (INFOKEY)
    602 
    603   return cmd->func_name;
    604 
    605 #else /* !INFOKEY */
    606 
    607   register int i;
    608 
    609   for (i = 0; function_doc_array[i].func; i++)
    610     if (InfoFunction(cmd) == function_doc_array[i].func)
    611       break;
    612 
    613   return (function_doc_array[i].func_name);
    614 
    615 #endif /* !INFOKEY */
    616 }
    617 
    618 /* Return a pointer to the info command for function NAME. */
    619 InfoCommand *
    620 named_function (char *name)
    621 {
    622   register int i;
    623 
    624   for (i = 0; function_doc_array[i].func; i++)
    625     if (strcmp (function_doc_array[i].func_name, name) == 0)
    626       break;
    627 
    628   return (DocInfoCmd(&function_doc_array[i]));
    629 }
    630 #endif /* NAMED_FUNCTIONS */
    631 
    632 /* Return the documentation associated with KEY in MAP. */
    633 char *
    634 key_documentation (char key, Keymap map)
    635 {
    636   InfoCommand *function = map[key].function;
    637 
    638   if (function)
    639     return (function_documentation (function));
    640   else
    641     return ((char *)NULL);
    642 }
    643 
    644 DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
    645 {
    646   char keys[50];
    647   unsigned char keystroke;
    648   char *k = keys;
    649   Keymap map;
    650 
    651   *k = '\0';
    652   map = window->keymap;
    653 
    654   for (;;)
    655     {
    656       message_in_echo_area ((char *) _("Describe key: %s"),
    657           pretty_keyseq (keys), NULL);
    658       keystroke = info_get_input_char ();
    659       unmessage_in_echo_area ();
    660 
    661 #if !defined (INFOKEY)
    662       if (Meta_p (keystroke))
    663         {
    664           if (map[ESC].type != ISKMAP)
    665             {
    666               window_message_in_echo_area
    667               (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke)));
    668               return;
    669             }
    670 
    671           *k++ = '\e';
    672           keystroke = UnMeta (keystroke);
    673           map = (Keymap)map[ESC].function;
    674         }
    675 #endif /* !INFOKEY */
    676 
    677       /* Add the KEYSTROKE to our list. */
    678       *k++ = keystroke;
    679       *k = '\0';
    680 
    681       if (map[keystroke].function == (InfoCommand *)NULL)
    682         {
    683           message_in_echo_area ((char *) _("%s is undefined."),
    684               pretty_keyseq (keys), NULL);
    685           return;
    686         }
    687       else if (map[keystroke].type == ISKMAP)
    688         {
    689           map = (Keymap)map[keystroke].function;
    690           continue;
    691         }
    692       else
    693         {
    694           char *keyname, *message, *fundoc, *funname = "";
    695 
    696 #if defined (INFOKEY)
    697           /* If the key is bound to do-lowercase-version, but its
    698              lower-case variant is undefined, say that this key is
    699              also undefined.  This is especially important for unbound
    700              edit keys that emit an escape sequence: it's terribly
    701              confusing to see a message "Home (do-lowercase-version)"
    702              or some such when Home is unbound.  */
    703           if (InfoFunction(map[keystroke].function)
    704               == (VFunction *) info_do_lowercase_version)
    705             {
    706               unsigned char lowerkey = Meta_p(keystroke)
    707                                        ? Meta (tolower (UnMeta (keystroke)))
    708                                        : tolower (keystroke);
    709 
    710               if (map[lowerkey].function == (InfoCommand *)NULL)
    711                 {
    712                   message_in_echo_area ((char *) _("%s is undefined."),
    713                                         pretty_keyseq (keys), NULL);
    714                   return;
    715                 }
    716             }
    717 #endif
    718 
    719           keyname = pretty_keyseq (keys);
    720 
    721 #if defined (NAMED_FUNCTIONS)
    722           funname = function_name (map[keystroke].function);
    723 #endif /* NAMED_FUNCTIONS */
    724 
    725           fundoc = function_documentation (map[keystroke].function);
    726 
    727           message = (char *)xmalloc
    728             (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
    729 
    730 #if defined (NAMED_FUNCTIONS)
    731           sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
    732 #else
    733           sprintf (message, _("%s is defined to %s."), keyname, fundoc);
    734 #endif /* !NAMED_FUNCTIONS */
    735 
    736           window_message_in_echo_area ("%s", message, NULL);
    737           free (message);
    738           break;
    739         }
    740     }
    741 }
    742 
    743 /* Return the pretty printable name of a single character. */
    744 char *
    745 pretty_keyname (unsigned char key)
    746 {
    747   static char rep_buffer[30];
    748   char *rep;
    749 
    750   if (Meta_p (key))
    751     {
    752       char temp[20];
    753 
    754       rep = pretty_keyname (UnMeta (key));
    755 
    756 #if defined (INFOKEY)
    757       sprintf (temp, "M-%s", rep);
    758 #else /* !INFOKEY */
    759       sprintf (temp, "ESC %s", rep);
    760 #endif /* !INFOKEY */
    761       strcpy (rep_buffer, temp);
    762       rep = rep_buffer;
    763     }
    764   else if (Control_p (key))
    765     {
    766       switch (key)
    767         {
    768         case '\n': rep = "LFD"; break;
    769         case '\t': rep = "TAB"; break;
    770         case '\r': rep = "RET"; break;
    771         case ESC:  rep = "ESC"; break;
    772 
    773         default:
    774           sprintf (rep_buffer, "C-%c", UnControl (key));
    775           rep = rep_buffer;
    776         }
    777     }
    778   else
    779     {
    780       switch (key)
    781         {
    782         case ' ': rep = "SPC"; break;
    783         case DEL: rep = "DEL"; break;
    784         default:
    785           rep_buffer[0] = key;
    786           rep_buffer[1] = '\0';
    787           rep = rep_buffer;
    788         }
    789     }
    790   return (rep);
    791 }
    792 
    793 /* Return the pretty printable string which represents KEYSEQ. */
    794 
    795 static void pretty_keyseq_internal (char *keyseq, char *rep);
    796 
    797 char *
    798 pretty_keyseq (char *keyseq)
    799 {
    800   static char keyseq_rep[200];
    801 
    802   keyseq_rep[0] = '\0';
    803   if (*keyseq)
    804     pretty_keyseq_internal (keyseq, keyseq_rep);
    805   return (keyseq_rep);
    806 }
    807 
    808 static void
    809 pretty_keyseq_internal (char *keyseq, char *rep)
    810 {
    811   if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0)
    812     {
    813       strcpy(rep, "PgUp");
    814       keyseq += strlen(term_kP);
    815     }
    816   else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0)
    817     {
    818       strcpy(rep, "PgDn");
    819       keyseq += strlen(term_kN);
    820     }
    821 #if defined(INFOKEY)
    822   else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0)
    823     {
    824       strcpy(rep, "Home");
    825       keyseq += strlen(term_kh);
    826     }
    827   else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0)
    828     {
    829       strcpy(rep, "End");
    830       keyseq += strlen(term_ke);
    831     }
    832   else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0)
    833     {
    834       strcpy(rep, "INS");
    835       keyseq += strlen(term_ki);
    836     }
    837   else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0)
    838     {
    839       strcpy(rep, "DEL");
    840       keyseq += strlen(term_kx);
    841     }
    842 #endif /* INFOKEY */
    843   else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0)
    844     {
    845       strcpy(rep, "Up");
    846       keyseq += strlen(term_ku);
    847     }
    848   else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0)
    849     {
    850       strcpy(rep, "Down");
    851       keyseq += strlen(term_kd);
    852     }
    853   else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0)
    854     {
    855       strcpy(rep, "Left");
    856       keyseq += strlen(term_kl);
    857     }
    858   else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0)
    859     {
    860       strcpy(rep, "Right");
    861       keyseq += strlen(term_kr);
    862     }
    863   else
    864     {
    865       strcpy (rep, pretty_keyname (keyseq[0]));
    866       keyseq++;
    867     }
    868   if (*keyseq)
    869     {
    870       strcat (rep, " ");
    871       pretty_keyseq_internal (keyseq, rep + strlen(rep));
    872     }
    873 }
    874 
    875 /* Return a pointer to the last character in s that is found in f. */
    876 static char *
    877 strrpbrk (const char *s, const char *f)
    878 {
    879   register const char *e = s + strlen(s);
    880   register const char *t;
    881 
    882   while (e-- != s)
    883     {
    884       for (t = f; *t; t++)
    885         if (*e == *t)
    886           return (char *)e;
    887     }
    888   return NULL;
    889 }
    890 
    891 /* Replace the names of functions with the key that invokes them. */
    892 char *
    893 replace_in_documentation (char *string, int help_is_only_window_p)
    894 {
    895   unsigned reslen = strlen (string);
    896   register int i, start, next;
    897   static char *result = (char *)NULL;
    898 
    899   maybe_free (result);
    900   result = (char *)xmalloc (1 + reslen);
    901 
    902   i = next = start = 0;
    903 
    904   /* Skip to the beginning of a replaceable function. */
    905   for (i = start; string[i]; i++)
    906     {
    907       int j = i + 1;
    908 
    909       /* Is this the start of a replaceable function name? */
    910       if (string[i] == '\\')
    911         {
    912           char *fmt = NULL;
    913           unsigned min = 0;
    914           unsigned max = 0;
    915 
    916           if(string[j] == '%')
    917             {
    918               if (string[++j] == '-')
    919                 j++;
    920               if (isdigit(string[j]))
    921                 {
    922                   min = atoi(string + j);
    923                   while (isdigit(string[j]))
    924                     j++;
    925                   if (string[j] == '.' && isdigit(string[j + 1]))
    926                     {
    927                       j += 1;
    928                       max = atoi(string + j);
    929                       while (isdigit(string[j]))
    930                         j++;
    931                     }
    932                   fmt = (char *)xmalloc (j - i + 2);
    933                   strncpy (fmt, string + i + 1, j - i);
    934                   fmt[j - i - 1] = 's';
    935                   fmt[j - i] = '\0';
    936                 }
    937               else
    938                 j = i + 1;
    939             }
    940           if (string[j] == '[')
    941             {
    942               unsigned arg = 0;
    943               char *argstr = NULL;
    944               char *rep_name, *fun_name, *rep;
    945               InfoCommand *command;
    946               char *repstr = NULL;
    947               unsigned replen;
    948 
    949               /* Copy in the old text. */
    950               strncpy (result + next, string + start, i - start);
    951               next += (i - start);
    952               start = j + 1;
    953 
    954               /* Look for an optional numeric arg. */
    955               i = start;
    956               if (isdigit(string[i])
    957                   || (string[i] == '-' && isdigit(string[i + 1])) )
    958                 {
    959                   arg = atoi(string + i);
    960                   if (string[i] == '-')
    961                     i++;
    962                   while (isdigit(string[i]))
    963                     i++;
    964                 }
    965               start = i;
    966 
    967               /* Move to the end of the function name. */
    968               for (i = start; string[i] && (string[i] != ']'); i++);
    969 
    970               rep_name = (char *)xmalloc (1 + i - start);
    971               strncpy (rep_name, string + start, i - start);
    972               rep_name[i - start] = '\0';
    973 
    974             /* If we have only one window (because the window size was too
    975                small to split it), we have to quit help by going back one
    976                noew in the history list, not deleting the window.  */
    977               if (strcmp (rep_name, "quit-help") == 0)
    978                 fun_name = help_is_only_window_p ? "history-node"
    979                                                  : "delete-window";
    980               else
    981                 fun_name = rep_name;
    982 
    983               /* Find a key which invokes this function in the info_keymap. */
    984               command = named_function (fun_name);
    985 
    986               free (rep_name);
    987 
    988               /* If the internal documentation string fails, there is a
    989                  serious problem with the associated command's documentation.
    990                  We croak so that it can be fixed immediately. */
    991               if (!command)
    992                 abort ();
    993 
    994               if (arg)
    995                 {
    996                   char *argrep, *p;
    997 
    998                   argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg));
    999                   p = argrep ? strrpbrk (argrep, "0123456789-") : NULL;
   1000                   if (p)
   1001                     {
   1002                       argstr = (char *)xmalloc (p - argrep + 21);
   1003                       strncpy (argstr, argrep, p - argrep);
   1004                       sprintf (argstr + (p - argrep), "%d", arg);
   1005                     }
   1006                   else
   1007                     command = NULL;
   1008                 }
   1009               rep = command ? where_is (info_keymap, command) : NULL;
   1010               if (!rep)
   1011                 rep = "N/A";
   1012               replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1;
   1013               repstr = (char *)xmalloc (replen);
   1014               repstr[0] = '\0';
   1015               if (argstr)
   1016                 {
   1017                   strcat(repstr, argstr);
   1018                   strcat(repstr, " ");
   1019                   free (argstr);
   1020                 }
   1021               strcat(repstr, rep);
   1022 
   1023               if (fmt)
   1024                 {
   1025                   if (replen > max)
   1026                     replen = max;
   1027                   if (replen < min)
   1028                     replen = min;
   1029                 }
   1030               if (next + replen > reslen)
   1031                 {
   1032                   reslen = next + replen + 1;
   1033                   result = (char *)xrealloc (result, reslen + 1);
   1034                 }
   1035 
   1036               if (fmt)
   1037                   sprintf (result + next, fmt, repstr);
   1038               else
   1039                   strcpy (result + next, repstr);
   1040 
   1041               next = strlen (result);
   1042               free (repstr);
   1043 
   1044               start = i;
   1045               if (string[i])
   1046                 start++;
   1047             }
   1048 
   1049           maybe_free (fmt);
   1050         }
   1051     }
   1052   strcpy (result + next, string + start);
   1053   return (result);
   1054 }
   1055 
   1056 /* Return a string of characters which could be typed from the keymap
   1057    MAP to invoke FUNCTION. */
   1058 static char *where_is_rep = (char *)NULL;
   1059 static int where_is_rep_index = 0;
   1060 static int where_is_rep_size = 0;
   1061 
   1062 char *
   1063 where_is (Keymap map, InfoCommand *cmd)
   1064 {
   1065   char *rep;
   1066 
   1067   if (!where_is_rep_size)
   1068     where_is_rep = (char *)xmalloc (where_is_rep_size = 100);
   1069   where_is_rep_index = 0;
   1070 
   1071   rep = where_is_internal (map, cmd);
   1072 
   1073   /* If it couldn't be found, return "M-x Foo" (or equivalent). */
   1074   if (!rep)
   1075     {
   1076       char *name;
   1077 
   1078       name = function_name (cmd);
   1079       if (!name)
   1080         return NULL; /* no such function */
   1081 
   1082       rep = where_is_internal (map, InfoCmd(info_execute_command));
   1083       if (!rep)
   1084         return ""; /* function exists but can't be got to by user */
   1085 
   1086       sprintf (where_is_rep, "%s %s", rep, name);
   1087 
   1088       rep = where_is_rep;
   1089     }
   1090   return (rep);
   1091 }
   1092 
   1093 /* Return the printed rep of the keystrokes that invoke FUNCTION,
   1094    as found in MAP, or NULL. */
   1095 static char *
   1096 where_is_internal (Keymap map, InfoCommand *cmd)
   1097 {
   1098 #if defined(INFOKEY)
   1099 
   1100   register FUNCTION_KEYSEQ *k;
   1101 
   1102   for (k = cmd->keys; k; k = k->next)
   1103     if (k->map == map)
   1104       return pretty_keyseq (k->keyseq);
   1105 
   1106   return NULL;
   1107 
   1108 #else /* !INFOKEY */
   1109   /* There is a bug in that create_internal_info_help_node calls
   1110      where_is_internal without setting where_is_rep_index to zero.  This
   1111      was found by Mandrake and reported by Thierry Vignaud
   1112      <tvignaud (at) mandrakesoft.com> around April 24, 2002.
   1113 
   1114      I think the best fix is to make where_is_rep_index another
   1115      parameter to this recursively-called function, instead of a static
   1116      variable.  But this [!INFOKEY] branch of the code is not enabled
   1117      any more, so let's just skip the whole thing.  --karl, 28sep02.  */
   1118   register int i;
   1119 
   1120   /* If the function is directly invokable in MAP, return the representation
   1121      of that keystroke. */
   1122   for (i = 0; i < 256; i++)
   1123     if ((map[i].type == ISFUNC) && map[i].function == cmd)
   1124       {
   1125         sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
   1126         return (where_is_rep);
   1127       }
   1128 
   1129   /* Okay, search subsequent maps for this function. */
   1130   for (i = 0; i < 256; i++)
   1131     {
   1132       if (map[i].type == ISKMAP)
   1133         {
   1134           int saved_index = where_is_rep_index;
   1135           char *rep;
   1136 
   1137           sprintf (where_is_rep + where_is_rep_index, "%s ",
   1138                    pretty_keyname (i));
   1139 
   1140           where_is_rep_index = strlen (where_is_rep);
   1141           rep = where_is_internal ((Keymap)map[i].function, cmd);
   1142 
   1143           if (rep)
   1144             return (where_is_rep);
   1145 
   1146           where_is_rep_index = saved_index;
   1147         }
   1148     }
   1149 
   1150   return NULL;
   1151 
   1152 #endif /* INFOKEY */
   1153 }
   1154 
   1155 DECLARE_INFO_COMMAND (info_where_is,
   1156    _("Show what to type to execute a given command"))
   1157 {
   1158   char *command_name;
   1159 
   1160   command_name = read_function_name ((char *) _("Where is command: "), window);
   1161 
   1162   if (!command_name)
   1163     {
   1164       info_abort_key (active_window, count, key);
   1165       return;
   1166     }
   1167 
   1168   if (*command_name)
   1169     {
   1170       InfoCommand *command;
   1171 
   1172       command = named_function (command_name);
   1173 
   1174       if (command)
   1175         {
   1176           char *location;
   1177 
   1178           location = where_is (active_window->keymap, command);
   1179 
   1180           if (!location || !location[0])
   1181             {
   1182               info_error ((char *) _("`%s' is not on any keys"),
   1183                   command_name, NULL);
   1184             }
   1185           else
   1186             {
   1187               if (strstr (location, function_name (command)))
   1188                 window_message_in_echo_area
   1189                   ((char *) _("%s can only be invoked via %s."),
   1190                    command_name, location);
   1191               else
   1192                 window_message_in_echo_area
   1193                   ((char *) _("%s can be invoked via %s."),
   1194                    command_name, location);
   1195             }
   1196         }
   1197       else
   1198         info_error ((char *) _("There is no function named `%s'"),
   1199             command_name, NULL);
   1200     }
   1201 
   1202   free (command_name);
   1203 }
   1204