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