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