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