2000-01-17 10:45:43 +00:00
|
|
|
|
/* session.c -- user windowing interface to Info.
|
2005-05-23 10:50:32 +00:00
|
|
|
|
$Id: session.c,v 1.16 2004/12/14 00:15:36 karl Exp $
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
|
2003-06-18 13:01:38 +00:00
|
|
|
|
Free Software Foundation, Inc.
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
Originally written by Brian Fox (bfox@ai.mit.edu). */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#include "info.h"
|
2005-05-23 10:50:32 +00:00
|
|
|
|
#include "search.h"
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#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
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
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);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Running an Info Session */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* The place that we are reading input from. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
static FILE *info_input_stream = NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* The last executed command. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
VFunction *info_last_executed_command = NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
INFO_WINDOW **info_windows = NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Where to add the next window, if we need to add one. */
|
|
|
|
|
static int info_windows_index = 0;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Number of slots allocated to `info_windows'. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
static int info_windows_slots = 0;
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
void remember_window_and_node (WINDOW *window, NODE *node);
|
|
|
|
|
void forget_window_and_nodes (WINDOW *window);
|
|
|
|
|
void display_startup_message_and_start (void);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
begin_multiple_window_info_session (char *filename, char **nodenames)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
WINDOW *window = (WINDOW *)NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; nodenames[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
node = info_get_node (filename, nodenames[i]);
|
|
|
|
|
|
|
|
|
|
if (!node)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this is the first node, initialize the info session. */
|
|
|
|
|
if (!window)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
initialize_info_session (node, 1);
|
|
|
|
|
window = active_window;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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. */
|
2000-01-17 10:45:43 +00:00
|
|
|
|
WINDOW *win, *largest = NULL;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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);
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_find_window, NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_session ();
|
2000-01-17 10:45:43 +00:00
|
|
|
|
xexit (0);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_win_too_small, NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_session ();
|
2000-01-17 10:45:43 +00:00
|
|
|
|
xexit (0);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
begin_info_session_with_error (NODE *initial_node, char *format,
|
|
|
|
|
void *arg1, void *arg2)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
initialize_info_session (initial_node, 1);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_error (format, arg1, arg2);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
info_session ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Start an info session with INITIAL_NODE. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
begin_info_session (NODE *initial_node)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
initialize_info_session (initial_node, 1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
display_startup_message_and_start ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
display_startup_message_and_start (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *format;
|
|
|
|
|
|
|
|
|
|
format = replace_in_documentation
|
2005-05-23 10:50:32 +00:00
|
|
|
|
((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."),
|
|
|
|
|
0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area (format, VERSION, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
info_session ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Run an info session with an already initialized window and node. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_session (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
display_update_display (windows);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_last_executed_command = NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_read_and_dispatch (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char key;
|
|
|
|
|
int done;
|
|
|
|
|
done = 0;
|
|
|
|
|
|
|
|
|
|
while (!done && !quit_info_immediately)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
int lk = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If we haven't just gone up or down a line, there is no
|
1999-01-14 19:56:32 +00:00
|
|
|
|
goal column for this window. */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if ((info_last_executed_command != (VFunction *) info_next_line) &&
|
|
|
|
|
(info_last_executed_command != (VFunction *) info_prev_line))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
active_window->goal_column = -1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
lk = echo_area_last_command_was_kill;
|
|
|
|
|
echo_area_prep_read ();
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!info_any_buffered_input_p ())
|
1999-01-14 19:56:32 +00:00
|
|
|
|
display_update_display (windows);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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
|
1999-01-14 19:56:32 +00:00
|
|
|
|
the echo_area if it is not currently active. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!echo_area_is_active)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window_clear_echo_area ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
info_error_was_printed = 0;
|
|
|
|
|
|
|
|
|
|
/* Do the selected command. */
|
|
|
|
|
info_dispatch_on_key (key, active_window->keymap);
|
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (ea_last_executed_command == (VFunction *) ea_newline ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_aborted_echo_area)
|
|
|
|
|
{
|
|
|
|
|
ea_last_executed_command = (VFunction *)NULL;
|
|
|
|
|
done = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (info_last_executed_command == (VFunction *) info_quit)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
quit_info_immediately = 1;
|
|
|
|
|
}
|
2005-05-23 10:50:32 +00:00
|
|
|
|
else if (info_last_executed_command == (VFunction *) info_quit)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
done = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Found in signals.c */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
extern void initialize_info_signal_handler (void );
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Initialize the first info session by starting the terminal, window,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
initialize_info_session (NODE *node, int clear_screen)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *term_name = getenv ("TERM");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
terminal_initialize_terminal (term_name);
|
|
|
|
|
|
|
|
|
|
if (terminal_is_dumb_p)
|
|
|
|
|
{
|
|
|
|
|
if (!term_name)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
term_name = "dumb";
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_term_too_dumb, term_name, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
xexit (1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (clear_screen)
|
|
|
|
|
{
|
|
|
|
|
terminal_prep_terminal ();
|
|
|
|
|
terminal_clear_screen ();
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
initialize_info_keymaps ();
|
|
|
|
|
window_initialize_windows (screenwidth, screenheight);
|
|
|
|
|
initialize_info_signal_handler ();
|
|
|
|
|
display_initialize_display (screenwidth, screenheight);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_set_node_of_window (0, active_window, node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Tell the window system how to notify us when a window needs to be
|
|
|
|
|
asynchronously deleted (e.g., user resizes window very small). */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_deletion_notifier = (VFunction *) forget_window_and_nodes;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* If input has not been redirected yet, make it come from unbuffered
|
|
|
|
|
standard input. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!info_input_stream)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2000-01-17 10:45:43 +00:00
|
|
|
|
setbuf (stdin, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_input_stream = stdin;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
info_windows_initialized_p = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tell Info that input is coming from the file FILENAME. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_set_input_from_file (char *filename)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
FILE *stream;
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* Input may include binary characters. */
|
|
|
|
|
stream = fopen (filename, FOPEN_RBIN);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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 *
|
2005-05-23 10:50:32 +00:00
|
|
|
|
get_info_window_of_window (WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
set_remembered_pagetop_and_point (WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
remember_window_and_node (WINDOW *window, NODE *node)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
/* See if we already have this window in our list. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
INFO_WINDOW *info_win = get_info_window_of_window (window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_windows_slots, 10, INFO_WINDOW *);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this node, the current pagetop, and the current point are the
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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++;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_win->nodes[info_win->nodes_index] = NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
consistency_check_info_windows (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < info_windows_index; i++)
|
|
|
|
|
{
|
|
|
|
|
WINDOW *win;
|
|
|
|
|
|
|
|
|
|
for (win = windows; win; win = win->next)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (win == info_windows[i]->window)
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!win)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
abort ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* DEBUG_FORGET_WINDOW_AND_NODES */
|
|
|
|
|
|
|
|
|
|
/* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
forget_window_and_nodes (WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
info_windows[i] = info_windows[i + 1];
|
|
|
|
|
i++;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
info_windows_index--;
|
|
|
|
|
info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
|
|
|
|
|
|
|
|
|
|
if (info_win->nodes)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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
|
2000-01-17 10:45:43 +00:00
|
|
|
|
the footnotes for this window. If REMEMBER is nonzero, first call
|
|
|
|
|
set_remembered_pagetop_and_point. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_set_node_of_window (int remember, WINDOW *window, NODE *node)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (remember)
|
|
|
|
|
set_remembered_pagetop_and_point (window);
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Info Movement Commands */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
|
|
|
|
|
to do so. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
set_window_pagetop (WINDOW *window, int desired_top)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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) ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(((window->height - amount) * 10) < window->height))
|
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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) ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(((window->height - amount) * 10) < window->height))
|
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_show_point (WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
move_to_new_line (int old, int new, WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (old == -1)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_find_point, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int goal;
|
|
|
|
|
|
|
|
|
|
if (new >= window->line_count || new < 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move WINDOW's point down to the next line if possible. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
int old_line, new_line;
|
|
|
|
|
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_prev_line (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
old_line = window_line_of_point (window);
|
|
|
|
|
new_line = old_line + count;
|
|
|
|
|
move_to_new_line (old_line, new_line, window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move WINDOW's point up to the previous line if possible. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
int old_line, new_line;
|
|
|
|
|
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_next_line (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
old_line = window_line_of_point (window);
|
|
|
|
|
new_line = old_line - count;
|
|
|
|
|
move_to_new_line (old_line, new_line, window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move WINDOW's point to the end of the true line. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int point;
|
|
|
|
|
register char *buffer;
|
|
|
|
|
|
|
|
|
|
buffer = window->node->contents;
|
|
|
|
|
point = window->point;
|
|
|
|
|
|
|
|
|
|
for (; (point) && (buffer[point - 1] != '\n'); point--);
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* If at a line start already, do nothing. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (point != window->point)
|
|
|
|
|
{
|
|
|
|
|
window->point = point;
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move point forward in the node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_backward_char (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
window->point += count;
|
|
|
|
|
|
|
|
|
|
if (window->point >= window->node->nodelen)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window->point = window->node->nodelen - 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move point backward in the node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_forward_char (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
window->point -= count;
|
|
|
|
|
|
|
|
|
|
if (window->point < 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window->point = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
|
|
|
|
|
|
|
|
|
|
/* Move forward a word in this node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If we are not in a word, move forward until we are in one.
|
1999-01-14 19:56:32 +00:00
|
|
|
|
Then, move forward until we hit a non-alphabetic character. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
c = buffer[point];
|
|
|
|
|
|
|
|
|
|
if (!alphabetic (c))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
while (++point < end)
|
|
|
|
|
{
|
|
|
|
|
c = buffer[point];
|
|
|
|
|
if (alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (point >= end) return;
|
|
|
|
|
|
|
|
|
|
while (++point < end)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
c = buffer[point];
|
|
|
|
|
if (!alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
--count;
|
|
|
|
|
}
|
|
|
|
|
window->point = point;
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Like info_forward_word (), except that we look at the
|
1999-01-14 19:56:32 +00:00
|
|
|
|
characters just before point. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
c = buffer[point - 1];
|
|
|
|
|
|
|
|
|
|
if (!alphabetic (c))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
while (--point)
|
|
|
|
|
{
|
|
|
|
|
c = buffer[point - 1];
|
|
|
|
|
if (alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
while (point)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
c = buffer[point - 1];
|
|
|
|
|
if (!alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
--point;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
--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:
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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)))
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Move to 1st menu item, Next, Up/Next, or error in this window. */
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
forward_move_node_structure (WINDOW *window, int behaviour)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
switch (behaviour)
|
|
|
|
|
{
|
|
|
|
|
case IS_PageOnly:
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_at_node_bottom, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_NextOnly:
|
|
|
|
|
info_next_label_of_node (window->node);
|
|
|
|
|
if (!info_parsed_nodename && !info_parsed_filename)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_pointer, (char *) _("Next"), NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area ((char *) _("Following Next node..."),
|
|
|
|
|
NULL, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Next", window);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_Continuous:
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* 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);
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area ((char *) _("Selecting first menu item..."),
|
|
|
|
|
NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_menu_digit (window, 1, '1');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Okay, this node does not contain a menu. If it contains a
|
|
|
|
|
"Next:" pointer, use that. */
|
|
|
|
|
info_next_label_of_node (window->node);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (INFO_LABEL_FOUND ())
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area ((char *) _("Selecting Next node..."),
|
|
|
|
|
NULL, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Next", window);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (INFO_LABEL_FOUND ())
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Up", window);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (info_error_was_printed)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
up_counter++;
|
|
|
|
|
|
|
|
|
|
info_next_label_of_node (window->node);
|
|
|
|
|
|
|
|
|
|
/* If no "Next" pointer, keep backing up. */
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (!INFO_LABEL_FOUND ())
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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. */
|
|
|
|
|
window_message_in_echo_area
|
2005-05-23 10:50:32 +00:00
|
|
|
|
((char *) _("Moving Up %d time(s), then Next."),
|
|
|
|
|
(void *) (long) up_counter, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Next", window);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
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;
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("No more nodes within this document."),
|
|
|
|
|
NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
backward_move_node_structure (WINDOW *window, int behaviour)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
switch (behaviour)
|
|
|
|
|
{
|
|
|
|
|
case IS_PageOnly:
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_at_node_top, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_NextOnly:
|
|
|
|
|
info_prev_label_of_node (window->node);
|
|
|
|
|
if (!info_parsed_nodename && !info_parsed_filename)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("No `Prev' for this node."), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area ((char *) _("Moving Prev in this window."),
|
|
|
|
|
NULL, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Prev", window);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_Continuous:
|
|
|
|
|
info_prev_label_of_node (window->node);
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (!info_parsed_nodename && (!info_parsed_filename
|
|
|
|
|
|| is_dir_name (info_parsed_filename)))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
info_up_label_of_node (window->node);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (!info_parsed_nodename && (!info_parsed_filename
|
|
|
|
|
|| is_dir_name (info_parsed_filename)))
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *)
|
|
|
|
|
_("No `Prev' or `Up' for this node within this document."),
|
|
|
|
|
NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area ((char *) _("Moving Up in this window."),
|
|
|
|
|
NULL, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Up", window);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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. */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area ((char *) _("Moving Prev in this window."),
|
|
|
|
|
NULL, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Prev", window);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
if (!inhibit_menu_traversing)
|
|
|
|
|
{
|
|
|
|
|
while (!info_error_was_printed &&
|
|
|
|
|
(menu = info_menu_of_node (window->node)))
|
|
|
|
|
{
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
window_message_in_echo_area
|
2005-05-23 10:50:32 +00:00
|
|
|
|
((char *) _("Moving to `Prev's last menu item."), NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_menu_digit (window, 1, '0');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move continuously forward through the node structure of this info file. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_global_next_node,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Move forwards or down through node structure"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_global_prev_node (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (count && !info_error_was_printed)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
forward_move_node_structure (window, IS_Continuous);
|
|
|
|
|
count--;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move continuously backward through the node structure of this info file. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_global_prev_node,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Move backwards or up through node structure"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_global_next_node (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (count && !info_error_was_printed)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
backward_move_node_structure (window, IS_Continuous);
|
|
|
|
|
count--;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
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);
|
2002-03-25 13:10:59 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
_scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
2002-03-25 13:10:59 +00:00
|
|
|
|
_scroll_backward (window, -count, key, behaviour);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int desired_top;
|
|
|
|
|
|
|
|
|
|
/* Without an explicit numeric argument, scroll the bottom two
|
1999-01-14 19:56:32 +00:00
|
|
|
|
lines to the top of this window, Or, if at bottom of window,
|
2002-03-25 13:10:59 +00:00
|
|
|
|
and the chosen behaviour is to scroll through nodes get the
|
|
|
|
|
"Next" node for this window. */
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (default_window_size > 0)
|
|
|
|
|
desired_top = window->pagetop + default_window_size;
|
|
|
|
|
else if (!info_explicit_arg && count == 1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
desired_top = window->pagetop + (window->height - 2);
|
|
|
|
|
|
|
|
|
|
/* If there are no more lines to scroll here, error, or get
|
2002-03-25 13:10:59 +00:00
|
|
|
|
another node, depending on BEHAVIOUR. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (desired_top > window->line_count)
|
|
|
|
|
{
|
|
|
|
|
forward_move_node_structure (window, behaviour);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_top = window->pagetop + count;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (desired_top >= window->line_count)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_top = window->line_count - 2;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (window->pagetop > desired_top)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
set_window_pagetop (window, desired_top);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
_scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
2002-03-25 13:10:59 +00:00
|
|
|
|
_scroll_forward (window, -count, key, behaviour);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int desired_top;
|
|
|
|
|
|
|
|
|
|
/* Without an explicit numeric argument, scroll the top two lines
|
2002-03-25 13:10:59 +00:00
|
|
|
|
to the bottom of this window, or, depending on the selected
|
|
|
|
|
behaviour, move to the previous, or Up'th node. */
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (default_window_size > 0)
|
|
|
|
|
desired_top = window->pagetop - default_window_size;
|
|
|
|
|
else if (!info_explicit_arg && count == 1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
desired_top = window->pagetop - (window->height - 2);
|
|
|
|
|
|
|
|
|
|
if ((desired_top < 0) && (window->pagetop == 0))
|
|
|
|
|
{
|
|
|
|
|
backward_move_node_structure (window, behaviour);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_top = window->pagetop - count;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (desired_top < 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_top = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
set_window_pagetop (window, desired_top);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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;
|
2002-03-25 13:10:59 +00:00
|
|
|
|
_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);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Move to the beginning of the node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
window->pagetop = window->point = 0;
|
|
|
|
|
window->flags |= W_UpdateWindow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move to the end of the node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
window->point = window->node->nodelen - 1;
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
2002-03-25 13:10:59 +00:00
|
|
|
|
info_scroll_half_screen_up (window, -count, key);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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)
|
2002-03-25 13:10:59 +00:00
|
|
|
|
info_scroll_half_screen_down (window, -count, key);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Commands for Manipulating Windows */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Make the next window in the chain be the active window. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
{
|
|
|
|
|
info_prev_window (window, -count, key);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If no other window, error now. */
|
|
|
|
|
if (!windows->next && !echo_area_is_active)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_one_window, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (count--)
|
|
|
|
|
{
|
|
|
|
|
if (window->next)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window = window->next;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (window == the_echo_area || !echo_area_is_active)
|
|
|
|
|
window = windows;
|
|
|
|
|
else
|
|
|
|
|
window = the_echo_area;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (active_window != window)
|
|
|
|
|
{
|
|
|
|
|
if (auto_footnotes_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_get_or_remove_footnotes (window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
window->flags |= W_UpdateWindow;
|
|
|
|
|
active_window = window;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the previous window in the chain be the active window. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
{
|
|
|
|
|
info_next_window (window, -count, key);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only one window? */
|
|
|
|
|
|
|
|
|
|
if (!windows->next && !echo_area_is_active)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_one_window, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (count--)
|
|
|
|
|
{
|
|
|
|
|
/* If we are in the echo area, or if the echo area isn't active and we
|
1999-01-14 19:56:32 +00:00
|
|
|
|
are in the first window, find the last window in the chain. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (window == the_echo_area ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(window == windows && !echo_area_is_active))
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
register WINDOW *win, *last = NULL;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (win = windows; win; win = win->next)
|
|
|
|
|
last = win;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window = last;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (window == windows)
|
|
|
|
|
window = the_echo_area;
|
|
|
|
|
else
|
|
|
|
|
window = window->prev;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (active_window != window)
|
|
|
|
|
{
|
|
|
|
|
if (auto_footnotes_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_get_or_remove_footnotes (window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_win_too_small, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#if defined (SPLIT_BEFORE_ACTIVE)
|
|
|
|
|
/* Try to scroll the old window into its new postion. */
|
|
|
|
|
if (pagetop == window->pagetop)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int start, end, amount;
|
|
|
|
|
|
|
|
|
|
start = split->first_row;
|
|
|
|
|
end = start + window->height;
|
|
|
|
|
amount = split->height + 1;
|
|
|
|
|
display_scroll_display (start, end, amount);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#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
|
1999-01-14 19:56:32 +00:00
|
|
|
|
something else in it. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (internal_info_node_p (split->node))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
split->pagetop = window->pagetop;
|
|
|
|
|
|
|
|
|
|
if (auto_tiling_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window_tile_windows (DONT_TILE_INTERNALS);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window_adjust_pagetop (split);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (!windows->next)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_kill_last, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else if (window->flags & W_WindowIsPerm)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info_delete_window_internal (window);
|
|
|
|
|
|
|
|
|
|
if (auto_footnotes_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_get_or_remove_footnotes (active_window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (auto_tiling_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window_tile_windows (DONT_TILE_INTERNALS);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do the physical deletion of WINDOW, and forget this window and
|
|
|
|
|
associated nodes. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_delete_window_internal (WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
|
|
|
|
|
{
|
|
|
|
|
/* We not only delete the window from the display, we forget it from
|
1999-01-14 19:56:32 +00:00
|
|
|
|
our list of remembered windows. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
forget_window_and_nodes (window);
|
|
|
|
|
window_delete_window (window);
|
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
echo_area_inform_of_deleted_window (window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Just keep WINDOW, deleting all others. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int num_deleted; /* The number of windows we deleted. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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
|
1999-01-14 19:56:32 +00:00
|
|
|
|
are found, we are done. A window is eligible for deletion if
|
|
|
|
|
is it not permanent, and it is not WINDOW. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
for (win = windows; win; win = win->next)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (win != window && ((win->flags & W_WindowIsPerm) == 0))
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!win)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
WINDOW *other;
|
|
|
|
|
|
|
|
|
|
/* If only one window, give up. */
|
|
|
|
|
if (!windows->next)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_one_window, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
other = window->next;
|
|
|
|
|
|
|
|
|
|
if (!other)
|
|
|
|
|
other = window->prev;
|
|
|
|
|
|
|
|
|
|
info_scroll_forward (other, count, key);
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Change the size of WINDOW by AMOUNT. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Divide the available screen space among the visible windows"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
window_tile_windows (TILE_INTERNALS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Toggle the state of this window's wrapping of lines. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_toggle_wrap,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Toggle the state of line wrapping in the current window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
window_toggle_wrap (window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Info Node Commands */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
|
|
|
|
|
filename is not set. */
|
|
|
|
|
char *
|
2005-05-23 10:50:32 +00:00
|
|
|
|
node_printed_rep (NODE *node)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* 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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_select_reference (WINDOW *window, REFERENCE *entry)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
filename = xstrdup (filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (entry->nodename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nodename = xstrdup (entry->nodename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nodename = xstrdup ("Top");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
file_system_error = xstrdup (info_recent_file_error);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
|
{
|
|
|
|
|
if (file_system_error)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error (file_system_error, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_find_node, nodename, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybe_free (file_system_error);
|
|
|
|
|
maybe_free (filename);
|
|
|
|
|
maybe_free (nodename);
|
|
|
|
|
|
|
|
|
|
if (node)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_set_node_of_window (1, window, node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_parse_and_select (char *line, WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_handle_pointer (char *label, WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (info_parsed_filename || info_parsed_nodename)
|
|
|
|
|
{
|
|
|
|
|
char *filename, *nodename;
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
filename = nodename = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
if (info_parsed_filename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
filename = xstrdup (info_parsed_filename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (window->node->parent)
|
|
|
|
|
filename = xstrdup (window->node->parent);
|
|
|
|
|
else if (window->node->filename)
|
|
|
|
|
filename = xstrdup (window->node->filename);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (info_parsed_nodename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nodename = xstrdup (info_parsed_nodename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nodename = xstrdup ("Top");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
node = info_get_node (filename, nodename);
|
|
|
|
|
|
|
|
|
|
if (node)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_set_node_of_window (1, window, node);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error (info_recent_file_error, NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_file_node, filename, nodename);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (filename);
|
|
|
|
|
free (nodename);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_pointer, label, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the "Next:" node of the node currently being
|
|
|
|
|
displayed. */
|
2000-01-17 10:45:43 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_next_label_of_node (window->node);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Next", window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the "Prev:" node of the node currently being
|
|
|
|
|
displayed. */
|
2000-01-17 10:45:43 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_prev_label_of_node (window->node);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Prev", window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the "Up:" node of the node currently being
|
|
|
|
|
displayed. */
|
2000-01-17 10:45:43 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_up_label_of_node (window->node);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_handle_pointer ("Up", window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the last node of this info file. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
FILE_BUFFER *fb = file_buffer_of_window (window);
|
|
|
|
|
NODE *node = (NODE *)NULL;
|
|
|
|
|
|
|
|
|
|
if (fb && fb->tags)
|
|
|
|
|
{
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!node)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_set_node_of_window (1, window, node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the first node of this info file. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
FILE_BUFFER *fb = file_buffer_of_window (window);
|
|
|
|
|
NODE *node = (NODE *)NULL;
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* If no explicit argument, or argument of zero, default to the
|
|
|
|
|
first node. */
|
|
|
|
|
if (count == 0)
|
|
|
|
|
count = 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (fb && fb->tags)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!node)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_set_node_of_window (1, window, node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Select the last menu item in WINDOW->node. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_last_menu_item,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Select the last item in this node's menu"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_menu_digit (window, 1, '0');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i, item;
|
2005-05-23 10:50:32 +00:00
|
|
|
|
register REFERENCE **menu;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
menu = info_menu_of_node (window->node);
|
|
|
|
|
|
|
|
|
|
if (!menu)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_menu_node, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
for (i = 0; menu[i]; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (i == item - 1)
|
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (menu[i])
|
2005-05-23 10:50:32 +00:00
|
|
|
|
{
|
|
|
|
|
info_select_reference (window, menu[i]);
|
|
|
|
|
if (menu[i]->line_number > 0)
|
|
|
|
|
info_next_line (window, menu[i]->line_number - 1, key);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("There aren't %d items in this menu."),
|
|
|
|
|
(void *) (long) item, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-18 13:01:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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 **
|
2005-05-23 10:50:32 +00:00
|
|
|
|
nearest_xref (REFERENCE **xref_list, long int pos)
|
2003-06-18 13:01:38 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* 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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_menu_or_ref_item (WINDOW *window, int count,
|
|
|
|
|
unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
2003-06-18 13:01:38 +00:00
|
|
|
|
REFERENCE *entry;
|
|
|
|
|
REFERENCE *defentry = NULL;
|
|
|
|
|
REFERENCE **menu = (*builder) (window->node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!menu)
|
|
|
|
|
{
|
|
|
|
|
if (builder == info_menu_of_node)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_menu_node, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_xref_node, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Default the selected reference to the one which is on the line that
|
|
|
|
|
point is in. */
|
|
|
|
|
{
|
2003-06-18 13:01:38 +00:00
|
|
|
|
REFERENCE **refs = NULL;
|
|
|
|
|
int point_line = window_line_of_point (window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (point_line != -1)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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
|
|
|
|
|
{
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (HANDLE_MAN_PAGES)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (window->node->flags & N_IsManPage)
|
|
|
|
|
refs = manpage_xrefs_in_binding (window->node, &binding);
|
|
|
|
|
else
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* HANDLE_MAN_PAGES */
|
2003-06-18 13:01:38 +00:00
|
|
|
|
refs = nearest_xref (menu, window->point);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-18 13:01:38 +00:00
|
|
|
|
if (refs && refs[0])
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2003-06-18 13:01:38 +00:00
|
|
|
|
if (strcmp (refs[0]->label, "Menu") != 0
|
|
|
|
|
|| builder == info_xrefs_of_node)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int which = 0;
|
|
|
|
|
|
2003-06-18 13:01:38 +00:00
|
|
|
|
/* 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])
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int closest = -1;
|
|
|
|
|
|
|
|
|
|
for (; refs[which]; which++)
|
|
|
|
|
{
|
2003-06-18 13:01:38 +00:00
|
|
|
|
if (window->point >= refs[which]->start
|
|
|
|
|
&& window->point <= refs[which]->end)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
closest = which;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (window->point < refs[which]->start)
|
2003-06-18 13:01:38 +00:00
|
|
|
|
break;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (which > 0)
|
|
|
|
|
{
|
|
|
|
|
if (closest == -1)
|
|
|
|
|
which--;
|
|
|
|
|
else
|
|
|
|
|
which = closest;
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
|
|
|
|
|
defentry->label = xstrdup (refs[which]->label);
|
|
|
|
|
defentry->filename = refs[which]->filename;
|
|
|
|
|
defentry->nodename = refs[which]->nodename;
|
2005-05-23 10:50:32 +00:00
|
|
|
|
defentry->line_number = refs[which]->line_number;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
if (defentry->filename)
|
|
|
|
|
defentry->filename = xstrdup (defentry->filename);
|
|
|
|
|
if (defentry->nodename)
|
|
|
|
|
defentry->nodename = xstrdup (defentry->nodename);
|
|
|
|
|
}
|
|
|
|
|
info_free_references (refs);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (defentry)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
{
|
|
|
|
|
prompt = xmalloc (strlen (defentry->label)
|
|
|
|
|
+ strlen (_("Menu item (%s): ")));
|
|
|
|
|
sprintf (prompt, _("Menu item (%s): "), defentry->label);
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
prompt = xstrdup (_("Menu item: "));
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (defentry)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
{
|
|
|
|
|
prompt = xmalloc (strlen (defentry->label)
|
|
|
|
|
+ strlen (_("Follow xref (%s): ")));
|
|
|
|
|
sprintf (prompt, _("Follow xref (%s): "), defentry->label);
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
prompt = xstrdup (_("Follow xref: "));
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
line = info_read_completing_in_echo_area (window, prompt, menu);
|
|
|
|
|
free (prompt);
|
|
|
|
|
|
|
|
|
|
window = active_window;
|
|
|
|
|
|
|
|
|
|
/* User aborts, just quit. */
|
|
|
|
|
if (!line)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
maybe_free (defentry);
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
info_abort_key (window, 0, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If we had a default and the user accepted it, use that. */
|
|
|
|
|
if (!*line)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
free (line);
|
|
|
|
|
if (defentry)
|
|
|
|
|
line = xstrdup (defentry->label);
|
|
|
|
|
else
|
|
|
|
|
line = (char *)NULL;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Not going to ask any questions. If we have a default entry, use
|
1999-01-14 19:56:32 +00:00
|
|
|
|
that, otherwise return. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!defentry)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line = xstrdup (defentry->label);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line)
|
|
|
|
|
{
|
2002-03-25 13:10:59 +00:00
|
|
|
|
/* 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 strcasecmp because LINE is downcased
|
|
|
|
|
inside info_read_completing_in_echo_area. */
|
|
|
|
|
if (strcasecmp (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;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!entry && defentry)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("The reference disappeared! (%s)."), line, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2000-01-17 10:45:43 +00:00
|
|
|
|
NODE *orig = window->node;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_select_reference (window, entry);
|
2005-05-23 10:50:32 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (builder == info_xrefs_of_node && window->node != orig
|
|
|
|
|
&& !(window->node->flags & N_FromAnchor))
|
|
|
|
|
{ /* Search for this reference in the node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-05-23 10:50:32 +00:00
|
|
|
|
|
|
|
|
|
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);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
free (line);
|
|
|
|
|
if (defentry)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
free (defentry->label);
|
|
|
|
|
maybe_free (defentry->filename);
|
|
|
|
|
maybe_free (defentry->nodename);
|
|
|
|
|
free (defentry);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(info_xref_item, _("Read a footnote or cross reference and select its node"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Position the cursor at the start of this node's menu. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_menu_node, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Visit as many menu items at once as possible"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
REFERENCE *entry, **menu;
|
|
|
|
|
|
|
|
|
|
menu = info_menu_of_node (window->node);
|
|
|
|
|
|
|
|
|
|
if (!menu)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_menu_node, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_win_too_small, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
active_window = new;
|
|
|
|
|
info_select_reference (new, entry);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read a line of input which is a node name, and go to that node. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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++)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
add_pointer_to_array
|
|
|
|
|
(entry, items_index, items, items_slots, 100, REFERENCE *);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
2005-05-23 10:50:32 +00:00
|
|
|
|
line = info_read_maybe_completing (window, (char *) _("Goto node: "),
|
|
|
|
|
items);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
info_free_references (items);
|
|
|
|
|
}
|
|
|
|
|
#else /* !GOTO_COMPLETES */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
line = info_read_in_echo_area (window, (char *) _("Goto node: "));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#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 ();
|
|
|
|
|
}
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
|
|
|
|
/* 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 *
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_follow_menus (NODE *initial_node, char **menus,
|
|
|
|
|
const char **errstr, char **errarg1, char **errarg2)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
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 (strcasecmp (entry->label, arg) == 0)
|
|
|
|
|
break;
|
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if ((best_guess == -1)
|
|
|
|
|
&& (strncasecmp (entry->label, arg, strlen (arg)) == 0))
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2002-03-25 13:10:59 +00:00
|
|
|
|
/* 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);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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 **
|
2005-05-23 10:50:32 +00:00
|
|
|
|
split_list_of_nodenames (char *str)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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"))
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
char *line = info_read_in_echo_area (window, (char *) _("Follow menus: "));
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
|
|
|
|
/* If the user aborted, quit now. */
|
|
|
|
|
if (!line)
|
|
|
|
|
{
|
|
|
|
|
info_abort_key (window, 0, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canonicalize_whitespace (line);
|
|
|
|
|
|
|
|
|
|
if (*line)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
const char *errstr;
|
|
|
|
|
char *errarg1, *errarg2;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
NODE *dir_node = info_get_node (NULL, NULL);
|
|
|
|
|
char **nodes = split_list_of_nodenames (line);
|
2005-05-23 10:50:32 +00:00
|
|
|
|
NODE *node = NULL;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_find_node, "Top", NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
|
|
|
|
free (nodes);
|
|
|
|
|
if (!errstr)
|
|
|
|
|
info_set_node_of_window (1, window, node);
|
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) errstr, errarg1, errarg2);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 *
|
2005-05-23 10:50:32 +00:00
|
|
|
|
entry_in_menu (char *arg, REFERENCE **menu, int exact)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
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 (strcasecmp (entry->label, arg) == 0)
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
if (strncasecmp (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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
REFERENCE *entry = NULL;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
|
|
|
|
/* 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++)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
char *nodename;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
nodename = xmalloc (strlen (program) + strlen (*try_node));
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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);
|
2005-05-23 10:50:32 +00:00
|
|
|
|
free (nodename);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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 *
|
2005-05-23 10:50:32 +00:00
|
|
|
|
program_name_from_file_name (char *file_name)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
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"))
|
|
|
|
|
{
|
2003-06-18 13:01:38 +00:00
|
|
|
|
const char *invocation_prompt = _("Find Invocation node of [%s]: ");
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_abort_key (window, 0, 0);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_find_node, "Top", NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
|
|
|
|
info_intuit_options_node (window, top_node, program_name);
|
|
|
|
|
free (line);
|
|
|
|
|
free (default_program_name);
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (HANDLE_MAN_PAGES)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
line = info_read_in_echo_area (window, (char *) _("Get Manpage: "));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!line)
|
|
|
|
|
{
|
|
|
|
|
info_abort_key (window, 0, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canonicalize_whitespace (line);
|
|
|
|
|
|
|
|
|
|
if (*line)
|
|
|
|
|
{
|
|
|
|
|
char *goto_command;
|
|
|
|
|
|
|
|
|
|
goto_command = (char *)xmalloc
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_parse_and_select ("Top", window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move to the node "(dir)Top". */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_parse_and_select ("(dir)Top", window);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
/* 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 *
|
2005-05-23 10:50:32 +00:00
|
|
|
|
read_nodename_to_kill (WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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);
|
2005-05-23 10:50:32 +00:00
|
|
|
|
char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
sprintf (prompt, _("Kill node (%s): "), default_nodename);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
|
|
|
|
|
REFERENCE *);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return nodename;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* Delete NODENAME from this window, showing the most
|
|
|
|
|
recently selected node in this window. */
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
kill_node (WINDOW *window, char *nodename)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
int iw, i;
|
|
|
|
|
INFO_WINDOW *info_win;
|
|
|
|
|
NODE *temp;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* 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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (iw = 0; (info_win = info_windows[iw]); iw++)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
|
|
|
|
|
&& info_win->window == window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!info_win)
|
|
|
|
|
{
|
|
|
|
|
if (*nodename)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window_clear_echo_area ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Cannot kill the last node"), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* 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. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
for (i = info_win->current; i < info_win->nodes_index; i++)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_win->nodes[i] = info_win->nodes[i + 1];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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])
|
1999-01-14 19:56:32 +00:00
|
|
|
|
stealer = info_windows[iw + 1];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
stealer = info_windows[0];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If the node being displayed in the next window is not the most
|
1999-01-14 19:56:32 +00:00
|
|
|
|
recently loaded one, get the most recently loaded one. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if ((stealer->nodes_index - 1) != stealer->current)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
which = stealer->nodes_index - 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Else, if there is another node behind the stealers current node,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
use that one. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (stealer->current > 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
which = stealer->current - 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Else, just use the node appearing in STEALER's window. */
|
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
which = stealer->current;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Copy this node. */
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
NODE *copy = xmalloc (sizeof (NODE));
|
2000-01-17 10:45:43 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
copy->display_pos = temp->display_pos;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
temp = copy;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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];
|
2000-01-17 10:45:43 +00:00
|
|
|
|
temp->display_pos = info_win->points[info_win->current];
|
1997-01-11 02:12:38 +00:00
|
|
|
|
window_set_node_of_window (info_win->window, temp);
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!info_error_was_printed)
|
|
|
|
|
window_clear_echo_area ();
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
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);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Read the name of a file and select the entire file. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
line = info_read_in_echo_area (window, (char *) _("Find file: "));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!line)
|
|
|
|
|
{
|
|
|
|
|
info_abort_key (active_window, 1, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*line)
|
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
node = info_get_node (line, "*");
|
|
|
|
|
if (!node)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error (info_recent_file_error, NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Cannot find `%s'."), line, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_set_node_of_window (1, window, node);
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!info_error_was_printed)
|
|
|
|
|
window_clear_echo_area ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Dumping and Printing Nodes */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
#define VERBOSE_NODE_DUMPING
|
2005-05-23 10:50:32 +00:00
|
|
|
|
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);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
dump_nodes_to_file (char *filename, char **nodenames,
|
|
|
|
|
char *output_filename, int dump_subnodes)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Could not create output file `%s'."),
|
|
|
|
|
output_filename, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Done."), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
initialize_dumping (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
dump_node_to_stream (char *filename, char *nodename,
|
|
|
|
|
FILE *stream, int dump_subnodes)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
node = info_get_node (filename, nodename);
|
|
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error (info_recent_file_error, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (filename && *nodename != '(')
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_file_node,
|
|
|
|
|
filename_non_directory (filename),
|
|
|
|
|
nodename);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_find_node, nodename, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
free (node);
|
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
dumped_already_slots, 50, char *);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#if defined (VERBOSE_NODE_DUMPING)
|
|
|
|
|
/* Maybe we should print some information about the node being output. */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Writing node %s..."), node_printed_rep (node), NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
menu = info_menu_of_node (node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (menu)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
dump_node_to_file (NODE *node, char *filename, int dump_subnodes)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Could not create output file `%s'."), filename,
|
|
|
|
|
NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Done."), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Pipe the contents of this node through INFO_PRINT_COMMAND"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
print_node (window->node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
print_node (NODE *node)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
FILE *printer_pipe;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
char *print_command = getenv ("INFO_PRINT_COMMAND");
|
2000-01-17 10:45:43 +00:00
|
|
|
|
int piping = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!print_command || !*print_command)
|
|
|
|
|
print_command = DEFAULT_INFO_PRINT_COMMAND;
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (!printer_pipe)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined (VERBOSE_NODE_DUMPING)
|
|
|
|
|
/* Maybe we should print some information about the node being output. */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Printing node %s..."), node_printed_rep (node), NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* VERBOSE_NODE_DUMPING */
|
|
|
|
|
|
|
|
|
|
write_node_to_stream (node, printer_pipe);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (piping)
|
|
|
|
|
pclose (printer_pipe);
|
|
|
|
|
else
|
|
|
|
|
fclose (printer_pipe);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#if defined (VERBOSE_NODE_DUMPING)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Done."), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* VERBOSE_NODE_DUMPING */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
write_node_to_stream (NODE *node, FILE *stream)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
fwrite (node->contents, 1, node->nodelen, stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Info Searching Commands */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
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);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
static char *search_string = (char *)NULL;
|
|
|
|
|
static int search_string_size = 0;
|
|
|
|
|
static int isearch_is_active = 0;
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
static int last_search_direction = 0;
|
|
|
|
|
static int last_search_case_sensitive = 0;
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* Return the file buffer which belongs to WINDOW's node. */
|
|
|
|
|
FILE_BUFFER *
|
2005-05-23 10:50:32 +00:00
|
|
|
|
file_buffer_of_window (WINDOW *window)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_search_in_node (char *string, NODE *node, long int start,
|
|
|
|
|
WINDOW *window, int dir, int case_sensitive)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
SEARCH_BINDING binding;
|
|
|
|
|
long offset;
|
|
|
|
|
|
|
|
|
|
binding.buffer = node->contents;
|
|
|
|
|
binding.start = start;
|
|
|
|
|
binding.end = node->nodelen;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
binding.flags = 0;
|
|
|
|
|
if (!case_sensitive)
|
|
|
|
|
binding.flags |= S_FoldCase;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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 = search (string, &binding);
|
|
|
|
|
|
|
|
|
|
if (offset != -1 && window)
|
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (window);
|
|
|
|
|
if (window->node != node)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window_set_node_of_window (window, node);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_target_search_node (NODE *node, char *string, long int start)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
2005-05-23 10:50:32 +00:00
|
|
|
|
long offset = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
char *target;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
target = xstrdup (string);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
i = strlen (target);
|
|
|
|
|
|
|
|
|
|
/* Try repeatedly searching for this string while removing words from
|
|
|
|
|
the end of it. */
|
|
|
|
|
while (i)
|
|
|
|
|
{
|
|
|
|
|
target[i] = '\0';
|
2000-01-17 10:45:43 +00:00
|
|
|
|
offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (offset != -1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
break;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_search_internal (char *string, WINDOW *window,
|
|
|
|
|
int dir, int case_sensitive)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (ret != -1)
|
|
|
|
|
{
|
|
|
|
|
/* We won! */
|
|
|
|
|
if (!echo_area_is_active && !isearch_is_active)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
window_clear_echo_area ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
register int current_tag = 0, number_of_tags;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
char *last_subfile;
|
|
|
|
|
TAG *tag;
|
|
|
|
|
|
|
|
|
|
/* Find number of tags and current tag. */
|
|
|
|
|
last_subfile = (char *)NULL;
|
|
|
|
|
for (i = 0; file_buffer->tags[i]; i++)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
|
|
|
|
|
{
|
|
|
|
|
current_tag = i;
|
|
|
|
|
last_subfile = file_buffer->tags[i]->filename;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
number_of_tags = i;
|
|
|
|
|
|
|
|
|
|
/* If there is no last_subfile, our tag wasn't found. */
|
|
|
|
|
if (!last_subfile)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return (-1);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Search through subsequent nodes, wrapping around to the top
|
1999-01-14 19:56:32 +00:00
|
|
|
|
of the info file until we find the string or return to this
|
|
|
|
|
window's node and point. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
while (1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
/* Allow C-g to quit the search, failing it if pressed. */
|
|
|
|
|
return_if_control_g (-1);
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* Find the next tag that isn't an anchor. */
|
|
|
|
|
for (i = current_tag + dir; i != current_tag; i += dir)
|
|
|
|
|
{
|
|
|
|
|
if (i < 0)
|
|
|
|
|
i = number_of_tags - 1;
|
|
|
|
|
else if (i == number_of_tags)
|
|
|
|
|
i = 0;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
tag = file_buffer->tags[i];
|
|
|
|
|
if (tag->nodelen != 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* If we got past out starting point, bail out. */
|
|
|
|
|
if (i == current_tag)
|
|
|
|
|
return (-1);
|
|
|
|
|
current_tag = i;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
if (!echo_area_is_active && (last_subfile != tag->filename))
|
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area
|
2005-05-23 10:50:32 +00:00
|
|
|
|
((char *) _("Searching subfile %s ..."),
|
|
|
|
|
filename_non_directory (tag->filename), NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error (info_recent_file_error, NULL, NULL);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_cant_file_node,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
filename_non_directory (file_buffer->filename),
|
|
|
|
|
tag->nodename);
|
|
|
|
|
}
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dir < 0)
|
|
|
|
|
start = tag->nodelen;
|
|
|
|
|
|
|
|
|
|
ret =
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_search_in_node (string, node, start, window, dir,
|
|
|
|
|
case_sensitive);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
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);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_search_1 (WINDOW *window, int count, unsigned char key,
|
|
|
|
|
int case_sensitive, int ask_for_string)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *line, *prompt;
|
|
|
|
|
int result, old_pagetop;
|
|
|
|
|
int direction;
|
|
|
|
|
|
|
|
|
|
if (count < 0)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
direction = -1;
|
|
|
|
|
count = -count;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
2000-01-17 10:45:43 +00:00
|
|
|
|
{
|
|
|
|
|
direction = 1;
|
|
|
|
|
if (count == 0)
|
|
|
|
|
count = 1; /* for backward compatibility */
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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';
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (ask_for_string)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: "))
|
|
|
|
|
+ strlen (_("Search backward"))
|
|
|
|
|
+ strlen (_("Search"))
|
|
|
|
|
+ strlen (_(" case-sensitively "))
|
|
|
|
|
+ strlen (_(" "))
|
|
|
|
|
+ strlen (search_string));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
sprintf (prompt, _("%s%sfor string [%s]: "),
|
|
|
|
|
direction < 0 ? _("Search backward") : _("Search"),
|
|
|
|
|
case_sensitive ? _(" case-sensitively ") : _(" "),
|
|
|
|
|
search_string);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
line = info_read_in_echo_area (window, prompt);
|
|
|
|
|
free (prompt);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (!line)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_abort_key (window, 0, 0);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (*line)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (strlen (line) + 1 > (unsigned int) search_string_size)
|
2000-01-17 10:45:43 +00:00
|
|
|
|
search_string = (char *) xrealloc
|
|
|
|
|
(search_string, (search_string_size += 50 + strlen (line)));
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
strcpy (search_string, line);
|
|
|
|
|
free (line);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
old_pagetop = active_window->pagetop;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
for (result = 0; result == 0 && count--; )
|
|
|
|
|
result = info_search_internal (search_string,
|
|
|
|
|
active_window, direction, case_sensitive);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (result != 0 && !info_error_was_printed)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Search failed."), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_get_or_remove_footnotes (active_window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perhaps free the unreferenced file buffers that were searched, but
|
|
|
|
|
not retained. */
|
|
|
|
|
info_gc_file_buffers ();
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_search_next,
|
|
|
|
|
_("Repeat last search in the same direction"))
|
|
|
|
|
{
|
|
|
|
|
if (!last_search_direction)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("No previous search string"), NULL, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
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)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("No previous search string"), NULL, NULL);
|
2000-01-17 10:45:43 +00:00
|
|
|
|
else
|
|
|
|
|
info_search_1 (window, -last_search_direction * count,
|
|
|
|
|
key, last_search_case_sensitive, 0);
|
|
|
|
|
}
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Incremental Searching */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
static void incremental_search (WINDOW *window, int count,
|
|
|
|
|
unsigned char ignore);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (isearch_forward,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Search interactively for a string as you type it"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
incremental_search (window, count, key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (isearch_backward,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Search interactively for a string as you type it"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
push_isearch (WINDOW *window, int search_index, int direction, int failing)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
isearch_states_slots, 20, SEARCH_STATE *);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
free_isearch_states (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
show_isearch_prompt (int dir, unsigned char *string, int failing_p)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
2003-06-18 13:01:38 +00:00
|
|
|
|
const char *prefix;
|
|
|
|
|
char *prompt, *p_rep;
|
2005-05-23 10:50:32 +00:00
|
|
|
|
unsigned int prompt_len, p_rep_index, p_rep_size;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (dir < 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
prefix = _("I-search backward: ");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
prefix = _("I-search: ");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
p_rep_index = p_rep_size = 0;
|
|
|
|
|
p_rep = (char *)NULL;
|
|
|
|
|
for (i = 0; string[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
switch (string[i])
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
case ' ': rep = " "; break;
|
|
|
|
|
case LFD: rep = "\\n"; break;
|
|
|
|
|
case TAB: rep = "\\t"; break;
|
|
|
|
|
default:
|
|
|
|
|
rep = pretty_keyname (string[i]);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
strcpy (p_rep + p_rep_index, rep);
|
|
|
|
|
p_rep_index += strlen (rep);
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-18 13:01:38 +00:00
|
|
|
|
prompt_len = strlen (prefix) + p_rep_index + 1;
|
|
|
|
|
if (failing_p)
|
|
|
|
|
prompt_len += strlen (_("Failing "));
|
|
|
|
|
prompt = xmalloc (prompt_len);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
|
|
|
|
|
p_rep ? p_rep : "");
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area ("%s", prompt, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
maybe_free (p_rep);
|
|
|
|
|
free (prompt);
|
|
|
|
|
display_cursor_at_point (active_window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
incremental_search (WINDOW *window, int count, unsigned char ignore)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char key;
|
|
|
|
|
int last_search_result, search_result, dir;
|
|
|
|
|
SEARCH_STATE mystate, orig_state;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
char *p;
|
|
|
|
|
int case_sensitive = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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';
|
2005-05-23 10:50:32 +00:00
|
|
|
|
show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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
|
1999-01-14 19:56:32 +00:00
|
|
|
|
it is convenient. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (!info_any_buffered_input_p () && display_was_interrupted_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
display_update_one_window (window);
|
|
|
|
|
display_cursor_at_point (active_window);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Read a character and dispatch on it. */
|
|
|
|
|
key = info_get_input_char ();
|
|
|
|
|
window_get_state (window, &mystate);
|
|
|
|
|
|
2003-05-02 00:50:37 +00:00
|
|
|
|
if (key == DEL || key == Control ('h'))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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';
|
2005-05-23 10:50:32 +00:00
|
|
|
|
show_isearch_prompt (dir, (unsigned char *) isearch_string,
|
|
|
|
|
search_result);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
goto after_search;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (key == Control ('q'))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
key = info_get_input_char ();
|
|
|
|
|
quoted = 1;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* We are about to search again, or quit. Save the current search. */
|
|
|
|
|
push_isearch (window, isearch_string_index, dir, search_result);
|
|
|
|
|
|
|
|
|
|
if (quoted)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
goto insert_and_search;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if (!Meta_p (key) || key > 32)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2003-05-02 00:50:37 +00:00
|
|
|
|
/* 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))
|
2002-03-25 13:10:59 +00:00
|
|
|
|
{
|
|
|
|
|
insert_and_search:
|
|
|
|
|
|
|
|
|
|
if (isearch_string_index + 2 >= isearch_string_size)
|
|
|
|
|
isearch_string = (char *)xrealloc
|
|
|
|
|
(isearch_string, isearch_string_size += 100);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
isearch_string[isearch_string_index++] = key;
|
|
|
|
|
isearch_string[isearch_string_index] = '\0';
|
|
|
|
|
goto search_now;
|
|
|
|
|
}
|
2005-05-23 10:50:32 +00:00
|
|
|
|
else if (func == (VFunction *) isearch_forward
|
|
|
|
|
|| func == (VFunction *) isearch_backward)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2002-03-25 13:10:59 +00:00
|
|
|
|
/* 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. */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if ((func == (VFunction *) isearch_forward && dir > 0) ||
|
|
|
|
|
(func == (VFunction *) isearch_backward && dir < 0))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (strlen ((char *) last_isearch_accepted) + 1
|
|
|
|
|
>= (unsigned int) isearch_string_size)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-05-23 10:50:32 +00:00
|
|
|
|
else if (func == (VFunction *) info_abort_key)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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';
|
2005-05-23 10:50:32 +00:00
|
|
|
|
show_isearch_prompt (dir, (unsigned char *) isearch_string,
|
|
|
|
|
search_result);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto exit_search;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto exit_search;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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. */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (isearch_string_index && func != (VFunction *) info_abort_key)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
maybe_free (last_isearch_accepted);
|
|
|
|
|
last_isearch_accepted = xstrdup (isearch_string);
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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. */
|
2002-03-25 13:10:59 +00:00
|
|
|
|
if (key != RET /* Emacs addicts want RET to get lost */
|
|
|
|
|
&& (key != isearch_terminate_search_key
|
|
|
|
|
|| info_any_buffered_input_p ()))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_set_pending_input (key);
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (func == (VFunction *) info_abort_key)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Search for the contents of isearch_string. */
|
|
|
|
|
search_now:
|
2005-05-23 10:50:32 +00:00
|
|
|
|
show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2000-01-17 10:45:43 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (search_result == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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) &&
|
2000-01-17 10:45:43 +00:00
|
|
|
|
((case_sensitive ? strncmp : strncasecmp)
|
|
|
|
|
(window->node->contents + window->point,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
isearch_string, isearch_string_index) == 0)) ||
|
|
|
|
|
((dir > 0) &&
|
|
|
|
|
((window->point - isearch_string_index) >= 0) &&
|
2000-01-17 10:45:43 +00:00
|
|
|
|
((case_sensitive ? strncmp : strncasecmp)
|
|
|
|
|
(window->node->contents +
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(window->point - (isearch_string_index - 1)),
|
|
|
|
|
isearch_string, isearch_string_index) == 0)))
|
|
|
|
|
{
|
|
|
|
|
if (dir > 0)
|
|
|
|
|
window->point++;
|
|
|
|
|
}
|
|
|
|
|
else
|
2000-01-17 10:45:43 +00:00
|
|
|
|
search_result = info_search_internal (isearch_string,
|
|
|
|
|
window, dir, case_sensitive);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this search failed, and we didn't already have a failed search,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
then ring the terminal bell. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (search_result != 0 && last_search_result == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
terminal_ring_bell ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
after_search:
|
2005-05-23 10:50:32 +00:00
|
|
|
|
show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (search_result == 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_gc_file_buffers (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
register int fb_index, iw_index, i;
|
|
|
|
|
register FILE_BUFFER *fb;
|
|
|
|
|
register INFO_WINDOW *iw;
|
|
|
|
|
|
|
|
|
|
if (!info_loaded_files)
|
|
|
|
|
return;
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
int fb_referenced_p = 0;
|
|
|
|
|
|
|
|
|
|
/* If already gc-ed, do nothing. */
|
|
|
|
|
if (!fb->contents)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
continue;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this file had to be uncompressed, check to see if we should
|
1999-01-14 19:56:32 +00:00
|
|
|
|
gc it. This means that the user-variable "gc-compressed-files"
|
|
|
|
|
is non-zero. */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
continue;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this file's contents are not gc-able, move on. */
|
|
|
|
|
if (fb->flags & N_CannotGC)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
continue;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* Check each INFO_WINDOW to see if it has any nodes which reference
|
1999-01-14 19:56:32 +00:00
|
|
|
|
this file. */
|
|
|
|
|
for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; iw->nodes && iw->nodes[i]; i++)
|
|
|
|
|
{
|
2000-01-17 10:45:43 +00:00
|
|
|
|
if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
|
|
|
|
|
(FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
fb_referenced_p = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* If this file buffer wasn't referenced, free its contents. */
|
|
|
|
|
if (!fb_referenced_p)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
free (fb->contents);
|
|
|
|
|
fb->contents = (char *)NULL;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Traversing and Selecting References */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Move to the next or previous cross reference in this node. */
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
long firstmenu, firstxref;
|
|
|
|
|
long nextmenu, nextxref;
|
|
|
|
|
long placement = -1;
|
|
|
|
|
long start = 0;
|
|
|
|
|
NODE *node = window->node;
|
|
|
|
|
|
|
|
|
|
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
|
2000-01-17 10:45:43 +00:00
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
firstmenu = info_search_in_node
|
2000-01-17 10:45:43 +00:00
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
firstxref =
|
2000-01-17 10:45:43 +00:00
|
|
|
|
info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#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)
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) msg_no_xref_node, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2000-01-17 10:45:43 +00:00
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
nextxref = info_search_in_node
|
2000-01-17 10:45:43 +00:00
|
|
|
|
(INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
#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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
nextmenu = info_search_in_node
|
2000-01-17 10:45:43 +00:00
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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)) ||
|
1999-01-14 19:56:32 +00:00
|
|
|
|
((dir == -1) && (nextmenu > nextxref)))
|
|
|
|
|
placement = nextmenu + 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
placement = nextxref;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
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 (firstmenu != -1 && firstxref != -1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (((dir == 1) && (firstmenu < firstxref)) ||
|
|
|
|
|
((dir == -1) && (firstmenu > firstxref)))
|
|
|
|
|
placement = firstmenu + 1;
|
|
|
|
|
else
|
|
|
|
|
placement = firstxref;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else if (firstmenu != -1)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
placement = firstmenu + 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
placement = firstxref;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
window->point = placement;
|
|
|
|
|
window_adjust_pagetop (window);
|
|
|
|
|
window->flags |= W_UpdateWindow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (info_move_to_prev_xref,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Move to the previous cross reference"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_move_to_prev_xref (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
info_move_to_xref (window, count, key, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (info_move_to_next_xref,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Move to the next cross reference"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_move_to_next_xref (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
info_move_to_xref (window, count, key, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Select the menu item or reference that appears on this line. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_select_reference_this_line,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Select reference or menu item appearing on this line"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (window->line_starts)
|
|
|
|
|
line = window->line_starts[window_line_of_point (window)];
|
|
|
|
|
else
|
|
|
|
|
line = "";
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Miscellaneous Info Commands */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* What to do when C-g is pressed in a window. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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 ();
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Quit"), NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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,
|
2000-01-17 10:45:43 +00:00
|
|
|
|
_("Move the cursor to a specific line of the window"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line = (window->height + count) + window->pagetop;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
line = window->pagetop + count;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_line = window->height + count;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_line = count;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (desired_line < 0)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_line = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (desired_line >= window->height)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
desired_line = window->height - 1;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
if (desired_line == point_line)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
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 (). */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Reading Keys and Dispatching on Them */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
/* 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"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
dispatch_error (char *keyseq)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
rep = pretty_keyseq (keyseq);
|
|
|
|
|
|
|
|
|
|
if (!echo_area_is_active)
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_error ((char *) _("Unknown command (%s)."), rep, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid")));
|
|
|
|
|
sprintf (temp, _("`%s' is invalid"), rep);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
initialize_keyseq (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_keyseq_index = 0;
|
|
|
|
|
info_keyseq_displayed_p = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add CHARACTER to the current key sequence. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
add_char_to_keyseq (char character)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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. */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
static void
|
|
|
|
|
display_info_keyseq (int expecting_future_input)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
rep = pretty_keyseq (info_keyseq);
|
|
|
|
|
if (expecting_future_input)
|
|
|
|
|
strcat (rep, "-");
|
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
|
|
|
|
inform_in_echo_area (rep);
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-05-23 10:50:32 +00:00
|
|
|
|
window_message_in_echo_area (rep, NULL, NULL);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
display_cursor_at_point (active_window);
|
|
|
|
|
}
|
|
|
|
|
info_keyseq_displayed_p = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called by interactive commands to read a keystroke. */
|
|
|
|
|
unsigned char
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_get_another_input_char (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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. */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (!info_keyseq_displayed_p)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
ready = 1;
|
|
|
|
|
if (!info_any_buffered_input_p () &&
|
|
|
|
|
!info_input_pending_p ())
|
|
|
|
|
{
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#if defined (FD_SET)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
#endif /* FD_SET */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_dispatch_on_key (unsigned char key, Keymap map)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#if !defined(INFOKEY)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
|
|
|
|
|
{
|
|
|
|
|
if (map[ESC].type == ISKMAP)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
map = (Keymap)map[ESC].function;
|
|
|
|
|
add_char_to_keyseq (ESC);
|
|
|
|
|
key = UnMeta (key);
|
|
|
|
|
info_dispatch_on_key (key, map);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
dispatch_error (info_keyseq);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#endif /* INFOKEY */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
switch (map[key].type)
|
|
|
|
|
{
|
|
|
|
|
case ISFUNC:
|
|
|
|
|
{
|
1999-01-14 19:56:32 +00:00
|
|
|
|
VFunction *func;
|
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
func = InfoFunction(map[key].function);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (func != (VFunction *)NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Special case info_do_lowercase_version (). */
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (func == (VFunction *) info_do_lowercase_version)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#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 */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
info_dispatch_on_key (tolower (key), map);
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#endif /* INFOKEY */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
add_char_to_keyseq (key);
|
|
|
|
|
|
|
|
|
|
if (info_keyseq_displayed_p)
|
|
|
|
|
display_info_keyseq (0);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
WINDOW *where;
|
|
|
|
|
|
|
|
|
|
where = active_window;
|
2002-03-25 13:10:59 +00:00
|
|
|
|
(*InfoFunction(map[key].function))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
(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)
|
2002-03-25 13:10:59 +00:00
|
|
|
|
ea_last_executed_command = InfoFunction(map[key].function);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
else
|
2002-03-25 13:10:59 +00:00
|
|
|
|
info_last_executed_command = InfoFunction(map[key].function);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
add_char_to_keyseq (key);
|
|
|
|
|
dispatch_error (info_keyseq);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ISKMAP:
|
|
|
|
|
add_char_to_keyseq (key);
|
2002-03-25 13:10:59 +00:00
|
|
|
|
if (map[key].function != (InfoCommand *)NULL)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char newkey;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
newkey = info_get_another_input_char ();
|
|
|
|
|
info_dispatch_on_key (newkey, (Keymap)map[key].function);
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
dispatch_error (info_keyseq);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Numeric Arguments */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* 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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Add this digit to the current numeric argument"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Start (or multiply by 4) the current numeric argument"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_numeric_arg *= 4;
|
|
|
|
|
info_numeric_arg_digit_loop (window, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a default argument. */
|
|
|
|
|
void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_initialize_numeric_arg (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
info_numeric_arg = info_numeric_arg_sign = 1;
|
|
|
|
|
info_explicit_arg = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
|
1999-01-14 19:56:32 +00:00
|
|
|
|
_("Internally used by \\[universal-argument]"))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char pure_key;
|
|
|
|
|
Keymap keymap = window->keymap;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
if (key)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
pure_key = key;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (display_was_interrupted_p && !info_any_buffered_input_p ())
|
|
|
|
|
display_update_display (windows);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (active_window != the_echo_area)
|
|
|
|
|
display_cursor_at_point (active_window);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
pure_key = key = info_get_another_input_char ();
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#if !defined(INFOKEY)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
if (Meta_p (key))
|
|
|
|
|
add_char_to_keyseq (ESC);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
add_char_to_keyseq (UnMeta (key));
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#else /* defined(INFOKEY) */
|
|
|
|
|
add_char_to_keyseq (key);
|
|
|
|
|
#endif /* defined(INFOKEY) */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#if !defined(INFOKEY)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (Meta_p (key))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
key = UnMeta (key);
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#endif /* !defined(INFOKEY) */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if (keymap[key].type == ISFUNC
|
|
|
|
|
&& InfoFunction(keymap[key].function)
|
|
|
|
|
== (VFunction *) info_universal_argument)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
info_numeric_arg *= 4;
|
|
|
|
|
key = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
2002-03-25 13:10:59 +00:00
|
|
|
|
#if defined(INFOKEY)
|
|
|
|
|
if (Meta_p (key))
|
|
|
|
|
key = UnMeta (key);
|
|
|
|
|
#endif /* !defined(INFOKEY) */
|
|
|
|
|
|
|
|
|
|
|
1997-01-11 02:12:38 +00:00
|
|
|
|
if (isdigit (key))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (info_explicit_arg)
|
|
|
|
|
info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
|
|
|
|
|
else
|
|
|
|
|
info_numeric_arg = (key - '0');
|
|
|
|
|
info_explicit_arg = 1;
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
else
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
key = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1999-01-14 19:56:32 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* Input Character Buffering */
|
|
|
|
|
/* */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Character waiting to be read next. */
|
|
|
|
|
static int pending_input_character = 0;
|
|
|
|
|
|
|
|
|
|
/* How to make there be no pending input. */
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_clear_pending_input (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
pending_input_character = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to set the pending input character. */
|
|
|
|
|
static void
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_set_pending_input (unsigned char key)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
pending_input_character = key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to see if there is any pending input. */
|
|
|
|
|
unsigned char
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_input_pending_p (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_push_typeahead (unsigned char key)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
/* 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;
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if ((unsigned int) push_index >= sizeof (info_input_buffer))
|
1999-01-14 19:56:32 +00:00
|
|
|
|
push_index = 0;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
|
|
|
|
|
static int
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_input_buffer_space_available (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_get_key_from_typeahead (unsigned char *key)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (push_index == pop_index)
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
*key = info_input_buffer[pop_index++];
|
|
|
|
|
|
2005-05-23 10:50:32 +00:00
|
|
|
|
if ((unsigned int) pop_index >= sizeof (info_input_buffer))
|
1997-01-11 02:12:38 +00:00
|
|
|
|
pop_index = 0;
|
|
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_any_buffered_input_p (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_gather_typeahead (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
chars_avail = read (tty, &input[0], chars_avail);
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
#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;
|
|
|
|
|
}
|
2000-01-17 10:45:43 +00:00
|
|
|
|
# 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__ */
|
1997-01-11 02:12:38 +00:00
|
|
|
|
# endif /* O_NDELAY */
|
|
|
|
|
#endif /* !FIONREAD */
|
|
|
|
|
|
|
|
|
|
while (i < chars_avail)
|
|
|
|
|
{
|
|
|
|
|
info_push_typeahead (input[i]);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to read a single character. */
|
|
|
|
|
unsigned char
|
2005-05-23 10:50:32 +00:00
|
|
|
|
info_get_input_char (void)
|
1997-01-11 02:12:38 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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
|
1997-01-11 02:12:38 +00:00
|
|
|
|
|
|
|
|
|
keystroke = rawkey;
|
|
|
|
|
|
|
|
|
|
if (rawkey == EOF)
|
1999-01-14 19:56:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (info_input_stream != stdin)
|
|
|
|
|
{
|
|
|
|
|
fclose (info_input_stream);
|
|
|
|
|
info_input_stream = stdin;
|
2000-01-17 10:45:43 +00:00
|
|
|
|
tty = fileno (info_input_stream);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
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 ();
|
2000-01-17 10:45:43 +00:00
|
|
|
|
xexit (0);
|
1999-01-14 19:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (info_dribble_file)
|
|
|
|
|
dribble (keystroke);
|
|
|
|
|
|
1999-01-14 19:56:32 +00:00
|
|
|
|
return keystroke;
|
1997-01-11 02:12:38 +00:00
|
|
|
|
}
|