#include "info.h"
#include <sys/ioctl.h>
#if defined (HAVE_SYS_TIME_H)
# include <sys/time.h>
# define HAVE_STRUCT_TIMEVAL
#endif
#if defined (HANDLE_MAN_PAGES)
# include "man.h"
#endif
static void info_clear_pending_input (), info_set_pending_input ();
static void info_handle_pointer ();
static FILE *info_input_stream = NULL;
VFunction *info_last_executed_command = NULL;
int quit_info_immediately = 0;
INFO_WINDOW **info_windows = NULL;
static int info_windows_index = 0;
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 ();
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)
break;
if (!window)
{
initialize_info_session (node, 1);
window = active_window;
}
else
{
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);
}
}
}
display_startup_message_and_start ();
}
void
begin_info_session_with_error (initial_node, format, arg)
NODE *initial_node;
char *format;
void *arg;
{
initialize_info_session (initial_node, 1);
info_error (format, arg, (void *)NULL);
info_session ();
}
void
begin_info_session (initial_node)
NODE *initial_node;
{
initialize_info_session (initial_node, 1);
display_startup_message_and_start ();
}
void
display_startup_message_and_start ()
{
char *format;
format = replace_in_documentation
(_("Welcome to Info version %s. \"\\[get-help-window]\" for help, \"\\[menu-item]\" for menu item."));
window_message_in_echo_area (format, version_string ());
info_session ();
}
void
info_session ()
{
display_update_display (windows);
info_last_executed_command = NULL;
info_read_and_dispatch ();
terminal_goto_xy (0, screenheight - 1);
terminal_clear_to_eol ();
fflush (stdout);
terminal_unprep_terminal ();
close_dribble_file ();
}
void
info_read_and_dispatch ()
{
unsigned char key;
int done;
done = 0;
while (!done && !quit_info_immediately)
{
int lk;
if ((info_last_executed_command != info_next_line) &&
(info_last_executed_command != info_prev_line))
active_window->goal_column = -1;
if (echo_area_is_active)
{
lk = echo_area_last_command_was_kill;
echo_area_prep_read ();
}
if (!info_any_buffered_input_p ())
display_update_display (windows);
display_cursor_at_point (active_window);
info_initialize_numeric_arg ();
initialize_keyseq ();
key = info_get_input_char ();
if (!echo_area_is_active)
window_clear_echo_area ();
info_error_was_printed = 0;
info_dispatch_on_key (key, active_window->keymap);
if (echo_area_is_active)
{
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;
}
else if (info_last_executed_command == info_quit)
done = 1;
}
}
extern void initialize_info_signal_handler ();
void
initialize_info_session (node, clear_screen)
NODE *node;
int clear_screen;
{
char *term_name = getenv ("TERM");
terminal_initialize_terminal (term_name);
if (terminal_is_dumb_p)
{
if (!term_name)
term_name = "dumb";
info_error (TERM_TOO_DUMB, term_name);
exit (1);
}
if (clear_screen)
{
terminal_prep_terminal ();
terminal_clear_screen ();
}
initialize_info_keymaps ();
window_initialize_windows (screenwidth, screenheight);
initialize_info_signal_handler ();
display_initialize_display (screenwidth, screenheight);
info_set_node_of_window (active_window, node);
window_deletion_notifier = forget_window_and_nodes;
if (!info_input_stream)
{
setbuf(stdin, NULL);
info_input_stream = stdin;
}
info_windows_initialized_p = 1;
}
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;
}
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);
}
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;
{
INFO_WINDOW *info_win = get_info_window_of_window (window);
if (!info_win)
{
info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
info_win->window = window;
info_win->nodes = (NODE **)NULL;
info_win->pagetops = (int *)NULL;
info_win->points = (long *)NULL;
info_win->current = 0;
info_win->nodes_index = 0;
info_win->nodes_slots = 0;
add_pointer_to_array (info_win, info_windows_index, info_windows,
info_windows_slots, 10, INFO_WINDOW *);
}
if (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;
if (info_win->nodes_index + 2 >= info_win->nodes_slots)
{
info_win->nodes_slots += 20;
info_win->nodes = (NODE **) xrealloc (info_win->nodes,
info_win->nodes_slots * sizeof (NODE *));
info_win->pagetops = (int *) xrealloc (info_win->pagetops,
info_win->nodes_slots * sizeof (int));
info_win->points = (long *) xrealloc (info_win->points,
info_win->nodes_slots * sizeof (long));
}
info_win->nodes[info_win->nodes_index] = node;
info_win->pagetops[info_win->nodes_index] = window->pagetop;
info_win->points[info_win->nodes_index] = window->point;
info_win->current = info_win->nodes_index++;
info_win->nodes[info_win->nodes_index] = NULL;
info_win->pagetops[info_win->nodes_index] = 0;
info_win->points[info_win->nodes_index] = 0;
}
#define DEBUG_FORGET_WINDOW_AND_NODES
#if defined (DEBUG_FORGET_WINDOW_AND_NODES)
static void
consistency_check_info_windows ()
{
register int i;
for (i = 0; i < info_windows_index; i++)
{
WINDOW *win;
for (win = windows; win; win = win->next)
if (win == info_windows[i]->window)
break;
if (!win)
abort ();
}
}
#endif
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 (info_win)
{
while (i < info_windows_index)
{
info_windows[i] = info_windows[i + 1];
i++;
}
info_windows_index--;
info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
if (info_win->nodes)
{
for (i = 0; info_win->nodes[i]; i++)
if (internal_info_node_p (info_win->nodes[i]))
free (info_win->nodes[i]);
free (info_win->nodes);
maybe_free (info_win->pagetops);
maybe_free (info_win->points);
}
free (info_win);
}
#if defined (DEBUG_FORGET_WINDOW_AND_NODES)
consistency_check_info_windows ();
#endif
}
void
info_set_node_of_window (window, node)
WINDOW *window;
NODE *node;
{
window_set_node_of_window (window, node);
remember_window_and_node (window, node);
if (auto_footnotes_p)
info_get_or_remove_footnotes (window);
}
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;
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;
if (old_pagetop < desired_top)
{
int start, end, amount;
amount = desired_top - old_pagetop;
if ((amount >= window->height) ||
(((window->height - amount) * 10) < window->height))
return;
start = amount + window->first_row;
end = window->height + window->first_row;
display_scroll_display (start, end, -amount);
}
else
{
int start, end, amount;
amount = old_pagetop - desired_top;
if ((amount >= window->height) ||
(((window->height - amount) * 10) < window->height))
return;
start = window->first_row;
end = (window->first_row + window->height) - amount;
display_scroll_display (start, end, amount);
}
}
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);
}
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)
return;
goal = window_get_goal_column (window);
window->goal_column = goal;
window->point = window->line_starts[new] - window->node->contents;
window->point += window_chars_to_goal (window->line_starts[new], goal);
info_show_point (window);
}
}
DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
{
int old_line, new_line;
if (count < 0)
info_prev_line (window, -count, key);
else
{
old_line = window_line_of_point (window);
new_line = old_line + count;
move_to_new_line (old_line, new_line, window);
}
}
DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
{
int old_line, new_line;
if (count < 0)
info_next_line (window, -count, key);
else
{
old_line = window_line_of_point (window);
new_line = old_line - count;
move_to_new_line (old_line, new_line, window);
}
}
DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
{
register int point, len;
register char *buffer;
buffer = window->node->contents;
len = window->node->nodelen;
for (point = window->point;
(point < len) && (buffer[point] != '\n');
point++);
if (point != window->point)
{
window->point = point;
info_show_point (window);
}
}
DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
{
register int point;
register char *buffer;
buffer = window->node->contents;
point = window->point;
for (; (point) && (buffer[point - 1] != '\n'); point--);
if (point != window->point)
{
window->point = point;
info_show_point (window);
}
}
DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
{
if (count < 0)
info_backward_char (window, -count, key);
else
{
window->point += count;
if (window->point >= window->node->nodelen)
window->point = window->node->nodelen - 1;
info_show_point (window);
}
}
DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
{
if (count < 0)
info_forward_char (window, -count, key);
else
{
window->point -= count;
if (window->point < 0)
window->point = 0;
info_show_point (window);
}
}
#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
{
long point;
char *buffer;
int end, c;
if (count < 0)
{
info_backward_word (window, -count, key);
return;
}
point = window->point;
buffer = window->node->contents;
end = window->node->nodelen;
while (count)
{
if (point + 1 >= end)
return;
c = buffer[point];
if (!alphabetic (c))
{
while (++point < end)
{
c = buffer[point];
if (alphabetic (c))
break;
}
}
if (point >= end) return;
while (++point < end)
{
c = buffer[point];
if (!alphabetic (c))
break;
}
--count;
}
window->point = point;
info_show_point (window);
}
DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
{
long point;
char *buffer;
int c;
if (count < 0)
{
info_forward_word (window, -count, key);
return;
}
buffer = window->node->contents;
point = window->point;
while (count)
{
if (point == 0)
break;
c = buffer[point - 1];
if (!alphabetic (c))
{
while (--point)
{
c = buffer[point - 1];
if (alphabetic (c))
break;
}
}
while (point)
{
c = buffer[point - 1];
if (!alphabetic (c))
break;
else
--point;
}
--count;
}
window->point = point;
info_show_point (window);
}
static char *counter_names[] = {
"not at all", "once", "twice", "three", "four", "five", "six",
(char *)NULL
};
static char td_buffer[50];
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])
sprintf (td_buffer, "%s%s", counter_names[i], count > 2 ? _(" times") : "");
else
sprintf (td_buffer, _("%d times"), count);
return (td_buffer);
}
int info_scroll_behaviour = IS_Continuous;
char *info_scroll_choices[] = {
"Continuous", "Next Only", "Page Only", (char *)NULL
};
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)
info_error (_("No \"Next\" pointer for this node."));
else
{
window_message_in_echo_area (_("Following \"Next\" node..."));
info_handle_pointer (_("Next"), window);
}
break;
case IS_Continuous:
{
{
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;
}
}
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;
}
{
int up_counter, old_current;
INFO_WINDOW *info_win;
info_win = get_info_window_of_window (window);
old_current = info_win->current;
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 (!info_label_was_found)
continue;
if (!info_parsed_filename)
{
REFERENCE **menu;
char *next_nodename;
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
{
info_free_references (menu);
free (next_nodename);
info_next_label_of_node (window->node);
}
}
window_message_in_echo_area
("Moving \"Up\" %s, then \"Next\".",
times_description (up_counter));
info_handle_pointer (_("Next"), window);
return;
}
else
{
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;
}
}
}
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)
info_error (_("No \"Prev\" for this node."));
else
{
window_message_in_echo_area (_("Moving \"Prev\" in this window."));
info_handle_pointer (_("Prev"), window);
}
break;
case IS_Continuous:
info_prev_label_of_node (window->node);
if (!info_parsed_nodename && !info_parsed_filename)
{
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);
}
}
else
{
REFERENCE **menu;
int inhibit_menu_traversing = 0;
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)
{
free (pnode);
inhibit_menu_traversing = 1;
}
else
{
free (pnode);
info_prev_label_of_node (window->node);
}
}
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');
}
}
}
break;
}
}
DECLARE_INFO_COMMAND (info_global_next_node,
_("Move forwards or down through node structure"))
{
if (count < 0)
info_global_prev_node (window, -count, key);
else
{
while (count && !info_error_was_printed)
{
forward_move_node_structure (window, IS_Continuous);
count--;
}
}
}
DECLARE_INFO_COMMAND (info_global_prev_node,
_("Move backwards or up through node structure"))
{
if (count < 0)
info_global_next_node (window, -count, key);
else
{
while (count && !info_error_was_printed)
{
backward_move_node_structure (window, IS_Continuous);
count--;
}
}
}
DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
{
if (count < 0)
info_scroll_backward (window, -count, key);
else
{
int desired_top;
if (!info_explicit_arg && count == 1)
{
desired_top = window->pagetop + (window->height - 2);
if (desired_top > window->line_count)
{
int behaviour = info_scroll_behaviour;
if (key != SPC && key != DEL)
behaviour = IS_PageOnly;
forward_move_node_structure (window, behaviour);
return;
}
}
else
desired_top = window->pagetop + count;
if (desired_top >= window->line_count)
desired_top = window->line_count - 2;
if (window->pagetop > desired_top)
return;
else
set_window_pagetop (window, desired_top);
}
}
DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
{
if (count < 0)
info_scroll_forward (window, -count, key);
else
{
int desired_top;
if (!info_explicit_arg && count == 1)
{
desired_top = window->pagetop - (window->height - 2);
if ((desired_top < 0) && (window->pagetop == 0))
{
int behaviour = info_scroll_behaviour;
if (key != DEL && key != SPC)
behaviour = IS_PageOnly;
backward_move_node_structure (window, behaviour);
return;
}
}
else
desired_top = window->pagetop - count;
if (desired_top < 0)
desired_top = 0;
set_window_pagetop (window, desired_top);
}
}
DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
{
window->pagetop = window->point = 0;
window->flags |= W_UpdateWindow;
}
DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
{
window->point = window->node->nodelen - 1;
info_show_point (window);
}
DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
{
if (count < 0)
{
info_prev_window (window, -count, key);
return;
}
if (!windows->next && !echo_area_is_active)
{
info_error (ONE_WINDOW);
return;
}
while (count--)
{
if (window->next)
window = window->next;
else
{
if (window == the_echo_area || !echo_area_is_active)
window = windows;
else
window = the_echo_area;
}
}
if (active_window != window)
{
if (auto_footnotes_p)
info_get_or_remove_footnotes (window);
window->flags |= W_UpdateWindow;
active_window = window;
}
}
DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
{
if (count < 0)
{
info_next_window (window, -count, key);
return;
}
if (!windows->next && !echo_area_is_active)
{
info_error (ONE_WINDOW);
return;
}
while (count--)
{
if (window == the_echo_area ||
(window == windows && !echo_area_is_active))
{
register WINDOW *win, *last;
for (win = windows; win; win = win->next)
last = win;
window = last;
}
else
{
if (window == windows)
window = the_echo_area;
else
window = window->prev;
}
}
if (active_window != window)
{
if (auto_footnotes_p)
info_get_or_remove_footnotes (window);
window->flags |= W_UpdateWindow;
active_window = window;
}
}
DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
{
WINDOW *split, *old_active;
int pagetop;
pagetop = window->pagetop;
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)
if (pagetop == window->pagetop)
{
int start, end, amount;
start = split->first_row;
end = start + window->height;
amount = split->height + 1;
display_scroll_display (start, end, amount);
}
#else
info_show_point (window);
#endif
if (internal_info_node_p (split->node))
{
register int i, j;
INFO_WINDOW *iw;
NODE *node = (NODE *)NULL;
char *filename;
for (i = 0; (iw = info_windows[i]); i++)
{
for (j = 0; j < iw->nodes_index; j++)
if (!internal_info_node_p (iw->nodes[j]))
{
if (iw->nodes[j]->parent)
filename = iw->nodes[j]->parent;
else
filename = iw->nodes[j]->filename;
node = info_get_node (filename, iw->nodes[j]->nodename);
if (node)
{
window_set_node_of_window (split, node);
i = info_windows_index - 1;
break;
}
}
}
}
split->pagetop = window->pagetop;
if (auto_tiling_p)
window_tile_windows (DONT_TILE_INTERNALS);
else
window_adjust_pagetop (split);
remember_window_and_node (split, split->node);
}
}
DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
{
if (!windows->next)
{
info_error (CANT_KILL_LAST);
}
else if (window->flags & W_WindowIsPerm)
{
info_error (_("Cannot delete a permanent window"));
}
else
{
info_delete_window_internal (window);
if (auto_footnotes_p)
info_get_or_remove_footnotes (active_window);
if (auto_tiling_p)
window_tile_windows (DONT_TILE_INTERNALS);
}
}
void
info_delete_window_internal (window)
WINDOW *window;
{
if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
{
forget_window_and_nodes (window);
window_delete_window (window);
if (echo_area_is_active)
echo_area_inform_of_deleted_window (window);
}
}
DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
{
int num_deleted;
int pagetop, start, end;
pagetop = window->pagetop;
start = window->first_row;
end = start + window->height;
num_deleted = 0;
while (1)
{
WINDOW *win;
for (win = windows; win; win = win->next)
if (win != window && ((win->flags & W_WindowIsPerm) == 0))
break;
if (!win)
break;
info_delete_window_internal (win);
num_deleted++;
}
if (num_deleted)
{
int amount;
amount = (window->first_row - start);
amount -= (window->pagetop - pagetop);
display_scroll_display (start, end, amount);
}
window->flags |= W_UpdateWindow;
}
DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
{
WINDOW *other;
if (!windows->next)
{
info_error (ONE_WINDOW);
return;
}
other = window->next;
if (!other)
other = window->prev;
info_scroll_forward (other, count, key);
}
DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
{
window_change_window_height (window, count);
}
int auto_tiling_p = 0;
DECLARE_INFO_COMMAND (info_tile_windows,
_("Divide the available screen space among the visible windows"))
{
window_tile_windows (TILE_INTERNALS);
}
DECLARE_INFO_COMMAND (info_toggle_wrap,
_("Toggle the state of line wrapping in the current window"))
{
window_toggle_wrap (window);
}
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)
filename = xstrdup (filename);
if (entry->nodename)
nodename = xstrdup (entry->nodename);
else
nodename = xstrdup ("Top");
node = info_get_node (filename, nodename);
if (!node)
{
if (info_recent_file_error)
file_system_error = xstrdup (info_recent_file_error);
if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
{
node = info_get_node (entry->label, "Top");
if (!node && info_recent_file_error)
{
maybe_free (file_system_error);
file_system_error = xstrdup (info_recent_file_error);
}
}
}
if (!node)
{
if (file_system_error)
info_error (file_system_error);
else
info_error (CANT_FIND_NODE, nodename);
}
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);
}
}
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);
}
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)
filename = xstrdup (info_parsed_filename);
else
{
if (window->node->parent)
filename = xstrdup (window->node->parent);
else if (window->node->filename)
filename = xstrdup (window->node->filename);
}
if (info_parsed_nodename)
nodename = xstrdup (info_parsed_nodename);
else
nodename = xstrdup ("Top");
node = info_get_node (filename, nodename);
if (node)
{
INFO_WINDOW *info_win;
info_win = get_info_window_of_window (window);
if (info_win)
{
info_win->pagetops[info_win->current] = window->pagetop;
info_win->points[info_win->current] = window->point;
}
set_remembered_pagetop_and_point (window);
info_set_node_of_window (window, node);
}
else
{
if (info_recent_file_error)
info_error (info_recent_file_error);
else
info_error (CANT_FILE_NODE, filename, nodename);
}
free (filename);
free (nodename);
}
else
{
info_error (NO_POINTER, label);
}
}
DECLARE_INFO_COMMAND (info_next_node, _("Select the `Next' node"))
{
info_next_label_of_node (window->node);
info_handle_pointer (_("Next"), window);
}
DECLARE_INFO_COMMAND (info_prev_node, _("Select the `Prev' node"))
{
info_prev_label_of_node (window->node);
info_handle_pointer (_("Prev"), window);
}
DECLARE_INFO_COMMAND (info_up_node, _("Select the `Up' node"))
{
info_up_label_of_node (window->node);
info_handle_pointer (_("Up"), window);
}
DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
{
register int i;
FILE_BUFFER *fb = file_buffer_of_window (window);
NODE *node = (NODE *)NULL;
if (fb && fb->tags)
{
for (i = 0; fb->tags[i]; i++);
node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
}
if (!node)
info_error (_("This window has no additional nodes"));
else
{
set_remembered_pagetop_and_point (window);
info_set_node_of_window (window, node);
}
}
DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
{
FILE_BUFFER *fb = file_buffer_of_window (window);
NODE *node = (NODE *)NULL;
if (fb && fb->tags)
node = info_get_node (fb->filename, fb->tags[0]->nodename);
if (!node)
info_error (_("This window has no additional nodes"));
else
{
set_remembered_pagetop_and_point (window);
info_set_node_of_window (window, node);
}
}
DECLARE_INFO_COMMAND (info_last_menu_item,
_("Select the last item in this node's menu"))
{
info_menu_digit (window, 1, '0');
}
DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
{
register int i, item;
register REFERENCE *entry, **menu;
menu = info_menu_of_node (window->node);
if (!menu)
{
info_error (NO_MENU_NODE);
return;
}
item = key - '0';
if (item == 0)
for (i = 0; menu[i + 1]; i++);
else
{
for (i = 0; (entry = menu[i]); i++)
if (i == item - 1)
break;
}
if (menu[i])
info_select_reference (window, menu[i]);
else
info_error (_("There aren't %d items in this menu."), item);
info_free_references (menu);
return;
}
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)
info_error (NO_MENU_NODE);
else
info_error (NO_XREF_NODE);
return;
}
{
REFERENCE **refs = (REFERENCE **)NULL;
int point_line;
point_line = window_line_of_point (window);
if (point_line != -1)
{
SEARCH_BINDING binding;
binding.buffer = window->node->contents;
binding.start = window->line_starts[point_line] - binding.buffer;
if (window->line_starts[point_line + 1])
binding.end = window->line_starts[point_line + 1] - binding.buffer;
else
binding.end = window->node->nodelen;
binding.flags = 0;
if (builder == info_menu_of_node)
{
if (point_line)
{
binding.start--;
refs = info_menu_items (&binding);
}
}
else
{
#if defined (HANDLE_MAN_PAGES)
if (window->node->flags & N_IsManPage)
refs = manpage_xrefs_in_binding (window->node, &binding);
else
#endif
refs = info_xrefs (&binding);
}
if (refs)
{
if ((strcmp (refs[0]->label, "Menu") != 0) ||
(builder == info_xrefs_of_node))
{
int which = 0;
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);
}
}
}
if (ask_p)
{
char *prompt;
if (defentry)
prompt = (char *)xmalloc (20 + strlen (defentry->label));
else
prompt = (char *)xmalloc (20);
if (builder == info_menu_of_node)
{
if (defentry)
sprintf (prompt, _("Menu item (%s): "), defentry->label);
else
sprintf (prompt, _("Menu item: "));
}
else
{
if (defentry)
sprintf (prompt, _("Follow xref (%s): "), defentry->label);
else
sprintf (prompt, _("Follow xref: "));
}
line = info_read_completing_in_echo_area (window, prompt, menu);
free (prompt);
window = active_window;
if (!line)
{
maybe_free (defentry);
info_free_references (menu);
info_abort_key (window, 0, 0);
return;
}
if (!*line)
{
free (line);
if (defentry)
line = xstrdup (defentry->label);
else
line = (char *)NULL;
}
}
else
{
if (!defentry)
return;
else
line = xstrdup (defentry->label);
}
if (line)
{
entry = info_get_labeled_reference (line, menu);
if (!entry && defentry)
info_error (_("The reference disappeared! (%s)."), line);
else
{
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);
}
}
}
free (line);
if (defentry)
{
free (defentry->label);
maybe_free (defentry->filename);
maybe_free (defentry->nodename);
free (defentry);
}
}
info_free_references (menu);
if (!info_error_was_printed)
window_clear_echo_area ();
}
DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
{
info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
}
DECLARE_INFO_COMMAND
(info_xref_item, _("Read a footnote or cross reference and select its node"))
{
info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
}
DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
{
SEARCH_BINDING binding;
long position;
binding.buffer = window->node->contents;
binding.start = 0;
binding.end = window->node->nodelen;
binding.flags = S_FoldCase | S_SkipDest;
position = search (INFO_MENU_LABEL, &binding);
if (position == -1)
info_error (NO_MENU_NODE);
else
{
window->point = position;
window_adjust_pagetop (window);
window->flags |= W_UpdateWindow;
}
}
DECLARE_INFO_COMMAND (info_visit_menu,
_("Visit as many menu items at once as possible"))
{
register int i;
REFERENCE *entry, **menu;
menu = info_menu_of_node (window->node);
if (!menu)
info_error (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)
info_error (WIN_TOO_SMALL);
else
{
active_window = new;
info_select_reference (new, entry);
}
}
}
DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
{
char *line;
#define GOTO_COMPLETES
#if defined (GOTO_COMPLETES)
{
register int fbi, i;
FILE_BUFFER *current;
REFERENCE **items = (REFERENCE **)NULL;
int items_index = 0;
int items_slots = 0;
current = file_buffer_of_window (window);
for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
{
FILE_BUFFER *fb;
REFERENCE *entry;
int this_is_the_current_fb;
fb = info_loaded_files[fbi];
this_is_the_current_fb = (current == fb);
entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
entry->filename = entry->nodename = (char *)NULL;
entry->label = (char *)xmalloc (4 + strlen (fb->filename));
sprintf (entry->label, "(%s)*", fb->filename);
add_pointer_to_array
(entry, items_index, items, items_slots, 10, REFERENCE *);
if (fb->tags)
{
for (i = 0; fb->tags[i]; i++)
{
entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
entry->filename = entry->nodename = (char *)NULL;
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 *);
}
}
}
}
line = info_read_maybe_completing (window, _("Goto Node: "), items);
info_free_references (items);
}
#else
line = info_read_in_echo_area (window, _("Goto Node: "));
#endif
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)
DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
{
char *line;
line = info_read_in_echo_area (window, _("Get Manpage: "));
if (!line)
{
info_abort_key (window, 0, 0);
return;
}
canonicalize_whitespace (line);
if (*line)
{
char *goto_command;
goto_command = (char *)xmalloc
(4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
info_parse_and_select (goto_command, window);
free (goto_command);
}
free (line);
if (!info_error_was_printed)
window_clear_echo_area ();
}
#endif
DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
{
info_parse_and_select (_("Top"), window);
}
DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
{
info_parse_and_select ("(dir)Top", window);
}
static char *
read_nodename_to_kill (window)
WINDOW *window;
{
int iw;
char *nodename;
INFO_WINDOW *info_win;
REFERENCE **menu = NULL;
int menu_index = 0, menu_slots = 0;
char *default_nodename = xstrdup (active_window->node->nodename);
char *prompt = xmalloc (40 + strlen (default_nodename));
sprintf (prompt, _("Kill node (%s): "), default_nodename);
for (iw = 0; (info_win = info_windows[iw]); iw++)
{
REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
entry->label = xstrdup (info_win->window->node->nodename);
entry->filename = entry->nodename = (char *)NULL;
add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
REFERENCE *);
}
nodename = info_read_completing_in_echo_area (window, prompt, menu);
free (prompt);
info_free_references (menu);
if (nodename && !*nodename)
{
free (nodename);
nodename = default_nodename;
}
else
free (default_nodename);
return nodename;
}
static void
kill_node (window, nodename)
WINDOW *window;
char *nodename;
{
int iw, i;
INFO_WINDOW *info_win;
NODE *temp;
if (!nodename)
{
info_abort_key (window, 0, 0);
return;
}
for (iw = 0; (info_win = info_windows[iw]); iw++)
if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0)
break;
if (!info_win)
{
if (*nodename)
info_error (_("Cannot kill node `%s'"), nodename);
else
window_clear_echo_area ();
return;
}
if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
{
info_error (_("Cannot kill the last node"));
return;
}
for (i = info_win->current; i < info_win->nodes_index; i++)
info_win->nodes[i] = info_win->nodes[i++];
info_win->nodes_index--;
info_win->current = info_win->nodes_index - 1;
if (info_win->current < 0)
{
INFO_WINDOW *stealer;
int which, pagetop;
long point;
if (info_windows[iw + 1])
stealer = info_windows[iw + 1];
else
stealer = info_windows[0];
if ((stealer->nodes_index - 1) != stealer->current)
which = stealer->nodes_index - 1;
else if (stealer->current > 0)
which = stealer->current - 1;
else
which = stealer->current;
{
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;
}
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);
}
if (!info_error_was_printed)
window_clear_echo_area ();
if (auto_footnotes_p)
info_get_or_remove_footnotes (window);
}
DECLARE_INFO_COMMAND (info_history_node,
_("Select the most recently selected node"))
{
kill_node (window, active_window->node->nodename);
}
DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
{
char *nodename = read_nodename_to_kill (window);
kill_node (window, nodename);
}
DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
{
char *line;
line = info_read_in_echo_area (window, _("Find file: "));
if (!line)
{
info_abort_key (active_window, 1, 0);
return;
}
if (*line)
{
NODE *node;
node = info_get_node (line, "*");
if (!node)
{
if (info_recent_file_error)
info_error (info_recent_file_error);
else
info_error (_("Cannot find \"%s\"."), line);
}
else
{
set_remembered_pagetop_and_point (active_window);
info_set_node_of_window (window, node);
}
free (line);
}
if (!info_error_was_printed)
window_clear_echo_area ();
}
#define VERBOSE_NODE_DUMPING
static void write_node_to_stream ();
static void dump_node_to_stream ();
static void initialize_dumping ();
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;
if (strcmp (output_filename, "-") == 0)
output_stream = stdout;
else
output_stream = fopen (output_filename, "w");
if (!output_stream)
{
info_error (_("Could not create output file \"%s\"."), output_filename);
return;
}
initialize_dumping ();
for (i = 0; nodenames[i]; i++)
dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
if (output_stream != stdout)
fclose (output_stream);
#if defined (VERBOSE_NODE_DUMPING)
info_error (_("Done."));
#endif
}
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;
}
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)
info_error (info_recent_file_error);
else
{
if (filename && *nodename != '(')
info_error
(CANT_FILE_NODE, filename_non_directory (filename), nodename);
else
info_error (CANT_FIND_NODE, nodename);
}
return;
}
for (i = 0; i < dumped_already_index; i++)
if (strcmp (node->nodename, dumped_already[i]) == 0)
{
free (node);
return;
}
add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
dumped_already_slots, 50, char *);
#if defined (VERBOSE_NODE_DUMPING)
if (node->filename)
info_error (_("Writing node \"(%s)%s\"..."),
filename_non_directory (node->filename), node->nodename);
else
info_error (_("Writing node \"%s\"..."), node->nodename);
#endif
write_node_to_stream (node, stream);
if (dump_subnodes)
{
REFERENCE **menu = (REFERENCE **)NULL;
if (string_in_line ("Index", node->nodename) == -1)
menu = info_menu_of_node (node);
if (menu)
{
for (i = 0; menu[i]; i++)
{
if (!menu[i]->filename)
dump_node_to_stream
(filename, menu[i]->nodename, stream, dump_subnodes);
}
info_free_references (menu);
}
}
free (node);
}
void
dump_node_to_file (node, filename, dump_subnodes)
NODE *node;
char *filename;
int dump_subnodes;
{
FILE *output_stream;
char *nodes_filename;
if (strcmp (filename, "-") == 0)
output_stream = stdout;
else
output_stream = fopen (filename, "w");
if (!output_stream)
{
info_error (_("Could not create output file \"%s\"."), filename);
return;
}
if (node->parent)
nodes_filename = node->parent;
else
nodes_filename = node->filename;
initialize_dumping ();
dump_node_to_stream
(nodes_filename, node->nodename, output_stream, dump_subnodes);
if (output_stream != stdout)
fclose (output_stream);
#if defined (VERBOSE_NODE_DUMPING)
info_error (_("Done."));
#endif
}
#if !defined (DEFAULT_INFO_PRINT_COMMAND)
# define DEFAULT_INFO_PRINT_COMMAND "lpr"
#endif
DECLARE_INFO_COMMAND (info_print_node,
_("Pipe the contents of this node through INFO_PRINT_COMMAND"))
{
print_node (window->node);
}
void
print_node (node)
NODE *node;
{
FILE *printer_pipe;
char *print_command = getenv ("INFO_PRINT_COMMAND");
if (!print_command || !*print_command)
print_command = DEFAULT_INFO_PRINT_COMMAND;
printer_pipe = popen (print_command, "w");
if (!printer_pipe)
{
info_error (_("Cannot open pipe to \"%s\"."), print_command);
return;
}
#if defined (VERBOSE_NODE_DUMPING)
if (node->filename)
info_error (_("Printing node \"(%s)%s\"..."),
filename_non_directory (node->filename), node->nodename);
else
info_error (_("Printing node \"%s\"..."), node->nodename);
#endif
write_node_to_stream (node, printer_pipe);
pclose (printer_pipe);
#if defined (VERBOSE_NODE_DUMPING)
info_error (_("Done."));
#endif
}
static void
write_node_to_stream (node, stream)
NODE *node;
FILE *stream;
{
fwrite (node->contents, 1, node->nodelen, stream);
}
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;
FILE_BUFFER *
file_buffer_of_window (window)
WINDOW *window;
{
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);
}
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);
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)
window_set_node_of_window (window, node);
window->point = offset;
window_adjust_pagetop (window);
}
return (offset);
}
long
info_target_search_node (node, string, start)
NODE *node;
char *string;
long start;
{
register int i;
long offset;
char *target;
target = xstrdup (string);
i = strlen (target);
while (i)
{
target[i] = '\0';
offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1);
if (offset != -1)
break;
for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
}
free (target);
return (offset);
}
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
(string, window->node, window->point + dir, window, dir);
}
else
{
ret = info_search_in_node
(string, window->node, window->point, window, dir);
}
maybe_free (last_searched_for_string);
last_searched_for_string = xstrdup (string);
if (ret != -1)
{
if (!echo_area_is_active && !isearch_is_active)
window_clear_echo_area ();
return (0);
}
if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
return (-1);
if (file_buffer->tags)
{
register int current_tag, number_of_tags;
char *last_subfile;
TAG *tag;
last_subfile = (char *)NULL;
for (i = 0; file_buffer->tags[i]; i++)
if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
{
current_tag = i;
last_subfile = file_buffer->tags[i]->filename;
}
number_of_tags = i;
if (!last_subfile)
return (-1);
while (1)
{
NODE *node;
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 (!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);
if (ret != -1)
{
remember_window_and_node (window, node);
if (!echo_area_is_active)
window_clear_echo_area ();
return (0);
}
free (node);
if (strcmp (initial_nodename, tag->nodename) == 0)
return (-1);
}
}
return (-1);
}
DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
{
char *line, *prompt;
int result, old_pagetop;
int direction;
if (count < 0)
direction = -1;
else
direction = 1;
if (!search_string)
{
search_string = (char *)xmalloc (search_string_size = 100);
search_string[0] = '\0';
}
prompt = (char *)xmalloc (50 + strlen (search_string));
sprintf (prompt, _("%s for string [%s]: "),
direction < 0 ? _("Search backward") : _("Search"),
search_string);
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)
search_string = (char *)
xrealloc (search_string, (search_string_size += 50 + strlen (line)));
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)
info_error (_("Search failed."));
else if (old_pagetop != active_window->pagetop)
{
int new_pagetop;
new_pagetop = active_window->pagetop;
active_window->pagetop = old_pagetop;
set_window_pagetop (active_window, new_pagetop);
if (auto_footnotes_p)
info_get_or_remove_footnotes (active_window);
}
info_gc_file_buffers ();
}
static void incremental_search ();
DECLARE_INFO_COMMAND (isearch_forward,
_("Search interactively for a string as you type it"))
{
incremental_search (window, count, key);
}
DECLARE_INFO_COMMAND (isearch_backward,
_("Search interactively for a string as you type it"))
{
incremental_search (window, -count, key);
}
static char *last_isearch_accepted = (char *)NULL;
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;
typedef struct {
WINDOW_STATE_DECL;
int search_index;
int direction;
int failing;
} SEARCH_STATE;
static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
static int isearch_states_index = 0;
static int isearch_states_slots = 0;
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,
isearch_states_slots, 20, SEARCH_STATE *);
}
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;
}
}
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;
}
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)
prefix = _("I-search backward: ");
else
prefix = _("I-search: ");
p_rep_index = p_rep_size = 0;
p_rep = (char *)NULL;
for (i = 0; string[i]; i++)
{
char *rep;
switch (string[i])
{
case ' ': rep = " "; break;
case LFD: rep = "\\n"; break;
case TAB: rep = "\\t"; break;
default:
rep = pretty_keyname (string[i]);
}
if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
strcpy (p_rep + p_rep_index, rep);
p_rep_index += strlen (rep);
}
prompt_len = strlen (prefix) + p_rep_index + 20;
prompt = (char *)xmalloc (prompt_len);
sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
p_rep ? p_rep : "");
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);
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 (!info_any_buffered_input_p () && display_was_interrupted_p)
{
display_update_one_window (window);
display_cursor_at_point (active_window);
}
key = info_get_input_char ();
window_get_state (window, &mystate);
if (key == DEL)
{
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;
}
}
else if (key == Control ('q'))
{
key = info_get_input_char ();
quoted = 1;
}
push_isearch (window, isearch_string_index, dir, search_result);
if (quoted)
goto insert_and_search;
if (!Meta_p (key) || (ISO_Latin_p && key < 160))
{
func = window->keymap[key].function;
if (func == isearch_forward || func == isearch_backward)
{
if ((func == isearch_forward && dir > 0) ||
(func == isearch_backward && dir < 0))
{
if (isearch_string_index == 0)
{
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
{
if (search_result == 0)
window->point += dir;
}
}
else
{
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 (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;
}
else
{
exit_search:
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;
}
search_now:
show_isearch_prompt (dir, isearch_string, search_result);
if (search_result == 0)
{
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);
}
if (search_result != 0 && last_search_result == 0)
terminal_ring_bell ();
after_search:
show_isearch_prompt (dir, isearch_string, search_result);
if (search_result == 0)
{
if ((mystate.node == window->node) &&
(mystate.pagetop != window->pagetop))
{
int newtop = window->pagetop;
window->pagetop = mystate.pagetop;
set_window_pagetop (window, newtop);
}
display_update_one_window (window);
display_cursor_at_point (window);
}
last_search_result = search_result;
}
free_isearch_states ();
info_gc_file_buffers ();
if (!echo_area_is_active)
window_clear_echo_area ();
}
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;
for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
{
int fb_referenced_p = 0;
if (!fb->contents)
continue;
if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
continue;
if (fb->flags & N_CannotGC)
continue;
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;
}
}
}
if (!fb_referenced_p)
{
free (fb->contents);
fb->contents = (char *)NULL;
}
}
}
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;
firstmenu = info_search_in_node
(INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir);
if (firstmenu != -1)
{
char *text = node->contents + firstmenu;
if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
firstmenu = info_search_in_node
(INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir);
}
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
if (firstmenu == -1 && firstxref == -1)
{
info_error (_("No cross references in this node."));
return;
}
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
if (nextmenu != -1)
{
char *text = node->contents + nextmenu;
if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
nextmenu = info_search_in_node
(INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir);
}
if (nextmenu != -1 && nextxref != -1)
{
if (((dir == 1) && (nextmenu < nextxref)) ||
((dir == -1) && (nextmenu > nextxref)))
placement = nextmenu + 1;
else
placement = nextxref;
}
else if (nextmenu != -1)
placement = nextmenu + 1;
else if (nextxref != -1)
placement = nextxref;
if (placement == -1)
{
if (firstmenu != -1 && firstxref != -1)
{
if (((dir == 1) && (firstmenu < firstxref)) ||
((dir == -1) && (firstmenu > firstxref)))
placement = firstmenu + 1;
else
placement = firstxref;
}
else if (firstmenu != -1)
placement = firstmenu + 1;
else
placement = firstxref;
}
window->point = placement;
window_adjust_pagetop (window);
window->flags |= W_UpdateWindow;
}
DECLARE_INFO_COMMAND (info_move_to_prev_xref,
_("Move to the previous cross reference"))
{
if (count < 0)
info_move_to_prev_xref (window, -count, key);
else
info_move_to_xref (window, count, key, -1);
}
DECLARE_INFO_COMMAND (info_move_to_next_xref,
_("Move to the next cross reference"))
{
if (count < 0)
info_move_to_next_xref (window, -count, key);
else
info_move_to_xref (window, count, key, 1);
}
DECLARE_INFO_COMMAND (info_select_reference_this_line,
_("Select reference or menu item appearing on this line"))
{
char *line;
NODE *orig;
line = window->line_starts[window_line_of_point (window)];
orig = window->node;
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);
}
DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
{
if (!info_error_rings_bell_p)
terminal_ring_bell ();
info_error (_("Quit"));
info_initialize_numeric_arg ();
info_clear_pending_input ();
info_last_executed_command = (VFunction *)NULL;
}
DECLARE_INFO_COMMAND (info_move_to_window_line,
_("Move to the cursor to a specific line of the window"))
{
int line;
if (!info_explicit_arg && count == 1)
line = (window->height / 2) + window->pagetop;
else
{
if (count < 0)
line = (window->height + count) + window->pagetop;
else
line = window->pagetop + count;
}
if ((line - window->pagetop) >= window->height)
line = window->pagetop + (window->height - 1);
if (line < window->pagetop)
line = window->pagetop;
if (line >= window->line_count)
line = window->line_count - 1;
window->point = (window->line_starts[line] - window->node->contents);
}
DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
{
if ((!info_explicit_arg && count == 1) || echo_area_is_active)
{
terminal_clear_screen ();
display_clear_display (the_display);
window_mark_chain (windows, W_UpdateWindow);
display_update_display (windows);
}
else
{
int desired_line, point_line;
int new_pagetop;
point_line = window_line_of_point (window) - window->pagetop;
if (count < 0)
desired_line = window->height + count;
else
desired_line = count;
if (desired_line < 0)
desired_line = 0;
if (desired_line >= window->height)
desired_line = window->height - 1;
if (desired_line == point_line)
return;
new_pagetop = window->pagetop + (point_line - desired_line);
set_window_pagetop (window, new_pagetop);
}
}
DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
{}
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)
info_error (_("Unknown command (%s)."), rep);
else
{
char *temp;
temp = (char *)xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid")));
sprintf (temp, _("\"%s\" is invalid"), rep);
terminal_ring_bell ();
inform_in_echo_area (temp);
free (temp);
}
}
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;
void
initialize_keyseq ()
{
info_keyseq_index = 0;
info_keyseq_displayed_p = 0;
}
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';
}
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",
strlen (keyseq_rep) ? " " : "",
pretty_keyname (keyseq[i]));
}
return (keyseq_rep);
}
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;
}
unsigned char
info_get_another_input_char ()
{
int ready = !info_keyseq_displayed_p;
if (!info_keyseq_displayed_p)
{
ready = 1;
if (!info_any_buffered_input_p () &&
!info_input_pending_p ())
{
#if defined (FD_SET)
struct timeval timer;
fd_set readfds;
FD_ZERO (&readfds);
FD_SET (fileno (info_input_stream), &readfds);
timer.tv_sec = 1;
timer.tv_usec = 750;
ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
#else
ready = 0;
#endif
}
}
if (!ready)
display_info_keyseq (1);
return (info_get_input_char ());
}
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)
{
map = (Keymap)map[ESC].function;
add_char_to_keyseq (ESC);
key = UnMeta (key);
info_dispatch_on_key (key, map);
}
else
{
dispatch_error (info_keyseq);
}
return;
}
switch (map[key].type)
{
case ISFUNC:
{
VFunction *func;
func = map[key].function;
if (func != (VFunction *)NULL)
{
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 (!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;
}
}
break;
case ISKMAP:
add_char_to_keyseq (key);
if (map[key].function != (VFunction *)NULL)
{
unsigned char newkey;
newkey = info_get_another_input_char ();
info_dispatch_on_key (newkey, (Keymap)map[key].function);
}
else
{
dispatch_error (info_keyseq);
return;
}
break;
}
}
int info_explicit_arg = 0;
int info_numeric_arg_sign = 1;
int info_numeric_arg = 1;
DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
_("Add this digit to the current numeric argument"))
{
info_numeric_arg_digit_loop (window, 0, key);
}
DECLARE_INFO_COMMAND (info_universal_argument,
_("Start (or multiply by 4) the current numeric argument"))
{
info_numeric_arg *= 4;
info_numeric_arg_digit_loop (window, 0, 0);
}
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,
_("Internally used by \\[universal-argument]"))
{
unsigned char pure_key;
Keymap keymap = window->keymap;
while (1)
{
if (key)
pure_key = key;
else
{
if (display_was_interrupted_p && !info_any_buffered_input_p ())
display_update_display (windows);
if (active_window != the_echo_area)
display_cursor_at_point (active_window);
pure_key = key = info_get_another_input_char ();
if (Meta_p (key))
add_char_to_keyseq (ESC);
add_char_to_keyseq (UnMeta (key));
}
if (Meta_p (key))
key = UnMeta (key);
if (keymap[key].type == ISFUNC &&
keymap[key].function == info_universal_argument)
{
info_numeric_arg *= 4;
key = 0;
continue;
}
if (isdigit (key))
{
if (info_explicit_arg)
info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
else
info_numeric_arg = (key - '0');
info_explicit_arg = 1;
}
else
{
if (key == '-' && !info_explicit_arg)
{
info_numeric_arg_sign = -1;
info_numeric_arg = 1;
}
else
{
info_keyseq_index--;
info_dispatch_on_key (pure_key, keymap);
return;
}
}
key = 0;
}
}
static int pending_input_character = 0;
static void
info_clear_pending_input ()
{
pending_input_character = 0;
}
static void
info_set_pending_input (key)
unsigned char key;
{
pending_input_character = key;
}
unsigned char
info_input_pending_p ()
{
return (pending_input_character);
}
#define MAX_INFO_INPUT_BUFFERING 512
static int pop_index = 0, push_index = 0;
static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
static void
info_push_typeahead (key)
unsigned char key;
{
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))
push_index = 0;
}
}
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));
}
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);
}
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 defined (FIONREAD)
{
ioctl (tty, FIONREAD, &chars_avail);
if (chars_avail > space_avail)
chars_avail = space_avail;
if (chars_avail)
chars_avail = read (tty, &input[0], chars_avail);
}
#else
# 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
#endif
while (i < chars_avail)
{
info_push_typeahead (input[i]);
i++;
}
}
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;
unsigned char c;
int tty = fileno (info_input_stream);
#ifdef EINTR
{
int n;
do
n = read (tty, &c, 1);
while (n == -1 && errno == EINTR);
rawkey = n == 1 ? c : EOF;
}
#else
rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
#endif
keystroke = rawkey;
if (rawkey == EOF)
{
if (info_input_stream != stdin)
{
fclose (info_input_stream);
info_input_stream = stdin;
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);
}
}
}
if (info_dribble_file)
dribble (keystroke);
return keystroke;
}