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