1998-03-22 21:43:15 +01:00
|
|
|
|
/* session.c -- The user windowing interface to Info.
|
|
|
|
|
$Id: session.c,v 1.12 1997/07/24 21:34:00 karl Exp $
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
Copyright (C) 1993, 96, 97 Free Software Foundation, Inc.
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
|
|
Written by Brian Fox (bfox@ai.mit.edu). */
|
|
|
|
|
|
|
|
|
|
#include "info.h"
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_SYS_TIME_H)
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# define HAVE_STRUCT_TIMEVAL
|
|
|
|
|
#endif /* HAVE_SYS_TIME_H */
|
|
|
|
|
|
|
|
|
|
#if defined (HANDLE_MAN_PAGES)
|
|
|
|
|
# include "man.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void info_clear_pending_input (), info_set_pending_input ();
|
|
|
|
|
static void info_handle_pointer ();
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Running an Info Session */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* The place that we are reading input from. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
static FILE *info_input_stream = NULL;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* The last executed command. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
VFunction *info_last_executed_command = NULL;
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
INFO_WINDOW **info_windows = NULL;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Where to add the next window, if we need to add one. */
|
|
|
|
|
static int info_windows_index = 0;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* Number of slots allocated to `info_windows'. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
static int info_windows_slots = 0;
|
|
|
|
|
|
|
|
|
|
void remember_window_and_node (), forget_window_and_nodes ();
|
|
|
|
|
void initialize_info_session (), info_session ();
|
|
|
|
|
void display_startup_message_and_start ();
|
|
|
|
|
|
|
|
|
|
/* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
|
|
|
|
|
For each loaded node, create a new window. Always split the largest of the
|
|
|
|
|
available windows. */
|
|
|
|
|
void
|
|
|
|
|
begin_multiple_window_info_session (filename, nodenames)
|
|
|
|
|
char *filename;
|
|
|
|
|
char **nodenames;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
WINDOW *window = (WINDOW *)NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; nodenames[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
node = info_get_node (filename, nodenames[i]);
|
|
|
|
|
|
|
|
|
|
if (!node)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
break;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If this is the first node, initialize the info session. */
|
|
|
|
|
if (!window)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
initialize_info_session (node, 1);
|
|
|
|
|
window = active_window;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01: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. */
|
|
|
|
|
register WINDOW *win, *largest = (WINDOW *)NULL;
|
|
|
|
|
int max_height = 0;
|
|
|
|
|
|
|
|
|
|
for (win = windows; win; win = win->next)
|
|
|
|
|
if (win->height > max_height)
|
|
|
|
|
{
|
|
|
|
|
max_height = win->height;
|
|
|
|
|
largest = win;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!largest)
|
|
|
|
|
{
|
|
|
|
|
display_update_display (windows);
|
|
|
|
|
info_error (CANT_FIND_WIND);
|
|
|
|
|
info_session ();
|
|
|
|
|
exit (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
active_window = largest;
|
|
|
|
|
window = window_make_window (node);
|
|
|
|
|
if (window)
|
|
|
|
|
{
|
|
|
|
|
window_tile_windows (TILE_INTERNALS);
|
|
|
|
|
remember_window_and_node (window, node);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
display_update_display (windows);
|
|
|
|
|
info_error (WIN_TOO_SMALL);
|
|
|
|
|
info_session ();
|
|
|
|
|
exit (0);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
begin_info_session_with_error (initial_node, format, arg)
|
|
|
|
|
NODE *initial_node;
|
|
|
|
|
char *format;
|
|
|
|
|
void *arg;
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
initialize_info_session (initial_node, 1);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
info_error (format, arg, (void *)NULL);
|
|
|
|
|
info_session ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Start an info session with INITIAL_NODE. */
|
|
|
|
|
void
|
|
|
|
|
begin_info_session (initial_node)
|
|
|
|
|
NODE *initial_node;
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
initialize_info_session (initial_node, 1);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
display_startup_message_and_start ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
display_startup_message_and_start ()
|
|
|
|
|
{
|
|
|
|
|
char *format;
|
|
|
|
|
|
|
|
|
|
format = replace_in_documentation
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(_("Welcome to Info version %s. \"\\[get-help-window]\" for help, \"\\[menu-item]\" for menu item."));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
window_message_in_echo_area (format, version_string ());
|
|
|
|
|
info_session ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Run an info session with an already initialized window and node. */
|
|
|
|
|
void
|
|
|
|
|
info_session ()
|
|
|
|
|
{
|
|
|
|
|
display_update_display (windows);
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_last_executed_command = NULL;
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
info_read_and_dispatch ()
|
|
|
|
|
{
|
|
|
|
|
unsigned char key;
|
|
|
|
|
int done;
|
|
|
|
|
done = 0;
|
|
|
|
|
|
|
|
|
|
while (!done && !quit_info_immediately)
|
|
|
|
|
{
|
|
|
|
|
int lk;
|
|
|
|
|
|
|
|
|
|
/* If we haven't just gone up or down a line, there is no
|
1998-03-22 21:43:15 +01:00
|
|
|
|
goal column for this window. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if ((info_last_executed_command != info_next_line) &&
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(info_last_executed_command != info_prev_line))
|
|
|
|
|
active_window->goal_column = -1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
lk = echo_area_last_command_was_kill;
|
|
|
|
|
echo_area_prep_read ();
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!info_any_buffered_input_p ())
|
1998-03-22 21:43:15 +01:00
|
|
|
|
display_update_display (windows);
|
1997-08-22 00:57:35 +02: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
|
1998-03-22 21:43:15 +01:00
|
|
|
|
the echo_area if it is not currently active. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!echo_area_is_active)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_clear_echo_area ();
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
info_error_was_printed = 0;
|
|
|
|
|
|
|
|
|
|
/* Do the selected command. */
|
|
|
|
|
info_dispatch_on_key (key, active_window->keymap);
|
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
1998-03-22 21:43:15 +01: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;
|
|
|
|
|
|
|
|
|
|
if (ea_last_executed_command == ea_newline ||
|
|
|
|
|
info_aborted_echo_area)
|
|
|
|
|
{
|
|
|
|
|
ea_last_executed_command = (VFunction *)NULL;
|
|
|
|
|
done = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (info_last_executed_command == info_quit)
|
|
|
|
|
quit_info_immediately = 1;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else if (info_last_executed_command == info_quit)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
done = 1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Found in signals.c */
|
|
|
|
|
extern void initialize_info_signal_handler ();
|
|
|
|
|
|
|
|
|
|
/* Initialize the first info session by starting the terminal, window,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
void
|
1998-03-22 21:43:15 +01:00
|
|
|
|
initialize_info_session (node, clear_screen)
|
1997-08-22 00:57:35 +02:00
|
|
|
|
NODE *node;
|
1998-03-22 21:43:15 +01:00
|
|
|
|
int clear_screen;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
char *term_name = getenv ("TERM");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
terminal_initialize_terminal (term_name);
|
|
|
|
|
|
|
|
|
|
if (terminal_is_dumb_p)
|
|
|
|
|
{
|
|
|
|
|
if (!term_name)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
term_name = "dumb";
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
info_error (TERM_TOO_DUMB, term_name);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (clear_screen)
|
|
|
|
|
{
|
|
|
|
|
terminal_prep_terminal ();
|
|
|
|
|
terminal_clear_screen ();
|
|
|
|
|
}
|
|
|
|
|
|
1997-08-22 00:57:35 +02:00
|
|
|
|
initialize_info_keymaps ();
|
|
|
|
|
window_initialize_windows (screenwidth, screenheight);
|
|
|
|
|
initialize_info_signal_handler ();
|
|
|
|
|
display_initialize_display (screenwidth, screenheight);
|
|
|
|
|
info_set_node_of_window (active_window, node);
|
|
|
|
|
|
|
|
|
|
/* Tell the window system how to notify us when a window needs to be
|
|
|
|
|
asynchronously deleted (e.g., user resizes window very small). */
|
|
|
|
|
window_deletion_notifier = forget_window_and_nodes;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* If input has not been redirected yet, make it come from unbuffered
|
|
|
|
|
standard input. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!info_input_stream)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
setbuf(stdin, NULL);
|
|
|
|
|
info_input_stream = stdin;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
info_windows_initialized_p = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tell Info that input is coming from the file FILENAME. */
|
|
|
|
|
void
|
|
|
|
|
info_set_input_from_file (filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
|
|
|
|
FILE *stream;
|
|
|
|
|
|
|
|
|
|
stream = fopen (filename, "r");
|
|
|
|
|
|
|
|
|
|
if (!stream)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((info_input_stream != (FILE *)NULL) &&
|
|
|
|
|
(info_input_stream != stdin))
|
|
|
|
|
fclose (info_input_stream);
|
|
|
|
|
|
|
|
|
|
info_input_stream = stream;
|
|
|
|
|
|
|
|
|
|
if (stream != stdin)
|
|
|
|
|
display_inhibited = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
|
|
|
|
|
static INFO_WINDOW *
|
|
|
|
|
get_info_window_of_window (window)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; info_windows && (info_win = info_windows[i]); i++)
|
|
|
|
|
if (info_win->window == window)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
return (info_win);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Reset the remembered pagetop and point of WINDOW to WINDOW's current
|
|
|
|
|
values if the window and node are the same as the current one being
|
|
|
|
|
displayed. */
|
|
|
|
|
void
|
|
|
|
|
set_remembered_pagetop_and_point (window)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
INFO_WINDOW *info_win;
|
|
|
|
|
|
|
|
|
|
info_win = get_info_window_of_window (window);
|
|
|
|
|
|
|
|
|
|
if (!info_win)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (info_win->nodes_index &&
|
|
|
|
|
(info_win->nodes[info_win->current] == window->node))
|
|
|
|
|
{
|
|
|
|
|
info_win->pagetops[info_win->current] = window->pagetop;
|
|
|
|
|
info_win->points[info_win->current] = window->point;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
remember_window_and_node (window, node)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
NODE *node;
|
|
|
|
|
{
|
|
|
|
|
/* See if we already have this window in our list. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
INFO_WINDOW *info_win = get_info_window_of_window (window);
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_windows_slots, 10, INFO_WINDOW *);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this node, the current pagetop, and the current point are the
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02: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)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02: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++;
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_win->nodes[info_win->nodes_index] = NULL;
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
consistency_check_info_windows ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < info_windows_index; i++)
|
|
|
|
|
{
|
|
|
|
|
WINDOW *win;
|
|
|
|
|
|
|
|
|
|
for (win = windows; win; win = win->next)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (win == info_windows[i]->window)
|
|
|
|
|
break;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!win)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
abort ();
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* DEBUG_FORGET_WINDOW_AND_NODES */
|
|
|
|
|
|
|
|
|
|
/* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
|
|
|
|
|
void
|
|
|
|
|
forget_window_and_nodes (window)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; info_windows && (info_win = info_windows[i]); i++)
|
|
|
|
|
if (info_win->window == window)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* If we found the window to forget, then do so. */
|
|
|
|
|
if (info_win)
|
|
|
|
|
{
|
|
|
|
|
while (i < info_windows_index)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
info_windows[i] = info_windows[i + 1];
|
|
|
|
|
i++;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
info_windows_index--;
|
|
|
|
|
info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
|
|
|
|
|
|
|
|
|
|
if (info_win->nodes)
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02: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
|
|
|
|
|
the footnotes for this window. */
|
|
|
|
|
void
|
|
|
|
|
info_set_node_of_window (window, node)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
NODE *node;
|
|
|
|
|
{
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Info Movement Commands */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
|
|
|
|
|
to do so. */
|
|
|
|
|
void
|
|
|
|
|
set_window_pagetop (window, desired_top)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int desired_top;
|
|
|
|
|
{
|
|
|
|
|
int point_line, old_pagetop;
|
|
|
|
|
|
|
|
|
|
if (desired_top < 0)
|
|
|
|
|
desired_top = 0;
|
|
|
|
|
else if (desired_top > window->line_count)
|
|
|
|
|
desired_top = window->line_count - 1;
|
|
|
|
|
|
|
|
|
|
if (window->pagetop == desired_top)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
old_pagetop = window->pagetop;
|
|
|
|
|
window->pagetop = desired_top;
|
|
|
|
|
|
|
|
|
|
/* Make sure that point appears in this window. */
|
|
|
|
|
point_line = window_line_of_point (window);
|
|
|
|
|
if ((point_line < window->pagetop) ||
|
|
|
|
|
((point_line - window->pagetop) > window->height - 1))
|
|
|
|
|
window->point =
|
|
|
|
|
window->line_starts[window->pagetop] - window->node->contents;
|
|
|
|
|
|
|
|
|
|
window->flags |= W_UpdateWindow;
|
|
|
|
|
|
|
|
|
|
/* Find out which direction to scroll, and scroll the window in that
|
|
|
|
|
direction. Do this only if there would be a savings in redisplay
|
|
|
|
|
time. This is true if the amount to scroll is less than the height
|
|
|
|
|
of the window, and if the number of lines scrolled would be greater
|
|
|
|
|
than 10 % of the window's height. */
|
|
|
|
|
if (old_pagetop < desired_top)
|
|
|
|
|
{
|
|
|
|
|
int start, end, amount;
|
|
|
|
|
|
|
|
|
|
amount = desired_top - old_pagetop;
|
|
|
|
|
|
|
|
|
|
if ((amount >= window->height) ||
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(((window->height - amount) * 10) < window->height))
|
|
|
|
|
return;
|
1997-08-22 00:57:35 +02: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) ||
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(((window->height - amount) * 10) < window->height))
|
|
|
|
|
return;
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
info_show_point (window)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
int old_pagetop;
|
|
|
|
|
|
|
|
|
|
old_pagetop = window->pagetop;
|
|
|
|
|
window_adjust_pagetop (window);
|
|
|
|
|
if (old_pagetop != window->pagetop)
|
|
|
|
|
{
|
|
|
|
|
int new_pagetop;
|
|
|
|
|
|
|
|
|
|
new_pagetop = window->pagetop;
|
|
|
|
|
window->pagetop = old_pagetop;
|
|
|
|
|
set_window_pagetop (window, new_pagetop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (window->flags & W_UpdateWindow)
|
|
|
|
|
display_update_one_window (window);
|
|
|
|
|
|
|
|
|
|
display_cursor_at_point (window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move WINDOW->point from OLD line index to NEW line index. */
|
|
|
|
|
static void
|
|
|
|
|
move_to_new_line (old, new, window)
|
|
|
|
|
int old, new;
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
if (old == -1)
|
|
|
|
|
{
|
|
|
|
|
info_error (CANT_FIND_POINT);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int goal;
|
|
|
|
|
|
|
|
|
|
if (new >= window->line_count || new < 0)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return;
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
register int point;
|
|
|
|
|
register char *buffer;
|
|
|
|
|
|
|
|
|
|
buffer = window->node->contents;
|
|
|
|
|
point = window->point;
|
|
|
|
|
|
|
|
|
|
for (; (point) && (buffer[point - 1] != '\n'); point--);
|
|
|
|
|
|
|
|
|
|
/* If at a line start alreay, do nothing. */
|
|
|
|
|
if (point != window->point)
|
|
|
|
|
{
|
|
|
|
|
window->point = point;
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move point forward in the node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_backward_char (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
window->point += count;
|
|
|
|
|
|
|
|
|
|
if (window->point >= window->node->nodelen)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window->point = window->node->nodelen - 1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move point backward in the node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_forward_char (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
window->point -= count;
|
|
|
|
|
|
|
|
|
|
if (window->point < 0)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window->point = 0;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
|
|
|
|
|
|
|
|
|
|
/* Move forward a word in this node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If we are not in a word, move forward until we are in one.
|
1998-03-22 21:43:15 +01:00
|
|
|
|
Then, move forward until we hit a non-alphabetic character. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
c = buffer[point];
|
|
|
|
|
|
|
|
|
|
if (!alphabetic (c))
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
while (++point < end)
|
|
|
|
|
{
|
|
|
|
|
c = buffer[point];
|
|
|
|
|
if (alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (point >= end) return;
|
|
|
|
|
|
|
|
|
|
while (++point < end)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
c = buffer[point];
|
|
|
|
|
if (!alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
--count;
|
|
|
|
|
}
|
|
|
|
|
window->point = point;
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
break;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Like info_forward_word (), except that we look at the
|
1998-03-22 21:43:15 +01:00
|
|
|
|
characters just before point. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
c = buffer[point - 1];
|
|
|
|
|
|
|
|
|
|
if (!alphabetic (c))
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
while (--point)
|
|
|
|
|
{
|
|
|
|
|
c = buffer[point - 1];
|
|
|
|
|
if (alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
while (point)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
c = buffer[point - 1];
|
|
|
|
|
if (!alphabetic (c))
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
--point;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
--count;
|
|
|
|
|
}
|
|
|
|
|
window->point = point;
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Here is a list of time counter names which correspond to ordinal numbers.
|
|
|
|
|
It is used to print "once" instead of "1". */
|
|
|
|
|
static char *counter_names[] = {
|
|
|
|
|
"not at all", "once", "twice", "three", "four", "five", "six",
|
|
|
|
|
(char *)NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Buffer used to return values from times_description (). */
|
|
|
|
|
static char td_buffer[50];
|
|
|
|
|
|
|
|
|
|
/* Function returns a static string fully describing the number of times
|
|
|
|
|
present in COUNT. */
|
|
|
|
|
static char *
|
|
|
|
|
times_description (count)
|
|
|
|
|
int count;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
td_buffer[0] = '\0';
|
|
|
|
|
|
|
|
|
|
for (i = 0; counter_names[i]; i++)
|
|
|
|
|
if (count == i)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (counter_names[i])
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (td_buffer, "%s%s", counter_names[i], count > 2 ? _(" times") : "");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (td_buffer, _("%d times"), count);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
return (td_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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:
|
|
|
|
|
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02: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
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Move to 1st menu item, Next, Up/Next, or error in this window. */
|
|
|
|
|
static void
|
|
|
|
|
forward_move_node_structure (window, behaviour)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int behaviour;
|
|
|
|
|
{
|
|
|
|
|
switch (behaviour)
|
|
|
|
|
{
|
|
|
|
|
case IS_PageOnly:
|
|
|
|
|
info_error (AT_NODE_BOTTOM);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_NextOnly:
|
|
|
|
|
info_next_label_of_node (window->node);
|
|
|
|
|
if (!info_parsed_nodename && !info_parsed_filename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("No \"Next\" pointer for this node."));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area (_("Following \"Next\" node..."));
|
|
|
|
|
info_handle_pointer (_("Next"), window);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_Continuous:
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01: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);
|
|
|
|
|
window_message_in_echo_area (_("Selecting first menu item..."));
|
|
|
|
|
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);
|
|
|
|
|
if (info_label_was_found)
|
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area (_("Selecting \"Next\" node..."));
|
|
|
|
|
info_handle_pointer (_("Next"), window);
|
|
|
|
|
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);
|
|
|
|
|
if (info_label_was_found)
|
|
|
|
|
{
|
|
|
|
|
info_handle_pointer (_("Up"), window);
|
|
|
|
|
if (info_error_was_printed)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
up_counter++;
|
|
|
|
|
|
|
|
|
|
info_next_label_of_node (window->node);
|
|
|
|
|
|
|
|
|
|
/* If no "Next" pointer, keep backing up. */
|
|
|
|
|
if (!info_label_was_found)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* If this node's first menu item is the same as this node's
|
|
|
|
|
Next pointer, keep backing up. */
|
|
|
|
|
if (!info_parsed_filename)
|
|
|
|
|
{
|
|
|
|
|
REFERENCE **menu;
|
|
|
|
|
char *next_nodename;
|
|
|
|
|
|
|
|
|
|
/* Remember the name of the Next node, since reading
|
|
|
|
|
the menu can overwrite the contents of the
|
|
|
|
|
info_parsed_xxx strings. */
|
|
|
|
|
next_nodename = xstrdup (info_parsed_nodename);
|
|
|
|
|
|
|
|
|
|
menu = info_menu_of_node (window->node);
|
|
|
|
|
if (menu &&
|
|
|
|
|
(strcmp
|
|
|
|
|
(menu[0]->nodename, next_nodename) == 0))
|
|
|
|
|
{
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
free (next_nodename);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Restore the world to where it was before
|
|
|
|
|
reading the menu contents. */
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
free (next_nodename);
|
|
|
|
|
info_next_label_of_node (window->node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This node has a "Next" pointer, and it is not the
|
|
|
|
|
same as the first menu item found in this node. */
|
|
|
|
|
window_message_in_echo_area
|
|
|
|
|
("Moving \"Up\" %s, then \"Next\".",
|
|
|
|
|
times_description (up_counter));
|
|
|
|
|
|
|
|
|
|
info_handle_pointer (_("Next"), window);
|
|
|
|
|
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;
|
|
|
|
|
info_error (_("No more nodes."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
|
|
|
|
|
static void
|
|
|
|
|
backward_move_node_structure (window, behaviour)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int behaviour;
|
|
|
|
|
{
|
|
|
|
|
switch (behaviour)
|
|
|
|
|
{
|
|
|
|
|
case IS_PageOnly:
|
|
|
|
|
info_error (AT_NODE_TOP);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_NextOnly:
|
|
|
|
|
info_prev_label_of_node (window->node);
|
|
|
|
|
if (!info_parsed_nodename && !info_parsed_filename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("No \"Prev\" for this node."));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area (_("Moving \"Prev\" in this window."));
|
|
|
|
|
info_handle_pointer (_("Prev"), window);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IS_Continuous:
|
|
|
|
|
info_prev_label_of_node (window->node);
|
|
|
|
|
|
|
|
|
|
if (!info_parsed_nodename && !info_parsed_filename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
info_up_label_of_node (window->node);
|
|
|
|
|
if (!info_parsed_nodename && !info_parsed_filename)
|
|
|
|
|
info_error (_("No \"Prev\" or \"Up\" for this node."));
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area (_("Moving \"Up\" in this window."));
|
|
|
|
|
info_handle_pointer (_("Up"), window);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01: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. */
|
|
|
|
|
window_message_in_echo_area (_("Moving \"Prev\" in this window."));
|
|
|
|
|
info_handle_pointer (_("Prev"), window);
|
|
|
|
|
|
|
|
|
|
if (!inhibit_menu_traversing)
|
|
|
|
|
{
|
|
|
|
|
while (!info_error_was_printed &&
|
|
|
|
|
(menu = info_menu_of_node (window->node)))
|
|
|
|
|
{
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
window_message_in_echo_area
|
|
|
|
|
(_("Moving to \"Prev\"'s last menu item."));
|
|
|
|
|
info_menu_digit (window, 1, '0');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move continuously forward through the node structure of this info file. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_global_next_node,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Move forwards or down through node structure"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_global_prev_node (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (count && !info_error_was_printed)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
forward_move_node_structure (window, IS_Continuous);
|
|
|
|
|
count--;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move continuously backward through the node structure of this info file. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_global_prev_node,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Move backwards or up through node structure"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_global_next_node (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (count && !info_error_was_printed)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
backward_move_node_structure (window, IS_Continuous);
|
|
|
|
|
count--;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Show the next screen of WINDOW's node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_scroll_backward (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int desired_top;
|
|
|
|
|
|
|
|
|
|
/* Without an explicit numeric argument, scroll the bottom two
|
1998-03-22 21:43:15 +01:00
|
|
|
|
lines to the top of this window, Or, if at bottom of window,
|
|
|
|
|
and the user wishes to scroll through nodes get the "Next" node
|
|
|
|
|
for this window. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!info_explicit_arg && count == 1)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
desired_top = window->pagetop + (window->height - 2);
|
|
|
|
|
|
|
|
|
|
/* If there are no more lines to scroll here, error, or get
|
|
|
|
|
another node, depending on INFO_SCROLL_BEHAVIOUR. */
|
|
|
|
|
if (desired_top > window->line_count)
|
|
|
|
|
{
|
|
|
|
|
int behaviour = info_scroll_behaviour;
|
|
|
|
|
|
|
|
|
|
/* Here is a hack. If the key being used is not SPC, do the
|
|
|
|
|
PageOnly behaviour. */
|
|
|
|
|
if (key != SPC && key != DEL)
|
|
|
|
|
behaviour = IS_PageOnly;
|
|
|
|
|
|
|
|
|
|
forward_move_node_structure (window, behaviour);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_top = window->pagetop + count;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (desired_top >= window->line_count)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_top = window->line_count - 2;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (window->pagetop > desired_top)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
set_window_pagetop (window, desired_top);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Show the previous screen of WINDOW's node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
info_scroll_forward (window, -count, key);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int desired_top;
|
|
|
|
|
|
|
|
|
|
/* Without an explicit numeric argument, scroll the top two lines
|
1998-03-22 21:43:15 +01:00
|
|
|
|
to the bottom of this window, or move to the previous, or Up'th
|
|
|
|
|
node. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!info_explicit_arg && count == 1)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
desired_top = window->pagetop - (window->height - 2);
|
|
|
|
|
|
|
|
|
|
if ((desired_top < 0) && (window->pagetop == 0))
|
|
|
|
|
{
|
|
|
|
|
int behaviour = info_scroll_behaviour;
|
|
|
|
|
|
|
|
|
|
/* Same kind of hack as in info_scroll_forward. If the key
|
|
|
|
|
used to invoke this command is not DEL, do only the PageOnly
|
|
|
|
|
behaviour. */
|
|
|
|
|
if (key != DEL && key != SPC)
|
|
|
|
|
behaviour = IS_PageOnly;
|
|
|
|
|
|
|
|
|
|
backward_move_node_structure (window, behaviour);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_top = window->pagetop - count;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (desired_top < 0)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_top = 0;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
set_window_pagetop (window, desired_top);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move to the beginning of the node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
window->pagetop = window->point = 0;
|
|
|
|
|
window->flags |= W_UpdateWindow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move to the end of the node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
window->point = window->node->nodelen - 1;
|
|
|
|
|
info_show_point (window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Commands for Manipulating Windows */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Make the next window in the chain be the active window. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
{
|
|
|
|
|
info_prev_window (window, -count, key);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If no other window, error now. */
|
|
|
|
|
if (!windows->next && !echo_area_is_active)
|
|
|
|
|
{
|
|
|
|
|
info_error (ONE_WINDOW);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (count--)
|
|
|
|
|
{
|
|
|
|
|
if (window->next)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window = window->next;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (window == the_echo_area || !echo_area_is_active)
|
|
|
|
|
window = windows;
|
|
|
|
|
else
|
|
|
|
|
window = the_echo_area;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (active_window != window)
|
|
|
|
|
{
|
|
|
|
|
if (auto_footnotes_p)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_get_or_remove_footnotes (window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
window->flags |= W_UpdateWindow;
|
|
|
|
|
active_window = window;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the previous window in the chain be the active window. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (count < 0)
|
|
|
|
|
{
|
|
|
|
|
info_next_window (window, -count, key);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only one window? */
|
|
|
|
|
|
|
|
|
|
if (!windows->next && !echo_area_is_active)
|
|
|
|
|
{
|
|
|
|
|
info_error (ONE_WINDOW);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (count--)
|
|
|
|
|
{
|
|
|
|
|
/* If we are in the echo area, or if the echo area isn't active and we
|
1998-03-22 21:43:15 +01:00
|
|
|
|
are in the first window, find the last window in the chain. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (window == the_echo_area ||
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(window == windows && !echo_area_is_active))
|
|
|
|
|
{
|
|
|
|
|
register WINDOW *win, *last;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
for (win = windows; win; win = win->next)
|
|
|
|
|
last = win;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window = last;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (window == windows)
|
|
|
|
|
window = the_echo_area;
|
|
|
|
|
else
|
|
|
|
|
window = window->prev;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (active_window != window)
|
|
|
|
|
{
|
|
|
|
|
if (auto_footnotes_p)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_get_or_remove_footnotes (window);
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
|
1997-08-22 00:57:35 +02: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)
|
|
|
|
|
{
|
|
|
|
|
info_error (WIN_TOO_SMALL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#if defined (SPLIT_BEFORE_ACTIVE)
|
|
|
|
|
/* Try to scroll the old window into its new postion. */
|
|
|
|
|
if (pagetop == window->pagetop)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
int start, end, amount;
|
|
|
|
|
|
|
|
|
|
start = split->first_row;
|
|
|
|
|
end = start + window->height;
|
|
|
|
|
amount = split->height + 1;
|
|
|
|
|
display_scroll_display (start, end, amount);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02: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
|
1998-03-22 21:43:15 +01:00
|
|
|
|
something else in it. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (internal_info_node_p (split->node))
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
split->pagetop = window->pagetop;
|
|
|
|
|
|
|
|
|
|
if (auto_tiling_p)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_tile_windows (DONT_TILE_INTERNALS);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_adjust_pagetop (split);
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
if (!windows->next)
|
|
|
|
|
{
|
|
|
|
|
info_error (CANT_KILL_LAST);
|
|
|
|
|
}
|
|
|
|
|
else if (window->flags & W_WindowIsPerm)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Cannot delete a permanent window"));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info_delete_window_internal (window);
|
|
|
|
|
|
|
|
|
|
if (auto_footnotes_p)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_get_or_remove_footnotes (active_window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (auto_tiling_p)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_tile_windows (DONT_TILE_INTERNALS);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do the physical deletion of WINDOW, and forget this window and
|
|
|
|
|
associated nodes. */
|
|
|
|
|
void
|
|
|
|
|
info_delete_window_internal (window)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
|
|
|
|
|
{
|
|
|
|
|
/* We not only delete the window from the display, we forget it from
|
1998-03-22 21:43:15 +01:00
|
|
|
|
our list of remembered windows. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
forget_window_and_nodes (window);
|
|
|
|
|
window_delete_window (window);
|
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
echo_area_inform_of_deleted_window (window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Just keep WINDOW, deleting all others. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
int num_deleted; /* The number of windows we deleted. */
|
1997-08-22 00:57:35 +02: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
|
1998-03-22 21:43:15 +01:00
|
|
|
|
are found, we are done. A window is eligible for deletion if
|
|
|
|
|
is it not permanent, and it is not WINDOW. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
for (win = windows; win; win = win->next)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (win != window && ((win->flags & W_WindowIsPerm) == 0))
|
|
|
|
|
break;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!win)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
break;
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
WINDOW *other;
|
|
|
|
|
|
|
|
|
|
/* If only one window, give up. */
|
|
|
|
|
if (!windows->next)
|
|
|
|
|
{
|
|
|
|
|
info_error (ONE_WINDOW);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
other = window->next;
|
|
|
|
|
|
|
|
|
|
if (!other)
|
|
|
|
|
other = window->prev;
|
|
|
|
|
|
|
|
|
|
info_scroll_forward (other, count, key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Change the size of WINDOW by AMOUNT. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Divide the available screen space among the visible windows"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
window_tile_windows (TILE_INTERNALS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Toggle the state of this window's wrapping of lines. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_toggle_wrap,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Toggle the state of line wrapping in the current window"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
window_toggle_wrap (window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Info Node Commands */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
info_select_reference (window, entry)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
REFERENCE *entry;
|
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
char *filename, *nodename, *file_system_error;
|
|
|
|
|
|
|
|
|
|
file_system_error = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
filename = entry->filename;
|
|
|
|
|
if (!filename)
|
|
|
|
|
filename = window->node->parent;
|
|
|
|
|
if (!filename)
|
|
|
|
|
filename = window->node->filename;
|
|
|
|
|
|
|
|
|
|
if (filename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
filename = xstrdup (filename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (entry->nodename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
nodename = xstrdup (entry->nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
nodename = xstrdup ("Top");
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
file_system_error = xstrdup (info_recent_file_error);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
|
{
|
|
|
|
|
if (file_system_error)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (file_system_error);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (CANT_FIND_NODE, nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybe_free (file_system_error);
|
|
|
|
|
maybe_free (filename);
|
|
|
|
|
maybe_free (nodename);
|
|
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (window);
|
|
|
|
|
info_set_node_of_window (window, node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse the node specification in LINE using WINDOW to default the filename.
|
|
|
|
|
Select the parsed node in WINDOW and remember it, or error if the node
|
|
|
|
|
couldn't be found. */
|
|
|
|
|
static void
|
|
|
|
|
info_parse_and_select (line, window)
|
|
|
|
|
char *line;
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
REFERENCE entry;
|
|
|
|
|
|
|
|
|
|
info_parse_node (line, DONT_SKIP_NEWLINES);
|
|
|
|
|
|
|
|
|
|
entry.nodename = info_parsed_nodename;
|
|
|
|
|
entry.filename = info_parsed_filename;
|
|
|
|
|
entry.label = "*info-parse-and-select*";
|
|
|
|
|
|
|
|
|
|
info_select_reference (window, &entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
|
|
|
|
|
are previously filled, try to get the node represented by them into
|
|
|
|
|
WINDOW. The node should have been pointed to by the LABEL pointer of
|
|
|
|
|
WINDOW->node. */
|
|
|
|
|
static void
|
|
|
|
|
info_handle_pointer (label, window)
|
|
|
|
|
char *label;
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
if (info_parsed_filename || info_parsed_nodename)
|
|
|
|
|
{
|
|
|
|
|
char *filename, *nodename;
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
filename = nodename = (char *)NULL;
|
|
|
|
|
|
|
|
|
|
if (info_parsed_filename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
filename = xstrdup (info_parsed_filename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (window->node->parent)
|
|
|
|
|
filename = xstrdup (window->node->parent);
|
|
|
|
|
else if (window->node->filename)
|
|
|
|
|
filename = xstrdup (window->node->filename);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (info_parsed_nodename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
nodename = xstrdup (info_parsed_nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
nodename = xstrdup ("Top");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
node = info_get_node (filename, nodename);
|
|
|
|
|
|
|
|
|
|
if (node)
|
1998-03-22 21:43:15 +01: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;
|
|
|
|
|
}
|
|
|
|
|
set_remembered_pagetop_and_point (window);
|
|
|
|
|
info_set_node_of_window (window, node);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
|
|
|
|
info_error (info_recent_file_error);
|
|
|
|
|
else
|
|
|
|
|
info_error (CANT_FILE_NODE, filename, nodename);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
free (filename);
|
|
|
|
|
free (nodename);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info_error (NO_POINTER, label);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the "Next:" node of the node currently being
|
|
|
|
|
displayed. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_next_node, _("Select the `Next' node"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
info_next_label_of_node (window->node);
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_handle_pointer (_("Next"), window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the "Prev:" node of the node currently being
|
|
|
|
|
displayed. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_prev_node, _("Select the `Prev' node"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
info_prev_label_of_node (window->node);
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_handle_pointer (_("Prev"), window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the "Up:" node of the node currently being
|
|
|
|
|
displayed. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_up_node, _("Select the `Up' node"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
info_up_label_of_node (window->node);
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_handle_pointer (_("Up"), window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the last node of this info file. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
FILE_BUFFER *fb = file_buffer_of_window (window);
|
|
|
|
|
NODE *node = (NODE *)NULL;
|
|
|
|
|
|
|
|
|
|
if (fb && fb->tags)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; fb->tags[i]; i++);
|
|
|
|
|
node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!node)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("This window has no additional nodes"));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (window);
|
|
|
|
|
info_set_node_of_window (window, node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make WINDOW display the first node of this info file. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
FILE_BUFFER *fb = file_buffer_of_window (window);
|
|
|
|
|
NODE *node = (NODE *)NULL;
|
|
|
|
|
|
|
|
|
|
if (fb && fb->tags)
|
|
|
|
|
node = info_get_node (fb->filename, fb->tags[0]->nodename);
|
|
|
|
|
|
|
|
|
|
if (!node)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("This window has no additional nodes"));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (window);
|
|
|
|
|
info_set_node_of_window (window, node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Select the last menu item in WINDOW->node. */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_last_menu_item,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Select the last item in this node's menu"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
info_menu_digit (window, 1, '0');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
register int i, item;
|
|
|
|
|
register REFERENCE *entry, **menu;
|
|
|
|
|
|
|
|
|
|
menu = info_menu_of_node (window->node);
|
|
|
|
|
|
|
|
|
|
if (!menu)
|
|
|
|
|
{
|
|
|
|
|
info_error (NO_MENU_NODE);
|
|
|
|
|
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
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
for (i = 0; (entry = menu[i]); i++)
|
|
|
|
|
if (i == item - 1)
|
|
|
|
|
break;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (menu[i])
|
|
|
|
|
info_select_reference (window, menu[i]);
|
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("There aren't %d items in this menu."), item);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read a menu or followed reference from the user defaulting to the
|
|
|
|
|
reference found on the current line, and select that node. The
|
|
|
|
|
reading is done with completion. BUILDER is the function used
|
|
|
|
|
to build the list of references. ASK_P is non-zero if the user
|
|
|
|
|
should be prompted, or zero to select the default item. */
|
|
|
|
|
static void
|
|
|
|
|
info_menu_or_ref_item (window, count, key, builder, ask_p)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int count;
|
|
|
|
|
unsigned char key;
|
|
|
|
|
REFERENCE **(*builder) ();
|
|
|
|
|
int ask_p;
|
|
|
|
|
{
|
|
|
|
|
REFERENCE **menu, *entry, *defentry = (REFERENCE *)NULL;
|
|
|
|
|
char *line;
|
|
|
|
|
|
|
|
|
|
menu = (*builder) (window->node);
|
|
|
|
|
|
|
|
|
|
if (!menu)
|
|
|
|
|
{
|
|
|
|
|
if (builder == info_menu_of_node)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (NO_MENU_NODE);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (NO_XREF_NODE);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Default the selected reference to the one which is on the line that
|
|
|
|
|
point is in. */
|
|
|
|
|
{
|
|
|
|
|
REFERENCE **refs = (REFERENCE **)NULL;
|
|
|
|
|
int point_line;
|
|
|
|
|
|
|
|
|
|
point_line = window_line_of_point (window);
|
|
|
|
|
|
|
|
|
|
if (point_line != -1)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
#if defined (HANDLE_MAN_PAGES)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (window->node->flags & N_IsManPage)
|
|
|
|
|
refs = manpage_xrefs_in_binding (window->node, &binding);
|
|
|
|
|
else
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#endif /* HANDLE_MAN_PAGES */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
refs = info_xrefs (&binding);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (refs)
|
|
|
|
|
{
|
|
|
|
|
if ((strcmp (refs[0]->label, "Menu") != 0) ||
|
|
|
|
|
(builder == info_xrefs_of_node))
|
|
|
|
|
{
|
|
|
|
|
int which = 0;
|
|
|
|
|
|
|
|
|
|
/* Find the closest reference to point. */
|
|
|
|
|
if (builder == info_xrefs_of_node)
|
|
|
|
|
{
|
|
|
|
|
int closest = -1;
|
|
|
|
|
|
|
|
|
|
for (; refs[which]; which++)
|
|
|
|
|
{
|
|
|
|
|
if ((window->point >= refs[which]->start) &&
|
|
|
|
|
(window->point <= refs[which]->end))
|
|
|
|
|
{
|
|
|
|
|
closest = which;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (window->point < refs[which]->start)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (closest == -1)
|
|
|
|
|
which--;
|
|
|
|
|
else
|
|
|
|
|
which = closest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
|
|
|
|
|
defentry->label = xstrdup (refs[which]->label);
|
|
|
|
|
defentry->filename = refs[which]->filename;
|
|
|
|
|
defentry->nodename = refs[which]->nodename;
|
|
|
|
|
|
|
|
|
|
if (defentry->filename)
|
|
|
|
|
defentry->filename = xstrdup (defentry->filename);
|
|
|
|
|
if (defentry->nodename)
|
|
|
|
|
defentry->nodename = xstrdup (defentry->nodename);
|
|
|
|
|
}
|
|
|
|
|
info_free_references (refs);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we are going to ask the user a question, do it now. */
|
|
|
|
|
if (ask_p)
|
|
|
|
|
{
|
|
|
|
|
char *prompt;
|
|
|
|
|
|
|
|
|
|
/* Build the prompt string. */
|
|
|
|
|
if (defentry)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
prompt = (char *)xmalloc (20 + strlen (defentry->label));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
prompt = (char *)xmalloc (20);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (builder == info_menu_of_node)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (defentry)
|
|
|
|
|
sprintf (prompt, _("Menu item (%s): "), defentry->label);
|
|
|
|
|
else
|
|
|
|
|
sprintf (prompt, _("Menu item: "));
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (defentry)
|
|
|
|
|
sprintf (prompt, _("Follow xref (%s): "), defentry->label);
|
|
|
|
|
else
|
|
|
|
|
sprintf (prompt, _("Follow xref: "));
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
line = info_read_completing_in_echo_area (window, prompt, menu);
|
|
|
|
|
free (prompt);
|
|
|
|
|
|
|
|
|
|
window = active_window;
|
|
|
|
|
|
|
|
|
|
/* User aborts, just quit. */
|
|
|
|
|
if (!line)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
maybe_free (defentry);
|
|
|
|
|
info_free_references (menu);
|
|
|
|
|
info_abort_key (window, 0, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If we had a default and the user accepted it, use that. */
|
|
|
|
|
if (!*line)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
free (line);
|
|
|
|
|
if (defentry)
|
|
|
|
|
line = xstrdup (defentry->label);
|
|
|
|
|
else
|
|
|
|
|
line = (char *)NULL;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Not going to ask any questions. If we have a default entry, use
|
1998-03-22 21:43:15 +01:00
|
|
|
|
that, otherwise return. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!defentry)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
line = xstrdup (defentry->label);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line)
|
|
|
|
|
{
|
|
|
|
|
/* Find the selected label in the references. */
|
|
|
|
|
entry = info_get_labeled_reference (line, menu);
|
|
|
|
|
|
|
|
|
|
if (!entry && defentry)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("The reference disappeared! (%s)."), line);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
NODE *orig;
|
|
|
|
|
|
|
|
|
|
orig = window->node;
|
|
|
|
|
info_select_reference (window, entry);
|
|
|
|
|
if ((builder == info_xrefs_of_node) && (window->node != orig))
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
free (line);
|
|
|
|
|
if (defentry)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
free (defentry->label);
|
|
|
|
|
maybe_free (defentry->filename);
|
|
|
|
|
maybe_free (defentry->nodename);
|
|
|
|
|
free (defentry);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
|
1997-08-22 00:57:35 +02: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
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(info_xref_item, _("Read a footnote or cross reference and select its node"))
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
|
1997-08-22 00:57:35 +02: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)
|
|
|
|
|
info_error (NO_MENU_NODE);
|
|
|
|
|
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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Visit as many menu items at once as possible"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
REFERENCE *entry, **menu;
|
|
|
|
|
|
|
|
|
|
menu = info_menu_of_node (window->node);
|
|
|
|
|
|
|
|
|
|
if (!menu)
|
|
|
|
|
info_error (NO_MENU_NODE);
|
|
|
|
|
|
|
|
|
|
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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (WIN_TOO_SMALL);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
active_window = new;
|
|
|
|
|
info_select_reference (new, entry);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read a line of input which is a node name, and go to that node. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
|
1997-08-22 00:57:35 +02: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++)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01: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;
|
|
|
|
|
entry->label = (char *) xmalloc
|
|
|
|
|
(4 + strlen (fb->filename) + strlen (fb->tags[i]->nodename));
|
|
|
|
|
sprintf (entry->label, "(%s)%s",
|
|
|
|
|
fb->filename, fb->tags[i]->nodename);
|
|
|
|
|
|
|
|
|
|
add_pointer_to_array
|
|
|
|
|
(entry, items_index, items, items_slots, 100, REFERENCE *);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this_is_the_current_fb)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; fb->tags[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
|
|
|
|
|
entry->filename = entry->nodename = (char *)NULL;
|
|
|
|
|
entry->label = xstrdup (fb->tags[i]->nodename);
|
|
|
|
|
add_pointer_to_array (entry, items_index, items,
|
|
|
|
|
items_slots, 100, REFERENCE *);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
1998-03-22 21:43:15 +01:00
|
|
|
|
line = info_read_maybe_completing (window, _("Goto Node: "), items);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
info_free_references (items);
|
|
|
|
|
}
|
|
|
|
|
#else /* !GOTO_COMPLETES */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
line = info_read_in_echo_area (window, _("Goto Node: "));
|
1997-08-22 00:57:35 +02: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 ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined (HANDLE_MAN_PAGES)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
line = info_read_in_echo_area (window, _("Get Manpage: "));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!line)
|
|
|
|
|
{
|
|
|
|
|
info_abort_key (window, 0, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canonicalize_whitespace (line);
|
|
|
|
|
|
|
|
|
|
if (*line)
|
|
|
|
|
{
|
|
|
|
|
char *goto_command;
|
|
|
|
|
|
|
|
|
|
goto_command = (char *)xmalloc
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_parse_and_select (_("Top"), window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move to the node "(dir)Top". */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
info_parse_and_select ("(dir)Top", window);
|
|
|
|
|
}
|
|
|
|
|
|
1998-03-22 21:43:15 +01: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 *
|
|
|
|
|
read_nodename_to_kill (window)
|
|
|
|
|
WINDOW *window;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
1998-03-22 21:43:15 +01: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);
|
|
|
|
|
char *prompt = xmalloc (40 + strlen (default_nodename));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (prompt, _("Kill node (%s): "), default_nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
|
|
|
|
|
REFERENCE *);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return nodename;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* Delete NODENAME from this window, showing the most
|
|
|
|
|
recently selected node in this window. */
|
|
|
|
|
static void
|
|
|
|
|
kill_node (window, nodename)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
char *nodename;
|
|
|
|
|
{
|
|
|
|
|
int iw, i;
|
|
|
|
|
INFO_WINDOW *info_win;
|
|
|
|
|
NODE *temp;
|
|
|
|
|
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
for (iw = 0; (info_win = info_windows[iw]); iw++)
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!info_win)
|
|
|
|
|
{
|
|
|
|
|
if (*nodename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Cannot kill node `%s'"), nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_clear_echo_area ();
|
1997-08-22 00:57:35 +02: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)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Cannot kill the last node"));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
for (i = info_win->current; i < info_win->nodes_index; i++)
|
|
|
|
|
info_win->nodes[i] = info_win->nodes[i++];
|
|
|
|
|
|
|
|
|
|
/* 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])
|
1998-03-22 21:43:15 +01:00
|
|
|
|
stealer = info_windows[iw + 1];
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
stealer = info_windows[0];
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If the node being displayed in the next window is not the most
|
1998-03-22 21:43:15 +01:00
|
|
|
|
recently loaded one, get the most recently loaded one. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if ((stealer->nodes_index - 1) != stealer->current)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
which = stealer->nodes_index - 1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Else, if there is another node behind the stealers current node,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
use that one. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else if (stealer->current > 0)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
which = stealer->current - 1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Else, just use the node appearing in STEALER's window. */
|
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
which = stealer->current;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Copy this node. */
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
NODE *copy = xmalloc (sizeof (NODE));
|
|
|
|
|
|
|
|
|
|
temp = stealer->nodes[which];
|
|
|
|
|
point = stealer->points[which];
|
|
|
|
|
pagetop = stealer->pagetops[which];
|
|
|
|
|
|
|
|
|
|
copy->filename = temp->filename;
|
|
|
|
|
copy->parent = temp->parent;
|
|
|
|
|
copy->nodename = temp->nodename;
|
|
|
|
|
copy->contents = temp->contents;
|
|
|
|
|
copy->nodelen = temp->nodelen;
|
|
|
|
|
copy->flags = temp->flags;
|
|
|
|
|
|
|
|
|
|
temp = copy;
|
1997-08-22 00:57:35 +02: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];
|
|
|
|
|
window_set_node_of_window (info_win->window, temp);
|
|
|
|
|
}
|
1998-03-22 21:43:15 +01:00
|
|
|
|
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!info_error_was_printed)
|
|
|
|
|
window_clear_echo_area ();
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* Read the name of a file and select the entire file. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
line = info_read_in_echo_area (window, _("Find file: "));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!line)
|
|
|
|
|
{
|
|
|
|
|
info_abort_key (active_window, 1, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*line)
|
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
node = info_get_node (line, "*");
|
|
|
|
|
if (!node)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
|
|
|
|
info_error (info_recent_file_error);
|
|
|
|
|
else
|
|
|
|
|
info_error (_("Cannot find \"%s\"."), line);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (active_window);
|
|
|
|
|
info_set_node_of_window (window, node);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!info_error_was_printed)
|
|
|
|
|
window_clear_echo_area ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Dumping and Printing Nodes */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
#define VERBOSE_NODE_DUMPING
|
|
|
|
|
static void write_node_to_stream ();
|
|
|
|
|
static void dump_node_to_stream ();
|
|
|
|
|
static void initialize_dumping ();
|
|
|
|
|
|
|
|
|
|
/* Dump the nodes specified by FILENAME and NODENAMES to the file named
|
|
|
|
|
in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
|
|
|
|
|
the nodes which appear in the menu of each node dumped. */
|
|
|
|
|
void
|
|
|
|
|
dump_nodes_to_file (filename, nodenames, output_filename, dump_subnodes)
|
|
|
|
|
char *filename;
|
|
|
|
|
char **nodenames;
|
|
|
|
|
char *output_filename;
|
|
|
|
|
int dump_subnodes;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
FILE *output_stream;
|
|
|
|
|
|
|
|
|
|
/* Get the stream to print the nodes to. Special case of an output
|
|
|
|
|
filename of "-" means to dump the nodes to stdout. */
|
|
|
|
|
if (strcmp (output_filename, "-") == 0)
|
|
|
|
|
output_stream = stdout;
|
|
|
|
|
else
|
|
|
|
|
output_stream = fopen (output_filename, "w");
|
|
|
|
|
|
|
|
|
|
if (!output_stream)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Could not create output file \"%s\"."), output_filename);
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Done."));
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
initialize_dumping ()
|
|
|
|
|
{
|
|
|
|
|
dumped_already_index = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get and print the node specified by FILENAME and NODENAME to STREAM.
|
|
|
|
|
If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
|
|
|
|
|
in the menu of each node dumped. */
|
|
|
|
|
static void
|
|
|
|
|
dump_node_to_stream (filename, nodename, stream, dump_subnodes)
|
|
|
|
|
char *filename, *nodename;
|
|
|
|
|
FILE *stream;
|
|
|
|
|
int dump_subnodes;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
node = info_get_node (filename, nodename);
|
|
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (info_recent_file_error);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (filename && *nodename != '(')
|
|
|
|
|
info_error
|
|
|
|
|
(CANT_FILE_NODE, filename_non_directory (filename), nodename);
|
|
|
|
|
else
|
|
|
|
|
info_error (CANT_FIND_NODE, nodename);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02: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)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
free (node);
|
|
|
|
|
return;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
dumped_already_slots, 50, char *);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
#if defined (VERBOSE_NODE_DUMPING)
|
|
|
|
|
/* Maybe we should print some information about the node being output. */
|
|
|
|
|
if (node->filename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Writing node \"(%s)%s\"..."),
|
|
|
|
|
filename_non_directory (node->filename), node->nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Writing node \"%s\"..."), node->nodename);
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
menu = info_menu_of_node (node);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (menu)
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02: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
|
|
|
|
|
dump_node_to_file (node, filename, dump_subnodes)
|
|
|
|
|
NODE *node;
|
|
|
|
|
char *filename;
|
|
|
|
|
int dump_subnodes;
|
|
|
|
|
{
|
|
|
|
|
FILE *output_stream;
|
|
|
|
|
char *nodes_filename;
|
|
|
|
|
|
|
|
|
|
/* Get the stream to print this node to. Special case of an output
|
|
|
|
|
filename of "-" means to dump the nodes to stdout. */
|
|
|
|
|
if (strcmp (filename, "-") == 0)
|
|
|
|
|
output_stream = stdout;
|
|
|
|
|
else
|
|
|
|
|
output_stream = fopen (filename, "w");
|
|
|
|
|
|
|
|
|
|
if (!output_stream)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Could not create output file \"%s\"."), filename);
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Done."));
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Pipe the contents of this node through INFO_PRINT_COMMAND"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
print_node (window->node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
|
|
|
|
|
void
|
|
|
|
|
print_node (node)
|
|
|
|
|
NODE *node;
|
|
|
|
|
{
|
|
|
|
|
FILE *printer_pipe;
|
1998-03-22 21:43:15 +01:00
|
|
|
|
char *print_command = getenv ("INFO_PRINT_COMMAND");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!print_command || !*print_command)
|
|
|
|
|
print_command = DEFAULT_INFO_PRINT_COMMAND;
|
|
|
|
|
|
|
|
|
|
printer_pipe = popen (print_command, "w");
|
|
|
|
|
|
|
|
|
|
if (!printer_pipe)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Cannot open pipe to \"%s\"."), print_command);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined (VERBOSE_NODE_DUMPING)
|
|
|
|
|
/* Maybe we should print some information about the node being output. */
|
|
|
|
|
if (node->filename)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Printing node \"(%s)%s\"..."),
|
|
|
|
|
filename_non_directory (node->filename), node->nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Printing node \"%s\"..."), node->nodename);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#endif /* VERBOSE_NODE_DUMPING */
|
|
|
|
|
|
|
|
|
|
write_node_to_stream (node, printer_pipe);
|
|
|
|
|
pclose (printer_pipe);
|
|
|
|
|
|
|
|
|
|
#if defined (VERBOSE_NODE_DUMPING)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Done."));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#endif /* VERBOSE_NODE_DUMPING */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
write_node_to_stream (node, stream)
|
|
|
|
|
NODE *node;
|
|
|
|
|
FILE *stream;
|
|
|
|
|
{
|
|
|
|
|
fwrite (node->contents, 1, node->nodelen, stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Info Searching Commands */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02: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;
|
|
|
|
|
|
|
|
|
|
static void info_gc_file_buffers ();
|
|
|
|
|
|
|
|
|
|
static char *search_string = (char *)NULL;
|
|
|
|
|
static int search_string_index = 0;
|
|
|
|
|
static int search_string_size = 0;
|
|
|
|
|
static int isearch_is_active = 0;
|
|
|
|
|
|
|
|
|
|
/* Return the file buffer which belongs to WINDOW's node. */
|
|
|
|
|
FILE_BUFFER *
|
|
|
|
|
file_buffer_of_window (window)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
{
|
|
|
|
|
/* If this window has no node, then it has no file buffer. */
|
|
|
|
|
if (!window->node)
|
|
|
|
|
return ((FILE_BUFFER *)NULL);
|
|
|
|
|
|
|
|
|
|
if (window->node->parent)
|
|
|
|
|
return (info_find_file (window->node->parent));
|
|
|
|
|
|
|
|
|
|
if (window->node->filename)
|
|
|
|
|
return (info_find_file (window->node->filename));
|
|
|
|
|
|
|
|
|
|
return ((FILE_BUFFER *)NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search for STRING in NODE starting at START. Return -1 if the string
|
|
|
|
|
was not found, or the location of the string if it was. If WINDOW is
|
|
|
|
|
passed as non-null, set the window's node to be NODE, its point to be
|
|
|
|
|
the found string, and readjust the window's pagetop. Final argument
|
|
|
|
|
DIR says which direction to search in. If it is positive, search
|
|
|
|
|
forward, else backwards. */
|
|
|
|
|
long
|
|
|
|
|
info_search_in_node (string, node, start, window, dir)
|
|
|
|
|
char *string;
|
|
|
|
|
NODE *node;
|
|
|
|
|
long start;
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int dir;
|
|
|
|
|
{
|
|
|
|
|
SEARCH_BINDING binding;
|
|
|
|
|
long offset;
|
|
|
|
|
|
|
|
|
|
binding.buffer = node->contents;
|
|
|
|
|
binding.start = start;
|
|
|
|
|
binding.end = node->nodelen;
|
|
|
|
|
binding.flags = S_FoldCase;
|
|
|
|
|
|
|
|
|
|
if (dir < 0)
|
|
|
|
|
{
|
|
|
|
|
binding.end = 0;
|
|
|
|
|
binding.flags |= S_SkipDest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (binding.start < 0)
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
/* For incremental searches, we always wish to skip past the string. */
|
|
|
|
|
if (isearch_is_active)
|
|
|
|
|
binding.flags |= S_SkipDest;
|
|
|
|
|
|
|
|
|
|
offset = search (string, &binding);
|
|
|
|
|
|
|
|
|
|
if (offset != -1 && window)
|
|
|
|
|
{
|
|
|
|
|
set_remembered_pagetop_and_point (window);
|
|
|
|
|
if (window->node != node)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_set_node_of_window (window, node);
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
info_target_search_node (node, string, start)
|
|
|
|
|
NODE *node;
|
|
|
|
|
char *string;
|
|
|
|
|
long start;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
long offset;
|
|
|
|
|
char *target;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
target = xstrdup (string);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
i = strlen (target);
|
|
|
|
|
|
|
|
|
|
/* Try repeatedly searching for this string while removing words from
|
|
|
|
|
the end of it. */
|
|
|
|
|
while (i)
|
|
|
|
|
{
|
|
|
|
|
target[i] = '\0';
|
|
|
|
|
offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1);
|
|
|
|
|
|
|
|
|
|
if (offset != -1)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
break;
|
1997-08-22 00:57:35 +02: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 char *last_searched_for_string = (char *)NULL;
|
|
|
|
|
static int
|
|
|
|
|
info_search_internal (string, window, dir)
|
|
|
|
|
char *string;
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int dir;
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
if ((info_last_executed_command == info_search) &&
|
|
|
|
|
(last_searched_for_string) &&
|
|
|
|
|
(strcmp (last_searched_for_string, string) == 0))
|
|
|
|
|
{
|
|
|
|
|
ret = info_search_in_node
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(string, window->node, window->point + dir, window, dir);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret = info_search_in_node
|
1998-03-22 21:43:15 +01:00
|
|
|
|
(string, window->node, window->point, window, dir);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybe_free (last_searched_for_string);
|
1998-03-22 21:43:15 +01:00
|
|
|
|
last_searched_for_string = xstrdup (string);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (ret != -1)
|
|
|
|
|
{
|
|
|
|
|
/* We won! */
|
|
|
|
|
if (!echo_area_is_active && !isearch_is_active)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
window_clear_echo_area ();
|
1997-08-22 00:57:35 +02: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)
|
|
|
|
|
{
|
|
|
|
|
register int current_tag, number_of_tags;
|
|
|
|
|
char *last_subfile;
|
|
|
|
|
TAG *tag;
|
|
|
|
|
|
|
|
|
|
/* Find number of tags and current tag. */
|
|
|
|
|
last_subfile = (char *)NULL;
|
|
|
|
|
for (i = 0; file_buffer->tags[i]; i++)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
|
|
|
|
|
{
|
|
|
|
|
current_tag = i;
|
|
|
|
|
last_subfile = file_buffer->tags[i]->filename;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
number_of_tags = i;
|
|
|
|
|
|
|
|
|
|
/* If there is no last_subfile, our tag wasn't found. */
|
|
|
|
|
if (!last_subfile)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return (-1);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Search through subsequent nodes, wrapping around to the top
|
1998-03-22 21:43:15 +01:00
|
|
|
|
of the info file until we find the string or return to this
|
|
|
|
|
window's node and point. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
while (1)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
NODE *node;
|
|
|
|
|
|
|
|
|
|
/* Allow C-g to quit the search, failing it if pressed. */
|
|
|
|
|
return_if_control_g (-1);
|
|
|
|
|
|
|
|
|
|
current_tag += dir;
|
|
|
|
|
|
|
|
|
|
if (current_tag < 0)
|
|
|
|
|
current_tag = number_of_tags - 1;
|
|
|
|
|
else if (current_tag == number_of_tags)
|
|
|
|
|
current_tag = 0;
|
|
|
|
|
|
|
|
|
|
tag = file_buffer->tags[current_tag];
|
|
|
|
|
|
|
|
|
|
if (!echo_area_is_active && (last_subfile != tag->filename))
|
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area
|
|
|
|
|
(_("Searching subfile \"%s\"..."),
|
|
|
|
|
filename_non_directory (tag->filename));
|
|
|
|
|
|
|
|
|
|
last_subfile = tag->filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node = info_get_node (file_buffer->filename, tag->nodename);
|
|
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
|
{
|
|
|
|
|
/* If not doing i-search... */
|
|
|
|
|
if (!echo_area_is_active)
|
|
|
|
|
{
|
|
|
|
|
if (info_recent_file_error)
|
|
|
|
|
info_error (info_recent_file_error);
|
|
|
|
|
else
|
|
|
|
|
info_error (CANT_FILE_NODE,
|
|
|
|
|
filename_non_directory (file_buffer->filename),
|
|
|
|
|
tag->nodename);
|
|
|
|
|
}
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dir < 0)
|
|
|
|
|
start = tag->nodelen;
|
|
|
|
|
|
|
|
|
|
ret =
|
|
|
|
|
info_search_in_node (string, node, start, window, dir);
|
|
|
|
|
|
|
|
|
|
/* 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-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
char *line, *prompt;
|
|
|
|
|
int result, old_pagetop;
|
|
|
|
|
int direction;
|
|
|
|
|
|
|
|
|
|
if (count < 0)
|
|
|
|
|
direction = -1;
|
|
|
|
|
else
|
|
|
|
|
direction = 1;
|
|
|
|
|
|
|
|
|
|
/* 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';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prompt = (char *)xmalloc (50 + strlen (search_string));
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (prompt, _("%s for string [%s]: "),
|
|
|
|
|
direction < 0 ? _("Search backward") : _("Search"),
|
|
|
|
|
search_string);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
line = info_read_in_echo_area (window, prompt);
|
|
|
|
|
free (prompt);
|
|
|
|
|
|
|
|
|
|
if (!line)
|
|
|
|
|
{
|
|
|
|
|
info_abort_key ();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*line)
|
|
|
|
|
{
|
|
|
|
|
if (strlen (line) + 1 > search_string_size)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
search_string = (char *)
|
|
|
|
|
xrealloc (search_string, (search_string_size += 50 + strlen (line)));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
strcpy (search_string, line);
|
|
|
|
|
search_string_index = strlen (line);
|
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
old_pagetop = active_window->pagetop;
|
|
|
|
|
result = info_search_internal (search_string, active_window, direction);
|
|
|
|
|
|
|
|
|
|
if (result != 0 && !info_error_was_printed)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Search failed."));
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_get_or_remove_footnotes (active_window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perhaps free the unreferenced file buffers that were searched, but
|
|
|
|
|
not retained. */
|
|
|
|
|
info_gc_file_buffers ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Incremental Searching */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
static void incremental_search ();
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (isearch_forward,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Search interactively for a string as you type it"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
incremental_search (window, count, key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (isearch_backward,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Search interactively for a string as you type it"))
|
1997-08-22 00:57:35 +02: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;
|
|
|
|
|
|
|
|
|
|
/* Structure defining the current state of an incremental search. */
|
|
|
|
|
typedef struct {
|
1998-03-22 21:43:15 +01:00
|
|
|
|
WINDOW_STATE_DECL; /* The node, pagetop and point. */
|
|
|
|
|
int search_index; /* Offset of the last char in the search string. */
|
|
|
|
|
int direction; /* The direction that this search is heading in. */
|
|
|
|
|
int failing; /* Whether or not this search failed. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
} SEARCH_STATE;
|
|
|
|
|
|
|
|
|
|
/* Array of search states. */
|
|
|
|
|
static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
|
|
|
|
|
static int isearch_states_index = 0;
|
|
|
|
|
static int isearch_states_slots = 0;
|
|
|
|
|
|
|
|
|
|
/* Push the state of this search. */
|
|
|
|
|
static void
|
|
|
|
|
push_isearch (window, search_index, direction, failing)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int search_index, direction, failing;
|
|
|
|
|
{
|
|
|
|
|
SEARCH_STATE *state;
|
|
|
|
|
|
|
|
|
|
state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
|
|
|
|
|
window_get_state (window, state);
|
|
|
|
|
state->search_index = search_index;
|
|
|
|
|
state->direction = direction;
|
|
|
|
|
state->failing = failing;
|
|
|
|
|
|
|
|
|
|
add_pointer_to_array (state, isearch_states_index, isearch_states,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
isearch_states_slots, 20, SEARCH_STATE *);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
|
|
|
|
|
static void
|
|
|
|
|
pop_isearch (window, search_index, direction, failing)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int *search_index, *direction, *failing;
|
|
|
|
|
{
|
|
|
|
|
SEARCH_STATE *state;
|
|
|
|
|
|
|
|
|
|
if (isearch_states_index)
|
|
|
|
|
{
|
|
|
|
|
isearch_states_index--;
|
|
|
|
|
state = isearch_states[isearch_states_index];
|
|
|
|
|
window_set_state (window, state);
|
|
|
|
|
*search_index = state->search_index;
|
|
|
|
|
*direction = state->direction;
|
|
|
|
|
*failing = state->failing;
|
|
|
|
|
|
|
|
|
|
free (state);
|
|
|
|
|
isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Free the memory used by isearch_states. */
|
|
|
|
|
static void
|
|
|
|
|
free_isearch_states ()
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < isearch_states_index; i++)
|
|
|
|
|
{
|
|
|
|
|
free (isearch_states[i]);
|
|
|
|
|
isearch_states[i] = (SEARCH_STATE *)NULL;
|
|
|
|
|
}
|
|
|
|
|
isearch_states_index = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Display the current search in the echo area. */
|
|
|
|
|
static void
|
|
|
|
|
show_isearch_prompt (dir, string, failing_p)
|
|
|
|
|
int dir;
|
|
|
|
|
unsigned char *string;
|
|
|
|
|
int failing_p;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
char *prefix, *prompt, *p_rep;
|
|
|
|
|
int prompt_len, p_rep_index, p_rep_size;
|
|
|
|
|
|
|
|
|
|
if (dir < 0)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
prefix = _("I-search backward: ");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
prefix = _("I-search: ");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
p_rep_index = p_rep_size = 0;
|
|
|
|
|
p_rep = (char *)NULL;
|
|
|
|
|
for (i = 0; string[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
switch (string[i])
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
case ' ': rep = " "; break;
|
|
|
|
|
case LFD: rep = "\\n"; break;
|
|
|
|
|
case TAB: rep = "\\t"; break;
|
|
|
|
|
default:
|
|
|
|
|
rep = pretty_keyname (string[i]);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
strcpy (p_rep + p_rep_index, rep);
|
|
|
|
|
p_rep_index += strlen (rep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prompt_len = strlen (prefix) + p_rep_index + 20;
|
|
|
|
|
prompt = (char *)xmalloc (prompt_len);
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
|
|
|
|
|
p_rep ? p_rep : "");
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
window_message_in_echo_area ("%s", prompt);
|
|
|
|
|
maybe_free (p_rep);
|
|
|
|
|
free (prompt);
|
|
|
|
|
display_cursor_at_point (active_window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
incremental_search (window, count, ignore)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int count;
|
|
|
|
|
unsigned char ignore;
|
|
|
|
|
{
|
|
|
|
|
unsigned char key;
|
|
|
|
|
int last_search_result, search_result, dir;
|
|
|
|
|
SEARCH_STATE mystate, orig_state;
|
|
|
|
|
|
|
|
|
|
if (count < 0)
|
|
|
|
|
dir = -1;
|
|
|
|
|
else
|
|
|
|
|
dir = 1;
|
|
|
|
|
|
|
|
|
|
last_search_result = search_result = 0;
|
|
|
|
|
|
|
|
|
|
window_get_state (window, &orig_state);
|
|
|
|
|
|
|
|
|
|
isearch_string_index = 0;
|
|
|
|
|
if (!isearch_string_size)
|
|
|
|
|
isearch_string = (char *)xmalloc (isearch_string_size = 50);
|
|
|
|
|
|
|
|
|
|
/* Show the search string in the echo area. */
|
|
|
|
|
isearch_string[isearch_string_index] = '\0';
|
|
|
|
|
show_isearch_prompt (dir, isearch_string, search_result);
|
|
|
|
|
|
|
|
|
|
isearch_is_active = 1;
|
|
|
|
|
|
|
|
|
|
while (isearch_is_active)
|
|
|
|
|
{
|
|
|
|
|
VFunction *func = (VFunction *)NULL;
|
|
|
|
|
int quoted = 0;
|
|
|
|
|
|
|
|
|
|
/* If a recent display was interrupted, then do the redisplay now if
|
1998-03-22 21:43:15 +01:00
|
|
|
|
it is convenient. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (!info_any_buffered_input_p () && display_was_interrupted_p)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
display_update_one_window (window);
|
|
|
|
|
display_cursor_at_point (active_window);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Read a character and dispatch on it. */
|
|
|
|
|
key = info_get_input_char ();
|
|
|
|
|
window_get_state (window, &mystate);
|
|
|
|
|
|
|
|
|
|
if (key == DEL)
|
1998-03-22 21:43:15 +01: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';
|
|
|
|
|
show_isearch_prompt (dir, isearch_string, search_result);
|
|
|
|
|
goto after_search;
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else if (key == Control ('q'))
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
key = info_get_input_char ();
|
|
|
|
|
quoted = 1;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* We are about to search again, or quit. Save the current search. */
|
|
|
|
|
push_isearch (window, isearch_string_index, dir, search_result);
|
|
|
|
|
|
|
|
|
|
if (quoted)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
goto insert_and_search;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (!Meta_p (key) || (ISO_Latin_p && key < 160))
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
func = window->keymap[key].function;
|
|
|
|
|
|
|
|
|
|
/* If this key invokes an incremental search, then this means that
|
|
|
|
|
we will either search again in the same direction, search
|
|
|
|
|
again in the reverse direction, or insert the last search
|
|
|
|
|
string that was accepted through incremental searching. */
|
|
|
|
|
if (func == isearch_forward || func == isearch_backward)
|
|
|
|
|
{
|
|
|
|
|
if ((func == isearch_forward && dir > 0) ||
|
|
|
|
|
(func == isearch_backward && dir < 0))
|
|
|
|
|
{
|
|
|
|
|
/* If the user has typed no characters, then insert the
|
|
|
|
|
last successful search into the current search string. */
|
|
|
|
|
if (isearch_string_index == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Of course, there must be something to insert. */
|
|
|
|
|
if (last_isearch_accepted)
|
|
|
|
|
{
|
|
|
|
|
if (strlen (last_isearch_accepted) + 1 >=
|
|
|
|
|
isearch_string_size)
|
|
|
|
|
isearch_string = (char *)
|
|
|
|
|
xrealloc (isearch_string,
|
|
|
|
|
isearch_string_size += 10 +
|
|
|
|
|
strlen (last_isearch_accepted));
|
|
|
|
|
strcpy (isearch_string, last_isearch_accepted);
|
|
|
|
|
isearch_string_index = strlen (isearch_string);
|
|
|
|
|
goto search_now;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Search again in the same direction. This means start
|
|
|
|
|
from a new place if the last search was successful. */
|
|
|
|
|
if (search_result == 0)
|
|
|
|
|
window->point += dir;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Reverse the direction of the search. */
|
|
|
|
|
dir = -dir;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (isprint (key) || func == (VFunction *)NULL)
|
|
|
|
|
{
|
|
|
|
|
insert_and_search:
|
|
|
|
|
|
|
|
|
|
if (isearch_string_index + 2 >= isearch_string_size)
|
|
|
|
|
isearch_string = (char *)xrealloc
|
|
|
|
|
(isearch_string, isearch_string_size += 100);
|
|
|
|
|
|
|
|
|
|
isearch_string[isearch_string_index++] = key;
|
|
|
|
|
isearch_string[isearch_string_index] = '\0';
|
|
|
|
|
goto search_now;
|
|
|
|
|
}
|
|
|
|
|
else if (func == info_abort_key)
|
|
|
|
|
{
|
|
|
|
|
/* If C-g pressed, and the search is failing, pop the search
|
|
|
|
|
stack back to the last unfailed search. */
|
|
|
|
|
if (isearch_states_index && (search_result != 0))
|
|
|
|
|
{
|
|
|
|
|
terminal_ring_bell ();
|
|
|
|
|
while (isearch_states_index && (search_result != 0))
|
|
|
|
|
pop_isearch
|
|
|
|
|
(window, &isearch_string_index, &dir, &search_result);
|
|
|
|
|
isearch_string[isearch_string_index] = '\0';
|
|
|
|
|
show_isearch_prompt (dir, isearch_string, search_result);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto exit_search;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto exit_search;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01: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. */
|
|
|
|
|
if (isearch_string_index && func != info_abort_key)
|
|
|
|
|
{
|
|
|
|
|
maybe_free (last_isearch_accepted);
|
|
|
|
|
last_isearch_accepted = xstrdup (isearch_string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key != isearch_terminate_search_key)
|
|
|
|
|
info_set_pending_input (key);
|
|
|
|
|
|
|
|
|
|
if (func == info_abort_key)
|
|
|
|
|
{
|
|
|
|
|
if (isearch_states_index)
|
|
|
|
|
window_set_state (window, &orig_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!echo_area_is_active)
|
|
|
|
|
window_clear_echo_area ();
|
|
|
|
|
|
|
|
|
|
if (auto_footnotes_p)
|
|
|
|
|
info_get_or_remove_footnotes (active_window);
|
|
|
|
|
|
|
|
|
|
isearch_is_active = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Search for the contents of isearch_string. */
|
|
|
|
|
search_now:
|
|
|
|
|
show_isearch_prompt (dir, isearch_string, search_result);
|
|
|
|
|
|
|
|
|
|
if (search_result == 0)
|
1998-03-22 21:43:15 +01: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) &&
|
|
|
|
|
(strncasecmp (window->node->contents + window->point,
|
|
|
|
|
isearch_string, isearch_string_index) == 0)) ||
|
|
|
|
|
((dir > 0) &&
|
|
|
|
|
((window->point - isearch_string_index) >= 0) &&
|
|
|
|
|
(strncasecmp (window->node->contents +
|
|
|
|
|
(window->point - (isearch_string_index - 1)),
|
|
|
|
|
isearch_string, isearch_string_index) == 0)))
|
|
|
|
|
{
|
|
|
|
|
if (dir > 0)
|
|
|
|
|
window->point++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
search_result = info_search_internal (isearch_string, window, dir);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If this search failed, and we didn't already have a failed search,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
then ring the terminal bell. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if (search_result != 0 && last_search_result == 0)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
terminal_ring_bell ();
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
after_search:
|
|
|
|
|
show_isearch_prompt (dir, isearch_string, search_result);
|
|
|
|
|
|
|
|
|
|
if (search_result == 0)
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02: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
|
|
|
|
|
info_gc_file_buffers ()
|
|
|
|
|
{
|
|
|
|
|
register int fb_index, iw_index, i;
|
|
|
|
|
register FILE_BUFFER *fb;
|
|
|
|
|
register INFO_WINDOW *iw;
|
|
|
|
|
|
|
|
|
|
if (!info_loaded_files)
|
|
|
|
|
return;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
int fb_referenced_p = 0;
|
|
|
|
|
|
|
|
|
|
/* If already gc-ed, do nothing. */
|
|
|
|
|
if (!fb->contents)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
continue;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If this file had to be uncompressed, check to see if we should
|
1998-03-22 21:43:15 +01:00
|
|
|
|
gc it. This means that the user-variable "gc-compressed-files"
|
|
|
|
|
is non-zero. */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
continue;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If this file's contents are not gc-able, move on. */
|
|
|
|
|
if (fb->flags & N_CannotGC)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
continue;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* Check each INFO_WINDOW to see if it has any nodes which reference
|
1998-03-22 21:43:15 +01:00
|
|
|
|
this file. */
|
|
|
|
|
for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; iw->nodes && iw->nodes[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
if ((strcmp (fb->fullpath, iw->nodes[i]->filename) == 0) ||
|
|
|
|
|
(strcmp (fb->filename, iw->nodes[i]->filename) == 0))
|
|
|
|
|
{
|
|
|
|
|
fb_referenced_p = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
/* If this file buffer wasn't referenced, free its contents. */
|
|
|
|
|
if (!fb_referenced_p)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
free (fb->contents);
|
|
|
|
|
fb->contents = (char *)NULL;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Traversing and Selecting References */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Move to the next or previous cross reference in this node. */
|
|
|
|
|
static void
|
|
|
|
|
info_move_to_xref (window, count, key, dir)
|
|
|
|
|
WINDOW *window;
|
|
|
|
|
int count;
|
|
|
|
|
unsigned char key;
|
|
|
|
|
int dir;
|
|
|
|
|
{
|
|
|
|
|
long firstmenu, firstxref;
|
|
|
|
|
long nextmenu, nextxref;
|
|
|
|
|
long placement = -1;
|
|
|
|
|
long start = 0;
|
|
|
|
|
NODE *node = window->node;
|
|
|
|
|
|
|
|
|
|
if (dir < 0)
|
|
|
|
|
start = node->nodelen;
|
|
|
|
|
|
|
|
|
|
/* This search is only allowed to fail if there is no menu or cross
|
|
|
|
|
reference in the current node. Otherwise, the first menu or xref
|
|
|
|
|
found is moved to. */
|
|
|
|
|
|
|
|
|
|
firstmenu = info_search_in_node
|
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir);
|
|
|
|
|
|
|
|
|
|
/* 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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
firstmenu = info_search_in_node
|
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
firstxref =
|
|
|
|
|
info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir);
|
|
|
|
|
|
|
|
|
|
#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)
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("No cross references in this node."));
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir);
|
|
|
|
|
|
|
|
|
|
nextxref = info_search_in_node
|
|
|
|
|
(INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir);
|
|
|
|
|
|
|
|
|
|
#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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
nextmenu = info_search_in_node
|
|
|
|
|
(INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir);
|
1997-08-22 00:57:35 +02: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)) ||
|
1998-03-22 21:43:15 +01:00
|
|
|
|
((dir == -1) && (nextmenu > nextxref)))
|
|
|
|
|
placement = nextmenu + 1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
placement = nextxref;
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (((dir == 1) && (firstmenu < firstxref)) ||
|
|
|
|
|
((dir == -1) && (firstmenu > firstxref)))
|
|
|
|
|
placement = firstmenu + 1;
|
|
|
|
|
else
|
|
|
|
|
placement = firstxref;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else if (firstmenu != -1)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
placement = firstmenu + 1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
placement = firstxref;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
window->point = placement;
|
|
|
|
|
window_adjust_pagetop (window);
|
|
|
|
|
window->flags |= W_UpdateWindow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (info_move_to_prev_xref,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Move to the previous cross reference"))
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Move to the next cross reference"))
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Select reference or menu item appearing on this line"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
NODE *orig;
|
|
|
|
|
|
|
|
|
|
line = window->line_starts[window_line_of_point (window)];
|
|
|
|
|
orig = window->node;
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Miscellaneous Info Commands */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* What to do when C-g is pressed in a window. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
|
1997-08-22 00:57:35 +02: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 ();
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Quit"));
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Move to the cursor to a specific line of the window"))
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
line = (window->height + count) + window->pagetop;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
line = window->pagetop + count;
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
|
1997-08-22 00:57:35 +02: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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_line = window->height + count;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_line = count;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (desired_line < 0)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_line = 0;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (desired_line >= window->height)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
desired_line = window->height - 1;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (desired_line == point_line)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return;
|
1997-08-22 00:57:35 +02: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 (). */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Reading Keys and Dispatching on Them */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Declaration only. Special cased in info_dispatch_on_key (). */
|
|
|
|
|
DECLARE_INFO_COMMAND (info_do_lowercase_version, "")
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
dispatch_error (keyseq)
|
|
|
|
|
char *keyseq;
|
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
rep = pretty_keyseq (keyseq);
|
|
|
|
|
|
|
|
|
|
if (!echo_area_is_active)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
info_error (_("Unknown command (%s)."), rep);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *temp;
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
temp = (char *)xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid")));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
sprintf (temp, _("\"%s\" is invalid"), rep);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
terminal_ring_bell ();
|
|
|
|
|
inform_in_echo_area (temp);
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Keeping track of key sequences. */
|
|
|
|
|
static char *info_keyseq = (char *)NULL;
|
|
|
|
|
static char keyseq_rep[100];
|
|
|
|
|
static int info_keyseq_index = 0;
|
|
|
|
|
static int info_keyseq_size = 0;
|
|
|
|
|
static int info_keyseq_displayed_p = 0;
|
|
|
|
|
|
|
|
|
|
/* Initialize the length of the current key sequence. */
|
|
|
|
|
void
|
|
|
|
|
initialize_keyseq ()
|
|
|
|
|
{
|
|
|
|
|
info_keyseq_index = 0;
|
|
|
|
|
info_keyseq_displayed_p = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add CHARACTER to the current key sequence. */
|
|
|
|
|
void
|
|
|
|
|
add_char_to_keyseq (character)
|
|
|
|
|
char character;
|
|
|
|
|
{
|
|
|
|
|
if (info_keyseq_index + 2 >= info_keyseq_size)
|
|
|
|
|
info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
|
|
|
|
|
|
|
|
|
|
info_keyseq[info_keyseq_index++] = character;
|
|
|
|
|
info_keyseq[info_keyseq_index] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the pretty printable string which represents KEYSEQ. */
|
|
|
|
|
char *
|
|
|
|
|
pretty_keyseq (keyseq)
|
|
|
|
|
char *keyseq;
|
|
|
|
|
{
|
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
|
|
keyseq_rep[0] = '\0';
|
|
|
|
|
|
|
|
|
|
for (i = 0; keyseq[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
sprintf (keyseq_rep + strlen (keyseq_rep), "%s%s",
|
1998-03-22 21:43:15 +01:00
|
|
|
|
strlen (keyseq_rep) ? " " : "",
|
|
|
|
|
pretty_keyname (keyseq[i]));
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (keyseq_rep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
void
|
|
|
|
|
display_info_keyseq (expecting_future_input)
|
|
|
|
|
int expecting_future_input;
|
|
|
|
|
{
|
|
|
|
|
char *rep;
|
|
|
|
|
|
|
|
|
|
rep = pretty_keyseq (info_keyseq);
|
|
|
|
|
if (expecting_future_input)
|
|
|
|
|
strcat (rep, "-");
|
|
|
|
|
|
|
|
|
|
if (echo_area_is_active)
|
|
|
|
|
inform_in_echo_area (rep);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
window_message_in_echo_area (rep);
|
|
|
|
|
display_cursor_at_point (active_window);
|
|
|
|
|
}
|
|
|
|
|
info_keyseq_displayed_p = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called by interactive commands to read a keystroke. */
|
|
|
|
|
unsigned char
|
|
|
|
|
info_get_another_input_char ()
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
|
1997-08-22 00:57:35 +02: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. */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (!info_keyseq_displayed_p)
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
ready = 1;
|
|
|
|
|
if (!info_any_buffered_input_p () &&
|
|
|
|
|
!info_input_pending_p ())
|
|
|
|
|
{
|
1997-08-22 00:57:35 +02:00
|
|
|
|
#if defined (FD_SET)
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
#endif /* FD_SET */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
}
|
1997-08-22 00:57:35 +02: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
|
|
|
|
|
info_dispatch_on_key (key, map)
|
|
|
|
|
unsigned char key;
|
|
|
|
|
Keymap map;
|
|
|
|
|
{
|
|
|
|
|
if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
|
|
|
|
|
{
|
|
|
|
|
if (map[ESC].type == ISKMAP)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
map = (Keymap)map[ESC].function;
|
|
|
|
|
add_char_to_keyseq (ESC);
|
|
|
|
|
key = UnMeta (key);
|
|
|
|
|
info_dispatch_on_key (key, map);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
dispatch_error (info_keyseq);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (map[key].type)
|
|
|
|
|
{
|
|
|
|
|
case ISFUNC:
|
|
|
|
|
{
|
1998-03-22 21:43:15 +01:00
|
|
|
|
VFunction *func;
|
|
|
|
|
|
|
|
|
|
func = map[key].function;
|
|
|
|
|
if (func != (VFunction *)NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Special case info_do_lowercase_version (). */
|
|
|
|
|
if (func == info_do_lowercase_version)
|
|
|
|
|
{
|
|
|
|
|
info_dispatch_on_key (tolower (key), map);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
add_char_to_keyseq (key);
|
|
|
|
|
|
|
|
|
|
if (info_keyseq_displayed_p)
|
|
|
|
|
display_info_keyseq (0);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
WINDOW *where;
|
|
|
|
|
|
|
|
|
|
where = active_window;
|
|
|
|
|
(*map[key].function)
|
|
|
|
|
(active_window, info_numeric_arg * info_numeric_arg_sign, key);
|
|
|
|
|
|
|
|
|
|
/* If we have input pending, then the last command was a prefix
|
|
|
|
|
command. Don't change the value of the last function vars.
|
|
|
|
|
Otherwise, remember the last command executed in the var
|
|
|
|
|
appropriate to the window in which it was executed. */
|
|
|
|
|
if (!info_input_pending_p ())
|
|
|
|
|
{
|
|
|
|
|
if (where == the_echo_area)
|
|
|
|
|
ea_last_executed_command = map[key].function;
|
|
|
|
|
else
|
|
|
|
|
info_last_executed_command = map[key].function;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
add_char_to_keyseq (key);
|
|
|
|
|
dispatch_error (info_keyseq);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ISKMAP:
|
|
|
|
|
add_char_to_keyseq (key);
|
|
|
|
|
if (map[key].function != (VFunction *)NULL)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char newkey;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
newkey = info_get_another_input_char ();
|
|
|
|
|
info_dispatch_on_key (newkey, (Keymap)map[key].function);
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
dispatch_error (info_keyseq);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Numeric Arguments */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Add this digit to the current numeric argument"))
|
1997-08-22 00:57:35 +02: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,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Start (or multiply by 4) the current numeric argument"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
info_numeric_arg *= 4;
|
|
|
|
|
info_numeric_arg_digit_loop (window, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a default argument. */
|
|
|
|
|
void
|
|
|
|
|
info_initialize_numeric_arg ()
|
|
|
|
|
{
|
|
|
|
|
info_numeric_arg = info_numeric_arg_sign = 1;
|
|
|
|
|
info_explicit_arg = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
|
1998-03-22 21:43:15 +01:00
|
|
|
|
_("Internally used by \\[universal-argument]"))
|
1997-08-22 00:57:35 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned char pure_key;
|
|
|
|
|
Keymap keymap = window->keymap;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
if (key)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
pure_key = key;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (display_was_interrupted_p && !info_any_buffered_input_p ())
|
|
|
|
|
display_update_display (windows);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (active_window != the_echo_area)
|
|
|
|
|
display_cursor_at_point (active_window);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
pure_key = key = info_get_another_input_char ();
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
if (Meta_p (key))
|
|
|
|
|
add_char_to_keyseq (ESC);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
add_char_to_keyseq (UnMeta (key));
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (Meta_p (key))
|
1998-03-22 21:43:15 +01:00
|
|
|
|
key = UnMeta (key);
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (keymap[key].type == ISFUNC &&
|
1998-03-22 21:43:15 +01:00
|
|
|
|
keymap[key].function == info_universal_argument)
|
|
|
|
|
{
|
|
|
|
|
info_numeric_arg *= 4;
|
|
|
|
|
key = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
if (isdigit (key))
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
else
|
1998-03-22 21:43:15 +01: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-08-22 00:57:35 +02:00
|
|
|
|
key = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* **************************************************************** */
|
1998-03-22 21:43:15 +01:00
|
|
|
|
/* */
|
|
|
|
|
/* Input Character Buffering */
|
|
|
|
|
/* */
|
1997-08-22 00:57:35 +02:00
|
|
|
|
/* **************************************************************** */
|
|
|
|
|
|
|
|
|
|
/* Character waiting to be read next. */
|
|
|
|
|
static int pending_input_character = 0;
|
|
|
|
|
|
|
|
|
|
/* How to make there be no pending input. */
|
|
|
|
|
static void
|
|
|
|
|
info_clear_pending_input ()
|
|
|
|
|
{
|
|
|
|
|
pending_input_character = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to set the pending input character. */
|
|
|
|
|
static void
|
|
|
|
|
info_set_pending_input (key)
|
|
|
|
|
unsigned char key;
|
|
|
|
|
{
|
|
|
|
|
pending_input_character = key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to see if there is any pending input. */
|
|
|
|
|
unsigned char
|
|
|
|
|
info_input_pending_p ()
|
|
|
|
|
{
|
|
|
|
|
return (pending_input_character);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Largest number of characters that we can read in advance. */
|
|
|
|
|
#define MAX_INFO_INPUT_BUFFERING 512
|
|
|
|
|
|
|
|
|
|
static int pop_index = 0, push_index = 0;
|
|
|
|
|
static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
|
|
|
|
|
|
|
|
|
|
/* Add KEY to the buffer of characters to be read. */
|
|
|
|
|
static void
|
|
|
|
|
info_push_typeahead (key)
|
|
|
|
|
unsigned char key;
|
|
|
|
|
{
|
|
|
|
|
/* Flush all pending input in the case of C-g pressed. */
|
|
|
|
|
if (key == Control ('g'))
|
|
|
|
|
{
|
|
|
|
|
push_index = pop_index;
|
|
|
|
|
info_set_pending_input (Control ('g'));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info_input_buffer[push_index++] = key;
|
|
|
|
|
if (push_index >= sizeof (info_input_buffer))
|
1998-03-22 21:43:15 +01:00
|
|
|
|
push_index = 0;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
|
|
|
|
|
static int
|
|
|
|
|
info_input_buffer_space_available ()
|
|
|
|
|
{
|
|
|
|
|
if (pop_index > push_index)
|
|
|
|
|
return (pop_index - push_index);
|
|
|
|
|
else
|
|
|
|
|
return (sizeof (info_input_buffer) - (push_index - pop_index));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get a key from the buffer of characters to be read.
|
|
|
|
|
Return the key in KEY.
|
|
|
|
|
Result is non-zero if there was a key, or 0 if there wasn't. */
|
|
|
|
|
static int
|
|
|
|
|
info_get_key_from_typeahead (key)
|
|
|
|
|
unsigned char *key;
|
|
|
|
|
{
|
|
|
|
|
if (push_index == pop_index)
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
*key = info_input_buffer[pop_index++];
|
|
|
|
|
|
|
|
|
|
if (pop_index >= sizeof (info_input_buffer))
|
|
|
|
|
pop_index = 0;
|
|
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
info_any_buffered_input_p ()
|
|
|
|
|
{
|
|
|
|
|
info_gather_typeahead ();
|
|
|
|
|
return (push_index != pop_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If characters are available to be read, then read them and stuff them into
|
|
|
|
|
info_input_buffer. Otherwise, do nothing. */
|
|
|
|
|
void
|
|
|
|
|
info_gather_typeahead ()
|
|
|
|
|
{
|
|
|
|
|
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)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
chars_avail = read (tty, &input[0], chars_avail);
|
1997-08-22 00:57:35 +02: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;
|
|
|
|
|
}
|
|
|
|
|
# endif /* O_NDELAY */
|
|
|
|
|
#endif /* !FIONREAD */
|
|
|
|
|
|
|
|
|
|
while (i < chars_avail)
|
|
|
|
|
{
|
|
|
|
|
info_push_typeahead (input[i]);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to read a single character. */
|
|
|
|
|
unsigned char
|
|
|
|
|
info_get_input_char ()
|
|
|
|
|
{
|
|
|
|
|
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;
|
1998-03-22 21:43:15 +01: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. */
|
|
|
|
|
rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
|
|
|
|
|
keystroke = rawkey;
|
|
|
|
|
|
|
|
|
|
if (rawkey == EOF)
|
1998-03-22 21:43:15 +01:00
|
|
|
|
{
|
|
|
|
|
if (info_input_stream != stdin)
|
|
|
|
|
{
|
|
|
|
|
fclose (info_input_stream);
|
|
|
|
|
info_input_stream = stdin;
|
|
|
|
|
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 ();
|
|
|
|
|
exit (0);
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (info_dribble_file)
|
|
|
|
|
dribble (keystroke);
|
|
|
|
|
|
1998-03-22 21:43:15 +01:00
|
|
|
|
return keystroke;
|
1997-08-22 00:57:35 +02:00
|
|
|
|
}
|