1 /* infodoc.c -- functions which build documentation nodes.
2 $Id: infodoc.c,v 1.26 2008/06/11 09:55:42 gray Exp $
4 Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2006,
5 2007, 2008 Free Software Foundation, Inc.
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.
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.
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/>.
20 Written by Brian Fox (bfox@ai.mit.edu). */
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
29 /* The name of the node used in the help window. */
30 static char *info_help_nodename = "*Info Help*";
32 /* A node containing printed key bindings and their documentation. */
33 static NODE *internal_info_help_node = NULL;
35 /* A pointer to the contents of the help node. */
36 static char *internal_info_help_node_contents = NULL;
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. */
44 static char *info_internal_help_text[] = {
45 N_("Basic Info command keys\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"),
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"),
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"),
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"),
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"),
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"),
88 static char *info_internal_help_text[] = {
89 N_("Basic Commands in Info Windows\n\
90 ******************************\n"),
92 N_(" %-10s Quit this help.\n"),
93 N_(" %-10s Quit Info altogether.\n"),
94 N_(" %-10s Invoke the Info tutorial.\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"),
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"),
119 N_("Other commands:\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"),
136 static char *info_help_keys_text[][2] = {
140 { "CTRL-x 0", "CTRL-x 0" },
150 { "ESC 1 SPC", "RET" },
151 { "ESC 1 DEL", "y" },
163 { "RET", "CTRL-x RET" },
169 { "1-9", "ESC 1-9" },
182 #endif /* !INFOKEY */
184 static char *where_is_internal (Keymap map, InfoCommand *cmd);
187 dump_map_to_message_buffer (char *prefix, Keymap map)
190 unsigned prefix_len = strlen (prefix);
191 char *new_prefix = xmalloc (prefix_len + 2);
193 strncpy (new_prefix, prefix, prefix_len);
194 new_prefix[prefix_len + 1] = '\0';
196 for (i = 0; i < 256; i++)
198 new_prefix[prefix_len] = i;
199 if (map[i].type == ISKMAP)
201 dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function);
203 else if (map[i].function)
208 doc = function_documentation (map[i].function);
209 name = function_name (map[i].function);
214 /* Find out if there is a series of identical functions, as in
216 for (last = i + 1; last < 256; last++)
217 if ((map[last].type != ISFUNC) ||
218 (map[last].function != map[i].function))
223 printf_to_message_buffer ("%s .. ", pretty_keyseq (new_prefix),
225 new_prefix[prefix_len] = last - 1;
226 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
231 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
234 #if defined (NAMED_FUNCTIONS)
235 /* Print the name of the function, and some padding before the
236 documentation string is printed. */
239 int desired_doc_start = 40; /* Must be multiple of 8. */
241 printf_to_message_buffer ("(%s)", name, NULL, NULL);
242 length_so_far = message_buffer_length_this_line ();
244 if ((desired_doc_start + strlen (doc))
245 >= (unsigned int) the_screen->width)
246 printf_to_message_buffer ("\n ", NULL, NULL, NULL);
249 while (length_so_far < desired_doc_start)
251 printf_to_message_buffer ("\t", NULL, NULL, NULL);
252 length_so_far += character_width ('\t', length_so_far);
256 #endif /* NAMED_FUNCTIONS */
257 printf_to_message_buffer ("%s\n", doc, NULL, NULL);
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. */
270 create_internal_info_help_node (int help_is_only_window_p)
274 char *contents = NULL;
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 */
284 int printed_one_mx = 0;
286 initialize_message_buffer ();
288 for (i = 0; info_internal_help_text[i]; i++)
291 printf_to_message_buffer (replace_in_documentation
292 (_(info_internal_help_text[i]), help_is_only_window_p),
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];
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)
307 printf_to_message_buffer (msg, key, NULL, NULL);
308 #endif /* !INFOKEY */
311 printf_to_message_buffer ("---------------------\n", NULL, NULL, NULL);
312 printf_to_message_buffer (_("The current search path is:\n"),
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"),
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"),
322 dump_map_to_message_buffer ("", echo_area_keymap);
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));
328 exec_keys = xstrdup (exec_keys);
329 for (i = 0; function_doc_array[i].func; i++)
331 InfoCommand *cmd = DocInfoCmd(&function_doc_array[i]);
333 if (InfoFunction(cmd) != (VFunction *) info_do_lowercase_version
334 && !where_is_internal (info_keymap, cmd)
335 && !where_is_internal (echo_area_keymap, cmd))
339 printf_to_message_buffer ("---------------------\n\n",
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);
346 printf_to_message_buffer
347 (_("The following commands cannot be invoked at all:\n\n"),
352 printf_to_message_buffer
355 function_doc_array[i].func_name,
356 replace_in_documentation (strlen (function_doc_array[i].doc)
357 ? _(function_doc_array[i].doc) : "", 0)
364 printf_to_message_buffer ("\n", NULL, NULL, NULL);
366 maybe_free (exec_keys);
367 #endif /* NAMED_FUNCTIONS */
369 node = message_buffer_to_node ();
370 internal_info_help_node_contents = node->contents;
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);
381 internal_info_help_node = node;
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);
392 name_internal_node (internal_info_help_node, info_help_nodename);
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
397 internal_info_help_node->flags &= ~N_IsInternal;
400 /* Return a window which is the window showing help in this Info. */
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
407 info_find_or_create_help_window (void)
409 int help_is_only_window_p;
410 WINDOW *eligible = NULL;
411 WINDOW *help_window = get_window_of_node (internal_info_help_node);
413 /* If we couldn't find the help window, then make it. */
419 for (window = windows; window; window = window->next)
421 if (window->height > max)
423 max = window->height;
431 #ifndef HELP_NODE_GETS_REGENERATED
433 /* help window is static, just return it. */
435 #endif /* not HELP_NODE_GETS_REGENERATED */
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);
445 /* Either use the existing window to display the help node, or create
446 a new window if there was no existing help window. */
448 { /* Split the largest window into 2 windows, and show the help text
450 if (eligible->height >= HELP_SPLIT_SIZE)
452 active_window = eligible;
453 help_window = window_make_window (internal_info_help_node);
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;
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)
467 set_remembered_pagetop_and_point (active_window);
468 active_window = help_window;
470 window_set_node_of_window (active_window, internal_info_help_node);
472 remember_window_and_node (help_window, help_window->node);
476 /* Create or move to the help window. */
477 DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message"))
481 help_window = info_find_or_create_help_window ();
484 active_window = help_window;
485 active_window->flags |= W_UpdateWindow;
489 info_error (msg_cant_make_help, NULL, NULL);
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'"))
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. */
505 for (win = windows; win; win = win->next)
507 if (win->node && win->node->filename &&
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)))
519 /* If the current window is small, show the small screen help. */
520 if (active_window->height < 24)
521 nodename = "Help-Small-Screen";
525 /* Try to get the info file for Info. */
526 node = info_get_node ("Info", nodename);
530 if (info_recent_file_error)
531 info_error (info_recent_file_error, NULL, NULL);
533 info_error (msg_cant_file_node, "Info", nodename);
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. */
541 if (active_window->height > 45)
542 active_window = window_make_window (node);
545 set_remembered_pagetop_and_point (active_window);
546 window_set_node_of_window (active_window, node);
549 remember_window_and_node (active_window, node);
553 /* **************************************************************** */
555 /* Groveling Info Keymaps and Docs */
557 /* **************************************************************** */
559 /* Return the documentation associated with the Info command FUNCTION. */
561 function_documentation (InfoCommand *cmd)
565 #if defined (INFOKEY)
573 for (i = 0; function_doc_array[i].func; i++)
574 if (InfoFunction(cmd) == function_doc_array[i].func)
577 doc = function_doc_array[i].func ? function_doc_array[i].doc : "";
579 #endif /* !INFOKEY */
581 return replace_in_documentation ((strlen (doc) == 0) ? doc : _(doc), 0);
584 #if defined (NAMED_FUNCTIONS)
585 /* Return the user-visible name of the function associated with the
586 Info command FUNCTION. */
588 function_name (InfoCommand *cmd)
590 #if defined (INFOKEY)
592 return cmd->func_name;
598 for (i = 0; function_doc_array[i].func; i++)
599 if (InfoFunction(cmd) == function_doc_array[i].func)
602 return function_doc_array[i].func_name;
604 #endif /* !INFOKEY */
607 /* Return a pointer to the info command for function NAME. */
609 named_function (char *name)
613 for (i = 0; function_doc_array[i].func; i++)
614 if (strcmp (function_doc_array[i].func_name, name) == 0)
617 return DocInfoCmd(&function_doc_array[i]);
619 #endif /* NAMED_FUNCTIONS */
621 /* Return the documentation associated with KEY in MAP. */
623 key_documentation (char key, Keymap map)
625 InfoCommand *function = map[key].function;
628 return function_documentation (function);
633 DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
636 unsigned char keystroke;
641 map = window->keymap;
645 message_in_echo_area (_("Describe key: %s"),
646 pretty_keyseq (keys), NULL);
647 keystroke = info_get_input_char ();
648 unmessage_in_echo_area ();
650 #if !defined (INFOKEY)
651 if (Meta_p (keystroke))
653 if (map[ESC].type != ISKMAP)
655 window_message_in_echo_area
656 (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke)));
661 keystroke = UnMeta (keystroke);
662 map = (Keymap)map[ESC].function;
664 #endif /* !INFOKEY */
666 /* Add the KEYSTROKE to our list. */
670 if (map[keystroke].function == NULL)
672 message_in_echo_area (_("%s is undefined."),
673 pretty_keyseq (keys), NULL);
676 else if (map[keystroke].type == ISKMAP)
678 map = (Keymap)map[keystroke].function;
683 char *keyname, *message, *fundoc, *funname = "";
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)
695 unsigned char lowerkey = Meta_p(keystroke)
696 ? Meta (tolower (UnMeta (keystroke)))
697 : tolower (keystroke);
699 if (map[lowerkey].function == NULL)
701 message_in_echo_area (_("%s is undefined."),
702 pretty_keyseq (keys), NULL);
708 keyname = pretty_keyseq (keys);
710 #if defined (NAMED_FUNCTIONS)
711 funname = function_name (map[keystroke].function);
712 #endif /* NAMED_FUNCTIONS */
714 fundoc = function_documentation (map[keystroke].function);
717 (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
719 #if defined (NAMED_FUNCTIONS)
720 sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
722 sprintf (message, _("%s is defined to %s."), keyname, fundoc);
723 #endif /* !NAMED_FUNCTIONS */
725 window_message_in_echo_area ("%s", message, NULL);
732 /* Return the pretty printable name of a single character. */
734 pretty_keyname (unsigned char key)
736 static char rep_buffer[30];
743 rep = pretty_keyname (UnMeta (key));
745 #if defined (INFOKEY)
746 sprintf (temp, "M-%s", rep);
748 sprintf (temp, "ESC %s", rep);
749 #endif /* !INFOKEY */
750 strcpy (rep_buffer, temp);
753 else if (Control_p (key))
757 case '\n': rep = "LFD"; break;
758 case '\t': rep = "TAB"; break;
759 case '\r': rep = "RET"; break;
760 case ESC: rep = "ESC"; break;
763 sprintf (rep_buffer, "C-%c", UnControl (key));
771 case ' ': rep = "SPC"; break;
772 case DEL: rep = "DEL"; break;
775 rep_buffer[1] = '\0';
782 /* Return the pretty printable string which represents KEYSEQ. */
784 static void pretty_keyseq_internal (char *keyseq, char *rep);
787 pretty_keyseq (char *keyseq)
789 static char keyseq_rep[200];
791 keyseq_rep[0] = '\0';
793 pretty_keyseq_internal (keyseq, keyseq_rep);
798 pretty_keyseq_internal (char *keyseq, char *rep)
800 if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0)
803 keyseq += strlen(term_kP);
805 else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0)
808 keyseq += strlen(term_kN);
811 else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0)
814 keyseq += strlen(term_kh);
816 else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0)
819 keyseq += strlen(term_ke);
821 else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0)
824 keyseq += strlen(term_ki);
826 else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0)
829 keyseq += strlen(term_kx);
832 else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0)
835 keyseq += strlen(term_ku);
837 else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0)
840 keyseq += strlen(term_kd);
842 else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0)
845 keyseq += strlen(term_kl);
847 else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0)
849 strcpy(rep, "Right");
850 keyseq += strlen(term_kr);
854 strcpy (rep, pretty_keyname (keyseq[0]));
860 pretty_keyseq_internal (keyseq, rep + strlen(rep));
864 /* Return a pointer to the last character in s that is found in f. */
866 strrpbrk (const char *s, const char *f)
868 register const char *e = s + strlen(s);
869 register const char *t;
880 /* Replace the names of functions with the key that invokes them. */
882 replace_in_documentation (const char *string, int help_is_only_window_p)
884 unsigned reslen = strlen (string);
885 register int i, start, next;
886 static char *result = NULL;
889 result = xmalloc (1 + reslen);
891 i = next = start = 0;
893 /* Skip to the beginning of a replaceable function. */
894 for (i = start; string[i]; i++)
898 /* Is this the start of a replaceable function name? */
899 if (string[i] == '\\')
907 if (string[++j] == '-')
909 if (isdigit(string[j]))
911 min = atoi(string + j);
912 while (isdigit(string[j]))
914 if (string[j] == '.' && isdigit(string[j + 1]))
917 max = atoi(string + j);
918 while (isdigit(string[j]))
921 fmt = xmalloc (j - i + 2);
922 strncpy (fmt, string + i + 1, j - i);
923 fmt[j - i - 1] = 's';
929 if (string[j] == '[')
933 char *rep_name, *fun_name, *rep;
934 InfoCommand *command;
938 /* Copy in the old text. */
939 strncpy (result + next, string + start, i - start);
943 /* Look for an optional numeric arg. */
945 if (isdigit(string[i])
946 || (string[i] == '-' && isdigit(string[i + 1])) )
948 arg = atoi(string + i);
949 if (string[i] == '-')
951 while (isdigit(string[i]))
956 /* Move to the end of the function name. */
957 for (i = start; string[i] && (string[i] != ']'); i++);
959 rep_name = xmalloc (1 + i - start);
960 strncpy (rep_name, string + start, i - start);
961 rep_name[i - start] = '\0';
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"
972 /* Find a key which invokes this function in the info_keymap. */
973 command = named_function (fun_name);
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. */
988 argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg));
989 p = argrep ? strrpbrk (argrep, "0123456789-") : NULL;
992 argstr = xmalloc (p - argrep + 21);
993 strncpy (argstr, argrep, p - argrep);
994 sprintf (argstr + (p - argrep), "%d", arg);
999 rep = command ? where_is (info_keymap, command) : NULL;
1002 replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1;
1003 repstr = xmalloc (replen);
1007 strcat(repstr, argstr);
1008 strcat(repstr, " ");
1011 strcat(repstr, rep);
1020 if (next + replen > reslen)
1022 reslen = next + replen + 1;
1023 result = xrealloc (result, reslen + 1);
1027 sprintf (result + next, fmt, repstr);
1029 strcpy (result + next, repstr);
1031 next = strlen (result);
1042 strcpy (result + next, string + start);
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;
1053 where_is (Keymap map, InfoCommand *cmd)
1057 if (!where_is_rep_size)
1058 where_is_rep = xmalloc (where_is_rep_size = 100);
1059 where_is_rep_index = 0;
1061 rep = where_is_internal (map, cmd);
1063 /* If it couldn't be found, return "M-x Foo" (or equivalent). */
1068 name = function_name (cmd);
1070 return NULL; /* no such function */
1072 rep = where_is_internal (map, InfoCmd(info_execute_command));
1074 return ""; /* function exists but can't be got to by user */
1076 sprintf (where_is_rep, "%s %s", rep, name);
1083 /* Return the printed rep of the keystrokes that invoke FUNCTION,
1084 as found in MAP, or NULL. */
1086 where_is_internal (Keymap map, InfoCommand *cmd)
1088 #if defined(INFOKEY)
1090 register FUNCTION_KEYSEQ *k;
1092 for (k = cmd->keys; k; k = k->next)
1094 return pretty_keyseq (k->keyseq);
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.
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. */
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)
1115 sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
1116 return where_is_rep;
1119 /* Okay, search subsequent maps for this function. */
1120 for (i = 0; i < 256; i++)
1122 if (map[i].type == ISKMAP)
1124 int saved_index = where_is_rep_index;
1127 sprintf (where_is_rep + where_is_rep_index, "%s ",
1128 pretty_keyname (i));
1130 where_is_rep_index = strlen (where_is_rep);
1131 rep = where_is_internal ((Keymap)map[i].function, cmd);
1134 return where_is_rep;
1136 where_is_rep_index = saved_index;
1142 #endif /* INFOKEY */
1145 DECLARE_INFO_COMMAND (info_where_is,
1146 _("Show what to type to execute a given command"))
1150 command_name = read_function_name (_("Where is command: "), window);
1154 info_abort_key (active_window, count, key);
1160 InfoCommand *command;
1162 command = named_function (command_name);
1168 location = where_is (active_window->keymap, command);
1170 if (!location || !location[0])
1172 info_error (_("`%s' is not on any keys"),
1173 command_name, NULL);
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);
1182 window_message_in_echo_area
1183 (_("%s can be invoked via %s."),
1184 command_name, location);
1188 info_error (_("There is no function named `%s'"),
1189 command_name, NULL);
1192 free (command_name);