diff options
Diffstat (limited to 'Build/source/utils/texinfo/info/session.c')
-rw-r--r-- | Build/source/utils/texinfo/info/session.c | 5280 |
1 files changed, 0 insertions, 5280 deletions
diff --git a/Build/source/utils/texinfo/info/session.c b/Build/source/utils/texinfo/info/session.c deleted file mode 100644 index 36f12d6794e..00000000000 --- a/Build/source/utils/texinfo/info/session.c +++ /dev/null @@ -1,5280 +0,0 @@ -/* session.c -- user windowing interface to Info. - $Id: session.c,v 1.37 2008/03/23 23:32:22 karl Exp $ - - Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2007, 2008 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. - - Originally written by Brian Fox (bfox@ai.mit.edu). */ - -#include "info.h" -#include "search.h" -#include <sys/ioctl.h> - -#if defined (HAVE_SYS_TIME_H) -# include <sys/time.h> -# define HAVE_STRUCT_TIMEVAL -#endif /* HAVE_SYS_TIME_H */ - -#if defined (HANDLE_MAN_PAGES) -# include "man.h" -#endif - -static void info_clear_pending_input (void); -static void info_set_pending_input (unsigned char key); -static void info_handle_pointer (char *label, WINDOW *window); -static void display_info_keyseq (int expecting_future_input); -char *node_printed_rep (NODE *node); - -/* **************************************************************** */ -/* */ -/* Running an Info Session */ -/* */ -/* **************************************************************** */ - -/* The place that we are reading input from. */ -static FILE *info_input_stream = NULL; - -/* The last executed command. */ -VFunction *info_last_executed_command = NULL; - -/* Becomes non-zero when 'q' is typed to an Info window. */ -int quit_info_immediately = 0; - -/* Array of structures describing for each window which nodes have been - visited in that window. */ -INFO_WINDOW **info_windows = NULL; - -/* Where to add the next window, if we need to add one. */ -static int info_windows_index = 0; - -/* Number of slots allocated to `info_windows'. */ -static int info_windows_slots = 0; - -/* Whether to use regexps or not for search. */ -static int use_regex = 1; - -void remember_window_and_node (WINDOW *window, NODE *node); -void forget_window_and_nodes (WINDOW *window); -void display_startup_message_and_start (void); - -/* Begin an info session finding the nodes specified by FILENAME and NODENAMES. - For each loaded node, create a new window. Always split the largest of the - available windows. */ -void -begin_multiple_window_info_session (char *filename, char **nodenames) -{ - register int i; - WINDOW *window = (WINDOW *)NULL; - - for (i = 0; nodenames[i]; i++) - { - NODE *node; - - node = info_get_node (filename, nodenames[i]); - - if (!node) - break; - - /* If this is the first node, initialize the info session. */ - if (!window) - { - initialize_info_session (node, 1); - window = active_window; - } - else - { - /* Find the largest window in WINDOWS, and make that be the active - one. Then split it and add our window and node to the list - of remembered windows and nodes. Then tile the windows. */ - WINDOW *win, *largest = NULL; - int max_height = 0; - - for (win = windows; win; win = win->next) - if (win->height > max_height) - { - max_height = win->height; - largest = win; - } - - if (!largest) - { - display_update_display (windows); - info_error (msg_cant_find_window, NULL, NULL); - info_session (); - xexit (0); - } - - active_window = largest; - window = window_make_window (node); - if (window) - { - window_tile_windows (TILE_INTERNALS); - remember_window_and_node (window, node); - } - else - { - display_update_display (windows); - info_error (msg_win_too_small, NULL, NULL); - info_session (); - xexit (0); - } - } - } - display_startup_message_and_start (); -} - -/* Start an info session with INITIAL_NODE, and an error message in the echo - area made from FORMAT and ARG. */ -void -begin_info_session_with_error (NODE *initial_node, const char *format, - void *arg1, void *arg2) -{ - initialize_info_session (initial_node, 1); - info_error (format, arg1, arg2); - info_session (); -} - -/* Start an info session with INITIAL_NODE. */ -void -begin_info_session (NODE *initial_node) -{ - initialize_info_session (initial_node, 1); - display_startup_message_and_start (); -} - -void -display_startup_message_and_start (void) -{ - char *format; - - format = replace_in_documentation - (_("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."), - 0); - - window_message_in_echo_area (format, VERSION, NULL); - info_session (); -} - -/* Run an info session with an already initialized window and node. */ -void -info_session (void) -{ - display_update_display (windows); - info_last_executed_command = NULL; - info_read_and_dispatch (); - /* On program exit, leave the cursor at the bottom of the window, and - restore the terminal I/O. */ - terminal_goto_xy (0, screenheight - 1); - terminal_clear_to_eol (); - fflush (stdout); - terminal_unprep_terminal (); - close_dribble_file (); -} - -/* Here is a window-location dependent event loop. Called from the - functions info_session (), and from read_xxx_in_echo_area (). */ -void -info_read_and_dispatch (void) -{ - unsigned char key; - int done; - done = 0; - - while (!done && !quit_info_immediately) - { - int lk = 0; - - /* If we haven't just gone up or down a line, there is no - goal column for this window. */ - if ((info_last_executed_command != (VFunction *) info_next_line) && - (info_last_executed_command != (VFunction *) info_prev_line)) - active_window->goal_column = -1; - - if (echo_area_is_active) - { - lk = echo_area_last_command_was_kill; - echo_area_prep_read (); - } - - if (!info_any_buffered_input_p ()) - display_update_display (windows); - - display_cursor_at_point (active_window); - info_initialize_numeric_arg (); - - initialize_keyseq (); - key = info_get_input_char (); - - /* No errors yet. We just read a character, that's all. Only clear - the echo_area if it is not currently active. */ - if (!echo_area_is_active) - window_clear_echo_area (); - - info_error_was_printed = 0; - - /* Do the selected command. */ - info_dispatch_on_key (key, active_window->keymap); - - if (echo_area_is_active) - { - /* Echo area commands that do killing increment the value of - ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no - change in the value of this variable, the last command - executed was not a kill command. */ - if (lk == echo_area_last_command_was_kill) - echo_area_last_command_was_kill = 0; - - if (ea_last_executed_command == (VFunction *) ea_newline || - info_aborted_echo_area) - { - ea_last_executed_command = (VFunction *)NULL; - done = 1; - } - - if (info_last_executed_command == (VFunction *) info_quit) - quit_info_immediately = 1; - } - else if (info_last_executed_command == (VFunction *) info_quit) - done = 1; - } -} - -/* Found in signals.c */ -extern void initialize_info_signal_handler (void ); - -/* Initialize the first info session by starting the terminal, window, - and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ -void -initialize_info_session (NODE *node, int clear_screen) -{ - char *term_name = getenv ("TERM"); - terminal_initialize_terminal (term_name); - - if (terminal_is_dumb_p) - { - if (!term_name) - term_name = "dumb"; - - info_error (msg_term_too_dumb, term_name, NULL); - xexit (1); - } - - if (clear_screen) - { - terminal_prep_terminal (); - terminal_clear_screen (); - } - - initialize_info_keymaps (); - window_initialize_windows (screenwidth, screenheight); - initialize_info_signal_handler (); - display_initialize_display (screenwidth, screenheight); - info_set_node_of_window (0, active_window, node); - - /* Tell the window system how to notify us when a window needs to be - asynchronously deleted (e.g., user resizes window very small). */ - window_deletion_notifier = (VFunction *) forget_window_and_nodes; - - /* If input has not been redirected yet, make it come from unbuffered - standard input. */ - if (!info_input_stream) - { - setbuf (stdin, NULL); - info_input_stream = stdin; - } - - info_windows_initialized_p = 1; -} - -/* Tell Info that input is coming from the file FILENAME. */ -void -info_set_input_from_file (char *filename) -{ - FILE *stream; - - /* Input may include binary characters. */ - stream = fopen (filename, FOPEN_RBIN); - - if (!stream) - return; - - if ((info_input_stream != (FILE *)NULL) && - (info_input_stream != stdin)) - fclose (info_input_stream); - - info_input_stream = stream; - - if (stream != stdin) - display_inhibited = 1; -} - -/* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */ -static INFO_WINDOW * -get_info_window_of_window (WINDOW *window) -{ - register int i; - INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; - - for (i = 0; info_windows && (info_win = info_windows[i]); i++) - if (info_win->window == window) - break; - - return (info_win); -} - -/* Reset the remembered pagetop and point of WINDOW to WINDOW's current - values if the window and node are the same as the current one being - displayed. */ -void -set_remembered_pagetop_and_point (WINDOW *window) -{ - INFO_WINDOW *info_win; - - info_win = get_info_window_of_window (window); - - if (!info_win) - return; - - if (info_win->nodes_index && - (info_win->nodes[info_win->current] == window->node)) - { - info_win->pagetops[info_win->current] = window->pagetop; - info_win->points[info_win->current] = window->point; - } -} - -void -remember_window_and_node (WINDOW *window, NODE *node) -{ - /* See if we already have this window in our list. */ - INFO_WINDOW *info_win = get_info_window_of_window (window); - - /* If the window wasn't already on our list, then make a new entry. */ - if (!info_win) - { - info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW)); - info_win->window = window; - info_win->nodes = (NODE **)NULL; - info_win->pagetops = (int *)NULL; - info_win->points = (long *)NULL; - info_win->current = 0; - info_win->nodes_index = 0; - info_win->nodes_slots = 0; - - add_pointer_to_array (info_win, info_windows_index, info_windows, - info_windows_slots, 10, INFO_WINDOW *); - } - - /* If this node, the current pagetop, and the current point are the - same as the current saved node and pagetop, don't really add this to - the list of history nodes. This may happen only at the very - beginning of the program, I'm not sure. --karl */ - if (info_win->nodes - && info_win->current >= 0 - && info_win->nodes[info_win->current]->contents == node->contents - && info_win->pagetops[info_win->current] == window->pagetop - && info_win->points[info_win->current] == window->point) - return; - - /* Remember this node, the currently displayed pagetop, and the current - location of point in this window. Because we are updating pagetops - and points as well as nodes, it is more efficient to avoid the - add_pointer_to_array macro here. */ - if (info_win->nodes_index + 2 >= info_win->nodes_slots) - { - info_win->nodes_slots += 20; - info_win->nodes = (NODE **) xrealloc (info_win->nodes, - info_win->nodes_slots * sizeof (NODE *)); - info_win->pagetops = (int *) xrealloc (info_win->pagetops, - info_win->nodes_slots * sizeof (int)); - info_win->points = (long *) xrealloc (info_win->points, - info_win->nodes_slots * sizeof (long)); - } - - info_win->nodes[info_win->nodes_index] = node; - info_win->pagetops[info_win->nodes_index] = window->pagetop; - info_win->points[info_win->nodes_index] = window->point; - info_win->current = info_win->nodes_index++; - info_win->nodes[info_win->nodes_index] = NULL; - info_win->pagetops[info_win->nodes_index] = 0; - info_win->points[info_win->nodes_index] = 0; -} - -#define DEBUG_FORGET_WINDOW_AND_NODES -#if defined (DEBUG_FORGET_WINDOW_AND_NODES) -static void -consistency_check_info_windows (void) -{ - register int i; - - for (i = 0; i < info_windows_index; i++) - { - WINDOW *win; - - for (win = windows; win; win = win->next) - if (win == info_windows[i]->window) - break; - - if (!win) - abort (); - } -} -#endif /* DEBUG_FORGET_WINDOW_AND_NODES */ - -/* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */ -void -forget_window_and_nodes (WINDOW *window) -{ - register int i; - INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; - - for (i = 0; info_windows && (info_win = info_windows[i]); i++) - if (info_win->window == window) - break; - - /* If we found the window to forget, then do so. */ - if (info_win) - { - while (i < info_windows_index) - { - info_windows[i] = info_windows[i + 1]; - i++; - } - - info_windows_index--; - info_windows[info_windows_index] = (INFO_WINDOW *)NULL; - - if (info_win->nodes) - { - /* Free the node structures which held onto internal node contents - here. This doesn't free the contents; we have a garbage collector - which does that. */ - for (i = 0; info_win->nodes[i]; i++) - if (internal_info_node_p (info_win->nodes[i])) - free (info_win->nodes[i]); - free (info_win->nodes); - - maybe_free (info_win->pagetops); - maybe_free (info_win->points); - } - - free (info_win); - } -#if defined (DEBUG_FORGET_WINDOW_AND_NODES) - consistency_check_info_windows (); -#endif /* DEBUG_FORGET_WINDOW_AND_NODES */ -} - -/* Set WINDOW to show NODE. Remember the new window in our list of Info - windows. If we are doing automatic footnote display, also try to display - the footnotes for this window. If REMEMBER is nonzero, first call - set_remembered_pagetop_and_point. */ -void -info_set_node_of_window (int remember, WINDOW *window, NODE *node) -{ - if (remember) - set_remembered_pagetop_and_point (window); - - /* Put this node into the window. */ - window_set_node_of_window (window, node); - - /* Remember this node and window in our list of info windows. */ - remember_window_and_node (window, node); - - /* If doing auto-footnote display/undisplay, show the footnotes belonging - to this window's node. */ - if (auto_footnotes_p) - info_get_or_remove_footnotes (window); -} - - -/* **************************************************************** */ -/* */ -/* Info Movement Commands */ -/* */ -/* **************************************************************** */ - -/* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen - to do so. */ -void -set_window_pagetop (WINDOW *window, int desired_top) -{ - int point_line, old_pagetop; - - if (desired_top < 0) - desired_top = 0; - else if (desired_top > window->line_count) - desired_top = window->line_count - 1; - - if (window->pagetop == desired_top) - return; - - old_pagetop = window->pagetop; - window->pagetop = desired_top; - - /* Make sure that point appears in this window. */ - point_line = window_line_of_point (window); - if ((point_line < window->pagetop) || - ((point_line - window->pagetop) > window->height - 1)) - window->point = - window->line_starts[window->pagetop] - window->node->contents; - - window->flags |= W_UpdateWindow; - - /* Find out which direction to scroll, and scroll the window in that - direction. Do this only if there would be a savings in redisplay - time. This is true if the amount to scroll is less than the height - of the window, and if the number of lines scrolled would be greater - than 10 % of the window's height. */ - if (old_pagetop < desired_top) - { - int start, end, amount; - - amount = desired_top - old_pagetop; - - if ((amount >= window->height) || - (((window->height - amount) * 10) < window->height)) - return; - - start = amount + window->first_row; - end = window->height + window->first_row; - - display_scroll_display (start, end, -amount); - } - else - { - int start, end, amount; - - amount = old_pagetop - desired_top; - - if ((amount >= window->height) || - (((window->height - amount) * 10) < window->height)) - return; - - start = window->first_row; - end = (window->first_row + window->height) - amount; - display_scroll_display (start, end, amount); - } -} - -/* Immediately make WINDOW->point visible on the screen, and move the - terminal cursor there. */ -static void -info_show_point (WINDOW *window) -{ - int old_pagetop; - - old_pagetop = window->pagetop; - window_adjust_pagetop (window); - if (old_pagetop != window->pagetop) - { - int new_pagetop; - - new_pagetop = window->pagetop; - window->pagetop = old_pagetop; - set_window_pagetop (window, new_pagetop); - } - - if (window->flags & W_UpdateWindow) - display_update_one_window (window); - - display_cursor_at_point (window); -} - -/* Move WINDOW->point from OLD line index to NEW line index. */ -static void -move_to_new_line (int old, int new, WINDOW *window) -{ - if (old == -1) - { - info_error (msg_cant_find_point, NULL, NULL); - } - else - { - int goal; - - if (new >= window->line_count || new < 0) - return; - - goal = window_get_goal_column (window); - window->goal_column = goal; - - window->point = window->line_starts[new] - window->node->contents; - window->point += window_chars_to_goal (window->line_starts[new], goal); - info_show_point (window); - } -} - -static int forward_move_node_structure (WINDOW *window, int behaviour); -static int backward_move_node_structure (WINDOW *window, int behaviour); - -/* Move WINDOW's point down to the next line if possible. */ -DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line")) -{ - int old_line, new_line; - - if (count < 0) - info_prev_line (window, -count, key); - else - while (count) - { - int diff; - - old_line = window_line_of_point (window); - diff = window->line_count - old_line; - if (diff > count) - diff = count; - - count -= diff; - new_line = old_line + diff; - if (new_line >= window->line_count) - { - if (cursor_movement_scrolls_p) - { - if (forward_move_node_structure (window, - info_scroll_behaviour)) - break; - move_to_new_line (0, 0, window); - } - else - break; - } - else - move_to_new_line (old_line, new_line, window); - } -} - -/* Move WINDOW's point up to the previous line if possible. */ -DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line")) -{ - int old_line, new_line; - - if (count < 0) - info_next_line (window, -count, key); - else - while (count) - { - int diff; - - old_line = window_line_of_point (window); - diff = old_line + 1; - if (diff > count) - diff = count; - - count -= diff; - new_line = old_line - diff; - - if (new_line < 0 - && cursor_movement_scrolls_p) - { - if (backward_move_node_structure (window, info_scroll_behaviour)) - break; - if (window->line_count > window->height) - set_window_pagetop (window, window->line_count - window->height); - move_to_new_line (window->line_count, - window->line_count - 1, window); - } - else - move_to_new_line (old_line, new_line, window); - } -} - -/* Move WINDOW's point to the end of the true line. */ -DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line")) -{ - register int point, len; - register char *buffer; - - buffer = window->node->contents; - len = window->node->nodelen; - - for (point = window->point; - (point < len) && (buffer[point] != '\n'); - point++); - - if (point != window->point) - { - window->point = point; - info_show_point (window); - } -} - -/* Move WINDOW's point to the beginning of the true line. */ -DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line")) -{ - register int point; - register char *buffer; - - buffer = window->node->contents; - point = window->point; - - for (; (point) && (buffer[point - 1] != '\n'); point--); - - /* If at a line start already, do nothing. */ - if (point != window->point) - { - window->point = point; - info_show_point (window); - } -} - -/* Move point forward in the node. */ -DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character")) -{ - if (count < 0) - info_backward_char (window, -count, key); - else - { - while (count) - { - int diff = window->node->nodelen - window->point; - - if (diff > count) - diff = count; - window->point += diff; - count -= diff; - if (window->point >= window->node->nodelen) - { - if (cursor_movement_scrolls_p - && forward_move_node_structure (window, - info_scroll_behaviour) == 0) - window->point = 0; - else - { - window->point = window->node->nodelen - 1; - break; - } - } - } - info_show_point (window); - } -} - -/* Move point backward in the node. */ -DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character")) -{ - if (count < 0) - info_forward_char (window, -count, key); - else - { - while (count) - { - int diff = count; - - if (window->point < diff) - diff = window->point + 1; - - window->point -= diff; - count -= diff; - - if (window->point < 0) - { - if (cursor_movement_scrolls_p - && backward_move_node_structure (window, - info_scroll_behaviour) == 0) - { - window->point = window->node->nodelen - 1; - if (window->line_count > window->height) - set_window_pagetop (window, - window->line_count - window->height); - } - else - { - window->point = 0; - break; - } - } - } - info_show_point (window); - } -} - -#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c)) - -/* Move forward a word in this node. */ -DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word")) -{ - long point; - char *buffer; - int end, c; - - if (count < 0) - { - info_backward_word (window, -count, key); - return; - } - - point = window->point; - buffer = window->node->contents; - end = window->node->nodelen; - - while (count) - { - if (point + 1 >= end) - { - if (cursor_movement_scrolls_p - && forward_move_node_structure (window, - info_scroll_behaviour) == 0) - { - point = 0; - buffer = window->node->contents; - end = window->node->nodelen; - } - else - return; - } - - /* If we are not in a word, move forward until we are in one. - Then, move forward until we hit a non-alphabetic character. */ - c = buffer[point]; - - if (!alphabetic (c)) - { - while (++point < end) - { - c = buffer[point]; - if (alphabetic (c)) - break; - } - } - - if (point >= end) - { - if (cursor_movement_scrolls_p - && forward_move_node_structure (window, - info_scroll_behaviour) == 0) - { - point = 0; - buffer = window->node->contents; - end = window->node->nodelen; - } - else - return; - } - - - while (++point < end) - { - c = buffer[point]; - if (!alphabetic (c)) - break; - } - --count; - } - window->point = point; - info_show_point (window); -} - -DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word")) -{ - long point; - char *buffer; - int c; - - if (count < 0) - { - info_forward_word (window, -count, key); - return; - } - - buffer = window->node->contents; - point = window->point; - - while (count) - { - if (point == 0) - { - if (cursor_movement_scrolls_p - && backward_move_node_structure (window, - info_scroll_behaviour) == 0) - { - if (window->line_count > window->height) - set_window_pagetop (window, - window->line_count - window->height); - buffer = window->node->contents; - point = window->node->nodelen; - } - else - break; - } - - /* Like info_forward_word (), except that we look at the - characters just before point. */ - - c = buffer[point - 1]; - - if (!alphabetic (c)) - { - while (--point) - { - c = buffer[point - 1]; - if (alphabetic (c)) - break; - } - } - - while (point) - { - c = buffer[point - 1]; - if (!alphabetic (c)) - break; - else - --point; - } - --count; - } - window->point = point; - info_show_point (window); -} - -/* Variable controlling the behaviour of default scrolling when you are - already at the bottom of a node. Possible values are defined in session.h. - The meanings are: - - IS_Continuous Try to get first menu item, or failing that, the - "Next:" pointer, or failing that, the "Up:" and - "Next:" of the up. - IS_NextOnly Try to get "Next:" menu item. - IS_PageOnly Simply give up at the bottom of a node. */ - -int info_scroll_behaviour = IS_Continuous; - -/* Choices used by the completer when reading a value for the user-visible - variable "scroll-behaviour". */ -char *info_scroll_choices[] = { - "Continuous", "Next Only", "Page Only", (char *)NULL -}; - -/* Controls whether scroll-behavior affects line movement commands */ -int cursor_movement_scrolls_p = 1; - -/* Choices for the scroll-last-node variable */ -char *scroll_last_node_choices[] = { - "Stop", "Scroll", "Top", NULL -}; - -/* Controls what to do when a scrolling command is issued at the end of the - last node. */ -int scroll_last_node = SLN_Stop; - -/* Default window sizes for scrolling commands. */ -int default_window_size = -1; /* meaning 1 window-full */ -int default_scroll_size = -1; /* meaning half screen size */ - -#define INFO_LABEL_FOUND() \ - (info_parsed_nodename || (info_parsed_filename \ - && !is_dir_name (info_parsed_filename))) - -static int -last_node_p (NODE *node) -{ - info_next_label_of_node (node); - if (!INFO_LABEL_FOUND ()) - { - info_up_label_of_node (node); - return !INFO_LABEL_FOUND () || strcmp (info_parsed_nodename, "Top") == 0; - } - return 0; -} - -/* Move to 1st menu item, Next, Up/Next, or error in this window. */ -static int -forward_move_node_structure (WINDOW *window, int behaviour) -{ - switch (behaviour) - { - case IS_PageOnly: - info_error (msg_at_node_bottom, NULL, NULL); - return 1; - - case IS_NextOnly: - info_next_label_of_node (window->node); - if (!info_parsed_nodename && !info_parsed_filename) - { - info_error (msg_no_pointer, (char *) _("Next"), NULL); - return 1; - } - else - { - info_handle_pointer ("Next", window); - } - break; - - case IS_Continuous: - { - if (last_node_p (window->node)) - { - switch (scroll_last_node) - { - case SLN_Stop: - info_error (_("No more nodes within this document."), - NULL, NULL); - return 1; - - case SLN_Scroll: - break; - - case SLN_Top: - info_top_node (window, 1, 0); - return 0; - - default: - abort (); - } - } - - /* First things first. If this node contains a menu, move down - into the menu. */ - { - REFERENCE **menu; - - menu = info_menu_of_node (window->node); - - if (menu) - { - info_free_references (menu); - info_menu_digit (window, 1, '1'); - return 0; - } - } - - /* Okay, this node does not contain a menu. If it contains a - "Next:" pointer, use that. */ - info_next_label_of_node (window->node); - if (INFO_LABEL_FOUND ()) - { - info_handle_pointer ("Next", window); - return 0; - } - - /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we - can move "Next:". If that isn't possible, complain that there - are no more nodes. */ - { - int up_counter, old_current; - INFO_WINDOW *info_win; - - /* Remember the current node and location. */ - info_win = get_info_window_of_window (window); - old_current = info_win->current; - - /* Back up through the "Up:" pointers until we have found a "Next:" - that isn't the same as the first menu item found in that node. */ - up_counter = 0; - while (!info_error_was_printed) - { - info_up_label_of_node (window->node); - if (INFO_LABEL_FOUND ()) - { - info_handle_pointer ("Up", window); - if (info_error_was_printed) - continue; - - up_counter++; - - info_next_label_of_node (window->node); - - /* If no "Next" pointer, keep backing up. */ - if (!INFO_LABEL_FOUND ()) - continue; - - /* If this node's first menu item is the same as this node's - Next pointer, keep backing up. */ - if (!info_parsed_filename) - { - REFERENCE **menu; - char *next_nodename; - - /* Remember the name of the Next node, since reading - the menu can overwrite the contents of the - info_parsed_xxx strings. */ - next_nodename = xstrdup (info_parsed_nodename); - - menu = info_menu_of_node (window->node); - if (menu && - (strcmp - (menu[0]->nodename, next_nodename) == 0)) - { - info_free_references (menu); - free (next_nodename); - continue; - } - else - { - /* Restore the world to where it was before - reading the menu contents. */ - info_free_references (menu); - free (next_nodename); - info_next_label_of_node (window->node); - } - } - - /* This node has a "Next" pointer, and it is not the - same as the first menu item found in this node. */ - info_handle_pointer ("Next", window); - return 0; - } - else - { - /* No more "Up" pointers. Print an error, and call it - quits. */ - register int i; - - for (i = 0; i < up_counter; i++) - { - info_win->nodes_index--; - free (info_win->nodes[info_win->nodes_index]); - info_win->nodes[info_win->nodes_index] = (NODE *)NULL; - } - info_win->current = old_current; - window->node = info_win->nodes[old_current]; - window->pagetop = info_win->pagetops[old_current]; - window->point = info_win->points[old_current]; - recalculate_line_starts (window); - window->flags |= W_UpdateWindow; - info_error (_("No more nodes within this document."), - NULL, NULL); - return 1; - } - } - } - break; - } - } - return info_error_was_printed; /*FIXME*/ -} - -/* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */ -static int -backward_move_node_structure (WINDOW *window, int behaviour) -{ - switch (behaviour) - { - case IS_PageOnly: - info_error (msg_at_node_top, NULL, NULL); - return 1; - - case IS_NextOnly: - info_prev_label_of_node (window->node); - if (!info_parsed_nodename && !info_parsed_filename) - { - info_error (_("No `Prev' for this node."), NULL, NULL); - return 1; - } - else - { - info_handle_pointer ("Prev", window); - } - break; - - case IS_Continuous: - info_prev_label_of_node (window->node); - - if (!info_parsed_nodename && (!info_parsed_filename - || is_dir_name (info_parsed_filename))) - { - info_up_label_of_node (window->node); - if (!info_parsed_nodename && (!info_parsed_filename - || is_dir_name (info_parsed_filename))) - { - info_error ( - _("No `Prev' or `Up' for this node within this document."), - NULL, NULL); - return 1; - } - else - { - info_handle_pointer ("Up", window); - } - } - else - { - REFERENCE **menu; - int inhibit_menu_traversing = 0; - - /* Watch out! If this node's Prev is the same as the Up, then - move Up. Otherwise, we could move Prev, and then to the last - menu item in the Prev. This would cause the user to loop - through a subsection of the info file. */ - if (!info_parsed_filename && info_parsed_nodename) - { - char *pnode; - - pnode = xstrdup (info_parsed_nodename); - info_up_label_of_node (window->node); - - if (!info_parsed_filename && info_parsed_nodename && - strcmp (info_parsed_nodename, pnode) == 0) - { - /* The nodes are the same. Inhibit moving to the last - menu item. */ - free (pnode); - inhibit_menu_traversing = 1; - } - else - { - free (pnode); - info_prev_label_of_node (window->node); - } - } - - /* Move to the previous node. If this node now contains a menu, - and we have not inhibited movement to it, move to the node - corresponding to the last menu item. */ - info_handle_pointer ("Prev", window); - - if (!inhibit_menu_traversing) - { - while (!info_error_was_printed && - (menu = info_menu_of_node (window->node))) - { - info_free_references (menu); - info_menu_digit (window, 1, '0'); - } - } - } - break; - } - return 0; -} - -/* Move continuously forward through the node structure of this info file. */ -DECLARE_INFO_COMMAND (info_global_next_node, - _("Move forwards or down through node structure")) -{ - if (count < 0) - info_global_prev_node (window, -count, key); - else - { - while (count && !info_error_was_printed) - { - forward_move_node_structure (window, IS_Continuous); - count--; - } - } -} - -/* Move continuously backward through the node structure of this info file. */ -DECLARE_INFO_COMMAND (info_global_prev_node, - _("Move backwards or up through node structure")) -{ - if (count < 0) - info_global_next_node (window, -count, key); - else - { - while (count && !info_error_was_printed) - { - backward_move_node_structure (window, IS_Continuous); - count--; - } - } -} - -static void _scroll_forward(WINDOW *window, int count, - unsigned char key, int behaviour); -static void _scroll_backward(WINDOW *window, int count, - unsigned char key, int behaviour); - -static void -_scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour) -{ - if (count < 0) - _scroll_backward (window, -count, key, behaviour); - else - { - int desired_top; - - /* Without an explicit numeric argument, scroll the bottom two - lines to the top of this window, Or, if at bottom of window, - and the chosen behaviour is to scroll through nodes get the - "Next" node for this window. */ - if (default_window_size > 0) - desired_top = window->pagetop + default_window_size; - else if (!info_explicit_arg && count == 1) - { - desired_top = window->pagetop + (window->height - 2); - - /* If there are no more lines to scroll here, error, or get - another node, depending on BEHAVIOUR. */ - if (desired_top > window->line_count) - { - if (forward_move_node_structure (window, behaviour)) - info_end_of_node (window, 1, 0); - return; - } - } - else - desired_top = window->pagetop + count; - - if (desired_top >= window->line_count) - desired_top = window->line_count - 2; - - if (window->pagetop > desired_top) - return; - else - set_window_pagetop (window, desired_top); - } -} - -static void -_scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour) -{ - if (count < 0) - _scroll_forward (window, -count, key, behaviour); - else - { - int desired_top; - - /* Without an explicit numeric argument, scroll the top two lines - to the bottom of this window, or, depending on the selected - behaviour, move to the previous, or Up'th node. */ - if (default_window_size > 0) - desired_top = window->pagetop - default_window_size; - else if (!info_explicit_arg && count == 1) - { - desired_top = window->pagetop - (window->height - 2); - - if ((desired_top < 0) && (window->pagetop == 0)) - { - if ((backward_move_node_structure (window, behaviour) == 0) - && (cursor_movement_scrolls_p)) - info_end_of_node (window, 1, 0); - window->point = (window->line_starts[window->pagetop] - - window->node->contents); - return; - } - } - else - desired_top = window->pagetop - count; - - if (desired_top < 0) - desired_top = 0; - - set_window_pagetop (window, desired_top); - window->point = (window->line_starts[window->pagetop] - - window->node->contents); - } -} - -/* Show the next screen of WINDOW's node. */ -DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window")) -{ - _scroll_forward (window, count, key, info_scroll_behaviour); -} - -/* Like info_scroll_forward, but sets default_window_size as a side - effect. */ -DECLARE_INFO_COMMAND (info_scroll_forward_set_window, - _("Scroll forward in this window and set default window size")) -{ - if (info_explicit_arg) - default_window_size = count; - _scroll_forward (window, count, key, info_scroll_behaviour); -} - -/* Show the next screen of WINDOW's node but never advance to next node. */ -DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node")) -{ - _scroll_forward (window, count, key, IS_PageOnly); -} - -/* Like info_scroll_forward_page_only, but sets default_window_size as a side - effect. */ -DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window, - _("Scroll forward in this window staying within node and set default window size")) -{ - if (info_explicit_arg) - default_window_size = count; - _scroll_forward (window, count, key, IS_PageOnly); -} - -/* Show the previous screen of WINDOW's node. */ -DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) -{ - _scroll_backward (window, count, key, info_scroll_behaviour); -} - -/* Like info_scroll_backward, but sets default_window_size as a side - effect. */ -DECLARE_INFO_COMMAND (info_scroll_backward_set_window, - _("Scroll backward in this window and set default window size")) -{ - if (info_explicit_arg) - default_window_size = count; - _scroll_backward (window, count, key, info_scroll_behaviour); -} - -/* Show the previous screen of WINDOW's node but never move to previous - node. */ -DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node")) -{ - _scroll_backward (window, count, key, IS_PageOnly); -} - -/* Like info_scroll_backward_page_only, but sets default_window_size as a side - effect. */ -DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window, - _("Scroll backward in this window staying within node and set default window size")) -{ - if (info_explicit_arg) - default_window_size = count; - _scroll_backward (window, count, key, IS_PageOnly); -} - -/* Move to the beginning of the node. */ -DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node")) -{ - window->pagetop = window->point = 0; - window->flags |= W_UpdateWindow; -} - -/* Move to the end of the node. */ -DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node")) -{ - window->point = window->node->nodelen - 1; - info_show_point (window); -} - -/* Scroll the window forward by N lines. */ -DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines")) -{ - if (count < 0) - info_up_line (window, -count, key); - else - { - int desired_top = window->pagetop + count; - - if (desired_top >= window->line_count) - desired_top = window->line_count - 2; - - if (window->pagetop <= desired_top) - set_window_pagetop (window, desired_top); - } -} - -/* Scroll the window backward by N lines. */ -DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines")) -{ - if (count < 0) - info_down_line (window, -count, key); - else - { - int desired_top = window->pagetop - count; - - if (desired_top < 0) - desired_top = 0; - - set_window_pagetop (window, desired_top); - } -} - -/* Scroll the window forward by N lines and remember N as default for - subsequent commands. */ -DECLARE_INFO_COMMAND (info_scroll_half_screen_down, - _("Scroll down by half screen size")) -{ - if (count < 0) - info_scroll_half_screen_up (window, -count, key); - else - { - int scroll_size = (the_screen->height + 1) / 2; - int desired_top; - - if (info_explicit_arg) - default_scroll_size = count; - if (default_scroll_size > 0) - scroll_size = default_scroll_size; - - desired_top = window->pagetop + scroll_size; - if (desired_top >= window->line_count) - desired_top = window->line_count - 2; - - if (window->pagetop <= desired_top) - set_window_pagetop (window, desired_top); - } -} - -/* Scroll the window backward by N lines and remember N as default for - subsequent commands. */ -DECLARE_INFO_COMMAND (info_scroll_half_screen_up, - _("Scroll up by half screen size")) -{ - if (count < 0) - info_scroll_half_screen_down (window, -count, key); - else - { - int scroll_size = (the_screen->height + 1) / 2; - int desired_top; - - if (info_explicit_arg) - default_scroll_size = count; - if (default_scroll_size > 0) - scroll_size = default_scroll_size; - - desired_top = window->pagetop - scroll_size; - if (desired_top < 0) - desired_top = 0; - - set_window_pagetop (window, desired_top); - } -} - -/* **************************************************************** */ -/* */ -/* Commands for Manipulating Windows */ -/* */ -/* **************************************************************** */ - -/* Make the next window in the chain be the active window. */ -DECLARE_INFO_COMMAND (info_next_window, _("Select the next window")) -{ - if (count < 0) - { - info_prev_window (window, -count, key); - return; - } - - /* If no other window, error now. */ - if (!windows->next && !echo_area_is_active) - { - info_error (msg_one_window, NULL, NULL); - return; - } - - while (count--) - { - if (window->next) - window = window->next; - else - { - if (window == the_echo_area || !echo_area_is_active) - window = windows; - else - window = the_echo_area; - } - } - - if (active_window != window) - { - if (auto_footnotes_p) - info_get_or_remove_footnotes (window); - - window->flags |= W_UpdateWindow; - active_window = window; - } -} - -/* Make the previous window in the chain be the active window. */ -DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window")) -{ - if (count < 0) - { - info_next_window (window, -count, key); - return; - } - - /* Only one window? */ - - if (!windows->next && !echo_area_is_active) - { - info_error (msg_one_window, NULL, NULL); - return; - } - - while (count--) - { - /* If we are in the echo area, or if the echo area isn't active and we - are in the first window, find the last window in the chain. */ - if (window == the_echo_area || - (window == windows && !echo_area_is_active)) - { - register WINDOW *win, *last = NULL; - - for (win = windows; win; win = win->next) - last = win; - - window = last; - } - else - { - if (window == windows) - window = the_echo_area; - else - window = window->prev; - } - } - - if (active_window != window) - { - if (auto_footnotes_p) - info_get_or_remove_footnotes (window); - - window->flags |= W_UpdateWindow; - active_window = window; - } -} - -/* Split WINDOW into two windows, both showing the same node. If we - are automatically tiling windows, re-tile after the split. */ -DECLARE_INFO_COMMAND (info_split_window, _("Split the current window")) -{ - WINDOW *split, *old_active; - int pagetop; - - /* Remember the current pagetop of the window being split. If it doesn't - change, we can scroll its contents around after the split. */ - pagetop = window->pagetop; - - /* Make the new window. */ - old_active = active_window; - active_window = window; - split = window_make_window (window->node); - active_window = old_active; - - if (!split) - { - info_error (msg_win_too_small, NULL, NULL); - } - else - { -#if defined (SPLIT_BEFORE_ACTIVE) - /* Try to scroll the old window into its new postion. */ - if (pagetop == window->pagetop) - { - int start, end, amount; - - start = split->first_row; - end = start + window->height; - amount = split->height + 1; - display_scroll_display (start, end, amount); - } -#else /* !SPLIT_BEFORE_ACTIVE */ - /* Make sure point still appears in the active window. */ - info_show_point (window); -#endif /* !SPLIT_BEFORE_ACTIVE */ - - /* If the window just split was one internal to Info, try to display - something else in it. */ - if (internal_info_node_p (split->node)) - { - register int i, j; - INFO_WINDOW *iw; - NODE *node = (NODE *)NULL; - char *filename; - - for (i = 0; (iw = info_windows[i]); i++) - { - for (j = 0; j < iw->nodes_index; j++) - if (!internal_info_node_p (iw->nodes[j])) - { - if (iw->nodes[j]->parent) - filename = iw->nodes[j]->parent; - else - filename = iw->nodes[j]->filename; - - node = info_get_node (filename, iw->nodes[j]->nodename); - if (node) - { - window_set_node_of_window (split, node); - i = info_windows_index - 1; - break; - } - } - } - } - split->pagetop = window->pagetop; - - if (auto_tiling_p) - window_tile_windows (DONT_TILE_INTERNALS); - else - window_adjust_pagetop (split); - - remember_window_and_node (split, split->node); - } -} - -/* Delete WINDOW, forgetting the list of last visited nodes. If we are - automatically displaying footnotes, show or remove the footnotes - window. If we are automatically tiling windows, re-tile after the - deletion. */ -DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window")) -{ - if (!windows->next) - { - info_error (msg_cant_kill_last, NULL, NULL); - } - else if (window->flags & W_WindowIsPerm) - { - info_error (_("Cannot delete a permanent window"), NULL, NULL); - } - else - { - info_delete_window_internal (window); - - if (auto_footnotes_p) - info_get_or_remove_footnotes (active_window); - - if (auto_tiling_p) - window_tile_windows (DONT_TILE_INTERNALS); - } -} - -/* Do the physical deletion of WINDOW, and forget this window and - associated nodes. */ -void -info_delete_window_internal (WINDOW *window) -{ - if (windows->next && ((window->flags & W_WindowIsPerm) == 0)) - { - /* We not only delete the window from the display, we forget it from - our list of remembered windows. */ - forget_window_and_nodes (window); - window_delete_window (window); - - if (echo_area_is_active) - echo_area_inform_of_deleted_window (window); - } -} - -/* Just keep WINDOW, deleting all others. */ -DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows")) -{ - int num_deleted; /* The number of windows we deleted. */ - int pagetop, start, end; - - /* Remember a few things about this window. We may be able to speed up - redisplay later by scrolling its contents. */ - pagetop = window->pagetop; - start = window->first_row; - end = start + window->height; - - num_deleted = 0; - - while (1) - { - WINDOW *win; - - /* Find an eligible window and delete it. If no eligible windows - are found, we are done. A window is eligible for deletion if - is it not permanent, and it is not WINDOW. */ - for (win = windows; win; win = win->next) - if (win != window && ((win->flags & W_WindowIsPerm) == 0)) - break; - - if (!win) - break; - - info_delete_window_internal (win); - num_deleted++; - } - - /* Scroll the contents of this window into the right place so that the - user doesn't have to wait any longer than necessary for redisplay. */ - if (num_deleted) - { - int amount; - - amount = (window->first_row - start); - amount -= (window->pagetop - pagetop); - display_scroll_display (start, end, amount); - } - - window->flags |= W_UpdateWindow; -} - -/* Scroll the "other" window of WINDOW. */ -DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window")) -{ - WINDOW *other; - - /* If only one window, give up. */ - if (!windows->next) - { - info_error (msg_one_window, NULL, NULL); - return; - } - - other = window->next; - - if (!other) - other = window->prev; - - info_scroll_forward (other, count, key); -} - -/* Scroll the "other" window of WINDOW. */ -DECLARE_INFO_COMMAND (info_scroll_other_window_backward, - _("Scroll the other window backward")) -{ - info_scroll_other_window (window, -count, key); -} - -/* Change the size of WINDOW by AMOUNT. */ -DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window")) -{ - window_change_window_height (window, count); -} - -/* When non-zero, tiling takes place automatically when info_split_window - is called. */ -int auto_tiling_p = 0; - -/* Tile all of the visible windows. */ -DECLARE_INFO_COMMAND (info_tile_windows, - _("Divide the available screen space among the visible windows")) -{ - window_tile_windows (TILE_INTERNALS); -} - -/* Toggle the state of this window's wrapping of lines. */ -DECLARE_INFO_COMMAND (info_toggle_wrap, - _("Toggle the state of line wrapping in the current window")) -{ - window_toggle_wrap (window); -} - -/* Toggle the usage of regular expressions in searches. */ -DECLARE_INFO_COMMAND (info_toggle_regexp, - _("Toggle the usage of regular expressions in searches")) -{ - use_regex = 1 - use_regex; - window_message_in_echo_area (use_regex - ? _("Using regular expressions for searches.") - : _("Using literal strings for searches."), - NULL, NULL); -} - -/* **************************************************************** */ -/* */ -/* Info Node Commands */ -/* */ -/* **************************************************************** */ - -/* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's - filename is not set. */ -char * -node_printed_rep (NODE *node) -{ - char *rep; - - if (node->filename) - { - char *filename - = filename_non_directory (node->parent ? node->parent : node->filename); - rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1); - sprintf (rep, "(%s)%s", filename, node->nodename); - } - else - rep = node->nodename; - - return rep; -} - - -/* Using WINDOW for various defaults, select the node referenced by ENTRY - in it. If the node is selected, the window and node are remembered. */ -void -info_select_reference (WINDOW *window, REFERENCE *entry) -{ - NODE *node; - char *filename, *nodename, *file_system_error; - - file_system_error = (char *)NULL; - - filename = entry->filename; - if (!filename) - filename = window->node->parent; - if (!filename) - filename = window->node->filename; - - if (filename) - filename = xstrdup (filename); - - if (entry->nodename) - nodename = xstrdup (entry->nodename); - else - nodename = xstrdup ("Top"); - - node = info_get_node (filename, nodename); - - /* Try something a little weird. If the node couldn't be found, and the - reference was of the form "foo::", see if the entry->label can be found - as a file, with a node of "Top". */ - if (!node) - { - if (info_recent_file_error) - file_system_error = xstrdup (info_recent_file_error); - - if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0)) - { - node = info_get_node (entry->label, "Top"); - if (!node && info_recent_file_error) - { - maybe_free (file_system_error); - file_system_error = xstrdup (info_recent_file_error); - } - } - } - - if (!node) - { - if (file_system_error) - info_error (file_system_error, NULL, NULL); - else - info_error (msg_cant_find_node, nodename, NULL); - } - - maybe_free (file_system_error); - maybe_free (filename); - maybe_free (nodename); - - if (node) - info_set_node_of_window (1, window, node); -} - -/* Parse the node specification in LINE using WINDOW to default the filename. - Select the parsed node in WINDOW and remember it, or error if the node - couldn't be found. */ -static void -info_parse_and_select (char *line, WINDOW *window) -{ - REFERENCE entry; - - info_parse_node (line, DONT_SKIP_NEWLINES); - - entry.nodename = info_parsed_nodename; - entry.filename = info_parsed_filename; - entry.label = "*info-parse-and-select*"; - - info_select_reference (window, &entry); -} - -/* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME - are previously filled, try to get the node represented by them into - WINDOW. The node should have been pointed to by the LABEL pointer of - WINDOW->node. */ -static void -info_handle_pointer (char *label, WINDOW *window) -{ - if (info_parsed_filename || info_parsed_nodename) - { - char *filename, *nodename; - NODE *node; - - filename = nodename = (char *)NULL; - - if (info_parsed_filename) - filename = xstrdup (info_parsed_filename); - else - { - if (window->node->parent) - filename = xstrdup (window->node->parent); - else if (window->node->filename) - filename = xstrdup (window->node->filename); - } - - if (info_parsed_nodename) - nodename = xstrdup (info_parsed_nodename); - else - nodename = xstrdup ("Top"); - - node = info_get_node (filename, nodename); - - if (node) - { - INFO_WINDOW *info_win; - - info_win = get_info_window_of_window (window); - if (info_win) - { - info_win->pagetops[info_win->current] = window->pagetop; - info_win->points[info_win->current] = window->point; - } - info_set_node_of_window (1, window, node); - } - else - { - if (info_recent_file_error) - info_error (info_recent_file_error, NULL, NULL); - else - info_error (msg_cant_file_node, filename, nodename); - } - - free (filename); - free (nodename); - } - else - { - info_error (msg_no_pointer, label, NULL); - } -} - -/* Make WINDOW display the "Next:" node of the node currently being - displayed. */ -DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node")) -{ - info_next_label_of_node (window->node); - info_handle_pointer ("Next", window); -} - -/* Make WINDOW display the "Prev:" node of the node currently being - displayed. */ -DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node")) -{ - info_prev_label_of_node (window->node); - info_handle_pointer ("Prev", window); -} - -/* Make WINDOW display the "Up:" node of the node currently being - displayed. */ -DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node")) -{ - info_up_label_of_node (window->node); - info_handle_pointer ("Up", window); -} - -/* Make WINDOW display the last node of this info file. */ -DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file")) -{ - register int i; - FILE_BUFFER *fb = file_buffer_of_window (window); - NODE *node = (NODE *)NULL; - - if (fb && fb->tags) - { - int last_node_tag_idx = -1; - - /* If no explicit argument, or argument of zero, default to the - last node. */ - if (count == 0 || (count == 1 && !info_explicit_arg)) - count = -1; - for (i = 0; count && fb->tags[i]; i++) - if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ - { - count--; - last_node_tag_idx = i; - } - if (count > 0) - i = last_node_tag_idx + 1; - if (i > 0) - node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); - } - - if (!node) - info_error (_("This window has no additional nodes"), NULL, NULL); - else - info_set_node_of_window (1, window, node); -} - -/* Make WINDOW display the first node of this info file. */ -DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file")) -{ - FILE_BUFFER *fb = file_buffer_of_window (window); - NODE *node = (NODE *)NULL; - - /* If no explicit argument, or argument of zero, default to the - first node. */ - if (count == 0) - count = 1; - if (fb && fb->tags) - { - register int i; - int last_node_tag_idx = -1; - - for (i = 0; count && fb->tags[i]; i++) - if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ - { - count--; - last_node_tag_idx = i; - } - if (count > 0) - i = last_node_tag_idx + 1; - if (i > 0) - node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); - } - - if (!node) - info_error (_("This window has no additional nodes"), NULL, NULL); - else - info_set_node_of_window (1, window, node); -} - -/* Select the last menu item in WINDOW->node. */ -DECLARE_INFO_COMMAND (info_last_menu_item, - _("Select the last item in this node's menu")) -{ - info_menu_digit (window, 1, '0'); -} - -/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */ -DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item")) -{ - register int i, item; - register REFERENCE **menu; - - menu = info_menu_of_node (window->node); - - if (!menu) - { - info_error (msg_no_menu_node, NULL, NULL); - return; - } - - /* We have the menu. See if there are this many items in it. */ - item = key - '0'; - - /* Special case. Item "0" is the last item in this menu. */ - if (item == 0) - for (i = 0; menu[i + 1]; i++); - else - { - for (i = 0; menu[i]; i++) - if (i == item - 1) - break; - } - - if (menu[i]) - { - info_select_reference (window, menu[i]); - if (menu[i]->line_number > 0) - info_next_line (window, menu[i]->line_number - 1, key); - } - else - info_error (_("There aren't %d items in this menu."), - (void *) (long) item, NULL); - - info_free_references (menu); - return; -} - - - -/* Return a pointer to the xref in XREF_LIST that is nearest to POS, or - NULL if XREF_LIST is empty. That is, if POS is within any of the - given xrefs, return that one. Otherwise, return the one with the - nearest beginning or end. If there are two that are equidistant, - prefer the one forward. The return is in newly-allocated memory, - since the caller frees it. - - This is called from info_menu_or_ref_item with XREF_LIST being all - the xrefs in the node, and POS being point. The ui function that - starts it all off is select-reference-this-line. - - This is not the same logic as in info.el. Info-get-token prefers - searching backwards to searching forwards, and has a hardwired search - limit of 200 chars (in Emacs 21.2). */ - -static REFERENCE ** -nearest_xref (REFERENCE **xref_list, long int pos) -{ - int this_xref; - int nearest = -1; - long best_delta = -1; - - for (this_xref = 0; xref_list[this_xref]; this_xref++) - { - long delta; - REFERENCE *xref = xref_list[this_xref]; - if (xref->start <= pos && pos <= xref->end) - { /* POS is within this xref, we're done */ - nearest = this_xref; - break; - } - - /* See how far POS is from this xref. Take into account the - `*Note' that begins the xref, since as far as the user is - concerned, that's where it starts. */ - delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))), - labs (pos - xref->end)); - - /* It's the <= instead of < that makes us choose the forward xref - of POS if two are equidistant. Of course, because of all the - punctuation surrounding xrefs, it's not necessarily obvious - where one ends. */ - if (delta <= best_delta || best_delta < 0) - { - nearest = this_xref; - best_delta = delta; - } - } - - /* Maybe there was no list to search through. */ - if (nearest < 0) - return NULL; - - /* Ok, we have a nearest xref, make a list of it. */ - { - REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2); - ret[0] = info_copy_reference (xref_list[nearest]); - ret[1] = NULL; - return ret; - } -} - - -/* Read a menu or followed reference from the user defaulting to the - reference found on the current line, and select that node. The - reading is done with completion. BUILDER is the function used - to build the list of references. ASK_P is non-zero if the user - should be prompted, or zero to select the default item. */ -static void -info_menu_or_ref_item (WINDOW *window, int count, - unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p) -{ - char *line; - REFERENCE *entry; - REFERENCE *defentry = NULL; - REFERENCE **menu = (*builder) (window->node); - - if (!menu) - { - if (builder == info_menu_of_node) - info_error (msg_no_menu_node, NULL, NULL); - else - info_error (msg_no_xref_node, NULL, NULL); - return; - } - - /* Default the selected reference to the one which is on the line that - point is in. */ - { - REFERENCE **refs = NULL; - int point_line = window_line_of_point (window); - - if (point_line != -1) - { - SEARCH_BINDING binding; - - binding.buffer = window->node->contents; - binding.start = window->line_starts[point_line] - binding.buffer; - if (window->line_starts[point_line + 1]) - binding.end = window->line_starts[point_line + 1] - binding.buffer; - else - binding.end = window->node->nodelen; - binding.flags = 0; - - if (builder == info_menu_of_node) - { - if (point_line) - { - binding.start--; - refs = info_menu_items (&binding); - } - } - else - { -#if defined (HANDLE_MAN_PAGES) - if (window->node->flags & N_IsManPage) - refs = manpage_xrefs_in_binding (window->node, &binding); - else -#endif /* HANDLE_MAN_PAGES */ - refs = nearest_xref (menu, window->point); - } - - if (refs && refs[0]) - { - if (strcmp (refs[0]->label, "Menu") != 0 - || builder == info_xrefs_of_node) - { - int which = 0; - - /* For xrefs, find the closest reference to point, - unless we only have one reference (as we will if - we've called nearest_xref above). It would be better - to have only one piece of code, but the conditions - when we call this are tangled. */ - if (builder == info_xrefs_of_node && refs[1]) - { - int closest = -1; - - for (; refs[which]; which++) - { - if (window->point >= refs[which]->start - && window->point <= refs[which]->end) - { - closest = which; - break; - } - else if (window->point < refs[which]->start) - break; - } - if (which > 0) - { - if (closest == -1) - which--; - else - which = closest; - } - } - - defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - defentry->label = xstrdup (refs[which]->label); - defentry->filename = refs[which]->filename; - defentry->nodename = refs[which]->nodename; - defentry->line_number = refs[which]->line_number; - - if (defentry->filename) - defentry->filename = xstrdup (defentry->filename); - if (defentry->nodename) - defentry->nodename = xstrdup (defentry->nodename); - } - info_free_references (refs); - } - } - } - - /* If we are going to ask the user a question, do it now. */ - if (ask_p) - { - char *prompt; - - /* Build the prompt string. */ - if (builder == info_menu_of_node) - { - if (defentry) - { - prompt = xmalloc (strlen (defentry->label) - + strlen (_("Menu item (%s): "))); - sprintf (prompt, _("Menu item (%s): "), defentry->label); - } - else - prompt = xstrdup (_("Menu item: ")); - } - else - { - if (defentry) - { - prompt = xmalloc (strlen (defentry->label) - + strlen (_("Follow xref (%s): "))); - sprintf (prompt, _("Follow xref (%s): "), defentry->label); - } - else - prompt = xstrdup (_("Follow xref: ")); - } - - line = info_read_completing_in_echo_area (window, prompt, menu); - free (prompt); - - window = active_window; - - /* User aborts, just quit. */ - if (!line) - { - maybe_free (defentry); - info_free_references (menu); - info_abort_key (window, 0, 0); - return; - } - - /* If we had a default and the user accepted it, use that. */ - if (!*line) - { - free (line); - if (defentry) - line = xstrdup (defentry->label); - else - line = (char *)NULL; - } - } - else - { - /* Not going to ask any questions. If we have a default entry, use - that, otherwise return. */ - if (!defentry) - return; - else - line = xstrdup (defentry->label); - } - - if (line) - { - /* It is possible that the references have more than a single - entry with the same label, and also LINE is down-cased, which - complicates matters even more. Try to be as accurate as we - can: if they've chosen the default, use defentry directly. */ - if (defentry && strcmp (line, defentry->label) == 0) - entry = defentry; - else - /* Find the selected label in the references. If there are - more than one label which matches, find the one that's - closest to point. */ - { - register int i; - int best = -1, min_dist = window->node->nodelen; - REFERENCE *ref; - - for (i = 0; menu && (ref = menu[i]); i++) - { - /* Need to use mbscasecmp because LINE is downcased - inside info_read_completing_in_echo_area. */ - if (mbscasecmp (line, ref->label) == 0) - { - /* ref->end is more accurate estimate of position - for menus than ref->start. Go figure. */ - int dist = abs (window->point - ref->end); - - if (dist < min_dist) - { - min_dist = dist; - best = i; - } - } - } - if (best != -1) - entry = menu[best]; - else - entry = (REFERENCE *)NULL; - } - - if (!entry && defentry) - info_error (_("The reference disappeared! (%s)."), line, NULL); - else - { - NODE *orig = window->node; - info_select_reference (window, entry); - - if (builder == info_xrefs_of_node && window->node != orig - && !(window->node->flags & N_FromAnchor)) - { /* Search for this reference in the node. */ - long offset; - long start; - - if (window->line_count > 0) - start = window->line_starts[1] - window->node->contents; - else - start = 0; - - offset = - info_target_search_node (window->node, entry->label, start); - - if (offset != -1) - { - window->point = offset; - window_adjust_pagetop (window); - } - } - - if (entry->line_number > 0) - /* next_line starts at line 1? Anyway, the -1 makes it - move to the right line. */ - info_next_line (window, entry->line_number - 1, key); - } - - free (line); - if (defentry) - { - free (defentry->label); - maybe_free (defentry->filename); - maybe_free (defentry->nodename); - free (defentry); - } - } - - info_free_references (menu); - - if (!info_error_was_printed) - window_clear_echo_area (); -} - -/* Read a line (with completion) which is the name of a menu item, - and select that item. */ -DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node")) -{ - info_menu_or_ref_item (window, count, key, info_menu_of_node, 1); -} - -/* Read a line (with completion) which is the name of a reference to - follow, and select the node. */ -DECLARE_INFO_COMMAND - (info_xref_item, _("Read a footnote or cross reference and select its node")) -{ - info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1); -} - -/* Position the cursor at the start of this node's menu. */ -DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu")) -{ - SEARCH_BINDING binding; - long position; - - binding.buffer = window->node->contents; - binding.start = 0; - binding.end = window->node->nodelen; - binding.flags = S_FoldCase | S_SkipDest; - - position = search (INFO_MENU_LABEL, &binding); - - if (position == -1) - info_error (msg_no_menu_node, NULL, NULL); - else - { - window->point = position; - window_adjust_pagetop (window); - window->flags |= W_UpdateWindow; - } -} - -/* Visit as many menu items as is possible, each in a separate window. */ -DECLARE_INFO_COMMAND (info_visit_menu, - _("Visit as many menu items at once as possible")) -{ - register int i; - REFERENCE *entry, **menu; - - menu = info_menu_of_node (window->node); - - if (!menu) - info_error (msg_no_menu_node, NULL, NULL); - - for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++) - { - WINDOW *new; - - new = window_make_window (window->node); - window_tile_windows (TILE_INTERNALS); - - if (!new) - info_error (msg_win_too_small, NULL, NULL); - else - { - active_window = new; - info_select_reference (new, entry); - } - } -} - -/* Read a line of input which is a node name, and go to that node. */ -DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it")) -{ - char *line; - -#define GOTO_COMPLETES -#if defined (GOTO_COMPLETES) - /* Build a completion list of all of the known nodes. */ - { - register int fbi, i; - FILE_BUFFER *current; - REFERENCE **items = (REFERENCE **)NULL; - int items_index = 0; - int items_slots = 0; - - current = file_buffer_of_window (window); - - for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++) - { - FILE_BUFFER *fb; - REFERENCE *entry; - int this_is_the_current_fb; - - fb = info_loaded_files[fbi]; - this_is_the_current_fb = (current == fb); - - entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - entry->filename = entry->nodename = (char *)NULL; - entry->label = (char *)xmalloc (4 + strlen (fb->filename)); - sprintf (entry->label, "(%s)*", fb->filename); - - add_pointer_to_array - (entry, items_index, items, items_slots, 10, REFERENCE *); - - if (fb->tags) - { - for (i = 0; fb->tags[i]; i++) - { - entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - entry->filename = entry->nodename = (char *)NULL; - if (this_is_the_current_fb) - entry->label = xstrdup (fb->tags[i]->nodename); - else - { - entry->label = (char *) xmalloc - (4 + strlen (fb->filename) + - strlen (fb->tags[i]->nodename)); - sprintf (entry->label, "(%s)%s", - fb->filename, fb->tags[i]->nodename); - } - - add_pointer_to_array - (entry, items_index, items, items_slots, 100, REFERENCE *); - } - } - } - line = info_read_maybe_completing (window, _("Goto node: "), - items); - info_free_references (items); - } -#else /* !GOTO_COMPLETES */ - line = info_read_in_echo_area (window, _("Goto node: ")); -#endif /* !GOTO_COMPLETES */ - - /* If the user aborted, quit now. */ - if (!line) - { - info_abort_key (window, 0, 0); - return; - } - - canonicalize_whitespace (line); - - if (*line) - info_parse_and_select (line, window); - - free (line); - if (!info_error_was_printed) - window_clear_echo_area (); -} - -/* Follow the menu list in MENUS (list of strings terminated by a NULL - entry) from INITIAL_NODE. If can't continue at any point (no menu or - no menu entry for the next item), return the node so far -- that - might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will - be set to the error message and argument for message, otherwise they - will be NULL. */ - -NODE * -info_follow_menus (NODE *initial_node, char **menus, - const char **errstr, char **errarg1, char **errarg2) -{ - NODE *node = NULL; - *errstr = *errarg1 = *errarg2 = NULL; - - for (; *menus; menus++) - { - static char *first_arg = NULL; - REFERENCE **menu; - REFERENCE *entry; - char *arg = *menus; /* Remember the name of the menu entry we want. */ - - /* A leading space is certainly NOT part of a node name. Most - probably, they typed a space after the separating comma. The - strings in menus[] have their whitespace canonicalized, so - there's at most one space to ignore. */ - if (*arg == ' ') - arg++; - if (!first_arg) - first_arg = arg; - - /* Build and return a list of the menu items in this node. */ - menu = info_menu_of_node (initial_node); - - /* If no menu item in this node, stop here, but let the user - continue to use Info. Perhaps they wanted this node and didn't - realize it. */ - if (!menu) - { - if (arg == first_arg) - { - node = make_manpage_node (first_arg); - if (node) - goto maybe_got_node; - } - *errstr = _("No menu in node `%s'."); - *errarg1 = node_printed_rep (initial_node); - return initial_node; - } - - /* Find the specified menu item. */ - entry = info_get_labeled_reference (arg, menu); - - /* If the item wasn't found, search the list sloppily. Perhaps this - user typed "buffer" when they really meant "Buffers". */ - if (!entry) - { - int i; - int best_guess = -1; - - for (i = 0; (entry = menu[i]); i++) - { - if (mbscasecmp (entry->label, arg) == 0) - break; - else - if ((best_guess == -1) - && (mbsncasecmp (entry->label, arg, strlen (arg)) == 0)) - best_guess = i; - } - - if (!entry && best_guess != -1) - entry = menu[best_guess]; - } - - /* If we still failed to find the reference, start Info with the current - node anyway. It is probably a misspelling. */ - if (!entry) - { - if (arg == first_arg) - { - /* Maybe they typed "info foo" instead of "info -f foo". */ - node = info_get_node (first_arg, 0); - if (node) - add_file_directory_to_path (first_arg); - else - node = make_manpage_node (first_arg); - if (node) - goto maybe_got_node; - } - - info_free_references (menu); - *errstr = _("No menu item `%s' in node `%s'."); - *errarg1 = arg; - *errarg2 = node_printed_rep (initial_node); - return initial_node; - } - - /* We have found the reference that the user specified. If no - filename in this reference, define it. */ - if (!entry->filename) - entry->filename = xstrdup (initial_node->parent ? initial_node->parent - : initial_node->filename); - - /* Try to find this node. */ - node = info_get_node (entry->filename, entry->nodename); - if (!node && arg == first_arg) - { - node = make_manpage_node (first_arg); - if (node) - goto maybe_got_node; - } - - /* Since we cannot find it, try using the label of the entry as a - file, i.e., "(LABEL)Top". */ - if (!node && entry->nodename - && strcmp (entry->label, entry->nodename) == 0) - node = info_get_node (entry->label, "Top"); - - maybe_got_node: - if (!node) - { - *errstr = _("Unable to find node referenced by `%s' in `%s'."); - *errarg1 = xstrdup (entry->label); - *errarg2 = node_printed_rep (initial_node); - info_free_references (menu); - return initial_node; - } - - info_free_references (menu); - - /* Success. Go round the loop again. */ - free (initial_node); - initial_node = node; - } - - return initial_node; -} - -/* Split STR into individual node names by writing null bytes in wherever - there are commas and constructing a list of the resulting pointers. - (We can do this since STR has had canonicalize_whitespace called on it.) - Return array terminated with NULL. */ - -static char ** -split_list_of_nodenames (char *str) -{ - unsigned len = 2; - char **nodes = xmalloc (len * sizeof (char *)); - - nodes[len - 2] = str; - - while (*str++) - { - if (*str == ',') - { - *str++ = 0; /* get past the null byte */ - len++; - nodes = xrealloc (nodes, len * sizeof (char *)); - nodes[len - 2] = str; - } - } - - nodes[len - 1] = NULL; - - return nodes; -} - - -/* Read a line of input which is a sequence of menus (starting from - dir), and follow them. */ -DECLARE_INFO_COMMAND (info_menu_sequence, - _("Read a list of menus starting from dir and follow them")) -{ - char *line = info_read_in_echo_area (window, _("Follow menus: ")); - - /* If the user aborted, quit now. */ - if (!line) - { - info_abort_key (window, 0, 0); - return; - } - - canonicalize_whitespace (line); - - if (*line) - { - const char *errstr; - char *errarg1, *errarg2; - NODE *dir_node = info_get_node (NULL, NULL); - char **nodes = split_list_of_nodenames (line); - NODE *node = NULL; - - /* If DIR_NODE is NULL, they might be reading a file directly, - like in "info -d . -f ./foo". Try using "Top" instead. */ - if (!dir_node) - { - char *file_name = window->node->parent; - - if (!file_name) - file_name = window->node->filename; - dir_node = info_get_node (file_name, NULL); - } - - /* If we still cannot find the starting point, give up. - We cannot allow a NULL pointer inside info_follow_menus. */ - if (!dir_node) - info_error (msg_cant_find_node, "Top", NULL); - else - node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2); - - free (nodes); - if (!errstr) - info_set_node_of_window (1, window, node); - else - info_error (errstr, errarg1, errarg2); - } - - free (line); - if (!info_error_was_printed) - window_clear_echo_area (); -} - -/* Search the menu MENU for a (possibly mis-spelled) entry ARG. - Return the menu entry, or the best guess for what they meant by ARG, - or NULL if there's nothing in this menu seems to fit the bill. - If EXACT is non-zero, allow only exact matches. */ -static REFERENCE * -entry_in_menu (char *arg, REFERENCE **menu, int exact) -{ - REFERENCE *entry; - - /* First, try to find the specified menu item verbatim. */ - entry = info_get_labeled_reference (arg, menu); - - /* If the item wasn't found, search the list sloppily. Perhaps we - have "Option Summary", but ARG is "option". */ - if (!entry && !exact) - { - int i; - int best_guess = -1; - - for (i = 0; (entry = menu[i]); i++) - { - if (mbscasecmp (entry->label, arg) == 0) - break; - else - if (mbsncasecmp (entry->label, arg, strlen (arg)) == 0) - best_guess = i; - } - - if (!entry && best_guess != -1) - entry = menu[best_guess]; - } - - return entry; -} - -/* Find the node that is the best candidate to list the PROGRAM's - invocation info and its command-line options, by looking for menu - items and chains of menu items with characteristic names. */ -void -info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program) -{ - /* The list of node names typical for GNU manuals where the program - usage and specifically the command-line arguments are described. - This is pure heuristics. I gathered these node names by looking - at all the Info files I could put my hands on. If you are - looking for evidence to complain to the GNU project about - non-uniform style of documentation, here you have your case! */ - static const char *invocation_nodes[] = { - "%s invocation", - "Invoking %s", - "Preliminaries", /* m4 has Invoking under Preliminaries! */ - "Invocation", - "Command Arguments",/* Emacs */ - "Invoking `%s'", - "%s options", - "Options", - "Option ", /* e.g. "Option Summary" */ - "Invoking", - "All options", /* tar, paxutils */ - "Arguments", - "%s cmdline", /* ar */ - "%s", /* last resort */ - (const char *)0 - }; - NODE *node = NULL; - REFERENCE **menu; - const char **try_node; - - /* We keep looking deeper and deeper in the menu structure until - there are no more menus or no menu items from the above list. - Some manuals have the invocation node sitting 3 or 4 levels deep - in the menu hierarchy... */ - for (node = initial_node; node; initial_node = node) - { - REFERENCE *entry = NULL; - - /* Build and return a list of the menu items in this node. */ - menu = info_menu_of_node (initial_node); - - /* If no menu item in this node, stop here. Perhaps this node - is the one they need. */ - if (!menu) - break; - - /* Look for node names typical for usage nodes in this menu. */ - for (try_node = invocation_nodes; *try_node; try_node++) - { - char *nodename; - - nodename = xmalloc (strlen (program) + strlen (*try_node)); - sprintf (nodename, *try_node, program); - /* The last resort "%s" is dangerous, so we restrict it - to exact matches here. */ - entry = entry_in_menu (nodename, menu, - strcmp (*try_node, "%s") == 0); - free (nodename); - if (entry) - break; - } - - if (!entry) - break; - - if (!entry->filename) - entry->filename = xstrdup (initial_node->parent ? initial_node->parent - : initial_node->filename); - /* Try to find this node. */ - node = info_get_node (entry->filename, entry->nodename); - info_free_references (menu); - if (!node) - break; - } - - /* We've got our best shot at the invocation node. Now select it. */ - if (initial_node) - info_set_node_of_window (1, window, initial_node); - if (!info_error_was_printed) - window_clear_echo_area (); -} - -/* Given a name of an Info file, find the name of the package it - describes by removing the leading directories and extensions. */ -char * -program_name_from_file_name (char *file_name) -{ - int i; - char *program_name = xstrdup (filename_non_directory (file_name)); - - for (i = strlen (program_name) - 1; i > 0; i--) - if (program_name[i] == '.' - && (FILENAME_CMPN (program_name + i, ".info", 5) == 0 - || FILENAME_CMPN (program_name + i, ".inf", 4) == 0 -#ifdef __MSDOS__ - || FILENAME_CMPN (program_name + i, ".i", 2) == 0 -#endif - || isdigit (program_name[i + 1]))) /* a man page foo.1 */ - { - program_name[i] = 0; - break; - } - return program_name; -} - -DECLARE_INFO_COMMAND (info_goto_invocation_node, - _("Find the node describing program invocation")) -{ - const char *invocation_prompt = _("Find Invocation node of [%s]: "); - char *program_name, *line; - char *default_program_name, *prompt, *file_name; - NODE *top_node; - - /* Intuit the name of the program they are likely to want. - We use the file name of the current Info file as a hint. */ - file_name = window->node->parent ? window->node->parent - : window->node->filename; - default_program_name = program_name_from_file_name (file_name); - - prompt = (char *)xmalloc (strlen (default_program_name) + - strlen (invocation_prompt)); - sprintf (prompt, invocation_prompt, default_program_name); - line = info_read_in_echo_area (window, prompt); - free (prompt); - if (!line) - { - info_abort_key (window, 0, 0); - return; - } - if (*line) - program_name = line; - else - program_name = default_program_name; - - /* In interactive usage they'd probably expect us to begin looking - from the Top node. */ - top_node = info_get_node (file_name, NULL); - if (!top_node) - info_error (msg_cant_find_node, "Top", NULL); - - info_intuit_options_node (window, top_node, program_name); - free (line); - free (default_program_name); -} - -#if defined (HANDLE_MAN_PAGES) -DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it")) -{ - char *line; - - line = info_read_in_echo_area (window, _("Get Manpage: ")); - - if (!line) - { - info_abort_key (window, 0, 0); - return; - } - - canonicalize_whitespace (line); - - if (*line) - { - char *goto_command; - - goto_command = (char *)xmalloc - (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line)); - - sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line); - - info_parse_and_select (goto_command, window); - free (goto_command); - } - - free (line); - if (!info_error_was_printed) - window_clear_echo_area (); -} -#endif /* HANDLE_MAN_PAGES */ - -/* Move to the "Top" node in this file. */ -DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file")) -{ - info_parse_and_select ("Top", window); -} - -/* Move to the node "(dir)Top". */ -DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'")) -{ - info_parse_and_select ("(dir)Top", window); -} - - -/* Read the name of a node to kill. The list of available nodes comes - from the nodes appearing in the current window configuration. */ -static char * -read_nodename_to_kill (WINDOW *window) -{ - int iw; - char *nodename; - INFO_WINDOW *info_win; - REFERENCE **menu = NULL; - int menu_index = 0, menu_slots = 0; - char *default_nodename = xstrdup (active_window->node->nodename); - char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename)); - - sprintf (prompt, _("Kill node (%s): "), default_nodename); - - for (iw = 0; (info_win = info_windows[iw]); iw++) - { - REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - entry->label = xstrdup (info_win->window->node->nodename); - entry->filename = entry->nodename = (char *)NULL; - - add_pointer_to_array (entry, menu_index, menu, menu_slots, 10, - REFERENCE *); - } - - nodename = info_read_completing_in_echo_area (window, prompt, menu); - free (prompt); - info_free_references (menu); - if (nodename && !*nodename) - { - free (nodename); - nodename = default_nodename; - } - else - free (default_nodename); - - return nodename; -} - - -/* Delete NODENAME from this window, showing the most - recently selected node in this window. */ -static void -kill_node (WINDOW *window, char *nodename) -{ - int iw, i; - INFO_WINDOW *info_win; - NODE *temp; - - /* If there is no nodename to kill, quit now. */ - if (!nodename) - { - info_abort_key (window, 0, 0); - return; - } - - /* If there is a nodename, find it in our window list. */ - for (iw = 0; (info_win = info_windows[iw]); iw++) - if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0 - && info_win->window == window) - break; - - if (!info_win) - { - if (*nodename) - info_error (_("Cannot kill node `%s'"), nodename, NULL); - else - window_clear_echo_area (); - - return; - } - - /* If there are no more nodes left anywhere to view, complain and exit. */ - if (info_windows_index == 1 && info_windows[0]->nodes_index == 1) - { - info_error (_("Cannot kill the last node"), NULL, NULL); - return; - } - - /* INFO_WIN contains the node that the user wants to stop viewing. Delete - this node from the list of nodes previously shown in this window. */ - for (i = info_win->current; i < info_win->nodes_index; i++) - info_win->nodes[i] = info_win->nodes[i + 1]; - - /* There is one less node in this window's history list. */ - info_win->nodes_index--; - - /* Make this window show the most recent history node. */ - info_win->current = info_win->nodes_index - 1; - - /* If there aren't any nodes left in this window, steal one from the - next window. */ - if (info_win->current < 0) - { - INFO_WINDOW *stealer; - int which, pagetop; - long point; - - if (info_windows[iw + 1]) - stealer = info_windows[iw + 1]; - else - stealer = info_windows[0]; - - /* If the node being displayed in the next window is not the most - recently loaded one, get the most recently loaded one. */ - if ((stealer->nodes_index - 1) != stealer->current) - which = stealer->nodes_index - 1; - - /* Else, if there is another node behind the stealers current node, - use that one. */ - else if (stealer->current > 0) - which = stealer->current - 1; - - /* Else, just use the node appearing in STEALER's window. */ - else - which = stealer->current; - - /* Copy this node. */ - { - NODE *copy = xmalloc (sizeof (NODE)); - - temp = stealer->nodes[which]; - point = stealer->points[which]; - pagetop = stealer->pagetops[which]; - - copy->filename = temp->filename; - copy->parent = temp->parent; - copy->nodename = temp->nodename; - copy->contents = temp->contents; - copy->nodelen = temp->nodelen; - copy->flags = temp->flags; - copy->display_pos = temp->display_pos; - - temp = copy; - } - - window_set_node_of_window (info_win->window, temp); - window->point = point; - window->pagetop = pagetop; - remember_window_and_node (info_win->window, temp); - } - else - { - temp = info_win->nodes[info_win->current]; - temp->display_pos = info_win->points[info_win->current]; - window_set_node_of_window (info_win->window, temp); - } - - if (!info_error_was_printed) - window_clear_echo_area (); - - if (auto_footnotes_p) - info_get_or_remove_footnotes (window); -} - -/* Kill current node, thus going back one in the node history. I (karl) - do not think this is completely correct yet, because of the - window-changing stuff in kill_node, but it's a lot better than the - previous implementation, which did not account for nodes being - visited twice at all. */ -DECLARE_INFO_COMMAND (info_history_node, - _("Select the most recently selected node")) -{ - kill_node (window, active_window->node->nodename); -} - -/* Kill named node. */ -DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node")) -{ - char *nodename = read_nodename_to_kill (window); - kill_node (window, nodename); -} - - -/* Read the name of a file and select the entire file. */ -DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it")) -{ - char *line; - - line = info_read_in_echo_area (window, _("Find file: ")); - if (!line) - { - info_abort_key (active_window, 1, 0); - return; - } - - if (*line) - { - NODE *node; - - node = info_get_node (line, "*"); - if (!node) - { - if (info_recent_file_error) - info_error (info_recent_file_error, NULL, NULL); - else - info_error (_("Cannot find `%s'."), line, NULL); - } - else - info_set_node_of_window (1, window, node); - - free (line); - } - - if (!info_error_was_printed) - window_clear_echo_area (); -} - -/* **************************************************************** */ -/* */ -/* Dumping and Printing Nodes */ -/* */ -/* **************************************************************** */ - -#define VERBOSE_NODE_DUMPING -static void write_node_to_stream (NODE *node, FILE *stream); -static void dump_node_to_stream (char *filename, char *nodename, - FILE *stream, int dump_subnodes); -static void initialize_dumping (void); - -/* Dump the nodes specified by FILENAME and NODENAMES to the file named - in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump - the nodes which appear in the menu of each node dumped. */ -void -dump_nodes_to_file (char *filename, char **nodenames, - char *output_filename, int dump_subnodes) -{ - register int i; - FILE *output_stream; - - /* Get the stream to print the nodes to. Special case of an output - filename of "-" means to dump the nodes to stdout. */ - if (strcmp (output_filename, "-") == 0) - output_stream = stdout; - else - output_stream = fopen (output_filename, "w"); - - if (!output_stream) - { - info_error (_("Could not create output file `%s'."), - output_filename, NULL); - return; - } - - /* Print each node to stream. */ - initialize_dumping (); - for (i = 0; nodenames[i]; i++) - dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes); - - if (output_stream != stdout) - fclose (output_stream); - -#if defined (VERBOSE_NODE_DUMPING) - info_error (_("Done."), NULL, NULL); -#endif /* VERBOSE_NODE_DUMPING */ -} - -/* A place to remember already dumped nodes. */ -static char **dumped_already = (char **)NULL; -static int dumped_already_index = 0; -static int dumped_already_slots = 0; - -static void -initialize_dumping (void) -{ - dumped_already_index = 0; -} - -/* Get and print the node specified by FILENAME and NODENAME to STREAM. - If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear - in the menu of each node dumped. */ -static void -dump_node_to_stream (char *filename, char *nodename, - FILE *stream, int dump_subnodes) -{ - register int i; - NODE *node; - - node = info_get_node (filename, nodename); - - if (!node) - { - if (info_recent_file_error) - info_error (info_recent_file_error, NULL, NULL); - else - { - if (filename && *nodename != '(') - info_error (msg_cant_file_node, - filename_non_directory (filename), - nodename); - else - info_error (msg_cant_find_node, nodename, NULL); - } - return; - } - - /* If we have already dumped this node, don't dump it again. */ - for (i = 0; i < dumped_already_index; i++) - if (strcmp (node->nodename, dumped_already[i]) == 0) - { - free (node); - return; - } - add_pointer_to_array (node->nodename, dumped_already_index, dumped_already, - dumped_already_slots, 50, char *); - -#if defined (VERBOSE_NODE_DUMPING) - /* Maybe we should print some information about the node being output. */ - info_error (_("Writing node %s..."), node_printed_rep (node), NULL); -#endif /* VERBOSE_NODE_DUMPING */ - - write_node_to_stream (node, stream); - - /* If we are dumping subnodes, get the list of menu items in this node, - and dump each one recursively. */ - if (dump_subnodes) - { - REFERENCE **menu = (REFERENCE **)NULL; - - /* If this node is an Index, do not dump the menu references. */ - if (string_in_line ("Index", node->nodename) == -1) - menu = info_menu_of_node (node); - - if (menu) - { - for (i = 0; menu[i]; i++) - { - /* We don't dump Info files which are different than the - current one. */ - if (!menu[i]->filename) - dump_node_to_stream - (filename, menu[i]->nodename, stream, dump_subnodes); - } - info_free_references (menu); - } - } - - free (node); -} - -/* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump - the nodes which appear in the menu of each node dumped. */ -void -dump_node_to_file (NODE *node, char *filename, int dump_subnodes) -{ - FILE *output_stream; - char *nodes_filename; - - /* Get the stream to print this node to. Special case of an output - filename of "-" means to dump the nodes to stdout. */ - if (strcmp (filename, "-") == 0) - output_stream = stdout; - else - output_stream = fopen (filename, "w"); - - if (!output_stream) - { - info_error (_("Could not create output file `%s'."), filename, - NULL); - return; - } - - if (node->parent) - nodes_filename = node->parent; - else - nodes_filename = node->filename; - - initialize_dumping (); - dump_node_to_stream - (nodes_filename, node->nodename, output_stream, dump_subnodes); - - if (output_stream != stdout) - fclose (output_stream); - -#if defined (VERBOSE_NODE_DUMPING) - info_error (_("Done."), NULL, NULL); -#endif /* VERBOSE_NODE_DUMPING */ -} - -#if !defined (DEFAULT_INFO_PRINT_COMMAND) -# define DEFAULT_INFO_PRINT_COMMAND "lpr" -#endif /* !DEFAULT_INFO_PRINT_COMMAND */ - -DECLARE_INFO_COMMAND (info_print_node, - _("Pipe the contents of this node through INFO_PRINT_COMMAND")) -{ - print_node (window->node); -} - -/* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */ -void -print_node (NODE *node) -{ - FILE *printer_pipe; - char *print_command = getenv ("INFO_PRINT_COMMAND"); - int piping = 0; - - if (!print_command || !*print_command) - print_command = DEFAULT_INFO_PRINT_COMMAND; - - /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the - (default) text mode, since the printer drivers there need to see - DOS-style CRLF pairs at the end of each line. - - FIXME: if we are to support Mac-style text files, we might need - to convert the text here. */ - - /* INFO_PRINT_COMMAND which says ">file" means write to that file. - Presumably, the name of the file is the local printer device. */ - if (*print_command == '>') - printer_pipe = fopen (++print_command, "w"); - else - { - printer_pipe = popen (print_command, "w"); - piping = 1; - } - - if (!printer_pipe) - { - info_error (_("Cannot open pipe to `%s'."), print_command, NULL); - return; - } - -#if defined (VERBOSE_NODE_DUMPING) - /* Maybe we should print some information about the node being output. */ - info_error (_("Printing node %s..."), node_printed_rep (node), NULL); -#endif /* VERBOSE_NODE_DUMPING */ - - write_node_to_stream (node, printer_pipe); - if (piping) - pclose (printer_pipe); - else - fclose (printer_pipe); - -#if defined (VERBOSE_NODE_DUMPING) - info_error (_("Done."), NULL, NULL); -#endif /* VERBOSE_NODE_DUMPING */ -} - -static void -write_node_to_stream (NODE *node, FILE *stream) -{ - fwrite (node->contents, 1, node->nodelen, stream); -} - -/* **************************************************************** */ -/* */ -/* Info Searching Commands */ -/* */ -/* **************************************************************** */ - -/* Variable controlling the garbage collection of files briefly visited - during searches. Such files are normally gc'ed, unless they were - compressed to begin with. If this variable is non-zero, it says - to gc even those file buffer contents which had to be uncompressed. */ -int gc_compressed_files = 0; - -static void info_gc_file_buffers (void); -static void info_search_1 (WINDOW *window, int count, - unsigned char key, int case_sensitive, int ask_for_string); - -static char *search_string = (char *)NULL; -static int search_string_size = 0; -static int isearch_is_active = 0; - -static int last_search_direction = 0; -static int last_search_case_sensitive = 0; - -/* Return the file buffer which belongs to WINDOW's node. */ -FILE_BUFFER * -file_buffer_of_window (WINDOW *window) -{ - /* If this window has no node, then it has no file buffer. */ - if (!window->node) - return ((FILE_BUFFER *)NULL); - - if (window->node->parent) - return (info_find_file (window->node->parent)); - - if (window->node->filename) - return (info_find_file (window->node->filename)); - - return ((FILE_BUFFER *)NULL); -} - -/* Search for STRING in NODE starting at START. Return -1 if the string - was not found, or the location of the string if it was. If WINDOW is - passed as non-null, set the window's node to be NODE, its point to be - the found string, and readjust the window's pagetop. Final argument - DIR says which direction to search in. If it is positive, search - forward, else backwards. */ -long -info_search_in_node (char *string, NODE *node, long int start, - WINDOW *window, int dir, int case_sensitive) -{ - SEARCH_BINDING binding; - long offset; - - binding.buffer = node->contents; - binding.start = start; - binding.end = node->nodelen; - binding.flags = 0; - if (!case_sensitive) - binding.flags |= S_FoldCase; - - if (dir < 0) - { - binding.end = 0; - binding.flags |= S_SkipDest; - } - - if (binding.start < 0) - return (-1); - - /* For incremental searches, we always wish to skip past the string. */ - if (isearch_is_active) - binding.flags |= S_SkipDest; - - offset = (use_regex ? - regexp_search (string, &binding, node->nodelen): - search (string, &binding)); - - if (offset != -1 && window) - { - set_remembered_pagetop_and_point (window); - if (window->node != node) - window_set_node_of_window (window, node); - window->point = offset; - window_adjust_pagetop (window); - } - return (offset); -} - -/* Search NODE, looking for the largest possible match of STRING. Start the - search at START. Return the absolute position of the match, or -1, if - no part of the string could be found. */ -long -info_target_search_node (NODE *node, char *string, long int start) -{ - register int i; - long offset = 0; - char *target; - - target = xstrdup (string); - i = strlen (target); - - /* Try repeatedly searching for this string while removing words from - the end of it. */ - while (i) - { - target[i] = '\0'; - offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0); - - if (offset != -1) - break; - - /* Delete the last word from TARGET. */ - for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--); - } - free (target); - return (offset); -} - -/* Search for STRING starting in WINDOW at point. If the string is found - in this node, set point to that position. Otherwise, get the file buffer - associated with WINDOW's node, and search through each node in that file. - If the search fails, return non-zero, else zero. Side-effect window - leaving the node and point where the string was found current. */ -static int -info_search_internal (char *string, WINDOW *window, - int dir, int case_sensitive) -{ - register int i; - FILE_BUFFER *file_buffer; - char *initial_nodename; - long ret, start = 0; - - file_buffer = file_buffer_of_window (window); - initial_nodename = window->node->nodename; - - /* This used to begin from window->point, unless this was a repeated - search command. But invoking search with an argument loses with - that logic, since info_last_executed_command is then set to - info_add_digit_to_numeric_arg. I think there's no sense in - ``finding'' a string that is already under the cursor, anyway. */ - ret = info_search_in_node - (string, window->node, window->point + dir, window, dir, - case_sensitive); - - if (ret != -1) - { - /* We won! */ - if (!echo_area_is_active && !isearch_is_active) - window_clear_echo_area (); - return (0); - } - - /* The string wasn't found in the current node. Search through the - window's file buffer, iff the current node is not "*". */ - if (!file_buffer || (strcmp (initial_nodename, "*") == 0)) - return (-1); - - /* If this file has tags, search through every subfile, starting at - this node's subfile and node. Otherwise, search through the - file's node list. */ - if (file_buffer->tags) - { - register int current_tag = 0, number_of_tags; - char *last_subfile; - TAG *tag; - char *msg = NULL; - - /* Find number of tags and current tag. */ - last_subfile = (char *)NULL; - for (i = 0; file_buffer->tags[i]; i++) - if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0) - { - current_tag = i; - last_subfile = file_buffer->tags[i]->filename; - } - - number_of_tags = i; - - /* If there is no last_subfile, our tag wasn't found. */ - if (!last_subfile) - return (-1); - - /* Search through subsequent nodes, wrapping around to the top - of the info file until we find the string or return to this - window's node and point. */ - while (1) - { - NODE *node; - - /* Allow C-g to quit the search, failing it if pressed. */ - return_if_control_g (-1); - - /* Find the next tag that isn't an anchor. */ - for (i = current_tag + dir; i != current_tag; i += dir) - { - if (i < 0) - { - msg = N_("Search continued from the end of the document."); - i = number_of_tags - 1; - } - else if (i == number_of_tags) - { - msg = N_("Search continued from the beginning of the document."); - i = 0; - } - - tag = file_buffer->tags[i]; - if (tag->nodelen != 0) - break; - } - - /* If we got past out starting point, bail out. */ - if (i == current_tag) - return (-1); - current_tag = i; - - if (!echo_area_is_active && (last_subfile != tag->filename)) - { - window_message_in_echo_area - (_("Searching subfile %s ..."), - filename_non_directory (tag->filename), NULL); - - last_subfile = tag->filename; - } - - node = info_get_node (file_buffer->filename, tag->nodename); - - if (!node) - { - /* If not doing i-search... */ - if (!echo_area_is_active) - { - if (info_recent_file_error) - info_error (info_recent_file_error, NULL, NULL); - else - info_error (msg_cant_file_node, - filename_non_directory (file_buffer->filename), - tag->nodename); - } - return (-1); - } - - if (dir < 0) - start = tag->nodelen; - - ret = - info_search_in_node (string, node, start, window, dir, - case_sensitive); - - /* Did we find the string in this node? */ - if (ret != -1) - { - /* Yes! We win. */ - remember_window_and_node (window, node); - if (!echo_area_is_active) - { - if (msg) - window_message_in_echo_area ("%s", gettext (msg), NULL); - else - window_clear_echo_area (); - } - return (0); - } - - /* No. Free this node, and make sure that we haven't passed - our starting point. */ - free (node); - - if (strcmp (initial_nodename, tag->nodename) == 0) - return (-1); - } - } - return (-1); -} - -DECLARE_INFO_COMMAND (info_search_case_sensitively, - _("Read a string and search for it case-sensitively")) -{ - last_search_direction = count > 0 ? 1 : -1; - last_search_case_sensitive = 1; - info_search_1 (window, count, key, 1, 1); -} - -DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) -{ - last_search_direction = count > 0 ? 1 : -1; - last_search_case_sensitive = 0; - info_search_1 (window, count, key, 0, 1); -} - -DECLARE_INFO_COMMAND (info_search_backward, - _("Read a string and search backward for it")) -{ - last_search_direction = count > 0 ? -1 : 1; - last_search_case_sensitive = 0; - info_search_1 (window, -count, key, 0, 1); -} - -static void -info_search_1 (WINDOW *window, int count, unsigned char key, - int case_sensitive, int ask_for_string) -{ - char *line, *prompt; - int result, old_pagetop; - int direction; - - if (count < 0) - { - direction = -1; - count = -count; - } - else - { - direction = 1; - if (count == 0) - count = 1; /* for backward compatibility */ - } - - /* Read a string from the user, defaulting the search to SEARCH_STRING. */ - if (!search_string) - { - search_string = (char *)xmalloc (search_string_size = 100); - search_string[0] = '\0'; - } - - if (ask_for_string) - { - prompt = (char *)xmalloc (strlen (_("%s%s%s [%s]: ")) - + strlen (_("Regexp search")) - + strlen (_(" case-sensitively")) - + strlen (_(" backward")) - + strlen (search_string)); - - sprintf (prompt, _("%s%s%s [%s]: "), - use_regex ? _("Regexp search") : _("Search"), - case_sensitive ? _(" case-sensitively") : "", - direction < 0 ? _(" backward") : "", - search_string); - - line = info_read_in_echo_area (window, prompt); - free (prompt); - - if (!line) - { - info_abort_key (window, 0, 0); - return; - } - - if (*line) - { - if (strlen (line) + 1 > (unsigned int) search_string_size) - search_string = (char *) xrealloc - (search_string, (search_string_size += 50 + strlen (line))); - - strcpy (search_string, line); - free (line); - } - } - - /* If the search string includes upper-case letters, make the search - case-sensitive. */ - if (case_sensitive == 0) - for (line = search_string; *line; line++) - if (isupper (*line)) - { - case_sensitive = 1; - break; - } - - old_pagetop = active_window->pagetop; - for (result = 0; result == 0 && count--; ) - result = info_search_internal (search_string, - active_window, direction, case_sensitive); - - if (result != 0 && !info_error_was_printed) - info_error (_("Search failed."), NULL, NULL); - else if (old_pagetop != active_window->pagetop) - { - int new_pagetop; - - new_pagetop = active_window->pagetop; - active_window->pagetop = old_pagetop; - set_window_pagetop (active_window, new_pagetop); - if (auto_footnotes_p) - info_get_or_remove_footnotes (active_window); - } - - /* Perhaps free the unreferenced file buffers that were searched, but - not retained. */ - info_gc_file_buffers (); -} - -DECLARE_INFO_COMMAND (info_search_next, - _("Repeat last search in the same direction")) -{ - if (!last_search_direction) - info_error (_("No previous search string"), NULL, NULL); - else - info_search_1 (window, last_search_direction * count, - key, last_search_case_sensitive, 0); -} - -DECLARE_INFO_COMMAND (info_search_previous, - _("Repeat last search in the reverse direction")) -{ - if (!last_search_direction) - info_error (_("No previous search string"), NULL, NULL); - else - info_search_1 (window, -last_search_direction * count, - key, last_search_case_sensitive, 0); -} - -/* **************************************************************** */ -/* */ -/* Incremental Searching */ -/* */ -/* **************************************************************** */ - -static void incremental_search (WINDOW *window, int count, - unsigned char ignore); - -DECLARE_INFO_COMMAND (isearch_forward, - _("Search interactively for a string as you type it")) -{ - incremental_search (window, count, key); -} - -DECLARE_INFO_COMMAND (isearch_backward, - _("Search interactively for a string as you type it")) -{ - incremental_search (window, -count, key); -} - -/* Incrementally search for a string as it is typed. */ -/* The last accepted incremental search string. */ -static char *last_isearch_accepted = (char *)NULL; - -/* The current incremental search string. */ -static char *isearch_string = (char *)NULL; -static int isearch_string_index = 0; -static int isearch_string_size = 0; -static unsigned char isearch_terminate_search_key = ESC; - -/* Array of search states. */ -static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL; -static int isearch_states_index = 0; -static int isearch_states_slots = 0; - -/* Push the state of this search. */ -static void -push_isearch (WINDOW *window, int search_index, int direction, int failing) -{ - SEARCH_STATE *state; - - state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE)); - window_get_state (window, state); - state->search_index = search_index; - state->direction = direction; - state->failing = failing; - - add_pointer_to_array (state, isearch_states_index, isearch_states, - isearch_states_slots, 20, SEARCH_STATE *); -} - -/* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */ -static void -pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing) -{ - SEARCH_STATE *state; - - if (isearch_states_index) - { - isearch_states_index--; - state = isearch_states[isearch_states_index]; - window_set_state (window, state); - *search_index = state->search_index; - *direction = state->direction; - *failing = state->failing; - - free (state); - isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL; - } -} - -/* Free the memory used by isearch_states. */ -static void -free_isearch_states (void) -{ - register int i; - - for (i = 0; i < isearch_states_index; i++) - { - free (isearch_states[i]); - isearch_states[i] = (SEARCH_STATE *)NULL; - } - isearch_states_index = 0; -} - -/* Display the current search in the echo area. */ -static void -show_isearch_prompt (int dir, unsigned char *string, int failing_p) -{ - register int i; - const char *prefix; - char *prompt, *p_rep; - unsigned int prompt_len, p_rep_index, p_rep_size; - - if (dir < 0) - prefix = use_regex ? _("Regexp I-search backward: ") - : _("I-search backward: "); - else - prefix = use_regex ? _("Regexp I-search: ") - : _("I-search: "); - - p_rep_index = p_rep_size = 0; - p_rep = (char *)NULL; - for (i = 0; string[i]; i++) - { - char *rep; - - switch (string[i]) - { - case ' ': rep = " "; break; - case LFD: rep = "\\n"; break; - case TAB: rep = "\\t"; break; - default: - rep = pretty_keyname (string[i]); - } - if ((p_rep_index + strlen (rep) + 1) >= p_rep_size) - p_rep = (char *)xrealloc (p_rep, p_rep_size += 100); - - strcpy (p_rep + p_rep_index, rep); - p_rep_index += strlen (rep); - } - - prompt_len = strlen (prefix) + p_rep_index + 1; - if (failing_p) - prompt_len += strlen (_("Failing ")); - prompt = xmalloc (prompt_len); - sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix, - p_rep ? p_rep : ""); - - window_message_in_echo_area ("%s", prompt, NULL); - maybe_free (p_rep); - free (prompt); - display_cursor_at_point (active_window); -} - -static void -incremental_search (WINDOW *window, int count, unsigned char ignore) -{ - unsigned char key; - int last_search_result, search_result, dir; - SEARCH_STATE mystate, orig_state; - char *p; - int case_sensitive = 0; - - if (count < 0) - dir = -1; - else - dir = 1; - - last_search_result = search_result = 0; - - window_get_state (window, &orig_state); - - isearch_string_index = 0; - if (!isearch_string_size) - isearch_string = (char *)xmalloc (isearch_string_size = 50); - - /* Show the search string in the echo area. */ - isearch_string[isearch_string_index] = '\0'; - show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); - - isearch_is_active = 1; - - while (isearch_is_active) - { - VFunction *func = (VFunction *)NULL; - int quoted = 0; - - /* If a recent display was interrupted, then do the redisplay now if - it is convenient. */ - if (!info_any_buffered_input_p () && display_was_interrupted_p) - { - display_update_one_window (window); - display_cursor_at_point (active_window); - } - - /* Read a character and dispatch on it. */ - key = info_get_input_char (); - window_get_state (window, &mystate); - - if (key == DEL || key == Control ('h')) - { - /* User wants to delete one level of search? */ - if (!isearch_states_index) - { - terminal_ring_bell (); - continue; - } - else - { - pop_isearch - (window, &isearch_string_index, &dir, &search_result); - isearch_string[isearch_string_index] = '\0'; - show_isearch_prompt (dir, (unsigned char *) isearch_string, - search_result); - goto after_search; - } - } - else if (key == Control ('q')) - { - key = info_get_input_char (); - quoted = 1; - } - - /* We are about to search again, or quit. Save the current search. */ - push_isearch (window, isearch_string_index, dir, search_result); - - if (quoted) - goto insert_and_search; - - if (!Meta_p (key) || key > 32) - { - /* If this key is not a keymap, get its associated function, - if any. If it is a keymap, then it's probably ESC from an - arrow key, and we handle that case below. */ - char type = window->keymap[key].type; - func = type == ISFUNC - ? InfoFunction(window->keymap[key].function) - : NULL; /* function member is a Keymap if ISKMAP */ - - if (isprint (key) || (type == ISFUNC && func == NULL)) - { - insert_and_search: - - if (isearch_string_index + 2 >= isearch_string_size) - isearch_string = (char *)xrealloc - (isearch_string, isearch_string_size += 100); - - isearch_string[isearch_string_index++] = key; - isearch_string[isearch_string_index] = '\0'; - goto search_now; - } - else if (func == (VFunction *) isearch_forward - || func == (VFunction *) isearch_backward) - { - /* If this key invokes an incremental search, then this - means that we will either search again in the same - direction, search again in the reverse direction, or - insert the last search string that was accepted through - incremental searching. */ - if ((func == (VFunction *) isearch_forward && dir > 0) || - (func == (VFunction *) isearch_backward && dir < 0)) - { - /* If the user has typed no characters, then insert the - last successful search into the current search string. */ - if (isearch_string_index == 0) - { - /* Of course, there must be something to insert. */ - if (last_isearch_accepted) - { - if (strlen ((char *) last_isearch_accepted) + 1 - >= (unsigned int) isearch_string_size) - isearch_string = (char *) - xrealloc (isearch_string, - isearch_string_size += 10 + - strlen (last_isearch_accepted)); - strcpy (isearch_string, last_isearch_accepted); - isearch_string_index = strlen (isearch_string); - goto search_now; - } - else - continue; - } - else - { - /* Search again in the same direction. This means start - from a new place if the last search was successful. */ - if (search_result == 0) - window->point += dir; - } - } - else - { - /* Reverse the direction of the search. */ - dir = -dir; - } - } - else if (func == (VFunction *) info_abort_key) - { - /* If C-g pressed, and the search is failing, pop the search - stack back to the last unfailed search. */ - if (isearch_states_index && (search_result != 0)) - { - terminal_ring_bell (); - while (isearch_states_index && (search_result != 0)) - pop_isearch - (window, &isearch_string_index, &dir, &search_result); - isearch_string[isearch_string_index] = '\0'; - show_isearch_prompt (dir, (unsigned char *) isearch_string, - search_result); - continue; - } - else - goto exit_search; - } - else - goto exit_search; - } - else - { - exit_search: - /* The character is not printable, or it has a function which is - non-null. Exit the search, remembering the search string. If - the key is not the same as the isearch_terminate_search_key, - then push it into pending input. */ - if (isearch_string_index && func != (VFunction *) info_abort_key) - { - maybe_free (last_isearch_accepted); - last_isearch_accepted = xstrdup (isearch_string); - } - - /* If the key is the isearch_terminate_search_key, but some buffered - input is pending, it is almost invariably because the ESC key is - actually the beginning of an escape sequence, like in case they - pressed an arrow key. So don't gobble the ESC key, push it back - into pending input. */ - /* FIXME: this seems like a kludge! We need a more reliable - mechanism to know when ESC is a separate key and when it is - part of an escape sequence. */ - if (key != RET /* Emacs addicts want RET to get lost */ - && (key != isearch_terminate_search_key - || info_any_buffered_input_p ())) - info_set_pending_input (key); - - if (func == (VFunction *) info_abort_key) - { - if (isearch_states_index) - window_set_state (window, &orig_state); - } - - if (!echo_area_is_active) - window_clear_echo_area (); - - if (auto_footnotes_p) - info_get_or_remove_footnotes (active_window); - - isearch_is_active = 0; - continue; - } - - /* Search for the contents of isearch_string. */ - search_now: - show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); - - /* If the search string includes upper-case letters, make the - search case-sensitive. */ - for (p = isearch_string; *p; p++) - if (isupper (*p)) - { - case_sensitive = 1; - break; - } - - /* Regex isearch means we better search again every time. We - might have had a failed search for "\", for example, but now we - have "\.". */ - if (use_regex) - { - search_result = info_search_internal (isearch_string, - window, dir, case_sensitive); - } - else if (search_result == 0) - { /* I don't understand why we test for search_result being - zero; it means if the search failed we don't search again. - Clearly the special check isn't applicable to regex search - anyway. --karl, 11feb08. */ - /* Check to see if the current search string is right here. If - we are looking at it, then don't bother calling the search - function. */ - if (((dir < 0) && - ((case_sensitive ? strncmp : mbsncasecmp) - (window->node->contents + window->point, - isearch_string, isearch_string_index) == 0)) || - ((dir > 0) && - ((window->point - isearch_string_index) >= 0) && - ((case_sensitive ? strncmp : mbsncasecmp) - (window->node->contents + - (window->point - (isearch_string_index - 1)), - isearch_string, isearch_string_index) == 0))) - { - if (dir > 0) - window->point++; - } - else - search_result = info_search_internal (isearch_string, - window, dir, case_sensitive); - } - - /* If this search failed, and we didn't already have a failed search, - then ring the terminal bell. */ - if (search_result != 0 && last_search_result == 0) - terminal_ring_bell (); - - after_search: - show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); - - if (search_result == 0) - { - if ((mystate.node == window->node) && - (mystate.pagetop != window->pagetop)) - { - int newtop = window->pagetop; - window->pagetop = mystate.pagetop; - set_window_pagetop (window, newtop); - } - display_update_one_window (window); - display_cursor_at_point (window); - } - - last_search_result = search_result; - } - - /* Free the memory used to remember each search state. */ - free_isearch_states (); - - /* Perhaps GC some file buffers. */ - info_gc_file_buffers (); - - /* After searching, leave the window in the correct state. */ - if (!echo_area_is_active) - window_clear_echo_area (); -} - -/* GC some file buffers. A file buffer can be gc-ed if there we have - no nodes in INFO_WINDOWS that reference this file buffer's contents. - Garbage collecting a file buffer means to free the file buffers - contents. */ -static void -info_gc_file_buffers (void) -{ - register int fb_index, iw_index, i; - register FILE_BUFFER *fb; - register INFO_WINDOW *iw; - - if (!info_loaded_files) - return; - - for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++) - { - int fb_referenced_p = 0; - - /* If already gc-ed, do nothing. */ - if (!fb->contents) - continue; - - /* If this file had to be uncompressed, check to see if we should - gc it. This means that the user-variable "gc-compressed-files" - is non-zero. */ - if ((fb->flags & N_IsCompressed) && !gc_compressed_files) - continue; - - /* If this file's contents are not gc-able, move on. */ - if (fb->flags & N_CannotGC) - continue; - - /* Check each INFO_WINDOW to see if it has any nodes which reference - this file. */ - for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++) - { - for (i = 0; iw->nodes && iw->nodes[i]; i++) - { - if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) || - (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0)) - { - fb_referenced_p = 1; - break; - } - } - } - - /* If this file buffer wasn't referenced, free its contents. */ - if (!fb_referenced_p) - { - free (fb->contents); - fb->contents = (char *)NULL; - } - } -} - -/* **************************************************************** */ -/* */ -/* Traversing and Selecting References */ -/* */ -/* **************************************************************** */ - -/* Move to the next or previous cross reference in this node. */ -static int -info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir) -{ - long firstmenu, firstxref; - long nextmenu, nextxref; - long placement = -1; - long start = 0; - NODE *node = window->node; - int save_use_regex = use_regex; - - /* Most of our keywords contain * characters; don't use regexes. */ - use_regex = 0; - - if (dir < 0) - start = node->nodelen; - - /* This search is only allowed to fail if there is no menu or cross - reference in the current node. Otherwise, the first menu or xref - found is moved to. */ - - firstmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0); - - /* FIRSTMENU may point directly to the line defining the menu. Skip that - and go directly to the first item. */ - - if (firstmenu != -1) - { - char *text = node->contents + firstmenu; - - if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) - firstmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0); - } - - firstxref = - info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0); - -#if defined (HANDLE_MAN_PAGES) - if ((firstxref == -1) && (node->flags & N_IsManPage)) - { - firstxref = locate_manpage_xref (node, start, dir); - } -#endif /* HANDLE_MAN_PAGES */ - - if (firstmenu == -1 && firstxref == -1) - { - if (!cursor_movement_scrolls_p) - info_error (msg_no_xref_node, NULL, NULL); - use_regex = save_use_regex; - return cursor_movement_scrolls_p; - } - - /* There is at least one cross reference or menu entry in this node. - Try hard to find the next available one. */ - - nextmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); - - nextxref = info_search_in_node - (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); - -#if defined (HANDLE_MAN_PAGES) - if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1)) - nextxref = locate_manpage_xref (node, window->point + dir, dir); -#endif /* HANDLE_MAN_PAGES */ - - /* Ignore "Menu:" as a menu item. */ - if (nextmenu != -1) - { - char *text = node->contents + nextmenu; - - if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) - nextmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0); - } - - /* No more searches, back to whatever the user wanted. */ - use_regex = save_use_regex; - - /* If there is both a next menu entry, and a next xref entry, choose the - one which occurs first. Otherwise, select the one which actually - appears in this node following point. */ - if (nextmenu != -1 && nextxref != -1) - { - if (((dir == 1) && (nextmenu < nextxref)) || - ((dir == -1) && (nextmenu > nextxref))) - placement = nextmenu + 1; - else - placement = nextxref; - } - else if (nextmenu != -1) - placement = nextmenu + 1; - else if (nextxref != -1) - placement = nextxref; - - /* If there was neither a menu or xref entry appearing in this node after - point, choose the first menu or xref entry appearing in this node. */ - if (placement == -1) - { - if (cursor_movement_scrolls_p) - return 1; - else - { - if (firstmenu != -1 && firstxref != -1) - { - if (((dir == 1) && (firstmenu < firstxref)) || - ((dir == -1) && (firstmenu > firstxref))) - placement = firstmenu + 1; - else - placement = firstxref; - } - else if (firstmenu != -1) - placement = firstmenu + 1; - else - placement = firstxref; - } - } - window->point = placement; - window_adjust_pagetop (window); - window->flags |= W_UpdateWindow; - return 0; -} - -DECLARE_INFO_COMMAND (info_move_to_prev_xref, - _("Move to the previous cross reference")) -{ - if (count < 0) - info_move_to_prev_xref (window, -count, key); - else - { - while (info_move_to_xref (window, count, key, -1)) - { - info_error_was_printed = 0; - if (backward_move_node_structure (window, info_scroll_behaviour)) - break; - move_to_new_line (window->line_count, window->line_count - 1, - window); - } - } -} - -DECLARE_INFO_COMMAND (info_move_to_next_xref, - _("Move to the next cross reference")) -{ - if (count < 0) - info_move_to_next_xref (window, -count, key); - else - { - /* Note: This can cause some blinking when the next cross reference is - located several nodes further. This effect can be easily suppressed - by setting display_inhibited to 1, however this will also make - error messages to be dumped on stderr, instead on the echo area. */ - while (info_move_to_xref (window, count, key, 1)) - { - info_error_was_printed = 0; - if (forward_move_node_structure (window, info_scroll_behaviour)) - break; - move_to_new_line (0, 0, window); - } - } -} - -/* Select the menu item or reference that appears on this line. */ -DECLARE_INFO_COMMAND (info_select_reference_this_line, - _("Select reference or menu item appearing on this line")) -{ - char *line; - - if (window->line_starts) - line = window->line_starts[window_line_of_point (window)]; - else - line = ""; - - /* If this line contains a menu item, select that one. */ - if (strncmp ("* ", line, 2) == 0) - info_menu_or_ref_item (window, count, key, info_menu_of_node, 0); - else - info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0); -} - -/* **************************************************************** */ -/* */ -/* Miscellaneous Info Commands */ -/* */ -/* **************************************************************** */ - -/* What to do when C-g is pressed in a window. */ -DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation")) -{ - /* If error printing doesn't oridinarily ring the bell, do it now, - since C-g always rings the bell. Otherwise, let the error printer - do it. */ - if (!info_error_rings_bell_p) - terminal_ring_bell (); - info_error (_("Quit"), NULL, NULL); - - info_initialize_numeric_arg (); - info_clear_pending_input (); - info_last_executed_command = (VFunction *)NULL; -} - -/* Move the cursor to the desired line of the window. */ -DECLARE_INFO_COMMAND (info_move_to_window_line, - _("Move the cursor to a specific line of the window")) -{ - int line; - - /* With no numeric argument of any kind, default to the center line. */ - if (!info_explicit_arg && count == 1) - line = (window->height / 2) + window->pagetop; - else - { - if (count < 0) - line = (window->height + count) + window->pagetop; - else - line = window->pagetop + count; - } - - /* If the line doesn't appear in this window, make it do so. */ - if ((line - window->pagetop) >= window->height) - line = window->pagetop + (window->height - 1); - - /* If the line is too small, make it fit. */ - if (line < window->pagetop) - line = window->pagetop; - - /* If the selected line is past the bottom of the node, force it back. */ - if (line >= window->line_count) - line = window->line_count - 1; - - window->point = (window->line_starts[line] - window->node->contents); -} - -/* Clear the screen and redraw its contents. Given a numeric argument, - move the line the cursor is on to the COUNT'th line of the window. */ -DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display")) -{ - if ((!info_explicit_arg && count == 1) || echo_area_is_active) - { - terminal_clear_screen (); - display_clear_display (the_display); - window_mark_chain (windows, W_UpdateWindow); - display_update_display (windows); - } - else - { - int desired_line, point_line; - int new_pagetop; - - point_line = window_line_of_point (window) - window->pagetop; - - if (count < 0) - desired_line = window->height + count; - else - desired_line = count; - - if (desired_line < 0) - desired_line = 0; - - if (desired_line >= window->height) - desired_line = window->height - 1; - - if (desired_line == point_line) - return; - - new_pagetop = window->pagetop + (point_line - desired_line); - - set_window_pagetop (window, new_pagetop); - } -} -/* This command does nothing. It is the fact that a key is bound to it - that has meaning. See the code at the top of info_session (). */ -DECLARE_INFO_COMMAND (info_quit, _("Quit using Info")) -{} - - -/* **************************************************************** */ -/* */ -/* Reading Keys and Dispatching on Them */ -/* */ -/* **************************************************************** */ - -/* Declaration only. Special cased in info_dispatch_on_key (). - Doc string is to avoid ugly results with describe_key etc. */ -DECLARE_INFO_COMMAND (info_do_lowercase_version, - _("Run command bound to this key's lowercase variant")) -{} - -static void -dispatch_error (char *keyseq) -{ - char *rep; - - rep = pretty_keyseq (keyseq); - - if (!echo_area_is_active) - info_error (_("Unknown command (%s)."), rep, NULL); - else - { - char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid"))); - sprintf (temp, _("`%s' is invalid"), rep); - terminal_ring_bell (); - inform_in_echo_area (temp); - free (temp); - } -} - -/* Keeping track of key sequences. */ -static char *info_keyseq = (char *)NULL; -static int info_keyseq_index = 0; -static int info_keyseq_size = 0; -static int info_keyseq_displayed_p = 0; - -/* Initialize the length of the current key sequence. */ -void -initialize_keyseq (void) -{ - info_keyseq_index = 0; - info_keyseq_displayed_p = 0; -} - -/* Add CHARACTER to the current key sequence. */ -void -add_char_to_keyseq (char character) -{ - if (info_keyseq_index + 2 >= info_keyseq_size) - info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10); - - info_keyseq[info_keyseq_index++] = character; - info_keyseq[info_keyseq_index] = '\0'; -} - -/* Display the current value of info_keyseq. If argument EXPECTING is - non-zero, input is expected to be read after the key sequence is - displayed, so add an additional prompting character to the sequence. */ -static void -display_info_keyseq (int expecting_future_input) -{ - char *rep; - - rep = pretty_keyseq (info_keyseq); - if (expecting_future_input) - strcat (rep, "-"); - - if (echo_area_is_active) - inform_in_echo_area (rep); - else - { - window_message_in_echo_area (rep, NULL, NULL); - display_cursor_at_point (active_window); - } - info_keyseq_displayed_p = 1; -} - -/* Called by interactive commands to read a keystroke. */ -unsigned char -info_get_another_input_char (void) -{ - int ready = !info_keyseq_displayed_p; /* ready if new and pending key */ - - /* If there isn't any input currently available, then wait a - moment looking for input. If we don't get it fast enough, - prompt a little bit with the current key sequence. */ - if (!info_keyseq_displayed_p) - { - ready = 1; - if (!info_any_buffered_input_p () && - !info_input_pending_p ()) - { -#if defined (FD_SET) - struct timeval timer; - fd_set readfds; - - FD_ZERO (&readfds); - FD_SET (fileno (info_input_stream), &readfds); - timer.tv_sec = 1; - timer.tv_usec = 750; - ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); -#else - ready = 0; -#endif /* FD_SET */ - } - } - - if (!ready) - display_info_keyseq (1); - - return (info_get_input_char ()); -} - -/* Do the command associated with KEY in MAP. If the associated command is - really a keymap, then read another key, and dispatch into that map. */ -void -info_dispatch_on_key (unsigned char key, Keymap map) -{ -#if !defined(INFOKEY) - if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert)) - { - if (map[ESC].type == ISKMAP) - { - map = (Keymap)map[ESC].function; - add_char_to_keyseq (ESC); - key = UnMeta (key); - info_dispatch_on_key (key, map); - } - else - { - dispatch_error (info_keyseq); - } - return; - } -#endif /* INFOKEY */ - - switch (map[key].type) - { - case ISFUNC: - { - VFunction *func; - - func = InfoFunction(map[key].function); - if (func != (VFunction *)NULL) - { - /* Special case info_do_lowercase_version (). */ - if (func == (VFunction *) info_do_lowercase_version) - { -#if defined(INFOKEY) - unsigned char lowerkey; - - lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key); - if (lowerkey == key) - { - add_char_to_keyseq (key); - dispatch_error (info_keyseq); - return; - } - info_dispatch_on_key (lowerkey, map); -#else /* !INFOKEY */ - info_dispatch_on_key (tolower (key), map); -#endif /* INFOKEY */ - return; - } - - add_char_to_keyseq (key); - - if (info_keyseq_displayed_p) - display_info_keyseq (0); - - { - WINDOW *where; - - where = active_window; - (*InfoFunction(map[key].function)) - (active_window, info_numeric_arg * info_numeric_arg_sign, key); - - /* If we have input pending, then the last command was a prefix - command. Don't change the value of the last function vars. - Otherwise, remember the last command executed in the var - appropriate to the window in which it was executed. */ - if (!info_input_pending_p ()) - { - if (where == the_echo_area) - ea_last_executed_command = InfoFunction(map[key].function); - else - info_last_executed_command = InfoFunction(map[key].function); - } - } - } - else - { - add_char_to_keyseq (key); - dispatch_error (info_keyseq); - return; - } - } - break; - - case ISKMAP: - add_char_to_keyseq (key); - if (map[key].function != (InfoCommand *)NULL) - { - unsigned char newkey; - - newkey = info_get_another_input_char (); - info_dispatch_on_key (newkey, (Keymap)map[key].function); - } - else - { - dispatch_error (info_keyseq); - return; - } - break; - } -} - -/* **************************************************************** */ -/* */ -/* Numeric Arguments */ -/* */ -/* **************************************************************** */ - -/* Handle C-u style numeric args, as well as M--, and M-digits. */ - -/* Non-zero means that an explicit argument has been passed to this - command, as in C-u C-v. */ -int info_explicit_arg = 0; - -/* The sign of the numeric argument. */ -int info_numeric_arg_sign = 1; - -/* The value of the argument itself. */ -int info_numeric_arg = 1; - -/* Add the current digit to the argument in progress. */ -DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg, - _("Add this digit to the current numeric argument")) -{ - info_numeric_arg_digit_loop (window, 0, key); -} - -/* C-u, universal argument. Multiply the current argument by 4. - Read a key. If the key has nothing to do with arguments, then - dispatch on it. If the key is the abort character then abort. */ -DECLARE_INFO_COMMAND (info_universal_argument, - _("Start (or multiply by 4) the current numeric argument")) -{ - info_numeric_arg *= 4; - info_numeric_arg_digit_loop (window, 0, 0); -} - -/* Create a default argument. */ -void -info_initialize_numeric_arg (void) -{ - info_numeric_arg = info_numeric_arg_sign = 1; - info_explicit_arg = 0; -} - -DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop, - _("Internally used by \\[universal-argument]")) -{ - unsigned char pure_key; - Keymap keymap = window->keymap; - - while (1) - { - if (key) - pure_key = key; - else - { - if (display_was_interrupted_p && !info_any_buffered_input_p ()) - display_update_display (windows); - - if (active_window != the_echo_area) - display_cursor_at_point (active_window); - - pure_key = key = info_get_another_input_char (); - -#if !defined(INFOKEY) - if (Meta_p (key)) - add_char_to_keyseq (ESC); - - add_char_to_keyseq (UnMeta (key)); -#else /* defined(INFOKEY) */ - add_char_to_keyseq (key); -#endif /* defined(INFOKEY) */ - } - -#if !defined(INFOKEY) - if (Meta_p (key)) - key = UnMeta (key); -#endif /* !defined(INFOKEY) */ - - if (keymap[key].type == ISFUNC - && InfoFunction(keymap[key].function) - == (VFunction *) info_universal_argument) - { - info_numeric_arg *= 4; - key = 0; - continue; - } - -#if defined(INFOKEY) - if (Meta_p (key)) - key = UnMeta (key); -#endif /* !defined(INFOKEY) */ - - - if (isdigit (key)) - { - if (info_explicit_arg) - info_numeric_arg = (info_numeric_arg * 10) + (key - '0'); - else - info_numeric_arg = (key - '0'); - info_explicit_arg = 1; - } - else - { - if (key == '-' && !info_explicit_arg) - { - info_numeric_arg_sign = -1; - info_numeric_arg = 1; - } - else - { - info_keyseq_index--; - info_dispatch_on_key (pure_key, keymap); - return; - } - } - key = 0; - } -} - -/* **************************************************************** */ -/* */ -/* Input Character Buffering */ -/* */ -/* **************************************************************** */ - -/* Character waiting to be read next. */ -static int pending_input_character = 0; - -/* How to make there be no pending input. */ -static void -info_clear_pending_input (void) -{ - pending_input_character = 0; -} - -/* How to set the pending input character. */ -static void -info_set_pending_input (unsigned char key) -{ - pending_input_character = key; -} - -/* How to see if there is any pending input. */ -unsigned char -info_input_pending_p (void) -{ - return (pending_input_character); -} - -/* Largest number of characters that we can read in advance. */ -#define MAX_INFO_INPUT_BUFFERING 512 - -static int pop_index = 0, push_index = 0; -static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING]; - -/* Add KEY to the buffer of characters to be read. */ -static void -info_push_typeahead (unsigned char key) -{ - /* Flush all pending input in the case of C-g pressed. */ - if (key == Control ('g')) - { - push_index = pop_index; - info_set_pending_input (Control ('g')); - } - else - { - info_input_buffer[push_index++] = key; - if ((unsigned int) push_index >= sizeof (info_input_buffer)) - push_index = 0; - } -} - -/* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */ -static int -info_input_buffer_space_available (void) -{ - if (pop_index > push_index) - return (pop_index - push_index); - else - return (sizeof (info_input_buffer) - (push_index - pop_index)); -} - -/* Get a key from the buffer of characters to be read. - Return the key in KEY. - Result is non-zero if there was a key, or 0 if there wasn't. */ -static int -info_get_key_from_typeahead (unsigned char *key) -{ - if (push_index == pop_index) - return (0); - - *key = info_input_buffer[pop_index++]; - - if ((unsigned int) pop_index >= sizeof (info_input_buffer)) - pop_index = 0; - - return (1); -} - -int -info_any_buffered_input_p (void) -{ - info_gather_typeahead (); - return (push_index != pop_index); -} - -/* If characters are available to be read, then read them and stuff them into - info_input_buffer. Otherwise, do nothing. */ -void -info_gather_typeahead (void) -{ - register int i = 0; - int tty, space_avail; - long chars_avail; - unsigned char input[MAX_INFO_INPUT_BUFFERING]; - - tty = fileno (info_input_stream); - chars_avail = 0; - - space_avail = info_input_buffer_space_available (); - - /* If we can just find out how many characters there are to read, do so. */ -#if defined (FIONREAD) - { - ioctl (tty, FIONREAD, &chars_avail); - - if (chars_avail > space_avail) - chars_avail = space_avail; - - if (chars_avail) - chars_avail = read (tty, &input[0], chars_avail); - } -#else /* !FIONREAD */ -# if defined (O_NDELAY) - { - int flags; - - flags = fcntl (tty, F_GETFL, 0); - - fcntl (tty, F_SETFL, (flags | O_NDELAY)); - chars_avail = read (tty, &input[0], space_avail); - fcntl (tty, F_SETFL, flags); - - if (chars_avail == -1) - chars_avail = 0; - } -# else /* !O_NDELAY */ -# ifdef __DJGPP__ - { - extern long pc_term_chars_avail (void); - - if (isatty (tty)) - chars_avail = pc_term_chars_avail (); - else - { - /* We could be more accurate by calling ltell, but we have no idea - whether tty is buffered by stdio functions, and if so, how many - characters are already waiting in the buffer. So we punt. */ - struct stat st; - - if (fstat (tty, &st) < 0) - chars_avail = 1; - else - chars_avail = st.st_size; - } - if (chars_avail > space_avail) - chars_avail = space_avail; - if (chars_avail) - chars_avail = read (tty, &input[0], chars_avail); - } -# endif/* __DJGPP__ */ -# endif /* O_NDELAY */ -#endif /* !FIONREAD */ - - while (i < chars_avail) - { - info_push_typeahead (input[i]); - i++; - } -} - -/* How to read a single character. */ -unsigned char -info_get_input_char (void) -{ - unsigned char keystroke; - - info_gather_typeahead (); - - if (pending_input_character) - { - keystroke = pending_input_character; - pending_input_character = 0; - } - else if (info_get_key_from_typeahead (&keystroke) == 0) - { - int rawkey; - unsigned char c; - int tty = fileno (info_input_stream); - - /* Using stream I/O causes FIONREAD etc to fail to work - so unless someone can find a portable way of finding - out how many characters are currently buffered, we - should stay with away from stream I/O. - --Egil Kvaleberg <egilk@sn.no>, January 1997. */ -#ifdef EINTR - /* Keep reading if we got EINTR, so that we don't just exit. - --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>, - 22 Dec 1997. */ - { - int n; - do - n = read (tty, &c, 1); - while (n == -1 && errno == EINTR); - rawkey = n == 1 ? c : EOF; - } -#else - rawkey = (read (tty, &c, 1) == 1) ? c : EOF; -#endif - - keystroke = rawkey; - - if (rawkey == EOF) - { - if (info_input_stream != stdin) - { - fclose (info_input_stream); - info_input_stream = stdin; - tty = fileno (info_input_stream); - display_inhibited = 0; - display_update_display (windows); - display_cursor_at_point (active_window); - rawkey = (read (tty, &c, 1) == 1) ? c : EOF; - keystroke = rawkey; - } - - if (rawkey == EOF) - { - terminal_unprep_terminal (); - close_dribble_file (); - xexit (0); - } - } - } - - if (info_dribble_file) - dribble (keystroke); - - return keystroke; -} |