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