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