Mercurial > hg > octave-nkf
view info/infodoc.c @ 2391:b12625d6fbcd
[project @ 1996-10-12 19:35:37 by jwe]
author | jwe |
---|---|
date | Sat, 12 Oct 1996 19:43:12 +0000 |
parents | 611d403c7f3d |
children |
line wrap: on
line source
/* infohelp.c -- Functions which build documentation nodes. */ /* This file is part of GNU Info, a program for reading online documentation stored in Info format. Copyright (C) 1993 Free Software Foundation, Inc. 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). */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include "info.h" /* **************************************************************** */ /* */ /* Info Help Windows */ /* */ /* **************************************************************** */ /* 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; /* The static text which appears in the internal info help node. */ static char *info_internal_help_text[] = { "Basic Commands in Info Windows", "******************************", "", " h Invoke the Info tutorial.", "", "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.", "", "\"Advanced\" commands:", "--------------------", " q Quit Info.", " 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.", (char *)NULL }; 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) { char *new_prefix, *keyname; keyname = pretty_keyname (i); new_prefix = (char *) xmalloc (3 + strlen (prefix) + strlen (keyname)); sprintf (new_prefix, "%s%s%s ", prefix, *prefix ? " " : "", keyname); dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function); free (new_prefix); } else if (map[i].function) { 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)); #if defined (NAMED_FUNCTIONS) /* 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); } } } #endif /* NAMED_FUNCTIONS */ printf_to_message_buffer ("%s\n", doc); } } } /* How to create internal_info_help_node. */ static void create_internal_info_help_node () { register int i; initialize_message_buffer (); for (i = 0; info_internal_help_text[i]; i++) printf_to_message_buffer ("%s\n", info_internal_help_text[i]); 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); { char *message; message = replace_in_documentation ("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"); printf_to_message_buffer ("%s", message); } internal_info_help_node = message_buffer_to_node (); add_gcable_pointer (internal_info_help_node->contents); 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; help_window = get_internal_info_window (info_help_nodename); /* If we couldn't find the help window, then make it. */ if (!help_window) { WINDOW *window, *eligible = (WINDOW *)NULL; int max = 0; for (window = windows; window; window = window->next) { if (window->height > max) { max = window->height; eligible = window; } } if (!eligible) return ((WINDOW *)NULL); else { /* Make a new node containing the help text. Split the largest window into 2 windows, and show the help text in that window. */ if (!internal_info_help_node) create_internal_info_help_node (); if (eligible->height > 30) { active_window = eligible; help_window = window_make_window (internal_info_help_node); } else { set_remembered_pagetop_and_point (active_window); window_set_node_of_window (active_window, internal_info_help_node); help_window = active_window; } remember_window_and_node (help_window, help_window->node); } } return (help_window); } /* Create or move to the help window. */ DECLARE_INFO_COMMAND (info_get_help_window, "Display help message") { 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. */ DECLARE_INFO_COMMAND (info_get_info_help_node, "Visit Info node `(info)Help'") { 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) { if (win->node && win->node->filename && (stricmp (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; } } } /* 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) info_error (info_recent_file_error); else info_error (CANT_FILE_NODE, "Info", nodename); } else { /* If the current window is very large (greater than 45 lines), then split it and show the help node in another window. Otherwise, use the current window. */ if (active_window->height > 45) active_window = window_make_window (node); else { set_remembered_pagetop_and_point (active_window); window_set_node_of_window (active_window, node); } remember_window_and_node (active_window, node); } } /* **************************************************************** */ /* */ /* Groveling Info Keymaps and Docs */ /* */ /* **************************************************************** */ /* 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); } DECLARE_INFO_COMMAND (describe_key, "Print documentation for KEY") { char keyname[50]; int keyname_index = 0; unsigned char keystroke; char *rep; Keymap map; keyname[0] = '\0'; map = window->keymap; while (1) { message_in_echo_area ("Describe key: %s", keyname); keystroke = info_get_input_char (); unmessage_in_echo_area (); if (Meta_p (keystroke) && (!ISO_Latin_p || key < 160)) { 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; } /* 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) { message_in_echo_area ("%s is undefined.", keyname); return; } else if (map[keystroke].type == ISKMAP) { map = (Keymap)map[keystroke].function; strcat (keyname, " "); keyname_index = strlen (keyname); continue; } else { char *message, *fundoc, *funname = ""; #if defined (NAMED_FUNCTIONS) funname = function_name (map[keystroke].function); #endif /* NAMED_FUNCTIONS */ fundoc = function_documentation (map[keystroke].function); message = (char *)xmalloc (10 + strlen (keyname) + strlen (fundoc) + strlen (funname)); #if defined (NAMED_FUNCTIONS) sprintf (message, "%s (%s): %s.", keyname, funname, fundoc); #else sprintf (message, "%s is defined to %s.", keyname, fundoc); #endif /* !NAMED_FUNCTIONS */ window_message_in_echo_area ("%s", message); free (message); break; } } } /* 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) { 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; } } else { switch (key) { case ' ': rep = "SPC"; break; case DEL: rep = "DEL"; break; default: rep_buffer[0] = key; rep_buffer[1] = '\0'; rep = rep_buffer; } } return (rep); } /* Replace the names of functions with the key that invokes them. */ static char *where_is (), *where_is_internal (); 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] == '[') { 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++; } } 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) sprintf (where_is_rep, "M-x %s", name); 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) { sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i)); return (where_is_rep); } /* Okay, search subsequent maps for this function. */ for (i = 0; i < 256; i++) { if (map[i].type == ISKMAP) { int saved_index = where_is_rep_index; char *rep; sprintf (where_is_rep + where_is_rep_index, "%s ", pretty_keyname (i)); where_is_rep_index = strlen (where_is_rep); rep = where_is_internal ((Keymap)map[i].function, function); if (rep) return (where_is_rep); where_is_rep_index = saved_index; } } 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; command_name = read_function_name ("Where is command: ", window); if (!command_name) { info_abort_key (active_window, count, key); return; } if (*command_name) { VFunction *function; function = named_function (command_name); if (function) { 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); } } else info_error ("There is no function named `%s'", command_name); } free (command_name); }