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. */
31static char *info_help_nodename = "*Info Help*";
32
33/* A node containing printed key bindings and their documentation. */
34static NODE *internal_info_help_node = (NODE *)NULL;
35
36/* A pointer to the contents of the help node. */
37static 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
45static 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
94static 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
140static 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
188static char *where_is_internal (Keymap map, InfoCommand *cmd);
189
190void
191dump_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
273static void
274create_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
414static WINDOW *
415info_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. */
485DECLARE_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. */
503DECLARE_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
561/* **************************************************************** */
562/*                                                                  */
563/*                   Groveling Info Keymaps and Docs                */
564/*                                                                  */
565/* **************************************************************** */
566
567/* Return the documentation associated with the Info command FUNCTION. */
568char *
569function_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. */
595char *
596function_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. */
616InfoCommand *
617named_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. */
630char *
631key_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
641DECLARE_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. */
741char *
742pretty_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
792static void pretty_keyseq_internal (char *keyseq, char *rep);
793
794char *
795pretty_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
805static void
806pretty_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. */
873static char *
874strrpbrk (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. */
889char *
890replace_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. */
1055static char *where_is_rep = (char *)NULL;
1056static int where_is_rep_index = 0;
1057static int where_is_rep_size = 0;
1058
1059char *
1060where_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. */
1092static char *
1093where_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
1152DECLARE_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}
1201