1998-03-22 21:43:15 +01:00
|
|
|
|
/* infodoc.c -- Functions which build documentation nodes.
|
1998-03-24 19:08:49 +01:00
|
|
|
|
$Id: infodoc.c,v 1.1.1.2 1998/03/22 20:42:39 law Exp $
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
Copyright (C) 1993, 97 Free Software Foundation, Inc.
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
|
|
Written by Brian Fox (bfox@ai.mit.edu). */
|
|
|
|
|
|
|
|
|
|
#include "info.h"
|
|
|
|
|
|
|
|
|
|
/* Normally we do not define HELP_NODE_GETS_REGENERATED because the
|
|
|
|
|
contents of the help node currently can never change once an info
|
|
|
|
|
session has been started. You should consider defining this in
|
|
|
|
|
the case that you place information about dynamic variables in the
|
|
|
|
|
help text. When that happens, the contents of the help node will
|
|
|
|
|
change dependent on the value of those variables, and the user will
|
|
|
|
|
expect to see those changes. */
|
|
|
|
|
/* #define HELP_NODE_GETS_REGENERATED 1 */
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Info Help Windows */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* The name of the node used in the help window. */
|
|
|
|
|
static char *info_help_nodename = "*Info Help*";
|
|
|
|
|
|
|
|
|
|
/* A node containing printed key bindings and their documentation. */
|
|
|
|
|
static NODE *internal_info_help_node = (NODE *)NULL;
|
|
|
|
|
|
|
|
|
|
/* A pointer to the contents of the help node. */
|
|
|
|
|
static char *internal_info_help_node_contents = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
/* The static text which appears in the internal info help node. */
|
|
|
|
|
static char *info_internal_help_text[] = {
|
1998-03-22 21:43:15 +01:00
|
|
|
|
N_ ("Basic Commands in Info Windows"),
|
1997-08-22 00:57:35 +02:00
|
|
|
|
"******************************",
|
|
|
|
|
"",
|
1998-03-22 21:43:15 +01:00
|
|
|
|
" h Invoke the Info tutorial.",
|
|
|
|
|
" CTRL-x 0 Quit this help.",
|
|
|
|
|
" q Quit Info altogether.",
|
1997-08-22 00:57:35 +02:00
|
|
|
|
"",
|
|
|
|
|
"Selecting other nodes:",
|
|
|
|
|
"----------------------",
|
|
|
|
|
" n Move to the \"next\" node of this node.",
|
|
|
|
|
" p Move to the \"previous\" node of this node.",
|
|
|
|
|
" u Move \"up\" from this node.",
|
|
|
|
|
" m Pick menu item specified by name.",
|
|
|
|
|
" Picking a menu item causes another node to be selected.",
|
|
|
|
|
" f Follow a cross reference. Reads name of reference.",
|
|
|
|
|
" l Move to the last node seen in this window.",
|
|
|
|
|
" d Move to the `directory' node. Equivalent to `g(DIR)'.",
|
|
|
|
|
"",
|
|
|
|
|
"Moving within a node:",
|
|
|
|
|
"---------------------",
|
|
|
|
|
" SPC Scroll forward a page.",
|
|
|
|
|
" DEL Scroll backward a page.",
|
|
|
|
|
" b Go to the beginning of this node.",
|
|
|
|
|
" e Go to the end of this node.",
|
|
|
|
|
"",
|
1998-03-22 21:43:15 +01:00
|
|
|
|
"Other commands:",
|
1997-08-22 00:57:35 +02:00
|
|
|
|
"--------------------",
|
|
|
|
|
" 1 Pick first item in node's menu.",
|
|
|
|
|
" 2-9 Pick second ... ninth item in node's menu.",
|
|
|
|
|
" 0 Pick last item in node's menu.",
|
|
|
|
|
" g Move to node specified by name.",
|
|
|
|
|
" You may include a filename as well, as in (FILENAME)NODENAME.",
|
|
|
|
|
" s Search through this Info file for a specified string,",
|
|
|
|
|
" and select the node in which the next occurrence is found.",
|
1998-03-22 21:43:15 +01:00
|
|
|
|
NULL
|
1997-08-22 00:57:35 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static char *where_is (), *where_is_internal ();
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
dump_map_to_message_buffer (prefix, map)
|
|
|
|
|
char *prefix;
|
|
|
|
|
Keymap map;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
if (map[i].type == ISKMAP)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
char *new_prefix, *keyname;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
keyname = pretty_keyname (i);
|
|
|
|
|
new_prefix = (char *)
|
|
|
|
|
xmalloc (3 + strlen (prefix) + strlen (keyname));
|
|
|
|
|
sprintf (new_prefix, "%s%s%s ", prefix, *prefix ? " " : "", keyname);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function);
|
|
|
|
|
free (new_prefix);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else if (map[i].function)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
register int last;
|
|
|
|
|
char *doc, *name;
|
|
|
|
|
|
|
|
|
|
doc = function_documentation (map[i].function);
|
|
|
|
|
name = function_name (map[i].function);
|
|
|
|
|
|
|
|
|
|
if (!*doc)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Find out if there is a series of identical functions, as in
|
|
|
|
|
ea_insert (). */
|
|
|
|
|
for (last = i + 1; last < 256; last++)
|
|
|
|
|
if ((map[last].type != ISFUNC) ||
|
|
|
|
|
(map[last].function != map[i].function))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (last - 1 != i)
|
|
|
|
|
{
|
|
|
|
|
printf_to_message_buffer
|
|
|
|
|
("%s%s .. ", prefix, pretty_keyname (i));
|
|
|
|
|
printf_to_message_buffer
|
|
|
|
|
("%s%s\t", prefix, pretty_keyname (last - 1));
|
|
|
|
|
i = last - 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
printf_to_message_buffer ("%s%s\t", prefix, pretty_keyname (i));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
#if defined (NAMED_FUNCTIONS)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* Print the name of the function, and some padding before the
|
|
|
|
|
documentation string is printed. */
|
|
|
|
|
{
|
|
|
|
|
int length_so_far;
|
|
|
|
|
int desired_doc_start = 40; /* Must be multiple of 8. */
|
|
|
|
|
|
|
|
|
|
printf_to_message_buffer ("(%s)", name);
|
|
|
|
|
length_so_far = message_buffer_length_this_line ();
|
|
|
|
|
|
|
|
|
|
if ((desired_doc_start + strlen (doc)) >= the_screen->width)
|
|
|
|
|
printf_to_message_buffer ("\n ");
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (length_so_far < desired_doc_start)
|
|
|
|
|
{
|
|
|
|
|
printf_to_message_buffer ("\t");
|
|
|
|
|
length_so_far += character_width ('\t', length_so_far);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#endif /* NAMED_FUNCTIONS */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
printf_to_message_buffer ("%s\n", doc);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to create internal_info_help_node. */
|
|
|
|
|
static void
|
|
|
|
|
create_internal_info_help_node ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
char *contents = (char *)NULL;
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
#if !defined (HELP_NODE_GETS_REGENERATED)
|
|
|
|
|
if (internal_info_help_node_contents)
|
|
|
|
|
contents = internal_info_help_node_contents;
|
|
|
|
|
#endif /* !HELP_NODE_GETS_REGENERATED */
|
|
|
|
|
|
|
|
|
|
if (!contents)
|
|
|
|
|
{
|
|
|
|
|
int printed_one_mx = 0;
|
|
|
|
|
|
|
|
|
|
initialize_message_buffer ();
|
|
|
|
|
|
|
|
|
|
for (i = 0; info_internal_help_text[i]; i++)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
printf_to_message_buffer ("%s\n", info_internal_help_text[i]);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
printf_to_message_buffer ("---------------------\n\n");
|
|
|
|
|
printf_to_message_buffer ("The current search path is:\n");
|
|
|
|
|
printf_to_message_buffer (" \"%s\"\n", infopath);
|
|
|
|
|
printf_to_message_buffer ("---------------------\n\n");
|
|
|
|
|
printf_to_message_buffer ("Commands available in Info windows:\n\n");
|
|
|
|
|
dump_map_to_message_buffer ("", info_keymap);
|
|
|
|
|
printf_to_message_buffer ("---------------------\n\n");
|
|
|
|
|
printf_to_message_buffer ("Commands available in the echo area:\n\n");
|
|
|
|
|
dump_map_to_message_buffer ("", echo_area_keymap);
|
|
|
|
|
|
|
|
|
|
#if defined (NAMED_FUNCTIONS)
|
|
|
|
|
/* Get a list of the M-x commands which have no keystroke equivs. */
|
|
|
|
|
for (i = 0; function_doc_array[i].func; i++)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
VFunction *func = function_doc_array[i].func;
|
|
|
|
|
|
|
|
|
|
if ((!where_is_internal (info_keymap, func)) &&
|
|
|
|
|
(!where_is_internal (echo_area_keymap, func)))
|
|
|
|
|
{
|
|
|
|
|
if (!printed_one_mx)
|
|
|
|
|
{
|
|
|
|
|
printf_to_message_buffer ("---------------------\n\n");
|
|
|
|
|
printf_to_message_buffer
|
|
|
|
|
(_("The following commands can only be invoked via M-x:\n\n"));
|
|
|
|
|
printed_one_mx = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf_to_message_buffer
|
|
|
|
|
("M-x %s\n %s\n",
|
|
|
|
|
function_doc_array[i].func_name,
|
|
|
|
|
replace_in_documentation (function_doc_array[i].doc));
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (printed_one_mx)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
printf_to_message_buffer ("\n");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#endif /* NAMED_FUNCTIONS */
|
|
|
|
|
|
|
|
|
|
printf_to_message_buffer
|
1998-03-22 21:43:15 +01:00
|
|
|
|
("%s", replace_in_documentation
|
|
|
|
|
(_("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n")));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
node = message_buffer_to_node ();
|
|
|
|
|
internal_info_help_node_contents = node->contents;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* We already had the right contents, so simply use them. */
|
|
|
|
|
node = build_message_node ("", 0, 0);
|
|
|
|
|
free (node->contents);
|
|
|
|
|
node->contents = contents;
|
|
|
|
|
node->nodelen = 1 + strlen (contents);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal_info_help_node = node;
|
|
|
|
|
|
|
|
|
|
/* Do not GC this node's contents. It never changes, and we never need
|
|
|
|
|
to delete it once it is made. If you change some things (such as
|
|
|
|
|
placing information about dynamic variables in the help text) then
|
|
|
|
|
you will need to allow the contents to be gc'd, and you will have to
|
|
|
|
|
arrange to always regenerate the help node. */
|
|
|
|
|
#if defined (HELP_NODE_GETS_REGENERATED)
|
|
|
|
|
add_gcable_pointer (internal_info_help_node->contents);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
name_internal_node (internal_info_help_node, info_help_nodename);
|
|
|
|
|
|
|
|
|
|
/* Even though this is an internal node, we don't want the window
|
|
|
|
|
system to treat it specially. So we turn off the internalness
|
|
|
|
|
of it here. */
|
|
|
|
|
internal_info_help_node->flags &= ~N_IsInternal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a window which is the window showing help in this Info. */
|
|
|
|
|
static WINDOW *
|
|
|
|
|
info_find_or_create_help_window ()
|
|
|
|
|
{
|
|
|
|
|
WINDOW *help_window, *eligible, *window;
|
|
|
|
|
|
|
|
|
|
eligible = (WINDOW *)NULL;
|
|
|
|
|
help_window = get_internal_info_window (info_help_nodename);
|
|
|
|
|
|
|
|
|
|
/* If we couldn't find the help window, then make it. */
|
|
|
|
|
if (!help_window)
|
|
|
|
|
{
|
|
|
|
|
int max = 0;
|
|
|
|
|
|
|
|
|
|
for (window = windows; window; window = window->next)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (window->height > max)
|
|
|
|
|
{
|
|
|
|
|
max = window->height;
|
|
|
|
|
eligible = window;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!eligible)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return ((WINDOW *)NULL);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
#if !defined (HELP_NODE_GETS_REGENERATED)
|
|
|
|
|
else
|
|
|
|
|
return (help_window);
|
|
|
|
|
#endif /* !HELP_NODE_GETS_REGENERATED */
|
|
|
|
|
|
|
|
|
|
/* Make sure that we have a node containing the help text. */
|
|
|
|
|
create_internal_info_help_node ();
|
|
|
|
|
|
|
|
|
|
/* Either use the existing window to display the help node, or create
|
|
|
|
|
a new window if there was no existing help window. */
|
|
|
|
|
if (!help_window)
|
|
|
|
|
{
|
|
|
|
|
/* Split the largest window into 2 windows, and show the help text
|
1998-03-22 21:43:15 +01:00
|
|
|
|
in that window. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (eligible->height > 30)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
active_window = eligible;
|
|
|
|
|
help_window = window_make_window (internal_info_help_node);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (active_window);
|
|
|
|
|
window_set_node_of_window (active_window, internal_info_help_node);
|
|
|
|
|
help_window = active_window;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Case where help node always gets regenerated, and we have an
|
1998-03-22 21:43:15 +01:00
|
|
|
|
existing window in which to place the node. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (active_window != help_window)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (active_window);
|
|
|
|
|
active_window = help_window;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
window_set_node_of_window (active_window, internal_info_help_node);
|
|
|
|
|
}
|
|
|
|
|
remember_window_and_node (help_window, help_window->node);
|
|
|
|
|
return (help_window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create or move to the help window. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
WINDOW *help_window;
|
|
|
|
|
|
|
|
|
|
help_window = info_find_or_create_help_window ();
|
|
|
|
|
if (help_window)
|
|
|
|
|
{
|
|
|
|
|
active_window = help_window;
|
|
|
|
|
active_window->flags |= W_UpdateWindow;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info_error (CANT_MAKE_HELP);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Show the Info help node. This means that the "info" file is installed
|
|
|
|
|
where it can easily be found on your system. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
char *nodename;
|
|
|
|
|
|
|
|
|
|
/* If there is a window on the screen showing the node "(info)Help" or
|
|
|
|
|
the node "(info)Help-Small-Screen", simply select that window. */
|
|
|
|
|
{
|
|
|
|
|
WINDOW *win;
|
|
|
|
|
|
|
|
|
|
for (win = windows; win; win = win->next)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (win->node && win->node->filename &&
|
|
|
|
|
(strcasecmp
|
|
|
|
|
(filename_non_directory (win->node->filename), "info") == 0) &&
|
|
|
|
|
((strcmp (win->node->nodename, "Help") == 0) ||
|
|
|
|
|
(strcmp (win->node->nodename, "Help-Small-Screen") == 0)))
|
|
|
|
|
{
|
|
|
|
|
active_window = win;
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the current window is small, show the small screen help. */
|
|
|
|
|
if (active_window->height < 24)
|
|
|
|
|
nodename = "Help-Small-Screen";
|
|
|
|
|
else
|
|
|
|
|
nodename = "Help";
|
|
|
|
|
|
|
|
|
|
/* Try to get the info file for Info. */
|
|
|
|
|
node = info_get_node ("Info", nodename);
|
|
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (info_recent_file_error);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (CANT_FILE_NODE, "Info", nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If the current window is very large (greater than 45 lines),
|
1998-03-22 21:43:15 +01:00
|
|
|
|
then split it and show the help node in another window.
|
|
|
|
|
Otherwise, use the current window. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (active_window->height > 45)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
active_window = window_make_window (node);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (active_window);
|
|
|
|
|
window_set_node_of_window (active_window, node);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
remember_window_and_node (active_window, node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Groveling Info Keymaps and Docs */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Return the documentation associated with the Info command FUNCTION. */
|
|
|
|
|
char *
|
|
|
|
|
function_documentation (function)
|
|
|
|
|
VFunction *function;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; function_doc_array[i].func; i++)
|
|
|
|
|
if (function == function_doc_array[i].func)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
return (replace_in_documentation (function_doc_array[i].doc));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined (NAMED_FUNCTIONS)
|
|
|
|
|
/* Return the user-visible name of the function associated with the
|
|
|
|
|
Info command FUNCTION. */
|
|
|
|
|
char *
|
|
|
|
|
function_name (function)
|
|
|
|
|
|
|
|
|
|
VFunction *function;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; function_doc_array[i].func; i++)
|
|
|
|
|
if (function == function_doc_array[i].func)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
return (function_doc_array[i].func_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a pointer to the function named NAME. */
|
|
|
|
|
VFunction *
|
|
|
|
|
named_function (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; function_doc_array[i].func; i++)
|
|
|
|
|
if (strcmp (function_doc_array[i].func_name, name) == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
return (function_doc_array[i].func);
|
|
|
|
|
}
|
|
|
|
|
#endif /* NAMED_FUNCTIONS */
|
|
|
|
|
|
|
|
|
|
/* Return the documentation associated with KEY in MAP. */
|
|
|
|
|
char *
|
|
|
|
|
key_documentation (key, map)
|
|
|
|
|
char key;
|
|
|
|
|
Keymap map;
|
|
|
|
|
{
|
|
|
|
|
VFunction *function = map[key].function;
|
|
|
|
|
|
|
|
|
|
if (function)
|
|
|
|
|
return (function_documentation (function));
|
|
|
|
|
else
|
|
|
|
|
return ((char *)NULL);
|
|
|
|
|
}
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
char keyname[50];
|
|
|
|
|
int keyname_index = 0;
|
|
|
|
|
unsigned char keystroke;
|
|
|
|
|
char *rep;
|
|
|
|
|
Keymap map;
|
|
|
|
|
|
|
|
|
|
keyname[0] = '\0';
|
|
|
|
|
map = window->keymap;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
message_in_echo_area (_("Describe key: %s"), keyname);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
keystroke = info_get_input_char ();
|
|
|
|
|
unmessage_in_echo_area ();
|
|
|
|
|
|
|
|
|
|
if (Meta_p (keystroke) && (!ISO_Latin_p || key < 160))
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (map[ESC].type != ISKMAP)
|
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area
|
|
|
|
|
(_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strcpy (keyname + keyname_index, "ESC ");
|
|
|
|
|
keyname_index = strlen (keyname);
|
|
|
|
|
keystroke = UnMeta (keystroke);
|
|
|
|
|
map = (Keymap)map[ESC].function;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Add the printed representation of KEYSTROKE to our keyname. */
|
|
|
|
|
rep = pretty_keyname (keystroke);
|
|
|
|
|
strcpy (keyname + keyname_index, rep);
|
|
|
|
|
keyname_index = strlen (keyname);
|
|
|
|
|
|
|
|
|
|
if (map[keystroke].function == (VFunction *)NULL)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
message_in_echo_area (_("%s is undefined."), keyname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else if (map[keystroke].type == ISKMAP)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
map = (Keymap)map[keystroke].function;
|
|
|
|
|
strcat (keyname, " ");
|
|
|
|
|
keyname_index = strlen (keyname);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
char *message, *fundoc, *funname = "";
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
#if defined (NAMED_FUNCTIONS)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
funname = function_name (map[keystroke].function);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#endif /* NAMED_FUNCTIONS */
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
fundoc = function_documentation (map[keystroke].function);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
message = (char *)xmalloc
|
|
|
|
|
(10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
#if defined (NAMED_FUNCTIONS)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (message, _("%s is defined to %s."), keyname, fundoc);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#endif /* !NAMED_FUNCTIONS */
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_message_in_echo_area ("%s", message);
|
|
|
|
|
free (message);
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to get the pretty printable name of a character. */
|
|
|
|
|
static char rep_buffer[30];
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
pretty_keyname (key)
|
|
|
|
|
unsigned char key;
|
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
if (Meta_p (key))
|
|
|
|
|
{
|
|
|
|
|
char temp[20];
|
|
|
|
|
|
|
|
|
|
rep = pretty_keyname (UnMeta (key));
|
|
|
|
|
|
|
|
|
|
sprintf (temp, "ESC %s", rep);
|
|
|
|
|
strcpy (rep_buffer, temp);
|
|
|
|
|
rep = rep_buffer;
|
|
|
|
|
}
|
|
|
|
|
else if (Control_p (key))
|
|
|
|
|
{
|
|
|
|
|
switch (key)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
case '\n': rep = "LFD"; break;
|
|
|
|
|
case '\t': rep = "TAB"; break;
|
|
|
|
|
case '\r': rep = "RET"; break;
|
|
|
|
|
case ESC: rep = "ESC"; break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
sprintf (rep_buffer, "C-%c", UnControl (key));
|
|
|
|
|
rep = rep_buffer;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (key)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
case ' ': rep = "SPC"; break;
|
|
|
|
|
case DEL: rep = "DEL"; break;
|
|
|
|
|
default:
|
|
|
|
|
rep_buffer[0] = key;
|
|
|
|
|
rep_buffer[1] = '\0';
|
|
|
|
|
rep = rep_buffer;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
return (rep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Replace the names of functions with the key that invokes them. */
|
|
|
|
|
char *
|
|
|
|
|
replace_in_documentation (string)
|
|
|
|
|
char *string;
|
|
|
|
|
{
|
|
|
|
|
register int i, start, next;
|
|
|
|
|
static char *result = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
maybe_free (result);
|
|
|
|
|
result = (char *)xmalloc (1 + strlen (string));
|
|
|
|
|
|
|
|
|
|
i = next = start = 0;
|
|
|
|
|
|
|
|
|
|
/* Skip to the beginning of a replaceable function. */
|
|
|
|
|
for (i = start; string[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
/* Is this the start of a replaceable function name? */
|
|
|
|
|
if (string[i] == '\\' && string[i + 1] == '[')
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
char *fun_name, *rep;
|
|
|
|
|
VFunction *function;
|
|
|
|
|
|
|
|
|
|
/* Copy in the old text. */
|
|
|
|
|
strncpy (result + next, string + start, i - start);
|
|
|
|
|
next += (i - start);
|
|
|
|
|
start = i + 2;
|
|
|
|
|
|
|
|
|
|
/* Move to the end of the function name. */
|
|
|
|
|
for (i = start; string[i] && (string[i] != ']'); i++);
|
|
|
|
|
|
|
|
|
|
fun_name = (char *)xmalloc (1 + i - start);
|
|
|
|
|
strncpy (fun_name, string + start, i - start);
|
|
|
|
|
fun_name[i - start] = '\0';
|
|
|
|
|
|
|
|
|
|
/* Find a key which invokes this function in the info_keymap. */
|
|
|
|
|
function = named_function (fun_name);
|
|
|
|
|
|
|
|
|
|
/* If the internal documentation string fails, there is a
|
|
|
|
|
serious problem with the associated command's documentation.
|
|
|
|
|
We croak so that it can be fixed immediately. */
|
|
|
|
|
if (!function)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
rep = where_is (info_keymap, function);
|
|
|
|
|
strcpy (result + next, rep);
|
|
|
|
|
next = strlen (result);
|
|
|
|
|
|
|
|
|
|
start = i;
|
|
|
|
|
if (string[i])
|
|
|
|
|
start++;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
strcpy (result + next, string + start);
|
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a string of characters which could be typed from the keymap
|
|
|
|
|
MAP to invoke FUNCTION. */
|
|
|
|
|
static char *where_is_rep = (char *)NULL;
|
|
|
|
|
static int where_is_rep_index = 0;
|
|
|
|
|
static int where_is_rep_size = 0;
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
where_is (map, function)
|
|
|
|
|
Keymap map;
|
|
|
|
|
VFunction *function;
|
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
if (!where_is_rep_size)
|
|
|
|
|
where_is_rep = (char *)xmalloc (where_is_rep_size = 100);
|
|
|
|
|
where_is_rep_index = 0;
|
|
|
|
|
|
|
|
|
|
rep = where_is_internal (map, function);
|
|
|
|
|
|
|
|
|
|
/* If it couldn't be found, return "M-x Foo". */
|
|
|
|
|
if (!rep)
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
|
|
name = function_name (function);
|
|
|
|
|
|
|
|
|
|
if (name)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (where_is_rep, "M-x %s", name);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
rep = where_is_rep;
|
|
|
|
|
}
|
|
|
|
|
return (rep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the printed rep of FUNCTION as found in MAP, or NULL. */
|
|
|
|
|
static char *
|
|
|
|
|
where_is_internal (map, function)
|
|
|
|
|
Keymap map;
|
|
|
|
|
VFunction *function;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
/* If the function is directly invokable in MAP, return the representation
|
|
|
|
|
of that keystroke. */
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
|
if ((map[i].type == ISFUNC) && map[i].function == function)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
|
|
|
|
|
return (where_is_rep);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Okay, search subsequent maps for this function. */
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
if (map[i].type == ISKMAP)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
int saved_index = where_is_rep_index;
|
|
|
|
|
char *rep;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (where_is_rep + where_is_rep_index, "%s ",
|
|
|
|
|
pretty_keyname (i));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
where_is_rep_index = strlen (where_is_rep);
|
|
|
|
|
rep = where_is_internal ((Keymap)map[i].function, function);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (rep)
|
|
|
|
|
return (where_is_rep);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
where_is_rep_index = saved_index;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ((char *)NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern char *read_function_name ();
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (info_where_is,
|
|
|
|
|
"Show what to type to execute a given command")
|
|
|
|
|
{
|
|
|
|
|
char *command_name;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
command_name = read_function_name (_("Where is command: "), window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!command_name)
|
|
|
|
|
{
|
|
|
|
|
info_abort_key (active_window, count, key);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*command_name)
|
|
|
|
|
{
|
|
|
|
|
VFunction *function;
|
|
|
|
|
|
|
|
|
|
function = named_function (command_name);
|
|
|
|
|
|
|
|
|
|
if (function)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
char *location;
|
|
|
|
|
|
|
|
|
|
location = where_is (active_window->keymap, function);
|
|
|
|
|
|
|
|
|
|
if (!location)
|
|
|
|
|
{
|
|
|
|
|
info_error (_("`%s' is not on any keys"), command_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (strncmp (location, "M-x ", 4) == 0)
|
|
|
|
|
window_message_in_echo_area
|
|
|
|
|
(_("%s can only be invoked via %s."), command_name, location);
|
|
|
|
|
else
|
|
|
|
|
window_message_in_echo_area
|
|
|
|
|
(_("%s can be invoked via %s."), command_name, location);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("There is no function named `%s'"), command_name);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (command_name);
|
|
|
|
|
}
|