156164Sru/* session.c -- user windowing interface to Info. 2146520Sru $Id: session.c,v 1.16 2004/12/14 00:15:36 karl Exp $ 321495Sjmacd 4146520Sru Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 5116530Sru Free Software Foundation, Inc. 621495Sjmacd 721495Sjmacd This program is free software; you can redistribute it and/or modify 821495Sjmacd it under the terms of the GNU General Public License as published by 921495Sjmacd the Free Software Foundation; either version 2, or (at your option) 1021495Sjmacd any later version. 1121495Sjmacd 1221495Sjmacd This program is distributed in the hope that it will be useful, 1321495Sjmacd but WITHOUT ANY WARRANTY; without even the implied warranty of 1421495Sjmacd MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1521495Sjmacd GNU General Public License for more details. 1621495Sjmacd 1721495Sjmacd You should have received a copy of the GNU General Public License 1821495Sjmacd along with this program; if not, write to the Free Software 1921495Sjmacd Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2021495Sjmacd 21146520Sru Originally written by Brian Fox (bfox@ai.mit.edu). */ 2221495Sjmacd 2321495Sjmacd#include "info.h" 24146520Sru#include "search.h" 2521495Sjmacd#include <sys/ioctl.h> 2621495Sjmacd 2721495Sjmacd#if defined (HAVE_SYS_TIME_H) 2821495Sjmacd# include <sys/time.h> 2921495Sjmacd# define HAVE_STRUCT_TIMEVAL 3021495Sjmacd#endif /* HAVE_SYS_TIME_H */ 3121495Sjmacd 3221495Sjmacd#if defined (HANDLE_MAN_PAGES) 3321495Sjmacd# include "man.h" 3421495Sjmacd#endif 3521495Sjmacd 36146520Srustatic void info_clear_pending_input (void); 37146520Srustatic void info_set_pending_input (unsigned char key); 38146520Srustatic void info_handle_pointer (char *label, WINDOW *window); 39146520Srustatic void display_info_keyseq (int expecting_future_input); 40146520Sruchar *node_printed_rep (NODE *node); 4156164Sru 4221495Sjmacd/* **************************************************************** */ 4342664Smarkm/* */ 4442664Smarkm/* Running an Info Session */ 4542664Smarkm/* */ 4621495Sjmacd/* **************************************************************** */ 4721495Sjmacd 4821495Sjmacd/* The place that we are reading input from. */ 4942664Smarkmstatic FILE *info_input_stream = NULL; 5021495Sjmacd 5121495Sjmacd/* The last executed command. */ 5242664SmarkmVFunction *info_last_executed_command = NULL; 5321495Sjmacd 5421495Sjmacd/* Becomes non-zero when 'q' is typed to an Info window. */ 5521495Sjmacdint quit_info_immediately = 0; 5621495Sjmacd 5721495Sjmacd/* Array of structures describing for each window which nodes have been 5821495Sjmacd visited in that window. */ 5942664SmarkmINFO_WINDOW **info_windows = NULL; 6021495Sjmacd 6121495Sjmacd/* Where to add the next window, if we need to add one. */ 6221495Sjmacdstatic int info_windows_index = 0; 6321495Sjmacd 6442664Smarkm/* Number of slots allocated to `info_windows'. */ 6521495Sjmacdstatic int info_windows_slots = 0; 6621495Sjmacd 67146520Sruvoid remember_window_and_node (WINDOW *window, NODE *node); 68146520Sruvoid forget_window_and_nodes (WINDOW *window); 69146520Sruvoid display_startup_message_and_start (void); 7021495Sjmacd 7121495Sjmacd/* Begin an info session finding the nodes specified by FILENAME and NODENAMES. 7221495Sjmacd For each loaded node, create a new window. Always split the largest of the 7321495Sjmacd available windows. */ 7421495Sjmacdvoid 75146520Srubegin_multiple_window_info_session (char *filename, char **nodenames) 7621495Sjmacd{ 7721495Sjmacd register int i; 7821495Sjmacd WINDOW *window = (WINDOW *)NULL; 7921495Sjmacd 8021495Sjmacd for (i = 0; nodenames[i]; i++) 8121495Sjmacd { 8221495Sjmacd NODE *node; 8321495Sjmacd 8421495Sjmacd node = info_get_node (filename, nodenames[i]); 8521495Sjmacd 8621495Sjmacd if (!node) 8742664Smarkm break; 8821495Sjmacd 8921495Sjmacd /* If this is the first node, initialize the info session. */ 9021495Sjmacd if (!window) 9142664Smarkm { 9242664Smarkm initialize_info_session (node, 1); 9342664Smarkm window = active_window; 9442664Smarkm } 9521495Sjmacd else 9642664Smarkm { 9742664Smarkm /* Find the largest window in WINDOWS, and make that be the active 9842664Smarkm one. Then split it and add our window and node to the list 9942664Smarkm of remembered windows and nodes. Then tile the windows. */ 10056164Sru WINDOW *win, *largest = NULL; 10142664Smarkm int max_height = 0; 10221495Sjmacd 10342664Smarkm for (win = windows; win; win = win->next) 10442664Smarkm if (win->height > max_height) 10542664Smarkm { 10642664Smarkm max_height = win->height; 10742664Smarkm largest = win; 10842664Smarkm } 10921495Sjmacd 11042664Smarkm if (!largest) 11142664Smarkm { 11242664Smarkm display_update_display (windows); 113146520Sru info_error ((char *) msg_cant_find_window, NULL, NULL); 11442664Smarkm info_session (); 11556164Sru xexit (0); 11642664Smarkm } 11721495Sjmacd 11842664Smarkm active_window = largest; 11942664Smarkm window = window_make_window (node); 12042664Smarkm if (window) 12142664Smarkm { 12242664Smarkm window_tile_windows (TILE_INTERNALS); 12342664Smarkm remember_window_and_node (window, node); 12442664Smarkm } 12542664Smarkm else 12642664Smarkm { 12742664Smarkm display_update_display (windows); 128146520Sru info_error ((char *) msg_win_too_small, NULL, NULL); 12942664Smarkm info_session (); 13056164Sru xexit (0); 13142664Smarkm } 13242664Smarkm } 13321495Sjmacd } 13421495Sjmacd display_startup_message_and_start (); 13521495Sjmacd} 13621495Sjmacd 13721495Sjmacd/* Start an info session with INITIAL_NODE, and an error message in the echo 13821495Sjmacd area made from FORMAT and ARG. */ 13921495Sjmacdvoid 140146520Srubegin_info_session_with_error (NODE *initial_node, char *format, 141146520Sru void *arg1, void *arg2) 14221495Sjmacd{ 14342664Smarkm initialize_info_session (initial_node, 1); 14456164Sru info_error (format, arg1, arg2); 14521495Sjmacd info_session (); 14621495Sjmacd} 14721495Sjmacd 14821495Sjmacd/* Start an info session with INITIAL_NODE. */ 14921495Sjmacdvoid 150146520Srubegin_info_session (NODE *initial_node) 15121495Sjmacd{ 15242664Smarkm initialize_info_session (initial_node, 1); 15321495Sjmacd display_startup_message_and_start (); 15421495Sjmacd} 15521495Sjmacd 15621495Sjmacdvoid 157146520Srudisplay_startup_message_and_start (void) 15821495Sjmacd{ 15921495Sjmacd char *format; 16021495Sjmacd 16121495Sjmacd format = replace_in_documentation 162146520Sru ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."), 163146520Sru 0); 16421495Sjmacd 165146520Sru window_message_in_echo_area (format, VERSION, NULL); 16621495Sjmacd info_session (); 16721495Sjmacd} 16821495Sjmacd 16921495Sjmacd/* Run an info session with an already initialized window and node. */ 17021495Sjmacdvoid 171146520Sruinfo_session (void) 17221495Sjmacd{ 17321495Sjmacd display_update_display (windows); 17442664Smarkm info_last_executed_command = NULL; 17521495Sjmacd info_read_and_dispatch (); 17621495Sjmacd /* On program exit, leave the cursor at the bottom of the window, and 17721495Sjmacd restore the terminal I/O. */ 17821495Sjmacd terminal_goto_xy (0, screenheight - 1); 17921495Sjmacd terminal_clear_to_eol (); 18021495Sjmacd fflush (stdout); 18121495Sjmacd terminal_unprep_terminal (); 18221495Sjmacd close_dribble_file (); 18321495Sjmacd} 18421495Sjmacd 18521495Sjmacd/* Here is a window-location dependent event loop. Called from the 18621495Sjmacd functions info_session (), and from read_xxx_in_echo_area (). */ 18721495Sjmacdvoid 188146520Sruinfo_read_and_dispatch (void) 18921495Sjmacd{ 19021495Sjmacd unsigned char key; 19121495Sjmacd int done; 19221495Sjmacd done = 0; 19321495Sjmacd 19421495Sjmacd while (!done && !quit_info_immediately) 19521495Sjmacd { 196146520Sru int lk = 0; 19721495Sjmacd 19821495Sjmacd /* If we haven't just gone up or down a line, there is no 19942664Smarkm goal column for this window. */ 200146520Sru if ((info_last_executed_command != (VFunction *) info_next_line) && 201146520Sru (info_last_executed_command != (VFunction *) info_prev_line)) 20242664Smarkm active_window->goal_column = -1; 20321495Sjmacd 20421495Sjmacd if (echo_area_is_active) 20542664Smarkm { 20642664Smarkm lk = echo_area_last_command_was_kill; 20742664Smarkm echo_area_prep_read (); 20842664Smarkm } 20921495Sjmacd 21021495Sjmacd if (!info_any_buffered_input_p ()) 21142664Smarkm display_update_display (windows); 21221495Sjmacd 21321495Sjmacd display_cursor_at_point (active_window); 21421495Sjmacd info_initialize_numeric_arg (); 21521495Sjmacd 21621495Sjmacd initialize_keyseq (); 21721495Sjmacd key = info_get_input_char (); 21821495Sjmacd 21921495Sjmacd /* No errors yet. We just read a character, that's all. Only clear 22042664Smarkm the echo_area if it is not currently active. */ 22121495Sjmacd if (!echo_area_is_active) 22242664Smarkm window_clear_echo_area (); 22321495Sjmacd 22421495Sjmacd info_error_was_printed = 0; 22521495Sjmacd 22621495Sjmacd /* Do the selected command. */ 22721495Sjmacd info_dispatch_on_key (key, active_window->keymap); 22821495Sjmacd 22921495Sjmacd if (echo_area_is_active) 23042664Smarkm { 23142664Smarkm /* Echo area commands that do killing increment the value of 23242664Smarkm ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no 23342664Smarkm change in the value of this variable, the last command 23442664Smarkm executed was not a kill command. */ 23542664Smarkm if (lk == echo_area_last_command_was_kill) 23642664Smarkm echo_area_last_command_was_kill = 0; 23721495Sjmacd 238146520Sru if (ea_last_executed_command == (VFunction *) ea_newline || 23942664Smarkm info_aborted_echo_area) 24042664Smarkm { 24142664Smarkm ea_last_executed_command = (VFunction *)NULL; 24242664Smarkm done = 1; 24342664Smarkm } 24421495Sjmacd 245146520Sru if (info_last_executed_command == (VFunction *) info_quit) 24642664Smarkm quit_info_immediately = 1; 24742664Smarkm } 248146520Sru else if (info_last_executed_command == (VFunction *) info_quit) 24942664Smarkm done = 1; 25021495Sjmacd } 25121495Sjmacd} 25221495Sjmacd 25321495Sjmacd/* Found in signals.c */ 254146520Sruextern void initialize_info_signal_handler (void ); 25521495Sjmacd 25621495Sjmacd/* Initialize the first info session by starting the terminal, window, 25742664Smarkm and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ 25821495Sjmacdvoid 259146520Sruinitialize_info_session (NODE *node, int clear_screen) 26021495Sjmacd{ 26142664Smarkm char *term_name = getenv ("TERM"); 26221495Sjmacd terminal_initialize_terminal (term_name); 26321495Sjmacd 26421495Sjmacd if (terminal_is_dumb_p) 26521495Sjmacd { 26621495Sjmacd if (!term_name) 26742664Smarkm term_name = "dumb"; 26821495Sjmacd 269146520Sru info_error ((char *) msg_term_too_dumb, term_name, NULL); 27056164Sru xexit (1); 27121495Sjmacd } 27221495Sjmacd 27342664Smarkm if (clear_screen) 27442664Smarkm { 27542664Smarkm terminal_prep_terminal (); 27642664Smarkm terminal_clear_screen (); 27742664Smarkm } 27842664Smarkm 27921495Sjmacd initialize_info_keymaps (); 28021495Sjmacd window_initialize_windows (screenwidth, screenheight); 28121495Sjmacd initialize_info_signal_handler (); 28221495Sjmacd display_initialize_display (screenwidth, screenheight); 28356164Sru info_set_node_of_window (0, active_window, node); 28421495Sjmacd 28521495Sjmacd /* Tell the window system how to notify us when a window needs to be 28621495Sjmacd asynchronously deleted (e.g., user resizes window very small). */ 287146520Sru window_deletion_notifier = (VFunction *) forget_window_and_nodes; 28821495Sjmacd 28942664Smarkm /* If input has not been redirected yet, make it come from unbuffered 29042664Smarkm standard input. */ 29121495Sjmacd if (!info_input_stream) 29242664Smarkm { 29356164Sru setbuf (stdin, NULL); 29442664Smarkm info_input_stream = stdin; 29542664Smarkm } 29621495Sjmacd 29721495Sjmacd info_windows_initialized_p = 1; 29821495Sjmacd} 29921495Sjmacd 30021495Sjmacd/* Tell Info that input is coming from the file FILENAME. */ 30121495Sjmacdvoid 302146520Sruinfo_set_input_from_file (char *filename) 30321495Sjmacd{ 30421495Sjmacd FILE *stream; 30521495Sjmacd 30656164Sru /* Input may include binary characters. */ 30756164Sru stream = fopen (filename, FOPEN_RBIN); 30821495Sjmacd 30921495Sjmacd if (!stream) 31021495Sjmacd return; 31121495Sjmacd 31221495Sjmacd if ((info_input_stream != (FILE *)NULL) && 31321495Sjmacd (info_input_stream != stdin)) 31421495Sjmacd fclose (info_input_stream); 31521495Sjmacd 31621495Sjmacd info_input_stream = stream; 31721495Sjmacd 31821495Sjmacd if (stream != stdin) 31921495Sjmacd display_inhibited = 1; 32021495Sjmacd} 32121495Sjmacd 32221495Sjmacd/* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */ 32321495Sjmacdstatic INFO_WINDOW * 324146520Sruget_info_window_of_window (WINDOW *window) 32521495Sjmacd{ 32621495Sjmacd register int i; 32721495Sjmacd INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; 32821495Sjmacd 32921495Sjmacd for (i = 0; info_windows && (info_win = info_windows[i]); i++) 33021495Sjmacd if (info_win->window == window) 33121495Sjmacd break; 33221495Sjmacd 33321495Sjmacd return (info_win); 33421495Sjmacd} 33521495Sjmacd 33621495Sjmacd/* Reset the remembered pagetop and point of WINDOW to WINDOW's current 33721495Sjmacd values if the window and node are the same as the current one being 33821495Sjmacd displayed. */ 33921495Sjmacdvoid 340146520Sruset_remembered_pagetop_and_point (WINDOW *window) 34121495Sjmacd{ 34221495Sjmacd INFO_WINDOW *info_win; 34321495Sjmacd 34421495Sjmacd info_win = get_info_window_of_window (window); 34521495Sjmacd 34621495Sjmacd if (!info_win) 34721495Sjmacd return; 34821495Sjmacd 34921495Sjmacd if (info_win->nodes_index && 35021495Sjmacd (info_win->nodes[info_win->current] == window->node)) 35121495Sjmacd { 35221495Sjmacd info_win->pagetops[info_win->current] = window->pagetop; 35321495Sjmacd info_win->points[info_win->current] = window->point; 35421495Sjmacd } 35521495Sjmacd} 35621495Sjmacd 35721495Sjmacdvoid 358146520Sruremember_window_and_node (WINDOW *window, NODE *node) 35921495Sjmacd{ 36021495Sjmacd /* See if we already have this window in our list. */ 36142664Smarkm INFO_WINDOW *info_win = get_info_window_of_window (window); 36221495Sjmacd 36321495Sjmacd /* If the window wasn't already on our list, then make a new entry. */ 36421495Sjmacd if (!info_win) 36521495Sjmacd { 36621495Sjmacd info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW)); 36721495Sjmacd info_win->window = window; 36821495Sjmacd info_win->nodes = (NODE **)NULL; 36921495Sjmacd info_win->pagetops = (int *)NULL; 37021495Sjmacd info_win->points = (long *)NULL; 37121495Sjmacd info_win->current = 0; 37221495Sjmacd info_win->nodes_index = 0; 37321495Sjmacd info_win->nodes_slots = 0; 37421495Sjmacd 37521495Sjmacd add_pointer_to_array (info_win, info_windows_index, info_windows, 37642664Smarkm info_windows_slots, 10, INFO_WINDOW *); 37721495Sjmacd } 37821495Sjmacd 37921495Sjmacd /* If this node, the current pagetop, and the current point are the 38042664Smarkm same as the current saved node and pagetop, don't really add this to 38142664Smarkm the list of history nodes. This may happen only at the very 38242664Smarkm beginning of the program, I'm not sure. --karl */ 38342664Smarkm if (info_win->nodes 38442664Smarkm && info_win->current >= 0 38542664Smarkm && info_win->nodes[info_win->current]->contents == node->contents 38642664Smarkm && info_win->pagetops[info_win->current] == window->pagetop 38742664Smarkm && info_win->points[info_win->current] == window->point) 38842664Smarkm return; 38921495Sjmacd 39021495Sjmacd /* Remember this node, the currently displayed pagetop, and the current 39121495Sjmacd location of point in this window. Because we are updating pagetops 39221495Sjmacd and points as well as nodes, it is more efficient to avoid the 39321495Sjmacd add_pointer_to_array macro here. */ 39421495Sjmacd if (info_win->nodes_index + 2 >= info_win->nodes_slots) 39521495Sjmacd { 39642664Smarkm info_win->nodes_slots += 20; 39742664Smarkm info_win->nodes = (NODE **) xrealloc (info_win->nodes, 39842664Smarkm info_win->nodes_slots * sizeof (NODE *)); 39942664Smarkm info_win->pagetops = (int *) xrealloc (info_win->pagetops, 40042664Smarkm info_win->nodes_slots * sizeof (int)); 40142664Smarkm info_win->points = (long *) xrealloc (info_win->points, 40242664Smarkm info_win->nodes_slots * sizeof (long)); 40321495Sjmacd } 40421495Sjmacd 40521495Sjmacd info_win->nodes[info_win->nodes_index] = node; 40621495Sjmacd info_win->pagetops[info_win->nodes_index] = window->pagetop; 40721495Sjmacd info_win->points[info_win->nodes_index] = window->point; 40821495Sjmacd info_win->current = info_win->nodes_index++; 40942664Smarkm info_win->nodes[info_win->nodes_index] = NULL; 41021495Sjmacd info_win->pagetops[info_win->nodes_index] = 0; 41121495Sjmacd info_win->points[info_win->nodes_index] = 0; 41221495Sjmacd} 41321495Sjmacd 41421495Sjmacd#define DEBUG_FORGET_WINDOW_AND_NODES 41521495Sjmacd#if defined (DEBUG_FORGET_WINDOW_AND_NODES) 41621495Sjmacdstatic void 417146520Sruconsistency_check_info_windows (void) 41821495Sjmacd{ 41921495Sjmacd register int i; 42021495Sjmacd 42121495Sjmacd for (i = 0; i < info_windows_index; i++) 42221495Sjmacd { 42321495Sjmacd WINDOW *win; 42421495Sjmacd 42521495Sjmacd for (win = windows; win; win = win->next) 42642664Smarkm if (win == info_windows[i]->window) 42742664Smarkm break; 42821495Sjmacd 42921495Sjmacd if (!win) 43042664Smarkm abort (); 43121495Sjmacd } 43221495Sjmacd} 43321495Sjmacd#endif /* DEBUG_FORGET_WINDOW_AND_NODES */ 43421495Sjmacd 43521495Sjmacd/* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */ 43621495Sjmacdvoid 437146520Sruforget_window_and_nodes (WINDOW *window) 43821495Sjmacd{ 43921495Sjmacd register int i; 44021495Sjmacd INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; 44121495Sjmacd 44221495Sjmacd for (i = 0; info_windows && (info_win = info_windows[i]); i++) 44321495Sjmacd if (info_win->window == window) 44421495Sjmacd break; 44521495Sjmacd 44621495Sjmacd /* If we found the window to forget, then do so. */ 44721495Sjmacd if (info_win) 44821495Sjmacd { 44921495Sjmacd while (i < info_windows_index) 45042664Smarkm { 45142664Smarkm info_windows[i] = info_windows[i + 1]; 45242664Smarkm i++; 45342664Smarkm } 45421495Sjmacd 45521495Sjmacd info_windows_index--; 45621495Sjmacd info_windows[info_windows_index] = (INFO_WINDOW *)NULL; 45721495Sjmacd 45821495Sjmacd if (info_win->nodes) 45942664Smarkm { 46042664Smarkm /* Free the node structures which held onto internal node contents 46142664Smarkm here. This doesn't free the contents; we have a garbage collector 46242664Smarkm which does that. */ 46342664Smarkm for (i = 0; info_win->nodes[i]; i++) 46442664Smarkm if (internal_info_node_p (info_win->nodes[i])) 46542664Smarkm free (info_win->nodes[i]); 46642664Smarkm free (info_win->nodes); 46721495Sjmacd 46842664Smarkm maybe_free (info_win->pagetops); 46942664Smarkm maybe_free (info_win->points); 47042664Smarkm } 47121495Sjmacd 47221495Sjmacd free (info_win); 47321495Sjmacd } 47421495Sjmacd#if defined (DEBUG_FORGET_WINDOW_AND_NODES) 47521495Sjmacd consistency_check_info_windows (); 47621495Sjmacd#endif /* DEBUG_FORGET_WINDOW_AND_NODES */ 47721495Sjmacd} 47821495Sjmacd 47921495Sjmacd/* Set WINDOW to show NODE. Remember the new window in our list of Info 48021495Sjmacd windows. If we are doing automatic footnote display, also try to display 48156164Sru the footnotes for this window. If REMEMBER is nonzero, first call 48256164Sru set_remembered_pagetop_and_point. */ 48321495Sjmacdvoid 484146520Sruinfo_set_node_of_window (int remember, WINDOW *window, NODE *node) 48521495Sjmacd{ 48656164Sru if (remember) 48756164Sru set_remembered_pagetop_and_point (window); 48856164Sru 48921495Sjmacd /* Put this node into the window. */ 49021495Sjmacd window_set_node_of_window (window, node); 49121495Sjmacd 49221495Sjmacd /* Remember this node and window in our list of info windows. */ 49321495Sjmacd remember_window_and_node (window, node); 49421495Sjmacd 49521495Sjmacd /* If doing auto-footnote display/undisplay, show the footnotes belonging 49621495Sjmacd to this window's node. */ 49721495Sjmacd if (auto_footnotes_p) 49821495Sjmacd info_get_or_remove_footnotes (window); 49921495Sjmacd} 50021495Sjmacd 50121495Sjmacd 50221495Sjmacd/* **************************************************************** */ 50342664Smarkm/* */ 50442664Smarkm/* Info Movement Commands */ 50542664Smarkm/* */ 50621495Sjmacd/* **************************************************************** */ 50721495Sjmacd 50821495Sjmacd/* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen 50921495Sjmacd to do so. */ 51021495Sjmacdvoid 511146520Sruset_window_pagetop (WINDOW *window, int desired_top) 51221495Sjmacd{ 51321495Sjmacd int point_line, old_pagetop; 51421495Sjmacd 51521495Sjmacd if (desired_top < 0) 51621495Sjmacd desired_top = 0; 51721495Sjmacd else if (desired_top > window->line_count) 51821495Sjmacd desired_top = window->line_count - 1; 51921495Sjmacd 52021495Sjmacd if (window->pagetop == desired_top) 52121495Sjmacd return; 52221495Sjmacd 52321495Sjmacd old_pagetop = window->pagetop; 52421495Sjmacd window->pagetop = desired_top; 52521495Sjmacd 52621495Sjmacd /* Make sure that point appears in this window. */ 52721495Sjmacd point_line = window_line_of_point (window); 52821495Sjmacd if ((point_line < window->pagetop) || 52921495Sjmacd ((point_line - window->pagetop) > window->height - 1)) 53021495Sjmacd window->point = 53121495Sjmacd window->line_starts[window->pagetop] - window->node->contents; 53221495Sjmacd 53321495Sjmacd window->flags |= W_UpdateWindow; 53421495Sjmacd 53521495Sjmacd /* Find out which direction to scroll, and scroll the window in that 53621495Sjmacd direction. Do this only if there would be a savings in redisplay 53721495Sjmacd time. This is true if the amount to scroll is less than the height 53821495Sjmacd of the window, and if the number of lines scrolled would be greater 53921495Sjmacd than 10 % of the window's height. */ 54021495Sjmacd if (old_pagetop < desired_top) 54121495Sjmacd { 54221495Sjmacd int start, end, amount; 54321495Sjmacd 54421495Sjmacd amount = desired_top - old_pagetop; 54521495Sjmacd 54621495Sjmacd if ((amount >= window->height) || 54742664Smarkm (((window->height - amount) * 10) < window->height)) 54842664Smarkm return; 54921495Sjmacd 55021495Sjmacd start = amount + window->first_row; 55121495Sjmacd end = window->height + window->first_row; 55221495Sjmacd 55321495Sjmacd display_scroll_display (start, end, -amount); 55421495Sjmacd } 55521495Sjmacd else 55621495Sjmacd { 55721495Sjmacd int start, end, amount; 55821495Sjmacd 55921495Sjmacd amount = old_pagetop - desired_top; 56021495Sjmacd 56121495Sjmacd if ((amount >= window->height) || 56242664Smarkm (((window->height - amount) * 10) < window->height)) 56342664Smarkm return; 56421495Sjmacd 56521495Sjmacd start = window->first_row; 56621495Sjmacd end = (window->first_row + window->height) - amount; 56721495Sjmacd display_scroll_display (start, end, amount); 56821495Sjmacd } 56921495Sjmacd} 57021495Sjmacd 57121495Sjmacd/* Immediately make WINDOW->point visible on the screen, and move the 57221495Sjmacd terminal cursor there. */ 57321495Sjmacdstatic void 574146520Sruinfo_show_point (WINDOW *window) 57521495Sjmacd{ 57621495Sjmacd int old_pagetop; 57721495Sjmacd 57821495Sjmacd old_pagetop = window->pagetop; 57921495Sjmacd window_adjust_pagetop (window); 58021495Sjmacd if (old_pagetop != window->pagetop) 58121495Sjmacd { 58221495Sjmacd int new_pagetop; 58321495Sjmacd 58421495Sjmacd new_pagetop = window->pagetop; 58521495Sjmacd window->pagetop = old_pagetop; 58621495Sjmacd set_window_pagetop (window, new_pagetop); 58721495Sjmacd } 58821495Sjmacd 58921495Sjmacd if (window->flags & W_UpdateWindow) 59021495Sjmacd display_update_one_window (window); 59121495Sjmacd 59221495Sjmacd display_cursor_at_point (window); 59321495Sjmacd} 59421495Sjmacd 59521495Sjmacd/* Move WINDOW->point from OLD line index to NEW line index. */ 59621495Sjmacdstatic void 597146520Srumove_to_new_line (int old, int new, WINDOW *window) 59821495Sjmacd{ 59921495Sjmacd if (old == -1) 60021495Sjmacd { 601146520Sru info_error ((char *) msg_cant_find_point, NULL, NULL); 60221495Sjmacd } 60321495Sjmacd else 60421495Sjmacd { 60521495Sjmacd int goal; 60621495Sjmacd 60721495Sjmacd if (new >= window->line_count || new < 0) 60842664Smarkm return; 60921495Sjmacd 61021495Sjmacd goal = window_get_goal_column (window); 61121495Sjmacd window->goal_column = goal; 61221495Sjmacd 61321495Sjmacd window->point = window->line_starts[new] - window->node->contents; 61421495Sjmacd window->point += window_chars_to_goal (window->line_starts[new], goal); 61521495Sjmacd info_show_point (window); 61621495Sjmacd } 61721495Sjmacd} 61821495Sjmacd 61921495Sjmacd/* Move WINDOW's point down to the next line if possible. */ 62042664SmarkmDECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line")) 62121495Sjmacd{ 62221495Sjmacd int old_line, new_line; 62321495Sjmacd 62421495Sjmacd if (count < 0) 62521495Sjmacd info_prev_line (window, -count, key); 62621495Sjmacd else 62721495Sjmacd { 62821495Sjmacd old_line = window_line_of_point (window); 62921495Sjmacd new_line = old_line + count; 63021495Sjmacd move_to_new_line (old_line, new_line, window); 63121495Sjmacd } 63221495Sjmacd} 63321495Sjmacd 63421495Sjmacd/* Move WINDOW's point up to the previous line if possible. */ 63542664SmarkmDECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line")) 63621495Sjmacd{ 63721495Sjmacd int old_line, new_line; 63821495Sjmacd 63921495Sjmacd if (count < 0) 64021495Sjmacd info_next_line (window, -count, key); 64121495Sjmacd else 64221495Sjmacd { 64321495Sjmacd old_line = window_line_of_point (window); 64421495Sjmacd new_line = old_line - count; 64521495Sjmacd move_to_new_line (old_line, new_line, window); 64621495Sjmacd } 64721495Sjmacd} 64821495Sjmacd 64921495Sjmacd/* Move WINDOW's point to the end of the true line. */ 65042664SmarkmDECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line")) 65121495Sjmacd{ 65221495Sjmacd register int point, len; 65321495Sjmacd register char *buffer; 65421495Sjmacd 65521495Sjmacd buffer = window->node->contents; 65621495Sjmacd len = window->node->nodelen; 65721495Sjmacd 65821495Sjmacd for (point = window->point; 65921495Sjmacd (point < len) && (buffer[point] != '\n'); 66021495Sjmacd point++); 66121495Sjmacd 66221495Sjmacd if (point != window->point) 66321495Sjmacd { 66421495Sjmacd window->point = point; 66521495Sjmacd info_show_point (window); 66621495Sjmacd } 66721495Sjmacd} 66821495Sjmacd 66921495Sjmacd/* Move WINDOW's point to the beginning of the true line. */ 67042664SmarkmDECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line")) 67121495Sjmacd{ 67221495Sjmacd register int point; 67321495Sjmacd register char *buffer; 67421495Sjmacd 67521495Sjmacd buffer = window->node->contents; 67621495Sjmacd point = window->point; 67721495Sjmacd 67821495Sjmacd for (; (point) && (buffer[point - 1] != '\n'); point--); 67921495Sjmacd 68056164Sru /* If at a line start already, do nothing. */ 68121495Sjmacd if (point != window->point) 68221495Sjmacd { 68321495Sjmacd window->point = point; 68421495Sjmacd info_show_point (window); 68521495Sjmacd } 68621495Sjmacd} 68721495Sjmacd 68821495Sjmacd/* Move point forward in the node. */ 68942664SmarkmDECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character")) 69021495Sjmacd{ 69121495Sjmacd if (count < 0) 69221495Sjmacd info_backward_char (window, -count, key); 69321495Sjmacd else 69421495Sjmacd { 69521495Sjmacd window->point += count; 69621495Sjmacd 69721495Sjmacd if (window->point >= window->node->nodelen) 69842664Smarkm window->point = window->node->nodelen - 1; 69921495Sjmacd 70021495Sjmacd info_show_point (window); 70121495Sjmacd } 70221495Sjmacd} 70321495Sjmacd 70421495Sjmacd/* Move point backward in the node. */ 70542664SmarkmDECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character")) 70621495Sjmacd{ 70721495Sjmacd if (count < 0) 70821495Sjmacd info_forward_char (window, -count, key); 70921495Sjmacd else 71021495Sjmacd { 71121495Sjmacd window->point -= count; 71221495Sjmacd 71321495Sjmacd if (window->point < 0) 71442664Smarkm window->point = 0; 71521495Sjmacd 71621495Sjmacd info_show_point (window); 71721495Sjmacd } 71821495Sjmacd} 71921495Sjmacd 72021495Sjmacd#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c)) 72121495Sjmacd 72221495Sjmacd/* Move forward a word in this node. */ 72342664SmarkmDECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word")) 72421495Sjmacd{ 72521495Sjmacd long point; 72621495Sjmacd char *buffer; 72721495Sjmacd int end, c; 72821495Sjmacd 72921495Sjmacd if (count < 0) 73021495Sjmacd { 73121495Sjmacd info_backward_word (window, -count, key); 73221495Sjmacd return; 73321495Sjmacd } 73421495Sjmacd 73521495Sjmacd point = window->point; 73621495Sjmacd buffer = window->node->contents; 73721495Sjmacd end = window->node->nodelen; 73821495Sjmacd 73921495Sjmacd while (count) 74021495Sjmacd { 74121495Sjmacd if (point + 1 >= end) 74242664Smarkm return; 74321495Sjmacd 74421495Sjmacd /* If we are not in a word, move forward until we are in one. 74542664Smarkm Then, move forward until we hit a non-alphabetic character. */ 74621495Sjmacd c = buffer[point]; 74721495Sjmacd 74821495Sjmacd if (!alphabetic (c)) 74942664Smarkm { 75042664Smarkm while (++point < end) 75142664Smarkm { 75242664Smarkm c = buffer[point]; 75342664Smarkm if (alphabetic (c)) 75442664Smarkm break; 75542664Smarkm } 75642664Smarkm } 75721495Sjmacd 75821495Sjmacd if (point >= end) return; 75921495Sjmacd 76021495Sjmacd while (++point < end) 76142664Smarkm { 76242664Smarkm c = buffer[point]; 76342664Smarkm if (!alphabetic (c)) 76442664Smarkm break; 76542664Smarkm } 76621495Sjmacd --count; 76721495Sjmacd } 76821495Sjmacd window->point = point; 76921495Sjmacd info_show_point (window); 77021495Sjmacd} 77121495Sjmacd 77242664SmarkmDECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word")) 77321495Sjmacd{ 77421495Sjmacd long point; 77521495Sjmacd char *buffer; 77621495Sjmacd int c; 77721495Sjmacd 77821495Sjmacd if (count < 0) 77921495Sjmacd { 78021495Sjmacd info_forward_word (window, -count, key); 78121495Sjmacd return; 78221495Sjmacd } 78321495Sjmacd 78421495Sjmacd buffer = window->node->contents; 78521495Sjmacd point = window->point; 78621495Sjmacd 78721495Sjmacd while (count) 78821495Sjmacd { 78921495Sjmacd if (point == 0) 79042664Smarkm break; 79121495Sjmacd 79221495Sjmacd /* Like info_forward_word (), except that we look at the 79342664Smarkm characters just before point. */ 79421495Sjmacd 79521495Sjmacd c = buffer[point - 1]; 79621495Sjmacd 79721495Sjmacd if (!alphabetic (c)) 79842664Smarkm { 79942664Smarkm while (--point) 80042664Smarkm { 80142664Smarkm c = buffer[point - 1]; 80242664Smarkm if (alphabetic (c)) 80342664Smarkm break; 80442664Smarkm } 80542664Smarkm } 80621495Sjmacd 80721495Sjmacd while (point) 80842664Smarkm { 80942664Smarkm c = buffer[point - 1]; 81042664Smarkm if (!alphabetic (c)) 81142664Smarkm break; 81242664Smarkm else 81342664Smarkm --point; 81442664Smarkm } 81521495Sjmacd --count; 81621495Sjmacd } 81721495Sjmacd window->point = point; 81821495Sjmacd info_show_point (window); 81921495Sjmacd} 82021495Sjmacd 82121495Sjmacd/* Variable controlling the behaviour of default scrolling when you are 82221495Sjmacd already at the bottom of a node. Possible values are defined in session.h. 82321495Sjmacd The meanings are: 82421495Sjmacd 82542664Smarkm IS_Continuous Try to get first menu item, or failing that, the 82642664Smarkm "Next:" pointer, or failing that, the "Up:" and 82742664Smarkm "Next:" of the up. 82842664Smarkm IS_NextOnly Try to get "Next:" menu item. 82942664Smarkm IS_PageOnly Simply give up at the bottom of a node. */ 83021495Sjmacd 83121495Sjmacdint info_scroll_behaviour = IS_Continuous; 83221495Sjmacd 83321495Sjmacd/* Choices used by the completer when reading a value for the user-visible 83421495Sjmacd variable "scroll-behaviour". */ 83521495Sjmacdchar *info_scroll_choices[] = { 83621495Sjmacd "Continuous", "Next Only", "Page Only", (char *)NULL 83721495Sjmacd}; 83821495Sjmacd 83956164Sru/* Default window sizes for scrolling commands. */ 84056164Sruint default_window_size = -1; /* meaning 1 window-full */ 84156164Sruint default_scroll_size = -1; /* meaning half screen size */ 84256164Sru 84356164Sru#define INFO_LABEL_FOUND() \ 84456164Sru (info_parsed_nodename || (info_parsed_filename \ 84556164Sru && !is_dir_name (info_parsed_filename))) 84656164Sru 84721495Sjmacd/* Move to 1st menu item, Next, Up/Next, or error in this window. */ 84821495Sjmacdstatic void 849146520Sruforward_move_node_structure (WINDOW *window, int behaviour) 85021495Sjmacd{ 85121495Sjmacd switch (behaviour) 85221495Sjmacd { 85321495Sjmacd case IS_PageOnly: 854146520Sru info_error ((char *) msg_at_node_bottom, NULL, NULL); 85521495Sjmacd break; 85621495Sjmacd 85721495Sjmacd case IS_NextOnly: 85821495Sjmacd info_next_label_of_node (window->node); 85921495Sjmacd if (!info_parsed_nodename && !info_parsed_filename) 860146520Sru info_error ((char *) msg_no_pointer, (char *) _("Next"), NULL); 86121495Sjmacd else 86242664Smarkm { 863146520Sru window_message_in_echo_area ((char *) _("Following Next node..."), 864146520Sru NULL, NULL); 86556164Sru info_handle_pointer ("Next", window); 86642664Smarkm } 86721495Sjmacd break; 86821495Sjmacd 86921495Sjmacd case IS_Continuous: 87021495Sjmacd { 87142664Smarkm /* First things first. If this node contains a menu, move down 87242664Smarkm into the menu. */ 87342664Smarkm { 87442664Smarkm REFERENCE **menu; 87521495Sjmacd 87642664Smarkm menu = info_menu_of_node (window->node); 87721495Sjmacd 87842664Smarkm if (menu) 87942664Smarkm { 88042664Smarkm info_free_references (menu); 881146520Sru window_message_in_echo_area ((char *) _("Selecting first menu item..."), 882146520Sru NULL, NULL); 88342664Smarkm info_menu_digit (window, 1, '1'); 88442664Smarkm return; 88542664Smarkm } 88642664Smarkm } 88721495Sjmacd 88842664Smarkm /* Okay, this node does not contain a menu. If it contains a 88942664Smarkm "Next:" pointer, use that. */ 89042664Smarkm info_next_label_of_node (window->node); 89156164Sru if (INFO_LABEL_FOUND ()) 89242664Smarkm { 893146520Sru window_message_in_echo_area ((char *) _("Selecting Next node..."), 894146520Sru NULL, NULL); 89556164Sru info_handle_pointer ("Next", window); 89642664Smarkm return; 89742664Smarkm } 89821495Sjmacd 89942664Smarkm /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we 90042664Smarkm can move "Next:". If that isn't possible, complain that there 90142664Smarkm are no more nodes. */ 90242664Smarkm { 90342664Smarkm int up_counter, old_current; 90442664Smarkm INFO_WINDOW *info_win; 90521495Sjmacd 90642664Smarkm /* Remember the current node and location. */ 90742664Smarkm info_win = get_info_window_of_window (window); 90842664Smarkm old_current = info_win->current; 90921495Sjmacd 91042664Smarkm /* Back up through the "Up:" pointers until we have found a "Next:" 91142664Smarkm that isn't the same as the first menu item found in that node. */ 91242664Smarkm up_counter = 0; 91342664Smarkm while (!info_error_was_printed) 91442664Smarkm { 91542664Smarkm info_up_label_of_node (window->node); 91656164Sru if (INFO_LABEL_FOUND ()) 91742664Smarkm { 91856164Sru info_handle_pointer ("Up", window); 91942664Smarkm if (info_error_was_printed) 92042664Smarkm continue; 92121495Sjmacd 92242664Smarkm up_counter++; 92321495Sjmacd 92442664Smarkm info_next_label_of_node (window->node); 92521495Sjmacd 92642664Smarkm /* If no "Next" pointer, keep backing up. */ 92756164Sru if (!INFO_LABEL_FOUND ()) 92842664Smarkm continue; 92921495Sjmacd 93042664Smarkm /* If this node's first menu item is the same as this node's 93142664Smarkm Next pointer, keep backing up. */ 93242664Smarkm if (!info_parsed_filename) 93342664Smarkm { 93442664Smarkm REFERENCE **menu; 93542664Smarkm char *next_nodename; 93621495Sjmacd 93742664Smarkm /* Remember the name of the Next node, since reading 93842664Smarkm the menu can overwrite the contents of the 93942664Smarkm info_parsed_xxx strings. */ 94042664Smarkm next_nodename = xstrdup (info_parsed_nodename); 94121495Sjmacd 94242664Smarkm menu = info_menu_of_node (window->node); 94342664Smarkm if (menu && 94442664Smarkm (strcmp 94542664Smarkm (menu[0]->nodename, next_nodename) == 0)) 94642664Smarkm { 94742664Smarkm info_free_references (menu); 94842664Smarkm free (next_nodename); 94942664Smarkm continue; 95042664Smarkm } 95142664Smarkm else 95242664Smarkm { 95342664Smarkm /* Restore the world to where it was before 95442664Smarkm reading the menu contents. */ 95542664Smarkm info_free_references (menu); 95642664Smarkm free (next_nodename); 95742664Smarkm info_next_label_of_node (window->node); 95842664Smarkm } 95942664Smarkm } 96021495Sjmacd 96142664Smarkm /* This node has a "Next" pointer, and it is not the 96242664Smarkm same as the first menu item found in this node. */ 96342664Smarkm window_message_in_echo_area 964146520Sru ((char *) _("Moving Up %d time(s), then Next."), 965146520Sru (void *) (long) up_counter, NULL); 96621495Sjmacd 96756164Sru info_handle_pointer ("Next", window); 96842664Smarkm return; 96942664Smarkm } 97042664Smarkm else 97142664Smarkm { 97242664Smarkm /* No more "Up" pointers. Print an error, and call it 97342664Smarkm quits. */ 97442664Smarkm register int i; 97521495Sjmacd 97642664Smarkm for (i = 0; i < up_counter; i++) 97742664Smarkm { 97842664Smarkm info_win->nodes_index--; 97942664Smarkm free (info_win->nodes[info_win->nodes_index]); 98042664Smarkm info_win->nodes[info_win->nodes_index] = (NODE *)NULL; 98142664Smarkm } 98242664Smarkm info_win->current = old_current; 98342664Smarkm window->node = info_win->nodes[old_current]; 98442664Smarkm window->pagetop = info_win->pagetops[old_current]; 98542664Smarkm window->point = info_win->points[old_current]; 98642664Smarkm recalculate_line_starts (window); 98742664Smarkm window->flags |= W_UpdateWindow; 988146520Sru info_error ((char *) _("No more nodes within this document."), 989146520Sru NULL, NULL); 99042664Smarkm } 99142664Smarkm } 99242664Smarkm } 99342664Smarkm break; 99421495Sjmacd } 99521495Sjmacd } 99621495Sjmacd} 99721495Sjmacd 99821495Sjmacd/* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */ 99921495Sjmacdstatic void 1000146520Srubackward_move_node_structure (WINDOW *window, int behaviour) 100121495Sjmacd{ 100221495Sjmacd switch (behaviour) 100321495Sjmacd { 100421495Sjmacd case IS_PageOnly: 1005146520Sru info_error ((char *) msg_at_node_top, NULL, NULL); 100621495Sjmacd break; 100721495Sjmacd 100821495Sjmacd case IS_NextOnly: 100921495Sjmacd info_prev_label_of_node (window->node); 101021495Sjmacd if (!info_parsed_nodename && !info_parsed_filename) 1011146520Sru info_error ((char *) _("No `Prev' for this node."), NULL, NULL); 101221495Sjmacd else 101342664Smarkm { 1014146520Sru window_message_in_echo_area ((char *) _("Moving Prev in this window."), 1015146520Sru NULL, NULL); 101656164Sru info_handle_pointer ("Prev", window); 101742664Smarkm } 101821495Sjmacd break; 101921495Sjmacd 102021495Sjmacd case IS_Continuous: 102121495Sjmacd info_prev_label_of_node (window->node); 102221495Sjmacd 102356164Sru if (!info_parsed_nodename && (!info_parsed_filename 102456164Sru || is_dir_name (info_parsed_filename))) 102542664Smarkm { 102642664Smarkm info_up_label_of_node (window->node); 102756164Sru if (!info_parsed_nodename && (!info_parsed_filename 102856164Sru || is_dir_name (info_parsed_filename))) 1029146520Sru info_error ((char *) 1030146520Sru _("No `Prev' or `Up' for this node within this document."), 1031146520Sru NULL, NULL); 103242664Smarkm else 103342664Smarkm { 1034146520Sru window_message_in_echo_area ((char *) _("Moving Up in this window."), 1035146520Sru NULL, NULL); 103656164Sru info_handle_pointer ("Up", window); 103742664Smarkm } 103842664Smarkm } 103921495Sjmacd else 104042664Smarkm { 104142664Smarkm REFERENCE **menu; 104242664Smarkm int inhibit_menu_traversing = 0; 104321495Sjmacd 104442664Smarkm /* Watch out! If this node's Prev is the same as the Up, then 104542664Smarkm move Up. Otherwise, we could move Prev, and then to the last 104642664Smarkm menu item in the Prev. This would cause the user to loop 104742664Smarkm through a subsection of the info file. */ 104842664Smarkm if (!info_parsed_filename && info_parsed_nodename) 104942664Smarkm { 105042664Smarkm char *pnode; 105121495Sjmacd 105242664Smarkm pnode = xstrdup (info_parsed_nodename); 105342664Smarkm info_up_label_of_node (window->node); 105421495Sjmacd 105542664Smarkm if (!info_parsed_filename && info_parsed_nodename && 105642664Smarkm strcmp (info_parsed_nodename, pnode) == 0) 105742664Smarkm { 105842664Smarkm /* The nodes are the same. Inhibit moving to the last 105942664Smarkm menu item. */ 106042664Smarkm free (pnode); 106142664Smarkm inhibit_menu_traversing = 1; 106242664Smarkm } 106342664Smarkm else 106442664Smarkm { 106542664Smarkm free (pnode); 106642664Smarkm info_prev_label_of_node (window->node); 106742664Smarkm } 106842664Smarkm } 106921495Sjmacd 107042664Smarkm /* Move to the previous node. If this node now contains a menu, 107142664Smarkm and we have not inhibited movement to it, move to the node 107242664Smarkm corresponding to the last menu item. */ 1073146520Sru window_message_in_echo_area ((char *) _("Moving Prev in this window."), 1074146520Sru NULL, NULL); 107556164Sru info_handle_pointer ("Prev", window); 107621495Sjmacd 107742664Smarkm if (!inhibit_menu_traversing) 107842664Smarkm { 107942664Smarkm while (!info_error_was_printed && 108042664Smarkm (menu = info_menu_of_node (window->node))) 108142664Smarkm { 108242664Smarkm info_free_references (menu); 108342664Smarkm window_message_in_echo_area 1084146520Sru ((char *) _("Moving to `Prev's last menu item."), NULL, NULL); 108542664Smarkm info_menu_digit (window, 1, '0'); 108642664Smarkm } 108742664Smarkm } 108842664Smarkm } 108921495Sjmacd break; 109021495Sjmacd } 109121495Sjmacd} 109221495Sjmacd 109321495Sjmacd/* Move continuously forward through the node structure of this info file. */ 109421495SjmacdDECLARE_INFO_COMMAND (info_global_next_node, 109542664Smarkm _("Move forwards or down through node structure")) 109621495Sjmacd{ 109721495Sjmacd if (count < 0) 109821495Sjmacd info_global_prev_node (window, -count, key); 109921495Sjmacd else 110021495Sjmacd { 110121495Sjmacd while (count && !info_error_was_printed) 110242664Smarkm { 110342664Smarkm forward_move_node_structure (window, IS_Continuous); 110442664Smarkm count--; 110542664Smarkm } 110621495Sjmacd } 110721495Sjmacd} 110821495Sjmacd 110921495Sjmacd/* Move continuously backward through the node structure of this info file. */ 111021495SjmacdDECLARE_INFO_COMMAND (info_global_prev_node, 111142664Smarkm _("Move backwards or up through node structure")) 111221495Sjmacd{ 111321495Sjmacd if (count < 0) 111421495Sjmacd info_global_next_node (window, -count, key); 111521495Sjmacd else 111621495Sjmacd { 111721495Sjmacd while (count && !info_error_was_printed) 111842664Smarkm { 111942664Smarkm backward_move_node_structure (window, IS_Continuous); 112042664Smarkm count--; 112142664Smarkm } 112221495Sjmacd } 112321495Sjmacd} 112421495Sjmacd 1125146520Srustatic void _scroll_forward(WINDOW *window, int count, 1126146520Sru unsigned char key, int behaviour); 1127146520Srustatic void _scroll_backward(WINDOW *window, int count, 1128146520Sru unsigned char key, int behaviour); 112993142Sru 113093142Srustatic void 1131146520Sru_scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour) 113221495Sjmacd{ 113321495Sjmacd if (count < 0) 113493142Sru _scroll_backward (window, -count, key, behaviour); 113521495Sjmacd else 113621495Sjmacd { 113721495Sjmacd int desired_top; 113821495Sjmacd 113921495Sjmacd /* Without an explicit numeric argument, scroll the bottom two 114042664Smarkm lines to the top of this window, Or, if at bottom of window, 114193142Sru and the chosen behaviour is to scroll through nodes get the 114293142Sru "Next" node for this window. */ 114356164Sru if (default_window_size > 0) 114456164Sru desired_top = window->pagetop + default_window_size; 114556164Sru else if (!info_explicit_arg && count == 1) 114642664Smarkm { 114742664Smarkm desired_top = window->pagetop + (window->height - 2); 114821495Sjmacd 114942664Smarkm /* If there are no more lines to scroll here, error, or get 115093142Sru another node, depending on BEHAVIOUR. */ 115142664Smarkm if (desired_top > window->line_count) 115242664Smarkm { 115342664Smarkm forward_move_node_structure (window, behaviour); 115442664Smarkm return; 115542664Smarkm } 115642664Smarkm } 115721495Sjmacd else 115842664Smarkm desired_top = window->pagetop + count; 115921495Sjmacd 116021495Sjmacd if (desired_top >= window->line_count) 116142664Smarkm desired_top = window->line_count - 2; 116221495Sjmacd 116321495Sjmacd if (window->pagetop > desired_top) 116442664Smarkm return; 116521495Sjmacd else 116642664Smarkm set_window_pagetop (window, desired_top); 116721495Sjmacd } 116821495Sjmacd} 116921495Sjmacd 117093142Srustatic void 1171146520Sru_scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour) 117256164Sru{ 117321495Sjmacd if (count < 0) 117493142Sru _scroll_forward (window, -count, key, behaviour); 117521495Sjmacd else 117621495Sjmacd { 117721495Sjmacd int desired_top; 117821495Sjmacd 117921495Sjmacd /* Without an explicit numeric argument, scroll the top two lines 118093142Sru to the bottom of this window, or, depending on the selected 118193142Sru behaviour, move to the previous, or Up'th node. */ 118256164Sru if (default_window_size > 0) 118356164Sru desired_top = window->pagetop - default_window_size; 118456164Sru else if (!info_explicit_arg && count == 1) 118542664Smarkm { 118642664Smarkm desired_top = window->pagetop - (window->height - 2); 118721495Sjmacd 118842664Smarkm if ((desired_top < 0) && (window->pagetop == 0)) 118942664Smarkm { 119042664Smarkm backward_move_node_structure (window, behaviour); 119142664Smarkm return; 119242664Smarkm } 119342664Smarkm } 119421495Sjmacd else 119542664Smarkm desired_top = window->pagetop - count; 119621495Sjmacd 119721495Sjmacd if (desired_top < 0) 119842664Smarkm desired_top = 0; 119921495Sjmacd 120021495Sjmacd set_window_pagetop (window, desired_top); 120121495Sjmacd } 120221495Sjmacd} 120321495Sjmacd 120493142Sru/* Show the next screen of WINDOW's node. */ 120593142SruDECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window")) 120693142Sru{ 120793142Sru _scroll_forward (window, count, key, info_scroll_behaviour); 120893142Sru} 120993142Sru 121093142Sru/* Like info_scroll_forward, but sets default_window_size as a side 121193142Sru effect. */ 121293142SruDECLARE_INFO_COMMAND (info_scroll_forward_set_window, 121393142Sru _("Scroll forward in this window and set default window size")) 121493142Sru{ 121593142Sru if (info_explicit_arg) 121693142Sru default_window_size = count; 121793142Sru _scroll_forward (window, count, key, info_scroll_behaviour); 121893142Sru} 121993142Sru 122093142Sru/* Show the next screen of WINDOW's node but never advance to next node. */ 122193142SruDECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node")) 122293142Sru{ 122393142Sru _scroll_forward (window, count, key, IS_PageOnly); 122493142Sru} 122593142Sru 122693142Sru/* Like info_scroll_forward_page_only, but sets default_window_size as a side 122793142Sru effect. */ 122893142SruDECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window, 122993142Sru _("Scroll forward in this window staying within node and set default window size")) 123093142Sru{ 123193142Sru if (info_explicit_arg) 123293142Sru default_window_size = count; 123393142Sru _scroll_forward (window, count, key, IS_PageOnly); 123493142Sru} 123593142Sru 123693142Sru/* Show the previous screen of WINDOW's node. */ 123793142SruDECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) 123893142Sru{ 123993142Sru _scroll_backward (window, count, key, info_scroll_behaviour); 124093142Sru} 124193142Sru 124256164Sru/* Like info_scroll_backward, but sets default_window_size as a side 124356164Sru effect. */ 124456164SruDECLARE_INFO_COMMAND (info_scroll_backward_set_window, 124556164Sru _("Scroll backward in this window and set default window size")) 124656164Sru{ 124756164Sru if (info_explicit_arg) 124856164Sru default_window_size = count; 124993142Sru _scroll_backward (window, count, key, info_scroll_behaviour); 125056164Sru} 125156164Sru 125293142Sru/* Show the previous screen of WINDOW's node but never move to previous 125393142Sru node. */ 125493142SruDECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node")) 125593142Sru{ 125693142Sru _scroll_backward (window, count, key, IS_PageOnly); 125793142Sru} 125893142Sru 125993142Sru/* Like info_scroll_backward_page_only, but sets default_window_size as a side 126093142Sru effect. */ 126193142SruDECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window, 126293142Sru _("Scroll backward in this window staying within node and set default window size")) 126393142Sru{ 126493142Sru if (info_explicit_arg) 126593142Sru default_window_size = count; 126693142Sru _scroll_backward (window, count, key, IS_PageOnly); 126793142Sru} 126893142Sru 126921495Sjmacd/* Move to the beginning of the node. */ 127042664SmarkmDECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node")) 127121495Sjmacd{ 127221495Sjmacd window->pagetop = window->point = 0; 127321495Sjmacd window->flags |= W_UpdateWindow; 127421495Sjmacd} 127521495Sjmacd 127621495Sjmacd/* Move to the end of the node. */ 127742664SmarkmDECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node")) 127821495Sjmacd{ 127921495Sjmacd window->point = window->node->nodelen - 1; 128021495Sjmacd info_show_point (window); 128121495Sjmacd} 128256164Sru 128356164Sru/* Scroll the window forward by N lines. */ 128456164SruDECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines")) 128556164Sru{ 128656164Sru if (count < 0) 128756164Sru info_up_line (window, -count, key); 128856164Sru else 128956164Sru { 129056164Sru int desired_top = window->pagetop + count; 129156164Sru 129256164Sru if (desired_top >= window->line_count) 129356164Sru desired_top = window->line_count - 2; 129456164Sru 129556164Sru if (window->pagetop <= desired_top) 129656164Sru set_window_pagetop (window, desired_top); 129756164Sru } 129856164Sru} 129956164Sru 130056164Sru/* Scroll the window backward by N lines. */ 130156164SruDECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines")) 130256164Sru{ 130356164Sru if (count < 0) 130456164Sru info_down_line (window, -count, key); 130556164Sru else 130656164Sru { 130756164Sru int desired_top = window->pagetop - count; 130856164Sru 130956164Sru if (desired_top < 0) 131056164Sru desired_top = 0; 131156164Sru 131256164Sru set_window_pagetop (window, desired_top); 131356164Sru } 131456164Sru} 131556164Sru 131656164Sru/* Scroll the window forward by N lines and remember N as default for 131756164Sru subsequent commands. */ 131856164SruDECLARE_INFO_COMMAND (info_scroll_half_screen_down, 131956164Sru _("Scroll down by half screen size")) 132056164Sru{ 132156164Sru if (count < 0) 132293142Sru info_scroll_half_screen_up (window, -count, key); 132356164Sru else 132456164Sru { 132556164Sru int scroll_size = (the_screen->height + 1) / 2; 132656164Sru int desired_top; 132756164Sru 132856164Sru if (info_explicit_arg) 132956164Sru default_scroll_size = count; 133056164Sru if (default_scroll_size > 0) 133156164Sru scroll_size = default_scroll_size; 133256164Sru 133356164Sru desired_top = window->pagetop + scroll_size; 133456164Sru if (desired_top >= window->line_count) 133556164Sru desired_top = window->line_count - 2; 133656164Sru 133756164Sru if (window->pagetop <= desired_top) 133856164Sru set_window_pagetop (window, desired_top); 133956164Sru } 134056164Sru} 134156164Sru 134256164Sru/* Scroll the window backward by N lines and remember N as default for 134356164Sru subsequent commands. */ 134456164SruDECLARE_INFO_COMMAND (info_scroll_half_screen_up, 134556164Sru _("Scroll up by half screen size")) 134656164Sru{ 134756164Sru if (count < 0) 134893142Sru info_scroll_half_screen_down (window, -count, key); 134956164Sru else 135056164Sru { 135156164Sru int scroll_size = (the_screen->height + 1) / 2; 135256164Sru int desired_top; 135356164Sru 135456164Sru if (info_explicit_arg) 135556164Sru default_scroll_size = count; 135656164Sru if (default_scroll_size > 0) 135756164Sru scroll_size = default_scroll_size; 135856164Sru 135956164Sru desired_top = window->pagetop - scroll_size; 136056164Sru if (desired_top < 0) 136156164Sru desired_top = 0; 136256164Sru 136356164Sru set_window_pagetop (window, desired_top); 136456164Sru } 136556164Sru} 136621495Sjmacd 136721495Sjmacd/* **************************************************************** */ 136842664Smarkm/* */ 136942664Smarkm/* Commands for Manipulating Windows */ 137042664Smarkm/* */ 137121495Sjmacd/* **************************************************************** */ 137221495Sjmacd 137321495Sjmacd/* Make the next window in the chain be the active window. */ 137442664SmarkmDECLARE_INFO_COMMAND (info_next_window, _("Select the next window")) 137521495Sjmacd{ 137621495Sjmacd if (count < 0) 137721495Sjmacd { 137821495Sjmacd info_prev_window (window, -count, key); 137921495Sjmacd return; 138021495Sjmacd } 138121495Sjmacd 138221495Sjmacd /* If no other window, error now. */ 138321495Sjmacd if (!windows->next && !echo_area_is_active) 138421495Sjmacd { 1385146520Sru info_error ((char *) msg_one_window, NULL, NULL); 138621495Sjmacd return; 138721495Sjmacd } 138821495Sjmacd 138921495Sjmacd while (count--) 139021495Sjmacd { 139121495Sjmacd if (window->next) 139242664Smarkm window = window->next; 139321495Sjmacd else 139442664Smarkm { 139542664Smarkm if (window == the_echo_area || !echo_area_is_active) 139642664Smarkm window = windows; 139742664Smarkm else 139842664Smarkm window = the_echo_area; 139942664Smarkm } 140021495Sjmacd } 140121495Sjmacd 140221495Sjmacd if (active_window != window) 140321495Sjmacd { 140421495Sjmacd if (auto_footnotes_p) 140542664Smarkm info_get_or_remove_footnotes (window); 140621495Sjmacd 140721495Sjmacd window->flags |= W_UpdateWindow; 140821495Sjmacd active_window = window; 140921495Sjmacd } 141021495Sjmacd} 141121495Sjmacd 141221495Sjmacd/* Make the previous window in the chain be the active window. */ 141342664SmarkmDECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window")) 141421495Sjmacd{ 141521495Sjmacd if (count < 0) 141621495Sjmacd { 141721495Sjmacd info_next_window (window, -count, key); 141821495Sjmacd return; 141921495Sjmacd } 142021495Sjmacd 142121495Sjmacd /* Only one window? */ 142221495Sjmacd 142321495Sjmacd if (!windows->next && !echo_area_is_active) 142421495Sjmacd { 1425146520Sru info_error ((char *) msg_one_window, NULL, NULL); 142621495Sjmacd return; 142721495Sjmacd } 142821495Sjmacd 142921495Sjmacd while (count--) 143021495Sjmacd { 143121495Sjmacd /* If we are in the echo area, or if the echo area isn't active and we 143242664Smarkm are in the first window, find the last window in the chain. */ 143321495Sjmacd if (window == the_echo_area || 143442664Smarkm (window == windows && !echo_area_is_active)) 143542664Smarkm { 1436146520Sru register WINDOW *win, *last = NULL; 143721495Sjmacd 143842664Smarkm for (win = windows; win; win = win->next) 143942664Smarkm last = win; 144021495Sjmacd 144142664Smarkm window = last; 144242664Smarkm } 144321495Sjmacd else 144442664Smarkm { 144542664Smarkm if (window == windows) 144642664Smarkm window = the_echo_area; 144742664Smarkm else 144842664Smarkm window = window->prev; 144942664Smarkm } 145021495Sjmacd } 145121495Sjmacd 145221495Sjmacd if (active_window != window) 145321495Sjmacd { 145421495Sjmacd if (auto_footnotes_p) 145542664Smarkm info_get_or_remove_footnotes (window); 145621495Sjmacd 145721495Sjmacd window->flags |= W_UpdateWindow; 145821495Sjmacd active_window = window; 145921495Sjmacd } 146021495Sjmacd} 146121495Sjmacd 146221495Sjmacd/* Split WINDOW into two windows, both showing the same node. If we 146321495Sjmacd are automatically tiling windows, re-tile after the split. */ 146442664SmarkmDECLARE_INFO_COMMAND (info_split_window, _("Split the current window")) 146521495Sjmacd{ 146621495Sjmacd WINDOW *split, *old_active; 146721495Sjmacd int pagetop; 146821495Sjmacd 146921495Sjmacd /* Remember the current pagetop of the window being split. If it doesn't 147021495Sjmacd change, we can scroll its contents around after the split. */ 147121495Sjmacd pagetop = window->pagetop; 147221495Sjmacd 147321495Sjmacd /* Make the new window. */ 147421495Sjmacd old_active = active_window; 147521495Sjmacd active_window = window; 147621495Sjmacd split = window_make_window (window->node); 147721495Sjmacd active_window = old_active; 147821495Sjmacd 147921495Sjmacd if (!split) 148021495Sjmacd { 1481146520Sru info_error ((char *) msg_win_too_small, NULL, NULL); 148221495Sjmacd } 148321495Sjmacd else 148421495Sjmacd { 148521495Sjmacd#if defined (SPLIT_BEFORE_ACTIVE) 148621495Sjmacd /* Try to scroll the old window into its new postion. */ 148721495Sjmacd if (pagetop == window->pagetop) 148842664Smarkm { 148942664Smarkm int start, end, amount; 149021495Sjmacd 149142664Smarkm start = split->first_row; 149242664Smarkm end = start + window->height; 149342664Smarkm amount = split->height + 1; 149442664Smarkm display_scroll_display (start, end, amount); 149542664Smarkm } 149621495Sjmacd#else /* !SPLIT_BEFORE_ACTIVE */ 149721495Sjmacd /* Make sure point still appears in the active window. */ 149821495Sjmacd info_show_point (window); 149921495Sjmacd#endif /* !SPLIT_BEFORE_ACTIVE */ 150021495Sjmacd 150121495Sjmacd /* If the window just split was one internal to Info, try to display 150242664Smarkm something else in it. */ 150321495Sjmacd if (internal_info_node_p (split->node)) 150442664Smarkm { 150542664Smarkm register int i, j; 150642664Smarkm INFO_WINDOW *iw; 150742664Smarkm NODE *node = (NODE *)NULL; 150842664Smarkm char *filename; 150921495Sjmacd 151042664Smarkm for (i = 0; (iw = info_windows[i]); i++) 151142664Smarkm { 151242664Smarkm for (j = 0; j < iw->nodes_index; j++) 151342664Smarkm if (!internal_info_node_p (iw->nodes[j])) 151442664Smarkm { 151542664Smarkm if (iw->nodes[j]->parent) 151642664Smarkm filename = iw->nodes[j]->parent; 151742664Smarkm else 151842664Smarkm filename = iw->nodes[j]->filename; 151921495Sjmacd 152042664Smarkm node = info_get_node (filename, iw->nodes[j]->nodename); 152142664Smarkm if (node) 152242664Smarkm { 152342664Smarkm window_set_node_of_window (split, node); 152442664Smarkm i = info_windows_index - 1; 152542664Smarkm break; 152642664Smarkm } 152742664Smarkm } 152842664Smarkm } 152942664Smarkm } 153021495Sjmacd split->pagetop = window->pagetop; 153121495Sjmacd 153221495Sjmacd if (auto_tiling_p) 153342664Smarkm window_tile_windows (DONT_TILE_INTERNALS); 153421495Sjmacd else 153542664Smarkm window_adjust_pagetop (split); 153621495Sjmacd 153721495Sjmacd remember_window_and_node (split, split->node); 153821495Sjmacd } 153921495Sjmacd} 154021495Sjmacd 154121495Sjmacd/* Delete WINDOW, forgetting the list of last visited nodes. If we are 154221495Sjmacd automatically displaying footnotes, show or remove the footnotes 154321495Sjmacd window. If we are automatically tiling windows, re-tile after the 154421495Sjmacd deletion. */ 154542664SmarkmDECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window")) 154621495Sjmacd{ 154721495Sjmacd if (!windows->next) 154821495Sjmacd { 1549146520Sru info_error ((char *) msg_cant_kill_last, NULL, NULL); 155021495Sjmacd } 155121495Sjmacd else if (window->flags & W_WindowIsPerm) 155221495Sjmacd { 1553146520Sru info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL); 155421495Sjmacd } 155521495Sjmacd else 155621495Sjmacd { 155721495Sjmacd info_delete_window_internal (window); 155821495Sjmacd 155921495Sjmacd if (auto_footnotes_p) 156042664Smarkm info_get_or_remove_footnotes (active_window); 156121495Sjmacd 156221495Sjmacd if (auto_tiling_p) 156342664Smarkm window_tile_windows (DONT_TILE_INTERNALS); 156421495Sjmacd } 156521495Sjmacd} 156621495Sjmacd 156721495Sjmacd/* Do the physical deletion of WINDOW, and forget this window and 156821495Sjmacd associated nodes. */ 156921495Sjmacdvoid 1570146520Sruinfo_delete_window_internal (WINDOW *window) 157121495Sjmacd{ 157221495Sjmacd if (windows->next && ((window->flags & W_WindowIsPerm) == 0)) 157321495Sjmacd { 157421495Sjmacd /* We not only delete the window from the display, we forget it from 157542664Smarkm our list of remembered windows. */ 157621495Sjmacd forget_window_and_nodes (window); 157721495Sjmacd window_delete_window (window); 157821495Sjmacd 157921495Sjmacd if (echo_area_is_active) 158042664Smarkm echo_area_inform_of_deleted_window (window); 158121495Sjmacd } 158221495Sjmacd} 158321495Sjmacd 158421495Sjmacd/* Just keep WINDOW, deleting all others. */ 158542664SmarkmDECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows")) 158621495Sjmacd{ 158742664Smarkm int num_deleted; /* The number of windows we deleted. */ 158821495Sjmacd int pagetop, start, end; 158921495Sjmacd 159021495Sjmacd /* Remember a few things about this window. We may be able to speed up 159121495Sjmacd redisplay later by scrolling its contents. */ 159221495Sjmacd pagetop = window->pagetop; 159321495Sjmacd start = window->first_row; 159421495Sjmacd end = start + window->height; 159521495Sjmacd 159621495Sjmacd num_deleted = 0; 159721495Sjmacd 159821495Sjmacd while (1) 159921495Sjmacd { 160021495Sjmacd WINDOW *win; 160121495Sjmacd 160221495Sjmacd /* Find an eligible window and delete it. If no eligible windows 160342664Smarkm are found, we are done. A window is eligible for deletion if 160442664Smarkm is it not permanent, and it is not WINDOW. */ 160521495Sjmacd for (win = windows; win; win = win->next) 160642664Smarkm if (win != window && ((win->flags & W_WindowIsPerm) == 0)) 160742664Smarkm break; 160821495Sjmacd 160921495Sjmacd if (!win) 161042664Smarkm break; 161121495Sjmacd 161221495Sjmacd info_delete_window_internal (win); 161321495Sjmacd num_deleted++; 161421495Sjmacd } 161521495Sjmacd 161621495Sjmacd /* Scroll the contents of this window into the right place so that the 161721495Sjmacd user doesn't have to wait any longer than necessary for redisplay. */ 161821495Sjmacd if (num_deleted) 161921495Sjmacd { 162021495Sjmacd int amount; 162121495Sjmacd 162221495Sjmacd amount = (window->first_row - start); 162321495Sjmacd amount -= (window->pagetop - pagetop); 162421495Sjmacd display_scroll_display (start, end, amount); 162521495Sjmacd } 162621495Sjmacd 162721495Sjmacd window->flags |= W_UpdateWindow; 162821495Sjmacd} 162921495Sjmacd 163021495Sjmacd/* Scroll the "other" window of WINDOW. */ 163142664SmarkmDECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window")) 163221495Sjmacd{ 163321495Sjmacd WINDOW *other; 163421495Sjmacd 163521495Sjmacd /* If only one window, give up. */ 163621495Sjmacd if (!windows->next) 163721495Sjmacd { 1638146520Sru info_error ((char *) msg_one_window, NULL, NULL); 163921495Sjmacd return; 164021495Sjmacd } 164121495Sjmacd 164221495Sjmacd other = window->next; 164321495Sjmacd 164421495Sjmacd if (!other) 164521495Sjmacd other = window->prev; 164621495Sjmacd 164721495Sjmacd info_scroll_forward (other, count, key); 164821495Sjmacd} 164921495Sjmacd 165056164Sru/* Scroll the "other" window of WINDOW. */ 165156164SruDECLARE_INFO_COMMAND (info_scroll_other_window_backward, 165256164Sru _("Scroll the other window backward")) 165356164Sru{ 165456164Sru info_scroll_other_window (window, -count, key); 165556164Sru} 165656164Sru 165721495Sjmacd/* Change the size of WINDOW by AMOUNT. */ 165842664SmarkmDECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window")) 165921495Sjmacd{ 166021495Sjmacd window_change_window_height (window, count); 166121495Sjmacd} 166221495Sjmacd 166321495Sjmacd/* When non-zero, tiling takes place automatically when info_split_window 166421495Sjmacd is called. */ 166521495Sjmacdint auto_tiling_p = 0; 166621495Sjmacd 166721495Sjmacd/* Tile all of the visible windows. */ 166821495SjmacdDECLARE_INFO_COMMAND (info_tile_windows, 166942664Smarkm _("Divide the available screen space among the visible windows")) 167021495Sjmacd{ 167121495Sjmacd window_tile_windows (TILE_INTERNALS); 167221495Sjmacd} 167321495Sjmacd 167421495Sjmacd/* Toggle the state of this window's wrapping of lines. */ 167521495SjmacdDECLARE_INFO_COMMAND (info_toggle_wrap, 167642664Smarkm _("Toggle the state of line wrapping in the current window")) 167721495Sjmacd{ 167821495Sjmacd window_toggle_wrap (window); 167921495Sjmacd} 168021495Sjmacd 168121495Sjmacd/* **************************************************************** */ 168242664Smarkm/* */ 168342664Smarkm/* Info Node Commands */ 168442664Smarkm/* */ 168521495Sjmacd/* **************************************************************** */ 168621495Sjmacd 168756164Sru/* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's 168856164Sru filename is not set. */ 168956164Sruchar * 1690146520Srunode_printed_rep (NODE *node) 169156164Sru{ 169256164Sru char *rep; 169356164Sru 169456164Sru if (node->filename) 169556164Sru { 169656164Sru char *filename 169756164Sru = filename_non_directory (node->parent ? node->parent : node->filename); 169856164Sru rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1); 169956164Sru sprintf (rep, "(%s)%s", filename, node->nodename); 170056164Sru } 170156164Sru else 170256164Sru rep = node->nodename; 170356164Sru 170456164Sru return rep; 170556164Sru} 170656164Sru 170756164Sru 170821495Sjmacd/* Using WINDOW for various defaults, select the node referenced by ENTRY 170921495Sjmacd in it. If the node is selected, the window and node are remembered. */ 171021495Sjmacdvoid 1711146520Sruinfo_select_reference (WINDOW *window, REFERENCE *entry) 171221495Sjmacd{ 171321495Sjmacd NODE *node; 171421495Sjmacd char *filename, *nodename, *file_system_error; 171521495Sjmacd 171621495Sjmacd file_system_error = (char *)NULL; 171721495Sjmacd 171821495Sjmacd filename = entry->filename; 171921495Sjmacd if (!filename) 172021495Sjmacd filename = window->node->parent; 172121495Sjmacd if (!filename) 172221495Sjmacd filename = window->node->filename; 172321495Sjmacd 172421495Sjmacd if (filename) 172542664Smarkm filename = xstrdup (filename); 172621495Sjmacd 172721495Sjmacd if (entry->nodename) 172842664Smarkm nodename = xstrdup (entry->nodename); 172921495Sjmacd else 173042664Smarkm nodename = xstrdup ("Top"); 173121495Sjmacd 173221495Sjmacd node = info_get_node (filename, nodename); 173321495Sjmacd 173421495Sjmacd /* Try something a little weird. If the node couldn't be found, and the 173521495Sjmacd reference was of the form "foo::", see if the entry->label can be found 173621495Sjmacd as a file, with a node of "Top". */ 173721495Sjmacd if (!node) 173821495Sjmacd { 173921495Sjmacd if (info_recent_file_error) 174042664Smarkm file_system_error = xstrdup (info_recent_file_error); 174121495Sjmacd 174221495Sjmacd if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0)) 174342664Smarkm { 174442664Smarkm node = info_get_node (entry->label, "Top"); 174542664Smarkm if (!node && info_recent_file_error) 174642664Smarkm { 174742664Smarkm maybe_free (file_system_error); 174842664Smarkm file_system_error = xstrdup (info_recent_file_error); 174942664Smarkm } 175042664Smarkm } 175121495Sjmacd } 175221495Sjmacd 175321495Sjmacd if (!node) 175421495Sjmacd { 175521495Sjmacd if (file_system_error) 1756146520Sru info_error (file_system_error, NULL, NULL); 175721495Sjmacd else 1758146520Sru info_error ((char *) msg_cant_find_node, nodename, NULL); 175921495Sjmacd } 176021495Sjmacd 176121495Sjmacd maybe_free (file_system_error); 176221495Sjmacd maybe_free (filename); 176321495Sjmacd maybe_free (nodename); 176421495Sjmacd 176521495Sjmacd if (node) 176656164Sru info_set_node_of_window (1, window, node); 176721495Sjmacd} 176821495Sjmacd 176921495Sjmacd/* Parse the node specification in LINE using WINDOW to default the filename. 177021495Sjmacd Select the parsed node in WINDOW and remember it, or error if the node 177121495Sjmacd couldn't be found. */ 177221495Sjmacdstatic void 1773146520Sruinfo_parse_and_select (char *line, WINDOW *window) 177421495Sjmacd{ 177521495Sjmacd REFERENCE entry; 177621495Sjmacd 177721495Sjmacd info_parse_node (line, DONT_SKIP_NEWLINES); 177821495Sjmacd 177921495Sjmacd entry.nodename = info_parsed_nodename; 178021495Sjmacd entry.filename = info_parsed_filename; 178121495Sjmacd entry.label = "*info-parse-and-select*"; 178221495Sjmacd 178321495Sjmacd info_select_reference (window, &entry); 178421495Sjmacd} 178521495Sjmacd 178621495Sjmacd/* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME 178721495Sjmacd are previously filled, try to get the node represented by them into 178821495Sjmacd WINDOW. The node should have been pointed to by the LABEL pointer of 178921495Sjmacd WINDOW->node. */ 179021495Sjmacdstatic void 1791146520Sruinfo_handle_pointer (char *label, WINDOW *window) 179221495Sjmacd{ 179321495Sjmacd if (info_parsed_filename || info_parsed_nodename) 179421495Sjmacd { 179521495Sjmacd char *filename, *nodename; 179621495Sjmacd NODE *node; 179721495Sjmacd 179821495Sjmacd filename = nodename = (char *)NULL; 179921495Sjmacd 180021495Sjmacd if (info_parsed_filename) 180142664Smarkm filename = xstrdup (info_parsed_filename); 180221495Sjmacd else 180342664Smarkm { 180442664Smarkm if (window->node->parent) 180542664Smarkm filename = xstrdup (window->node->parent); 180642664Smarkm else if (window->node->filename) 180742664Smarkm filename = xstrdup (window->node->filename); 180842664Smarkm } 180921495Sjmacd 181021495Sjmacd if (info_parsed_nodename) 181142664Smarkm nodename = xstrdup (info_parsed_nodename); 181221495Sjmacd else 181342664Smarkm nodename = xstrdup ("Top"); 181421495Sjmacd 181521495Sjmacd node = info_get_node (filename, nodename); 181621495Sjmacd 181721495Sjmacd if (node) 181842664Smarkm { 181942664Smarkm INFO_WINDOW *info_win; 182021495Sjmacd 182142664Smarkm info_win = get_info_window_of_window (window); 182242664Smarkm if (info_win) 182342664Smarkm { 182442664Smarkm info_win->pagetops[info_win->current] = window->pagetop; 182542664Smarkm info_win->points[info_win->current] = window->point; 182642664Smarkm } 182756164Sru info_set_node_of_window (1, window, node); 182842664Smarkm } 182921495Sjmacd else 183042664Smarkm { 183142664Smarkm if (info_recent_file_error) 1832146520Sru info_error (info_recent_file_error, NULL, NULL); 183342664Smarkm else 1834146520Sru info_error ((char *) msg_cant_file_node, filename, nodename); 183542664Smarkm } 183621495Sjmacd 183721495Sjmacd free (filename); 183821495Sjmacd free (nodename); 183921495Sjmacd } 184021495Sjmacd else 184121495Sjmacd { 1842146520Sru info_error ((char *) msg_no_pointer, label, NULL); 184321495Sjmacd } 184421495Sjmacd} 184521495Sjmacd 184621495Sjmacd/* Make WINDOW display the "Next:" node of the node currently being 184721495Sjmacd displayed. */ 184856164SruDECLARE_INFO_COMMAND (info_next_node, _("Select the Next node")) 184921495Sjmacd{ 185021495Sjmacd info_next_label_of_node (window->node); 185156164Sru info_handle_pointer ("Next", window); 185221495Sjmacd} 185321495Sjmacd 185421495Sjmacd/* Make WINDOW display the "Prev:" node of the node currently being 185521495Sjmacd displayed. */ 185656164SruDECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node")) 185721495Sjmacd{ 185821495Sjmacd info_prev_label_of_node (window->node); 185956164Sru info_handle_pointer ("Prev", window); 186021495Sjmacd} 186121495Sjmacd 186221495Sjmacd/* Make WINDOW display the "Up:" node of the node currently being 186321495Sjmacd displayed. */ 186456164SruDECLARE_INFO_COMMAND (info_up_node, _("Select the Up node")) 186521495Sjmacd{ 186621495Sjmacd info_up_label_of_node (window->node); 186756164Sru info_handle_pointer ("Up", window); 186821495Sjmacd} 186921495Sjmacd 187021495Sjmacd/* Make WINDOW display the last node of this info file. */ 187142664SmarkmDECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file")) 187221495Sjmacd{ 187321495Sjmacd register int i; 187421495Sjmacd FILE_BUFFER *fb = file_buffer_of_window (window); 187521495Sjmacd NODE *node = (NODE *)NULL; 187621495Sjmacd 187721495Sjmacd if (fb && fb->tags) 187821495Sjmacd { 187956164Sru int last_node_tag_idx = -1; 188056164Sru 188156164Sru /* If no explicit argument, or argument of zero, default to the 188256164Sru last node. */ 188356164Sru if (count == 0 || (count == 1 && !info_explicit_arg)) 188456164Sru count = -1; 188556164Sru for (i = 0; count && fb->tags[i]; i++) 188656164Sru if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ 188756164Sru { 188856164Sru count--; 188956164Sru last_node_tag_idx = i; 189056164Sru } 189156164Sru if (count > 0) 189256164Sru i = last_node_tag_idx + 1; 189356164Sru if (i > 0) 189456164Sru node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); 189521495Sjmacd } 189621495Sjmacd 189721495Sjmacd if (!node) 1898146520Sru info_error ((char *) _("This window has no additional nodes"), NULL, NULL); 189921495Sjmacd else 190056164Sru info_set_node_of_window (1, window, node); 190121495Sjmacd} 190221495Sjmacd 190321495Sjmacd/* Make WINDOW display the first node of this info file. */ 190442664SmarkmDECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file")) 190521495Sjmacd{ 190621495Sjmacd FILE_BUFFER *fb = file_buffer_of_window (window); 190721495Sjmacd NODE *node = (NODE *)NULL; 190821495Sjmacd 190956164Sru /* If no explicit argument, or argument of zero, default to the 191056164Sru first node. */ 191156164Sru if (count == 0) 191256164Sru count = 1; 191321495Sjmacd if (fb && fb->tags) 191456164Sru { 191556164Sru register int i; 191656164Sru int last_node_tag_idx = -1; 191721495Sjmacd 191856164Sru for (i = 0; count && fb->tags[i]; i++) 191956164Sru if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ 192056164Sru { 192156164Sru count--; 192256164Sru last_node_tag_idx = i; 192356164Sru } 192456164Sru if (count > 0) 192556164Sru i = last_node_tag_idx + 1; 192656164Sru if (i > 0) 192756164Sru node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); 192856164Sru } 192956164Sru 193021495Sjmacd if (!node) 1931146520Sru info_error ((char *) _("This window has no additional nodes"), NULL, NULL); 193221495Sjmacd else 193356164Sru info_set_node_of_window (1, window, node); 193421495Sjmacd} 193521495Sjmacd 193621495Sjmacd/* Select the last menu item in WINDOW->node. */ 193721495SjmacdDECLARE_INFO_COMMAND (info_last_menu_item, 193842664Smarkm _("Select the last item in this node's menu")) 193921495Sjmacd{ 194021495Sjmacd info_menu_digit (window, 1, '0'); 194121495Sjmacd} 194221495Sjmacd 194321495Sjmacd/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */ 194442664SmarkmDECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item")) 194521495Sjmacd{ 194621495Sjmacd register int i, item; 1947146520Sru register REFERENCE **menu; 194821495Sjmacd 194921495Sjmacd menu = info_menu_of_node (window->node); 195021495Sjmacd 195121495Sjmacd if (!menu) 195221495Sjmacd { 1953146520Sru info_error ((char *) msg_no_menu_node, NULL, NULL); 195421495Sjmacd return; 195521495Sjmacd } 195621495Sjmacd 195721495Sjmacd /* We have the menu. See if there are this many items in it. */ 195821495Sjmacd item = key - '0'; 195921495Sjmacd 196021495Sjmacd /* Special case. Item "0" is the last item in this menu. */ 196121495Sjmacd if (item == 0) 196221495Sjmacd for (i = 0; menu[i + 1]; i++); 196321495Sjmacd else 196421495Sjmacd { 1965146520Sru for (i = 0; menu[i]; i++) 196642664Smarkm if (i == item - 1) 196742664Smarkm break; 196821495Sjmacd } 196921495Sjmacd 197021495Sjmacd if (menu[i]) 1971146520Sru { 1972146520Sru info_select_reference (window, menu[i]); 1973146520Sru if (menu[i]->line_number > 0) 1974146520Sru info_next_line (window, menu[i]->line_number - 1, key); 1975146520Sru } 197621495Sjmacd else 1977146520Sru info_error ((char *) _("There aren't %d items in this menu."), 1978146520Sru (void *) (long) item, NULL); 197921495Sjmacd 198021495Sjmacd info_free_references (menu); 198121495Sjmacd return; 198221495Sjmacd} 198321495Sjmacd 1984116530Sru 1985116530Sru 1986116530Sru/* Return a pointer to the xref in XREF_LIST that is nearest to POS, or 1987116530Sru NULL if XREF_LIST is empty. That is, if POS is within any of the 1988116530Sru given xrefs, return that one. Otherwise, return the one with the 1989116530Sru nearest beginning or end. If there are two that are equidistant, 1990116530Sru prefer the one forward. The return is in newly-allocated memory, 1991116530Sru since the caller frees it. 1992116530Sru 1993116530Sru This is called from info_menu_or_ref_item with XREF_LIST being all 1994116530Sru the xrefs in the node, and POS being point. The ui function that 1995116530Sru starts it all off is select-reference-this-line. 1996116530Sru 1997116530Sru This is not the same logic as in info.el. Info-get-token prefers 1998116530Sru searching backwards to searching forwards, and has a hardwired search 1999116530Sru limit of 200 chars (in Emacs 21.2). */ 2000116530Sru 2001116530Srustatic REFERENCE ** 2002146520Srunearest_xref (REFERENCE **xref_list, long int pos) 2003116530Sru{ 2004116530Sru int this_xref; 2005116530Sru int nearest = -1; 2006116530Sru long best_delta = -1; 2007116530Sru 2008116530Sru for (this_xref = 0; xref_list[this_xref]; this_xref++) 2009116530Sru { 2010116530Sru long delta; 2011116530Sru REFERENCE *xref = xref_list[this_xref]; 2012116530Sru if (xref->start <= pos && pos <= xref->end) 2013116530Sru { /* POS is within this xref, we're done */ 2014116530Sru nearest = this_xref; 2015116530Sru break; 2016116530Sru } 2017116530Sru 2018116530Sru /* See how far POS is from this xref. Take into account the 2019116530Sru `*Note' that begins the xref, since as far as the user is 2020116530Sru concerned, that's where it starts. */ 2021116530Sru delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))), 2022116530Sru labs (pos - xref->end)); 2023116530Sru 2024116530Sru /* It's the <= instead of < that makes us choose the forward xref 2025116530Sru of POS if two are equidistant. Of course, because of all the 2026116530Sru punctuation surrounding xrefs, it's not necessarily obvious 2027116530Sru where one ends. */ 2028116530Sru if (delta <= best_delta || best_delta < 0) 2029116530Sru { 2030116530Sru nearest = this_xref; 2031116530Sru best_delta = delta; 2032116530Sru } 2033116530Sru } 2034116530Sru 2035116530Sru /* Maybe there was no list to search through. */ 2036116530Sru if (nearest < 0) 2037116530Sru return NULL; 2038116530Sru 2039116530Sru /* Ok, we have a nearest xref, make a list of it. */ 2040116530Sru { 2041116530Sru REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2); 2042116530Sru ret[0] = info_copy_reference (xref_list[nearest]); 2043116530Sru ret[1] = NULL; 2044116530Sru return ret; 2045116530Sru } 2046116530Sru} 2047116530Sru 2048116530Sru 204921495Sjmacd/* Read a menu or followed reference from the user defaulting to the 205021495Sjmacd reference found on the current line, and select that node. The 205121495Sjmacd reading is done with completion. BUILDER is the function used 205221495Sjmacd to build the list of references. ASK_P is non-zero if the user 205321495Sjmacd should be prompted, or zero to select the default item. */ 205421495Sjmacdstatic void 2055146520Sruinfo_menu_or_ref_item (WINDOW *window, int count, 2056146520Sru unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p) 205721495Sjmacd{ 205821495Sjmacd char *line; 2059116530Sru REFERENCE *entry; 2060116530Sru REFERENCE *defentry = NULL; 2061116530Sru REFERENCE **menu = (*builder) (window->node); 206221495Sjmacd 206321495Sjmacd if (!menu) 206421495Sjmacd { 206521495Sjmacd if (builder == info_menu_of_node) 2066146520Sru info_error ((char *) msg_no_menu_node, NULL, NULL); 206721495Sjmacd else 2068146520Sru info_error ((char *) msg_no_xref_node, NULL, NULL); 206921495Sjmacd return; 207021495Sjmacd } 207121495Sjmacd 207221495Sjmacd /* Default the selected reference to the one which is on the line that 207321495Sjmacd point is in. */ 207421495Sjmacd { 2075116530Sru REFERENCE **refs = NULL; 2076116530Sru int point_line = window_line_of_point (window); 207721495Sjmacd 207821495Sjmacd if (point_line != -1) 207921495Sjmacd { 208042664Smarkm SEARCH_BINDING binding; 208121495Sjmacd 208242664Smarkm binding.buffer = window->node->contents; 208342664Smarkm binding.start = window->line_starts[point_line] - binding.buffer; 208442664Smarkm if (window->line_starts[point_line + 1]) 208542664Smarkm binding.end = window->line_starts[point_line + 1] - binding.buffer; 208642664Smarkm else 208742664Smarkm binding.end = window->node->nodelen; 208842664Smarkm binding.flags = 0; 208921495Sjmacd 209042664Smarkm if (builder == info_menu_of_node) 209142664Smarkm { 209242664Smarkm if (point_line) 209342664Smarkm { 209442664Smarkm binding.start--; 209542664Smarkm refs = info_menu_items (&binding); 209642664Smarkm } 209742664Smarkm } 209842664Smarkm else 209942664Smarkm { 210021495Sjmacd#if defined (HANDLE_MAN_PAGES) 210142664Smarkm if (window->node->flags & N_IsManPage) 210242664Smarkm refs = manpage_xrefs_in_binding (window->node, &binding); 210342664Smarkm else 210421495Sjmacd#endif /* HANDLE_MAN_PAGES */ 2105116530Sru refs = nearest_xref (menu, window->point); 210642664Smarkm } 210721495Sjmacd 2108116530Sru if (refs && refs[0]) 210942664Smarkm { 2110116530Sru if (strcmp (refs[0]->label, "Menu") != 0 2111116530Sru || builder == info_xrefs_of_node) 211242664Smarkm { 211342664Smarkm int which = 0; 211421495Sjmacd 2115116530Sru /* For xrefs, find the closest reference to point, 2116116530Sru unless we only have one reference (as we will if 2117116530Sru we've called nearest_xref above). It would be better 2118116530Sru to have only one piece of code, but the conditions 2119116530Sru when we call this are tangled. */ 2120116530Sru if (builder == info_xrefs_of_node && refs[1]) 212142664Smarkm { 212242664Smarkm int closest = -1; 212321495Sjmacd 212442664Smarkm for (; refs[which]; which++) 212542664Smarkm { 2126116530Sru if (window->point >= refs[which]->start 2127116530Sru && window->point <= refs[which]->end) 212842664Smarkm { 212942664Smarkm closest = which; 213042664Smarkm break; 213142664Smarkm } 213242664Smarkm else if (window->point < refs[which]->start) 2133116530Sru break; 213442664Smarkm } 2135146520Sru if (which > 0) 2136146520Sru { 2137146520Sru if (closest == -1) 2138146520Sru which--; 2139146520Sru else 2140146520Sru which = closest; 2141146520Sru } 214242664Smarkm } 214321495Sjmacd 214442664Smarkm defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 214542664Smarkm defentry->label = xstrdup (refs[which]->label); 214642664Smarkm defentry->filename = refs[which]->filename; 214742664Smarkm defentry->nodename = refs[which]->nodename; 2148146520Sru defentry->line_number = refs[which]->line_number; 214921495Sjmacd 215042664Smarkm if (defentry->filename) 215142664Smarkm defentry->filename = xstrdup (defentry->filename); 215242664Smarkm if (defentry->nodename) 215342664Smarkm defentry->nodename = xstrdup (defentry->nodename); 215442664Smarkm } 215542664Smarkm info_free_references (refs); 215642664Smarkm } 215721495Sjmacd } 215821495Sjmacd } 215921495Sjmacd 216021495Sjmacd /* If we are going to ask the user a question, do it now. */ 216121495Sjmacd if (ask_p) 216221495Sjmacd { 216321495Sjmacd char *prompt; 216421495Sjmacd 216521495Sjmacd /* Build the prompt string. */ 216621495Sjmacd if (builder == info_menu_of_node) 216742664Smarkm { 216842664Smarkm if (defentry) 2169146520Sru { 2170146520Sru prompt = xmalloc (strlen (defentry->label) 2171146520Sru + strlen (_("Menu item (%s): "))); 2172146520Sru sprintf (prompt, _("Menu item (%s): "), defentry->label); 2173146520Sru } 217442664Smarkm else 2175146520Sru prompt = xstrdup (_("Menu item: ")); 217642664Smarkm } 217721495Sjmacd else 217842664Smarkm { 217942664Smarkm if (defentry) 2180146520Sru { 2181146520Sru prompt = xmalloc (strlen (defentry->label) 2182146520Sru + strlen (_("Follow xref (%s): "))); 2183146520Sru sprintf (prompt, _("Follow xref (%s): "), defentry->label); 2184146520Sru } 218542664Smarkm else 2186146520Sru prompt = xstrdup (_("Follow xref: ")); 218742664Smarkm } 218821495Sjmacd 218921495Sjmacd line = info_read_completing_in_echo_area (window, prompt, menu); 219021495Sjmacd free (prompt); 219121495Sjmacd 219221495Sjmacd window = active_window; 219321495Sjmacd 219421495Sjmacd /* User aborts, just quit. */ 219521495Sjmacd if (!line) 219642664Smarkm { 219742664Smarkm maybe_free (defentry); 219842664Smarkm info_free_references (menu); 219942664Smarkm info_abort_key (window, 0, 0); 220042664Smarkm return; 220142664Smarkm } 220221495Sjmacd 220321495Sjmacd /* If we had a default and the user accepted it, use that. */ 220421495Sjmacd if (!*line) 220542664Smarkm { 220642664Smarkm free (line); 220742664Smarkm if (defentry) 220842664Smarkm line = xstrdup (defentry->label); 220942664Smarkm else 221042664Smarkm line = (char *)NULL; 221142664Smarkm } 221221495Sjmacd } 221321495Sjmacd else 221421495Sjmacd { 221521495Sjmacd /* Not going to ask any questions. If we have a default entry, use 221642664Smarkm that, otherwise return. */ 221721495Sjmacd if (!defentry) 221842664Smarkm return; 221921495Sjmacd else 222042664Smarkm line = xstrdup (defentry->label); 222121495Sjmacd } 222221495Sjmacd 222321495Sjmacd if (line) 222421495Sjmacd { 222593142Sru /* It is possible that the references have more than a single 222693142Sru entry with the same label, and also LINE is down-cased, which 222793142Sru complicates matters even more. Try to be as accurate as we 222893142Sru can: if they've chosen the default, use defentry directly. */ 222993142Sru if (defentry && strcmp (line, defentry->label) == 0) 223093142Sru entry = defentry; 223193142Sru else 223293142Sru /* Find the selected label in the references. If there are 223393142Sru more than one label which matches, find the one that's 223493142Sru closest to point. */ 223593142Sru { 223693142Sru register int i; 223793142Sru int best = -1, min_dist = window->node->nodelen; 223893142Sru REFERENCE *ref; 223921495Sjmacd 224093142Sru for (i = 0; menu && (ref = menu[i]); i++) 224193142Sru { 224293142Sru /* Need to use strcasecmp because LINE is downcased 224393142Sru inside info_read_completing_in_echo_area. */ 224493142Sru if (strcasecmp (line, ref->label) == 0) 224593142Sru { 224693142Sru /* ref->end is more accurate estimate of position 224793142Sru for menus than ref->start. Go figure. */ 224893142Sru int dist = abs (window->point - ref->end); 224993142Sru 225093142Sru if (dist < min_dist) 225193142Sru { 225293142Sru min_dist = dist; 225393142Sru best = i; 225493142Sru } 225593142Sru } 225693142Sru } 225793142Sru if (best != -1) 225893142Sru entry = menu[best]; 225993142Sru else 226093142Sru entry = (REFERENCE *)NULL; 226193142Sru } 226293142Sru 226321495Sjmacd if (!entry && defentry) 2264146520Sru info_error ((char *) _("The reference disappeared! (%s)."), line, NULL); 226521495Sjmacd else 226642664Smarkm { 226756164Sru NODE *orig = window->node; 226842664Smarkm info_select_reference (window, entry); 2269146520Sru 227056164Sru if (builder == info_xrefs_of_node && window->node != orig 227156164Sru && !(window->node->flags & N_FromAnchor)) 227256164Sru { /* Search for this reference in the node. */ 227342664Smarkm long offset; 227442664Smarkm long start; 227521495Sjmacd 227642664Smarkm if (window->line_count > 0) 227742664Smarkm start = window->line_starts[1] - window->node->contents; 227842664Smarkm else 227942664Smarkm start = 0; 228021495Sjmacd 228142664Smarkm offset = 228242664Smarkm info_target_search_node (window->node, entry->label, start); 228321495Sjmacd 228442664Smarkm if (offset != -1) 228542664Smarkm { 228642664Smarkm window->point = offset; 228742664Smarkm window_adjust_pagetop (window); 228842664Smarkm } 228942664Smarkm } 2290146520Sru 2291146520Sru if (entry->line_number > 0) 2292146520Sru /* next_line starts at line 1? Anyway, the -1 makes it 2293146520Sru move to the right line. */ 2294146520Sru info_next_line (window, entry->line_number - 1, key); 229542664Smarkm } 229621495Sjmacd 229721495Sjmacd free (line); 229821495Sjmacd if (defentry) 229942664Smarkm { 230042664Smarkm free (defentry->label); 230142664Smarkm maybe_free (defentry->filename); 230242664Smarkm maybe_free (defentry->nodename); 230342664Smarkm free (defentry); 230442664Smarkm } 230521495Sjmacd } 230621495Sjmacd 230721495Sjmacd info_free_references (menu); 230821495Sjmacd 230921495Sjmacd if (!info_error_was_printed) 231021495Sjmacd window_clear_echo_area (); 231121495Sjmacd} 231221495Sjmacd 231321495Sjmacd/* Read a line (with completion) which is the name of a menu item, 231421495Sjmacd and select that item. */ 231542664SmarkmDECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node")) 231621495Sjmacd{ 231721495Sjmacd info_menu_or_ref_item (window, count, key, info_menu_of_node, 1); 231821495Sjmacd} 231921495Sjmacd 232021495Sjmacd/* Read a line (with completion) which is the name of a reference to 232121495Sjmacd follow, and select the node. */ 232221495SjmacdDECLARE_INFO_COMMAND 232342664Smarkm (info_xref_item, _("Read a footnote or cross reference and select its node")) 232421495Sjmacd{ 232521495Sjmacd info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1); 232621495Sjmacd} 232721495Sjmacd 232821495Sjmacd/* Position the cursor at the start of this node's menu. */ 232942664SmarkmDECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu")) 233021495Sjmacd{ 233121495Sjmacd SEARCH_BINDING binding; 233221495Sjmacd long position; 233321495Sjmacd 233421495Sjmacd binding.buffer = window->node->contents; 233521495Sjmacd binding.start = 0; 233621495Sjmacd binding.end = window->node->nodelen; 233721495Sjmacd binding.flags = S_FoldCase | S_SkipDest; 233821495Sjmacd 233921495Sjmacd position = search (INFO_MENU_LABEL, &binding); 234021495Sjmacd 234121495Sjmacd if (position == -1) 2342146520Sru info_error ((char *) msg_no_menu_node, NULL, NULL); 234321495Sjmacd else 234421495Sjmacd { 234521495Sjmacd window->point = position; 234621495Sjmacd window_adjust_pagetop (window); 234721495Sjmacd window->flags |= W_UpdateWindow; 234821495Sjmacd } 234921495Sjmacd} 235021495Sjmacd 235121495Sjmacd/* Visit as many menu items as is possible, each in a separate window. */ 235221495SjmacdDECLARE_INFO_COMMAND (info_visit_menu, 235342664Smarkm _("Visit as many menu items at once as possible")) 235421495Sjmacd{ 235521495Sjmacd register int i; 235621495Sjmacd REFERENCE *entry, **menu; 235721495Sjmacd 235821495Sjmacd menu = info_menu_of_node (window->node); 235921495Sjmacd 236021495Sjmacd if (!menu) 2361146520Sru info_error ((char *) msg_no_menu_node, NULL, NULL); 236221495Sjmacd 236321495Sjmacd for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++) 236421495Sjmacd { 236521495Sjmacd WINDOW *new; 236621495Sjmacd 236721495Sjmacd new = window_make_window (window->node); 236821495Sjmacd window_tile_windows (TILE_INTERNALS); 236921495Sjmacd 237021495Sjmacd if (!new) 2371146520Sru info_error ((char *) msg_win_too_small, NULL, NULL); 237221495Sjmacd else 237342664Smarkm { 237442664Smarkm active_window = new; 237542664Smarkm info_select_reference (new, entry); 237642664Smarkm } 237721495Sjmacd } 237821495Sjmacd} 237921495Sjmacd 238021495Sjmacd/* Read a line of input which is a node name, and go to that node. */ 238142664SmarkmDECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it")) 238221495Sjmacd{ 238321495Sjmacd char *line; 238421495Sjmacd 238521495Sjmacd#define GOTO_COMPLETES 238621495Sjmacd#if defined (GOTO_COMPLETES) 238721495Sjmacd /* Build a completion list of all of the known nodes. */ 238821495Sjmacd { 238921495Sjmacd register int fbi, i; 239021495Sjmacd FILE_BUFFER *current; 239121495Sjmacd REFERENCE **items = (REFERENCE **)NULL; 239221495Sjmacd int items_index = 0; 239321495Sjmacd int items_slots = 0; 239421495Sjmacd 239521495Sjmacd current = file_buffer_of_window (window); 239621495Sjmacd 239721495Sjmacd for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++) 239821495Sjmacd { 239942664Smarkm FILE_BUFFER *fb; 240042664Smarkm REFERENCE *entry; 240142664Smarkm int this_is_the_current_fb; 240221495Sjmacd 240342664Smarkm fb = info_loaded_files[fbi]; 240442664Smarkm this_is_the_current_fb = (current == fb); 240521495Sjmacd 240642664Smarkm entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 240742664Smarkm entry->filename = entry->nodename = (char *)NULL; 240842664Smarkm entry->label = (char *)xmalloc (4 + strlen (fb->filename)); 240942664Smarkm sprintf (entry->label, "(%s)*", fb->filename); 241021495Sjmacd 241142664Smarkm add_pointer_to_array 241242664Smarkm (entry, items_index, items, items_slots, 10, REFERENCE *); 241321495Sjmacd 241442664Smarkm if (fb->tags) 241542664Smarkm { 241642664Smarkm for (i = 0; fb->tags[i]; i++) 241742664Smarkm { 241842664Smarkm entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 241942664Smarkm entry->filename = entry->nodename = (char *)NULL; 242056164Sru if (this_is_the_current_fb) 242156164Sru entry->label = xstrdup (fb->tags[i]->nodename); 242256164Sru else 242356164Sru { 242456164Sru entry->label = (char *) xmalloc 242556164Sru (4 + strlen (fb->filename) + 242656164Sru strlen (fb->tags[i]->nodename)); 242756164Sru sprintf (entry->label, "(%s)%s", 242856164Sru fb->filename, fb->tags[i]->nodename); 242956164Sru } 243021495Sjmacd 243142664Smarkm add_pointer_to_array 243242664Smarkm (entry, items_index, items, items_slots, 100, REFERENCE *); 243342664Smarkm } 243442664Smarkm } 243521495Sjmacd } 2436146520Sru line = info_read_maybe_completing (window, (char *) _("Goto node: "), 2437146520Sru items); 243821495Sjmacd info_free_references (items); 243921495Sjmacd } 244021495Sjmacd#else /* !GOTO_COMPLETES */ 2441146520Sru line = info_read_in_echo_area (window, (char *) _("Goto node: ")); 244221495Sjmacd#endif /* !GOTO_COMPLETES */ 244321495Sjmacd 244421495Sjmacd /* If the user aborted, quit now. */ 244521495Sjmacd if (!line) 244621495Sjmacd { 244721495Sjmacd info_abort_key (window, 0, 0); 244821495Sjmacd return; 244921495Sjmacd } 245021495Sjmacd 245121495Sjmacd canonicalize_whitespace (line); 245221495Sjmacd 245321495Sjmacd if (*line) 245421495Sjmacd info_parse_and_select (line, window); 245521495Sjmacd 245621495Sjmacd free (line); 245721495Sjmacd if (!info_error_was_printed) 245821495Sjmacd window_clear_echo_area (); 245921495Sjmacd} 246056164Sru 246156164Sru/* Follow the menu list in MENUS (list of strings terminated by a NULL 246256164Sru entry) from INITIAL_NODE. If can't continue at any point (no menu or 246356164Sru no menu entry for the next item), return the node so far -- that 246456164Sru might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will 246556164Sru be set to the error message and argument for message, otherwise they 246656164Sru will be NULL. */ 246721495Sjmacd 246856164SruNODE * 2469146520Sruinfo_follow_menus (NODE *initial_node, char **menus, 2470146520Sru const char **errstr, char **errarg1, char **errarg2) 247156164Sru{ 247256164Sru NODE *node = NULL; 247356164Sru *errstr = *errarg1 = *errarg2 = NULL; 247456164Sru 247556164Sru for (; *menus; menus++) 247656164Sru { 247756164Sru static char *first_arg = NULL; 247856164Sru REFERENCE **menu; 247956164Sru REFERENCE *entry; 248056164Sru char *arg = *menus; /* Remember the name of the menu entry we want. */ 248156164Sru 248256164Sru /* A leading space is certainly NOT part of a node name. Most 248356164Sru probably, they typed a space after the separating comma. The 248456164Sru strings in menus[] have their whitespace canonicalized, so 248556164Sru there's at most one space to ignore. */ 248656164Sru if (*arg == ' ') 248756164Sru arg++; 248856164Sru if (!first_arg) 248956164Sru first_arg = arg; 249056164Sru 249156164Sru /* Build and return a list of the menu items in this node. */ 249256164Sru menu = info_menu_of_node (initial_node); 249356164Sru 249456164Sru /* If no menu item in this node, stop here, but let the user 249556164Sru continue to use Info. Perhaps they wanted this node and didn't 249656164Sru realize it. */ 249756164Sru if (!menu) 249856164Sru { 249956164Sru if (arg == first_arg) 250056164Sru { 250156164Sru node = make_manpage_node (first_arg); 250256164Sru if (node) 250356164Sru goto maybe_got_node; 250456164Sru } 250556164Sru *errstr = _("No menu in node `%s'."); 250656164Sru *errarg1 = node_printed_rep (initial_node); 250756164Sru return initial_node; 250856164Sru } 250956164Sru 251056164Sru /* Find the specified menu item. */ 251156164Sru entry = info_get_labeled_reference (arg, menu); 251256164Sru 251356164Sru /* If the item wasn't found, search the list sloppily. Perhaps this 251456164Sru user typed "buffer" when they really meant "Buffers". */ 251556164Sru if (!entry) 251656164Sru { 251756164Sru int i; 251856164Sru int best_guess = -1; 251956164Sru 252056164Sru for (i = 0; (entry = menu[i]); i++) 252156164Sru { 252256164Sru if (strcasecmp (entry->label, arg) == 0) 252356164Sru break; 252456164Sru else 2525146520Sru if ((best_guess == -1) 2526146520Sru && (strncasecmp (entry->label, arg, strlen (arg)) == 0)) 252756164Sru best_guess = i; 252856164Sru } 252956164Sru 253056164Sru if (!entry && best_guess != -1) 253156164Sru entry = menu[best_guess]; 253256164Sru } 253356164Sru 253456164Sru /* If we still failed to find the reference, start Info with the current 253556164Sru node anyway. It is probably a misspelling. */ 253656164Sru if (!entry) 253756164Sru { 253856164Sru if (arg == first_arg) 253956164Sru { 254093142Sru /* Maybe they typed "info foo" instead of "info -f foo". */ 254193142Sru node = info_get_node (first_arg, 0); 254293142Sru if (node) 254393142Sru add_file_directory_to_path (first_arg); 254493142Sru else 254593142Sru node = make_manpage_node (first_arg); 254656164Sru if (node) 254756164Sru goto maybe_got_node; 254856164Sru } 254956164Sru 255056164Sru info_free_references (menu); 255156164Sru *errstr = _("No menu item `%s' in node `%s'."); 255256164Sru *errarg1 = arg; 255356164Sru *errarg2 = node_printed_rep (initial_node); 255456164Sru return initial_node; 255556164Sru } 255656164Sru 255756164Sru /* We have found the reference that the user specified. If no 255856164Sru filename in this reference, define it. */ 255956164Sru if (!entry->filename) 256056164Sru entry->filename = xstrdup (initial_node->parent ? initial_node->parent 256156164Sru : initial_node->filename); 256256164Sru 256356164Sru /* Try to find this node. */ 256456164Sru node = info_get_node (entry->filename, entry->nodename); 256556164Sru if (!node && arg == first_arg) 256656164Sru { 256756164Sru node = make_manpage_node (first_arg); 256856164Sru if (node) 256956164Sru goto maybe_got_node; 257056164Sru } 257156164Sru 257256164Sru /* Since we cannot find it, try using the label of the entry as a 257356164Sru file, i.e., "(LABEL)Top". */ 257456164Sru if (!node && entry->nodename 257556164Sru && strcmp (entry->label, entry->nodename) == 0) 257656164Sru node = info_get_node (entry->label, "Top"); 257756164Sru 257856164Sru maybe_got_node: 257956164Sru if (!node) 258056164Sru { 258156164Sru *errstr = _("Unable to find node referenced by `%s' in `%s'."); 258256164Sru *errarg1 = xstrdup (entry->label); 258356164Sru *errarg2 = node_printed_rep (initial_node); 258456164Sru info_free_references (menu); 258556164Sru return initial_node; 258656164Sru } 258756164Sru 258856164Sru info_free_references (menu); 258956164Sru 259056164Sru /* Success. Go round the loop again. */ 259156164Sru free (initial_node); 259256164Sru initial_node = node; 259356164Sru } 259456164Sru 259556164Sru return initial_node; 259656164Sru} 259756164Sru 259856164Sru/* Split STR into individual node names by writing null bytes in wherever 259956164Sru there are commas and constructing a list of the resulting pointers. 260056164Sru (We can do this since STR has had canonicalize_whitespace called on it.) 260156164Sru Return array terminated with NULL. */ 260256164Sru 260356164Srustatic char ** 2604146520Srusplit_list_of_nodenames (char *str) 260556164Sru{ 260656164Sru unsigned len = 2; 260756164Sru char **nodes = xmalloc (len * sizeof (char *)); 260856164Sru 260956164Sru nodes[len - 2] = str; 261056164Sru 261156164Sru while (*str++) 261256164Sru { 261356164Sru if (*str == ',') 261456164Sru { 261556164Sru *str++ = 0; /* get past the null byte */ 261656164Sru len++; 261756164Sru nodes = xrealloc (nodes, len * sizeof (char *)); 261856164Sru nodes[len - 2] = str; 261956164Sru } 262056164Sru } 262156164Sru 262256164Sru nodes[len - 1] = NULL; 262356164Sru 262456164Sru return nodes; 262556164Sru} 262656164Sru 262756164Sru 262856164Sru/* Read a line of input which is a sequence of menus (starting from 262956164Sru dir), and follow them. */ 263056164SruDECLARE_INFO_COMMAND (info_menu_sequence, 263156164Sru _("Read a list of menus starting from dir and follow them")) 263256164Sru{ 2633146520Sru char *line = info_read_in_echo_area (window, (char *) _("Follow menus: ")); 263456164Sru 263556164Sru /* If the user aborted, quit now. */ 263656164Sru if (!line) 263756164Sru { 263856164Sru info_abort_key (window, 0, 0); 263956164Sru return; 264056164Sru } 264156164Sru 264256164Sru canonicalize_whitespace (line); 264356164Sru 264456164Sru if (*line) 264556164Sru { 2646146520Sru const char *errstr; 2647146520Sru char *errarg1, *errarg2; 264856164Sru NODE *dir_node = info_get_node (NULL, NULL); 264956164Sru char **nodes = split_list_of_nodenames (line); 2650146520Sru NODE *node = NULL; 265156164Sru 265256164Sru /* If DIR_NODE is NULL, they might be reading a file directly, 265356164Sru like in "info -d . -f ./foo". Try using "Top" instead. */ 265456164Sru if (!dir_node) 265556164Sru { 265656164Sru char *file_name = window->node->parent; 265756164Sru 265856164Sru if (!file_name) 265956164Sru file_name = window->node->filename; 266056164Sru dir_node = info_get_node (file_name, NULL); 266156164Sru } 266256164Sru 266356164Sru /* If we still cannot find the starting point, give up. 266456164Sru We cannot allow a NULL pointer inside info_follow_menus. */ 266556164Sru if (!dir_node) 2666146520Sru info_error ((char *) msg_cant_find_node, "Top", NULL); 266756164Sru else 2668146520Sru node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2); 266956164Sru 267056164Sru free (nodes); 267156164Sru if (!errstr) 267256164Sru info_set_node_of_window (1, window, node); 267356164Sru else 2674146520Sru info_error ((char *) errstr, errarg1, errarg2); 267556164Sru } 267656164Sru 267756164Sru free (line); 267856164Sru if (!info_error_was_printed) 267956164Sru window_clear_echo_area (); 268056164Sru} 268156164Sru 268256164Sru/* Search the menu MENU for a (possibly mis-spelled) entry ARG. 268356164Sru Return the menu entry, or the best guess for what they meant by ARG, 268456164Sru or NULL if there's nothing in this menu seems to fit the bill. 268556164Sru If EXACT is non-zero, allow only exact matches. */ 268656164Srustatic REFERENCE * 2687146520Sruentry_in_menu (char *arg, REFERENCE **menu, int exact) 268856164Sru{ 268956164Sru REFERENCE *entry; 269056164Sru 269156164Sru /* First, try to find the specified menu item verbatim. */ 269256164Sru entry = info_get_labeled_reference (arg, menu); 269356164Sru 269456164Sru /* If the item wasn't found, search the list sloppily. Perhaps we 269556164Sru have "Option Summary", but ARG is "option". */ 269656164Sru if (!entry && !exact) 269756164Sru { 269856164Sru int i; 269956164Sru int best_guess = -1; 270056164Sru 270156164Sru for (i = 0; (entry = menu[i]); i++) 270256164Sru { 270356164Sru if (strcasecmp (entry->label, arg) == 0) 270456164Sru break; 270556164Sru else 270656164Sru if (strncasecmp (entry->label, arg, strlen (arg)) == 0) 270756164Sru best_guess = i; 270856164Sru } 270956164Sru 271056164Sru if (!entry && best_guess != -1) 271156164Sru entry = menu[best_guess]; 271256164Sru } 271356164Sru 271456164Sru return entry; 271556164Sru} 271656164Sru 271756164Sru/* Find the node that is the best candidate to list the PROGRAM's 271856164Sru invocation info and its command-line options, by looking for menu 271956164Sru items and chains of menu items with characteristic names. */ 272056164Sruvoid 2721146520Sruinfo_intuit_options_node (WINDOW *window, NODE *initial_node, char *program) 272256164Sru{ 272356164Sru /* The list of node names typical for GNU manuals where the program 272456164Sru usage and specifically the command-line arguments are described. 272556164Sru This is pure heuristics. I gathered these node names by looking 272656164Sru at all the Info files I could put my hands on. If you are 272756164Sru looking for evidence to complain to the GNU project about 272856164Sru non-uniform style of documentation, here you have your case! */ 272956164Sru static const char *invocation_nodes[] = { 273056164Sru "%s invocation", 273156164Sru "Invoking %s", 273256164Sru "Preliminaries", /* m4 has Invoking under Preliminaries! */ 273356164Sru "Invocation", 273456164Sru "Command Arguments",/* Emacs */ 273556164Sru "Invoking `%s'", 273656164Sru "%s options", 273756164Sru "Options", 273856164Sru "Option ", /* e.g. "Option Summary" */ 273956164Sru "Invoking", 274056164Sru "All options", /* tar, paxutils */ 274156164Sru "Arguments", 274256164Sru "%s cmdline", /* ar */ 274356164Sru "%s", /* last resort */ 274456164Sru (const char *)0 274556164Sru }; 274656164Sru NODE *node = NULL; 274756164Sru REFERENCE **menu; 274856164Sru const char **try_node; 274956164Sru 275056164Sru /* We keep looking deeper and deeper in the menu structure until 275156164Sru there are no more menus or no menu items from the above list. 275256164Sru Some manuals have the invocation node sitting 3 or 4 levels deep 275356164Sru in the menu hierarchy... */ 275456164Sru for (node = initial_node; node; initial_node = node) 275556164Sru { 2756146520Sru REFERENCE *entry = NULL; 275756164Sru 275856164Sru /* Build and return a list of the menu items in this node. */ 275956164Sru menu = info_menu_of_node (initial_node); 276056164Sru 276156164Sru /* If no menu item in this node, stop here. Perhaps this node 276256164Sru is the one they need. */ 276356164Sru if (!menu) 276456164Sru break; 276556164Sru 276656164Sru /* Look for node names typical for usage nodes in this menu. */ 276756164Sru for (try_node = invocation_nodes; *try_node; try_node++) 276856164Sru { 2769146520Sru char *nodename; 277056164Sru 2771146520Sru nodename = xmalloc (strlen (program) + strlen (*try_node)); 277256164Sru sprintf (nodename, *try_node, program); 277356164Sru /* The last resort "%s" is dangerous, so we restrict it 277456164Sru to exact matches here. */ 277556164Sru entry = entry_in_menu (nodename, menu, 277656164Sru strcmp (*try_node, "%s") == 0); 2777146520Sru free (nodename); 277856164Sru if (entry) 277956164Sru break; 278056164Sru } 278156164Sru 278256164Sru if (!entry) 278356164Sru break; 278456164Sru 278556164Sru if (!entry->filename) 278656164Sru entry->filename = xstrdup (initial_node->parent ? initial_node->parent 278756164Sru : initial_node->filename); 278856164Sru /* Try to find this node. */ 278956164Sru node = info_get_node (entry->filename, entry->nodename); 279056164Sru info_free_references (menu); 279156164Sru if (!node) 279256164Sru break; 279356164Sru } 279456164Sru 279556164Sru /* We've got our best shot at the invocation node. Now select it. */ 279656164Sru if (initial_node) 279756164Sru info_set_node_of_window (1, window, initial_node); 279856164Sru if (!info_error_was_printed) 279956164Sru window_clear_echo_area (); 280056164Sru} 280156164Sru 280256164Sru/* Given a name of an Info file, find the name of the package it 280356164Sru describes by removing the leading directories and extensions. */ 280456164Sruchar * 2805146520Sruprogram_name_from_file_name (char *file_name) 280656164Sru{ 280756164Sru int i; 280856164Sru char *program_name = xstrdup (filename_non_directory (file_name)); 280956164Sru 281056164Sru for (i = strlen (program_name) - 1; i > 0; i--) 281156164Sru if (program_name[i] == '.' 281256164Sru && (FILENAME_CMPN (program_name + i, ".info", 5) == 0 281356164Sru || FILENAME_CMPN (program_name + i, ".inf", 4) == 0 281456164Sru#ifdef __MSDOS__ 281556164Sru || FILENAME_CMPN (program_name + i, ".i", 2) == 0 281656164Sru#endif 281756164Sru || isdigit (program_name[i + 1]))) /* a man page foo.1 */ 281856164Sru { 281956164Sru program_name[i] = 0; 282056164Sru break; 282156164Sru } 282256164Sru return program_name; 282356164Sru} 282456164Sru 282556164SruDECLARE_INFO_COMMAND (info_goto_invocation_node, 282656164Sru _("Find the node describing program invocation")) 282756164Sru{ 2828116530Sru const char *invocation_prompt = _("Find Invocation node of [%s]: "); 282956164Sru char *program_name, *line; 283056164Sru char *default_program_name, *prompt, *file_name; 283156164Sru NODE *top_node; 283256164Sru 283356164Sru /* Intuit the name of the program they are likely to want. 283456164Sru We use the file name of the current Info file as a hint. */ 283556164Sru file_name = window->node->parent ? window->node->parent 283656164Sru : window->node->filename; 283756164Sru default_program_name = program_name_from_file_name (file_name); 283856164Sru 283956164Sru prompt = (char *)xmalloc (strlen (default_program_name) + 284056164Sru strlen (invocation_prompt)); 284156164Sru sprintf (prompt, invocation_prompt, default_program_name); 284256164Sru line = info_read_in_echo_area (window, prompt); 284356164Sru free (prompt); 284456164Sru if (!line) 284556164Sru { 2846146520Sru info_abort_key (window, 0, 0); 284756164Sru return; 284856164Sru } 284956164Sru if (*line) 285056164Sru program_name = line; 285156164Sru else 285256164Sru program_name = default_program_name; 285356164Sru 285456164Sru /* In interactive usage they'd probably expect us to begin looking 285556164Sru from the Top node. */ 285656164Sru top_node = info_get_node (file_name, NULL); 285756164Sru if (!top_node) 2858146520Sru info_error ((char *) msg_cant_find_node, "Top", NULL); 285956164Sru 286056164Sru info_intuit_options_node (window, top_node, program_name); 286156164Sru free (line); 286256164Sru free (default_program_name); 286356164Sru} 286456164Sru 286521495Sjmacd#if defined (HANDLE_MAN_PAGES) 286642664SmarkmDECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it")) 286721495Sjmacd{ 286821495Sjmacd char *line; 286921495Sjmacd 2870146520Sru line = info_read_in_echo_area (window, (char *) _("Get Manpage: ")); 287121495Sjmacd 287221495Sjmacd if (!line) 287321495Sjmacd { 287421495Sjmacd info_abort_key (window, 0, 0); 287521495Sjmacd return; 287621495Sjmacd } 287721495Sjmacd 287821495Sjmacd canonicalize_whitespace (line); 287921495Sjmacd 288021495Sjmacd if (*line) 288121495Sjmacd { 288221495Sjmacd char *goto_command; 288321495Sjmacd 288421495Sjmacd goto_command = (char *)xmalloc 288542664Smarkm (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line)); 288621495Sjmacd 288721495Sjmacd sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line); 288821495Sjmacd 288921495Sjmacd info_parse_and_select (goto_command, window); 289021495Sjmacd free (goto_command); 289121495Sjmacd } 289221495Sjmacd 289321495Sjmacd free (line); 289421495Sjmacd if (!info_error_was_printed) 289521495Sjmacd window_clear_echo_area (); 289621495Sjmacd} 289721495Sjmacd#endif /* HANDLE_MAN_PAGES */ 289821495Sjmacd 289921495Sjmacd/* Move to the "Top" node in this file. */ 290042664SmarkmDECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file")) 290121495Sjmacd{ 290256164Sru info_parse_and_select ("Top", window); 290321495Sjmacd} 290421495Sjmacd 290521495Sjmacd/* Move to the node "(dir)Top". */ 290642664SmarkmDECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'")) 290721495Sjmacd{ 290821495Sjmacd info_parse_and_select ("(dir)Top", window); 290921495Sjmacd} 291021495Sjmacd 291142664Smarkm 291242664Smarkm/* Read the name of a node to kill. The list of available nodes comes 291342664Smarkm from the nodes appearing in the current window configuration. */ 291442664Smarkmstatic char * 2915146520Sruread_nodename_to_kill (WINDOW *window) 291621495Sjmacd{ 291742664Smarkm int iw; 291842664Smarkm char *nodename; 291942664Smarkm INFO_WINDOW *info_win; 292042664Smarkm REFERENCE **menu = NULL; 292142664Smarkm int menu_index = 0, menu_slots = 0; 292242664Smarkm char *default_nodename = xstrdup (active_window->node->nodename); 2923146520Sru char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename)); 292421495Sjmacd 292542664Smarkm sprintf (prompt, _("Kill node (%s): "), default_nodename); 292621495Sjmacd 292742664Smarkm for (iw = 0; (info_win = info_windows[iw]); iw++) 292842664Smarkm { 292942664Smarkm REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 293042664Smarkm entry->label = xstrdup (info_win->window->node->nodename); 293142664Smarkm entry->filename = entry->nodename = (char *)NULL; 293221495Sjmacd 293342664Smarkm add_pointer_to_array (entry, menu_index, menu, menu_slots, 10, 293442664Smarkm REFERENCE *); 293542664Smarkm } 293621495Sjmacd 293742664Smarkm nodename = info_read_completing_in_echo_area (window, prompt, menu); 293842664Smarkm free (prompt); 293942664Smarkm info_free_references (menu); 294042664Smarkm if (nodename && !*nodename) 294142664Smarkm { 294242664Smarkm free (nodename); 294342664Smarkm nodename = default_nodename; 294442664Smarkm } 294542664Smarkm else 294642664Smarkm free (default_nodename); 294721495Sjmacd 294842664Smarkm return nodename; 294942664Smarkm} 295021495Sjmacd 295121495Sjmacd 295242664Smarkm/* Delete NODENAME from this window, showing the most 295342664Smarkm recently selected node in this window. */ 295442664Smarkmstatic void 2955146520Srukill_node (WINDOW *window, char *nodename) 295642664Smarkm{ 295742664Smarkm int iw, i; 295842664Smarkm INFO_WINDOW *info_win; 295942664Smarkm NODE *temp; 296056164Sru 296121495Sjmacd /* If there is no nodename to kill, quit now. */ 296221495Sjmacd if (!nodename) 296321495Sjmacd { 296421495Sjmacd info_abort_key (window, 0, 0); 296521495Sjmacd return; 296621495Sjmacd } 296721495Sjmacd 296821495Sjmacd /* If there is a nodename, find it in our window list. */ 296942664Smarkm for (iw = 0; (info_win = info_windows[iw]); iw++) 297056164Sru if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0 297156164Sru && info_win->window == window) 297221495Sjmacd break; 297321495Sjmacd 297421495Sjmacd if (!info_win) 297521495Sjmacd { 297621495Sjmacd if (*nodename) 2977146520Sru info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL); 297821495Sjmacd else 297942664Smarkm window_clear_echo_area (); 298021495Sjmacd 298121495Sjmacd return; 298221495Sjmacd } 298321495Sjmacd 298421495Sjmacd /* If there are no more nodes left anywhere to view, complain and exit. */ 298521495Sjmacd if (info_windows_index == 1 && info_windows[0]->nodes_index == 1) 298621495Sjmacd { 2987146520Sru info_error ((char *) _("Cannot kill the last node"), NULL, NULL); 298821495Sjmacd return; 298921495Sjmacd } 299021495Sjmacd 299142664Smarkm /* INFO_WIN contains the node that the user wants to stop viewing. Delete 299242664Smarkm this node from the list of nodes previously shown in this window. */ 299321495Sjmacd for (i = info_win->current; i < info_win->nodes_index; i++) 299456164Sru info_win->nodes[i] = info_win->nodes[i + 1]; 299521495Sjmacd 299621495Sjmacd /* There is one less node in this window's history list. */ 299721495Sjmacd info_win->nodes_index--; 299821495Sjmacd 299921495Sjmacd /* Make this window show the most recent history node. */ 300021495Sjmacd info_win->current = info_win->nodes_index - 1; 300121495Sjmacd 300221495Sjmacd /* If there aren't any nodes left in this window, steal one from the 300321495Sjmacd next window. */ 300421495Sjmacd if (info_win->current < 0) 300521495Sjmacd { 300621495Sjmacd INFO_WINDOW *stealer; 300721495Sjmacd int which, pagetop; 300821495Sjmacd long point; 300921495Sjmacd 301021495Sjmacd if (info_windows[iw + 1]) 301142664Smarkm stealer = info_windows[iw + 1]; 301221495Sjmacd else 301342664Smarkm stealer = info_windows[0]; 301421495Sjmacd 301521495Sjmacd /* If the node being displayed in the next window is not the most 301642664Smarkm recently loaded one, get the most recently loaded one. */ 301721495Sjmacd if ((stealer->nodes_index - 1) != stealer->current) 301842664Smarkm which = stealer->nodes_index - 1; 301921495Sjmacd 302021495Sjmacd /* Else, if there is another node behind the stealers current node, 302142664Smarkm use that one. */ 302221495Sjmacd else if (stealer->current > 0) 302342664Smarkm which = stealer->current - 1; 302421495Sjmacd 302521495Sjmacd /* Else, just use the node appearing in STEALER's window. */ 302621495Sjmacd else 302742664Smarkm which = stealer->current; 302821495Sjmacd 302921495Sjmacd /* Copy this node. */ 303021495Sjmacd { 303142664Smarkm NODE *copy = xmalloc (sizeof (NODE)); 303256164Sru 303342664Smarkm temp = stealer->nodes[which]; 303442664Smarkm point = stealer->points[which]; 303542664Smarkm pagetop = stealer->pagetops[which]; 303621495Sjmacd 303742664Smarkm copy->filename = temp->filename; 303842664Smarkm copy->parent = temp->parent; 303942664Smarkm copy->nodename = temp->nodename; 304042664Smarkm copy->contents = temp->contents; 304142664Smarkm copy->nodelen = temp->nodelen; 304242664Smarkm copy->flags = temp->flags; 304356164Sru copy->display_pos = temp->display_pos; 304421495Sjmacd 304542664Smarkm temp = copy; 304621495Sjmacd } 304721495Sjmacd 304821495Sjmacd window_set_node_of_window (info_win->window, temp); 304921495Sjmacd window->point = point; 305021495Sjmacd window->pagetop = pagetop; 305121495Sjmacd remember_window_and_node (info_win->window, temp); 305221495Sjmacd } 305321495Sjmacd else 305421495Sjmacd { 305521495Sjmacd temp = info_win->nodes[info_win->current]; 305656164Sru temp->display_pos = info_win->points[info_win->current]; 305721495Sjmacd window_set_node_of_window (info_win->window, temp); 305821495Sjmacd } 305942664Smarkm 306021495Sjmacd if (!info_error_was_printed) 306121495Sjmacd window_clear_echo_area (); 306242664Smarkm 306342664Smarkm if (auto_footnotes_p) 306442664Smarkm info_get_or_remove_footnotes (window); 306521495Sjmacd} 306621495Sjmacd 306742664Smarkm/* Kill current node, thus going back one in the node history. I (karl) 306842664Smarkm do not think this is completely correct yet, because of the 306942664Smarkm window-changing stuff in kill_node, but it's a lot better than the 307042664Smarkm previous implementation, which did not account for nodes being 307142664Smarkm visited twice at all. */ 307242664SmarkmDECLARE_INFO_COMMAND (info_history_node, 307342664Smarkm _("Select the most recently selected node")) 307442664Smarkm{ 307542664Smarkm kill_node (window, active_window->node->nodename); 307642664Smarkm} 307742664Smarkm 307842664Smarkm/* Kill named node. */ 307942664SmarkmDECLARE_INFO_COMMAND (info_kill_node, _("Kill this node")) 308042664Smarkm{ 308142664Smarkm char *nodename = read_nodename_to_kill (window); 308242664Smarkm kill_node (window, nodename); 308342664Smarkm} 308442664Smarkm 308542664Smarkm 308621495Sjmacd/* Read the name of a file and select the entire file. */ 308742664SmarkmDECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it")) 308821495Sjmacd{ 308921495Sjmacd char *line; 309021495Sjmacd 3091146520Sru line = info_read_in_echo_area (window, (char *) _("Find file: ")); 309221495Sjmacd if (!line) 309321495Sjmacd { 309421495Sjmacd info_abort_key (active_window, 1, 0); 309521495Sjmacd return; 309621495Sjmacd } 309721495Sjmacd 309821495Sjmacd if (*line) 309921495Sjmacd { 310021495Sjmacd NODE *node; 310121495Sjmacd 310221495Sjmacd node = info_get_node (line, "*"); 310321495Sjmacd if (!node) 310442664Smarkm { 310542664Smarkm if (info_recent_file_error) 3106146520Sru info_error (info_recent_file_error, NULL, NULL); 310742664Smarkm else 3108146520Sru info_error ((char *) _("Cannot find `%s'."), line, NULL); 310942664Smarkm } 311021495Sjmacd else 311156164Sru info_set_node_of_window (1, window, node); 311256164Sru 311321495Sjmacd free (line); 311421495Sjmacd } 311521495Sjmacd 311621495Sjmacd if (!info_error_was_printed) 311721495Sjmacd window_clear_echo_area (); 311821495Sjmacd} 311921495Sjmacd 312021495Sjmacd/* **************************************************************** */ 312142664Smarkm/* */ 312242664Smarkm/* Dumping and Printing Nodes */ 312342664Smarkm/* */ 312421495Sjmacd/* **************************************************************** */ 312521495Sjmacd 312621495Sjmacd#define VERBOSE_NODE_DUMPING 3127146520Srustatic void write_node_to_stream (NODE *node, FILE *stream); 3128146520Srustatic void dump_node_to_stream (char *filename, char *nodename, 3129146520Sru FILE *stream, int dump_subnodes); 3130146520Srustatic void initialize_dumping (void); 313121495Sjmacd 313221495Sjmacd/* Dump the nodes specified by FILENAME and NODENAMES to the file named 313321495Sjmacd in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump 313421495Sjmacd the nodes which appear in the menu of each node dumped. */ 313521495Sjmacdvoid 3136146520Srudump_nodes_to_file (char *filename, char **nodenames, 3137146520Sru char *output_filename, int dump_subnodes) 313821495Sjmacd{ 313921495Sjmacd register int i; 314021495Sjmacd FILE *output_stream; 314121495Sjmacd 314221495Sjmacd /* Get the stream to print the nodes to. Special case of an output 314321495Sjmacd filename of "-" means to dump the nodes to stdout. */ 314421495Sjmacd if (strcmp (output_filename, "-") == 0) 314521495Sjmacd output_stream = stdout; 314621495Sjmacd else 314721495Sjmacd output_stream = fopen (output_filename, "w"); 314821495Sjmacd 314921495Sjmacd if (!output_stream) 315021495Sjmacd { 3151146520Sru info_error ((char *) _("Could not create output file `%s'."), 3152146520Sru output_filename, NULL); 315321495Sjmacd return; 315421495Sjmacd } 315521495Sjmacd 315621495Sjmacd /* Print each node to stream. */ 315721495Sjmacd initialize_dumping (); 315821495Sjmacd for (i = 0; nodenames[i]; i++) 315921495Sjmacd dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes); 316021495Sjmacd 316121495Sjmacd if (output_stream != stdout) 316221495Sjmacd fclose (output_stream); 316321495Sjmacd 316421495Sjmacd#if defined (VERBOSE_NODE_DUMPING) 3165146520Sru info_error ((char *) _("Done."), NULL, NULL); 316621495Sjmacd#endif /* VERBOSE_NODE_DUMPING */ 316721495Sjmacd} 316821495Sjmacd 316921495Sjmacd/* A place to remember already dumped nodes. */ 317021495Sjmacdstatic char **dumped_already = (char **)NULL; 317121495Sjmacdstatic int dumped_already_index = 0; 317221495Sjmacdstatic int dumped_already_slots = 0; 317321495Sjmacd 317421495Sjmacdstatic void 3175146520Sruinitialize_dumping (void) 317621495Sjmacd{ 317721495Sjmacd dumped_already_index = 0; 317821495Sjmacd} 317921495Sjmacd 318021495Sjmacd/* Get and print the node specified by FILENAME and NODENAME to STREAM. 318121495Sjmacd If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear 318221495Sjmacd in the menu of each node dumped. */ 318321495Sjmacdstatic void 3184146520Srudump_node_to_stream (char *filename, char *nodename, 3185146520Sru FILE *stream, int dump_subnodes) 318621495Sjmacd{ 318721495Sjmacd register int i; 318821495Sjmacd NODE *node; 318921495Sjmacd 319021495Sjmacd node = info_get_node (filename, nodename); 319121495Sjmacd 319221495Sjmacd if (!node) 319321495Sjmacd { 319421495Sjmacd if (info_recent_file_error) 3195146520Sru info_error (info_recent_file_error, NULL, NULL); 319621495Sjmacd else 319742664Smarkm { 319842664Smarkm if (filename && *nodename != '(') 3199146520Sru info_error ((char *) msg_cant_file_node, 3200146520Sru filename_non_directory (filename), 3201146520Sru nodename); 320242664Smarkm else 3203146520Sru info_error ((char *) msg_cant_find_node, nodename, NULL); 320442664Smarkm } 320521495Sjmacd return; 320621495Sjmacd } 320721495Sjmacd 320821495Sjmacd /* If we have already dumped this node, don't dump it again. */ 320921495Sjmacd for (i = 0; i < dumped_already_index; i++) 321021495Sjmacd if (strcmp (node->nodename, dumped_already[i]) == 0) 321121495Sjmacd { 321242664Smarkm free (node); 321342664Smarkm return; 321421495Sjmacd } 321521495Sjmacd add_pointer_to_array (node->nodename, dumped_already_index, dumped_already, 321642664Smarkm dumped_already_slots, 50, char *); 321721495Sjmacd 321821495Sjmacd#if defined (VERBOSE_NODE_DUMPING) 321921495Sjmacd /* Maybe we should print some information about the node being output. */ 3220146520Sru info_error ((char *) _("Writing node %s..."), node_printed_rep (node), NULL); 322121495Sjmacd#endif /* VERBOSE_NODE_DUMPING */ 322221495Sjmacd 322321495Sjmacd write_node_to_stream (node, stream); 322421495Sjmacd 322521495Sjmacd /* If we are dumping subnodes, get the list of menu items in this node, 322621495Sjmacd and dump each one recursively. */ 322721495Sjmacd if (dump_subnodes) 322821495Sjmacd { 322921495Sjmacd REFERENCE **menu = (REFERENCE **)NULL; 323021495Sjmacd 323121495Sjmacd /* If this node is an Index, do not dump the menu references. */ 323221495Sjmacd if (string_in_line ("Index", node->nodename) == -1) 323342664Smarkm menu = info_menu_of_node (node); 323421495Sjmacd 323521495Sjmacd if (menu) 323642664Smarkm { 323742664Smarkm for (i = 0; menu[i]; i++) 323842664Smarkm { 323942664Smarkm /* We don't dump Info files which are different than the 324042664Smarkm current one. */ 324142664Smarkm if (!menu[i]->filename) 324242664Smarkm dump_node_to_stream 324342664Smarkm (filename, menu[i]->nodename, stream, dump_subnodes); 324442664Smarkm } 324542664Smarkm info_free_references (menu); 324642664Smarkm } 324721495Sjmacd } 324821495Sjmacd 324921495Sjmacd free (node); 325021495Sjmacd} 325121495Sjmacd 325221495Sjmacd/* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump 325321495Sjmacd the nodes which appear in the menu of each node dumped. */ 325421495Sjmacdvoid 3255146520Srudump_node_to_file (NODE *node, char *filename, int dump_subnodes) 325621495Sjmacd{ 325721495Sjmacd FILE *output_stream; 325821495Sjmacd char *nodes_filename; 325921495Sjmacd 326021495Sjmacd /* Get the stream to print this node to. Special case of an output 326121495Sjmacd filename of "-" means to dump the nodes to stdout. */ 326221495Sjmacd if (strcmp (filename, "-") == 0) 326321495Sjmacd output_stream = stdout; 326421495Sjmacd else 326521495Sjmacd output_stream = fopen (filename, "w"); 326621495Sjmacd 326721495Sjmacd if (!output_stream) 326821495Sjmacd { 3269146520Sru info_error ((char *) _("Could not create output file `%s'."), filename, 3270146520Sru NULL); 327121495Sjmacd return; 327221495Sjmacd } 327321495Sjmacd 327421495Sjmacd if (node->parent) 327521495Sjmacd nodes_filename = node->parent; 327621495Sjmacd else 327721495Sjmacd nodes_filename = node->filename; 327821495Sjmacd 327921495Sjmacd initialize_dumping (); 328021495Sjmacd dump_node_to_stream 328121495Sjmacd (nodes_filename, node->nodename, output_stream, dump_subnodes); 328221495Sjmacd 328321495Sjmacd if (output_stream != stdout) 328421495Sjmacd fclose (output_stream); 328521495Sjmacd 328621495Sjmacd#if defined (VERBOSE_NODE_DUMPING) 3287146520Sru info_error ((char *) _("Done."), NULL, NULL); 328821495Sjmacd#endif /* VERBOSE_NODE_DUMPING */ 328921495Sjmacd} 329021495Sjmacd 329121495Sjmacd#if !defined (DEFAULT_INFO_PRINT_COMMAND) 329221495Sjmacd# define DEFAULT_INFO_PRINT_COMMAND "lpr" 329321495Sjmacd#endif /* !DEFAULT_INFO_PRINT_COMMAND */ 329421495Sjmacd 329521495SjmacdDECLARE_INFO_COMMAND (info_print_node, 329642664Smarkm _("Pipe the contents of this node through INFO_PRINT_COMMAND")) 329721495Sjmacd{ 329821495Sjmacd print_node (window->node); 329921495Sjmacd} 330021495Sjmacd 330121495Sjmacd/* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */ 330221495Sjmacdvoid 3303146520Sruprint_node (NODE *node) 330421495Sjmacd{ 330521495Sjmacd FILE *printer_pipe; 330642664Smarkm char *print_command = getenv ("INFO_PRINT_COMMAND"); 330756164Sru int piping = 0; 330821495Sjmacd 330921495Sjmacd if (!print_command || !*print_command) 331021495Sjmacd print_command = DEFAULT_INFO_PRINT_COMMAND; 331121495Sjmacd 331256164Sru /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the 331356164Sru (default) text mode, since the printer drivers there need to see 331456164Sru DOS-style CRLF pairs at the end of each line. 331521495Sjmacd 331656164Sru FIXME: if we are to support Mac-style text files, we might need 331756164Sru to convert the text here. */ 331856164Sru 331956164Sru /* INFO_PRINT_COMMAND which says ">file" means write to that file. 332056164Sru Presumably, the name of the file is the local printer device. */ 332156164Sru if (*print_command == '>') 332256164Sru printer_pipe = fopen (++print_command, "w"); 332356164Sru else 332456164Sru { 332556164Sru printer_pipe = popen (print_command, "w"); 332656164Sru piping = 1; 332756164Sru } 332856164Sru 332921495Sjmacd if (!printer_pipe) 333021495Sjmacd { 3331146520Sru info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL); 333221495Sjmacd return; 333321495Sjmacd } 333421495Sjmacd 333521495Sjmacd#if defined (VERBOSE_NODE_DUMPING) 333621495Sjmacd /* Maybe we should print some information about the node being output. */ 3337146520Sru info_error ((char *) _("Printing node %s..."), node_printed_rep (node), NULL); 333821495Sjmacd#endif /* VERBOSE_NODE_DUMPING */ 333921495Sjmacd 334021495Sjmacd write_node_to_stream (node, printer_pipe); 334156164Sru if (piping) 334256164Sru pclose (printer_pipe); 334356164Sru else 334456164Sru fclose (printer_pipe); 334521495Sjmacd 334621495Sjmacd#if defined (VERBOSE_NODE_DUMPING) 3347146520Sru info_error ((char *) _("Done."), NULL, NULL); 334821495Sjmacd#endif /* VERBOSE_NODE_DUMPING */ 334921495Sjmacd} 335021495Sjmacd 335121495Sjmacdstatic void 3352146520Sruwrite_node_to_stream (NODE *node, FILE *stream) 335321495Sjmacd{ 335421495Sjmacd fwrite (node->contents, 1, node->nodelen, stream); 335521495Sjmacd} 335621495Sjmacd 335721495Sjmacd/* **************************************************************** */ 335842664Smarkm/* */ 335942664Smarkm/* Info Searching Commands */ 336042664Smarkm/* */ 336121495Sjmacd/* **************************************************************** */ 336221495Sjmacd 336321495Sjmacd/* Variable controlling the garbage collection of files briefly visited 336421495Sjmacd during searches. Such files are normally gc'ed, unless they were 336521495Sjmacd compressed to begin with. If this variable is non-zero, it says 336621495Sjmacd to gc even those file buffer contents which had to be uncompressed. */ 336721495Sjmacdint gc_compressed_files = 0; 336821495Sjmacd 3369146520Srustatic void info_gc_file_buffers (void); 3370146520Srustatic void info_search_1 (WINDOW *window, int count, 3371146520Sru unsigned char key, int case_sensitive, int ask_for_string); 337221495Sjmacd 337321495Sjmacdstatic char *search_string = (char *)NULL; 337421495Sjmacdstatic int search_string_size = 0; 337521495Sjmacdstatic int isearch_is_active = 0; 337621495Sjmacd 337756164Srustatic int last_search_direction = 0; 337856164Srustatic int last_search_case_sensitive = 0; 337956164Sru 338021495Sjmacd/* Return the file buffer which belongs to WINDOW's node. */ 338121495SjmacdFILE_BUFFER * 3382146520Srufile_buffer_of_window (WINDOW *window) 338321495Sjmacd{ 338421495Sjmacd /* If this window has no node, then it has no file buffer. */ 338521495Sjmacd if (!window->node) 338621495Sjmacd return ((FILE_BUFFER *)NULL); 338721495Sjmacd 338821495Sjmacd if (window->node->parent) 338921495Sjmacd return (info_find_file (window->node->parent)); 339021495Sjmacd 339121495Sjmacd if (window->node->filename) 339221495Sjmacd return (info_find_file (window->node->filename)); 339321495Sjmacd 339421495Sjmacd return ((FILE_BUFFER *)NULL); 339521495Sjmacd} 339621495Sjmacd 339721495Sjmacd/* Search for STRING in NODE starting at START. Return -1 if the string 339821495Sjmacd was not found, or the location of the string if it was. If WINDOW is 339921495Sjmacd passed as non-null, set the window's node to be NODE, its point to be 340021495Sjmacd the found string, and readjust the window's pagetop. Final argument 340121495Sjmacd DIR says which direction to search in. If it is positive, search 340221495Sjmacd forward, else backwards. */ 340321495Sjmacdlong 3404146520Sruinfo_search_in_node (char *string, NODE *node, long int start, 3405146520Sru WINDOW *window, int dir, int case_sensitive) 340621495Sjmacd{ 340721495Sjmacd SEARCH_BINDING binding; 340821495Sjmacd long offset; 340921495Sjmacd 341021495Sjmacd binding.buffer = node->contents; 341121495Sjmacd binding.start = start; 341221495Sjmacd binding.end = node->nodelen; 341356164Sru binding.flags = 0; 341456164Sru if (!case_sensitive) 341556164Sru binding.flags |= S_FoldCase; 341621495Sjmacd 341721495Sjmacd if (dir < 0) 341821495Sjmacd { 341921495Sjmacd binding.end = 0; 342021495Sjmacd binding.flags |= S_SkipDest; 342121495Sjmacd } 342221495Sjmacd 342321495Sjmacd if (binding.start < 0) 342421495Sjmacd return (-1); 342521495Sjmacd 342621495Sjmacd /* For incremental searches, we always wish to skip past the string. */ 342721495Sjmacd if (isearch_is_active) 342821495Sjmacd binding.flags |= S_SkipDest; 342921495Sjmacd 343021495Sjmacd offset = search (string, &binding); 343121495Sjmacd 343221495Sjmacd if (offset != -1 && window) 343321495Sjmacd { 343421495Sjmacd set_remembered_pagetop_and_point (window); 343521495Sjmacd if (window->node != node) 343642664Smarkm window_set_node_of_window (window, node); 343721495Sjmacd window->point = offset; 343821495Sjmacd window_adjust_pagetop (window); 343921495Sjmacd } 344021495Sjmacd return (offset); 344121495Sjmacd} 344221495Sjmacd 344321495Sjmacd/* Search NODE, looking for the largest possible match of STRING. Start the 344421495Sjmacd search at START. Return the absolute position of the match, or -1, if 344521495Sjmacd no part of the string could be found. */ 344621495Sjmacdlong 3447146520Sruinfo_target_search_node (NODE *node, char *string, long int start) 344821495Sjmacd{ 344921495Sjmacd register int i; 3450146520Sru long offset = 0; 345121495Sjmacd char *target; 345221495Sjmacd 345342664Smarkm target = xstrdup (string); 345421495Sjmacd i = strlen (target); 345521495Sjmacd 345621495Sjmacd /* Try repeatedly searching for this string while removing words from 345721495Sjmacd the end of it. */ 345821495Sjmacd while (i) 345921495Sjmacd { 346021495Sjmacd target[i] = '\0'; 346156164Sru offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0); 346221495Sjmacd 346321495Sjmacd if (offset != -1) 346442664Smarkm break; 346521495Sjmacd 346621495Sjmacd /* Delete the last word from TARGET. */ 346721495Sjmacd for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--); 346821495Sjmacd } 346921495Sjmacd free (target); 347021495Sjmacd return (offset); 347121495Sjmacd} 347221495Sjmacd 347321495Sjmacd/* Search for STRING starting in WINDOW at point. If the string is found 347421495Sjmacd in this node, set point to that position. Otherwise, get the file buffer 347521495Sjmacd associated with WINDOW's node, and search through each node in that file. 347621495Sjmacd If the search fails, return non-zero, else zero. Side-effect window 347721495Sjmacd leaving the node and point where the string was found current. */ 347821495Sjmacdstatic int 3479146520Sruinfo_search_internal (char *string, WINDOW *window, 3480146520Sru int dir, int case_sensitive) 348121495Sjmacd{ 348221495Sjmacd register int i; 348321495Sjmacd FILE_BUFFER *file_buffer; 348421495Sjmacd char *initial_nodename; 348521495Sjmacd long ret, start = 0; 348621495Sjmacd 348721495Sjmacd file_buffer = file_buffer_of_window (window); 348821495Sjmacd initial_nodename = window->node->nodename; 348921495Sjmacd 349056164Sru /* This used to begin from window->point, unless this was a repeated 349156164Sru search command. But invoking search with an argument loses with 349256164Sru that logic, since info_last_executed_command is then set to 349356164Sru info_add_digit_to_numeric_arg. I think there's no sense in 349456164Sru ``finding'' a string that is already under the cursor, anyway. */ 349556164Sru ret = info_search_in_node 349656164Sru (string, window->node, window->point + dir, window, dir, 349756164Sru case_sensitive); 349821495Sjmacd 349921495Sjmacd if (ret != -1) 350021495Sjmacd { 350121495Sjmacd /* We won! */ 350221495Sjmacd if (!echo_area_is_active && !isearch_is_active) 350342664Smarkm window_clear_echo_area (); 350421495Sjmacd return (0); 350521495Sjmacd } 350621495Sjmacd 350721495Sjmacd /* The string wasn't found in the current node. Search through the 350821495Sjmacd window's file buffer, iff the current node is not "*". */ 350921495Sjmacd if (!file_buffer || (strcmp (initial_nodename, "*") == 0)) 351021495Sjmacd return (-1); 351121495Sjmacd 351221495Sjmacd /* If this file has tags, search through every subfile, starting at 351321495Sjmacd this node's subfile and node. Otherwise, search through the 351421495Sjmacd file's node list. */ 351521495Sjmacd if (file_buffer->tags) 351621495Sjmacd { 3517146520Sru register int current_tag = 0, number_of_tags; 351821495Sjmacd char *last_subfile; 351921495Sjmacd TAG *tag; 352021495Sjmacd 352121495Sjmacd /* Find number of tags and current tag. */ 352221495Sjmacd last_subfile = (char *)NULL; 352321495Sjmacd for (i = 0; file_buffer->tags[i]; i++) 352442664Smarkm if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0) 352542664Smarkm { 352642664Smarkm current_tag = i; 352742664Smarkm last_subfile = file_buffer->tags[i]->filename; 352842664Smarkm } 352921495Sjmacd 353021495Sjmacd number_of_tags = i; 353121495Sjmacd 353221495Sjmacd /* If there is no last_subfile, our tag wasn't found. */ 353321495Sjmacd if (!last_subfile) 353442664Smarkm return (-1); 353521495Sjmacd 353621495Sjmacd /* Search through subsequent nodes, wrapping around to the top 353742664Smarkm of the info file until we find the string or return to this 353842664Smarkm window's node and point. */ 353921495Sjmacd while (1) 354042664Smarkm { 354142664Smarkm NODE *node; 354221495Sjmacd 354342664Smarkm /* Allow C-g to quit the search, failing it if pressed. */ 354442664Smarkm return_if_control_g (-1); 354521495Sjmacd 354656164Sru /* Find the next tag that isn't an anchor. */ 354756164Sru for (i = current_tag + dir; i != current_tag; i += dir) 354856164Sru { 354956164Sru if (i < 0) 355056164Sru i = number_of_tags - 1; 355156164Sru else if (i == number_of_tags) 355256164Sru i = 0; 355321495Sjmacd 355456164Sru tag = file_buffer->tags[i]; 355556164Sru if (tag->nodelen != 0) 355656164Sru break; 355756164Sru } 355821495Sjmacd 355956164Sru /* If we got past out starting point, bail out. */ 356056164Sru if (i == current_tag) 356156164Sru return (-1); 356256164Sru current_tag = i; 356321495Sjmacd 356442664Smarkm if (!echo_area_is_active && (last_subfile != tag->filename)) 356542664Smarkm { 356642664Smarkm window_message_in_echo_area 3567146520Sru ((char *) _("Searching subfile %s ..."), 3568146520Sru filename_non_directory (tag->filename), NULL); 356921495Sjmacd 357042664Smarkm last_subfile = tag->filename; 357142664Smarkm } 357221495Sjmacd 357342664Smarkm node = info_get_node (file_buffer->filename, tag->nodename); 357421495Sjmacd 357542664Smarkm if (!node) 357642664Smarkm { 357742664Smarkm /* If not doing i-search... */ 357842664Smarkm if (!echo_area_is_active) 357942664Smarkm { 358042664Smarkm if (info_recent_file_error) 3581146520Sru info_error (info_recent_file_error, NULL, NULL); 358242664Smarkm else 3583146520Sru info_error ((char *) msg_cant_file_node, 358442664Smarkm filename_non_directory (file_buffer->filename), 358542664Smarkm tag->nodename); 358642664Smarkm } 358742664Smarkm return (-1); 358842664Smarkm } 358921495Sjmacd 359042664Smarkm if (dir < 0) 359142664Smarkm start = tag->nodelen; 359221495Sjmacd 359342664Smarkm ret = 359456164Sru info_search_in_node (string, node, start, window, dir, 359556164Sru case_sensitive); 359621495Sjmacd 359742664Smarkm /* Did we find the string in this node? */ 359842664Smarkm if (ret != -1) 359942664Smarkm { 360042664Smarkm /* Yes! We win. */ 360142664Smarkm remember_window_and_node (window, node); 360242664Smarkm if (!echo_area_is_active) 360342664Smarkm window_clear_echo_area (); 360442664Smarkm return (0); 360542664Smarkm } 360621495Sjmacd 360742664Smarkm /* No. Free this node, and make sure that we haven't passed 360842664Smarkm our starting point. */ 360942664Smarkm free (node); 361021495Sjmacd 361142664Smarkm if (strcmp (initial_nodename, tag->nodename) == 0) 361242664Smarkm return (-1); 361342664Smarkm } 361421495Sjmacd } 361521495Sjmacd return (-1); 361621495Sjmacd} 361721495Sjmacd 361856164SruDECLARE_INFO_COMMAND (info_search_case_sensitively, 361956164Sru _("Read a string and search for it case-sensitively")) 362056164Sru{ 362156164Sru last_search_direction = count > 0 ? 1 : -1; 362256164Sru last_search_case_sensitive = 1; 362356164Sru info_search_1 (window, count, key, 1, 1); 362456164Sru} 362556164Sru 362642664SmarkmDECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) 362721495Sjmacd{ 362856164Sru last_search_direction = count > 0 ? 1 : -1; 362956164Sru last_search_case_sensitive = 0; 363056164Sru info_search_1 (window, count, key, 0, 1); 363156164Sru} 363256164Sru 363356164SruDECLARE_INFO_COMMAND (info_search_backward, 363456164Sru _("Read a string and search backward for it")) 363556164Sru{ 363656164Sru last_search_direction = count > 0 ? -1 : 1; 363756164Sru last_search_case_sensitive = 0; 363856164Sru info_search_1 (window, -count, key, 0, 1); 363956164Sru} 364056164Sru 364156164Srustatic void 3642146520Sruinfo_search_1 (WINDOW *window, int count, unsigned char key, 3643146520Sru int case_sensitive, int ask_for_string) 364456164Sru{ 364521495Sjmacd char *line, *prompt; 364621495Sjmacd int result, old_pagetop; 364721495Sjmacd int direction; 364821495Sjmacd 364921495Sjmacd if (count < 0) 365056164Sru { 365156164Sru direction = -1; 365256164Sru count = -count; 365356164Sru } 365421495Sjmacd else 365556164Sru { 365656164Sru direction = 1; 365756164Sru if (count == 0) 365856164Sru count = 1; /* for backward compatibility */ 365956164Sru } 366021495Sjmacd 366121495Sjmacd /* Read a string from the user, defaulting the search to SEARCH_STRING. */ 366221495Sjmacd if (!search_string) 366321495Sjmacd { 366421495Sjmacd search_string = (char *)xmalloc (search_string_size = 100); 366521495Sjmacd search_string[0] = '\0'; 366621495Sjmacd } 366721495Sjmacd 366856164Sru if (ask_for_string) 366956164Sru { 3670146520Sru prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: ")) 3671146520Sru + strlen (_("Search backward")) 3672146520Sru + strlen (_("Search")) 3673146520Sru + strlen (_(" case-sensitively ")) 3674146520Sru + strlen (_(" ")) 3675146520Sru + strlen (search_string)); 367621495Sjmacd 367756164Sru sprintf (prompt, _("%s%sfor string [%s]: "), 367856164Sru direction < 0 ? _("Search backward") : _("Search"), 367956164Sru case_sensitive ? _(" case-sensitively ") : _(" "), 368056164Sru search_string); 368121495Sjmacd 368256164Sru line = info_read_in_echo_area (window, prompt); 368356164Sru free (prompt); 368421495Sjmacd 368556164Sru if (!line) 368656164Sru { 3687146520Sru info_abort_key (window, 0, 0); 368856164Sru return; 368956164Sru } 369021495Sjmacd 369156164Sru if (*line) 369256164Sru { 3693146520Sru if (strlen (line) + 1 > (unsigned int) search_string_size) 369456164Sru search_string = (char *) xrealloc 369556164Sru (search_string, (search_string_size += 50 + strlen (line))); 369621495Sjmacd 369756164Sru strcpy (search_string, line); 369856164Sru free (line); 369956164Sru } 370021495Sjmacd } 370121495Sjmacd 370256164Sru /* If the search string includes upper-case letters, make the search 370356164Sru case-sensitive. */ 370456164Sru if (case_sensitive == 0) 370556164Sru for (line = search_string; *line; line++) 370656164Sru if (isupper (*line)) 370756164Sru { 370856164Sru case_sensitive = 1; 370956164Sru break; 371056164Sru } 371156164Sru 371221495Sjmacd old_pagetop = active_window->pagetop; 371356164Sru for (result = 0; result == 0 && count--; ) 371456164Sru result = info_search_internal (search_string, 371556164Sru active_window, direction, case_sensitive); 371621495Sjmacd 371721495Sjmacd if (result != 0 && !info_error_was_printed) 3718146520Sru info_error ((char *) _("Search failed."), NULL, NULL); 371921495Sjmacd else if (old_pagetop != active_window->pagetop) 372021495Sjmacd { 372121495Sjmacd int new_pagetop; 372221495Sjmacd 372321495Sjmacd new_pagetop = active_window->pagetop; 372421495Sjmacd active_window->pagetop = old_pagetop; 372521495Sjmacd set_window_pagetop (active_window, new_pagetop); 372621495Sjmacd if (auto_footnotes_p) 372742664Smarkm info_get_or_remove_footnotes (active_window); 372821495Sjmacd } 372921495Sjmacd 373021495Sjmacd /* Perhaps free the unreferenced file buffers that were searched, but 373121495Sjmacd not retained. */ 373221495Sjmacd info_gc_file_buffers (); 373321495Sjmacd} 373421495Sjmacd 373556164SruDECLARE_INFO_COMMAND (info_search_next, 373656164Sru _("Repeat last search in the same direction")) 373756164Sru{ 373856164Sru if (!last_search_direction) 3739146520Sru info_error ((char *) _("No previous search string"), NULL, NULL); 374056164Sru else 374156164Sru info_search_1 (window, last_search_direction * count, 374256164Sru key, last_search_case_sensitive, 0); 374356164Sru} 374456164Sru 374556164SruDECLARE_INFO_COMMAND (info_search_previous, 374656164Sru _("Repeat last search in the reverse direction")) 374756164Sru{ 374856164Sru if (!last_search_direction) 3749146520Sru info_error ((char *) _("No previous search string"), NULL, NULL); 375056164Sru else 375156164Sru info_search_1 (window, -last_search_direction * count, 375256164Sru key, last_search_case_sensitive, 0); 375356164Sru} 375456164Sru 375521495Sjmacd/* **************************************************************** */ 375642664Smarkm/* */ 375742664Smarkm/* Incremental Searching */ 375842664Smarkm/* */ 375921495Sjmacd/* **************************************************************** */ 376021495Sjmacd 3761146520Srustatic void incremental_search (WINDOW *window, int count, 3762146520Sru unsigned char ignore); 376321495Sjmacd 376421495SjmacdDECLARE_INFO_COMMAND (isearch_forward, 376542664Smarkm _("Search interactively for a string as you type it")) 376621495Sjmacd{ 376721495Sjmacd incremental_search (window, count, key); 376821495Sjmacd} 376921495Sjmacd 377021495SjmacdDECLARE_INFO_COMMAND (isearch_backward, 377142664Smarkm _("Search interactively for a string as you type it")) 377221495Sjmacd{ 377321495Sjmacd incremental_search (window, -count, key); 377421495Sjmacd} 377521495Sjmacd 377621495Sjmacd/* Incrementally search for a string as it is typed. */ 377721495Sjmacd/* The last accepted incremental search string. */ 377821495Sjmacdstatic char *last_isearch_accepted = (char *)NULL; 377921495Sjmacd 378021495Sjmacd/* The current incremental search string. */ 378121495Sjmacdstatic char *isearch_string = (char *)NULL; 378221495Sjmacdstatic int isearch_string_index = 0; 378321495Sjmacdstatic int isearch_string_size = 0; 378421495Sjmacdstatic unsigned char isearch_terminate_search_key = ESC; 378521495Sjmacd 378621495Sjmacd/* Array of search states. */ 378721495Sjmacdstatic SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL; 378821495Sjmacdstatic int isearch_states_index = 0; 378921495Sjmacdstatic int isearch_states_slots = 0; 379021495Sjmacd 379121495Sjmacd/* Push the state of this search. */ 379221495Sjmacdstatic void 3793146520Srupush_isearch (WINDOW *window, int search_index, int direction, int failing) 379421495Sjmacd{ 379521495Sjmacd SEARCH_STATE *state; 379621495Sjmacd 379721495Sjmacd state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE)); 379821495Sjmacd window_get_state (window, state); 379921495Sjmacd state->search_index = search_index; 380021495Sjmacd state->direction = direction; 380121495Sjmacd state->failing = failing; 380221495Sjmacd 380321495Sjmacd add_pointer_to_array (state, isearch_states_index, isearch_states, 380442664Smarkm isearch_states_slots, 20, SEARCH_STATE *); 380521495Sjmacd} 380621495Sjmacd 380721495Sjmacd/* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */ 380821495Sjmacdstatic void 3809146520Srupop_isearch (WINDOW *window, int *search_index, int *direction, int *failing) 381021495Sjmacd{ 381121495Sjmacd SEARCH_STATE *state; 381221495Sjmacd 381321495Sjmacd if (isearch_states_index) 381421495Sjmacd { 381521495Sjmacd isearch_states_index--; 381621495Sjmacd state = isearch_states[isearch_states_index]; 381721495Sjmacd window_set_state (window, state); 381821495Sjmacd *search_index = state->search_index; 381921495Sjmacd *direction = state->direction; 382021495Sjmacd *failing = state->failing; 382121495Sjmacd 382221495Sjmacd free (state); 382321495Sjmacd isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL; 382421495Sjmacd } 382521495Sjmacd} 382621495Sjmacd 382721495Sjmacd/* Free the memory used by isearch_states. */ 382821495Sjmacdstatic void 3829146520Srufree_isearch_states (void) 383021495Sjmacd{ 383121495Sjmacd register int i; 383221495Sjmacd 383321495Sjmacd for (i = 0; i < isearch_states_index; i++) 383421495Sjmacd { 383521495Sjmacd free (isearch_states[i]); 383621495Sjmacd isearch_states[i] = (SEARCH_STATE *)NULL; 383721495Sjmacd } 383821495Sjmacd isearch_states_index = 0; 383921495Sjmacd} 384021495Sjmacd 384121495Sjmacd/* Display the current search in the echo area. */ 384221495Sjmacdstatic void 3843146520Srushow_isearch_prompt (int dir, unsigned char *string, int failing_p) 384421495Sjmacd{ 384521495Sjmacd register int i; 3846116530Sru const char *prefix; 3847116530Sru char *prompt, *p_rep; 3848146520Sru unsigned int prompt_len, p_rep_index, p_rep_size; 384921495Sjmacd 385021495Sjmacd if (dir < 0) 385142664Smarkm prefix = _("I-search backward: "); 385221495Sjmacd else 385342664Smarkm prefix = _("I-search: "); 385421495Sjmacd 385521495Sjmacd p_rep_index = p_rep_size = 0; 385621495Sjmacd p_rep = (char *)NULL; 385721495Sjmacd for (i = 0; string[i]; i++) 385821495Sjmacd { 385921495Sjmacd char *rep; 386021495Sjmacd 386121495Sjmacd switch (string[i]) 386242664Smarkm { 386342664Smarkm case ' ': rep = " "; break; 386442664Smarkm case LFD: rep = "\\n"; break; 386542664Smarkm case TAB: rep = "\\t"; break; 386642664Smarkm default: 386742664Smarkm rep = pretty_keyname (string[i]); 386842664Smarkm } 386921495Sjmacd if ((p_rep_index + strlen (rep) + 1) >= p_rep_size) 387042664Smarkm p_rep = (char *)xrealloc (p_rep, p_rep_size += 100); 387121495Sjmacd 387221495Sjmacd strcpy (p_rep + p_rep_index, rep); 387321495Sjmacd p_rep_index += strlen (rep); 387421495Sjmacd } 387521495Sjmacd 3876116530Sru prompt_len = strlen (prefix) + p_rep_index + 1; 3877116530Sru if (failing_p) 3878116530Sru prompt_len += strlen (_("Failing ")); 3879116530Sru prompt = xmalloc (prompt_len); 388042664Smarkm sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix, 388142664Smarkm p_rep ? p_rep : ""); 388221495Sjmacd 3883146520Sru window_message_in_echo_area ("%s", prompt, NULL); 388421495Sjmacd maybe_free (p_rep); 388521495Sjmacd free (prompt); 388621495Sjmacd display_cursor_at_point (active_window); 388721495Sjmacd} 388821495Sjmacd 388921495Sjmacdstatic void 3890146520Sruincremental_search (WINDOW *window, int count, unsigned char ignore) 389121495Sjmacd{ 389221495Sjmacd unsigned char key; 389321495Sjmacd int last_search_result, search_result, dir; 389421495Sjmacd SEARCH_STATE mystate, orig_state; 389556164Sru char *p; 389656164Sru int case_sensitive = 0; 389721495Sjmacd 389821495Sjmacd if (count < 0) 389921495Sjmacd dir = -1; 390021495Sjmacd else 390121495Sjmacd dir = 1; 390221495Sjmacd 390321495Sjmacd last_search_result = search_result = 0; 390421495Sjmacd 390521495Sjmacd window_get_state (window, &orig_state); 390621495Sjmacd 390721495Sjmacd isearch_string_index = 0; 390821495Sjmacd if (!isearch_string_size) 390921495Sjmacd isearch_string = (char *)xmalloc (isearch_string_size = 50); 391021495Sjmacd 391121495Sjmacd /* Show the search string in the echo area. */ 391221495Sjmacd isearch_string[isearch_string_index] = '\0'; 3913146520Sru show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); 391421495Sjmacd 391521495Sjmacd isearch_is_active = 1; 391621495Sjmacd 391721495Sjmacd while (isearch_is_active) 391821495Sjmacd { 391921495Sjmacd VFunction *func = (VFunction *)NULL; 392021495Sjmacd int quoted = 0; 392121495Sjmacd 392221495Sjmacd /* If a recent display was interrupted, then do the redisplay now if 392342664Smarkm it is convenient. */ 392421495Sjmacd if (!info_any_buffered_input_p () && display_was_interrupted_p) 392542664Smarkm { 392642664Smarkm display_update_one_window (window); 392742664Smarkm display_cursor_at_point (active_window); 392842664Smarkm } 392921495Sjmacd 393021495Sjmacd /* Read a character and dispatch on it. */ 393121495Sjmacd key = info_get_input_char (); 393221495Sjmacd window_get_state (window, &mystate); 393321495Sjmacd 3934114477Sru if (key == DEL || key == Control ('h')) 393542664Smarkm { 393642664Smarkm /* User wants to delete one level of search? */ 393742664Smarkm if (!isearch_states_index) 393842664Smarkm { 393942664Smarkm terminal_ring_bell (); 394042664Smarkm continue; 394142664Smarkm } 394242664Smarkm else 394342664Smarkm { 394442664Smarkm pop_isearch 394542664Smarkm (window, &isearch_string_index, &dir, &search_result); 394642664Smarkm isearch_string[isearch_string_index] = '\0'; 3947146520Sru show_isearch_prompt (dir, (unsigned char *) isearch_string, 3948146520Sru search_result); 394942664Smarkm goto after_search; 395042664Smarkm } 395142664Smarkm } 395221495Sjmacd else if (key == Control ('q')) 395342664Smarkm { 395442664Smarkm key = info_get_input_char (); 395542664Smarkm quoted = 1; 395642664Smarkm } 395721495Sjmacd 395821495Sjmacd /* We are about to search again, or quit. Save the current search. */ 395921495Sjmacd push_isearch (window, isearch_string_index, dir, search_result); 396021495Sjmacd 396121495Sjmacd if (quoted) 396242664Smarkm goto insert_and_search; 396321495Sjmacd 396456164Sru if (!Meta_p (key) || key > 32) 396542664Smarkm { 3966114477Sru /* If this key is not a keymap, get its associated function, 3967114477Sru if any. If it is a keymap, then it's probably ESC from an 3968114477Sru arrow key, and we handle that case below. */ 3969114477Sru char type = window->keymap[key].type; 3970114477Sru func = type == ISFUNC 3971114477Sru ? InfoFunction(window->keymap[key].function) 3972114477Sru : NULL; /* function member is a Keymap if ISKMAP */ 397321495Sjmacd 3974114477Sru if (isprint (key) || (type == ISFUNC && func == NULL)) 397542664Smarkm { 397693142Sru insert_and_search: 397793142Sru 397893142Sru if (isearch_string_index + 2 >= isearch_string_size) 397993142Sru isearch_string = (char *)xrealloc 398093142Sru (isearch_string, isearch_string_size += 100); 398193142Sru 398293142Sru isearch_string[isearch_string_index++] = key; 398393142Sru isearch_string[isearch_string_index] = '\0'; 398493142Sru goto search_now; 398593142Sru } 3986146520Sru else if (func == (VFunction *) isearch_forward 3987146520Sru || func == (VFunction *) isearch_backward) 398893142Sru { 398993142Sru /* If this key invokes an incremental search, then this 399093142Sru means that we will either search again in the same 399193142Sru direction, search again in the reverse direction, or 399293142Sru insert the last search string that was accepted through 399393142Sru incremental searching. */ 3994146520Sru if ((func == (VFunction *) isearch_forward && dir > 0) || 3995146520Sru (func == (VFunction *) isearch_backward && dir < 0)) 399642664Smarkm { 399742664Smarkm /* If the user has typed no characters, then insert the 399842664Smarkm last successful search into the current search string. */ 399942664Smarkm if (isearch_string_index == 0) 400042664Smarkm { 400142664Smarkm /* Of course, there must be something to insert. */ 400242664Smarkm if (last_isearch_accepted) 400342664Smarkm { 4004146520Sru if (strlen ((char *) last_isearch_accepted) + 1 4005146520Sru >= (unsigned int) isearch_string_size) 400642664Smarkm isearch_string = (char *) 400742664Smarkm xrealloc (isearch_string, 400842664Smarkm isearch_string_size += 10 + 400942664Smarkm strlen (last_isearch_accepted)); 401042664Smarkm strcpy (isearch_string, last_isearch_accepted); 401142664Smarkm isearch_string_index = strlen (isearch_string); 401242664Smarkm goto search_now; 401342664Smarkm } 401442664Smarkm else 401542664Smarkm continue; 401642664Smarkm } 401742664Smarkm else 401842664Smarkm { 401942664Smarkm /* Search again in the same direction. This means start 402042664Smarkm from a new place if the last search was successful. */ 402142664Smarkm if (search_result == 0) 402242664Smarkm window->point += dir; 402342664Smarkm } 402442664Smarkm } 402542664Smarkm else 402642664Smarkm { 402742664Smarkm /* Reverse the direction of the search. */ 402842664Smarkm dir = -dir; 402942664Smarkm } 403042664Smarkm } 4031146520Sru else if (func == (VFunction *) info_abort_key) 403242664Smarkm { 403342664Smarkm /* If C-g pressed, and the search is failing, pop the search 403442664Smarkm stack back to the last unfailed search. */ 403542664Smarkm if (isearch_states_index && (search_result != 0)) 403642664Smarkm { 403742664Smarkm terminal_ring_bell (); 403842664Smarkm while (isearch_states_index && (search_result != 0)) 403942664Smarkm pop_isearch 404042664Smarkm (window, &isearch_string_index, &dir, &search_result); 404142664Smarkm isearch_string[isearch_string_index] = '\0'; 4042146520Sru show_isearch_prompt (dir, (unsigned char *) isearch_string, 4043146520Sru search_result); 404442664Smarkm continue; 404542664Smarkm } 404642664Smarkm else 404742664Smarkm goto exit_search; 404842664Smarkm } 404942664Smarkm else 405042664Smarkm goto exit_search; 405142664Smarkm } 405221495Sjmacd else 405342664Smarkm { 405442664Smarkm exit_search: 405542664Smarkm /* The character is not printable, or it has a function which is 405642664Smarkm non-null. Exit the search, remembering the search string. If 405742664Smarkm the key is not the same as the isearch_terminate_search_key, 405842664Smarkm then push it into pending input. */ 4059146520Sru if (isearch_string_index && func != (VFunction *) info_abort_key) 406042664Smarkm { 406142664Smarkm maybe_free (last_isearch_accepted); 406242664Smarkm last_isearch_accepted = xstrdup (isearch_string); 406342664Smarkm } 406421495Sjmacd 406556164Sru /* If the key is the isearch_terminate_search_key, but some buffered 406656164Sru input is pending, it is almost invariably because the ESC key is 406756164Sru actually the beginning of an escape sequence, like in case they 406856164Sru pressed an arrow key. So don't gobble the ESC key, push it back 406956164Sru into pending input. */ 407056164Sru /* FIXME: this seems like a kludge! We need a more reliable 407156164Sru mechanism to know when ESC is a separate key and when it is 407256164Sru part of an escape sequence. */ 407393142Sru if (key != RET /* Emacs addicts want RET to get lost */ 407493142Sru && (key != isearch_terminate_search_key 407593142Sru || info_any_buffered_input_p ())) 407642664Smarkm info_set_pending_input (key); 407721495Sjmacd 4078146520Sru if (func == (VFunction *) info_abort_key) 407942664Smarkm { 408042664Smarkm if (isearch_states_index) 408142664Smarkm window_set_state (window, &orig_state); 408242664Smarkm } 408321495Sjmacd 408442664Smarkm if (!echo_area_is_active) 408542664Smarkm window_clear_echo_area (); 408621495Sjmacd 408742664Smarkm if (auto_footnotes_p) 408842664Smarkm info_get_or_remove_footnotes (active_window); 408921495Sjmacd 409042664Smarkm isearch_is_active = 0; 409142664Smarkm continue; 409242664Smarkm } 409321495Sjmacd 409421495Sjmacd /* Search for the contents of isearch_string. */ 409521495Sjmacd search_now: 4096146520Sru show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); 409721495Sjmacd 409856164Sru /* If the search string includes upper-case letters, make the 409956164Sru search case-sensitive. */ 410056164Sru for (p = isearch_string; *p; p++) 410156164Sru if (isupper (*p)) 410256164Sru { 410356164Sru case_sensitive = 1; 410456164Sru break; 410556164Sru } 410656164Sru 410756164Sru 410821495Sjmacd if (search_result == 0) 410942664Smarkm { 411042664Smarkm /* Check to see if the current search string is right here. If 411142664Smarkm we are looking at it, then don't bother calling the search 411242664Smarkm function. */ 411342664Smarkm if (((dir < 0) && 411456164Sru ((case_sensitive ? strncmp : strncasecmp) 411556164Sru (window->node->contents + window->point, 411642664Smarkm isearch_string, isearch_string_index) == 0)) || 411742664Smarkm ((dir > 0) && 411842664Smarkm ((window->point - isearch_string_index) >= 0) && 411956164Sru ((case_sensitive ? strncmp : strncasecmp) 412056164Sru (window->node->contents + 412142664Smarkm (window->point - (isearch_string_index - 1)), 412242664Smarkm isearch_string, isearch_string_index) == 0))) 412342664Smarkm { 412442664Smarkm if (dir > 0) 412542664Smarkm window->point++; 412642664Smarkm } 412742664Smarkm else 412856164Sru search_result = info_search_internal (isearch_string, 412956164Sru window, dir, case_sensitive); 413042664Smarkm } 413121495Sjmacd 413221495Sjmacd /* If this search failed, and we didn't already have a failed search, 413342664Smarkm then ring the terminal bell. */ 413421495Sjmacd if (search_result != 0 && last_search_result == 0) 413542664Smarkm terminal_ring_bell (); 413621495Sjmacd 413721495Sjmacd after_search: 4138146520Sru show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); 413921495Sjmacd 414021495Sjmacd if (search_result == 0) 414142664Smarkm { 414242664Smarkm if ((mystate.node == window->node) && 414342664Smarkm (mystate.pagetop != window->pagetop)) 414442664Smarkm { 414542664Smarkm int newtop = window->pagetop; 414642664Smarkm window->pagetop = mystate.pagetop; 414742664Smarkm set_window_pagetop (window, newtop); 414842664Smarkm } 414942664Smarkm display_update_one_window (window); 415042664Smarkm display_cursor_at_point (window); 415142664Smarkm } 415221495Sjmacd 415321495Sjmacd last_search_result = search_result; 415421495Sjmacd } 415521495Sjmacd 415621495Sjmacd /* Free the memory used to remember each search state. */ 415721495Sjmacd free_isearch_states (); 415821495Sjmacd 415921495Sjmacd /* Perhaps GC some file buffers. */ 416021495Sjmacd info_gc_file_buffers (); 416121495Sjmacd 416221495Sjmacd /* After searching, leave the window in the correct state. */ 416321495Sjmacd if (!echo_area_is_active) 416421495Sjmacd window_clear_echo_area (); 416521495Sjmacd} 416621495Sjmacd 416721495Sjmacd/* GC some file buffers. A file buffer can be gc-ed if there we have 416821495Sjmacd no nodes in INFO_WINDOWS that reference this file buffer's contents. 416921495Sjmacd Garbage collecting a file buffer means to free the file buffers 417021495Sjmacd contents. */ 417121495Sjmacdstatic void 4172146520Sruinfo_gc_file_buffers (void) 417321495Sjmacd{ 417421495Sjmacd register int fb_index, iw_index, i; 417521495Sjmacd register FILE_BUFFER *fb; 417621495Sjmacd register INFO_WINDOW *iw; 417721495Sjmacd 417821495Sjmacd if (!info_loaded_files) 417921495Sjmacd return; 418021495Sjmacd 418142664Smarkm for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++) 418221495Sjmacd { 418321495Sjmacd int fb_referenced_p = 0; 418421495Sjmacd 418521495Sjmacd /* If already gc-ed, do nothing. */ 418621495Sjmacd if (!fb->contents) 418742664Smarkm continue; 418821495Sjmacd 418921495Sjmacd /* If this file had to be uncompressed, check to see if we should 419042664Smarkm gc it. This means that the user-variable "gc-compressed-files" 419142664Smarkm is non-zero. */ 419221495Sjmacd if ((fb->flags & N_IsCompressed) && !gc_compressed_files) 419342664Smarkm continue; 419421495Sjmacd 419521495Sjmacd /* If this file's contents are not gc-able, move on. */ 419621495Sjmacd if (fb->flags & N_CannotGC) 419742664Smarkm continue; 419821495Sjmacd 419921495Sjmacd /* Check each INFO_WINDOW to see if it has any nodes which reference 420042664Smarkm this file. */ 420142664Smarkm for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++) 420242664Smarkm { 420342664Smarkm for (i = 0; iw->nodes && iw->nodes[i]; i++) 420442664Smarkm { 420556164Sru if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) || 420656164Sru (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0)) 420742664Smarkm { 420842664Smarkm fb_referenced_p = 1; 420942664Smarkm break; 421042664Smarkm } 421142664Smarkm } 421242664Smarkm } 421321495Sjmacd 421421495Sjmacd /* If this file buffer wasn't referenced, free its contents. */ 421521495Sjmacd if (!fb_referenced_p) 421642664Smarkm { 421742664Smarkm free (fb->contents); 421842664Smarkm fb->contents = (char *)NULL; 421942664Smarkm } 422021495Sjmacd } 422121495Sjmacd} 422221495Sjmacd 422321495Sjmacd/* **************************************************************** */ 422442664Smarkm/* */ 422542664Smarkm/* Traversing and Selecting References */ 422642664Smarkm/* */ 422721495Sjmacd/* **************************************************************** */ 422821495Sjmacd 422921495Sjmacd/* Move to the next or previous cross reference in this node. */ 423021495Sjmacdstatic void 4231146520Sruinfo_move_to_xref (WINDOW *window, int count, unsigned char key, int dir) 423221495Sjmacd{ 423321495Sjmacd long firstmenu, firstxref; 423421495Sjmacd long nextmenu, nextxref; 423521495Sjmacd long placement = -1; 423621495Sjmacd long start = 0; 423721495Sjmacd NODE *node = window->node; 423821495Sjmacd 423921495Sjmacd if (dir < 0) 424021495Sjmacd start = node->nodelen; 424121495Sjmacd 424221495Sjmacd /* This search is only allowed to fail if there is no menu or cross 424321495Sjmacd reference in the current node. Otherwise, the first menu or xref 424421495Sjmacd found is moved to. */ 424521495Sjmacd 424621495Sjmacd firstmenu = info_search_in_node 424756164Sru (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0); 424821495Sjmacd 424921495Sjmacd /* FIRSTMENU may point directly to the line defining the menu. Skip that 425021495Sjmacd and go directly to the first item. */ 425121495Sjmacd 425221495Sjmacd if (firstmenu != -1) 425321495Sjmacd { 425421495Sjmacd char *text = node->contents + firstmenu; 425521495Sjmacd 425621495Sjmacd if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) 425742664Smarkm firstmenu = info_search_in_node 425856164Sru (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0); 425921495Sjmacd } 426021495Sjmacd 426121495Sjmacd firstxref = 426256164Sru info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0); 426321495Sjmacd 426421495Sjmacd#if defined (HANDLE_MAN_PAGES) 426521495Sjmacd if ((firstxref == -1) && (node->flags & N_IsManPage)) 426621495Sjmacd { 426721495Sjmacd firstxref = locate_manpage_xref (node, start, dir); 426821495Sjmacd } 426921495Sjmacd#endif /* HANDLE_MAN_PAGES */ 427021495Sjmacd 427121495Sjmacd if (firstmenu == -1 && firstxref == -1) 427221495Sjmacd { 4273146520Sru info_error ((char *) msg_no_xref_node, NULL, NULL); 427421495Sjmacd return; 427521495Sjmacd } 427621495Sjmacd 427721495Sjmacd /* There is at least one cross reference or menu entry in this node. 427821495Sjmacd Try hard to find the next available one. */ 427921495Sjmacd 428021495Sjmacd nextmenu = info_search_in_node 428156164Sru (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); 428221495Sjmacd 428321495Sjmacd nextxref = info_search_in_node 428456164Sru (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); 428521495Sjmacd 428621495Sjmacd#if defined (HANDLE_MAN_PAGES) 428721495Sjmacd if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1)) 428821495Sjmacd nextxref = locate_manpage_xref (node, window->point + dir, dir); 428921495Sjmacd#endif /* HANDLE_MAN_PAGES */ 429021495Sjmacd 429121495Sjmacd /* Ignore "Menu:" as a menu item. */ 429221495Sjmacd if (nextmenu != -1) 429321495Sjmacd { 429421495Sjmacd char *text = node->contents + nextmenu; 429521495Sjmacd 429621495Sjmacd if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) 429742664Smarkm nextmenu = info_search_in_node 429856164Sru (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0); 429921495Sjmacd } 430021495Sjmacd 430121495Sjmacd /* If there is both a next menu entry, and a next xref entry, choose the 430221495Sjmacd one which occurs first. Otherwise, select the one which actually 430321495Sjmacd appears in this node following point. */ 430421495Sjmacd if (nextmenu != -1 && nextxref != -1) 430521495Sjmacd { 430621495Sjmacd if (((dir == 1) && (nextmenu < nextxref)) || 430742664Smarkm ((dir == -1) && (nextmenu > nextxref))) 430842664Smarkm placement = nextmenu + 1; 430921495Sjmacd else 431042664Smarkm placement = nextxref; 431121495Sjmacd } 431221495Sjmacd else if (nextmenu != -1) 431321495Sjmacd placement = nextmenu + 1; 431421495Sjmacd else if (nextxref != -1) 431521495Sjmacd placement = nextxref; 431621495Sjmacd 431721495Sjmacd /* If there was neither a menu or xref entry appearing in this node after 431821495Sjmacd point, choose the first menu or xref entry appearing in this node. */ 431921495Sjmacd if (placement == -1) 432021495Sjmacd { 432121495Sjmacd if (firstmenu != -1 && firstxref != -1) 432242664Smarkm { 432342664Smarkm if (((dir == 1) && (firstmenu < firstxref)) || 432442664Smarkm ((dir == -1) && (firstmenu > firstxref))) 432542664Smarkm placement = firstmenu + 1; 432642664Smarkm else 432742664Smarkm placement = firstxref; 432842664Smarkm } 432921495Sjmacd else if (firstmenu != -1) 433042664Smarkm placement = firstmenu + 1; 433121495Sjmacd else 433242664Smarkm placement = firstxref; 433321495Sjmacd } 433421495Sjmacd window->point = placement; 433521495Sjmacd window_adjust_pagetop (window); 433621495Sjmacd window->flags |= W_UpdateWindow; 433721495Sjmacd} 433821495Sjmacd 433921495SjmacdDECLARE_INFO_COMMAND (info_move_to_prev_xref, 434042664Smarkm _("Move to the previous cross reference")) 434121495Sjmacd{ 434221495Sjmacd if (count < 0) 434321495Sjmacd info_move_to_prev_xref (window, -count, key); 434421495Sjmacd else 434521495Sjmacd info_move_to_xref (window, count, key, -1); 434621495Sjmacd} 434721495Sjmacd 434821495SjmacdDECLARE_INFO_COMMAND (info_move_to_next_xref, 434942664Smarkm _("Move to the next cross reference")) 435021495Sjmacd{ 435121495Sjmacd if (count < 0) 435221495Sjmacd info_move_to_next_xref (window, -count, key); 435321495Sjmacd else 435421495Sjmacd info_move_to_xref (window, count, key, 1); 435521495Sjmacd} 435621495Sjmacd 435721495Sjmacd/* Select the menu item or reference that appears on this line. */ 435821495SjmacdDECLARE_INFO_COMMAND (info_select_reference_this_line, 435942664Smarkm _("Select reference or menu item appearing on this line")) 436021495Sjmacd{ 436121495Sjmacd char *line; 436221495Sjmacd 4363146520Sru if (window->line_starts) 4364146520Sru line = window->line_starts[window_line_of_point (window)]; 4365146520Sru else 4366146520Sru line = ""; 436721495Sjmacd 436821495Sjmacd /* If this line contains a menu item, select that one. */ 436921495Sjmacd if (strncmp ("* ", line, 2) == 0) 437021495Sjmacd info_menu_or_ref_item (window, count, key, info_menu_of_node, 0); 437121495Sjmacd else 437221495Sjmacd info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0); 437321495Sjmacd} 437421495Sjmacd 437521495Sjmacd/* **************************************************************** */ 437642664Smarkm/* */ 437742664Smarkm/* Miscellaneous Info Commands */ 437842664Smarkm/* */ 437921495Sjmacd/* **************************************************************** */ 438021495Sjmacd 438121495Sjmacd/* What to do when C-g is pressed in a window. */ 438242664SmarkmDECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation")) 438321495Sjmacd{ 438421495Sjmacd /* If error printing doesn't oridinarily ring the bell, do it now, 438521495Sjmacd since C-g always rings the bell. Otherwise, let the error printer 438621495Sjmacd do it. */ 438721495Sjmacd if (!info_error_rings_bell_p) 438821495Sjmacd terminal_ring_bell (); 4389146520Sru info_error ((char *) _("Quit"), NULL, NULL); 439021495Sjmacd 439121495Sjmacd info_initialize_numeric_arg (); 439221495Sjmacd info_clear_pending_input (); 439321495Sjmacd info_last_executed_command = (VFunction *)NULL; 439421495Sjmacd} 439521495Sjmacd 439621495Sjmacd/* Move the cursor to the desired line of the window. */ 439721495SjmacdDECLARE_INFO_COMMAND (info_move_to_window_line, 439856164Sru _("Move the cursor to a specific line of the window")) 439921495Sjmacd{ 440021495Sjmacd int line; 440121495Sjmacd 440221495Sjmacd /* With no numeric argument of any kind, default to the center line. */ 440321495Sjmacd if (!info_explicit_arg && count == 1) 440421495Sjmacd line = (window->height / 2) + window->pagetop; 440521495Sjmacd else 440621495Sjmacd { 440721495Sjmacd if (count < 0) 440842664Smarkm line = (window->height + count) + window->pagetop; 440921495Sjmacd else 441042664Smarkm line = window->pagetop + count; 441121495Sjmacd } 441221495Sjmacd 441321495Sjmacd /* If the line doesn't appear in this window, make it do so. */ 441421495Sjmacd if ((line - window->pagetop) >= window->height) 441521495Sjmacd line = window->pagetop + (window->height - 1); 441621495Sjmacd 441721495Sjmacd /* If the line is too small, make it fit. */ 441821495Sjmacd if (line < window->pagetop) 441921495Sjmacd line = window->pagetop; 442021495Sjmacd 442121495Sjmacd /* If the selected line is past the bottom of the node, force it back. */ 442221495Sjmacd if (line >= window->line_count) 442321495Sjmacd line = window->line_count - 1; 442421495Sjmacd 442521495Sjmacd window->point = (window->line_starts[line] - window->node->contents); 442621495Sjmacd} 442721495Sjmacd 442821495Sjmacd/* Clear the screen and redraw its contents. Given a numeric argument, 442921495Sjmacd move the line the cursor is on to the COUNT'th line of the window. */ 443042664SmarkmDECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display")) 443121495Sjmacd{ 443221495Sjmacd if ((!info_explicit_arg && count == 1) || echo_area_is_active) 443321495Sjmacd { 443421495Sjmacd terminal_clear_screen (); 443521495Sjmacd display_clear_display (the_display); 443621495Sjmacd window_mark_chain (windows, W_UpdateWindow); 443721495Sjmacd display_update_display (windows); 443821495Sjmacd } 443921495Sjmacd else 444021495Sjmacd { 444121495Sjmacd int desired_line, point_line; 444221495Sjmacd int new_pagetop; 444321495Sjmacd 444421495Sjmacd point_line = window_line_of_point (window) - window->pagetop; 444521495Sjmacd 444621495Sjmacd if (count < 0) 444742664Smarkm desired_line = window->height + count; 444821495Sjmacd else 444942664Smarkm desired_line = count; 445021495Sjmacd 445121495Sjmacd if (desired_line < 0) 445242664Smarkm desired_line = 0; 445321495Sjmacd 445421495Sjmacd if (desired_line >= window->height) 445542664Smarkm desired_line = window->height - 1; 445621495Sjmacd 445721495Sjmacd if (desired_line == point_line) 445842664Smarkm return; 445921495Sjmacd 446021495Sjmacd new_pagetop = window->pagetop + (point_line - desired_line); 446121495Sjmacd 446221495Sjmacd set_window_pagetop (window, new_pagetop); 446321495Sjmacd } 446421495Sjmacd} 446521495Sjmacd/* This command does nothing. It is the fact that a key is bound to it 446621495Sjmacd that has meaning. See the code at the top of info_session (). */ 446742664SmarkmDECLARE_INFO_COMMAND (info_quit, _("Quit using Info")) 446821495Sjmacd{} 446921495Sjmacd 447021495Sjmacd 447121495Sjmacd/* **************************************************************** */ 447242664Smarkm/* */ 447342664Smarkm/* Reading Keys and Dispatching on Them */ 447442664Smarkm/* */ 447521495Sjmacd/* **************************************************************** */ 447621495Sjmacd 447793142Sru/* Declaration only. Special cased in info_dispatch_on_key (). 447893142Sru Doc string is to avoid ugly results with describe_key etc. */ 447993142SruDECLARE_INFO_COMMAND (info_do_lowercase_version, 448093142Sru _("Run command bound to this key's lowercase variant")) 448121495Sjmacd{} 448221495Sjmacd 448321495Sjmacdstatic void 4484146520Srudispatch_error (char *keyseq) 448521495Sjmacd{ 448621495Sjmacd char *rep; 448721495Sjmacd 448821495Sjmacd rep = pretty_keyseq (keyseq); 448921495Sjmacd 449021495Sjmacd if (!echo_area_is_active) 4491146520Sru info_error ((char *) _("Unknown command (%s)."), rep, NULL); 449221495Sjmacd else 449321495Sjmacd { 4494146520Sru char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid"))); 4495146520Sru sprintf (temp, _("`%s' is invalid"), rep); 449621495Sjmacd terminal_ring_bell (); 449721495Sjmacd inform_in_echo_area (temp); 449821495Sjmacd free (temp); 449921495Sjmacd } 450021495Sjmacd} 450121495Sjmacd 450221495Sjmacd/* Keeping track of key sequences. */ 450321495Sjmacdstatic char *info_keyseq = (char *)NULL; 450421495Sjmacdstatic int info_keyseq_index = 0; 450521495Sjmacdstatic int info_keyseq_size = 0; 450621495Sjmacdstatic int info_keyseq_displayed_p = 0; 450721495Sjmacd 450821495Sjmacd/* Initialize the length of the current key sequence. */ 450921495Sjmacdvoid 4510146520Sruinitialize_keyseq (void) 451121495Sjmacd{ 451221495Sjmacd info_keyseq_index = 0; 451321495Sjmacd info_keyseq_displayed_p = 0; 451421495Sjmacd} 451521495Sjmacd 451621495Sjmacd/* Add CHARACTER to the current key sequence. */ 451721495Sjmacdvoid 4518146520Sruadd_char_to_keyseq (char character) 451921495Sjmacd{ 452021495Sjmacd if (info_keyseq_index + 2 >= info_keyseq_size) 452121495Sjmacd info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10); 452221495Sjmacd 452321495Sjmacd info_keyseq[info_keyseq_index++] = character; 452421495Sjmacd info_keyseq[info_keyseq_index] = '\0'; 452521495Sjmacd} 452621495Sjmacd 452721495Sjmacd/* Display the current value of info_keyseq. If argument EXPECTING is 452821495Sjmacd non-zero, input is expected to be read after the key sequence is 452921495Sjmacd displayed, so add an additional prompting character to the sequence. */ 4530146520Srustatic void 4531146520Srudisplay_info_keyseq (int expecting_future_input) 453221495Sjmacd{ 453321495Sjmacd char *rep; 453421495Sjmacd 453521495Sjmacd rep = pretty_keyseq (info_keyseq); 453621495Sjmacd if (expecting_future_input) 453721495Sjmacd strcat (rep, "-"); 453821495Sjmacd 453921495Sjmacd if (echo_area_is_active) 454021495Sjmacd inform_in_echo_area (rep); 454121495Sjmacd else 454221495Sjmacd { 4543146520Sru window_message_in_echo_area (rep, NULL, NULL); 454421495Sjmacd display_cursor_at_point (active_window); 454521495Sjmacd } 454621495Sjmacd info_keyseq_displayed_p = 1; 454721495Sjmacd} 454821495Sjmacd 454921495Sjmacd/* Called by interactive commands to read a keystroke. */ 455021495Sjmacdunsigned char 4551146520Sruinfo_get_another_input_char (void) 455221495Sjmacd{ 455342664Smarkm int ready = !info_keyseq_displayed_p; /* ready if new and pending key */ 455421495Sjmacd 455521495Sjmacd /* If there isn't any input currently available, then wait a 455621495Sjmacd moment looking for input. If we don't get it fast enough, 455721495Sjmacd prompt a little bit with the current key sequence. */ 455842664Smarkm if (!info_keyseq_displayed_p) 455921495Sjmacd { 456042664Smarkm ready = 1; 456142664Smarkm if (!info_any_buffered_input_p () && 456242664Smarkm !info_input_pending_p ()) 456342664Smarkm { 456421495Sjmacd#if defined (FD_SET) 456542664Smarkm struct timeval timer; 456642664Smarkm fd_set readfds; 456721495Sjmacd 456842664Smarkm FD_ZERO (&readfds); 456942664Smarkm FD_SET (fileno (info_input_stream), &readfds); 457042664Smarkm timer.tv_sec = 1; 457142664Smarkm timer.tv_usec = 750; 457242664Smarkm ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); 457342664Smarkm#else 457442664Smarkm ready = 0; 457521495Sjmacd#endif /* FD_SET */ 457642664Smarkm } 457721495Sjmacd } 457821495Sjmacd 457921495Sjmacd if (!ready) 458021495Sjmacd display_info_keyseq (1); 458121495Sjmacd 458221495Sjmacd return (info_get_input_char ()); 458321495Sjmacd} 458421495Sjmacd 458521495Sjmacd/* Do the command associated with KEY in MAP. If the associated command is 458621495Sjmacd really a keymap, then read another key, and dispatch into that map. */ 458721495Sjmacdvoid 4588146520Sruinfo_dispatch_on_key (unsigned char key, Keymap map) 458921495Sjmacd{ 459093142Sru#if !defined(INFOKEY) 459121495Sjmacd if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert)) 459221495Sjmacd { 459321495Sjmacd if (map[ESC].type == ISKMAP) 459442664Smarkm { 459542664Smarkm map = (Keymap)map[ESC].function; 459642664Smarkm add_char_to_keyseq (ESC); 459742664Smarkm key = UnMeta (key); 459842664Smarkm info_dispatch_on_key (key, map); 459942664Smarkm } 460021495Sjmacd else 460142664Smarkm { 460242664Smarkm dispatch_error (info_keyseq); 460342664Smarkm } 460421495Sjmacd return; 460521495Sjmacd } 460693142Sru#endif /* INFOKEY */ 460721495Sjmacd 460821495Sjmacd switch (map[key].type) 460921495Sjmacd { 461021495Sjmacd case ISFUNC: 461121495Sjmacd { 461242664Smarkm VFunction *func; 461321495Sjmacd 461493142Sru func = InfoFunction(map[key].function); 461542664Smarkm if (func != (VFunction *)NULL) 461642664Smarkm { 461742664Smarkm /* Special case info_do_lowercase_version (). */ 4618146520Sru if (func == (VFunction *) info_do_lowercase_version) 461942664Smarkm { 462093142Sru#if defined(INFOKEY) 462193142Sru unsigned char lowerkey; 462293142Sru 462393142Sru lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key); 462493142Sru if (lowerkey == key) 462593142Sru { 462693142Sru add_char_to_keyseq (key); 462793142Sru dispatch_error (info_keyseq); 462893142Sru return; 462993142Sru } 463093142Sru info_dispatch_on_key (lowerkey, map); 463193142Sru#else /* !INFOKEY */ 463242664Smarkm info_dispatch_on_key (tolower (key), map); 463393142Sru#endif /* INFOKEY */ 463442664Smarkm return; 463542664Smarkm } 463621495Sjmacd 463742664Smarkm add_char_to_keyseq (key); 463821495Sjmacd 463942664Smarkm if (info_keyseq_displayed_p) 464042664Smarkm display_info_keyseq (0); 464121495Sjmacd 464242664Smarkm { 464342664Smarkm WINDOW *where; 464421495Sjmacd 464542664Smarkm where = active_window; 464693142Sru (*InfoFunction(map[key].function)) 464742664Smarkm (active_window, info_numeric_arg * info_numeric_arg_sign, key); 464821495Sjmacd 464942664Smarkm /* If we have input pending, then the last command was a prefix 465042664Smarkm command. Don't change the value of the last function vars. 465142664Smarkm Otherwise, remember the last command executed in the var 465242664Smarkm appropriate to the window in which it was executed. */ 465342664Smarkm if (!info_input_pending_p ()) 465442664Smarkm { 465542664Smarkm if (where == the_echo_area) 465693142Sru ea_last_executed_command = InfoFunction(map[key].function); 465742664Smarkm else 465893142Sru info_last_executed_command = InfoFunction(map[key].function); 465942664Smarkm } 466042664Smarkm } 466142664Smarkm } 466242664Smarkm else 466342664Smarkm { 466442664Smarkm add_char_to_keyseq (key); 466542664Smarkm dispatch_error (info_keyseq); 466642664Smarkm return; 466742664Smarkm } 466821495Sjmacd } 466921495Sjmacd break; 467021495Sjmacd 467121495Sjmacd case ISKMAP: 467221495Sjmacd add_char_to_keyseq (key); 467393142Sru if (map[key].function != (InfoCommand *)NULL) 467442664Smarkm { 467542664Smarkm unsigned char newkey; 467621495Sjmacd 467742664Smarkm newkey = info_get_another_input_char (); 467842664Smarkm info_dispatch_on_key (newkey, (Keymap)map[key].function); 467942664Smarkm } 468021495Sjmacd else 468142664Smarkm { 468242664Smarkm dispatch_error (info_keyseq); 468342664Smarkm return; 468442664Smarkm } 468521495Sjmacd break; 468621495Sjmacd } 468721495Sjmacd} 468821495Sjmacd 468921495Sjmacd/* **************************************************************** */ 469042664Smarkm/* */ 469142664Smarkm/* Numeric Arguments */ 469242664Smarkm/* */ 469321495Sjmacd/* **************************************************************** */ 469421495Sjmacd 469521495Sjmacd/* Handle C-u style numeric args, as well as M--, and M-digits. */ 469621495Sjmacd 469721495Sjmacd/* Non-zero means that an explicit argument has been passed to this 469821495Sjmacd command, as in C-u C-v. */ 469921495Sjmacdint info_explicit_arg = 0; 470021495Sjmacd 470121495Sjmacd/* The sign of the numeric argument. */ 470221495Sjmacdint info_numeric_arg_sign = 1; 470321495Sjmacd 470421495Sjmacd/* The value of the argument itself. */ 470521495Sjmacdint info_numeric_arg = 1; 470621495Sjmacd 470721495Sjmacd/* Add the current digit to the argument in progress. */ 470821495SjmacdDECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg, 470942664Smarkm _("Add this digit to the current numeric argument")) 471021495Sjmacd{ 471121495Sjmacd info_numeric_arg_digit_loop (window, 0, key); 471221495Sjmacd} 471321495Sjmacd 471421495Sjmacd/* C-u, universal argument. Multiply the current argument by 4. 471521495Sjmacd Read a key. If the key has nothing to do with arguments, then 471621495Sjmacd dispatch on it. If the key is the abort character then abort. */ 471721495SjmacdDECLARE_INFO_COMMAND (info_universal_argument, 471842664Smarkm _("Start (or multiply by 4) the current numeric argument")) 471921495Sjmacd{ 472021495Sjmacd info_numeric_arg *= 4; 472121495Sjmacd info_numeric_arg_digit_loop (window, 0, 0); 472221495Sjmacd} 472321495Sjmacd 472421495Sjmacd/* Create a default argument. */ 472521495Sjmacdvoid 4726146520Sruinfo_initialize_numeric_arg (void) 472721495Sjmacd{ 472821495Sjmacd info_numeric_arg = info_numeric_arg_sign = 1; 472921495Sjmacd info_explicit_arg = 0; 473021495Sjmacd} 473121495Sjmacd 473221495SjmacdDECLARE_INFO_COMMAND (info_numeric_arg_digit_loop, 473342664Smarkm _("Internally used by \\[universal-argument]")) 473421495Sjmacd{ 473521495Sjmacd unsigned char pure_key; 473621495Sjmacd Keymap keymap = window->keymap; 473721495Sjmacd 473821495Sjmacd while (1) 473921495Sjmacd { 474021495Sjmacd if (key) 474142664Smarkm pure_key = key; 474221495Sjmacd else 474342664Smarkm { 474442664Smarkm if (display_was_interrupted_p && !info_any_buffered_input_p ()) 474542664Smarkm display_update_display (windows); 474621495Sjmacd 474742664Smarkm if (active_window != the_echo_area) 474842664Smarkm display_cursor_at_point (active_window); 474921495Sjmacd 475042664Smarkm pure_key = key = info_get_another_input_char (); 475121495Sjmacd 475293142Sru#if !defined(INFOKEY) 475342664Smarkm if (Meta_p (key)) 475442664Smarkm add_char_to_keyseq (ESC); 475521495Sjmacd 475642664Smarkm add_char_to_keyseq (UnMeta (key)); 475793142Sru#else /* defined(INFOKEY) */ 475893142Sru add_char_to_keyseq (key); 475993142Sru#endif /* defined(INFOKEY) */ 476042664Smarkm } 476121495Sjmacd 476293142Sru#if !defined(INFOKEY) 476321495Sjmacd if (Meta_p (key)) 476442664Smarkm key = UnMeta (key); 476593142Sru#endif /* !defined(INFOKEY) */ 476621495Sjmacd 4767146520Sru if (keymap[key].type == ISFUNC 4768146520Sru && InfoFunction(keymap[key].function) 4769146520Sru == (VFunction *) info_universal_argument) 477042664Smarkm { 477142664Smarkm info_numeric_arg *= 4; 477242664Smarkm key = 0; 477342664Smarkm continue; 477442664Smarkm } 477521495Sjmacd 477693142Sru#if defined(INFOKEY) 477793142Sru if (Meta_p (key)) 477893142Sru key = UnMeta (key); 477993142Sru#endif /* !defined(INFOKEY) */ 478093142Sru 478193142Sru 478221495Sjmacd if (isdigit (key)) 478342664Smarkm { 478442664Smarkm if (info_explicit_arg) 478542664Smarkm info_numeric_arg = (info_numeric_arg * 10) + (key - '0'); 478642664Smarkm else 478742664Smarkm info_numeric_arg = (key - '0'); 478842664Smarkm info_explicit_arg = 1; 478942664Smarkm } 479021495Sjmacd else 479142664Smarkm { 479242664Smarkm if (key == '-' && !info_explicit_arg) 479342664Smarkm { 479442664Smarkm info_numeric_arg_sign = -1; 479542664Smarkm info_numeric_arg = 1; 479642664Smarkm } 479742664Smarkm else 479842664Smarkm { 479942664Smarkm info_keyseq_index--; 480042664Smarkm info_dispatch_on_key (pure_key, keymap); 480142664Smarkm return; 480242664Smarkm } 480342664Smarkm } 480421495Sjmacd key = 0; 480521495Sjmacd } 480621495Sjmacd} 480721495Sjmacd 480821495Sjmacd/* **************************************************************** */ 480942664Smarkm/* */ 481042664Smarkm/* Input Character Buffering */ 481142664Smarkm/* */ 481221495Sjmacd/* **************************************************************** */ 481321495Sjmacd 481421495Sjmacd/* Character waiting to be read next. */ 481521495Sjmacdstatic int pending_input_character = 0; 481621495Sjmacd 481721495Sjmacd/* How to make there be no pending input. */ 481821495Sjmacdstatic void 4819146520Sruinfo_clear_pending_input (void) 482021495Sjmacd{ 482121495Sjmacd pending_input_character = 0; 482221495Sjmacd} 482321495Sjmacd 482421495Sjmacd/* How to set the pending input character. */ 482521495Sjmacdstatic void 4826146520Sruinfo_set_pending_input (unsigned char key) 482721495Sjmacd{ 482821495Sjmacd pending_input_character = key; 482921495Sjmacd} 483021495Sjmacd 483121495Sjmacd/* How to see if there is any pending input. */ 483221495Sjmacdunsigned char 4833146520Sruinfo_input_pending_p (void) 483421495Sjmacd{ 483521495Sjmacd return (pending_input_character); 483621495Sjmacd} 483721495Sjmacd 483821495Sjmacd/* Largest number of characters that we can read in advance. */ 483921495Sjmacd#define MAX_INFO_INPUT_BUFFERING 512 484021495Sjmacd 484121495Sjmacdstatic int pop_index = 0, push_index = 0; 484221495Sjmacdstatic unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING]; 484321495Sjmacd 484421495Sjmacd/* Add KEY to the buffer of characters to be read. */ 484521495Sjmacdstatic void 4846146520Sruinfo_push_typeahead (unsigned char key) 484721495Sjmacd{ 484821495Sjmacd /* Flush all pending input in the case of C-g pressed. */ 484921495Sjmacd if (key == Control ('g')) 485021495Sjmacd { 485121495Sjmacd push_index = pop_index; 485221495Sjmacd info_set_pending_input (Control ('g')); 485321495Sjmacd } 485421495Sjmacd else 485521495Sjmacd { 485621495Sjmacd info_input_buffer[push_index++] = key; 4857146520Sru if ((unsigned int) push_index >= sizeof (info_input_buffer)) 485842664Smarkm push_index = 0; 485921495Sjmacd } 486021495Sjmacd} 486121495Sjmacd 486221495Sjmacd/* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */ 486321495Sjmacdstatic int 4864146520Sruinfo_input_buffer_space_available (void) 486521495Sjmacd{ 486621495Sjmacd if (pop_index > push_index) 486721495Sjmacd return (pop_index - push_index); 486821495Sjmacd else 486921495Sjmacd return (sizeof (info_input_buffer) - (push_index - pop_index)); 487021495Sjmacd} 487121495Sjmacd 487221495Sjmacd/* Get a key from the buffer of characters to be read. 487321495Sjmacd Return the key in KEY. 487421495Sjmacd Result is non-zero if there was a key, or 0 if there wasn't. */ 487521495Sjmacdstatic int 4876146520Sruinfo_get_key_from_typeahead (unsigned char *key) 487721495Sjmacd{ 487821495Sjmacd if (push_index == pop_index) 487921495Sjmacd return (0); 488021495Sjmacd 488121495Sjmacd *key = info_input_buffer[pop_index++]; 488221495Sjmacd 4883146520Sru if ((unsigned int) pop_index >= sizeof (info_input_buffer)) 488421495Sjmacd pop_index = 0; 488521495Sjmacd 488621495Sjmacd return (1); 488721495Sjmacd} 488821495Sjmacd 488921495Sjmacdint 4890146520Sruinfo_any_buffered_input_p (void) 489121495Sjmacd{ 489221495Sjmacd info_gather_typeahead (); 489321495Sjmacd return (push_index != pop_index); 489421495Sjmacd} 489521495Sjmacd 489621495Sjmacd/* If characters are available to be read, then read them and stuff them into 489721495Sjmacd info_input_buffer. Otherwise, do nothing. */ 489821495Sjmacdvoid 4899146520Sruinfo_gather_typeahead (void) 490021495Sjmacd{ 490121495Sjmacd register int i = 0; 490221495Sjmacd int tty, space_avail; 490321495Sjmacd long chars_avail; 490421495Sjmacd unsigned char input[MAX_INFO_INPUT_BUFFERING]; 490521495Sjmacd 490621495Sjmacd tty = fileno (info_input_stream); 490721495Sjmacd chars_avail = 0; 490821495Sjmacd 490921495Sjmacd space_avail = info_input_buffer_space_available (); 491021495Sjmacd 491121495Sjmacd /* If we can just find out how many characters there are to read, do so. */ 491221495Sjmacd#if defined (FIONREAD) 491321495Sjmacd { 491421495Sjmacd ioctl (tty, FIONREAD, &chars_avail); 491521495Sjmacd 491621495Sjmacd if (chars_avail > space_avail) 491721495Sjmacd chars_avail = space_avail; 491821495Sjmacd 491921495Sjmacd if (chars_avail) 492042664Smarkm chars_avail = read (tty, &input[0], chars_avail); 492121495Sjmacd } 492221495Sjmacd#else /* !FIONREAD */ 492321495Sjmacd# if defined (O_NDELAY) 492421495Sjmacd { 492521495Sjmacd int flags; 492621495Sjmacd 492721495Sjmacd flags = fcntl (tty, F_GETFL, 0); 492821495Sjmacd 492921495Sjmacd fcntl (tty, F_SETFL, (flags | O_NDELAY)); 493021495Sjmacd chars_avail = read (tty, &input[0], space_avail); 493121495Sjmacd fcntl (tty, F_SETFL, flags); 493221495Sjmacd 493321495Sjmacd if (chars_avail == -1) 493421495Sjmacd chars_avail = 0; 493521495Sjmacd } 493656164Sru# else /* !O_NDELAY */ 493756164Sru# ifdef __DJGPP__ 493856164Sru { 493956164Sru extern long pc_term_chars_avail (void); 494056164Sru 494156164Sru if (isatty (tty)) 494256164Sru chars_avail = pc_term_chars_avail (); 494356164Sru else 494456164Sru { 494556164Sru /* We could be more accurate by calling ltell, but we have no idea 494656164Sru whether tty is buffered by stdio functions, and if so, how many 494756164Sru characters are already waiting in the buffer. So we punt. */ 494856164Sru struct stat st; 494956164Sru 495056164Sru if (fstat (tty, &st) < 0) 495156164Sru chars_avail = 1; 495256164Sru else 495356164Sru chars_avail = st.st_size; 495456164Sru } 495556164Sru if (chars_avail > space_avail) 495656164Sru chars_avail = space_avail; 495756164Sru if (chars_avail) 495856164Sru chars_avail = read (tty, &input[0], chars_avail); 495956164Sru } 496056164Sru# endif/* __DJGPP__ */ 496121495Sjmacd# endif /* O_NDELAY */ 496221495Sjmacd#endif /* !FIONREAD */ 496321495Sjmacd 496421495Sjmacd while (i < chars_avail) 496521495Sjmacd { 496621495Sjmacd info_push_typeahead (input[i]); 496721495Sjmacd i++; 496821495Sjmacd } 496921495Sjmacd} 497021495Sjmacd 497121495Sjmacd/* How to read a single character. */ 497221495Sjmacdunsigned char 4973146520Sruinfo_get_input_char (void) 497421495Sjmacd{ 497521495Sjmacd unsigned char keystroke; 497621495Sjmacd 497721495Sjmacd info_gather_typeahead (); 497821495Sjmacd 497921495Sjmacd if (pending_input_character) 498021495Sjmacd { 498121495Sjmacd keystroke = pending_input_character; 498221495Sjmacd pending_input_character = 0; 498321495Sjmacd } 498421495Sjmacd else if (info_get_key_from_typeahead (&keystroke) == 0) 498521495Sjmacd { 498621495Sjmacd int rawkey; 498742664Smarkm unsigned char c; 498842664Smarkm int tty = fileno (info_input_stream); 498921495Sjmacd 499042664Smarkm /* Using stream I/O causes FIONREAD etc to fail to work 499142664Smarkm so unless someone can find a portable way of finding 499242664Smarkm out how many characters are currently buffered, we 499342664Smarkm should stay with away from stream I/O. 499442664Smarkm --Egil Kvaleberg <egilk@sn.no>, January 1997. */ 499542664Smarkm#ifdef EINTR 499642664Smarkm /* Keep reading if we got EINTR, so that we don't just exit. 499742664Smarkm --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>, 499842664Smarkm 22 Dec 1997. */ 499942664Smarkm { 500042664Smarkm int n; 500142664Smarkm do 500242664Smarkm n = read (tty, &c, 1); 500342664Smarkm while (n == -1 && errno == EINTR); 500442664Smarkm rawkey = n == 1 ? c : EOF; 500542664Smarkm } 500642664Smarkm#else 500742664Smarkm rawkey = (read (tty, &c, 1) == 1) ? c : EOF; 500842664Smarkm#endif 500942664Smarkm 501021495Sjmacd keystroke = rawkey; 501121495Sjmacd 501221495Sjmacd if (rawkey == EOF) 501342664Smarkm { 501442664Smarkm if (info_input_stream != stdin) 501542664Smarkm { 501642664Smarkm fclose (info_input_stream); 501742664Smarkm info_input_stream = stdin; 501856164Sru tty = fileno (info_input_stream); 501942664Smarkm display_inhibited = 0; 502042664Smarkm display_update_display (windows); 502142664Smarkm display_cursor_at_point (active_window); 502242664Smarkm rawkey = (read (tty, &c, 1) == 1) ? c : EOF; 502342664Smarkm keystroke = rawkey; 502442664Smarkm } 502521495Sjmacd 502642664Smarkm if (rawkey == EOF) 502742664Smarkm { 502842664Smarkm terminal_unprep_terminal (); 502942664Smarkm close_dribble_file (); 503056164Sru xexit (0); 503142664Smarkm } 503242664Smarkm } 503321495Sjmacd } 503421495Sjmacd 503521495Sjmacd if (info_dribble_file) 503621495Sjmacd dribble (keystroke); 503721495Sjmacd 503842664Smarkm return keystroke; 503921495Sjmacd} 5040