1672dff93Sespie /* session.c -- user windowing interface to Info.
2*1076333cSespie $Id: session.c,v 1.6 2006/07/17 16:12:36 espie Exp $
3fbc94a17Sniklas
4*1076333cSespie Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
53aa90977Sespie Free Software Foundation, Inc.
6fbc94a17Sniklas
7fbc94a17Sniklas This program is free software; you can redistribute it and/or modify
8fbc94a17Sniklas it under the terms of the GNU General Public License as published by
9fbc94a17Sniklas the Free Software Foundation; either version 2, or (at your option)
10fbc94a17Sniklas any later version.
11fbc94a17Sniklas
12fbc94a17Sniklas This program is distributed in the hope that it will be useful,
13fbc94a17Sniklas but WITHOUT ANY WARRANTY; without even the implied warranty of
14fbc94a17Sniklas MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15fbc94a17Sniklas GNU General Public License for more details.
16fbc94a17Sniklas
17fbc94a17Sniklas You should have received a copy of the GNU General Public License
18fbc94a17Sniklas along with this program; if not, write to the Free Software
19fbc94a17Sniklas Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20fbc94a17Sniklas
21*1076333cSespie Originally written by Brian Fox (bfox@ai.mit.edu). */
22fbc94a17Sniklas
23fbc94a17Sniklas #include "info.h"
24*1076333cSespie #include "search.h"
25fbc94a17Sniklas #include <sys/ioctl.h>
26fbc94a17Sniklas
27fbc94a17Sniklas #if defined (HAVE_SYS_TIME_H)
28fbc94a17Sniklas # include <sys/time.h>
29fbc94a17Sniklas # define HAVE_STRUCT_TIMEVAL
30fbc94a17Sniklas #endif /* HAVE_SYS_TIME_H */
31fbc94a17Sniklas
32fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
33fbc94a17Sniklas # include "man.h"
34fbc94a17Sniklas #endif
35fbc94a17Sniklas
36*1076333cSespie static void info_clear_pending_input (void);
37*1076333cSespie static void info_set_pending_input (unsigned char key);
38*1076333cSespie static void info_handle_pointer (char *label, WINDOW *window);
39*1076333cSespie static void display_info_keyseq (int expecting_future_input);
40*1076333cSespie char *node_printed_rep (NODE *node);
41fbc94a17Sniklas
42fbc94a17Sniklas /* **************************************************************** */
43fbc94a17Sniklas /* */
44fbc94a17Sniklas /* Running an Info Session */
45fbc94a17Sniklas /* */
46fbc94a17Sniklas /* **************************************************************** */
47fbc94a17Sniklas
48fbc94a17Sniklas /* The place that we are reading input from. */
49840175f0Skstailey static FILE *info_input_stream = NULL;
50fbc94a17Sniklas
51fbc94a17Sniklas /* The last executed command. */
52840175f0Skstailey VFunction *info_last_executed_command = NULL;
53fbc94a17Sniklas
54fbc94a17Sniklas /* Becomes non-zero when 'q' is typed to an Info window. */
55fbc94a17Sniklas int quit_info_immediately = 0;
56fbc94a17Sniklas
57fbc94a17Sniklas /* Array of structures describing for each window which nodes have been
58fbc94a17Sniklas visited in that window. */
59840175f0Skstailey INFO_WINDOW **info_windows = NULL;
60fbc94a17Sniklas
61fbc94a17Sniklas /* Where to add the next window, if we need to add one. */
62fbc94a17Sniklas static int info_windows_index = 0;
63fbc94a17Sniklas
64840175f0Skstailey /* Number of slots allocated to `info_windows'. */
65fbc94a17Sniklas static int info_windows_slots = 0;
66fbc94a17Sniklas
67*1076333cSespie void remember_window_and_node (WINDOW *window, NODE *node);
68*1076333cSespie void forget_window_and_nodes (WINDOW *window);
69*1076333cSespie void display_startup_message_and_start (void);
70fbc94a17Sniklas
71fbc94a17Sniklas /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
72fbc94a17Sniklas For each loaded node, create a new window. Always split the largest of the
73fbc94a17Sniklas available windows. */
74fbc94a17Sniklas void
begin_multiple_window_info_session(char * filename,char ** nodenames)75*1076333cSespie begin_multiple_window_info_session (char *filename, char **nodenames)
76fbc94a17Sniklas {
77fbc94a17Sniklas register int i;
78fbc94a17Sniklas WINDOW *window = (WINDOW *)NULL;
79fbc94a17Sniklas
80fbc94a17Sniklas for (i = 0; nodenames[i]; i++)
81fbc94a17Sniklas {
82fbc94a17Sniklas NODE *node;
83fbc94a17Sniklas
84fbc94a17Sniklas node = info_get_node (filename, nodenames[i]);
85fbc94a17Sniklas
86fbc94a17Sniklas if (!node)
87fbc94a17Sniklas break;
88fbc94a17Sniklas
89fbc94a17Sniklas /* If this is the first node, initialize the info session. */
90fbc94a17Sniklas if (!window)
91fbc94a17Sniklas {
92840175f0Skstailey initialize_info_session (node, 1);
93fbc94a17Sniklas window = active_window;
94fbc94a17Sniklas }
95fbc94a17Sniklas else
96fbc94a17Sniklas {
97fbc94a17Sniklas /* Find the largest window in WINDOWS, and make that be the active
98fbc94a17Sniklas one. Then split it and add our window and node to the list
99fbc94a17Sniklas of remembered windows and nodes. Then tile the windows. */
100672dff93Sespie WINDOW *win, *largest = NULL;
101fbc94a17Sniklas int max_height = 0;
102fbc94a17Sniklas
103fbc94a17Sniklas for (win = windows; win; win = win->next)
104fbc94a17Sniklas if (win->height > max_height)
105fbc94a17Sniklas {
106fbc94a17Sniklas max_height = win->height;
107fbc94a17Sniklas largest = win;
108fbc94a17Sniklas }
109fbc94a17Sniklas
110fbc94a17Sniklas if (!largest)
111fbc94a17Sniklas {
112fbc94a17Sniklas display_update_display (windows);
113*1076333cSespie info_error ((char *) msg_cant_find_window, NULL, NULL);
114fbc94a17Sniklas info_session ();
115672dff93Sespie xexit (0);
116fbc94a17Sniklas }
117fbc94a17Sniklas
118fbc94a17Sniklas active_window = largest;
119fbc94a17Sniklas window = window_make_window (node);
120fbc94a17Sniklas if (window)
121fbc94a17Sniklas {
122fbc94a17Sniklas window_tile_windows (TILE_INTERNALS);
123fbc94a17Sniklas remember_window_and_node (window, node);
124fbc94a17Sniklas }
125fbc94a17Sniklas else
126fbc94a17Sniklas {
127fbc94a17Sniklas display_update_display (windows);
128*1076333cSespie info_error ((char *) msg_win_too_small, NULL, NULL);
129fbc94a17Sniklas info_session ();
130672dff93Sespie xexit (0);
131fbc94a17Sniklas }
132fbc94a17Sniklas }
133fbc94a17Sniklas }
134fbc94a17Sniklas display_startup_message_and_start ();
135fbc94a17Sniklas }
136fbc94a17Sniklas
137fbc94a17Sniklas /* Start an info session with INITIAL_NODE, and an error message in the echo
138fbc94a17Sniklas area made from FORMAT and ARG. */
139fbc94a17Sniklas void
begin_info_session_with_error(NODE * initial_node,char * format,void * arg1,void * arg2)140*1076333cSespie begin_info_session_with_error (NODE *initial_node, char *format,
141*1076333cSespie void *arg1, void *arg2)
142fbc94a17Sniklas {
143840175f0Skstailey initialize_info_session (initial_node, 1);
144672dff93Sespie info_error (format, arg1, arg2);
145fbc94a17Sniklas info_session ();
146fbc94a17Sniklas }
147fbc94a17Sniklas
148fbc94a17Sniklas /* Start an info session with INITIAL_NODE. */
149fbc94a17Sniklas void
begin_info_session(NODE * initial_node)150*1076333cSespie begin_info_session (NODE *initial_node)
151fbc94a17Sniklas {
152840175f0Skstailey initialize_info_session (initial_node, 1);
153fbc94a17Sniklas display_startup_message_and_start ();
154fbc94a17Sniklas }
155fbc94a17Sniklas
156fbc94a17Sniklas void
display_startup_message_and_start(void)157*1076333cSespie display_startup_message_and_start (void)
158fbc94a17Sniklas {
159fbc94a17Sniklas char *format;
160fbc94a17Sniklas
161fbc94a17Sniklas format = replace_in_documentation
162*1076333cSespie ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."),
163*1076333cSespie 0);
164fbc94a17Sniklas
165*1076333cSespie window_message_in_echo_area (format, VERSION, NULL);
166fbc94a17Sniklas info_session ();
167fbc94a17Sniklas }
168fbc94a17Sniklas
169fbc94a17Sniklas /* Run an info session with an already initialized window and node. */
170fbc94a17Sniklas void
info_session(void)171*1076333cSespie info_session (void)
172fbc94a17Sniklas {
173fbc94a17Sniklas display_update_display (windows);
174840175f0Skstailey info_last_executed_command = NULL;
175fbc94a17Sniklas info_read_and_dispatch ();
176fbc94a17Sniklas /* On program exit, leave the cursor at the bottom of the window, and
177fbc94a17Sniklas restore the terminal I/O. */
178fbc94a17Sniklas terminal_goto_xy (0, screenheight - 1);
179fbc94a17Sniklas terminal_clear_to_eol ();
180fbc94a17Sniklas fflush (stdout);
181fbc94a17Sniklas terminal_unprep_terminal ();
182fbc94a17Sniklas close_dribble_file ();
183fbc94a17Sniklas }
184fbc94a17Sniklas
185fbc94a17Sniklas /* Here is a window-location dependent event loop. Called from the
186fbc94a17Sniklas functions info_session (), and from read_xxx_in_echo_area (). */
187fbc94a17Sniklas void
info_read_and_dispatch(void)188*1076333cSespie info_read_and_dispatch (void)
189fbc94a17Sniklas {
190fbc94a17Sniklas unsigned char key;
191fbc94a17Sniklas int done;
192fbc94a17Sniklas done = 0;
193fbc94a17Sniklas
194fbc94a17Sniklas while (!done && !quit_info_immediately)
195fbc94a17Sniklas {
196*1076333cSespie int lk = 0;
197fbc94a17Sniklas
198fbc94a17Sniklas /* If we haven't just gone up or down a line, there is no
199fbc94a17Sniklas goal column for this window. */
200*1076333cSespie if ((info_last_executed_command != (VFunction *) info_next_line) &&
201*1076333cSespie (info_last_executed_command != (VFunction *) info_prev_line))
202fbc94a17Sniklas active_window->goal_column = -1;
203fbc94a17Sniklas
204fbc94a17Sniklas if (echo_area_is_active)
205fbc94a17Sniklas {
206fbc94a17Sniklas lk = echo_area_last_command_was_kill;
207fbc94a17Sniklas echo_area_prep_read ();
208fbc94a17Sniklas }
209fbc94a17Sniklas
210fbc94a17Sniklas if (!info_any_buffered_input_p ())
211fbc94a17Sniklas display_update_display (windows);
212fbc94a17Sniklas
213fbc94a17Sniklas display_cursor_at_point (active_window);
214fbc94a17Sniklas info_initialize_numeric_arg ();
215fbc94a17Sniklas
216fbc94a17Sniklas initialize_keyseq ();
217fbc94a17Sniklas key = info_get_input_char ();
218fbc94a17Sniklas
219fbc94a17Sniklas /* No errors yet. We just read a character, that's all. Only clear
220fbc94a17Sniklas the echo_area if it is not currently active. */
221fbc94a17Sniklas if (!echo_area_is_active)
222fbc94a17Sniklas window_clear_echo_area ();
223fbc94a17Sniklas
224fbc94a17Sniklas info_error_was_printed = 0;
225fbc94a17Sniklas
226fbc94a17Sniklas /* Do the selected command. */
227fbc94a17Sniklas info_dispatch_on_key (key, active_window->keymap);
228fbc94a17Sniklas
229fbc94a17Sniklas if (echo_area_is_active)
230fbc94a17Sniklas {
231fbc94a17Sniklas /* Echo area commands that do killing increment the value of
232fbc94a17Sniklas ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no
233fbc94a17Sniklas change in the value of this variable, the last command
234fbc94a17Sniklas executed was not a kill command. */
235fbc94a17Sniklas if (lk == echo_area_last_command_was_kill)
236fbc94a17Sniklas echo_area_last_command_was_kill = 0;
237fbc94a17Sniklas
238*1076333cSespie if (ea_last_executed_command == (VFunction *) ea_newline ||
239fbc94a17Sniklas info_aborted_echo_area)
240fbc94a17Sniklas {
241fbc94a17Sniklas ea_last_executed_command = (VFunction *)NULL;
242fbc94a17Sniklas done = 1;
243fbc94a17Sniklas }
244fbc94a17Sniklas
245*1076333cSespie if (info_last_executed_command == (VFunction *) info_quit)
246fbc94a17Sniklas quit_info_immediately = 1;
247fbc94a17Sniklas }
248*1076333cSespie else if (info_last_executed_command == (VFunction *) info_quit)
249fbc94a17Sniklas done = 1;
250fbc94a17Sniklas }
251fbc94a17Sniklas }
252fbc94a17Sniklas
253fbc94a17Sniklas /* Found in signals.c */
254*1076333cSespie extern void initialize_info_signal_handler (void );
255fbc94a17Sniklas
256fbc94a17Sniklas /* Initialize the first info session by starting the terminal, window,
257840175f0Skstailey and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
258fbc94a17Sniklas void
initialize_info_session(NODE * node,int clear_screen)259*1076333cSespie initialize_info_session (NODE *node, int clear_screen)
260fbc94a17Sniklas {
261840175f0Skstailey char *term_name = getenv ("TERM");
262fbc94a17Sniklas terminal_initialize_terminal (term_name);
263fbc94a17Sniklas
264fbc94a17Sniklas if (terminal_is_dumb_p)
265fbc94a17Sniklas {
266fbc94a17Sniklas if (!term_name)
267fbc94a17Sniklas term_name = "dumb";
268fbc94a17Sniklas
269*1076333cSespie info_error ((char *) msg_term_too_dumb, term_name, NULL);
270672dff93Sespie xexit (1);
271fbc94a17Sniklas }
27240248eceSdownsj
273840175f0Skstailey if (clear_screen)
274840175f0Skstailey {
275840175f0Skstailey terminal_prep_terminal ();
276fbc94a17Sniklas terminal_clear_screen ();
277840175f0Skstailey }
278840175f0Skstailey
279fbc94a17Sniklas initialize_info_keymaps ();
280fbc94a17Sniklas window_initialize_windows (screenwidth, screenheight);
281fbc94a17Sniklas initialize_info_signal_handler ();
282fbc94a17Sniklas display_initialize_display (screenwidth, screenheight);
283672dff93Sespie info_set_node_of_window (0, active_window, node);
284fbc94a17Sniklas
285fbc94a17Sniklas /* Tell the window system how to notify us when a window needs to be
286fbc94a17Sniklas asynchronously deleted (e.g., user resizes window very small). */
287*1076333cSespie window_deletion_notifier = (VFunction *) forget_window_and_nodes;
288fbc94a17Sniklas
289840175f0Skstailey /* If input has not been redirected yet, make it come from unbuffered
290840175f0Skstailey standard input. */
291fbc94a17Sniklas if (!info_input_stream)
292840175f0Skstailey {
293840175f0Skstailey setbuf (stdin, NULL);
294fbc94a17Sniklas info_input_stream = stdin;
295840175f0Skstailey }
296fbc94a17Sniklas
297fbc94a17Sniklas info_windows_initialized_p = 1;
298fbc94a17Sniklas }
299fbc94a17Sniklas
300fbc94a17Sniklas /* Tell Info that input is coming from the file FILENAME. */
301fbc94a17Sniklas void
info_set_input_from_file(char * filename)302*1076333cSespie info_set_input_from_file (char *filename)
303fbc94a17Sniklas {
304fbc94a17Sniklas FILE *stream;
305fbc94a17Sniklas
306672dff93Sespie /* Input may include binary characters. */
307672dff93Sespie stream = fopen (filename, FOPEN_RBIN);
308fbc94a17Sniklas
309fbc94a17Sniklas if (!stream)
310fbc94a17Sniklas return;
311fbc94a17Sniklas
312fbc94a17Sniklas if ((info_input_stream != (FILE *)NULL) &&
313fbc94a17Sniklas (info_input_stream != stdin))
314fbc94a17Sniklas fclose (info_input_stream);
315fbc94a17Sniklas
316fbc94a17Sniklas info_input_stream = stream;
317fbc94a17Sniklas
318fbc94a17Sniklas if (stream != stdin)
319fbc94a17Sniklas display_inhibited = 1;
320fbc94a17Sniklas }
321fbc94a17Sniklas
322fbc94a17Sniklas /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
323fbc94a17Sniklas static INFO_WINDOW *
get_info_window_of_window(WINDOW * window)324*1076333cSespie get_info_window_of_window (WINDOW *window)
325fbc94a17Sniklas {
326fbc94a17Sniklas register int i;
327fbc94a17Sniklas INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
328fbc94a17Sniklas
329fbc94a17Sniklas for (i = 0; info_windows && (info_win = info_windows[i]); i++)
330fbc94a17Sniklas if (info_win->window == window)
331fbc94a17Sniklas break;
332fbc94a17Sniklas
333fbc94a17Sniklas return (info_win);
334fbc94a17Sniklas }
335fbc94a17Sniklas
336fbc94a17Sniklas /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
337fbc94a17Sniklas values if the window and node are the same as the current one being
338fbc94a17Sniklas displayed. */
339fbc94a17Sniklas void
set_remembered_pagetop_and_point(WINDOW * window)340*1076333cSespie set_remembered_pagetop_and_point (WINDOW *window)
341fbc94a17Sniklas {
342fbc94a17Sniklas INFO_WINDOW *info_win;
343fbc94a17Sniklas
344fbc94a17Sniklas info_win = get_info_window_of_window (window);
345fbc94a17Sniklas
346fbc94a17Sniklas if (!info_win)
347fbc94a17Sniklas return;
348fbc94a17Sniklas
349fbc94a17Sniklas if (info_win->nodes_index &&
350fbc94a17Sniklas (info_win->nodes[info_win->current] == window->node))
351fbc94a17Sniklas {
352fbc94a17Sniklas info_win->pagetops[info_win->current] = window->pagetop;
353fbc94a17Sniklas info_win->points[info_win->current] = window->point;
354fbc94a17Sniklas }
355fbc94a17Sniklas }
356fbc94a17Sniklas
357fbc94a17Sniklas void
remember_window_and_node(WINDOW * window,NODE * node)358*1076333cSespie remember_window_and_node (WINDOW *window, NODE *node)
359fbc94a17Sniklas {
360fbc94a17Sniklas /* See if we already have this window in our list. */
361840175f0Skstailey INFO_WINDOW *info_win = get_info_window_of_window (window);
362fbc94a17Sniklas
363fbc94a17Sniklas /* If the window wasn't already on our list, then make a new entry. */
364fbc94a17Sniklas if (!info_win)
365fbc94a17Sniklas {
366fbc94a17Sniklas info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
367fbc94a17Sniklas info_win->window = window;
368fbc94a17Sniklas info_win->nodes = (NODE **)NULL;
369fbc94a17Sniklas info_win->pagetops = (int *)NULL;
370fbc94a17Sniklas info_win->points = (long *)NULL;
371fbc94a17Sniklas info_win->current = 0;
372fbc94a17Sniklas info_win->nodes_index = 0;
373fbc94a17Sniklas info_win->nodes_slots = 0;
374fbc94a17Sniklas
375fbc94a17Sniklas add_pointer_to_array (info_win, info_windows_index, info_windows,
376fbc94a17Sniklas info_windows_slots, 10, INFO_WINDOW *);
377fbc94a17Sniklas }
378fbc94a17Sniklas
379fbc94a17Sniklas /* If this node, the current pagetop, and the current point are the
380840175f0Skstailey same as the current saved node and pagetop, don't really add this to
381840175f0Skstailey the list of history nodes. This may happen only at the very
382840175f0Skstailey beginning of the program, I'm not sure. --karl */
383840175f0Skstailey if (info_win->nodes
384840175f0Skstailey && info_win->current >= 0
385840175f0Skstailey && info_win->nodes[info_win->current]->contents == node->contents
386840175f0Skstailey && info_win->pagetops[info_win->current] == window->pagetop
387840175f0Skstailey && info_win->points[info_win->current] == window->point)
388fbc94a17Sniklas return;
389fbc94a17Sniklas
390fbc94a17Sniklas /* Remember this node, the currently displayed pagetop, and the current
391fbc94a17Sniklas location of point in this window. Because we are updating pagetops
392fbc94a17Sniklas and points as well as nodes, it is more efficient to avoid the
393fbc94a17Sniklas add_pointer_to_array macro here. */
394fbc94a17Sniklas if (info_win->nodes_index + 2 >= info_win->nodes_slots)
395fbc94a17Sniklas {
396840175f0Skstailey info_win->nodes_slots += 20;
397840175f0Skstailey info_win->nodes = (NODE **) xrealloc (info_win->nodes,
398840175f0Skstailey info_win->nodes_slots * sizeof (NODE *));
399840175f0Skstailey info_win->pagetops = (int *) xrealloc (info_win->pagetops,
400840175f0Skstailey info_win->nodes_slots * sizeof (int));
401840175f0Skstailey info_win->points = (long *) xrealloc (info_win->points,
402840175f0Skstailey info_win->nodes_slots * sizeof (long));
403fbc94a17Sniklas }
404fbc94a17Sniklas
405fbc94a17Sniklas info_win->nodes[info_win->nodes_index] = node;
406fbc94a17Sniklas info_win->pagetops[info_win->nodes_index] = window->pagetop;
407fbc94a17Sniklas info_win->points[info_win->nodes_index] = window->point;
408fbc94a17Sniklas info_win->current = info_win->nodes_index++;
409840175f0Skstailey info_win->nodes[info_win->nodes_index] = NULL;
410fbc94a17Sniklas info_win->pagetops[info_win->nodes_index] = 0;
411fbc94a17Sniklas info_win->points[info_win->nodes_index] = 0;
412fbc94a17Sniklas }
413fbc94a17Sniklas
414fbc94a17Sniklas #define DEBUG_FORGET_WINDOW_AND_NODES
415fbc94a17Sniklas #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
416fbc94a17Sniklas static void
consistency_check_info_windows(void)417*1076333cSespie consistency_check_info_windows (void)
418fbc94a17Sniklas {
419fbc94a17Sniklas register int i;
420fbc94a17Sniklas
421fbc94a17Sniklas for (i = 0; i < info_windows_index; i++)
422fbc94a17Sniklas {
423fbc94a17Sniklas WINDOW *win;
424fbc94a17Sniklas
425fbc94a17Sniklas for (win = windows; win; win = win->next)
426fbc94a17Sniklas if (win == info_windows[i]->window)
427fbc94a17Sniklas break;
428fbc94a17Sniklas
429fbc94a17Sniklas if (!win)
430fbc94a17Sniklas abort ();
431fbc94a17Sniklas }
432fbc94a17Sniklas }
433fbc94a17Sniklas #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
434fbc94a17Sniklas
435fbc94a17Sniklas /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
436fbc94a17Sniklas void
forget_window_and_nodes(WINDOW * window)437*1076333cSespie forget_window_and_nodes (WINDOW *window)
438fbc94a17Sniklas {
439fbc94a17Sniklas register int i;
440fbc94a17Sniklas INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
441fbc94a17Sniklas
442fbc94a17Sniklas for (i = 0; info_windows && (info_win = info_windows[i]); i++)
443fbc94a17Sniklas if (info_win->window == window)
444fbc94a17Sniklas break;
445fbc94a17Sniklas
446fbc94a17Sniklas /* If we found the window to forget, then do so. */
447fbc94a17Sniklas if (info_win)
448fbc94a17Sniklas {
449fbc94a17Sniklas while (i < info_windows_index)
450fbc94a17Sniklas {
451fbc94a17Sniklas info_windows[i] = info_windows[i + 1];
452fbc94a17Sniklas i++;
453fbc94a17Sniklas }
454fbc94a17Sniklas
455fbc94a17Sniklas info_windows_index--;
456fbc94a17Sniklas info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
457fbc94a17Sniklas
458fbc94a17Sniklas if (info_win->nodes)
459fbc94a17Sniklas {
460fbc94a17Sniklas /* Free the node structures which held onto internal node contents
461fbc94a17Sniklas here. This doesn't free the contents; we have a garbage collector
462fbc94a17Sniklas which does that. */
463fbc94a17Sniklas for (i = 0; info_win->nodes[i]; i++)
464fbc94a17Sniklas if (internal_info_node_p (info_win->nodes[i]))
465fbc94a17Sniklas free (info_win->nodes[i]);
466fbc94a17Sniklas free (info_win->nodes);
467fbc94a17Sniklas
468fbc94a17Sniklas maybe_free (info_win->pagetops);
469fbc94a17Sniklas maybe_free (info_win->points);
470fbc94a17Sniklas }
471fbc94a17Sniklas
472fbc94a17Sniklas free (info_win);
473fbc94a17Sniklas }
474fbc94a17Sniklas #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
475fbc94a17Sniklas consistency_check_info_windows ();
476fbc94a17Sniklas #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
477fbc94a17Sniklas }
478fbc94a17Sniklas
479fbc94a17Sniklas /* Set WINDOW to show NODE. Remember the new window in our list of Info
480fbc94a17Sniklas windows. If we are doing automatic footnote display, also try to display
481672dff93Sespie the footnotes for this window. If REMEMBER is nonzero, first call
482672dff93Sespie set_remembered_pagetop_and_point. */
483fbc94a17Sniklas void
info_set_node_of_window(int remember,WINDOW * window,NODE * node)484*1076333cSespie info_set_node_of_window (int remember, WINDOW *window, NODE *node)
485fbc94a17Sniklas {
486672dff93Sespie if (remember)
487672dff93Sespie set_remembered_pagetop_and_point (window);
488672dff93Sespie
489fbc94a17Sniklas /* Put this node into the window. */
490fbc94a17Sniklas window_set_node_of_window (window, node);
491fbc94a17Sniklas
492fbc94a17Sniklas /* Remember this node and window in our list of info windows. */
493fbc94a17Sniklas remember_window_and_node (window, node);
494fbc94a17Sniklas
495fbc94a17Sniklas /* If doing auto-footnote display/undisplay, show the footnotes belonging
496fbc94a17Sniklas to this window's node. */
497fbc94a17Sniklas if (auto_footnotes_p)
498fbc94a17Sniklas info_get_or_remove_footnotes (window);
499fbc94a17Sniklas }
500fbc94a17Sniklas
501fbc94a17Sniklas
502fbc94a17Sniklas /* **************************************************************** */
503fbc94a17Sniklas /* */
504fbc94a17Sniklas /* Info Movement Commands */
505fbc94a17Sniklas /* */
506fbc94a17Sniklas /* **************************************************************** */
507fbc94a17Sniklas
508fbc94a17Sniklas /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
509fbc94a17Sniklas to do so. */
510fbc94a17Sniklas void
set_window_pagetop(WINDOW * window,int desired_top)511*1076333cSespie set_window_pagetop (WINDOW *window, int desired_top)
512fbc94a17Sniklas {
513fbc94a17Sniklas int point_line, old_pagetop;
514fbc94a17Sniklas
515fbc94a17Sniklas if (desired_top < 0)
516fbc94a17Sniklas desired_top = 0;
517fbc94a17Sniklas else if (desired_top > window->line_count)
518fbc94a17Sniklas desired_top = window->line_count - 1;
519fbc94a17Sniklas
520fbc94a17Sniklas if (window->pagetop == desired_top)
521fbc94a17Sniklas return;
522fbc94a17Sniklas
523fbc94a17Sniklas old_pagetop = window->pagetop;
524fbc94a17Sniklas window->pagetop = desired_top;
525fbc94a17Sniklas
526fbc94a17Sniklas /* Make sure that point appears in this window. */
527fbc94a17Sniklas point_line = window_line_of_point (window);
528fbc94a17Sniklas if ((point_line < window->pagetop) ||
529fbc94a17Sniklas ((point_line - window->pagetop) > window->height - 1))
530fbc94a17Sniklas window->point =
531fbc94a17Sniklas window->line_starts[window->pagetop] - window->node->contents;
532fbc94a17Sniklas
533fbc94a17Sniklas window->flags |= W_UpdateWindow;
534fbc94a17Sniklas
535fbc94a17Sniklas /* Find out which direction to scroll, and scroll the window in that
536fbc94a17Sniklas direction. Do this only if there would be a savings in redisplay
537fbc94a17Sniklas time. This is true if the amount to scroll is less than the height
538fbc94a17Sniklas of the window, and if the number of lines scrolled would be greater
539fbc94a17Sniklas than 10 % of the window's height. */
540fbc94a17Sniklas if (old_pagetop < desired_top)
541fbc94a17Sniklas {
542fbc94a17Sniklas int start, end, amount;
543fbc94a17Sniklas
544fbc94a17Sniklas amount = desired_top - old_pagetop;
545fbc94a17Sniklas
546fbc94a17Sniklas if ((amount >= window->height) ||
547fbc94a17Sniklas (((window->height - amount) * 10) < window->height))
548fbc94a17Sniklas return;
549fbc94a17Sniklas
550fbc94a17Sniklas start = amount + window->first_row;
551fbc94a17Sniklas end = window->height + window->first_row;
552fbc94a17Sniklas
553fbc94a17Sniklas display_scroll_display (start, end, -amount);
554fbc94a17Sniklas }
555fbc94a17Sniklas else
556fbc94a17Sniklas {
557fbc94a17Sniklas int start, end, amount;
558fbc94a17Sniklas
559fbc94a17Sniklas amount = old_pagetop - desired_top;
560fbc94a17Sniklas
561fbc94a17Sniklas if ((amount >= window->height) ||
562fbc94a17Sniklas (((window->height - amount) * 10) < window->height))
563fbc94a17Sniklas return;
564fbc94a17Sniklas
565fbc94a17Sniklas start = window->first_row;
566fbc94a17Sniklas end = (window->first_row + window->height) - amount;
567fbc94a17Sniklas display_scroll_display (start, end, amount);
568fbc94a17Sniklas }
569fbc94a17Sniklas }
570fbc94a17Sniklas
571fbc94a17Sniklas /* Immediately make WINDOW->point visible on the screen, and move the
572fbc94a17Sniklas terminal cursor there. */
573fbc94a17Sniklas static void
info_show_point(WINDOW * window)574*1076333cSespie info_show_point (WINDOW *window)
575fbc94a17Sniklas {
576fbc94a17Sniklas int old_pagetop;
577fbc94a17Sniklas
578fbc94a17Sniklas old_pagetop = window->pagetop;
579fbc94a17Sniklas window_adjust_pagetop (window);
580fbc94a17Sniklas if (old_pagetop != window->pagetop)
581fbc94a17Sniklas {
582fbc94a17Sniklas int new_pagetop;
583fbc94a17Sniklas
584fbc94a17Sniklas new_pagetop = window->pagetop;
585fbc94a17Sniklas window->pagetop = old_pagetop;
586fbc94a17Sniklas set_window_pagetop (window, new_pagetop);
587fbc94a17Sniklas }
588fbc94a17Sniklas
589fbc94a17Sniklas if (window->flags & W_UpdateWindow)
590fbc94a17Sniklas display_update_one_window (window);
591fbc94a17Sniklas
592fbc94a17Sniklas display_cursor_at_point (window);
593fbc94a17Sniklas }
594fbc94a17Sniklas
595fbc94a17Sniklas /* Move WINDOW->point from OLD line index to NEW line index. */
596fbc94a17Sniklas static void
move_to_new_line(int old,int new,WINDOW * window)597*1076333cSespie move_to_new_line (int old, int new, WINDOW *window)
598fbc94a17Sniklas {
599fbc94a17Sniklas if (old == -1)
600fbc94a17Sniklas {
601*1076333cSespie info_error ((char *) msg_cant_find_point, NULL, NULL);
602fbc94a17Sniklas }
603fbc94a17Sniklas else
604fbc94a17Sniklas {
605fbc94a17Sniklas int goal;
606fbc94a17Sniklas
607fbc94a17Sniklas if (new >= window->line_count || new < 0)
608fbc94a17Sniklas return;
609fbc94a17Sniklas
610fbc94a17Sniklas goal = window_get_goal_column (window);
611fbc94a17Sniklas window->goal_column = goal;
612fbc94a17Sniklas
613fbc94a17Sniklas window->point = window->line_starts[new] - window->node->contents;
614fbc94a17Sniklas window->point += window_chars_to_goal (window->line_starts[new], goal);
615fbc94a17Sniklas info_show_point (window);
616fbc94a17Sniklas }
617fbc94a17Sniklas }
618fbc94a17Sniklas
619fbc94a17Sniklas /* Move WINDOW's point down to the next line if possible. */
620840175f0Skstailey DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
621fbc94a17Sniklas {
622fbc94a17Sniklas int old_line, new_line;
623fbc94a17Sniklas
624fbc94a17Sniklas if (count < 0)
625fbc94a17Sniklas info_prev_line (window, -count, key);
626fbc94a17Sniklas else
627fbc94a17Sniklas {
628fbc94a17Sniklas old_line = window_line_of_point (window);
629fbc94a17Sniklas new_line = old_line + count;
630fbc94a17Sniklas move_to_new_line (old_line, new_line, window);
631fbc94a17Sniklas }
632fbc94a17Sniklas }
633fbc94a17Sniklas
634fbc94a17Sniklas /* Move WINDOW's point up to the previous line if possible. */
635840175f0Skstailey DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
636fbc94a17Sniklas {
637fbc94a17Sniklas int old_line, new_line;
638fbc94a17Sniklas
639fbc94a17Sniklas if (count < 0)
640fbc94a17Sniklas info_next_line (window, -count, key);
641fbc94a17Sniklas else
642fbc94a17Sniklas {
643fbc94a17Sniklas old_line = window_line_of_point (window);
644fbc94a17Sniklas new_line = old_line - count;
645fbc94a17Sniklas move_to_new_line (old_line, new_line, window);
646fbc94a17Sniklas }
647fbc94a17Sniklas }
648fbc94a17Sniklas
649fbc94a17Sniklas /* Move WINDOW's point to the end of the true line. */
650840175f0Skstailey DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
651fbc94a17Sniklas {
652fbc94a17Sniklas register int point, len;
653fbc94a17Sniklas register char *buffer;
654fbc94a17Sniklas
655fbc94a17Sniklas buffer = window->node->contents;
656fbc94a17Sniklas len = window->node->nodelen;
657fbc94a17Sniklas
658fbc94a17Sniklas for (point = window->point;
659fbc94a17Sniklas (point < len) && (buffer[point] != '\n');
660fbc94a17Sniklas point++);
661fbc94a17Sniklas
662fbc94a17Sniklas if (point != window->point)
663fbc94a17Sniklas {
664fbc94a17Sniklas window->point = point;
665fbc94a17Sniklas info_show_point (window);
666fbc94a17Sniklas }
667fbc94a17Sniklas }
668fbc94a17Sniklas
669fbc94a17Sniklas /* Move WINDOW's point to the beginning of the true line. */
670840175f0Skstailey DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
671fbc94a17Sniklas {
672fbc94a17Sniklas register int point;
673fbc94a17Sniklas register char *buffer;
674fbc94a17Sniklas
675fbc94a17Sniklas buffer = window->node->contents;
676fbc94a17Sniklas point = window->point;
677fbc94a17Sniklas
678fbc94a17Sniklas for (; (point) && (buffer[point - 1] != '\n'); point--);
679fbc94a17Sniklas
680672dff93Sespie /* If at a line start already, do nothing. */
681fbc94a17Sniklas if (point != window->point)
682fbc94a17Sniklas {
683fbc94a17Sniklas window->point = point;
684fbc94a17Sniklas info_show_point (window);
685fbc94a17Sniklas }
686fbc94a17Sniklas }
687fbc94a17Sniklas
688fbc94a17Sniklas /* Move point forward in the node. */
689840175f0Skstailey DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
690fbc94a17Sniklas {
691fbc94a17Sniklas if (count < 0)
692fbc94a17Sniklas info_backward_char (window, -count, key);
693fbc94a17Sniklas else
694fbc94a17Sniklas {
695fbc94a17Sniklas window->point += count;
696fbc94a17Sniklas
697fbc94a17Sniklas if (window->point >= window->node->nodelen)
698fbc94a17Sniklas window->point = window->node->nodelen - 1;
699fbc94a17Sniklas
700fbc94a17Sniklas info_show_point (window);
701fbc94a17Sniklas }
702fbc94a17Sniklas }
703fbc94a17Sniklas
704fbc94a17Sniklas /* Move point backward in the node. */
705840175f0Skstailey DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
706fbc94a17Sniklas {
707fbc94a17Sniklas if (count < 0)
708fbc94a17Sniklas info_forward_char (window, -count, key);
709fbc94a17Sniklas else
710fbc94a17Sniklas {
711fbc94a17Sniklas window->point -= count;
712fbc94a17Sniklas
713fbc94a17Sniklas if (window->point < 0)
714fbc94a17Sniklas window->point = 0;
715fbc94a17Sniklas
716fbc94a17Sniklas info_show_point (window);
717fbc94a17Sniklas }
718fbc94a17Sniklas }
719fbc94a17Sniklas
720fbc94a17Sniklas #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
721fbc94a17Sniklas
722fbc94a17Sniklas /* Move forward a word in this node. */
723840175f0Skstailey DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
724fbc94a17Sniklas {
725fbc94a17Sniklas long point;
726fbc94a17Sniklas char *buffer;
727fbc94a17Sniklas int end, c;
728fbc94a17Sniklas
729fbc94a17Sniklas if (count < 0)
730fbc94a17Sniklas {
731fbc94a17Sniklas info_backward_word (window, -count, key);
732fbc94a17Sniklas return;
733fbc94a17Sniklas }
734fbc94a17Sniklas
735fbc94a17Sniklas point = window->point;
736fbc94a17Sniklas buffer = window->node->contents;
737fbc94a17Sniklas end = window->node->nodelen;
738fbc94a17Sniklas
739fbc94a17Sniklas while (count)
740fbc94a17Sniklas {
741fbc94a17Sniklas if (point + 1 >= end)
742fbc94a17Sniklas return;
743fbc94a17Sniklas
744fbc94a17Sniklas /* If we are not in a word, move forward until we are in one.
745fbc94a17Sniklas Then, move forward until we hit a non-alphabetic character. */
746fbc94a17Sniklas c = buffer[point];
747fbc94a17Sniklas
748fbc94a17Sniklas if (!alphabetic (c))
749fbc94a17Sniklas {
750fbc94a17Sniklas while (++point < end)
751fbc94a17Sniklas {
752fbc94a17Sniklas c = buffer[point];
753fbc94a17Sniklas if (alphabetic (c))
754fbc94a17Sniklas break;
755fbc94a17Sniklas }
756fbc94a17Sniklas }
757fbc94a17Sniklas
758fbc94a17Sniklas if (point >= end) return;
759fbc94a17Sniklas
760fbc94a17Sniklas while (++point < end)
761fbc94a17Sniklas {
762fbc94a17Sniklas c = buffer[point];
763fbc94a17Sniklas if (!alphabetic (c))
764fbc94a17Sniklas break;
765fbc94a17Sniklas }
766fbc94a17Sniklas --count;
767fbc94a17Sniklas }
768fbc94a17Sniklas window->point = point;
769fbc94a17Sniklas info_show_point (window);
770fbc94a17Sniklas }
771fbc94a17Sniklas
772840175f0Skstailey DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
773fbc94a17Sniklas {
774fbc94a17Sniklas long point;
775fbc94a17Sniklas char *buffer;
776fbc94a17Sniklas int c;
777fbc94a17Sniklas
778fbc94a17Sniklas if (count < 0)
779fbc94a17Sniklas {
780fbc94a17Sniklas info_forward_word (window, -count, key);
781fbc94a17Sniklas return;
782fbc94a17Sniklas }
783fbc94a17Sniklas
784fbc94a17Sniklas buffer = window->node->contents;
785fbc94a17Sniklas point = window->point;
786fbc94a17Sniklas
787fbc94a17Sniklas while (count)
788fbc94a17Sniklas {
789fbc94a17Sniklas if (point == 0)
790fbc94a17Sniklas break;
791fbc94a17Sniklas
792fbc94a17Sniklas /* Like info_forward_word (), except that we look at the
793fbc94a17Sniklas characters just before point. */
794fbc94a17Sniklas
795fbc94a17Sniklas c = buffer[point - 1];
796fbc94a17Sniklas
797fbc94a17Sniklas if (!alphabetic (c))
798fbc94a17Sniklas {
799fbc94a17Sniklas while (--point)
800fbc94a17Sniklas {
801fbc94a17Sniklas c = buffer[point - 1];
802fbc94a17Sniklas if (alphabetic (c))
803fbc94a17Sniklas break;
804fbc94a17Sniklas }
805fbc94a17Sniklas }
806fbc94a17Sniklas
807fbc94a17Sniklas while (point)
808fbc94a17Sniklas {
809fbc94a17Sniklas c = buffer[point - 1];
810fbc94a17Sniklas if (!alphabetic (c))
811fbc94a17Sniklas break;
812fbc94a17Sniklas else
813fbc94a17Sniklas --point;
814fbc94a17Sniklas }
815fbc94a17Sniklas --count;
816fbc94a17Sniklas }
817fbc94a17Sniklas window->point = point;
818fbc94a17Sniklas info_show_point (window);
819fbc94a17Sniklas }
820fbc94a17Sniklas
821fbc94a17Sniklas /* Variable controlling the behaviour of default scrolling when you are
822fbc94a17Sniklas already at the bottom of a node. Possible values are defined in session.h.
823fbc94a17Sniklas The meanings are:
824fbc94a17Sniklas
825fbc94a17Sniklas IS_Continuous Try to get first menu item, or failing that, the
826fbc94a17Sniklas "Next:" pointer, or failing that, the "Up:" and
827fbc94a17Sniklas "Next:" of the up.
828fbc94a17Sniklas IS_NextOnly Try to get "Next:" menu item.
829fbc94a17Sniklas IS_PageOnly Simply give up at the bottom of a node. */
830fbc94a17Sniklas
831fbc94a17Sniklas int info_scroll_behaviour = IS_Continuous;
832fbc94a17Sniklas
833fbc94a17Sniklas /* Choices used by the completer when reading a value for the user-visible
834fbc94a17Sniklas variable "scroll-behaviour". */
835fbc94a17Sniklas char *info_scroll_choices[] = {
836fbc94a17Sniklas "Continuous", "Next Only", "Page Only", (char *)NULL
837fbc94a17Sniklas };
838fbc94a17Sniklas
839672dff93Sespie /* Default window sizes for scrolling commands. */
840672dff93Sespie int default_window_size = -1; /* meaning 1 window-full */
841672dff93Sespie int default_scroll_size = -1; /* meaning half screen size */
842672dff93Sespie
843672dff93Sespie #define INFO_LABEL_FOUND() \
844672dff93Sespie (info_parsed_nodename || (info_parsed_filename \
845672dff93Sespie && !is_dir_name (info_parsed_filename)))
846672dff93Sespie
847fbc94a17Sniklas /* Move to 1st menu item, Next, Up/Next, or error in this window. */
848fbc94a17Sniklas static void
forward_move_node_structure(WINDOW * window,int behaviour)849*1076333cSespie forward_move_node_structure (WINDOW *window, int behaviour)
850fbc94a17Sniklas {
851fbc94a17Sniklas switch (behaviour)
852fbc94a17Sniklas {
853fbc94a17Sniklas case IS_PageOnly:
854*1076333cSespie info_error ((char *) msg_at_node_bottom, NULL, NULL);
855fbc94a17Sniklas break;
856fbc94a17Sniklas
857fbc94a17Sniklas case IS_NextOnly:
858fbc94a17Sniklas info_next_label_of_node (window->node);
859fbc94a17Sniklas if (!info_parsed_nodename && !info_parsed_filename)
860*1076333cSespie info_error ((char *) msg_no_pointer, (char *) _("Next"), NULL);
861fbc94a17Sniklas else
862fbc94a17Sniklas {
863*1076333cSespie window_message_in_echo_area ((char *) _("Following Next node..."),
864*1076333cSespie NULL, NULL);
865672dff93Sespie info_handle_pointer ("Next", window);
866fbc94a17Sniklas }
867fbc94a17Sniklas break;
868fbc94a17Sniklas
869fbc94a17Sniklas case IS_Continuous:
870fbc94a17Sniklas {
871fbc94a17Sniklas /* First things first. If this node contains a menu, move down
872fbc94a17Sniklas into the menu. */
873fbc94a17Sniklas {
874fbc94a17Sniklas REFERENCE **menu;
875fbc94a17Sniklas
876fbc94a17Sniklas menu = info_menu_of_node (window->node);
877fbc94a17Sniklas
878fbc94a17Sniklas if (menu)
879fbc94a17Sniklas {
880fbc94a17Sniklas info_free_references (menu);
881*1076333cSespie window_message_in_echo_area ((char *) _("Selecting first menu item..."),
882*1076333cSespie NULL, NULL);
883fbc94a17Sniklas info_menu_digit (window, 1, '1');
884fbc94a17Sniklas return;
885fbc94a17Sniklas }
886fbc94a17Sniklas }
887fbc94a17Sniklas
888fbc94a17Sniklas /* Okay, this node does not contain a menu. If it contains a
889fbc94a17Sniklas "Next:" pointer, use that. */
890fbc94a17Sniklas info_next_label_of_node (window->node);
891672dff93Sespie if (INFO_LABEL_FOUND ())
892fbc94a17Sniklas {
893*1076333cSespie window_message_in_echo_area ((char *) _("Selecting Next node..."),
894*1076333cSespie NULL, NULL);
895672dff93Sespie info_handle_pointer ("Next", window);
896fbc94a17Sniklas return;
897fbc94a17Sniklas }
898fbc94a17Sniklas
899fbc94a17Sniklas /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we
900fbc94a17Sniklas can move "Next:". If that isn't possible, complain that there
901fbc94a17Sniklas are no more nodes. */
902fbc94a17Sniklas {
903fbc94a17Sniklas int up_counter, old_current;
904fbc94a17Sniklas INFO_WINDOW *info_win;
905fbc94a17Sniklas
906fbc94a17Sniklas /* Remember the current node and location. */
907fbc94a17Sniklas info_win = get_info_window_of_window (window);
908fbc94a17Sniklas old_current = info_win->current;
909fbc94a17Sniklas
910fbc94a17Sniklas /* Back up through the "Up:" pointers until we have found a "Next:"
911fbc94a17Sniklas that isn't the same as the first menu item found in that node. */
912fbc94a17Sniklas up_counter = 0;
913fbc94a17Sniklas while (!info_error_was_printed)
914fbc94a17Sniklas {
915fbc94a17Sniklas info_up_label_of_node (window->node);
916672dff93Sespie if (INFO_LABEL_FOUND ())
917fbc94a17Sniklas {
918672dff93Sespie info_handle_pointer ("Up", window);
919fbc94a17Sniklas if (info_error_was_printed)
920fbc94a17Sniklas continue;
921fbc94a17Sniklas
922fbc94a17Sniklas up_counter++;
923fbc94a17Sniklas
924fbc94a17Sniklas info_next_label_of_node (window->node);
925fbc94a17Sniklas
926fbc94a17Sniklas /* If no "Next" pointer, keep backing up. */
927672dff93Sespie if (!INFO_LABEL_FOUND ())
928fbc94a17Sniklas continue;
929fbc94a17Sniklas
930fbc94a17Sniklas /* If this node's first menu item is the same as this node's
931fbc94a17Sniklas Next pointer, keep backing up. */
932fbc94a17Sniklas if (!info_parsed_filename)
933fbc94a17Sniklas {
934fbc94a17Sniklas REFERENCE **menu;
935fbc94a17Sniklas char *next_nodename;
936fbc94a17Sniklas
937fbc94a17Sniklas /* Remember the name of the Next node, since reading
938fbc94a17Sniklas the menu can overwrite the contents of the
939fbc94a17Sniklas info_parsed_xxx strings. */
940840175f0Skstailey next_nodename = xstrdup (info_parsed_nodename);
941fbc94a17Sniklas
942fbc94a17Sniklas menu = info_menu_of_node (window->node);
943fbc94a17Sniklas if (menu &&
944fbc94a17Sniklas (strcmp
945fbc94a17Sniklas (menu[0]->nodename, next_nodename) == 0))
946fbc94a17Sniklas {
947fbc94a17Sniklas info_free_references (menu);
948fbc94a17Sniklas free (next_nodename);
949fbc94a17Sniklas continue;
950fbc94a17Sniklas }
951fbc94a17Sniklas else
952fbc94a17Sniklas {
953fbc94a17Sniklas /* Restore the world to where it was before
954fbc94a17Sniklas reading the menu contents. */
955fbc94a17Sniklas info_free_references (menu);
956fbc94a17Sniklas free (next_nodename);
957fbc94a17Sniklas info_next_label_of_node (window->node);
958fbc94a17Sniklas }
959fbc94a17Sniklas }
960fbc94a17Sniklas
961fbc94a17Sniklas /* This node has a "Next" pointer, and it is not the
962fbc94a17Sniklas same as the first menu item found in this node. */
963fbc94a17Sniklas window_message_in_echo_area
964*1076333cSespie ((char *) _("Moving Up %d time(s), then Next."),
965*1076333cSespie (void *) (long) up_counter, NULL);
966fbc94a17Sniklas
967672dff93Sespie info_handle_pointer ("Next", window);
968fbc94a17Sniklas return;
969fbc94a17Sniklas }
970fbc94a17Sniklas else
971fbc94a17Sniklas {
972fbc94a17Sniklas /* No more "Up" pointers. Print an error, and call it
973fbc94a17Sniklas quits. */
974fbc94a17Sniklas register int i;
975fbc94a17Sniklas
976fbc94a17Sniklas for (i = 0; i < up_counter; i++)
977fbc94a17Sniklas {
978fbc94a17Sniklas info_win->nodes_index--;
979fbc94a17Sniklas free (info_win->nodes[info_win->nodes_index]);
980fbc94a17Sniklas info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
981fbc94a17Sniklas }
982fbc94a17Sniklas info_win->current = old_current;
983fbc94a17Sniklas window->node = info_win->nodes[old_current];
984fbc94a17Sniklas window->pagetop = info_win->pagetops[old_current];
985fbc94a17Sniklas window->point = info_win->points[old_current];
986fbc94a17Sniklas recalculate_line_starts (window);
987fbc94a17Sniklas window->flags |= W_UpdateWindow;
988*1076333cSespie info_error ((char *) _("No more nodes within this document."),
989*1076333cSespie NULL, NULL);
990fbc94a17Sniklas }
991fbc94a17Sniklas }
992fbc94a17Sniklas }
993fbc94a17Sniklas break;
994fbc94a17Sniklas }
995fbc94a17Sniklas }
996fbc94a17Sniklas }
997fbc94a17Sniklas
998fbc94a17Sniklas /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
999fbc94a17Sniklas static void
backward_move_node_structure(WINDOW * window,int behaviour)1000*1076333cSespie backward_move_node_structure (WINDOW *window, int behaviour)
1001fbc94a17Sniklas {
1002fbc94a17Sniklas switch (behaviour)
1003fbc94a17Sniklas {
1004fbc94a17Sniklas case IS_PageOnly:
1005*1076333cSespie info_error ((char *) msg_at_node_top, NULL, NULL);
1006fbc94a17Sniklas break;
1007fbc94a17Sniklas
1008fbc94a17Sniklas case IS_NextOnly:
1009fbc94a17Sniklas info_prev_label_of_node (window->node);
1010fbc94a17Sniklas if (!info_parsed_nodename && !info_parsed_filename)
1011*1076333cSespie info_error ((char *) _("No `Prev' for this node."), NULL, NULL);
1012fbc94a17Sniklas else
1013fbc94a17Sniklas {
1014*1076333cSespie window_message_in_echo_area ((char *) _("Moving Prev in this window."),
1015*1076333cSespie NULL, NULL);
1016672dff93Sespie info_handle_pointer ("Prev", window);
1017fbc94a17Sniklas }
1018fbc94a17Sniklas break;
1019fbc94a17Sniklas
1020fbc94a17Sniklas case IS_Continuous:
1021fbc94a17Sniklas info_prev_label_of_node (window->node);
1022fbc94a17Sniklas
1023672dff93Sespie if (!info_parsed_nodename && (!info_parsed_filename
1024672dff93Sespie || is_dir_name (info_parsed_filename)))
1025fbc94a17Sniklas {
1026fbc94a17Sniklas info_up_label_of_node (window->node);
1027672dff93Sespie if (!info_parsed_nodename && (!info_parsed_filename
1028672dff93Sespie || is_dir_name (info_parsed_filename)))
1029*1076333cSespie info_error ((char *)
1030*1076333cSespie _("No `Prev' or `Up' for this node within this document."),
1031*1076333cSespie NULL, NULL);
1032fbc94a17Sniklas else
1033fbc94a17Sniklas {
1034*1076333cSespie window_message_in_echo_area ((char *) _("Moving Up in this window."),
1035*1076333cSespie NULL, NULL);
1036672dff93Sespie info_handle_pointer ("Up", window);
1037fbc94a17Sniklas }
1038fbc94a17Sniklas }
1039fbc94a17Sniklas else
1040fbc94a17Sniklas {
1041fbc94a17Sniklas REFERENCE **menu;
1042fbc94a17Sniklas int inhibit_menu_traversing = 0;
1043fbc94a17Sniklas
1044fbc94a17Sniklas /* Watch out! If this node's Prev is the same as the Up, then
1045fbc94a17Sniklas move Up. Otherwise, we could move Prev, and then to the last
1046fbc94a17Sniklas menu item in the Prev. This would cause the user to loop
1047fbc94a17Sniklas through a subsection of the info file. */
1048fbc94a17Sniklas if (!info_parsed_filename && info_parsed_nodename)
1049fbc94a17Sniklas {
1050fbc94a17Sniklas char *pnode;
1051fbc94a17Sniklas
1052840175f0Skstailey pnode = xstrdup (info_parsed_nodename);
1053fbc94a17Sniklas info_up_label_of_node (window->node);
1054fbc94a17Sniklas
1055fbc94a17Sniklas if (!info_parsed_filename && info_parsed_nodename &&
1056fbc94a17Sniklas strcmp (info_parsed_nodename, pnode) == 0)
1057fbc94a17Sniklas {
1058fbc94a17Sniklas /* The nodes are the same. Inhibit moving to the last
1059fbc94a17Sniklas menu item. */
1060fbc94a17Sniklas free (pnode);
1061fbc94a17Sniklas inhibit_menu_traversing = 1;
1062fbc94a17Sniklas }
1063fbc94a17Sniklas else
1064fbc94a17Sniklas {
1065fbc94a17Sniklas free (pnode);
1066fbc94a17Sniklas info_prev_label_of_node (window->node);
1067fbc94a17Sniklas }
1068fbc94a17Sniklas }
1069fbc94a17Sniklas
1070fbc94a17Sniklas /* Move to the previous node. If this node now contains a menu,
1071fbc94a17Sniklas and we have not inhibited movement to it, move to the node
1072fbc94a17Sniklas corresponding to the last menu item. */
1073*1076333cSespie window_message_in_echo_area ((char *) _("Moving Prev in this window."),
1074*1076333cSespie NULL, NULL);
1075672dff93Sespie info_handle_pointer ("Prev", window);
1076fbc94a17Sniklas
1077fbc94a17Sniklas if (!inhibit_menu_traversing)
1078fbc94a17Sniklas {
1079fbc94a17Sniklas while (!info_error_was_printed &&
1080fbc94a17Sniklas (menu = info_menu_of_node (window->node)))
1081fbc94a17Sniklas {
1082fbc94a17Sniklas info_free_references (menu);
1083fbc94a17Sniklas window_message_in_echo_area
1084*1076333cSespie ((char *) _("Moving to `Prev's last menu item."), NULL, NULL);
1085fbc94a17Sniklas info_menu_digit (window, 1, '0');
1086fbc94a17Sniklas }
1087fbc94a17Sniklas }
1088fbc94a17Sniklas }
1089fbc94a17Sniklas break;
1090fbc94a17Sniklas }
1091fbc94a17Sniklas }
1092fbc94a17Sniklas
1093fbc94a17Sniklas /* Move continuously forward through the node structure of this info file. */
1094fbc94a17Sniklas DECLARE_INFO_COMMAND (info_global_next_node,
1095840175f0Skstailey _("Move forwards or down through node structure"))
1096fbc94a17Sniklas {
1097fbc94a17Sniklas if (count < 0)
1098fbc94a17Sniklas info_global_prev_node (window, -count, key);
1099fbc94a17Sniklas else
1100fbc94a17Sniklas {
1101fbc94a17Sniklas while (count && !info_error_was_printed)
1102fbc94a17Sniklas {
1103fbc94a17Sniklas forward_move_node_structure (window, IS_Continuous);
1104fbc94a17Sniklas count--;
1105fbc94a17Sniklas }
1106fbc94a17Sniklas }
1107fbc94a17Sniklas }
1108fbc94a17Sniklas
1109fbc94a17Sniklas /* Move continuously backward through the node structure of this info file. */
1110fbc94a17Sniklas DECLARE_INFO_COMMAND (info_global_prev_node,
1111840175f0Skstailey _("Move backwards or up through node structure"))
1112fbc94a17Sniklas {
1113fbc94a17Sniklas if (count < 0)
1114fbc94a17Sniklas info_global_next_node (window, -count, key);
1115fbc94a17Sniklas else
1116fbc94a17Sniklas {
1117fbc94a17Sniklas while (count && !info_error_was_printed)
1118fbc94a17Sniklas {
1119fbc94a17Sniklas backward_move_node_structure (window, IS_Continuous);
1120fbc94a17Sniklas count--;
1121fbc94a17Sniklas }
1122fbc94a17Sniklas }
1123fbc94a17Sniklas }
1124fbc94a17Sniklas
1125*1076333cSespie static void _scroll_forward(WINDOW *window, int count,
1126*1076333cSespie unsigned char key, int behaviour);
1127*1076333cSespie static void _scroll_backward(WINDOW *window, int count,
1128*1076333cSespie unsigned char key, int behaviour);
11293aa90977Sespie
11303aa90977Sespie static void
_scroll_forward(WINDOW * window,int count,unsigned char key,int behaviour)1131*1076333cSespie _scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour)
1132fbc94a17Sniklas {
1133fbc94a17Sniklas if (count < 0)
11343aa90977Sespie _scroll_backward (window, -count, key, behaviour);
1135fbc94a17Sniklas else
1136fbc94a17Sniklas {
1137fbc94a17Sniklas int desired_top;
1138fbc94a17Sniklas
1139fbc94a17Sniklas /* Without an explicit numeric argument, scroll the bottom two
1140fbc94a17Sniklas lines to the top of this window, Or, if at bottom of window,
11413aa90977Sespie and the chosen behaviour is to scroll through nodes get the
11423aa90977Sespie "Next" node for this window. */
1143672dff93Sespie if (default_window_size > 0)
1144672dff93Sespie desired_top = window->pagetop + default_window_size;
1145672dff93Sespie else if (!info_explicit_arg && count == 1)
1146fbc94a17Sniklas {
1147fbc94a17Sniklas desired_top = window->pagetop + (window->height - 2);
1148fbc94a17Sniklas
1149fbc94a17Sniklas /* If there are no more lines to scroll here, error, or get
11503aa90977Sespie another node, depending on BEHAVIOUR. */
1151fbc94a17Sniklas if (desired_top > window->line_count)
1152fbc94a17Sniklas {
1153fbc94a17Sniklas forward_move_node_structure (window, behaviour);
1154fbc94a17Sniklas return;
1155fbc94a17Sniklas }
1156fbc94a17Sniklas }
1157fbc94a17Sniklas else
1158fbc94a17Sniklas desired_top = window->pagetop + count;
1159fbc94a17Sniklas
1160fbc94a17Sniklas if (desired_top >= window->line_count)
1161fbc94a17Sniklas desired_top = window->line_count - 2;
1162fbc94a17Sniklas
1163fbc94a17Sniklas if (window->pagetop > desired_top)
1164fbc94a17Sniklas return;
1165fbc94a17Sniklas else
1166fbc94a17Sniklas set_window_pagetop (window, desired_top);
1167fbc94a17Sniklas }
1168fbc94a17Sniklas }
1169fbc94a17Sniklas
11703aa90977Sespie static void
_scroll_backward(WINDOW * window,int count,unsigned char key,int behaviour)1171*1076333cSespie _scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour)
1172fbc94a17Sniklas {
1173fbc94a17Sniklas if (count < 0)
11743aa90977Sespie _scroll_forward (window, -count, key, behaviour);
1175fbc94a17Sniklas else
1176fbc94a17Sniklas {
1177fbc94a17Sniklas int desired_top;
1178fbc94a17Sniklas
1179fbc94a17Sniklas /* Without an explicit numeric argument, scroll the top two lines
11803aa90977Sespie to the bottom of this window, or, depending on the selected
11813aa90977Sespie behaviour, move to the previous, or Up'th node. */
1182672dff93Sespie if (default_window_size > 0)
1183672dff93Sespie desired_top = window->pagetop - default_window_size;
1184672dff93Sespie else if (!info_explicit_arg && count == 1)
1185fbc94a17Sniklas {
1186fbc94a17Sniklas desired_top = window->pagetop - (window->height - 2);
1187fbc94a17Sniklas
1188fbc94a17Sniklas if ((desired_top < 0) && (window->pagetop == 0))
1189fbc94a17Sniklas {
1190fbc94a17Sniklas backward_move_node_structure (window, behaviour);
1191fbc94a17Sniklas return;
1192fbc94a17Sniklas }
1193fbc94a17Sniklas }
1194fbc94a17Sniklas else
1195fbc94a17Sniklas desired_top = window->pagetop - count;
1196fbc94a17Sniklas
1197fbc94a17Sniklas if (desired_top < 0)
1198fbc94a17Sniklas desired_top = 0;
1199fbc94a17Sniklas
1200fbc94a17Sniklas set_window_pagetop (window, desired_top);
1201fbc94a17Sniklas }
1202fbc94a17Sniklas }
1203fbc94a17Sniklas
12043aa90977Sespie /* Show the next screen of WINDOW's node. */
12053aa90977Sespie DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
12063aa90977Sespie {
12073aa90977Sespie _scroll_forward (window, count, key, info_scroll_behaviour);
12083aa90977Sespie }
12093aa90977Sespie
12103aa90977Sespie /* Like info_scroll_forward, but sets default_window_size as a side
12113aa90977Sespie effect. */
12123aa90977Sespie DECLARE_INFO_COMMAND (info_scroll_forward_set_window,
12133aa90977Sespie _("Scroll forward in this window and set default window size"))
12143aa90977Sespie {
12153aa90977Sespie if (info_explicit_arg)
12163aa90977Sespie default_window_size = count;
12173aa90977Sespie _scroll_forward (window, count, key, info_scroll_behaviour);
12183aa90977Sespie }
12193aa90977Sespie
12203aa90977Sespie /* Show the next screen of WINDOW's node but never advance to next node. */
12213aa90977Sespie DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))
12223aa90977Sespie {
12233aa90977Sespie _scroll_forward (window, count, key, IS_PageOnly);
12243aa90977Sespie }
12253aa90977Sespie
12263aa90977Sespie /* Like info_scroll_forward_page_only, but sets default_window_size as a side
12273aa90977Sespie effect. */
12283aa90977Sespie DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,
12293aa90977Sespie _("Scroll forward in this window staying within node and set default window size"))
12303aa90977Sespie {
12313aa90977Sespie if (info_explicit_arg)
12323aa90977Sespie default_window_size = count;
12333aa90977Sespie _scroll_forward (window, count, key, IS_PageOnly);
12343aa90977Sespie }
12353aa90977Sespie
12363aa90977Sespie /* Show the previous screen of WINDOW's node. */
12373aa90977Sespie DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
12383aa90977Sespie {
12393aa90977Sespie _scroll_backward (window, count, key, info_scroll_behaviour);
12403aa90977Sespie }
12413aa90977Sespie
1242672dff93Sespie /* Like info_scroll_backward, but sets default_window_size as a side
1243672dff93Sespie effect. */
1244672dff93Sespie DECLARE_INFO_COMMAND (info_scroll_backward_set_window,
1245672dff93Sespie _("Scroll backward in this window and set default window size"))
1246672dff93Sespie {
1247672dff93Sespie if (info_explicit_arg)
1248672dff93Sespie default_window_size = count;
12493aa90977Sespie _scroll_backward (window, count, key, info_scroll_behaviour);
12503aa90977Sespie }
12513aa90977Sespie
12523aa90977Sespie /* Show the previous screen of WINDOW's node but never move to previous
12533aa90977Sespie node. */
12543aa90977Sespie DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))
12553aa90977Sespie {
12563aa90977Sespie _scroll_backward (window, count, key, IS_PageOnly);
12573aa90977Sespie }
12583aa90977Sespie
12593aa90977Sespie /* Like info_scroll_backward_page_only, but sets default_window_size as a side
12603aa90977Sespie effect. */
12613aa90977Sespie DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,
12623aa90977Sespie _("Scroll backward in this window staying within node and set default window size"))
12633aa90977Sespie {
12643aa90977Sespie if (info_explicit_arg)
12653aa90977Sespie default_window_size = count;
12663aa90977Sespie _scroll_backward (window, count, key, IS_PageOnly);
1267672dff93Sespie }
1268672dff93Sespie
1269fbc94a17Sniklas /* Move to the beginning of the node. */
1270840175f0Skstailey DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1271fbc94a17Sniklas {
1272fbc94a17Sniklas window->pagetop = window->point = 0;
1273fbc94a17Sniklas window->flags |= W_UpdateWindow;
1274fbc94a17Sniklas }
1275fbc94a17Sniklas
1276fbc94a17Sniklas /* Move to the end of the node. */
1277840175f0Skstailey DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1278fbc94a17Sniklas {
1279fbc94a17Sniklas window->point = window->node->nodelen - 1;
1280fbc94a17Sniklas info_show_point (window);
1281fbc94a17Sniklas }
1282672dff93Sespie
1283672dff93Sespie /* Scroll the window forward by N lines. */
1284672dff93Sespie DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
1285672dff93Sespie {
1286672dff93Sespie if (count < 0)
1287672dff93Sespie info_up_line (window, -count, key);
1288672dff93Sespie else
1289672dff93Sespie {
1290672dff93Sespie int desired_top = window->pagetop + count;
1291672dff93Sespie
1292672dff93Sespie if (desired_top >= window->line_count)
1293672dff93Sespie desired_top = window->line_count - 2;
1294672dff93Sespie
1295672dff93Sespie if (window->pagetop <= desired_top)
1296672dff93Sespie set_window_pagetop (window, desired_top);
1297672dff93Sespie }
1298672dff93Sespie }
1299672dff93Sespie
1300672dff93Sespie /* Scroll the window backward by N lines. */
1301672dff93Sespie DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
1302672dff93Sespie {
1303672dff93Sespie if (count < 0)
1304672dff93Sespie info_down_line (window, -count, key);
1305672dff93Sespie else
1306672dff93Sespie {
1307672dff93Sespie int desired_top = window->pagetop - count;
1308672dff93Sespie
1309672dff93Sespie if (desired_top < 0)
1310672dff93Sespie desired_top = 0;
1311672dff93Sespie
1312672dff93Sespie set_window_pagetop (window, desired_top);
1313672dff93Sespie }
1314672dff93Sespie }
1315672dff93Sespie
1316672dff93Sespie /* Scroll the window forward by N lines and remember N as default for
1317672dff93Sespie subsequent commands. */
1318672dff93Sespie DECLARE_INFO_COMMAND (info_scroll_half_screen_down,
1319672dff93Sespie _("Scroll down by half screen size"))
1320672dff93Sespie {
1321672dff93Sespie if (count < 0)
13223aa90977Sespie info_scroll_half_screen_up (window, -count, key);
1323672dff93Sespie else
1324672dff93Sespie {
1325672dff93Sespie int scroll_size = (the_screen->height + 1) / 2;
1326672dff93Sespie int desired_top;
1327672dff93Sespie
1328672dff93Sespie if (info_explicit_arg)
1329672dff93Sespie default_scroll_size = count;
1330672dff93Sespie if (default_scroll_size > 0)
1331672dff93Sespie scroll_size = default_scroll_size;
1332672dff93Sespie
1333672dff93Sespie desired_top = window->pagetop + scroll_size;
1334672dff93Sespie if (desired_top >= window->line_count)
1335672dff93Sespie desired_top = window->line_count - 2;
1336672dff93Sespie
1337672dff93Sespie if (window->pagetop <= desired_top)
1338672dff93Sespie set_window_pagetop (window, desired_top);
1339672dff93Sespie }
1340672dff93Sespie }
1341672dff93Sespie
1342672dff93Sespie /* Scroll the window backward by N lines and remember N as default for
1343672dff93Sespie subsequent commands. */
1344672dff93Sespie DECLARE_INFO_COMMAND (info_scroll_half_screen_up,
1345672dff93Sespie _("Scroll up by half screen size"))
1346672dff93Sespie {
1347672dff93Sespie if (count < 0)
13483aa90977Sespie info_scroll_half_screen_down (window, -count, key);
1349672dff93Sespie else
1350672dff93Sespie {
1351672dff93Sespie int scroll_size = (the_screen->height + 1) / 2;
1352672dff93Sespie int desired_top;
1353672dff93Sespie
1354672dff93Sespie if (info_explicit_arg)
1355672dff93Sespie default_scroll_size = count;
1356672dff93Sespie if (default_scroll_size > 0)
1357672dff93Sespie scroll_size = default_scroll_size;
1358672dff93Sespie
1359672dff93Sespie desired_top = window->pagetop - scroll_size;
1360672dff93Sespie if (desired_top < 0)
1361672dff93Sespie desired_top = 0;
1362672dff93Sespie
1363672dff93Sespie set_window_pagetop (window, desired_top);
1364672dff93Sespie }
1365672dff93Sespie }
1366fbc94a17Sniklas
1367fbc94a17Sniklas /* **************************************************************** */
1368fbc94a17Sniklas /* */
1369fbc94a17Sniklas /* Commands for Manipulating Windows */
1370fbc94a17Sniklas /* */
1371fbc94a17Sniklas /* **************************************************************** */
1372fbc94a17Sniklas
1373fbc94a17Sniklas /* Make the next window in the chain be the active window. */
1374840175f0Skstailey DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1375fbc94a17Sniklas {
1376fbc94a17Sniklas if (count < 0)
1377fbc94a17Sniklas {
1378fbc94a17Sniklas info_prev_window (window, -count, key);
1379fbc94a17Sniklas return;
1380fbc94a17Sniklas }
1381fbc94a17Sniklas
1382fbc94a17Sniklas /* If no other window, error now. */
1383fbc94a17Sniklas if (!windows->next && !echo_area_is_active)
1384fbc94a17Sniklas {
1385*1076333cSespie info_error ((char *) msg_one_window, NULL, NULL);
1386fbc94a17Sniklas return;
1387fbc94a17Sniklas }
1388fbc94a17Sniklas
1389fbc94a17Sniklas while (count--)
1390fbc94a17Sniklas {
1391fbc94a17Sniklas if (window->next)
1392fbc94a17Sniklas window = window->next;
1393fbc94a17Sniklas else
1394fbc94a17Sniklas {
1395fbc94a17Sniklas if (window == the_echo_area || !echo_area_is_active)
1396fbc94a17Sniklas window = windows;
1397fbc94a17Sniklas else
1398fbc94a17Sniklas window = the_echo_area;
1399fbc94a17Sniklas }
1400fbc94a17Sniklas }
1401fbc94a17Sniklas
1402fbc94a17Sniklas if (active_window != window)
1403fbc94a17Sniklas {
1404fbc94a17Sniklas if (auto_footnotes_p)
1405fbc94a17Sniklas info_get_or_remove_footnotes (window);
1406fbc94a17Sniklas
1407fbc94a17Sniklas window->flags |= W_UpdateWindow;
1408fbc94a17Sniklas active_window = window;
1409fbc94a17Sniklas }
1410fbc94a17Sniklas }
1411fbc94a17Sniklas
1412fbc94a17Sniklas /* Make the previous window in the chain be the active window. */
1413840175f0Skstailey DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1414fbc94a17Sniklas {
1415fbc94a17Sniklas if (count < 0)
1416fbc94a17Sniklas {
1417fbc94a17Sniklas info_next_window (window, -count, key);
1418fbc94a17Sniklas return;
1419fbc94a17Sniklas }
1420fbc94a17Sniklas
1421fbc94a17Sniklas /* Only one window? */
1422fbc94a17Sniklas
1423fbc94a17Sniklas if (!windows->next && !echo_area_is_active)
1424fbc94a17Sniklas {
1425*1076333cSespie info_error ((char *) msg_one_window, NULL, NULL);
1426fbc94a17Sniklas return;
1427fbc94a17Sniklas }
1428fbc94a17Sniklas
1429fbc94a17Sniklas while (count--)
1430fbc94a17Sniklas {
1431fbc94a17Sniklas /* If we are in the echo area, or if the echo area isn't active and we
1432fbc94a17Sniklas are in the first window, find the last window in the chain. */
1433fbc94a17Sniklas if (window == the_echo_area ||
1434fbc94a17Sniklas (window == windows && !echo_area_is_active))
1435fbc94a17Sniklas {
1436*1076333cSespie register WINDOW *win, *last = NULL;
1437fbc94a17Sniklas
1438fbc94a17Sniklas for (win = windows; win; win = win->next)
1439fbc94a17Sniklas last = win;
1440fbc94a17Sniklas
1441fbc94a17Sniklas window = last;
1442fbc94a17Sniklas }
1443fbc94a17Sniklas else
1444fbc94a17Sniklas {
1445fbc94a17Sniklas if (window == windows)
1446fbc94a17Sniklas window = the_echo_area;
1447fbc94a17Sniklas else
1448fbc94a17Sniklas window = window->prev;
1449fbc94a17Sniklas }
1450fbc94a17Sniklas }
1451fbc94a17Sniklas
1452fbc94a17Sniklas if (active_window != window)
1453fbc94a17Sniklas {
1454fbc94a17Sniklas if (auto_footnotes_p)
1455fbc94a17Sniklas info_get_or_remove_footnotes (window);
1456fbc94a17Sniklas
1457fbc94a17Sniklas window->flags |= W_UpdateWindow;
1458fbc94a17Sniklas active_window = window;
1459fbc94a17Sniklas }
1460fbc94a17Sniklas }
1461fbc94a17Sniklas
1462fbc94a17Sniklas /* Split WINDOW into two windows, both showing the same node. If we
1463fbc94a17Sniklas are automatically tiling windows, re-tile after the split. */
1464840175f0Skstailey DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
1465fbc94a17Sniklas {
1466fbc94a17Sniklas WINDOW *split, *old_active;
1467fbc94a17Sniklas int pagetop;
1468fbc94a17Sniklas
1469fbc94a17Sniklas /* Remember the current pagetop of the window being split. If it doesn't
1470fbc94a17Sniklas change, we can scroll its contents around after the split. */
1471fbc94a17Sniklas pagetop = window->pagetop;
1472fbc94a17Sniklas
1473fbc94a17Sniklas /* Make the new window. */
1474fbc94a17Sniklas old_active = active_window;
1475fbc94a17Sniklas active_window = window;
1476fbc94a17Sniklas split = window_make_window (window->node);
1477fbc94a17Sniklas active_window = old_active;
1478fbc94a17Sniklas
1479fbc94a17Sniklas if (!split)
1480fbc94a17Sniklas {
1481*1076333cSespie info_error ((char *) msg_win_too_small, NULL, NULL);
1482fbc94a17Sniklas }
1483fbc94a17Sniklas else
1484fbc94a17Sniklas {
1485fbc94a17Sniklas #if defined (SPLIT_BEFORE_ACTIVE)
1486fbc94a17Sniklas /* Try to scroll the old window into its new postion. */
1487fbc94a17Sniklas if (pagetop == window->pagetop)
1488fbc94a17Sniklas {
1489fbc94a17Sniklas int start, end, amount;
1490fbc94a17Sniklas
1491fbc94a17Sniklas start = split->first_row;
1492fbc94a17Sniklas end = start + window->height;
1493fbc94a17Sniklas amount = split->height + 1;
1494fbc94a17Sniklas display_scroll_display (start, end, amount);
1495fbc94a17Sniklas }
1496fbc94a17Sniklas #else /* !SPLIT_BEFORE_ACTIVE */
1497fbc94a17Sniklas /* Make sure point still appears in the active window. */
1498fbc94a17Sniklas info_show_point (window);
1499fbc94a17Sniklas #endif /* !SPLIT_BEFORE_ACTIVE */
1500fbc94a17Sniklas
1501fbc94a17Sniklas /* If the window just split was one internal to Info, try to display
1502fbc94a17Sniklas something else in it. */
1503fbc94a17Sniklas if (internal_info_node_p (split->node))
1504fbc94a17Sniklas {
1505fbc94a17Sniklas register int i, j;
1506fbc94a17Sniklas INFO_WINDOW *iw;
1507fbc94a17Sniklas NODE *node = (NODE *)NULL;
1508fbc94a17Sniklas char *filename;
1509fbc94a17Sniklas
1510840175f0Skstailey for (i = 0; (iw = info_windows[i]); i++)
1511fbc94a17Sniklas {
1512fbc94a17Sniklas for (j = 0; j < iw->nodes_index; j++)
1513fbc94a17Sniklas if (!internal_info_node_p (iw->nodes[j]))
1514fbc94a17Sniklas {
1515fbc94a17Sniklas if (iw->nodes[j]->parent)
1516fbc94a17Sniklas filename = iw->nodes[j]->parent;
1517fbc94a17Sniklas else
1518fbc94a17Sniklas filename = iw->nodes[j]->filename;
1519fbc94a17Sniklas
1520fbc94a17Sniklas node = info_get_node (filename, iw->nodes[j]->nodename);
1521fbc94a17Sniklas if (node)
1522fbc94a17Sniklas {
1523fbc94a17Sniklas window_set_node_of_window (split, node);
1524fbc94a17Sniklas i = info_windows_index - 1;
1525fbc94a17Sniklas break;
1526fbc94a17Sniklas }
1527fbc94a17Sniklas }
1528fbc94a17Sniklas }
1529fbc94a17Sniklas }
1530fbc94a17Sniklas split->pagetop = window->pagetop;
1531fbc94a17Sniklas
1532fbc94a17Sniklas if (auto_tiling_p)
1533fbc94a17Sniklas window_tile_windows (DONT_TILE_INTERNALS);
1534fbc94a17Sniklas else
1535fbc94a17Sniklas window_adjust_pagetop (split);
1536fbc94a17Sniklas
1537fbc94a17Sniklas remember_window_and_node (split, split->node);
1538fbc94a17Sniklas }
1539fbc94a17Sniklas }
1540fbc94a17Sniklas
1541fbc94a17Sniklas /* Delete WINDOW, forgetting the list of last visited nodes. If we are
1542fbc94a17Sniklas automatically displaying footnotes, show or remove the footnotes
1543fbc94a17Sniklas window. If we are automatically tiling windows, re-tile after the
1544fbc94a17Sniklas deletion. */
1545840175f0Skstailey DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
1546fbc94a17Sniklas {
1547fbc94a17Sniklas if (!windows->next)
1548fbc94a17Sniklas {
1549*1076333cSespie info_error ((char *) msg_cant_kill_last, NULL, NULL);
1550fbc94a17Sniklas }
1551fbc94a17Sniklas else if (window->flags & W_WindowIsPerm)
1552fbc94a17Sniklas {
1553*1076333cSespie info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL);
1554fbc94a17Sniklas }
1555fbc94a17Sniklas else
1556fbc94a17Sniklas {
1557fbc94a17Sniklas info_delete_window_internal (window);
1558fbc94a17Sniklas
1559fbc94a17Sniklas if (auto_footnotes_p)
1560fbc94a17Sniklas info_get_or_remove_footnotes (active_window);
1561fbc94a17Sniklas
1562fbc94a17Sniklas if (auto_tiling_p)
1563fbc94a17Sniklas window_tile_windows (DONT_TILE_INTERNALS);
1564fbc94a17Sniklas }
1565fbc94a17Sniklas }
1566fbc94a17Sniklas
1567fbc94a17Sniklas /* Do the physical deletion of WINDOW, and forget this window and
1568fbc94a17Sniklas associated nodes. */
1569fbc94a17Sniklas void
info_delete_window_internal(WINDOW * window)1570*1076333cSespie info_delete_window_internal (WINDOW *window)
1571fbc94a17Sniklas {
1572fbc94a17Sniklas if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
1573fbc94a17Sniklas {
1574fbc94a17Sniklas /* We not only delete the window from the display, we forget it from
1575fbc94a17Sniklas our list of remembered windows. */
1576fbc94a17Sniklas forget_window_and_nodes (window);
1577fbc94a17Sniklas window_delete_window (window);
1578fbc94a17Sniklas
1579fbc94a17Sniklas if (echo_area_is_active)
1580fbc94a17Sniklas echo_area_inform_of_deleted_window (window);
1581fbc94a17Sniklas }
1582fbc94a17Sniklas }
1583fbc94a17Sniklas
1584fbc94a17Sniklas /* Just keep WINDOW, deleting all others. */
1585840175f0Skstailey DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1586fbc94a17Sniklas {
1587fbc94a17Sniklas int num_deleted; /* The number of windows we deleted. */
1588fbc94a17Sniklas int pagetop, start, end;
1589fbc94a17Sniklas
1590fbc94a17Sniklas /* Remember a few things about this window. We may be able to speed up
1591fbc94a17Sniklas redisplay later by scrolling its contents. */
1592fbc94a17Sniklas pagetop = window->pagetop;
1593fbc94a17Sniklas start = window->first_row;
1594fbc94a17Sniklas end = start + window->height;
1595fbc94a17Sniklas
1596fbc94a17Sniklas num_deleted = 0;
1597fbc94a17Sniklas
1598fbc94a17Sniklas while (1)
1599fbc94a17Sniklas {
1600fbc94a17Sniklas WINDOW *win;
1601fbc94a17Sniklas
1602fbc94a17Sniklas /* Find an eligible window and delete it. If no eligible windows
1603fbc94a17Sniklas are found, we are done. A window is eligible for deletion if
1604fbc94a17Sniklas is it not permanent, and it is not WINDOW. */
1605fbc94a17Sniklas for (win = windows; win; win = win->next)
1606fbc94a17Sniklas if (win != window && ((win->flags & W_WindowIsPerm) == 0))
1607fbc94a17Sniklas break;
1608fbc94a17Sniklas
1609fbc94a17Sniklas if (!win)
1610fbc94a17Sniklas break;
1611fbc94a17Sniklas
1612fbc94a17Sniklas info_delete_window_internal (win);
1613fbc94a17Sniklas num_deleted++;
1614fbc94a17Sniklas }
1615fbc94a17Sniklas
1616fbc94a17Sniklas /* Scroll the contents of this window into the right place so that the
1617fbc94a17Sniklas user doesn't have to wait any longer than necessary for redisplay. */
1618fbc94a17Sniklas if (num_deleted)
1619fbc94a17Sniklas {
1620fbc94a17Sniklas int amount;
1621fbc94a17Sniklas
1622fbc94a17Sniklas amount = (window->first_row - start);
1623fbc94a17Sniklas amount -= (window->pagetop - pagetop);
1624fbc94a17Sniklas display_scroll_display (start, end, amount);
1625fbc94a17Sniklas }
1626fbc94a17Sniklas
1627fbc94a17Sniklas window->flags |= W_UpdateWindow;
1628fbc94a17Sniklas }
1629fbc94a17Sniklas
1630fbc94a17Sniklas /* Scroll the "other" window of WINDOW. */
1631840175f0Skstailey DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1632fbc94a17Sniklas {
1633fbc94a17Sniklas WINDOW *other;
1634fbc94a17Sniklas
1635fbc94a17Sniklas /* If only one window, give up. */
1636fbc94a17Sniklas if (!windows->next)
1637fbc94a17Sniklas {
1638*1076333cSespie info_error ((char *) msg_one_window, NULL, NULL);
1639fbc94a17Sniklas return;
1640fbc94a17Sniklas }
1641fbc94a17Sniklas
1642fbc94a17Sniklas other = window->next;
1643fbc94a17Sniklas
1644fbc94a17Sniklas if (!other)
1645fbc94a17Sniklas other = window->prev;
1646fbc94a17Sniklas
1647fbc94a17Sniklas info_scroll_forward (other, count, key);
1648fbc94a17Sniklas }
1649fbc94a17Sniklas
1650672dff93Sespie /* Scroll the "other" window of WINDOW. */
1651672dff93Sespie DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
1652672dff93Sespie _("Scroll the other window backward"))
1653672dff93Sespie {
1654672dff93Sespie info_scroll_other_window (window, -count, key);
1655672dff93Sespie }
1656672dff93Sespie
1657fbc94a17Sniklas /* Change the size of WINDOW by AMOUNT. */
1658840175f0Skstailey DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1659fbc94a17Sniklas {
1660fbc94a17Sniklas window_change_window_height (window, count);
1661fbc94a17Sniklas }
1662fbc94a17Sniklas
1663fbc94a17Sniklas /* When non-zero, tiling takes place automatically when info_split_window
1664fbc94a17Sniklas is called. */
1665fbc94a17Sniklas int auto_tiling_p = 0;
1666fbc94a17Sniklas
1667fbc94a17Sniklas /* Tile all of the visible windows. */
1668fbc94a17Sniklas DECLARE_INFO_COMMAND (info_tile_windows,
1669840175f0Skstailey _("Divide the available screen space among the visible windows"))
1670fbc94a17Sniklas {
1671fbc94a17Sniklas window_tile_windows (TILE_INTERNALS);
1672fbc94a17Sniklas }
1673fbc94a17Sniklas
1674fbc94a17Sniklas /* Toggle the state of this window's wrapping of lines. */
1675fbc94a17Sniklas DECLARE_INFO_COMMAND (info_toggle_wrap,
1676840175f0Skstailey _("Toggle the state of line wrapping in the current window"))
1677fbc94a17Sniklas {
1678fbc94a17Sniklas window_toggle_wrap (window);
1679fbc94a17Sniklas }
1680fbc94a17Sniklas
1681fbc94a17Sniklas /* **************************************************************** */
1682fbc94a17Sniklas /* */
1683fbc94a17Sniklas /* Info Node Commands */
1684fbc94a17Sniklas /* */
1685fbc94a17Sniklas /* **************************************************************** */
1686fbc94a17Sniklas
1687672dff93Sespie /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
1688672dff93Sespie filename is not set. */
1689672dff93Sespie char *
node_printed_rep(NODE * node)1690*1076333cSespie node_printed_rep (NODE *node)
1691672dff93Sespie {
1692672dff93Sespie char *rep;
1693672dff93Sespie
1694672dff93Sespie if (node->filename)
1695672dff93Sespie {
1696672dff93Sespie char *filename
1697672dff93Sespie = filename_non_directory (node->parent ? node->parent : node->filename);
1698672dff93Sespie rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
1699672dff93Sespie sprintf (rep, "(%s)%s", filename, node->nodename);
1700672dff93Sespie }
1701672dff93Sespie else
1702672dff93Sespie rep = node->nodename;
1703672dff93Sespie
1704672dff93Sespie return rep;
1705672dff93Sespie }
1706672dff93Sespie
1707672dff93Sespie
1708fbc94a17Sniklas /* Using WINDOW for various defaults, select the node referenced by ENTRY
1709fbc94a17Sniklas in it. If the node is selected, the window and node are remembered. */
1710fbc94a17Sniklas void
info_select_reference(WINDOW * window,REFERENCE * entry)1711*1076333cSespie info_select_reference (WINDOW *window, REFERENCE *entry)
1712fbc94a17Sniklas {
1713fbc94a17Sniklas NODE *node;
1714fbc94a17Sniklas char *filename, *nodename, *file_system_error;
1715fbc94a17Sniklas
1716fbc94a17Sniklas file_system_error = (char *)NULL;
1717fbc94a17Sniklas
1718fbc94a17Sniklas filename = entry->filename;
1719fbc94a17Sniklas if (!filename)
1720fbc94a17Sniklas filename = window->node->parent;
1721fbc94a17Sniklas if (!filename)
1722fbc94a17Sniklas filename = window->node->filename;
1723fbc94a17Sniklas
1724fbc94a17Sniklas if (filename)
1725840175f0Skstailey filename = xstrdup (filename);
1726fbc94a17Sniklas
1727fbc94a17Sniklas if (entry->nodename)
1728840175f0Skstailey nodename = xstrdup (entry->nodename);
1729fbc94a17Sniklas else
1730840175f0Skstailey nodename = xstrdup ("Top");
1731fbc94a17Sniklas
1732fbc94a17Sniklas node = info_get_node (filename, nodename);
1733fbc94a17Sniklas
1734fbc94a17Sniklas /* Try something a little weird. If the node couldn't be found, and the
1735fbc94a17Sniklas reference was of the form "foo::", see if the entry->label can be found
1736fbc94a17Sniklas as a file, with a node of "Top". */
1737fbc94a17Sniklas if (!node)
1738fbc94a17Sniklas {
1739fbc94a17Sniklas if (info_recent_file_error)
1740840175f0Skstailey file_system_error = xstrdup (info_recent_file_error);
1741fbc94a17Sniklas
1742fbc94a17Sniklas if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
1743fbc94a17Sniklas {
1744fbc94a17Sniklas node = info_get_node (entry->label, "Top");
1745fbc94a17Sniklas if (!node && info_recent_file_error)
1746fbc94a17Sniklas {
1747fbc94a17Sniklas maybe_free (file_system_error);
1748840175f0Skstailey file_system_error = xstrdup (info_recent_file_error);
1749fbc94a17Sniklas }
1750fbc94a17Sniklas }
1751fbc94a17Sniklas }
1752fbc94a17Sniklas
1753fbc94a17Sniklas if (!node)
1754fbc94a17Sniklas {
1755fbc94a17Sniklas if (file_system_error)
1756*1076333cSespie info_error (file_system_error, NULL, NULL);
1757fbc94a17Sniklas else
1758*1076333cSespie info_error ((char *) msg_cant_find_node, nodename, NULL);
1759fbc94a17Sniklas }
1760fbc94a17Sniklas
1761fbc94a17Sniklas maybe_free (file_system_error);
1762fbc94a17Sniklas maybe_free (filename);
1763fbc94a17Sniklas maybe_free (nodename);
1764fbc94a17Sniklas
1765fbc94a17Sniklas if (node)
1766672dff93Sespie info_set_node_of_window (1, window, node);
1767fbc94a17Sniklas }
1768fbc94a17Sniklas
1769fbc94a17Sniklas /* Parse the node specification in LINE using WINDOW to default the filename.
1770fbc94a17Sniklas Select the parsed node in WINDOW and remember it, or error if the node
1771fbc94a17Sniklas couldn't be found. */
1772fbc94a17Sniklas static void
info_parse_and_select(char * line,WINDOW * window)1773*1076333cSespie info_parse_and_select (char *line, WINDOW *window)
1774fbc94a17Sniklas {
1775fbc94a17Sniklas REFERENCE entry;
1776fbc94a17Sniklas
1777fbc94a17Sniklas info_parse_node (line, DONT_SKIP_NEWLINES);
1778fbc94a17Sniklas
1779fbc94a17Sniklas entry.nodename = info_parsed_nodename;
1780fbc94a17Sniklas entry.filename = info_parsed_filename;
1781fbc94a17Sniklas entry.label = "*info-parse-and-select*";
1782fbc94a17Sniklas
1783fbc94a17Sniklas info_select_reference (window, &entry);
1784fbc94a17Sniklas }
1785fbc94a17Sniklas
1786fbc94a17Sniklas /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
1787fbc94a17Sniklas are previously filled, try to get the node represented by them into
1788fbc94a17Sniklas WINDOW. The node should have been pointed to by the LABEL pointer of
1789fbc94a17Sniklas WINDOW->node. */
1790fbc94a17Sniklas static void
info_handle_pointer(char * label,WINDOW * window)1791*1076333cSespie info_handle_pointer (char *label, WINDOW *window)
1792fbc94a17Sniklas {
1793fbc94a17Sniklas if (info_parsed_filename || info_parsed_nodename)
1794fbc94a17Sniklas {
1795fbc94a17Sniklas char *filename, *nodename;
1796fbc94a17Sniklas NODE *node;
1797fbc94a17Sniklas
1798fbc94a17Sniklas filename = nodename = (char *)NULL;
1799fbc94a17Sniklas
1800fbc94a17Sniklas if (info_parsed_filename)
1801840175f0Skstailey filename = xstrdup (info_parsed_filename);
1802fbc94a17Sniklas else
1803fbc94a17Sniklas {
1804fbc94a17Sniklas if (window->node->parent)
1805840175f0Skstailey filename = xstrdup (window->node->parent);
1806fbc94a17Sniklas else if (window->node->filename)
1807840175f0Skstailey filename = xstrdup (window->node->filename);
1808fbc94a17Sniklas }
1809fbc94a17Sniklas
1810fbc94a17Sniklas if (info_parsed_nodename)
1811840175f0Skstailey nodename = xstrdup (info_parsed_nodename);
1812fbc94a17Sniklas else
1813840175f0Skstailey nodename = xstrdup ("Top");
1814fbc94a17Sniklas
1815fbc94a17Sniklas node = info_get_node (filename, nodename);
1816fbc94a17Sniklas
1817fbc94a17Sniklas if (node)
1818fbc94a17Sniklas {
1819fbc94a17Sniklas INFO_WINDOW *info_win;
1820fbc94a17Sniklas
1821fbc94a17Sniklas info_win = get_info_window_of_window (window);
1822fbc94a17Sniklas if (info_win)
1823fbc94a17Sniklas {
1824fbc94a17Sniklas info_win->pagetops[info_win->current] = window->pagetop;
1825fbc94a17Sniklas info_win->points[info_win->current] = window->point;
1826fbc94a17Sniklas }
1827672dff93Sespie info_set_node_of_window (1, window, node);
1828fbc94a17Sniklas }
1829fbc94a17Sniklas else
1830fbc94a17Sniklas {
1831fbc94a17Sniklas if (info_recent_file_error)
1832*1076333cSespie info_error (info_recent_file_error, NULL, NULL);
1833fbc94a17Sniklas else
1834*1076333cSespie info_error ((char *) msg_cant_file_node, filename, nodename);
1835fbc94a17Sniklas }
1836fbc94a17Sniklas
1837fbc94a17Sniklas free (filename);
1838fbc94a17Sniklas free (nodename);
1839fbc94a17Sniklas }
1840fbc94a17Sniklas else
1841fbc94a17Sniklas {
1842*1076333cSespie info_error ((char *) msg_no_pointer, label, NULL);
1843fbc94a17Sniklas }
1844fbc94a17Sniklas }
1845fbc94a17Sniklas
1846fbc94a17Sniklas /* Make WINDOW display the "Next:" node of the node currently being
1847fbc94a17Sniklas displayed. */
1848672dff93Sespie DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
1849fbc94a17Sniklas {
1850fbc94a17Sniklas info_next_label_of_node (window->node);
1851672dff93Sespie info_handle_pointer ("Next", window);
1852fbc94a17Sniklas }
1853fbc94a17Sniklas
1854fbc94a17Sniklas /* Make WINDOW display the "Prev:" node of the node currently being
1855fbc94a17Sniklas displayed. */
1856672dff93Sespie DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
1857fbc94a17Sniklas {
1858fbc94a17Sniklas info_prev_label_of_node (window->node);
1859672dff93Sespie info_handle_pointer ("Prev", window);
1860fbc94a17Sniklas }
1861fbc94a17Sniklas
1862fbc94a17Sniklas /* Make WINDOW display the "Up:" node of the node currently being
1863fbc94a17Sniklas displayed. */
1864672dff93Sespie DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
1865fbc94a17Sniklas {
1866fbc94a17Sniklas info_up_label_of_node (window->node);
1867672dff93Sespie info_handle_pointer ("Up", window);
1868fbc94a17Sniklas }
1869fbc94a17Sniklas
1870fbc94a17Sniklas /* Make WINDOW display the last node of this info file. */
1871840175f0Skstailey DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
1872fbc94a17Sniklas {
1873fbc94a17Sniklas register int i;
1874fbc94a17Sniklas FILE_BUFFER *fb = file_buffer_of_window (window);
1875fbc94a17Sniklas NODE *node = (NODE *)NULL;
1876fbc94a17Sniklas
1877fbc94a17Sniklas if (fb && fb->tags)
1878fbc94a17Sniklas {
1879672dff93Sespie int last_node_tag_idx = -1;
1880672dff93Sespie
1881672dff93Sespie /* If no explicit argument, or argument of zero, default to the
1882672dff93Sespie last node. */
1883672dff93Sespie if (count == 0 || (count == 1 && !info_explicit_arg))
1884672dff93Sespie count = -1;
1885672dff93Sespie for (i = 0; count && fb->tags[i]; i++)
1886672dff93Sespie if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1887672dff93Sespie {
1888672dff93Sespie count--;
1889672dff93Sespie last_node_tag_idx = i;
1890672dff93Sespie }
1891672dff93Sespie if (count > 0)
1892672dff93Sespie i = last_node_tag_idx + 1;
1893672dff93Sespie if (i > 0)
1894fbc94a17Sniklas node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1895fbc94a17Sniklas }
1896fbc94a17Sniklas
1897fbc94a17Sniklas if (!node)
1898*1076333cSespie info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
1899fbc94a17Sniklas else
1900672dff93Sespie info_set_node_of_window (1, window, node);
1901fbc94a17Sniklas }
1902fbc94a17Sniklas
1903fbc94a17Sniklas /* Make WINDOW display the first node of this info file. */
1904840175f0Skstailey DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
1905fbc94a17Sniklas {
1906fbc94a17Sniklas FILE_BUFFER *fb = file_buffer_of_window (window);
1907fbc94a17Sniklas NODE *node = (NODE *)NULL;
1908fbc94a17Sniklas
1909672dff93Sespie /* If no explicit argument, or argument of zero, default to the
1910672dff93Sespie first node. */
1911672dff93Sespie if (count == 0)
1912672dff93Sespie count = 1;
1913fbc94a17Sniklas if (fb && fb->tags)
1914672dff93Sespie {
1915672dff93Sespie register int i;
1916672dff93Sespie int last_node_tag_idx = -1;
1917672dff93Sespie
1918672dff93Sespie for (i = 0; count && fb->tags[i]; i++)
1919672dff93Sespie if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1920672dff93Sespie {
1921672dff93Sespie count--;
1922672dff93Sespie last_node_tag_idx = i;
1923672dff93Sespie }
1924672dff93Sespie if (count > 0)
1925672dff93Sespie i = last_node_tag_idx + 1;
1926672dff93Sespie if (i > 0)
1927672dff93Sespie node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1928672dff93Sespie }
1929fbc94a17Sniklas
1930fbc94a17Sniklas if (!node)
1931*1076333cSespie info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
1932fbc94a17Sniklas else
1933672dff93Sespie info_set_node_of_window (1, window, node);
1934fbc94a17Sniklas }
1935fbc94a17Sniklas
1936fbc94a17Sniklas /* Select the last menu item in WINDOW->node. */
1937fbc94a17Sniklas DECLARE_INFO_COMMAND (info_last_menu_item,
1938840175f0Skstailey _("Select the last item in this node's menu"))
1939fbc94a17Sniklas {
1940fbc94a17Sniklas info_menu_digit (window, 1, '0');
1941fbc94a17Sniklas }
1942fbc94a17Sniklas
1943fbc94a17Sniklas /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
1944840175f0Skstailey DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
1945fbc94a17Sniklas {
1946fbc94a17Sniklas register int i, item;
1947*1076333cSespie register REFERENCE **menu;
1948fbc94a17Sniklas
1949fbc94a17Sniklas menu = info_menu_of_node (window->node);
1950fbc94a17Sniklas
1951fbc94a17Sniklas if (!menu)
1952fbc94a17Sniklas {
1953*1076333cSespie info_error ((char *) msg_no_menu_node, NULL, NULL);
1954fbc94a17Sniklas return;
1955fbc94a17Sniklas }
1956fbc94a17Sniklas
1957fbc94a17Sniklas /* We have the menu. See if there are this many items in it. */
1958fbc94a17Sniklas item = key - '0';
1959fbc94a17Sniklas
1960fbc94a17Sniklas /* Special case. Item "0" is the last item in this menu. */
1961fbc94a17Sniklas if (item == 0)
1962fbc94a17Sniklas for (i = 0; menu[i + 1]; i++);
1963fbc94a17Sniklas else
1964fbc94a17Sniklas {
1965*1076333cSespie for (i = 0; menu[i]; i++)
1966fbc94a17Sniklas if (i == item - 1)
1967fbc94a17Sniklas break;
1968fbc94a17Sniklas }
1969fbc94a17Sniklas
1970fbc94a17Sniklas if (menu[i])
1971*1076333cSespie {
1972fbc94a17Sniklas info_select_reference (window, menu[i]);
1973*1076333cSespie if (menu[i]->line_number > 0)
1974*1076333cSespie info_next_line (window, menu[i]->line_number - 1, key);
1975*1076333cSespie }
1976fbc94a17Sniklas else
1977*1076333cSespie info_error ((char *) _("There aren't %d items in this menu."),
1978*1076333cSespie (void *) (long) item, NULL);
1979fbc94a17Sniklas
1980fbc94a17Sniklas info_free_references (menu);
1981fbc94a17Sniklas return;
1982fbc94a17Sniklas }
1983fbc94a17Sniklas
1984*1076333cSespie
1985*1076333cSespie
1986*1076333cSespie /* Return a pointer to the xref in XREF_LIST that is nearest to POS, or
1987*1076333cSespie NULL if XREF_LIST is empty. That is, if POS is within any of the
1988*1076333cSespie given xrefs, return that one. Otherwise, return the one with the
1989*1076333cSespie nearest beginning or end. If there are two that are equidistant,
1990*1076333cSespie prefer the one forward. The return is in newly-allocated memory,
1991*1076333cSespie since the caller frees it.
1992*1076333cSespie
1993*1076333cSespie This is called from info_menu_or_ref_item with XREF_LIST being all
1994*1076333cSespie the xrefs in the node, and POS being point. The ui function that
1995*1076333cSespie starts it all off is select-reference-this-line.
1996*1076333cSespie
1997*1076333cSespie This is not the same logic as in info.el. Info-get-token prefers
1998*1076333cSespie searching backwards to searching forwards, and has a hardwired search
1999*1076333cSespie limit of 200 chars (in Emacs 21.2). */
2000*1076333cSespie
2001*1076333cSespie static REFERENCE **
nearest_xref(REFERENCE ** xref_list,long int pos)2002*1076333cSespie nearest_xref (REFERENCE **xref_list, long int pos)
2003*1076333cSespie {
2004*1076333cSespie int this_xref;
2005*1076333cSespie int nearest = -1;
2006*1076333cSespie long best_delta = -1;
2007*1076333cSespie
2008*1076333cSespie for (this_xref = 0; xref_list[this_xref]; this_xref++)
2009*1076333cSespie {
2010*1076333cSespie long delta;
2011*1076333cSespie REFERENCE *xref = xref_list[this_xref];
2012*1076333cSespie if (xref->start <= pos && pos <= xref->end)
2013*1076333cSespie { /* POS is within this xref, we're done */
2014*1076333cSespie nearest = this_xref;
2015*1076333cSespie break;
2016*1076333cSespie }
2017*1076333cSespie
2018*1076333cSespie /* See how far POS is from this xref. Take into account the
2019*1076333cSespie `*Note' that begins the xref, since as far as the user is
2020*1076333cSespie concerned, that's where it starts. */
2021*1076333cSespie delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))),
2022*1076333cSespie labs (pos - xref->end));
2023*1076333cSespie
2024*1076333cSespie /* It's the <= instead of < that makes us choose the forward xref
2025*1076333cSespie of POS if two are equidistant. Of course, because of all the
2026*1076333cSespie punctuation surrounding xrefs, it's not necessarily obvious
2027*1076333cSespie where one ends. */
2028*1076333cSespie if (delta <= best_delta || best_delta < 0)
2029*1076333cSespie {
2030*1076333cSespie nearest = this_xref;
2031*1076333cSespie best_delta = delta;
2032*1076333cSespie }
2033*1076333cSespie }
2034*1076333cSespie
2035*1076333cSespie /* Maybe there was no list to search through. */
2036*1076333cSespie if (nearest < 0)
2037*1076333cSespie return NULL;
2038*1076333cSespie
2039*1076333cSespie /* Ok, we have a nearest xref, make a list of it. */
2040*1076333cSespie {
2041*1076333cSespie REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2);
2042*1076333cSespie ret[0] = info_copy_reference (xref_list[nearest]);
2043*1076333cSespie ret[1] = NULL;
2044*1076333cSespie return ret;
2045*1076333cSespie }
2046*1076333cSespie }
2047*1076333cSespie
2048*1076333cSespie
2049fbc94a17Sniklas /* Read a menu or followed reference from the user defaulting to the
2050fbc94a17Sniklas reference found on the current line, and select that node. The
2051fbc94a17Sniklas reading is done with completion. BUILDER is the function used
2052fbc94a17Sniklas to build the list of references. ASK_P is non-zero if the user
2053fbc94a17Sniklas should be prompted, or zero to select the default item. */
2054fbc94a17Sniklas static void
info_menu_or_ref_item(WINDOW * window,int count,unsigned char key,REFERENCE ** (* builder)(NODE * node),int ask_p)2055*1076333cSespie info_menu_or_ref_item (WINDOW *window, int count,
2056*1076333cSespie unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p)
2057fbc94a17Sniklas {
2058fbc94a17Sniklas char *line;
2059*1076333cSespie REFERENCE *entry;
2060*1076333cSespie REFERENCE *defentry = NULL;
2061*1076333cSespie REFERENCE **menu = (*builder) (window->node);
2062fbc94a17Sniklas
2063fbc94a17Sniklas if (!menu)
2064fbc94a17Sniklas {
2065fbc94a17Sniklas if (builder == info_menu_of_node)
2066*1076333cSespie info_error ((char *) msg_no_menu_node, NULL, NULL);
2067fbc94a17Sniklas else
2068*1076333cSespie info_error ((char *) msg_no_xref_node, NULL, NULL);
2069fbc94a17Sniklas return;
2070fbc94a17Sniklas }
2071fbc94a17Sniklas
2072fbc94a17Sniklas /* Default the selected reference to the one which is on the line that
2073fbc94a17Sniklas point is in. */
2074fbc94a17Sniklas {
2075*1076333cSespie REFERENCE **refs = NULL;
2076*1076333cSespie int point_line = window_line_of_point (window);
2077fbc94a17Sniklas
2078fbc94a17Sniklas if (point_line != -1)
2079fbc94a17Sniklas {
2080fbc94a17Sniklas SEARCH_BINDING binding;
2081fbc94a17Sniklas
20827ab9cc48Sniklas binding.buffer = window->node->contents;
20837ab9cc48Sniklas binding.start = window->line_starts[point_line] - binding.buffer;
2084fbc94a17Sniklas if (window->line_starts[point_line + 1])
2085fbc94a17Sniklas binding.end = window->line_starts[point_line + 1] - binding.buffer;
2086fbc94a17Sniklas else
20877ab9cc48Sniklas binding.end = window->node->nodelen;
2088fbc94a17Sniklas binding.flags = 0;
2089fbc94a17Sniklas
2090fbc94a17Sniklas if (builder == info_menu_of_node)
2091fbc94a17Sniklas {
2092fbc94a17Sniklas if (point_line)
2093fbc94a17Sniklas {
20947ab9cc48Sniklas binding.start--;
2095fbc94a17Sniklas refs = info_menu_items (&binding);
2096fbc94a17Sniklas }
2097fbc94a17Sniklas }
2098fbc94a17Sniklas else
2099fbc94a17Sniklas {
2100fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
2101fbc94a17Sniklas if (window->node->flags & N_IsManPage)
2102fbc94a17Sniklas refs = manpage_xrefs_in_binding (window->node, &binding);
2103fbc94a17Sniklas else
2104fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
2105*1076333cSespie refs = nearest_xref (menu, window->point);
2106fbc94a17Sniklas }
2107fbc94a17Sniklas
2108*1076333cSespie if (refs && refs[0])
2109fbc94a17Sniklas {
2110*1076333cSespie if (strcmp (refs[0]->label, "Menu") != 0
2111*1076333cSespie || builder == info_xrefs_of_node)
2112fbc94a17Sniklas {
2113fbc94a17Sniklas int which = 0;
2114fbc94a17Sniklas
2115*1076333cSespie /* For xrefs, find the closest reference to point,
2116*1076333cSespie unless we only have one reference (as we will if
2117*1076333cSespie we've called nearest_xref above). It would be better
2118*1076333cSespie to have only one piece of code, but the conditions
2119*1076333cSespie when we call this are tangled. */
2120*1076333cSespie if (builder == info_xrefs_of_node && refs[1])
2121fbc94a17Sniklas {
2122fbc94a17Sniklas int closest = -1;
2123fbc94a17Sniklas
2124fbc94a17Sniklas for (; refs[which]; which++)
2125fbc94a17Sniklas {
2126*1076333cSespie if (window->point >= refs[which]->start
2127*1076333cSespie && window->point <= refs[which]->end)
2128fbc94a17Sniklas {
2129fbc94a17Sniklas closest = which;
2130fbc94a17Sniklas break;
2131fbc94a17Sniklas }
2132fbc94a17Sniklas else if (window->point < refs[which]->start)
2133fbc94a17Sniklas break;
2134fbc94a17Sniklas }
2135*1076333cSespie if (which > 0)
2136*1076333cSespie {
2137fbc94a17Sniklas if (closest == -1)
2138fbc94a17Sniklas which--;
2139fbc94a17Sniklas else
2140fbc94a17Sniklas which = closest;
2141fbc94a17Sniklas }
2142*1076333cSespie }
2143fbc94a17Sniklas
21442d8d5b66Sderaadt if (which < 0)
21452d8d5b66Sderaadt which = 0;
2146fbc94a17Sniklas defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2147840175f0Skstailey defentry->label = xstrdup (refs[which]->label);
2148fbc94a17Sniklas defentry->filename = refs[which]->filename;
2149fbc94a17Sniklas defentry->nodename = refs[which]->nodename;
2150*1076333cSespie defentry->line_number = refs[which]->line_number;
2151fbc94a17Sniklas
2152fbc94a17Sniklas if (defentry->filename)
2153840175f0Skstailey defentry->filename = xstrdup (defentry->filename);
2154fbc94a17Sniklas if (defentry->nodename)
2155840175f0Skstailey defentry->nodename = xstrdup (defentry->nodename);
2156fbc94a17Sniklas }
2157fbc94a17Sniklas info_free_references (refs);
2158fbc94a17Sniklas }
2159fbc94a17Sniklas }
2160fbc94a17Sniklas }
2161fbc94a17Sniklas
2162fbc94a17Sniklas /* If we are going to ask the user a question, do it now. */
2163fbc94a17Sniklas if (ask_p)
2164fbc94a17Sniklas {
2165fbc94a17Sniklas char *prompt;
2166fbc94a17Sniklas
2167fbc94a17Sniklas /* Build the prompt string. */
2168fbc94a17Sniklas if (builder == info_menu_of_node)
2169fbc94a17Sniklas {
2170fbc94a17Sniklas if (defentry)
2171*1076333cSespie {
2172*1076333cSespie prompt = xmalloc (strlen (defentry->label)
2173*1076333cSespie + strlen (_("Menu item (%s): ")));
2174840175f0Skstailey sprintf (prompt, _("Menu item (%s): "), defentry->label);
2175*1076333cSespie }
2176fbc94a17Sniklas else
2177*1076333cSespie prompt = xstrdup (_("Menu item: "));
2178fbc94a17Sniklas }
2179fbc94a17Sniklas else
2180fbc94a17Sniklas {
2181fbc94a17Sniklas if (defentry)
2182*1076333cSespie {
2183*1076333cSespie prompt = xmalloc (strlen (defentry->label)
2184*1076333cSespie + strlen (_("Follow xref (%s): ")));
2185840175f0Skstailey sprintf (prompt, _("Follow xref (%s): "), defentry->label);
2186*1076333cSespie }
2187fbc94a17Sniklas else
2188*1076333cSespie prompt = xstrdup (_("Follow xref: "));
2189fbc94a17Sniklas }
2190fbc94a17Sniklas
2191fbc94a17Sniklas line = info_read_completing_in_echo_area (window, prompt, menu);
2192fbc94a17Sniklas free (prompt);
2193fbc94a17Sniklas
2194fbc94a17Sniklas window = active_window;
2195fbc94a17Sniklas
2196fbc94a17Sniklas /* User aborts, just quit. */
2197fbc94a17Sniklas if (!line)
2198fbc94a17Sniklas {
2199fbc94a17Sniklas maybe_free (defentry);
2200fbc94a17Sniklas info_free_references (menu);
2201fbc94a17Sniklas info_abort_key (window, 0, 0);
2202fbc94a17Sniklas return;
2203fbc94a17Sniklas }
2204fbc94a17Sniklas
2205fbc94a17Sniklas /* If we had a default and the user accepted it, use that. */
2206fbc94a17Sniklas if (!*line)
2207fbc94a17Sniklas {
2208fbc94a17Sniklas free (line);
2209fbc94a17Sniklas if (defentry)
2210840175f0Skstailey line = xstrdup (defentry->label);
2211fbc94a17Sniklas else
2212fbc94a17Sniklas line = (char *)NULL;
2213fbc94a17Sniklas }
2214fbc94a17Sniklas }
2215fbc94a17Sniklas else
2216fbc94a17Sniklas {
2217fbc94a17Sniklas /* Not going to ask any questions. If we have a default entry, use
2218fbc94a17Sniklas that, otherwise return. */
2219fbc94a17Sniklas if (!defentry)
2220fbc94a17Sniklas return;
2221fbc94a17Sniklas else
2222840175f0Skstailey line = xstrdup (defentry->label);
2223fbc94a17Sniklas }
2224fbc94a17Sniklas
2225fbc94a17Sniklas if (line)
2226fbc94a17Sniklas {
22273aa90977Sespie /* It is possible that the references have more than a single
22283aa90977Sespie entry with the same label, and also LINE is down-cased, which
22293aa90977Sespie complicates matters even more. Try to be as accurate as we
22303aa90977Sespie can: if they've chosen the default, use defentry directly. */
22313aa90977Sespie if (defentry && strcmp (line, defentry->label) == 0)
22323aa90977Sespie entry = defentry;
22333aa90977Sespie else
22343aa90977Sespie /* Find the selected label in the references. If there are
22353aa90977Sespie more than one label which matches, find the one that's
22363aa90977Sespie closest to point. */
22373aa90977Sespie {
22383aa90977Sespie register int i;
22393aa90977Sespie int best = -1, min_dist = window->node->nodelen;
22403aa90977Sespie REFERENCE *ref;
22413aa90977Sespie
22423aa90977Sespie for (i = 0; menu && (ref = menu[i]); i++)
22433aa90977Sespie {
22443aa90977Sespie /* Need to use strcasecmp because LINE is downcased
22453aa90977Sespie inside info_read_completing_in_echo_area. */
22463aa90977Sespie if (strcasecmp (line, ref->label) == 0)
22473aa90977Sespie {
22483aa90977Sespie /* ref->end is more accurate estimate of position
22493aa90977Sespie for menus than ref->start. Go figure. */
22503aa90977Sespie int dist = abs (window->point - ref->end);
22513aa90977Sespie
22523aa90977Sespie if (dist < min_dist)
22533aa90977Sespie {
22543aa90977Sespie min_dist = dist;
22553aa90977Sespie best = i;
22563aa90977Sespie }
22573aa90977Sespie }
22583aa90977Sespie }
22593aa90977Sespie if (best != -1)
22603aa90977Sespie entry = menu[best];
22613aa90977Sespie else
22623aa90977Sespie entry = (REFERENCE *)NULL;
22633aa90977Sespie }
2264fbc94a17Sniklas
2265fbc94a17Sniklas if (!entry && defentry)
2266*1076333cSespie info_error ((char *) _("The reference disappeared! (%s)."), line, NULL);
2267fbc94a17Sniklas else
2268fbc94a17Sniklas {
2269672dff93Sespie NODE *orig = window->node;
2270fbc94a17Sniklas info_select_reference (window, entry);
2271*1076333cSespie
2272672dff93Sespie if (builder == info_xrefs_of_node && window->node != orig
2273672dff93Sespie && !(window->node->flags & N_FromAnchor))
2274672dff93Sespie { /* Search for this reference in the node. */
2275fbc94a17Sniklas long offset;
2276fbc94a17Sniklas long start;
2277fbc94a17Sniklas
2278fbc94a17Sniklas if (window->line_count > 0)
2279fbc94a17Sniklas start = window->line_starts[1] - window->node->contents;
2280fbc94a17Sniklas else
2281fbc94a17Sniklas start = 0;
2282fbc94a17Sniklas
2283fbc94a17Sniklas offset =
2284fbc94a17Sniklas info_target_search_node (window->node, entry->label, start);
2285fbc94a17Sniklas
2286fbc94a17Sniklas if (offset != -1)
2287fbc94a17Sniklas {
2288fbc94a17Sniklas window->point = offset;
2289fbc94a17Sniklas window_adjust_pagetop (window);
2290fbc94a17Sniklas }
2291fbc94a17Sniklas }
2292*1076333cSespie
2293*1076333cSespie if (entry->line_number > 0)
2294*1076333cSespie /* next_line starts at line 1? Anyway, the -1 makes it
2295*1076333cSespie move to the right line. */
2296*1076333cSespie info_next_line (window, entry->line_number - 1, key);
2297fbc94a17Sniklas }
2298fbc94a17Sniklas
2299fbc94a17Sniklas free (line);
2300fbc94a17Sniklas if (defentry)
2301fbc94a17Sniklas {
2302fbc94a17Sniklas free (defentry->label);
2303fbc94a17Sniklas maybe_free (defentry->filename);
2304fbc94a17Sniklas maybe_free (defentry->nodename);
2305fbc94a17Sniklas free (defentry);
2306fbc94a17Sniklas }
2307fbc94a17Sniklas }
2308fbc94a17Sniklas
2309fbc94a17Sniklas info_free_references (menu);
2310fbc94a17Sniklas
2311fbc94a17Sniklas if (!info_error_was_printed)
2312fbc94a17Sniklas window_clear_echo_area ();
2313fbc94a17Sniklas }
2314fbc94a17Sniklas
2315fbc94a17Sniklas /* Read a line (with completion) which is the name of a menu item,
2316fbc94a17Sniklas and select that item. */
2317840175f0Skstailey DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
2318fbc94a17Sniklas {
2319fbc94a17Sniklas info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2320fbc94a17Sniklas }
2321fbc94a17Sniklas
2322fbc94a17Sniklas /* Read a line (with completion) which is the name of a reference to
2323fbc94a17Sniklas follow, and select the node. */
2324fbc94a17Sniklas DECLARE_INFO_COMMAND
2325840175f0Skstailey (info_xref_item, _("Read a footnote or cross reference and select its node"))
2326fbc94a17Sniklas {
2327fbc94a17Sniklas info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2328fbc94a17Sniklas }
2329fbc94a17Sniklas
2330fbc94a17Sniklas /* Position the cursor at the start of this node's menu. */
2331840175f0Skstailey DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
2332fbc94a17Sniklas {
2333fbc94a17Sniklas SEARCH_BINDING binding;
2334fbc94a17Sniklas long position;
2335fbc94a17Sniklas
2336fbc94a17Sniklas binding.buffer = window->node->contents;
2337fbc94a17Sniklas binding.start = 0;
2338fbc94a17Sniklas binding.end = window->node->nodelen;
2339fbc94a17Sniklas binding.flags = S_FoldCase | S_SkipDest;
2340fbc94a17Sniklas
2341fbc94a17Sniklas position = search (INFO_MENU_LABEL, &binding);
2342fbc94a17Sniklas
2343fbc94a17Sniklas if (position == -1)
2344*1076333cSespie info_error ((char *) msg_no_menu_node, NULL, NULL);
2345fbc94a17Sniklas else
2346fbc94a17Sniklas {
2347fbc94a17Sniklas window->point = position;
2348fbc94a17Sniklas window_adjust_pagetop (window);
2349fbc94a17Sniklas window->flags |= W_UpdateWindow;
2350fbc94a17Sniklas }
2351fbc94a17Sniklas }
2352fbc94a17Sniklas
2353fbc94a17Sniklas /* Visit as many menu items as is possible, each in a separate window. */
2354fbc94a17Sniklas DECLARE_INFO_COMMAND (info_visit_menu,
2355840175f0Skstailey _("Visit as many menu items at once as possible"))
2356fbc94a17Sniklas {
2357fbc94a17Sniklas register int i;
2358fbc94a17Sniklas REFERENCE *entry, **menu;
2359fbc94a17Sniklas
2360fbc94a17Sniklas menu = info_menu_of_node (window->node);
2361fbc94a17Sniklas
2362fbc94a17Sniklas if (!menu)
2363*1076333cSespie info_error ((char *) msg_no_menu_node, NULL, NULL);
2364fbc94a17Sniklas
2365fbc94a17Sniklas for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2366fbc94a17Sniklas {
2367fbc94a17Sniklas WINDOW *new;
2368fbc94a17Sniklas
2369fbc94a17Sniklas new = window_make_window (window->node);
2370fbc94a17Sniklas window_tile_windows (TILE_INTERNALS);
2371fbc94a17Sniklas
2372fbc94a17Sniklas if (!new)
2373*1076333cSespie info_error ((char *) msg_win_too_small, NULL, NULL);
2374fbc94a17Sniklas else
2375fbc94a17Sniklas {
2376fbc94a17Sniklas active_window = new;
2377fbc94a17Sniklas info_select_reference (new, entry);
2378fbc94a17Sniklas }
2379fbc94a17Sniklas }
2380fbc94a17Sniklas }
2381fbc94a17Sniklas
2382fbc94a17Sniklas /* Read a line of input which is a node name, and go to that node. */
2383840175f0Skstailey DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
2384fbc94a17Sniklas {
2385fbc94a17Sniklas char *line;
2386fbc94a17Sniklas
2387fbc94a17Sniklas #define GOTO_COMPLETES
2388fbc94a17Sniklas #if defined (GOTO_COMPLETES)
2389fbc94a17Sniklas /* Build a completion list of all of the known nodes. */
2390fbc94a17Sniklas {
2391fbc94a17Sniklas register int fbi, i;
2392fbc94a17Sniklas FILE_BUFFER *current;
2393fbc94a17Sniklas REFERENCE **items = (REFERENCE **)NULL;
2394fbc94a17Sniklas int items_index = 0;
2395fbc94a17Sniklas int items_slots = 0;
2396fbc94a17Sniklas
2397fbc94a17Sniklas current = file_buffer_of_window (window);
2398fbc94a17Sniklas
2399fbc94a17Sniklas for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2400fbc94a17Sniklas {
2401fbc94a17Sniklas FILE_BUFFER *fb;
2402fbc94a17Sniklas REFERENCE *entry;
2403fbc94a17Sniklas int this_is_the_current_fb;
2404fbc94a17Sniklas
2405fbc94a17Sniklas fb = info_loaded_files[fbi];
2406fbc94a17Sniklas this_is_the_current_fb = (current == fb);
2407fbc94a17Sniklas
2408fbc94a17Sniklas entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2409fbc94a17Sniklas entry->filename = entry->nodename = (char *)NULL;
2410fbc94a17Sniklas entry->label = (char *)xmalloc (4 + strlen (fb->filename));
2411fbc94a17Sniklas sprintf (entry->label, "(%s)*", fb->filename);
2412fbc94a17Sniklas
2413fbc94a17Sniklas add_pointer_to_array
2414fbc94a17Sniklas (entry, items_index, items, items_slots, 10, REFERENCE *);
2415fbc94a17Sniklas
2416fbc94a17Sniklas if (fb->tags)
2417fbc94a17Sniklas {
2418fbc94a17Sniklas for (i = 0; fb->tags[i]; i++)
2419fbc94a17Sniklas {
2420fbc94a17Sniklas entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2421fbc94a17Sniklas entry->filename = entry->nodename = (char *)NULL;
2422672dff93Sespie if (this_is_the_current_fb)
2423672dff93Sespie entry->label = xstrdup (fb->tags[i]->nodename);
2424672dff93Sespie else
2425672dff93Sespie {
2426fbc94a17Sniklas entry->label = (char *) xmalloc
2427672dff93Sespie (4 + strlen (fb->filename) +
2428672dff93Sespie strlen (fb->tags[i]->nodename));
2429fbc94a17Sniklas sprintf (entry->label, "(%s)%s",
2430fbc94a17Sniklas fb->filename, fb->tags[i]->nodename);
2431672dff93Sespie }
2432fbc94a17Sniklas
2433fbc94a17Sniklas add_pointer_to_array
2434fbc94a17Sniklas (entry, items_index, items, items_slots, 100, REFERENCE *);
2435fbc94a17Sniklas }
2436fbc94a17Sniklas }
2437fbc94a17Sniklas }
2438*1076333cSespie line = info_read_maybe_completing (window, (char *) _("Goto node: "),
2439*1076333cSespie items);
2440fbc94a17Sniklas info_free_references (items);
2441fbc94a17Sniklas }
2442fbc94a17Sniklas #else /* !GOTO_COMPLETES */
2443*1076333cSespie line = info_read_in_echo_area (window, (char *) _("Goto node: "));
2444fbc94a17Sniklas #endif /* !GOTO_COMPLETES */
2445fbc94a17Sniklas
2446fbc94a17Sniklas /* If the user aborted, quit now. */
2447fbc94a17Sniklas if (!line)
2448fbc94a17Sniklas {
2449fbc94a17Sniklas info_abort_key (window, 0, 0);
2450fbc94a17Sniklas return;
2451fbc94a17Sniklas }
2452fbc94a17Sniklas
2453fbc94a17Sniklas canonicalize_whitespace (line);
2454fbc94a17Sniklas
2455fbc94a17Sniklas if (*line)
2456fbc94a17Sniklas info_parse_and_select (line, window);
2457fbc94a17Sniklas
2458fbc94a17Sniklas free (line);
2459fbc94a17Sniklas if (!info_error_was_printed)
2460fbc94a17Sniklas window_clear_echo_area ();
2461fbc94a17Sniklas }
2462672dff93Sespie
2463672dff93Sespie /* Follow the menu list in MENUS (list of strings terminated by a NULL
2464672dff93Sespie entry) from INITIAL_NODE. If can't continue at any point (no menu or
2465672dff93Sespie no menu entry for the next item), return the node so far -- that
2466672dff93Sespie might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will
2467672dff93Sespie be set to the error message and argument for message, otherwise they
2468672dff93Sespie will be NULL. */
2469fbc94a17Sniklas
2470672dff93Sespie NODE *
info_follow_menus(NODE * initial_node,char ** menus,const char ** errstr,char ** errarg1,char ** errarg2)2471*1076333cSespie info_follow_menus (NODE *initial_node, char **menus,
2472*1076333cSespie const char **errstr, char **errarg1, char **errarg2)
2473672dff93Sespie {
2474672dff93Sespie NODE *node = NULL;
2475672dff93Sespie *errstr = *errarg1 = *errarg2 = NULL;
2476672dff93Sespie
2477672dff93Sespie for (; *menus; menus++)
2478672dff93Sespie {
2479672dff93Sespie static char *first_arg = NULL;
2480672dff93Sespie REFERENCE **menu;
2481672dff93Sespie REFERENCE *entry;
2482672dff93Sespie char *arg = *menus; /* Remember the name of the menu entry we want. */
2483672dff93Sespie
2484672dff93Sespie /* A leading space is certainly NOT part of a node name. Most
2485672dff93Sespie probably, they typed a space after the separating comma. The
2486672dff93Sespie strings in menus[] have their whitespace canonicalized, so
2487672dff93Sespie there's at most one space to ignore. */
2488672dff93Sespie if (*arg == ' ')
2489672dff93Sespie arg++;
2490672dff93Sespie if (!first_arg)
2491672dff93Sespie first_arg = arg;
2492672dff93Sespie
2493672dff93Sespie /* Build and return a list of the menu items in this node. */
2494672dff93Sespie menu = info_menu_of_node (initial_node);
2495672dff93Sespie
2496672dff93Sespie /* If no menu item in this node, stop here, but let the user
2497672dff93Sespie continue to use Info. Perhaps they wanted this node and didn't
2498672dff93Sespie realize it. */
2499672dff93Sespie if (!menu)
2500672dff93Sespie {
2501672dff93Sespie if (arg == first_arg)
2502672dff93Sespie {
2503672dff93Sespie node = make_manpage_node (first_arg);
2504672dff93Sespie if (node)
2505672dff93Sespie goto maybe_got_node;
2506672dff93Sespie }
2507672dff93Sespie *errstr = _("No menu in node `%s'.");
2508672dff93Sespie *errarg1 = node_printed_rep (initial_node);
2509672dff93Sespie return initial_node;
2510672dff93Sespie }
2511672dff93Sespie
2512672dff93Sespie /* Find the specified menu item. */
2513672dff93Sespie entry = info_get_labeled_reference (arg, menu);
2514672dff93Sespie
2515672dff93Sespie /* If the item wasn't found, search the list sloppily. Perhaps this
2516672dff93Sespie user typed "buffer" when they really meant "Buffers". */
2517672dff93Sespie if (!entry)
2518672dff93Sespie {
2519672dff93Sespie int i;
2520672dff93Sespie int best_guess = -1;
2521672dff93Sespie
2522672dff93Sespie for (i = 0; (entry = menu[i]); i++)
2523672dff93Sespie {
2524672dff93Sespie if (strcasecmp (entry->label, arg) == 0)
2525672dff93Sespie break;
2526672dff93Sespie else
2527*1076333cSespie if ((best_guess == -1)
2528*1076333cSespie && (strncasecmp (entry->label, arg, strlen (arg)) == 0))
2529672dff93Sespie best_guess = i;
2530672dff93Sespie }
2531672dff93Sespie
2532672dff93Sespie if (!entry && best_guess != -1)
2533672dff93Sespie entry = menu[best_guess];
2534672dff93Sespie }
2535672dff93Sespie
2536672dff93Sespie /* If we still failed to find the reference, start Info with the current
2537672dff93Sespie node anyway. It is probably a misspelling. */
2538672dff93Sespie if (!entry)
2539672dff93Sespie {
2540672dff93Sespie if (arg == first_arg)
2541672dff93Sespie {
25423aa90977Sespie /* Maybe they typed "info foo" instead of "info -f foo". */
25433aa90977Sespie node = info_get_node (first_arg, 0);
25443aa90977Sespie if (node)
25453aa90977Sespie add_file_directory_to_path (first_arg);
25463aa90977Sespie else
2547672dff93Sespie node = make_manpage_node (first_arg);
2548672dff93Sespie if (node)
2549672dff93Sespie goto maybe_got_node;
2550672dff93Sespie }
2551672dff93Sespie
2552672dff93Sespie info_free_references (menu);
2553672dff93Sespie *errstr = _("No menu item `%s' in node `%s'.");
2554672dff93Sespie *errarg1 = arg;
2555672dff93Sespie *errarg2 = node_printed_rep (initial_node);
2556672dff93Sespie return initial_node;
2557672dff93Sespie }
2558672dff93Sespie
2559672dff93Sespie /* We have found the reference that the user specified. If no
2560672dff93Sespie filename in this reference, define it. */
2561672dff93Sespie if (!entry->filename)
2562672dff93Sespie entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2563672dff93Sespie : initial_node->filename);
2564672dff93Sespie
2565672dff93Sespie /* Try to find this node. */
2566672dff93Sespie node = info_get_node (entry->filename, entry->nodename);
2567672dff93Sespie if (!node && arg == first_arg)
2568672dff93Sespie {
2569672dff93Sespie node = make_manpage_node (first_arg);
2570672dff93Sespie if (node)
2571672dff93Sespie goto maybe_got_node;
2572672dff93Sespie }
2573672dff93Sespie
2574672dff93Sespie /* Since we cannot find it, try using the label of the entry as a
2575672dff93Sespie file, i.e., "(LABEL)Top". */
2576672dff93Sespie if (!node && entry->nodename
2577672dff93Sespie && strcmp (entry->label, entry->nodename) == 0)
2578672dff93Sespie node = info_get_node (entry->label, "Top");
2579672dff93Sespie
2580672dff93Sespie maybe_got_node:
2581672dff93Sespie if (!node)
2582672dff93Sespie {
2583672dff93Sespie *errstr = _("Unable to find node referenced by `%s' in `%s'.");
2584672dff93Sespie *errarg1 = xstrdup (entry->label);
2585672dff93Sespie *errarg2 = node_printed_rep (initial_node);
2586672dff93Sespie info_free_references (menu);
2587672dff93Sespie return initial_node;
2588672dff93Sespie }
2589672dff93Sespie
2590672dff93Sespie info_free_references (menu);
2591672dff93Sespie
2592672dff93Sespie /* Success. Go round the loop again. */
2593672dff93Sespie free (initial_node);
2594672dff93Sespie initial_node = node;
2595672dff93Sespie }
2596672dff93Sespie
2597672dff93Sespie return initial_node;
2598672dff93Sespie }
2599672dff93Sespie
2600672dff93Sespie /* Split STR into individual node names by writing null bytes in wherever
2601672dff93Sespie there are commas and constructing a list of the resulting pointers.
2602672dff93Sespie (We can do this since STR has had canonicalize_whitespace called on it.)
2603672dff93Sespie Return array terminated with NULL. */
2604672dff93Sespie
2605672dff93Sespie static char **
split_list_of_nodenames(char * str)2606*1076333cSespie split_list_of_nodenames (char *str)
2607672dff93Sespie {
2608672dff93Sespie unsigned len = 2;
2609672dff93Sespie char **nodes = xmalloc (len * sizeof (char *));
2610672dff93Sespie
2611672dff93Sespie nodes[len - 2] = str;
2612672dff93Sespie
2613672dff93Sespie while (*str++)
2614672dff93Sespie {
2615672dff93Sespie if (*str == ',')
2616672dff93Sespie {
2617672dff93Sespie *str++ = 0; /* get past the null byte */
2618672dff93Sespie len++;
2619672dff93Sespie nodes = xrealloc (nodes, len * sizeof (char *));
2620672dff93Sespie nodes[len - 2] = str;
2621672dff93Sespie }
2622672dff93Sespie }
2623672dff93Sespie
2624672dff93Sespie nodes[len - 1] = NULL;
2625672dff93Sespie
2626672dff93Sespie return nodes;
2627672dff93Sespie }
2628672dff93Sespie
2629672dff93Sespie
2630672dff93Sespie /* Read a line of input which is a sequence of menus (starting from
2631672dff93Sespie dir), and follow them. */
2632672dff93Sespie DECLARE_INFO_COMMAND (info_menu_sequence,
2633672dff93Sespie _("Read a list of menus starting from dir and follow them"))
2634672dff93Sespie {
2635*1076333cSespie char *line = info_read_in_echo_area (window, (char *) _("Follow menus: "));
2636672dff93Sespie
2637672dff93Sespie /* If the user aborted, quit now. */
2638672dff93Sespie if (!line)
2639672dff93Sespie {
2640672dff93Sespie info_abort_key (window, 0, 0);
2641672dff93Sespie return;
2642672dff93Sespie }
2643672dff93Sespie
2644672dff93Sespie canonicalize_whitespace (line);
2645672dff93Sespie
2646672dff93Sespie if (*line)
2647672dff93Sespie {
2648*1076333cSespie const char *errstr;
2649*1076333cSespie char *errarg1, *errarg2;
2650672dff93Sespie NODE *dir_node = info_get_node (NULL, NULL);
2651672dff93Sespie char **nodes = split_list_of_nodenames (line);
2652*1076333cSespie NODE *node = NULL;
2653672dff93Sespie
2654672dff93Sespie /* If DIR_NODE is NULL, they might be reading a file directly,
2655672dff93Sespie like in "info -d . -f ./foo". Try using "Top" instead. */
2656672dff93Sespie if (!dir_node)
2657672dff93Sespie {
2658672dff93Sespie char *file_name = window->node->parent;
2659672dff93Sespie
2660672dff93Sespie if (!file_name)
2661672dff93Sespie file_name = window->node->filename;
2662672dff93Sespie dir_node = info_get_node (file_name, NULL);
2663672dff93Sespie }
2664672dff93Sespie
2665672dff93Sespie /* If we still cannot find the starting point, give up.
2666672dff93Sespie We cannot allow a NULL pointer inside info_follow_menus. */
2667672dff93Sespie if (!dir_node)
2668*1076333cSespie info_error ((char *) msg_cant_find_node, "Top", NULL);
2669672dff93Sespie else
2670*1076333cSespie node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
2671672dff93Sespie
2672672dff93Sespie free (nodes);
2673672dff93Sespie if (!errstr)
2674672dff93Sespie info_set_node_of_window (1, window, node);
2675672dff93Sespie else
2676*1076333cSespie info_error ((char *) errstr, errarg1, errarg2);
2677672dff93Sespie }
2678672dff93Sespie
2679672dff93Sespie free (line);
2680672dff93Sespie if (!info_error_was_printed)
2681672dff93Sespie window_clear_echo_area ();
2682672dff93Sespie }
2683672dff93Sespie
2684672dff93Sespie /* Search the menu MENU for a (possibly mis-spelled) entry ARG.
2685672dff93Sespie Return the menu entry, or the best guess for what they meant by ARG,
2686672dff93Sespie or NULL if there's nothing in this menu seems to fit the bill.
2687672dff93Sespie If EXACT is non-zero, allow only exact matches. */
2688672dff93Sespie static REFERENCE *
entry_in_menu(char * arg,REFERENCE ** menu,int exact)2689*1076333cSespie entry_in_menu (char *arg, REFERENCE **menu, int exact)
2690672dff93Sespie {
2691672dff93Sespie REFERENCE *entry;
2692672dff93Sespie
2693672dff93Sespie /* First, try to find the specified menu item verbatim. */
2694672dff93Sespie entry = info_get_labeled_reference (arg, menu);
2695672dff93Sespie
2696672dff93Sespie /* If the item wasn't found, search the list sloppily. Perhaps we
2697672dff93Sespie have "Option Summary", but ARG is "option". */
2698672dff93Sespie if (!entry && !exact)
2699672dff93Sespie {
2700672dff93Sespie int i;
2701672dff93Sespie int best_guess = -1;
2702672dff93Sespie
2703672dff93Sespie for (i = 0; (entry = menu[i]); i++)
2704672dff93Sespie {
2705672dff93Sespie if (strcasecmp (entry->label, arg) == 0)
2706672dff93Sespie break;
2707672dff93Sespie else
2708672dff93Sespie if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2709672dff93Sespie best_guess = i;
2710672dff93Sespie }
2711672dff93Sespie
2712672dff93Sespie if (!entry && best_guess != -1)
2713672dff93Sespie entry = menu[best_guess];
2714672dff93Sespie }
2715672dff93Sespie
2716672dff93Sespie return entry;
2717672dff93Sespie }
2718672dff93Sespie
2719672dff93Sespie /* Find the node that is the best candidate to list the PROGRAM's
2720672dff93Sespie invocation info and its command-line options, by looking for menu
2721672dff93Sespie items and chains of menu items with characteristic names. */
2722672dff93Sespie void
info_intuit_options_node(WINDOW * window,NODE * initial_node,char * program)2723*1076333cSespie info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program)
2724672dff93Sespie {
2725672dff93Sespie /* The list of node names typical for GNU manuals where the program
2726672dff93Sespie usage and specifically the command-line arguments are described.
2727672dff93Sespie This is pure heuristics. I gathered these node names by looking
2728672dff93Sespie at all the Info files I could put my hands on. If you are
2729672dff93Sespie looking for evidence to complain to the GNU project about
2730672dff93Sespie non-uniform style of documentation, here you have your case! */
2731672dff93Sespie static const char *invocation_nodes[] = {
2732672dff93Sespie "%s invocation",
2733672dff93Sespie "Invoking %s",
2734672dff93Sespie "Preliminaries", /* m4 has Invoking under Preliminaries! */
2735672dff93Sespie "Invocation",
2736672dff93Sespie "Command Arguments",/* Emacs */
2737672dff93Sespie "Invoking `%s'",
2738672dff93Sespie "%s options",
2739672dff93Sespie "Options",
2740672dff93Sespie "Option ", /* e.g. "Option Summary" */
2741672dff93Sespie "Invoking",
2742672dff93Sespie "All options", /* tar, paxutils */
2743672dff93Sespie "Arguments",
2744672dff93Sespie "%s cmdline", /* ar */
2745672dff93Sespie "%s", /* last resort */
2746672dff93Sespie (const char *)0
2747672dff93Sespie };
2748672dff93Sespie NODE *node = NULL;
2749672dff93Sespie REFERENCE **menu;
2750672dff93Sespie const char **try_node;
2751672dff93Sespie
2752672dff93Sespie /* We keep looking deeper and deeper in the menu structure until
2753672dff93Sespie there are no more menus or no menu items from the above list.
2754672dff93Sespie Some manuals have the invocation node sitting 3 or 4 levels deep
2755672dff93Sespie in the menu hierarchy... */
2756672dff93Sespie for (node = initial_node; node; initial_node = node)
2757672dff93Sespie {
2758*1076333cSespie REFERENCE *entry = NULL;
2759672dff93Sespie
2760672dff93Sespie /* Build and return a list of the menu items in this node. */
2761672dff93Sespie menu = info_menu_of_node (initial_node);
2762672dff93Sespie
2763672dff93Sespie /* If no menu item in this node, stop here. Perhaps this node
2764672dff93Sespie is the one they need. */
2765672dff93Sespie if (!menu)
2766672dff93Sespie break;
2767672dff93Sespie
2768672dff93Sespie /* Look for node names typical for usage nodes in this menu. */
2769672dff93Sespie for (try_node = invocation_nodes; *try_node; try_node++)
2770672dff93Sespie {
2771*1076333cSespie char *nodename;
2772672dff93Sespie
2773*1076333cSespie nodename = xmalloc (strlen (program) + strlen (*try_node));
2774672dff93Sespie sprintf (nodename, *try_node, program);
2775672dff93Sespie /* The last resort "%s" is dangerous, so we restrict it
2776672dff93Sespie to exact matches here. */
2777672dff93Sespie entry = entry_in_menu (nodename, menu,
2778672dff93Sespie strcmp (*try_node, "%s") == 0);
2779*1076333cSespie free (nodename);
2780672dff93Sespie if (entry)
2781672dff93Sespie break;
2782672dff93Sespie }
2783672dff93Sespie
2784672dff93Sespie if (!entry)
2785672dff93Sespie break;
2786672dff93Sespie
2787672dff93Sespie if (!entry->filename)
2788672dff93Sespie entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2789672dff93Sespie : initial_node->filename);
2790672dff93Sespie /* Try to find this node. */
2791672dff93Sespie node = info_get_node (entry->filename, entry->nodename);
2792672dff93Sespie info_free_references (menu);
2793672dff93Sespie if (!node)
2794672dff93Sespie break;
2795672dff93Sespie }
2796672dff93Sespie
2797672dff93Sespie /* We've got our best shot at the invocation node. Now select it. */
2798672dff93Sespie if (initial_node)
2799672dff93Sespie info_set_node_of_window (1, window, initial_node);
2800672dff93Sespie if (!info_error_was_printed)
2801672dff93Sespie window_clear_echo_area ();
2802672dff93Sespie }
2803672dff93Sespie
2804672dff93Sespie /* Given a name of an Info file, find the name of the package it
2805672dff93Sespie describes by removing the leading directories and extensions. */
2806672dff93Sespie char *
program_name_from_file_name(char * file_name)2807*1076333cSespie program_name_from_file_name (char *file_name)
2808672dff93Sespie {
2809672dff93Sespie int i;
2810672dff93Sespie char *program_name = xstrdup (filename_non_directory (file_name));
2811672dff93Sespie
2812672dff93Sespie for (i = strlen (program_name) - 1; i > 0; i--)
2813672dff93Sespie if (program_name[i] == '.'
2814672dff93Sespie && (FILENAME_CMPN (program_name + i, ".info", 5) == 0
2815672dff93Sespie || FILENAME_CMPN (program_name + i, ".inf", 4) == 0
2816672dff93Sespie #ifdef __MSDOS__
2817672dff93Sespie || FILENAME_CMPN (program_name + i, ".i", 2) == 0
2818672dff93Sespie #endif
2819672dff93Sespie || isdigit (program_name[i + 1]))) /* a man page foo.1 */
2820672dff93Sespie {
2821672dff93Sespie program_name[i] = 0;
2822672dff93Sespie break;
2823672dff93Sespie }
2824672dff93Sespie return program_name;
2825672dff93Sespie }
2826672dff93Sespie
2827672dff93Sespie DECLARE_INFO_COMMAND (info_goto_invocation_node,
2828672dff93Sespie _("Find the node describing program invocation"))
2829672dff93Sespie {
2830*1076333cSespie const char *invocation_prompt = _("Find Invocation node of [%s]: ");
2831672dff93Sespie char *program_name, *line;
2832672dff93Sespie char *default_program_name, *prompt, *file_name;
2833672dff93Sespie NODE *top_node;
2834672dff93Sespie
2835672dff93Sespie /* Intuit the name of the program they are likely to want.
2836672dff93Sespie We use the file name of the current Info file as a hint. */
2837672dff93Sespie file_name = window->node->parent ? window->node->parent
2838672dff93Sespie : window->node->filename;
2839672dff93Sespie default_program_name = program_name_from_file_name (file_name);
2840672dff93Sespie
2841672dff93Sespie prompt = (char *)xmalloc (strlen (default_program_name) +
2842672dff93Sespie strlen (invocation_prompt));
2843672dff93Sespie sprintf (prompt, invocation_prompt, default_program_name);
2844672dff93Sespie line = info_read_in_echo_area (window, prompt);
2845672dff93Sespie free (prompt);
2846672dff93Sespie if (!line)
2847672dff93Sespie {
2848*1076333cSespie info_abort_key (window, 0, 0);
2849672dff93Sespie return;
2850672dff93Sespie }
2851672dff93Sespie if (*line)
2852672dff93Sespie program_name = line;
2853672dff93Sespie else
2854672dff93Sespie program_name = default_program_name;
2855672dff93Sespie
2856672dff93Sespie /* In interactive usage they'd probably expect us to begin looking
2857672dff93Sespie from the Top node. */
2858672dff93Sespie top_node = info_get_node (file_name, NULL);
2859672dff93Sespie if (!top_node)
2860*1076333cSespie info_error ((char *) msg_cant_find_node, "Top", NULL);
2861672dff93Sespie
2862672dff93Sespie info_intuit_options_node (window, top_node, program_name);
2863672dff93Sespie free (line);
2864672dff93Sespie free (default_program_name);
2865672dff93Sespie }
2866672dff93Sespie
2867fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
2868840175f0Skstailey DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
2869fbc94a17Sniklas {
2870fbc94a17Sniklas char *line;
2871fbc94a17Sniklas
2872*1076333cSespie line = info_read_in_echo_area (window, (char *) _("Get Manpage: "));
2873fbc94a17Sniklas
2874fbc94a17Sniklas if (!line)
2875fbc94a17Sniklas {
2876fbc94a17Sniklas info_abort_key (window, 0, 0);
2877fbc94a17Sniklas return;
2878fbc94a17Sniklas }
2879fbc94a17Sniklas
2880fbc94a17Sniklas canonicalize_whitespace (line);
2881fbc94a17Sniklas
2882fbc94a17Sniklas if (*line)
2883fbc94a17Sniklas {
2884fbc94a17Sniklas char *goto_command;
2885fbc94a17Sniklas
2886fbc94a17Sniklas goto_command = (char *)xmalloc
2887fbc94a17Sniklas (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
2888fbc94a17Sniklas
2889fbc94a17Sniklas sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
2890fbc94a17Sniklas
2891fbc94a17Sniklas info_parse_and_select (goto_command, window);
2892fbc94a17Sniklas free (goto_command);
2893fbc94a17Sniklas }
2894fbc94a17Sniklas
2895fbc94a17Sniklas free (line);
2896fbc94a17Sniklas if (!info_error_was_printed)
2897fbc94a17Sniklas window_clear_echo_area ();
2898fbc94a17Sniklas }
2899fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
2900fbc94a17Sniklas
2901fbc94a17Sniklas /* Move to the "Top" node in this file. */
2902840175f0Skstailey DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
2903fbc94a17Sniklas {
2904672dff93Sespie info_parse_and_select ("Top", window);
2905fbc94a17Sniklas }
2906fbc94a17Sniklas
2907fbc94a17Sniklas /* Move to the node "(dir)Top". */
2908840175f0Skstailey DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
2909fbc94a17Sniklas {
2910fbc94a17Sniklas info_parse_and_select ("(dir)Top", window);
2911fbc94a17Sniklas }
2912fbc94a17Sniklas
2913840175f0Skstailey
2914fbc94a17Sniklas /* Read the name of a node to kill. The list of available nodes comes
2915fbc94a17Sniklas from the nodes appearing in the current window configuration. */
2916840175f0Skstailey static char *
read_nodename_to_kill(WINDOW * window)2917*1076333cSespie read_nodename_to_kill (WINDOW *window)
2918fbc94a17Sniklas {
2919840175f0Skstailey int iw;
2920840175f0Skstailey char *nodename;
2921840175f0Skstailey INFO_WINDOW *info_win;
2922840175f0Skstailey REFERENCE **menu = NULL;
2923fbc94a17Sniklas int menu_index = 0, menu_slots = 0;
2924840175f0Skstailey char *default_nodename = xstrdup (active_window->node->nodename);
2925*1076333cSespie char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename));
2926fbc94a17Sniklas
2927840175f0Skstailey sprintf (prompt, _("Kill node (%s): "), default_nodename);
2928840175f0Skstailey
2929840175f0Skstailey for (iw = 0; (info_win = info_windows[iw]); iw++)
2930fbc94a17Sniklas {
2931840175f0Skstailey REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2932840175f0Skstailey entry->label = xstrdup (info_win->window->node->nodename);
2933fbc94a17Sniklas entry->filename = entry->nodename = (char *)NULL;
2934fbc94a17Sniklas
2935840175f0Skstailey add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
2936840175f0Skstailey REFERENCE *);
2937fbc94a17Sniklas }
2938fbc94a17Sniklas
2939fbc94a17Sniklas nodename = info_read_completing_in_echo_area (window, prompt, menu);
2940fbc94a17Sniklas free (prompt);
2941fbc94a17Sniklas info_free_references (menu);
2942fbc94a17Sniklas if (nodename && !*nodename)
2943fbc94a17Sniklas {
2944fbc94a17Sniklas free (nodename);
2945fbc94a17Sniklas nodename = default_nodename;
2946fbc94a17Sniklas }
2947fbc94a17Sniklas else
2948fbc94a17Sniklas free (default_nodename);
2949840175f0Skstailey
2950840175f0Skstailey return nodename;
2951fbc94a17Sniklas }
2952fbc94a17Sniklas
2953840175f0Skstailey
2954840175f0Skstailey /* Delete NODENAME from this window, showing the most
2955840175f0Skstailey recently selected node in this window. */
2956840175f0Skstailey static void
kill_node(WINDOW * window,char * nodename)2957*1076333cSespie kill_node (WINDOW *window, char *nodename)
2958840175f0Skstailey {
2959840175f0Skstailey int iw, i;
2960840175f0Skstailey INFO_WINDOW *info_win;
2961840175f0Skstailey NODE *temp;
2962840175f0Skstailey
2963fbc94a17Sniklas /* If there is no nodename to kill, quit now. */
2964fbc94a17Sniklas if (!nodename)
2965fbc94a17Sniklas {
2966fbc94a17Sniklas info_abort_key (window, 0, 0);
2967fbc94a17Sniklas return;
2968fbc94a17Sniklas }
2969fbc94a17Sniklas
2970fbc94a17Sniklas /* If there is a nodename, find it in our window list. */
2971840175f0Skstailey for (iw = 0; (info_win = info_windows[iw]); iw++)
2972672dff93Sespie if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
2973672dff93Sespie && info_win->window == window)
2974fbc94a17Sniklas break;
2975fbc94a17Sniklas
2976fbc94a17Sniklas if (!info_win)
2977fbc94a17Sniklas {
2978fbc94a17Sniklas if (*nodename)
2979*1076333cSespie info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL);
2980fbc94a17Sniklas else
2981fbc94a17Sniklas window_clear_echo_area ();
2982fbc94a17Sniklas
2983fbc94a17Sniklas return;
2984fbc94a17Sniklas }
2985fbc94a17Sniklas
2986fbc94a17Sniklas /* If there are no more nodes left anywhere to view, complain and exit. */
2987fbc94a17Sniklas if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
2988fbc94a17Sniklas {
2989*1076333cSespie info_error ((char *) _("Cannot kill the last node"), NULL, NULL);
2990fbc94a17Sniklas return;
2991fbc94a17Sniklas }
2992fbc94a17Sniklas
2993840175f0Skstailey /* INFO_WIN contains the node that the user wants to stop viewing. Delete
2994840175f0Skstailey this node from the list of nodes previously shown in this window. */
2995fbc94a17Sniklas for (i = info_win->current; i < info_win->nodes_index; i++)
2996672dff93Sespie info_win->nodes[i] = info_win->nodes[i + 1];
2997fbc94a17Sniklas
2998fbc94a17Sniklas /* There is one less node in this window's history list. */
2999fbc94a17Sniklas info_win->nodes_index--;
3000fbc94a17Sniklas
3001fbc94a17Sniklas /* Make this window show the most recent history node. */
3002fbc94a17Sniklas info_win->current = info_win->nodes_index - 1;
3003fbc94a17Sniklas
3004fbc94a17Sniklas /* If there aren't any nodes left in this window, steal one from the
3005fbc94a17Sniklas next window. */
3006fbc94a17Sniklas if (info_win->current < 0)
3007fbc94a17Sniklas {
3008fbc94a17Sniklas INFO_WINDOW *stealer;
3009fbc94a17Sniklas int which, pagetop;
3010fbc94a17Sniklas long point;
3011fbc94a17Sniklas
3012fbc94a17Sniklas if (info_windows[iw + 1])
3013fbc94a17Sniklas stealer = info_windows[iw + 1];
3014fbc94a17Sniklas else
3015fbc94a17Sniklas stealer = info_windows[0];
3016fbc94a17Sniklas
3017fbc94a17Sniklas /* If the node being displayed in the next window is not the most
3018fbc94a17Sniklas recently loaded one, get the most recently loaded one. */
3019fbc94a17Sniklas if ((stealer->nodes_index - 1) != stealer->current)
3020fbc94a17Sniklas which = stealer->nodes_index - 1;
3021fbc94a17Sniklas
3022fbc94a17Sniklas /* Else, if there is another node behind the stealers current node,
3023fbc94a17Sniklas use that one. */
3024fbc94a17Sniklas else if (stealer->current > 0)
3025fbc94a17Sniklas which = stealer->current - 1;
3026fbc94a17Sniklas
3027fbc94a17Sniklas /* Else, just use the node appearing in STEALER's window. */
3028fbc94a17Sniklas else
3029fbc94a17Sniklas which = stealer->current;
3030fbc94a17Sniklas
3031fbc94a17Sniklas /* Copy this node. */
3032fbc94a17Sniklas {
3033840175f0Skstailey NODE *copy = xmalloc (sizeof (NODE));
3034fbc94a17Sniklas
3035fbc94a17Sniklas temp = stealer->nodes[which];
3036fbc94a17Sniklas point = stealer->points[which];
3037fbc94a17Sniklas pagetop = stealer->pagetops[which];
3038fbc94a17Sniklas
3039fbc94a17Sniklas copy->filename = temp->filename;
3040fbc94a17Sniklas copy->parent = temp->parent;
3041fbc94a17Sniklas copy->nodename = temp->nodename;
3042fbc94a17Sniklas copy->contents = temp->contents;
3043fbc94a17Sniklas copy->nodelen = temp->nodelen;
3044fbc94a17Sniklas copy->flags = temp->flags;
3045672dff93Sespie copy->display_pos = temp->display_pos;
3046fbc94a17Sniklas
3047fbc94a17Sniklas temp = copy;
3048fbc94a17Sniklas }
3049fbc94a17Sniklas
3050fbc94a17Sniklas window_set_node_of_window (info_win->window, temp);
3051fbc94a17Sniklas window->point = point;
3052fbc94a17Sniklas window->pagetop = pagetop;
3053fbc94a17Sniklas remember_window_and_node (info_win->window, temp);
3054fbc94a17Sniklas }
3055fbc94a17Sniklas else
3056fbc94a17Sniklas {
3057fbc94a17Sniklas temp = info_win->nodes[info_win->current];
3058672dff93Sespie temp->display_pos = info_win->points[info_win->current];
3059fbc94a17Sniklas window_set_node_of_window (info_win->window, temp);
3060fbc94a17Sniklas }
3061840175f0Skstailey
3062fbc94a17Sniklas if (!info_error_was_printed)
3063fbc94a17Sniklas window_clear_echo_area ();
3064840175f0Skstailey
3065840175f0Skstailey if (auto_footnotes_p)
3066840175f0Skstailey info_get_or_remove_footnotes (window);
3067fbc94a17Sniklas }
3068fbc94a17Sniklas
3069840175f0Skstailey /* Kill current node, thus going back one in the node history. I (karl)
3070840175f0Skstailey do not think this is completely correct yet, because of the
3071840175f0Skstailey window-changing stuff in kill_node, but it's a lot better than the
3072840175f0Skstailey previous implementation, which did not account for nodes being
3073840175f0Skstailey visited twice at all. */
3074840175f0Skstailey DECLARE_INFO_COMMAND (info_history_node,
3075840175f0Skstailey _("Select the most recently selected node"))
3076840175f0Skstailey {
3077840175f0Skstailey kill_node (window, active_window->node->nodename);
3078840175f0Skstailey }
3079840175f0Skstailey
3080840175f0Skstailey /* Kill named node. */
3081840175f0Skstailey DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
3082840175f0Skstailey {
3083840175f0Skstailey char *nodename = read_nodename_to_kill (window);
3084840175f0Skstailey kill_node (window, nodename);
3085840175f0Skstailey }
3086840175f0Skstailey
3087840175f0Skstailey
3088fbc94a17Sniklas /* Read the name of a file and select the entire file. */
3089840175f0Skstailey DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
3090fbc94a17Sniklas {
3091fbc94a17Sniklas char *line;
3092fbc94a17Sniklas
3093*1076333cSespie line = info_read_in_echo_area (window, (char *) _("Find file: "));
3094fbc94a17Sniklas if (!line)
3095fbc94a17Sniklas {
3096fbc94a17Sniklas info_abort_key (active_window, 1, 0);
3097fbc94a17Sniklas return;
3098fbc94a17Sniklas }
3099fbc94a17Sniklas
3100fbc94a17Sniklas if (*line)
3101fbc94a17Sniklas {
3102fbc94a17Sniklas NODE *node;
3103fbc94a17Sniklas
3104fbc94a17Sniklas node = info_get_node (line, "*");
3105fbc94a17Sniklas if (!node)
3106fbc94a17Sniklas {
3107fbc94a17Sniklas if (info_recent_file_error)
3108*1076333cSespie info_error (info_recent_file_error, NULL, NULL);
3109fbc94a17Sniklas else
3110*1076333cSespie info_error ((char *) _("Cannot find `%s'."), line, NULL);
3111fbc94a17Sniklas }
3112fbc94a17Sniklas else
3113672dff93Sespie info_set_node_of_window (1, window, node);
3114672dff93Sespie
3115fbc94a17Sniklas free (line);
3116fbc94a17Sniklas }
3117fbc94a17Sniklas
3118fbc94a17Sniklas if (!info_error_was_printed)
3119fbc94a17Sniklas window_clear_echo_area ();
3120fbc94a17Sniklas }
3121fbc94a17Sniklas
3122fbc94a17Sniklas /* **************************************************************** */
3123fbc94a17Sniklas /* */
3124fbc94a17Sniklas /* Dumping and Printing Nodes */
3125fbc94a17Sniklas /* */
3126fbc94a17Sniklas /* **************************************************************** */
3127fbc94a17Sniklas
3128fbc94a17Sniklas #define VERBOSE_NODE_DUMPING
3129*1076333cSespie static void write_node_to_stream (NODE *node, FILE *stream);
3130*1076333cSespie static void dump_node_to_stream (char *filename, char *nodename,
3131*1076333cSespie FILE *stream, int dump_subnodes);
3132*1076333cSespie static void initialize_dumping (void);
3133fbc94a17Sniklas
3134fbc94a17Sniklas /* Dump the nodes specified by FILENAME and NODENAMES to the file named
3135fbc94a17Sniklas in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3136fbc94a17Sniklas the nodes which appear in the menu of each node dumped. */
3137fbc94a17Sniklas void
dump_nodes_to_file(char * filename,char ** nodenames,char * output_filename,int dump_subnodes)3138*1076333cSespie dump_nodes_to_file (char *filename, char **nodenames,
3139*1076333cSespie char *output_filename, int dump_subnodes)
3140fbc94a17Sniklas {
3141fbc94a17Sniklas register int i;
3142fbc94a17Sniklas FILE *output_stream;
3143fbc94a17Sniklas
3144fbc94a17Sniklas /* Get the stream to print the nodes to. Special case of an output
3145fbc94a17Sniklas filename of "-" means to dump the nodes to stdout. */
3146fbc94a17Sniklas if (strcmp (output_filename, "-") == 0)
3147fbc94a17Sniklas output_stream = stdout;
3148fbc94a17Sniklas else
3149fbc94a17Sniklas output_stream = fopen (output_filename, "w");
3150fbc94a17Sniklas
3151fbc94a17Sniklas if (!output_stream)
3152fbc94a17Sniklas {
3153*1076333cSespie info_error ((char *) _("Could not create output file `%s'."),
3154*1076333cSespie output_filename, NULL);
3155fbc94a17Sniklas return;
3156fbc94a17Sniklas }
3157fbc94a17Sniklas
3158fbc94a17Sniklas /* Print each node to stream. */
3159fbc94a17Sniklas initialize_dumping ();
3160fbc94a17Sniklas for (i = 0; nodenames[i]; i++)
3161fbc94a17Sniklas dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
3162fbc94a17Sniklas
3163fbc94a17Sniklas if (output_stream != stdout)
3164fbc94a17Sniklas fclose (output_stream);
3165fbc94a17Sniklas
3166fbc94a17Sniklas #if defined (VERBOSE_NODE_DUMPING)
3167*1076333cSespie info_error ((char *) _("Done."), NULL, NULL);
3168fbc94a17Sniklas #endif /* VERBOSE_NODE_DUMPING */
3169fbc94a17Sniklas }
3170fbc94a17Sniklas
3171fbc94a17Sniklas /* A place to remember already dumped nodes. */
3172fbc94a17Sniklas static char **dumped_already = (char **)NULL;
3173fbc94a17Sniklas static int dumped_already_index = 0;
3174fbc94a17Sniklas static int dumped_already_slots = 0;
3175fbc94a17Sniklas
3176fbc94a17Sniklas static void
initialize_dumping(void)3177*1076333cSespie initialize_dumping (void)
3178fbc94a17Sniklas {
3179fbc94a17Sniklas dumped_already_index = 0;
3180fbc94a17Sniklas }
3181fbc94a17Sniklas
3182fbc94a17Sniklas /* Get and print the node specified by FILENAME and NODENAME to STREAM.
3183fbc94a17Sniklas If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
3184fbc94a17Sniklas in the menu of each node dumped. */
3185fbc94a17Sniklas static void
dump_node_to_stream(char * filename,char * nodename,FILE * stream,int dump_subnodes)3186*1076333cSespie dump_node_to_stream (char *filename, char *nodename,
3187*1076333cSespie FILE *stream, int dump_subnodes)
3188fbc94a17Sniklas {
3189fbc94a17Sniklas register int i;
3190fbc94a17Sniklas NODE *node;
3191fbc94a17Sniklas
3192fbc94a17Sniklas node = info_get_node (filename, nodename);
3193fbc94a17Sniklas
3194fbc94a17Sniklas if (!node)
3195fbc94a17Sniklas {
3196fbc94a17Sniklas if (info_recent_file_error)
3197*1076333cSespie info_error (info_recent_file_error, NULL, NULL);
3198fbc94a17Sniklas else
3199fbc94a17Sniklas {
3200fbc94a17Sniklas if (filename && *nodename != '(')
3201*1076333cSespie info_error ((char *) msg_cant_file_node,
3202*1076333cSespie filename_non_directory (filename),
3203672dff93Sespie nodename);
3204fbc94a17Sniklas else
3205*1076333cSespie info_error ((char *) msg_cant_find_node, nodename, NULL);
3206fbc94a17Sniklas }
3207fbc94a17Sniklas return;
3208fbc94a17Sniklas }
3209fbc94a17Sniklas
3210fbc94a17Sniklas /* If we have already dumped this node, don't dump it again. */
3211fbc94a17Sniklas for (i = 0; i < dumped_already_index; i++)
3212fbc94a17Sniklas if (strcmp (node->nodename, dumped_already[i]) == 0)
3213fbc94a17Sniklas {
3214fbc94a17Sniklas free (node);
3215fbc94a17Sniklas return;
3216fbc94a17Sniklas }
3217fbc94a17Sniklas add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
3218fbc94a17Sniklas dumped_already_slots, 50, char *);
3219fbc94a17Sniklas
3220fbc94a17Sniklas #if defined (VERBOSE_NODE_DUMPING)
3221fbc94a17Sniklas /* Maybe we should print some information about the node being output. */
3222*1076333cSespie info_error ((char *) _("Writing node %s..."), node_printed_rep (node), NULL);
3223fbc94a17Sniklas #endif /* VERBOSE_NODE_DUMPING */
3224fbc94a17Sniklas
3225fbc94a17Sniklas write_node_to_stream (node, stream);
3226fbc94a17Sniklas
3227fbc94a17Sniklas /* If we are dumping subnodes, get the list of menu items in this node,
3228fbc94a17Sniklas and dump each one recursively. */
3229fbc94a17Sniklas if (dump_subnodes)
3230fbc94a17Sniklas {
3231fbc94a17Sniklas REFERENCE **menu = (REFERENCE **)NULL;
3232fbc94a17Sniklas
3233fbc94a17Sniklas /* If this node is an Index, do not dump the menu references. */
3234fbc94a17Sniklas if (string_in_line ("Index", node->nodename) == -1)
3235fbc94a17Sniklas menu = info_menu_of_node (node);
3236fbc94a17Sniklas
3237fbc94a17Sniklas if (menu)
3238fbc94a17Sniklas {
3239fbc94a17Sniklas for (i = 0; menu[i]; i++)
3240fbc94a17Sniklas {
3241fbc94a17Sniklas /* We don't dump Info files which are different than the
3242fbc94a17Sniklas current one. */
3243fbc94a17Sniklas if (!menu[i]->filename)
3244fbc94a17Sniklas dump_node_to_stream
3245fbc94a17Sniklas (filename, menu[i]->nodename, stream, dump_subnodes);
3246fbc94a17Sniklas }
3247fbc94a17Sniklas info_free_references (menu);
3248fbc94a17Sniklas }
3249fbc94a17Sniklas }
3250fbc94a17Sniklas
3251fbc94a17Sniklas free (node);
3252fbc94a17Sniklas }
3253fbc94a17Sniklas
3254fbc94a17Sniklas /* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3255fbc94a17Sniklas the nodes which appear in the menu of each node dumped. */
3256fbc94a17Sniklas void
dump_node_to_file(NODE * node,char * filename,int dump_subnodes)3257*1076333cSespie dump_node_to_file (NODE *node, char *filename, int dump_subnodes)
3258fbc94a17Sniklas {
3259fbc94a17Sniklas FILE *output_stream;
3260fbc94a17Sniklas char *nodes_filename;
3261fbc94a17Sniklas
3262fbc94a17Sniklas /* Get the stream to print this node to. Special case of an output
3263fbc94a17Sniklas filename of "-" means to dump the nodes to stdout. */
3264fbc94a17Sniklas if (strcmp (filename, "-") == 0)
3265fbc94a17Sniklas output_stream = stdout;
3266fbc94a17Sniklas else
3267fbc94a17Sniklas output_stream = fopen (filename, "w");
3268fbc94a17Sniklas
3269fbc94a17Sniklas if (!output_stream)
3270fbc94a17Sniklas {
3271*1076333cSespie info_error ((char *) _("Could not create output file `%s'."), filename,
3272*1076333cSespie NULL);
3273fbc94a17Sniklas return;
3274fbc94a17Sniklas }
3275fbc94a17Sniklas
3276fbc94a17Sniklas if (node->parent)
3277fbc94a17Sniklas nodes_filename = node->parent;
3278fbc94a17Sniklas else
3279fbc94a17Sniklas nodes_filename = node->filename;
3280fbc94a17Sniklas
3281fbc94a17Sniklas initialize_dumping ();
3282fbc94a17Sniklas dump_node_to_stream
3283fbc94a17Sniklas (nodes_filename, node->nodename, output_stream, dump_subnodes);
3284fbc94a17Sniklas
3285fbc94a17Sniklas if (output_stream != stdout)
3286fbc94a17Sniklas fclose (output_stream);
3287fbc94a17Sniklas
3288fbc94a17Sniklas #if defined (VERBOSE_NODE_DUMPING)
3289*1076333cSespie info_error ((char *) _("Done."), NULL, NULL);
3290fbc94a17Sniklas #endif /* VERBOSE_NODE_DUMPING */
3291fbc94a17Sniklas }
3292fbc94a17Sniklas
3293fbc94a17Sniklas #if !defined (DEFAULT_INFO_PRINT_COMMAND)
3294fbc94a17Sniklas # define DEFAULT_INFO_PRINT_COMMAND "lpr"
3295fbc94a17Sniklas #endif /* !DEFAULT_INFO_PRINT_COMMAND */
3296fbc94a17Sniklas
3297fbc94a17Sniklas DECLARE_INFO_COMMAND (info_print_node,
3298840175f0Skstailey _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
3299fbc94a17Sniklas {
3300fbc94a17Sniklas print_node (window->node);
3301fbc94a17Sniklas }
3302fbc94a17Sniklas
3303fbc94a17Sniklas /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3304fbc94a17Sniklas void
print_node(NODE * node)3305*1076333cSespie print_node (NODE *node)
3306fbc94a17Sniklas {
3307fbc94a17Sniklas FILE *printer_pipe;
3308840175f0Skstailey char *print_command = getenv ("INFO_PRINT_COMMAND");
3309672dff93Sespie int piping = 0;
3310fbc94a17Sniklas
3311fbc94a17Sniklas if (!print_command || !*print_command)
3312fbc94a17Sniklas print_command = DEFAULT_INFO_PRINT_COMMAND;
3313fbc94a17Sniklas
3314672dff93Sespie /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
3315672dff93Sespie (default) text mode, since the printer drivers there need to see
3316672dff93Sespie DOS-style CRLF pairs at the end of each line.
3317672dff93Sespie
3318672dff93Sespie FIXME: if we are to support Mac-style text files, we might need
3319672dff93Sespie to convert the text here. */
3320672dff93Sespie
3321672dff93Sespie /* INFO_PRINT_COMMAND which says ">file" means write to that file.
3322672dff93Sespie Presumably, the name of the file is the local printer device. */
3323672dff93Sespie if (*print_command == '>')
3324672dff93Sespie printer_pipe = fopen (++print_command, "w");
3325672dff93Sespie else
3326672dff93Sespie {
3327fbc94a17Sniklas printer_pipe = popen (print_command, "w");
3328672dff93Sespie piping = 1;
3329672dff93Sespie }
3330fbc94a17Sniklas
3331fbc94a17Sniklas if (!printer_pipe)
3332fbc94a17Sniklas {
3333*1076333cSespie info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL);
3334fbc94a17Sniklas return;
3335fbc94a17Sniklas }
3336fbc94a17Sniklas
3337fbc94a17Sniklas #if defined (VERBOSE_NODE_DUMPING)
3338fbc94a17Sniklas /* Maybe we should print some information about the node being output. */
3339*1076333cSespie info_error ((char *) _("Printing node %s..."), node_printed_rep (node), NULL);
3340fbc94a17Sniklas #endif /* VERBOSE_NODE_DUMPING */
3341fbc94a17Sniklas
3342fbc94a17Sniklas write_node_to_stream (node, printer_pipe);
3343672dff93Sespie if (piping)
3344fbc94a17Sniklas pclose (printer_pipe);
3345672dff93Sespie else
3346672dff93Sespie fclose (printer_pipe);
3347fbc94a17Sniklas
3348fbc94a17Sniklas #if defined (VERBOSE_NODE_DUMPING)
3349*1076333cSespie info_error ((char *) _("Done."), NULL, NULL);
3350fbc94a17Sniklas #endif /* VERBOSE_NODE_DUMPING */
3351fbc94a17Sniklas }
3352fbc94a17Sniklas
3353fbc94a17Sniklas static void
write_node_to_stream(NODE * node,FILE * stream)3354*1076333cSespie write_node_to_stream (NODE *node, FILE *stream)
3355fbc94a17Sniklas {
3356fbc94a17Sniklas fwrite (node->contents, 1, node->nodelen, stream);
3357fbc94a17Sniklas }
3358fbc94a17Sniklas
3359fbc94a17Sniklas /* **************************************************************** */
3360fbc94a17Sniklas /* */
3361fbc94a17Sniklas /* Info Searching Commands */
3362fbc94a17Sniklas /* */
3363fbc94a17Sniklas /* **************************************************************** */
3364fbc94a17Sniklas
3365fbc94a17Sniklas /* Variable controlling the garbage collection of files briefly visited
3366fbc94a17Sniklas during searches. Such files are normally gc'ed, unless they were
3367fbc94a17Sniklas compressed to begin with. If this variable is non-zero, it says
3368fbc94a17Sniklas to gc even those file buffer contents which had to be uncompressed. */
3369fbc94a17Sniklas int gc_compressed_files = 0;
3370fbc94a17Sniklas
3371*1076333cSespie static void info_gc_file_buffers (void);
3372*1076333cSespie static void info_search_1 (WINDOW *window, int count,
3373*1076333cSespie unsigned char key, int case_sensitive, int ask_for_string);
3374fbc94a17Sniklas
3375fbc94a17Sniklas static char *search_string = (char *)NULL;
3376fbc94a17Sniklas static int search_string_size = 0;
3377fbc94a17Sniklas static int isearch_is_active = 0;
3378fbc94a17Sniklas
3379672dff93Sespie static int last_search_direction = 0;
3380672dff93Sespie static int last_search_case_sensitive = 0;
3381672dff93Sespie
3382fbc94a17Sniklas /* Return the file buffer which belongs to WINDOW's node. */
3383fbc94a17Sniklas FILE_BUFFER *
file_buffer_of_window(WINDOW * window)3384*1076333cSespie file_buffer_of_window (WINDOW *window)
3385fbc94a17Sniklas {
3386fbc94a17Sniklas /* If this window has no node, then it has no file buffer. */
3387fbc94a17Sniklas if (!window->node)
3388fbc94a17Sniklas return ((FILE_BUFFER *)NULL);
3389fbc94a17Sniklas
3390fbc94a17Sniklas if (window->node->parent)
3391fbc94a17Sniklas return (info_find_file (window->node->parent));
3392fbc94a17Sniklas
3393fbc94a17Sniklas if (window->node->filename)
3394fbc94a17Sniklas return (info_find_file (window->node->filename));
3395fbc94a17Sniklas
3396fbc94a17Sniklas return ((FILE_BUFFER *)NULL);
3397fbc94a17Sniklas }
3398fbc94a17Sniklas
3399fbc94a17Sniklas /* Search for STRING in NODE starting at START. Return -1 if the string
3400fbc94a17Sniklas was not found, or the location of the string if it was. If WINDOW is
3401fbc94a17Sniklas passed as non-null, set the window's node to be NODE, its point to be
3402fbc94a17Sniklas the found string, and readjust the window's pagetop. Final argument
3403fbc94a17Sniklas DIR says which direction to search in. If it is positive, search
3404fbc94a17Sniklas forward, else backwards. */
3405fbc94a17Sniklas long
info_search_in_node(char * string,NODE * node,long int start,WINDOW * window,int dir,int case_sensitive)3406*1076333cSespie info_search_in_node (char *string, NODE *node, long int start,
3407*1076333cSespie WINDOW *window, int dir, int case_sensitive)
3408fbc94a17Sniklas {
3409fbc94a17Sniklas SEARCH_BINDING binding;
3410fbc94a17Sniklas long offset;
3411fbc94a17Sniklas
3412fbc94a17Sniklas binding.buffer = node->contents;
3413fbc94a17Sniklas binding.start = start;
3414fbc94a17Sniklas binding.end = node->nodelen;
3415672dff93Sespie binding.flags = 0;
3416672dff93Sespie if (!case_sensitive)
3417672dff93Sespie binding.flags |= S_FoldCase;
3418fbc94a17Sniklas
3419fbc94a17Sniklas if (dir < 0)
3420fbc94a17Sniklas {
3421fbc94a17Sniklas binding.end = 0;
3422fbc94a17Sniklas binding.flags |= S_SkipDest;
3423fbc94a17Sniklas }
3424fbc94a17Sniklas
3425fbc94a17Sniklas if (binding.start < 0)
3426fbc94a17Sniklas return (-1);
3427fbc94a17Sniklas
3428fbc94a17Sniklas /* For incremental searches, we always wish to skip past the string. */
3429fbc94a17Sniklas if (isearch_is_active)
3430fbc94a17Sniklas binding.flags |= S_SkipDest;
3431fbc94a17Sniklas
3432fbc94a17Sniklas offset = search (string, &binding);
3433fbc94a17Sniklas
3434fbc94a17Sniklas if (offset != -1 && window)
3435fbc94a17Sniklas {
3436fbc94a17Sniklas set_remembered_pagetop_and_point (window);
3437fbc94a17Sniklas if (window->node != node)
3438fbc94a17Sniklas window_set_node_of_window (window, node);
3439fbc94a17Sniklas window->point = offset;
3440fbc94a17Sniklas window_adjust_pagetop (window);
3441fbc94a17Sniklas }
3442fbc94a17Sniklas return (offset);
3443fbc94a17Sniklas }
3444fbc94a17Sniklas
3445fbc94a17Sniklas /* Search NODE, looking for the largest possible match of STRING. Start the
3446fbc94a17Sniklas search at START. Return the absolute position of the match, or -1, if
3447fbc94a17Sniklas no part of the string could be found. */
3448fbc94a17Sniklas long
info_target_search_node(NODE * node,char * string,long int start)3449*1076333cSespie info_target_search_node (NODE *node, char *string, long int start)
3450fbc94a17Sniklas {
3451fbc94a17Sniklas register int i;
3452*1076333cSespie long offset = 0;
3453fbc94a17Sniklas char *target;
3454fbc94a17Sniklas
3455840175f0Skstailey target = xstrdup (string);
3456fbc94a17Sniklas i = strlen (target);
3457fbc94a17Sniklas
3458fbc94a17Sniklas /* Try repeatedly searching for this string while removing words from
3459fbc94a17Sniklas the end of it. */
3460fbc94a17Sniklas while (i)
3461fbc94a17Sniklas {
3462fbc94a17Sniklas target[i] = '\0';
3463672dff93Sespie offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
3464fbc94a17Sniklas
3465fbc94a17Sniklas if (offset != -1)
3466fbc94a17Sniklas break;
3467fbc94a17Sniklas
3468fbc94a17Sniklas /* Delete the last word from TARGET. */
3469fbc94a17Sniklas for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
3470fbc94a17Sniklas }
3471fbc94a17Sniklas free (target);
3472fbc94a17Sniklas return (offset);
3473fbc94a17Sniklas }
3474fbc94a17Sniklas
3475fbc94a17Sniklas /* Search for STRING starting in WINDOW at point. If the string is found
3476fbc94a17Sniklas in this node, set point to that position. Otherwise, get the file buffer
3477fbc94a17Sniklas associated with WINDOW's node, and search through each node in that file.
3478fbc94a17Sniklas If the search fails, return non-zero, else zero. Side-effect window
3479fbc94a17Sniklas leaving the node and point where the string was found current. */
3480fbc94a17Sniklas static int
info_search_internal(char * string,WINDOW * window,int dir,int case_sensitive)3481*1076333cSespie info_search_internal (char *string, WINDOW *window,
3482*1076333cSespie int dir, int case_sensitive)
3483fbc94a17Sniklas {
3484fbc94a17Sniklas register int i;
3485fbc94a17Sniklas FILE_BUFFER *file_buffer;
3486fbc94a17Sniklas char *initial_nodename;
3487fbc94a17Sniklas long ret, start = 0;
3488fbc94a17Sniklas
3489fbc94a17Sniklas file_buffer = file_buffer_of_window (window);
3490fbc94a17Sniklas initial_nodename = window->node->nodename;
3491fbc94a17Sniklas
3492672dff93Sespie /* This used to begin from window->point, unless this was a repeated
3493672dff93Sespie search command. But invoking search with an argument loses with
3494672dff93Sespie that logic, since info_last_executed_command is then set to
3495672dff93Sespie info_add_digit_to_numeric_arg. I think there's no sense in
3496672dff93Sespie ``finding'' a string that is already under the cursor, anyway. */
3497fbc94a17Sniklas ret = info_search_in_node
3498672dff93Sespie (string, window->node, window->point + dir, window, dir,
3499672dff93Sespie case_sensitive);
3500fbc94a17Sniklas
3501fbc94a17Sniklas if (ret != -1)
3502fbc94a17Sniklas {
3503fbc94a17Sniklas /* We won! */
3504fbc94a17Sniklas if (!echo_area_is_active && !isearch_is_active)
3505fbc94a17Sniklas window_clear_echo_area ();
3506fbc94a17Sniklas return (0);
3507fbc94a17Sniklas }
3508fbc94a17Sniklas
3509fbc94a17Sniklas /* The string wasn't found in the current node. Search through the
3510fbc94a17Sniklas window's file buffer, iff the current node is not "*". */
3511fbc94a17Sniklas if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
3512fbc94a17Sniklas return (-1);
3513fbc94a17Sniklas
3514fbc94a17Sniklas /* If this file has tags, search through every subfile, starting at
3515fbc94a17Sniklas this node's subfile and node. Otherwise, search through the
3516fbc94a17Sniklas file's node list. */
3517fbc94a17Sniklas if (file_buffer->tags)
3518fbc94a17Sniklas {
3519*1076333cSespie register int current_tag = 0, number_of_tags;
3520fbc94a17Sniklas char *last_subfile;
3521fbc94a17Sniklas TAG *tag;
3522fbc94a17Sniklas
3523fbc94a17Sniklas /* Find number of tags and current tag. */
3524fbc94a17Sniklas last_subfile = (char *)NULL;
3525fbc94a17Sniklas for (i = 0; file_buffer->tags[i]; i++)
3526fbc94a17Sniklas if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
3527fbc94a17Sniklas {
3528fbc94a17Sniklas current_tag = i;
3529fbc94a17Sniklas last_subfile = file_buffer->tags[i]->filename;
3530fbc94a17Sniklas }
3531fbc94a17Sniklas
3532fbc94a17Sniklas number_of_tags = i;
3533fbc94a17Sniklas
3534fbc94a17Sniklas /* If there is no last_subfile, our tag wasn't found. */
3535fbc94a17Sniklas if (!last_subfile)
3536fbc94a17Sniklas return (-1);
3537fbc94a17Sniklas
3538fbc94a17Sniklas /* Search through subsequent nodes, wrapping around to the top
3539fbc94a17Sniklas of the info file until we find the string or return to this
3540fbc94a17Sniklas window's node and point. */
3541fbc94a17Sniklas while (1)
3542fbc94a17Sniklas {
3543fbc94a17Sniklas NODE *node;
3544fbc94a17Sniklas
3545fbc94a17Sniklas /* Allow C-g to quit the search, failing it if pressed. */
3546fbc94a17Sniklas return_if_control_g (-1);
3547fbc94a17Sniklas
3548672dff93Sespie /* Find the next tag that isn't an anchor. */
3549672dff93Sespie for (i = current_tag + dir; i != current_tag; i += dir)
3550672dff93Sespie {
3551672dff93Sespie if (i < 0)
3552672dff93Sespie i = number_of_tags - 1;
3553672dff93Sespie else if (i == number_of_tags)
3554672dff93Sespie i = 0;
3555fbc94a17Sniklas
3556672dff93Sespie tag = file_buffer->tags[i];
3557672dff93Sespie if (tag->nodelen != 0)
3558672dff93Sespie break;
3559672dff93Sespie }
3560fbc94a17Sniklas
3561672dff93Sespie /* If we got past out starting point, bail out. */
3562672dff93Sespie if (i == current_tag)
3563672dff93Sespie return (-1);
3564672dff93Sespie current_tag = i;
3565fbc94a17Sniklas
3566fbc94a17Sniklas if (!echo_area_is_active && (last_subfile != tag->filename))
3567fbc94a17Sniklas {
3568fbc94a17Sniklas window_message_in_echo_area
3569*1076333cSespie ((char *) _("Searching subfile %s ..."),
3570*1076333cSespie filename_non_directory (tag->filename), NULL);
3571fbc94a17Sniklas
3572fbc94a17Sniklas last_subfile = tag->filename;
3573fbc94a17Sniklas }
3574fbc94a17Sniklas
3575fbc94a17Sniklas node = info_get_node (file_buffer->filename, tag->nodename);
3576fbc94a17Sniklas
3577fbc94a17Sniklas if (!node)
3578fbc94a17Sniklas {
3579fbc94a17Sniklas /* If not doing i-search... */
3580fbc94a17Sniklas if (!echo_area_is_active)
3581fbc94a17Sniklas {
3582fbc94a17Sniklas if (info_recent_file_error)
3583*1076333cSespie info_error (info_recent_file_error, NULL, NULL);
3584fbc94a17Sniklas else
3585*1076333cSespie info_error ((char *) msg_cant_file_node,
3586fbc94a17Sniklas filename_non_directory (file_buffer->filename),
3587fbc94a17Sniklas tag->nodename);
3588fbc94a17Sniklas }
3589fbc94a17Sniklas return (-1);
3590fbc94a17Sniklas }
3591fbc94a17Sniklas
3592fbc94a17Sniklas if (dir < 0)
3593fbc94a17Sniklas start = tag->nodelen;
3594fbc94a17Sniklas
3595fbc94a17Sniklas ret =
3596672dff93Sespie info_search_in_node (string, node, start, window, dir,
3597672dff93Sespie case_sensitive);
3598fbc94a17Sniklas
3599fbc94a17Sniklas /* Did we find the string in this node? */
3600fbc94a17Sniklas if (ret != -1)
3601fbc94a17Sniklas {
3602fbc94a17Sniklas /* Yes! We win. */
3603fbc94a17Sniklas remember_window_and_node (window, node);
3604fbc94a17Sniklas if (!echo_area_is_active)
3605fbc94a17Sniklas window_clear_echo_area ();
3606fbc94a17Sniklas return (0);
3607fbc94a17Sniklas }
3608fbc94a17Sniklas
3609fbc94a17Sniklas /* No. Free this node, and make sure that we haven't passed
3610fbc94a17Sniklas our starting point. */
3611fbc94a17Sniklas free (node);
3612fbc94a17Sniklas
3613fbc94a17Sniklas if (strcmp (initial_nodename, tag->nodename) == 0)
3614fbc94a17Sniklas return (-1);
3615fbc94a17Sniklas }
3616fbc94a17Sniklas }
3617fbc94a17Sniklas return (-1);
3618fbc94a17Sniklas }
3619fbc94a17Sniklas
3620672dff93Sespie DECLARE_INFO_COMMAND (info_search_case_sensitively,
3621672dff93Sespie _("Read a string and search for it case-sensitively"))
3622672dff93Sespie {
3623672dff93Sespie last_search_direction = count > 0 ? 1 : -1;
3624672dff93Sespie last_search_case_sensitive = 1;
3625672dff93Sespie info_search_1 (window, count, key, 1, 1);
3626672dff93Sespie }
3627672dff93Sespie
3628840175f0Skstailey DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
3629fbc94a17Sniklas {
3630672dff93Sespie last_search_direction = count > 0 ? 1 : -1;
3631672dff93Sespie last_search_case_sensitive = 0;
3632672dff93Sespie info_search_1 (window, count, key, 0, 1);
3633672dff93Sespie }
3634672dff93Sespie
3635672dff93Sespie DECLARE_INFO_COMMAND (info_search_backward,
3636672dff93Sespie _("Read a string and search backward for it"))
3637672dff93Sespie {
3638672dff93Sespie last_search_direction = count > 0 ? -1 : 1;
3639672dff93Sespie last_search_case_sensitive = 0;
3640672dff93Sespie info_search_1 (window, -count, key, 0, 1);
3641672dff93Sespie }
3642672dff93Sespie
3643672dff93Sespie static void
info_search_1(WINDOW * window,int count,unsigned char key,int case_sensitive,int ask_for_string)3644*1076333cSespie info_search_1 (WINDOW *window, int count, unsigned char key,
3645*1076333cSespie int case_sensitive, int ask_for_string)
3646672dff93Sespie {
3647fbc94a17Sniklas char *line, *prompt;
3648fbc94a17Sniklas int result, old_pagetop;
3649fbc94a17Sniklas int direction;
3650fbc94a17Sniklas
3651fbc94a17Sniklas if (count < 0)
3652672dff93Sespie {
3653fbc94a17Sniklas direction = -1;
3654672dff93Sespie count = -count;
3655672dff93Sespie }
3656fbc94a17Sniklas else
3657672dff93Sespie {
3658fbc94a17Sniklas direction = 1;
3659672dff93Sespie if (count == 0)
3660672dff93Sespie count = 1; /* for backward compatibility */
3661672dff93Sespie }
3662fbc94a17Sniklas
3663fbc94a17Sniklas /* Read a string from the user, defaulting the search to SEARCH_STRING. */
3664fbc94a17Sniklas if (!search_string)
3665fbc94a17Sniklas {
3666fbc94a17Sniklas search_string = (char *)xmalloc (search_string_size = 100);
3667fbc94a17Sniklas search_string[0] = '\0';
3668fbc94a17Sniklas }
3669fbc94a17Sniklas
3670672dff93Sespie if (ask_for_string)
3671672dff93Sespie {
3672*1076333cSespie prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: "))
3673*1076333cSespie + strlen (_("Search backward"))
3674*1076333cSespie + strlen (_("Search"))
3675*1076333cSespie + strlen (_(" case-sensitively "))
3676*1076333cSespie + strlen (_(" "))
3677*1076333cSespie + strlen (search_string));
3678fbc94a17Sniklas
3679672dff93Sespie sprintf (prompt, _("%s%sfor string [%s]: "),
3680840175f0Skstailey direction < 0 ? _("Search backward") : _("Search"),
3681672dff93Sespie case_sensitive ? _(" case-sensitively ") : _(" "),
3682fbc94a17Sniklas search_string);
3683fbc94a17Sniklas
3684fbc94a17Sniklas line = info_read_in_echo_area (window, prompt);
3685fbc94a17Sniklas free (prompt);
3686fbc94a17Sniklas
3687fbc94a17Sniklas if (!line)
3688fbc94a17Sniklas {
3689*1076333cSespie info_abort_key (window, 0, 0);
3690fbc94a17Sniklas return;
3691fbc94a17Sniklas }
3692fbc94a17Sniklas
3693fbc94a17Sniklas if (*line)
3694fbc94a17Sniklas {
3695*1076333cSespie if (strlen (line) + 1 > (unsigned int) search_string_size)
3696672dff93Sespie search_string = (char *) xrealloc
3697672dff93Sespie (search_string, (search_string_size += 50 + strlen (line)));
3698fbc94a17Sniklas
3699fbc94a17Sniklas strcpy (search_string, line);
3700fbc94a17Sniklas free (line);
3701fbc94a17Sniklas }
3702672dff93Sespie }
3703672dff93Sespie
3704672dff93Sespie /* If the search string includes upper-case letters, make the search
3705672dff93Sespie case-sensitive. */
3706672dff93Sespie if (case_sensitive == 0)
3707672dff93Sespie for (line = search_string; *line; line++)
3708672dff93Sespie if (isupper (*line))
3709672dff93Sespie {
3710672dff93Sespie case_sensitive = 1;
3711672dff93Sespie break;
3712672dff93Sespie }
3713fbc94a17Sniklas
3714fbc94a17Sniklas old_pagetop = active_window->pagetop;
3715672dff93Sespie for (result = 0; result == 0 && count--; )
3716672dff93Sespie result = info_search_internal (search_string,
3717672dff93Sespie active_window, direction, case_sensitive);
3718fbc94a17Sniklas
3719fbc94a17Sniklas if (result != 0 && !info_error_was_printed)
3720*1076333cSespie info_error ((char *) _("Search failed."), NULL, NULL);
3721fbc94a17Sniklas else if (old_pagetop != active_window->pagetop)
3722fbc94a17Sniklas {
3723fbc94a17Sniklas int new_pagetop;
3724fbc94a17Sniklas
3725fbc94a17Sniklas new_pagetop = active_window->pagetop;
3726fbc94a17Sniklas active_window->pagetop = old_pagetop;
3727fbc94a17Sniklas set_window_pagetop (active_window, new_pagetop);
3728fbc94a17Sniklas if (auto_footnotes_p)
3729fbc94a17Sniklas info_get_or_remove_footnotes (active_window);
3730fbc94a17Sniklas }
3731fbc94a17Sniklas
3732fbc94a17Sniklas /* Perhaps free the unreferenced file buffers that were searched, but
3733fbc94a17Sniklas not retained. */
3734fbc94a17Sniklas info_gc_file_buffers ();
3735fbc94a17Sniklas }
3736fbc94a17Sniklas
3737672dff93Sespie DECLARE_INFO_COMMAND (info_search_next,
3738672dff93Sespie _("Repeat last search in the same direction"))
3739672dff93Sespie {
3740672dff93Sespie if (!last_search_direction)
3741*1076333cSespie info_error ((char *) _("No previous search string"), NULL, NULL);
3742672dff93Sespie else
3743672dff93Sespie info_search_1 (window, last_search_direction * count,
3744672dff93Sespie key, last_search_case_sensitive, 0);
3745672dff93Sespie }
3746672dff93Sespie
3747672dff93Sespie DECLARE_INFO_COMMAND (info_search_previous,
3748672dff93Sespie _("Repeat last search in the reverse direction"))
3749672dff93Sespie {
3750672dff93Sespie if (!last_search_direction)
3751*1076333cSespie info_error ((char *) _("No previous search string"), NULL, NULL);
3752672dff93Sespie else
3753672dff93Sespie info_search_1 (window, -last_search_direction * count,
3754672dff93Sespie key, last_search_case_sensitive, 0);
3755672dff93Sespie }
3756672dff93Sespie
3757fbc94a17Sniklas /* **************************************************************** */
3758fbc94a17Sniklas /* */
3759fbc94a17Sniklas /* Incremental Searching */
3760fbc94a17Sniklas /* */
3761fbc94a17Sniklas /* **************************************************************** */
3762fbc94a17Sniklas
3763*1076333cSespie static void incremental_search (WINDOW *window, int count,
3764*1076333cSespie unsigned char ignore);
3765fbc94a17Sniklas
3766fbc94a17Sniklas DECLARE_INFO_COMMAND (isearch_forward,
3767840175f0Skstailey _("Search interactively for a string as you type it"))
3768fbc94a17Sniklas {
3769fbc94a17Sniklas incremental_search (window, count, key);
3770fbc94a17Sniklas }
3771fbc94a17Sniklas
3772fbc94a17Sniklas DECLARE_INFO_COMMAND (isearch_backward,
3773840175f0Skstailey _("Search interactively for a string as you type it"))
3774fbc94a17Sniklas {
3775fbc94a17Sniklas incremental_search (window, -count, key);
3776fbc94a17Sniklas }
3777fbc94a17Sniklas
3778fbc94a17Sniklas /* Incrementally search for a string as it is typed. */
3779fbc94a17Sniklas /* The last accepted incremental search string. */
3780fbc94a17Sniklas static char *last_isearch_accepted = (char *)NULL;
3781fbc94a17Sniklas
3782fbc94a17Sniklas /* The current incremental search string. */
3783fbc94a17Sniklas static char *isearch_string = (char *)NULL;
3784fbc94a17Sniklas static int isearch_string_index = 0;
3785fbc94a17Sniklas static int isearch_string_size = 0;
3786fbc94a17Sniklas static unsigned char isearch_terminate_search_key = ESC;
3787fbc94a17Sniklas
3788fbc94a17Sniklas /* Array of search states. */
3789fbc94a17Sniklas static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
3790fbc94a17Sniklas static int isearch_states_index = 0;
3791fbc94a17Sniklas static int isearch_states_slots = 0;
3792fbc94a17Sniklas
3793fbc94a17Sniklas /* Push the state of this search. */
3794fbc94a17Sniklas static void
push_isearch(WINDOW * window,int search_index,int direction,int failing)3795*1076333cSespie push_isearch (WINDOW *window, int search_index, int direction, int failing)
3796fbc94a17Sniklas {
3797fbc94a17Sniklas SEARCH_STATE *state;
3798fbc94a17Sniklas
3799fbc94a17Sniklas state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
3800fbc94a17Sniklas window_get_state (window, state);
3801fbc94a17Sniklas state->search_index = search_index;
3802fbc94a17Sniklas state->direction = direction;
3803fbc94a17Sniklas state->failing = failing;
3804fbc94a17Sniklas
3805fbc94a17Sniklas add_pointer_to_array (state, isearch_states_index, isearch_states,
3806fbc94a17Sniklas isearch_states_slots, 20, SEARCH_STATE *);
3807fbc94a17Sniklas }
3808fbc94a17Sniklas
3809fbc94a17Sniklas /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3810fbc94a17Sniklas static void
pop_isearch(WINDOW * window,int * search_index,int * direction,int * failing)3811*1076333cSespie pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing)
3812fbc94a17Sniklas {
3813fbc94a17Sniklas SEARCH_STATE *state;
3814fbc94a17Sniklas
3815fbc94a17Sniklas if (isearch_states_index)
3816fbc94a17Sniklas {
3817fbc94a17Sniklas isearch_states_index--;
3818fbc94a17Sniklas state = isearch_states[isearch_states_index];
3819fbc94a17Sniklas window_set_state (window, state);
3820fbc94a17Sniklas *search_index = state->search_index;
3821fbc94a17Sniklas *direction = state->direction;
3822fbc94a17Sniklas *failing = state->failing;
3823fbc94a17Sniklas
3824fbc94a17Sniklas free (state);
3825fbc94a17Sniklas isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
3826fbc94a17Sniklas }
3827fbc94a17Sniklas }
3828fbc94a17Sniklas
3829fbc94a17Sniklas /* Free the memory used by isearch_states. */
3830fbc94a17Sniklas static void
free_isearch_states(void)3831*1076333cSespie free_isearch_states (void)
3832fbc94a17Sniklas {
3833fbc94a17Sniklas register int i;
3834fbc94a17Sniklas
3835fbc94a17Sniklas for (i = 0; i < isearch_states_index; i++)
3836fbc94a17Sniklas {
3837fbc94a17Sniklas free (isearch_states[i]);
3838fbc94a17Sniklas isearch_states[i] = (SEARCH_STATE *)NULL;
3839fbc94a17Sniklas }
3840fbc94a17Sniklas isearch_states_index = 0;
3841fbc94a17Sniklas }
3842fbc94a17Sniklas
3843fbc94a17Sniklas /* Display the current search in the echo area. */
3844fbc94a17Sniklas static void
show_isearch_prompt(int dir,unsigned char * string,int failing_p)3845*1076333cSespie show_isearch_prompt (int dir, unsigned char *string, int failing_p)
3846fbc94a17Sniklas {
3847fbc94a17Sniklas register int i;
3848*1076333cSespie const char *prefix;
3849*1076333cSespie char *prompt, *p_rep;
3850*1076333cSespie unsigned int prompt_len, p_rep_index, p_rep_size;
3851fbc94a17Sniklas
3852fbc94a17Sniklas if (dir < 0)
3853840175f0Skstailey prefix = _("I-search backward: ");
3854fbc94a17Sniklas else
3855840175f0Skstailey prefix = _("I-search: ");
3856fbc94a17Sniklas
3857fbc94a17Sniklas p_rep_index = p_rep_size = 0;
3858fbc94a17Sniklas p_rep = (char *)NULL;
3859fbc94a17Sniklas for (i = 0; string[i]; i++)
3860fbc94a17Sniklas {
3861fbc94a17Sniklas char *rep;
3862fbc94a17Sniklas
3863fbc94a17Sniklas switch (string[i])
3864fbc94a17Sniklas {
3865fbc94a17Sniklas case ' ': rep = " "; break;
3866fbc94a17Sniklas case LFD: rep = "\\n"; break;
3867fbc94a17Sniklas case TAB: rep = "\\t"; break;
3868fbc94a17Sniklas default:
3869fbc94a17Sniklas rep = pretty_keyname (string[i]);
3870fbc94a17Sniklas }
3871fbc94a17Sniklas if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3872fbc94a17Sniklas p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3873fbc94a17Sniklas
3874fbc94a17Sniklas strcpy (p_rep + p_rep_index, rep);
3875fbc94a17Sniklas p_rep_index += strlen (rep);
3876fbc94a17Sniklas }
3877fbc94a17Sniklas
3878*1076333cSespie prompt_len = strlen (prefix) + p_rep_index + 1;
3879*1076333cSespie if (failing_p)
3880*1076333cSespie prompt_len += strlen (_("Failing "));
3881*1076333cSespie prompt = xmalloc (prompt_len);
3882840175f0Skstailey sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
3883fbc94a17Sniklas p_rep ? p_rep : "");
3884fbc94a17Sniklas
3885*1076333cSespie window_message_in_echo_area ("%s", prompt, NULL);
3886fbc94a17Sniklas maybe_free (p_rep);
3887fbc94a17Sniklas free (prompt);
3888fbc94a17Sniklas display_cursor_at_point (active_window);
3889fbc94a17Sniklas }
3890fbc94a17Sniklas
3891fbc94a17Sniklas static void
incremental_search(WINDOW * window,int count,unsigned char ignore)3892*1076333cSespie incremental_search (WINDOW *window, int count, unsigned char ignore)
3893fbc94a17Sniklas {
3894fbc94a17Sniklas unsigned char key;
3895fbc94a17Sniklas int last_search_result, search_result, dir;
3896fbc94a17Sniklas SEARCH_STATE mystate, orig_state;
3897672dff93Sespie char *p;
3898672dff93Sespie int case_sensitive = 0;
3899fbc94a17Sniklas
3900fbc94a17Sniklas if (count < 0)
3901fbc94a17Sniklas dir = -1;
3902fbc94a17Sniklas else
3903fbc94a17Sniklas dir = 1;
3904fbc94a17Sniklas
3905fbc94a17Sniklas last_search_result = search_result = 0;
3906fbc94a17Sniklas
3907fbc94a17Sniklas window_get_state (window, &orig_state);
3908fbc94a17Sniklas
3909fbc94a17Sniklas isearch_string_index = 0;
3910fbc94a17Sniklas if (!isearch_string_size)
3911fbc94a17Sniklas isearch_string = (char *)xmalloc (isearch_string_size = 50);
3912fbc94a17Sniklas
3913fbc94a17Sniklas /* Show the search string in the echo area. */
3914fbc94a17Sniklas isearch_string[isearch_string_index] = '\0';
3915*1076333cSespie show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
3916fbc94a17Sniklas
3917fbc94a17Sniklas isearch_is_active = 1;
3918fbc94a17Sniklas
3919fbc94a17Sniklas while (isearch_is_active)
3920fbc94a17Sniklas {
3921fbc94a17Sniklas VFunction *func = (VFunction *)NULL;
3922fbc94a17Sniklas int quoted = 0;
3923fbc94a17Sniklas
3924fbc94a17Sniklas /* If a recent display was interrupted, then do the redisplay now if
3925fbc94a17Sniklas it is convenient. */
3926fbc94a17Sniklas if (!info_any_buffered_input_p () && display_was_interrupted_p)
3927fbc94a17Sniklas {
3928fbc94a17Sniklas display_update_one_window (window);
3929fbc94a17Sniklas display_cursor_at_point (active_window);
3930fbc94a17Sniklas }
3931fbc94a17Sniklas
3932fbc94a17Sniklas /* Read a character and dispatch on it. */
3933fbc94a17Sniklas key = info_get_input_char ();
3934fbc94a17Sniklas window_get_state (window, &mystate);
3935fbc94a17Sniklas
3936*1076333cSespie if (key == DEL || key == Control ('h'))
3937fbc94a17Sniklas {
3938fbc94a17Sniklas /* User wants to delete one level of search? */
3939fbc94a17Sniklas if (!isearch_states_index)
3940fbc94a17Sniklas {
3941fbc94a17Sniklas terminal_ring_bell ();
3942fbc94a17Sniklas continue;
3943fbc94a17Sniklas }
3944fbc94a17Sniklas else
3945fbc94a17Sniklas {
3946fbc94a17Sniklas pop_isearch
3947fbc94a17Sniklas (window, &isearch_string_index, &dir, &search_result);
3948fbc94a17Sniklas isearch_string[isearch_string_index] = '\0';
3949*1076333cSespie show_isearch_prompt (dir, (unsigned char *) isearch_string,
3950*1076333cSespie search_result);
3951fbc94a17Sniklas goto after_search;
3952fbc94a17Sniklas }
3953fbc94a17Sniklas }
3954fbc94a17Sniklas else if (key == Control ('q'))
3955fbc94a17Sniklas {
3956fbc94a17Sniklas key = info_get_input_char ();
3957fbc94a17Sniklas quoted = 1;
3958fbc94a17Sniklas }
3959fbc94a17Sniklas
3960fbc94a17Sniklas /* We are about to search again, or quit. Save the current search. */
3961fbc94a17Sniklas push_isearch (window, isearch_string_index, dir, search_result);
3962fbc94a17Sniklas
3963fbc94a17Sniklas if (quoted)
3964fbc94a17Sniklas goto insert_and_search;
3965fbc94a17Sniklas
3966672dff93Sespie if (!Meta_p (key) || key > 32)
3967fbc94a17Sniklas {
3968*1076333cSespie /* If this key is not a keymap, get its associated function,
3969*1076333cSespie if any. If it is a keymap, then it's probably ESC from an
3970*1076333cSespie arrow key, and we handle that case below. */
3971*1076333cSespie char type = window->keymap[key].type;
3972*1076333cSespie func = type == ISFUNC
3973*1076333cSespie ? InfoFunction(window->keymap[key].function)
3974*1076333cSespie : NULL; /* function member is a Keymap if ISKMAP */
3975fbc94a17Sniklas
3976*1076333cSespie if (isprint (key) || (type == ISFUNC && func == NULL))
3977fbc94a17Sniklas {
39783aa90977Sespie insert_and_search:
39793aa90977Sespie
39803aa90977Sespie if (isearch_string_index + 2 >= isearch_string_size)
39813aa90977Sespie isearch_string = (char *)xrealloc
39823aa90977Sespie (isearch_string, isearch_string_size += 100);
39833aa90977Sespie
39843aa90977Sespie isearch_string[isearch_string_index++] = key;
39853aa90977Sespie isearch_string[isearch_string_index] = '\0';
39863aa90977Sespie goto search_now;
39873aa90977Sespie }
3988*1076333cSespie else if (func == (VFunction *) isearch_forward
3989*1076333cSespie || func == (VFunction *) isearch_backward)
39903aa90977Sespie {
39913aa90977Sespie /* If this key invokes an incremental search, then this
39923aa90977Sespie means that we will either search again in the same
39933aa90977Sespie direction, search again in the reverse direction, or
39943aa90977Sespie insert the last search string that was accepted through
39953aa90977Sespie incremental searching. */
3996*1076333cSespie if ((func == (VFunction *) isearch_forward && dir > 0) ||
3997*1076333cSespie (func == (VFunction *) isearch_backward && dir < 0))
3998fbc94a17Sniklas {
3999fbc94a17Sniklas /* If the user has typed no characters, then insert the
4000fbc94a17Sniklas last successful search into the current search string. */
4001fbc94a17Sniklas if (isearch_string_index == 0)
4002fbc94a17Sniklas {
4003fbc94a17Sniklas /* Of course, there must be something to insert. */
4004fbc94a17Sniklas if (last_isearch_accepted)
4005fbc94a17Sniklas {
4006*1076333cSespie if (strlen ((char *) last_isearch_accepted) + 1
4007*1076333cSespie >= (unsigned int) isearch_string_size)
4008fbc94a17Sniklas isearch_string = (char *)
4009fbc94a17Sniklas xrealloc (isearch_string,
4010fbc94a17Sniklas isearch_string_size += 10 +
4011fbc94a17Sniklas strlen (last_isearch_accepted));
4012fbc94a17Sniklas strcpy (isearch_string, last_isearch_accepted);
4013fbc94a17Sniklas isearch_string_index = strlen (isearch_string);
4014fbc94a17Sniklas goto search_now;
4015fbc94a17Sniklas }
4016fbc94a17Sniklas else
4017fbc94a17Sniklas continue;
4018fbc94a17Sniklas }
4019fbc94a17Sniklas else
4020fbc94a17Sniklas {
4021fbc94a17Sniklas /* Search again in the same direction. This means start
4022fbc94a17Sniklas from a new place if the last search was successful. */
4023fbc94a17Sniklas if (search_result == 0)
4024fbc94a17Sniklas window->point += dir;
4025fbc94a17Sniklas }
4026fbc94a17Sniklas }
4027fbc94a17Sniklas else
4028fbc94a17Sniklas {
4029fbc94a17Sniklas /* Reverse the direction of the search. */
4030fbc94a17Sniklas dir = -dir;
4031fbc94a17Sniklas }
4032fbc94a17Sniklas }
4033*1076333cSespie else if (func == (VFunction *) info_abort_key)
4034fbc94a17Sniklas {
4035fbc94a17Sniklas /* If C-g pressed, and the search is failing, pop the search
4036fbc94a17Sniklas stack back to the last unfailed search. */
4037fbc94a17Sniklas if (isearch_states_index && (search_result != 0))
4038fbc94a17Sniklas {
4039fbc94a17Sniklas terminal_ring_bell ();
4040fbc94a17Sniklas while (isearch_states_index && (search_result != 0))
4041fbc94a17Sniklas pop_isearch
4042fbc94a17Sniklas (window, &isearch_string_index, &dir, &search_result);
4043fbc94a17Sniklas isearch_string[isearch_string_index] = '\0';
4044*1076333cSespie show_isearch_prompt (dir, (unsigned char *) isearch_string,
4045*1076333cSespie search_result);
4046fbc94a17Sniklas continue;
4047fbc94a17Sniklas }
4048fbc94a17Sniklas else
4049fbc94a17Sniklas goto exit_search;
4050fbc94a17Sniklas }
4051fbc94a17Sniklas else
4052fbc94a17Sniklas goto exit_search;
4053fbc94a17Sniklas }
4054fbc94a17Sniklas else
4055fbc94a17Sniklas {
4056fbc94a17Sniklas exit_search:
4057fbc94a17Sniklas /* The character is not printable, or it has a function which is
4058fbc94a17Sniklas non-null. Exit the search, remembering the search string. If
4059fbc94a17Sniklas the key is not the same as the isearch_terminate_search_key,
4060fbc94a17Sniklas then push it into pending input. */
4061*1076333cSespie if (isearch_string_index && func != (VFunction *) info_abort_key)
4062fbc94a17Sniklas {
4063fbc94a17Sniklas maybe_free (last_isearch_accepted);
4064840175f0Skstailey last_isearch_accepted = xstrdup (isearch_string);
4065fbc94a17Sniklas }
4066fbc94a17Sniklas
4067672dff93Sespie /* If the key is the isearch_terminate_search_key, but some buffered
4068672dff93Sespie input is pending, it is almost invariably because the ESC key is
4069672dff93Sespie actually the beginning of an escape sequence, like in case they
4070672dff93Sespie pressed an arrow key. So don't gobble the ESC key, push it back
4071672dff93Sespie into pending input. */
4072672dff93Sespie /* FIXME: this seems like a kludge! We need a more reliable
4073672dff93Sespie mechanism to know when ESC is a separate key and when it is
4074672dff93Sespie part of an escape sequence. */
40753aa90977Sespie if (key != RET /* Emacs addicts want RET to get lost */
40763aa90977Sespie && (key != isearch_terminate_search_key
40773aa90977Sespie || info_any_buffered_input_p ()))
4078fbc94a17Sniklas info_set_pending_input (key);
4079fbc94a17Sniklas
4080*1076333cSespie if (func == (VFunction *) info_abort_key)
4081fbc94a17Sniklas {
4082fbc94a17Sniklas if (isearch_states_index)
4083fbc94a17Sniklas window_set_state (window, &orig_state);
4084fbc94a17Sniklas }
4085fbc94a17Sniklas
4086fbc94a17Sniklas if (!echo_area_is_active)
4087fbc94a17Sniklas window_clear_echo_area ();
4088fbc94a17Sniklas
4089fbc94a17Sniklas if (auto_footnotes_p)
4090fbc94a17Sniklas info_get_or_remove_footnotes (active_window);
4091fbc94a17Sniklas
4092fbc94a17Sniklas isearch_is_active = 0;
4093fbc94a17Sniklas continue;
4094fbc94a17Sniklas }
4095fbc94a17Sniklas
4096fbc94a17Sniklas /* Search for the contents of isearch_string. */
4097fbc94a17Sniklas search_now:
4098*1076333cSespie show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4099fbc94a17Sniklas
4100672dff93Sespie /* If the search string includes upper-case letters, make the
4101672dff93Sespie search case-sensitive. */
4102672dff93Sespie for (p = isearch_string; *p; p++)
4103672dff93Sespie if (isupper (*p))
4104672dff93Sespie {
4105672dff93Sespie case_sensitive = 1;
4106672dff93Sespie break;
4107672dff93Sespie }
4108672dff93Sespie
4109672dff93Sespie
4110fbc94a17Sniklas if (search_result == 0)
4111fbc94a17Sniklas {
4112fbc94a17Sniklas /* Check to see if the current search string is right here. If
4113fbc94a17Sniklas we are looking at it, then don't bother calling the search
4114fbc94a17Sniklas function. */
4115fbc94a17Sniklas if (((dir < 0) &&
4116672dff93Sespie ((case_sensitive ? strncmp : strncasecmp)
4117672dff93Sespie (window->node->contents + window->point,
4118fbc94a17Sniklas isearch_string, isearch_string_index) == 0)) ||
4119fbc94a17Sniklas ((dir > 0) &&
4120fbc94a17Sniklas ((window->point - isearch_string_index) >= 0) &&
4121672dff93Sespie ((case_sensitive ? strncmp : strncasecmp)
4122672dff93Sespie (window->node->contents +
4123fbc94a17Sniklas (window->point - (isearch_string_index - 1)),
4124fbc94a17Sniklas isearch_string, isearch_string_index) == 0)))
4125fbc94a17Sniklas {
4126fbc94a17Sniklas if (dir > 0)
4127fbc94a17Sniklas window->point++;
4128fbc94a17Sniklas }
4129fbc94a17Sniklas else
4130672dff93Sespie search_result = info_search_internal (isearch_string,
4131672dff93Sespie window, dir, case_sensitive);
4132fbc94a17Sniklas }
4133fbc94a17Sniklas
4134fbc94a17Sniklas /* If this search failed, and we didn't already have a failed search,
4135fbc94a17Sniklas then ring the terminal bell. */
4136fbc94a17Sniklas if (search_result != 0 && last_search_result == 0)
4137fbc94a17Sniklas terminal_ring_bell ();
4138fbc94a17Sniklas
4139fbc94a17Sniklas after_search:
4140*1076333cSespie show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4141fbc94a17Sniklas
4142fbc94a17Sniklas if (search_result == 0)
4143fbc94a17Sniklas {
4144fbc94a17Sniklas if ((mystate.node == window->node) &&
4145fbc94a17Sniklas (mystate.pagetop != window->pagetop))
4146fbc94a17Sniklas {
4147fbc94a17Sniklas int newtop = window->pagetop;
4148fbc94a17Sniklas window->pagetop = mystate.pagetop;
4149fbc94a17Sniklas set_window_pagetop (window, newtop);
4150fbc94a17Sniklas }
4151fbc94a17Sniklas display_update_one_window (window);
4152fbc94a17Sniklas display_cursor_at_point (window);
4153fbc94a17Sniklas }
4154fbc94a17Sniklas
4155fbc94a17Sniklas last_search_result = search_result;
4156fbc94a17Sniklas }
4157fbc94a17Sniklas
4158fbc94a17Sniklas /* Free the memory used to remember each search state. */
4159fbc94a17Sniklas free_isearch_states ();
4160fbc94a17Sniklas
4161fbc94a17Sniklas /* Perhaps GC some file buffers. */
4162fbc94a17Sniklas info_gc_file_buffers ();
4163fbc94a17Sniklas
4164fbc94a17Sniklas /* After searching, leave the window in the correct state. */
4165fbc94a17Sniklas if (!echo_area_is_active)
4166fbc94a17Sniklas window_clear_echo_area ();
4167fbc94a17Sniklas }
4168fbc94a17Sniklas
4169fbc94a17Sniklas /* GC some file buffers. A file buffer can be gc-ed if there we have
4170fbc94a17Sniklas no nodes in INFO_WINDOWS that reference this file buffer's contents.
4171fbc94a17Sniklas Garbage collecting a file buffer means to free the file buffers
4172fbc94a17Sniklas contents. */
4173fbc94a17Sniklas static void
info_gc_file_buffers(void)4174*1076333cSespie info_gc_file_buffers (void)
4175fbc94a17Sniklas {
4176fbc94a17Sniklas register int fb_index, iw_index, i;
4177fbc94a17Sniklas register FILE_BUFFER *fb;
4178fbc94a17Sniklas register INFO_WINDOW *iw;
4179fbc94a17Sniklas
4180fbc94a17Sniklas if (!info_loaded_files)
4181fbc94a17Sniklas return;
4182fbc94a17Sniklas
4183840175f0Skstailey for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4184fbc94a17Sniklas {
4185fbc94a17Sniklas int fb_referenced_p = 0;
4186fbc94a17Sniklas
4187fbc94a17Sniklas /* If already gc-ed, do nothing. */
4188fbc94a17Sniklas if (!fb->contents)
4189fbc94a17Sniklas continue;
4190fbc94a17Sniklas
4191fbc94a17Sniklas /* If this file had to be uncompressed, check to see if we should
4192fbc94a17Sniklas gc it. This means that the user-variable "gc-compressed-files"
4193fbc94a17Sniklas is non-zero. */
4194fbc94a17Sniklas if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
4195fbc94a17Sniklas continue;
4196fbc94a17Sniklas
4197fbc94a17Sniklas /* If this file's contents are not gc-able, move on. */
4198fbc94a17Sniklas if (fb->flags & N_CannotGC)
4199fbc94a17Sniklas continue;
4200fbc94a17Sniklas
4201fbc94a17Sniklas /* Check each INFO_WINDOW to see if it has any nodes which reference
4202fbc94a17Sniklas this file. */
4203840175f0Skstailey for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
4204fbc94a17Sniklas {
4205fbc94a17Sniklas for (i = 0; iw->nodes && iw->nodes[i]; i++)
4206fbc94a17Sniklas {
4207672dff93Sespie if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4208672dff93Sespie (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
4209fbc94a17Sniklas {
4210fbc94a17Sniklas fb_referenced_p = 1;
4211fbc94a17Sniklas break;
4212fbc94a17Sniklas }
4213fbc94a17Sniklas }
4214fbc94a17Sniklas }
4215fbc94a17Sniklas
4216fbc94a17Sniklas /* If this file buffer wasn't referenced, free its contents. */
4217fbc94a17Sniklas if (!fb_referenced_p)
4218fbc94a17Sniklas {
4219fbc94a17Sniklas free (fb->contents);
4220fbc94a17Sniklas fb->contents = (char *)NULL;
4221fbc94a17Sniklas }
4222fbc94a17Sniklas }
4223fbc94a17Sniklas }
4224fbc94a17Sniklas
4225fbc94a17Sniklas /* **************************************************************** */
4226fbc94a17Sniklas /* */
4227fbc94a17Sniklas /* Traversing and Selecting References */
4228fbc94a17Sniklas /* */
4229fbc94a17Sniklas /* **************************************************************** */
4230fbc94a17Sniklas
4231fbc94a17Sniklas /* Move to the next or previous cross reference in this node. */
4232fbc94a17Sniklas static void
info_move_to_xref(WINDOW * window,int count,unsigned char key,int dir)4233*1076333cSespie info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir)
4234fbc94a17Sniklas {
4235fbc94a17Sniklas long firstmenu, firstxref;
4236fbc94a17Sniklas long nextmenu, nextxref;
4237fbc94a17Sniklas long placement = -1;
4238fbc94a17Sniklas long start = 0;
4239fbc94a17Sniklas NODE *node = window->node;
4240fbc94a17Sniklas
4241fbc94a17Sniklas if (dir < 0)
4242fbc94a17Sniklas start = node->nodelen;
4243fbc94a17Sniklas
4244fbc94a17Sniklas /* This search is only allowed to fail if there is no menu or cross
4245fbc94a17Sniklas reference in the current node. Otherwise, the first menu or xref
4246fbc94a17Sniklas found is moved to. */
4247fbc94a17Sniklas
4248fbc94a17Sniklas firstmenu = info_search_in_node
4249672dff93Sespie (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4250fbc94a17Sniklas
4251fbc94a17Sniklas /* FIRSTMENU may point directly to the line defining the menu. Skip that
4252fbc94a17Sniklas and go directly to the first item. */
4253fbc94a17Sniklas
4254fbc94a17Sniklas if (firstmenu != -1)
4255fbc94a17Sniklas {
4256fbc94a17Sniklas char *text = node->contents + firstmenu;
4257fbc94a17Sniklas
4258fbc94a17Sniklas if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4259fbc94a17Sniklas firstmenu = info_search_in_node
4260672dff93Sespie (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0);
4261fbc94a17Sniklas }
4262fbc94a17Sniklas
4263fbc94a17Sniklas firstxref =
4264672dff93Sespie info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4265fbc94a17Sniklas
4266fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
4267fbc94a17Sniklas if ((firstxref == -1) && (node->flags & N_IsManPage))
4268fbc94a17Sniklas {
4269fbc94a17Sniklas firstxref = locate_manpage_xref (node, start, dir);
4270fbc94a17Sniklas }
4271fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
4272fbc94a17Sniklas
4273fbc94a17Sniklas if (firstmenu == -1 && firstxref == -1)
4274fbc94a17Sniklas {
4275*1076333cSespie info_error ((char *) msg_no_xref_node, NULL, NULL);
4276fbc94a17Sniklas return;
4277fbc94a17Sniklas }
4278fbc94a17Sniklas
4279fbc94a17Sniklas /* There is at least one cross reference or menu entry in this node.
4280fbc94a17Sniklas Try hard to find the next available one. */
4281fbc94a17Sniklas
4282fbc94a17Sniklas nextmenu = info_search_in_node
4283672dff93Sespie (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4284fbc94a17Sniklas
4285fbc94a17Sniklas nextxref = info_search_in_node
4286672dff93Sespie (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4287fbc94a17Sniklas
4288fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
4289fbc94a17Sniklas if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
4290fbc94a17Sniklas nextxref = locate_manpage_xref (node, window->point + dir, dir);
4291fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
4292fbc94a17Sniklas
4293fbc94a17Sniklas /* Ignore "Menu:" as a menu item. */
4294fbc94a17Sniklas if (nextmenu != -1)
4295fbc94a17Sniklas {
4296fbc94a17Sniklas char *text = node->contents + nextmenu;
4297fbc94a17Sniklas
4298fbc94a17Sniklas if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4299fbc94a17Sniklas nextmenu = info_search_in_node
4300672dff93Sespie (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
4301fbc94a17Sniklas }
4302fbc94a17Sniklas
4303fbc94a17Sniklas /* If there is both a next menu entry, and a next xref entry, choose the
4304fbc94a17Sniklas one which occurs first. Otherwise, select the one which actually
4305fbc94a17Sniklas appears in this node following point. */
4306fbc94a17Sniklas if (nextmenu != -1 && nextxref != -1)
4307fbc94a17Sniklas {
4308fbc94a17Sniklas if (((dir == 1) && (nextmenu < nextxref)) ||
4309fbc94a17Sniklas ((dir == -1) && (nextmenu > nextxref)))
4310fbc94a17Sniklas placement = nextmenu + 1;
4311fbc94a17Sniklas else
4312fbc94a17Sniklas placement = nextxref;
4313fbc94a17Sniklas }
4314fbc94a17Sniklas else if (nextmenu != -1)
4315fbc94a17Sniklas placement = nextmenu + 1;
4316fbc94a17Sniklas else if (nextxref != -1)
4317fbc94a17Sniklas placement = nextxref;
4318fbc94a17Sniklas
4319fbc94a17Sniklas /* If there was neither a menu or xref entry appearing in this node after
4320fbc94a17Sniklas point, choose the first menu or xref entry appearing in this node. */
4321fbc94a17Sniklas if (placement == -1)
4322fbc94a17Sniklas {
4323fbc94a17Sniklas if (firstmenu != -1 && firstxref != -1)
4324fbc94a17Sniklas {
4325fbc94a17Sniklas if (((dir == 1) && (firstmenu < firstxref)) ||
4326fbc94a17Sniklas ((dir == -1) && (firstmenu > firstxref)))
4327fbc94a17Sniklas placement = firstmenu + 1;
4328fbc94a17Sniklas else
4329fbc94a17Sniklas placement = firstxref;
4330fbc94a17Sniklas }
4331fbc94a17Sniklas else if (firstmenu != -1)
4332fbc94a17Sniklas placement = firstmenu + 1;
4333fbc94a17Sniklas else
4334fbc94a17Sniklas placement = firstxref;
4335fbc94a17Sniklas }
4336fbc94a17Sniklas window->point = placement;
4337fbc94a17Sniklas window_adjust_pagetop (window);
4338fbc94a17Sniklas window->flags |= W_UpdateWindow;
4339fbc94a17Sniklas }
4340fbc94a17Sniklas
4341fbc94a17Sniklas DECLARE_INFO_COMMAND (info_move_to_prev_xref,
4342840175f0Skstailey _("Move to the previous cross reference"))
4343fbc94a17Sniklas {
4344fbc94a17Sniklas if (count < 0)
4345fbc94a17Sniklas info_move_to_prev_xref (window, -count, key);
4346fbc94a17Sniklas else
4347fbc94a17Sniklas info_move_to_xref (window, count, key, -1);
4348fbc94a17Sniklas }
4349fbc94a17Sniklas
4350fbc94a17Sniklas DECLARE_INFO_COMMAND (info_move_to_next_xref,
4351840175f0Skstailey _("Move to the next cross reference"))
4352fbc94a17Sniklas {
4353fbc94a17Sniklas if (count < 0)
4354fbc94a17Sniklas info_move_to_next_xref (window, -count, key);
4355fbc94a17Sniklas else
4356fbc94a17Sniklas info_move_to_xref (window, count, key, 1);
4357fbc94a17Sniklas }
4358fbc94a17Sniklas
4359fbc94a17Sniklas /* Select the menu item or reference that appears on this line. */
4360fbc94a17Sniklas DECLARE_INFO_COMMAND (info_select_reference_this_line,
4361840175f0Skstailey _("Select reference or menu item appearing on this line"))
4362fbc94a17Sniklas {
4363fbc94a17Sniklas char *line;
4364fbc94a17Sniklas
4365*1076333cSespie if (window->line_starts)
4366fbc94a17Sniklas line = window->line_starts[window_line_of_point (window)];
4367*1076333cSespie else
4368*1076333cSespie line = "";
4369fbc94a17Sniklas
4370fbc94a17Sniklas /* If this line contains a menu item, select that one. */
4371fbc94a17Sniklas if (strncmp ("* ", line, 2) == 0)
4372fbc94a17Sniklas info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
4373fbc94a17Sniklas else
4374fbc94a17Sniklas info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
4375fbc94a17Sniklas }
4376fbc94a17Sniklas
4377fbc94a17Sniklas /* **************************************************************** */
4378fbc94a17Sniklas /* */
4379fbc94a17Sniklas /* Miscellaneous Info Commands */
4380fbc94a17Sniklas /* */
4381fbc94a17Sniklas /* **************************************************************** */
4382fbc94a17Sniklas
4383fbc94a17Sniklas /* What to do when C-g is pressed in a window. */
4384840175f0Skstailey DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
4385fbc94a17Sniklas {
4386fbc94a17Sniklas /* If error printing doesn't oridinarily ring the bell, do it now,
4387fbc94a17Sniklas since C-g always rings the bell. Otherwise, let the error printer
4388fbc94a17Sniklas do it. */
4389fbc94a17Sniklas if (!info_error_rings_bell_p)
4390fbc94a17Sniklas terminal_ring_bell ();
4391*1076333cSespie info_error ((char *) _("Quit"), NULL, NULL);
4392fbc94a17Sniklas
4393fbc94a17Sniklas info_initialize_numeric_arg ();
4394fbc94a17Sniklas info_clear_pending_input ();
4395fbc94a17Sniklas info_last_executed_command = (VFunction *)NULL;
4396fbc94a17Sniklas }
4397fbc94a17Sniklas
4398fbc94a17Sniklas /* Move the cursor to the desired line of the window. */
4399fbc94a17Sniklas DECLARE_INFO_COMMAND (info_move_to_window_line,
4400672dff93Sespie _("Move the cursor to a specific line of the window"))
4401fbc94a17Sniklas {
4402fbc94a17Sniklas int line;
4403fbc94a17Sniklas
4404fbc94a17Sniklas /* With no numeric argument of any kind, default to the center line. */
4405fbc94a17Sniklas if (!info_explicit_arg && count == 1)
4406fbc94a17Sniklas line = (window->height / 2) + window->pagetop;
4407fbc94a17Sniklas else
4408fbc94a17Sniklas {
4409fbc94a17Sniklas if (count < 0)
4410fbc94a17Sniklas line = (window->height + count) + window->pagetop;
4411fbc94a17Sniklas else
4412fbc94a17Sniklas line = window->pagetop + count;
4413fbc94a17Sniklas }
4414fbc94a17Sniklas
4415fbc94a17Sniklas /* If the line doesn't appear in this window, make it do so. */
4416fbc94a17Sniklas if ((line - window->pagetop) >= window->height)
4417fbc94a17Sniklas line = window->pagetop + (window->height - 1);
4418fbc94a17Sniklas
4419fbc94a17Sniklas /* If the line is too small, make it fit. */
4420fbc94a17Sniklas if (line < window->pagetop)
4421fbc94a17Sniklas line = window->pagetop;
4422fbc94a17Sniklas
4423fbc94a17Sniklas /* If the selected line is past the bottom of the node, force it back. */
4424fbc94a17Sniklas if (line >= window->line_count)
4425fbc94a17Sniklas line = window->line_count - 1;
4426fbc94a17Sniklas
4427fbc94a17Sniklas window->point = (window->line_starts[line] - window->node->contents);
4428fbc94a17Sniklas }
4429fbc94a17Sniklas
4430fbc94a17Sniklas /* Clear the screen and redraw its contents. Given a numeric argument,
4431fbc94a17Sniklas move the line the cursor is on to the COUNT'th line of the window. */
4432840175f0Skstailey DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
4433fbc94a17Sniklas {
4434fbc94a17Sniklas if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4435fbc94a17Sniklas {
4436fbc94a17Sniklas terminal_clear_screen ();
4437fbc94a17Sniklas display_clear_display (the_display);
4438fbc94a17Sniklas window_mark_chain (windows, W_UpdateWindow);
4439fbc94a17Sniklas display_update_display (windows);
4440fbc94a17Sniklas }
4441fbc94a17Sniklas else
4442fbc94a17Sniklas {
4443fbc94a17Sniklas int desired_line, point_line;
4444fbc94a17Sniklas int new_pagetop;
4445fbc94a17Sniklas
4446fbc94a17Sniklas point_line = window_line_of_point (window) - window->pagetop;
4447fbc94a17Sniklas
4448fbc94a17Sniklas if (count < 0)
4449fbc94a17Sniklas desired_line = window->height + count;
4450fbc94a17Sniklas else
4451fbc94a17Sniklas desired_line = count;
4452fbc94a17Sniklas
4453fbc94a17Sniklas if (desired_line < 0)
4454fbc94a17Sniklas desired_line = 0;
4455fbc94a17Sniklas
4456fbc94a17Sniklas if (desired_line >= window->height)
4457fbc94a17Sniklas desired_line = window->height - 1;
4458fbc94a17Sniklas
4459fbc94a17Sniklas if (desired_line == point_line)
4460fbc94a17Sniklas return;
4461fbc94a17Sniklas
4462fbc94a17Sniklas new_pagetop = window->pagetop + (point_line - desired_line);
4463fbc94a17Sniklas
4464fbc94a17Sniklas set_window_pagetop (window, new_pagetop);
4465fbc94a17Sniklas }
4466fbc94a17Sniklas }
4467fbc94a17Sniklas /* This command does nothing. It is the fact that a key is bound to it
4468fbc94a17Sniklas that has meaning. See the code at the top of info_session (). */
4469840175f0Skstailey DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
4470fbc94a17Sniklas {}
4471fbc94a17Sniklas
4472fbc94a17Sniklas
4473fbc94a17Sniklas /* **************************************************************** */
4474fbc94a17Sniklas /* */
4475fbc94a17Sniklas /* Reading Keys and Dispatching on Them */
4476fbc94a17Sniklas /* */
4477fbc94a17Sniklas /* **************************************************************** */
4478fbc94a17Sniklas
44793aa90977Sespie /* Declaration only. Special cased in info_dispatch_on_key ().
44803aa90977Sespie Doc string is to avoid ugly results with describe_key etc. */
44813aa90977Sespie DECLARE_INFO_COMMAND (info_do_lowercase_version,
44823aa90977Sespie _("Run command bound to this key's lowercase variant"))
4483fbc94a17Sniklas {}
4484fbc94a17Sniklas
4485fbc94a17Sniklas static void
dispatch_error(char * keyseq)4486*1076333cSespie dispatch_error (char *keyseq)
4487fbc94a17Sniklas {
4488fbc94a17Sniklas char *rep;
4489fbc94a17Sniklas
4490fbc94a17Sniklas rep = pretty_keyseq (keyseq);
4491fbc94a17Sniklas
4492fbc94a17Sniklas if (!echo_area_is_active)
4493*1076333cSespie info_error ((char *) _("Unknown command (%s)."), rep, NULL);
4494fbc94a17Sniklas else
4495fbc94a17Sniklas {
4496*1076333cSespie char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid")));
4497*1076333cSespie sprintf (temp, _("`%s' is invalid"), rep);
4498fbc94a17Sniklas terminal_ring_bell ();
4499fbc94a17Sniklas inform_in_echo_area (temp);
4500fbc94a17Sniklas free (temp);
4501fbc94a17Sniklas }
4502fbc94a17Sniklas }
4503fbc94a17Sniklas
4504fbc94a17Sniklas /* Keeping track of key sequences. */
4505fbc94a17Sniklas static char *info_keyseq = (char *)NULL;
4506fbc94a17Sniklas static int info_keyseq_index = 0;
4507fbc94a17Sniklas static int info_keyseq_size = 0;
4508fbc94a17Sniklas static int info_keyseq_displayed_p = 0;
4509fbc94a17Sniklas
4510fbc94a17Sniklas /* Initialize the length of the current key sequence. */
4511fbc94a17Sniklas void
initialize_keyseq(void)4512*1076333cSespie initialize_keyseq (void)
4513fbc94a17Sniklas {
4514fbc94a17Sniklas info_keyseq_index = 0;
4515fbc94a17Sniklas info_keyseq_displayed_p = 0;
4516fbc94a17Sniklas }
4517fbc94a17Sniklas
4518fbc94a17Sniklas /* Add CHARACTER to the current key sequence. */
4519fbc94a17Sniklas void
add_char_to_keyseq(char character)4520*1076333cSespie add_char_to_keyseq (char character)
4521fbc94a17Sniklas {
4522fbc94a17Sniklas if (info_keyseq_index + 2 >= info_keyseq_size)
4523fbc94a17Sniklas info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
4524fbc94a17Sniklas
4525fbc94a17Sniklas info_keyseq[info_keyseq_index++] = character;
4526fbc94a17Sniklas info_keyseq[info_keyseq_index] = '\0';
4527fbc94a17Sniklas }
4528fbc94a17Sniklas
4529fbc94a17Sniklas /* Display the current value of info_keyseq. If argument EXPECTING is
4530fbc94a17Sniklas non-zero, input is expected to be read after the key sequence is
4531fbc94a17Sniklas displayed, so add an additional prompting character to the sequence. */
4532*1076333cSespie static void
display_info_keyseq(int expecting_future_input)4533*1076333cSespie display_info_keyseq (int expecting_future_input)
4534fbc94a17Sniklas {
4535fbc94a17Sniklas char *rep;
4536fbc94a17Sniklas
4537fbc94a17Sniklas rep = pretty_keyseq (info_keyseq);
4538fbc94a17Sniklas if (expecting_future_input)
4539fbc94a17Sniklas strcat (rep, "-");
4540fbc94a17Sniklas
4541fbc94a17Sniklas if (echo_area_is_active)
4542fbc94a17Sniklas inform_in_echo_area (rep);
4543fbc94a17Sniklas else
4544fbc94a17Sniklas {
4545*1076333cSespie window_message_in_echo_area (rep, NULL, NULL);
4546fbc94a17Sniklas display_cursor_at_point (active_window);
4547fbc94a17Sniklas }
4548fbc94a17Sniklas info_keyseq_displayed_p = 1;
4549fbc94a17Sniklas }
4550fbc94a17Sniklas
4551fbc94a17Sniklas /* Called by interactive commands to read a keystroke. */
4552fbc94a17Sniklas unsigned char
info_get_another_input_char(void)4553*1076333cSespie info_get_another_input_char (void)
4554fbc94a17Sniklas {
4555840175f0Skstailey int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
4556fbc94a17Sniklas
4557fbc94a17Sniklas /* If there isn't any input currently available, then wait a
4558fbc94a17Sniklas moment looking for input. If we don't get it fast enough,
4559fbc94a17Sniklas prompt a little bit with the current key sequence. */
4560840175f0Skstailey if (!info_keyseq_displayed_p)
4561840175f0Skstailey {
4562840175f0Skstailey ready = 1;
4563840175f0Skstailey if (!info_any_buffered_input_p () &&
4564fbc94a17Sniklas !info_input_pending_p ())
4565fbc94a17Sniklas {
4566fbc94a17Sniklas #if defined (FD_SET)
4567fbc94a17Sniklas struct timeval timer;
4568fbc94a17Sniklas fd_set readfds;
4569fbc94a17Sniklas
4570fbc94a17Sniklas FD_ZERO (&readfds);
4571fbc94a17Sniklas FD_SET (fileno (info_input_stream), &readfds);
4572fbc94a17Sniklas timer.tv_sec = 1;
4573fbc94a17Sniklas timer.tv_usec = 750;
4574840175f0Skstailey ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
4575840175f0Skstailey #else
4576840175f0Skstailey ready = 0;
4577fbc94a17Sniklas #endif /* FD_SET */
4578fbc94a17Sniklas }
4579840175f0Skstailey }
4580fbc94a17Sniklas
4581fbc94a17Sniklas if (!ready)
4582fbc94a17Sniklas display_info_keyseq (1);
4583fbc94a17Sniklas
4584fbc94a17Sniklas return (info_get_input_char ());
4585fbc94a17Sniklas }
4586fbc94a17Sniklas
4587fbc94a17Sniklas /* Do the command associated with KEY in MAP. If the associated command is
4588fbc94a17Sniklas really a keymap, then read another key, and dispatch into that map. */
4589fbc94a17Sniklas void
info_dispatch_on_key(unsigned char key,Keymap map)4590*1076333cSespie info_dispatch_on_key (unsigned char key, Keymap map)
4591fbc94a17Sniklas {
45923aa90977Sespie #if !defined(INFOKEY)
4593fbc94a17Sniklas if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
4594fbc94a17Sniklas {
4595fbc94a17Sniklas if (map[ESC].type == ISKMAP)
4596fbc94a17Sniklas {
4597fbc94a17Sniklas map = (Keymap)map[ESC].function;
4598fbc94a17Sniklas add_char_to_keyseq (ESC);
4599fbc94a17Sniklas key = UnMeta (key);
4600fbc94a17Sniklas info_dispatch_on_key (key, map);
4601fbc94a17Sniklas }
4602fbc94a17Sniklas else
4603fbc94a17Sniklas {
4604fbc94a17Sniklas dispatch_error (info_keyseq);
4605fbc94a17Sniklas }
4606fbc94a17Sniklas return;
4607fbc94a17Sniklas }
46083aa90977Sespie #endif /* INFOKEY */
4609fbc94a17Sniklas
4610fbc94a17Sniklas switch (map[key].type)
4611fbc94a17Sniklas {
4612fbc94a17Sniklas case ISFUNC:
4613fbc94a17Sniklas {
4614fbc94a17Sniklas VFunction *func;
4615fbc94a17Sniklas
46163aa90977Sespie func = InfoFunction(map[key].function);
4617fbc94a17Sniklas if (func != (VFunction *)NULL)
4618fbc94a17Sniklas {
4619fbc94a17Sniklas /* Special case info_do_lowercase_version (). */
4620*1076333cSespie if (func == (VFunction *) info_do_lowercase_version)
4621fbc94a17Sniklas {
46223aa90977Sespie #if defined(INFOKEY)
46233aa90977Sespie unsigned char lowerkey;
46243aa90977Sespie
46253aa90977Sespie lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
46263aa90977Sespie if (lowerkey == key)
46273aa90977Sespie {
46283aa90977Sespie add_char_to_keyseq (key);
46293aa90977Sespie dispatch_error (info_keyseq);
46303aa90977Sespie return;
46313aa90977Sespie }
46323aa90977Sespie info_dispatch_on_key (lowerkey, map);
46333aa90977Sespie #else /* !INFOKEY */
4634fbc94a17Sniklas info_dispatch_on_key (tolower (key), map);
46353aa90977Sespie #endif /* INFOKEY */
4636fbc94a17Sniklas return;
4637fbc94a17Sniklas }
4638fbc94a17Sniklas
4639fbc94a17Sniklas add_char_to_keyseq (key);
4640fbc94a17Sniklas
4641fbc94a17Sniklas if (info_keyseq_displayed_p)
4642fbc94a17Sniklas display_info_keyseq (0);
4643fbc94a17Sniklas
4644fbc94a17Sniklas {
4645fbc94a17Sniklas WINDOW *where;
4646fbc94a17Sniklas
4647fbc94a17Sniklas where = active_window;
46483aa90977Sespie (*InfoFunction(map[key].function))
4649fbc94a17Sniklas (active_window, info_numeric_arg * info_numeric_arg_sign, key);
4650fbc94a17Sniklas
4651fbc94a17Sniklas /* If we have input pending, then the last command was a prefix
4652fbc94a17Sniklas command. Don't change the value of the last function vars.
4653fbc94a17Sniklas Otherwise, remember the last command executed in the var
4654fbc94a17Sniklas appropriate to the window in which it was executed. */
4655fbc94a17Sniklas if (!info_input_pending_p ())
4656fbc94a17Sniklas {
4657fbc94a17Sniklas if (where == the_echo_area)
46583aa90977Sespie ea_last_executed_command = InfoFunction(map[key].function);
4659fbc94a17Sniklas else
46603aa90977Sespie info_last_executed_command = InfoFunction(map[key].function);
4661fbc94a17Sniklas }
4662fbc94a17Sniklas }
4663fbc94a17Sniklas }
4664fbc94a17Sniklas else
4665fbc94a17Sniklas {
4666fbc94a17Sniklas add_char_to_keyseq (key);
4667fbc94a17Sniklas dispatch_error (info_keyseq);
4668fbc94a17Sniklas return;
4669fbc94a17Sniklas }
4670fbc94a17Sniklas }
4671fbc94a17Sniklas break;
4672fbc94a17Sniklas
4673fbc94a17Sniklas case ISKMAP:
4674fbc94a17Sniklas add_char_to_keyseq (key);
46753aa90977Sespie if (map[key].function != (InfoCommand *)NULL)
4676fbc94a17Sniklas {
4677fbc94a17Sniklas unsigned char newkey;
4678fbc94a17Sniklas
4679fbc94a17Sniklas newkey = info_get_another_input_char ();
4680fbc94a17Sniklas info_dispatch_on_key (newkey, (Keymap)map[key].function);
4681fbc94a17Sniklas }
4682fbc94a17Sniklas else
4683fbc94a17Sniklas {
4684fbc94a17Sniklas dispatch_error (info_keyseq);
4685fbc94a17Sniklas return;
4686fbc94a17Sniklas }
4687fbc94a17Sniklas break;
4688fbc94a17Sniklas }
4689fbc94a17Sniklas }
4690fbc94a17Sniklas
4691fbc94a17Sniklas /* **************************************************************** */
4692fbc94a17Sniklas /* */
4693fbc94a17Sniklas /* Numeric Arguments */
4694fbc94a17Sniklas /* */
4695fbc94a17Sniklas /* **************************************************************** */
4696fbc94a17Sniklas
4697fbc94a17Sniklas /* Handle C-u style numeric args, as well as M--, and M-digits. */
4698fbc94a17Sniklas
4699fbc94a17Sniklas /* Non-zero means that an explicit argument has been passed to this
4700fbc94a17Sniklas command, as in C-u C-v. */
4701fbc94a17Sniklas int info_explicit_arg = 0;
4702fbc94a17Sniklas
4703fbc94a17Sniklas /* The sign of the numeric argument. */
4704fbc94a17Sniklas int info_numeric_arg_sign = 1;
4705fbc94a17Sniklas
4706fbc94a17Sniklas /* The value of the argument itself. */
4707fbc94a17Sniklas int info_numeric_arg = 1;
4708fbc94a17Sniklas
4709fbc94a17Sniklas /* Add the current digit to the argument in progress. */
4710fbc94a17Sniklas DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
4711840175f0Skstailey _("Add this digit to the current numeric argument"))
4712fbc94a17Sniklas {
4713fbc94a17Sniklas info_numeric_arg_digit_loop (window, 0, key);
4714fbc94a17Sniklas }
4715fbc94a17Sniklas
4716fbc94a17Sniklas /* C-u, universal argument. Multiply the current argument by 4.
4717fbc94a17Sniklas Read a key. If the key has nothing to do with arguments, then
4718fbc94a17Sniklas dispatch on it. If the key is the abort character then abort. */
4719fbc94a17Sniklas DECLARE_INFO_COMMAND (info_universal_argument,
4720840175f0Skstailey _("Start (or multiply by 4) the current numeric argument"))
4721fbc94a17Sniklas {
4722fbc94a17Sniklas info_numeric_arg *= 4;
4723fbc94a17Sniklas info_numeric_arg_digit_loop (window, 0, 0);
4724fbc94a17Sniklas }
4725fbc94a17Sniklas
4726fbc94a17Sniklas /* Create a default argument. */
4727fbc94a17Sniklas void
info_initialize_numeric_arg(void)4728*1076333cSespie info_initialize_numeric_arg (void)
4729fbc94a17Sniklas {
4730fbc94a17Sniklas info_numeric_arg = info_numeric_arg_sign = 1;
4731fbc94a17Sniklas info_explicit_arg = 0;
4732fbc94a17Sniklas }
4733fbc94a17Sniklas
4734fbc94a17Sniklas DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
4735840175f0Skstailey _("Internally used by \\[universal-argument]"))
4736fbc94a17Sniklas {
4737fbc94a17Sniklas unsigned char pure_key;
4738fbc94a17Sniklas Keymap keymap = window->keymap;
4739fbc94a17Sniklas
4740fbc94a17Sniklas while (1)
4741fbc94a17Sniklas {
4742fbc94a17Sniklas if (key)
4743fbc94a17Sniklas pure_key = key;
4744fbc94a17Sniklas else
4745fbc94a17Sniklas {
4746fbc94a17Sniklas if (display_was_interrupted_p && !info_any_buffered_input_p ())
4747fbc94a17Sniklas display_update_display (windows);
4748fbc94a17Sniklas
4749fbc94a17Sniklas if (active_window != the_echo_area)
4750fbc94a17Sniklas display_cursor_at_point (active_window);
4751fbc94a17Sniklas
4752fbc94a17Sniklas pure_key = key = info_get_another_input_char ();
4753fbc94a17Sniklas
47543aa90977Sespie #if !defined(INFOKEY)
4755fbc94a17Sniklas if (Meta_p (key))
4756fbc94a17Sniklas add_char_to_keyseq (ESC);
4757fbc94a17Sniklas
4758fbc94a17Sniklas add_char_to_keyseq (UnMeta (key));
47593aa90977Sespie #else /* defined(INFOKEY) */
47603aa90977Sespie add_char_to_keyseq (key);
47613aa90977Sespie #endif /* defined(INFOKEY) */
4762fbc94a17Sniklas }
4763fbc94a17Sniklas
47643aa90977Sespie #if !defined(INFOKEY)
4765fbc94a17Sniklas if (Meta_p (key))
4766fbc94a17Sniklas key = UnMeta (key);
47673aa90977Sespie #endif /* !defined(INFOKEY) */
4768fbc94a17Sniklas
4769*1076333cSespie if (keymap[key].type == ISFUNC
4770*1076333cSespie && InfoFunction(keymap[key].function)
4771*1076333cSespie == (VFunction *) info_universal_argument)
4772fbc94a17Sniklas {
4773fbc94a17Sniklas info_numeric_arg *= 4;
4774fbc94a17Sniklas key = 0;
4775fbc94a17Sniklas continue;
4776fbc94a17Sniklas }
4777fbc94a17Sniklas
47783aa90977Sespie #if defined(INFOKEY)
47793aa90977Sespie if (Meta_p (key))
47803aa90977Sespie key = UnMeta (key);
47813aa90977Sespie #endif /* !defined(INFOKEY) */
47823aa90977Sespie
47833aa90977Sespie
4784fbc94a17Sniklas if (isdigit (key))
4785fbc94a17Sniklas {
4786fbc94a17Sniklas if (info_explicit_arg)
4787fbc94a17Sniklas info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
4788fbc94a17Sniklas else
4789fbc94a17Sniklas info_numeric_arg = (key - '0');
4790fbc94a17Sniklas info_explicit_arg = 1;
4791fbc94a17Sniklas }
4792fbc94a17Sniklas else
4793fbc94a17Sniklas {
4794fbc94a17Sniklas if (key == '-' && !info_explicit_arg)
4795fbc94a17Sniklas {
4796fbc94a17Sniklas info_numeric_arg_sign = -1;
4797fbc94a17Sniklas info_numeric_arg = 1;
4798fbc94a17Sniklas }
4799fbc94a17Sniklas else
4800fbc94a17Sniklas {
4801fbc94a17Sniklas info_keyseq_index--;
4802fbc94a17Sniklas info_dispatch_on_key (pure_key, keymap);
4803fbc94a17Sniklas return;
4804fbc94a17Sniklas }
4805fbc94a17Sniklas }
4806fbc94a17Sniklas key = 0;
4807fbc94a17Sniklas }
4808fbc94a17Sniklas }
4809fbc94a17Sniklas
4810fbc94a17Sniklas /* **************************************************************** */
4811fbc94a17Sniklas /* */
4812fbc94a17Sniklas /* Input Character Buffering */
4813fbc94a17Sniklas /* */
4814fbc94a17Sniklas /* **************************************************************** */
4815fbc94a17Sniklas
4816fbc94a17Sniklas /* Character waiting to be read next. */
4817fbc94a17Sniklas static int pending_input_character = 0;
4818fbc94a17Sniklas
4819fbc94a17Sniklas /* How to make there be no pending input. */
4820fbc94a17Sniklas static void
info_clear_pending_input(void)4821*1076333cSespie info_clear_pending_input (void)
4822fbc94a17Sniklas {
4823fbc94a17Sniklas pending_input_character = 0;
4824fbc94a17Sniklas }
4825fbc94a17Sniklas
4826fbc94a17Sniklas /* How to set the pending input character. */
4827fbc94a17Sniklas static void
info_set_pending_input(unsigned char key)4828*1076333cSespie info_set_pending_input (unsigned char key)
4829fbc94a17Sniklas {
4830fbc94a17Sniklas pending_input_character = key;
4831fbc94a17Sniklas }
4832fbc94a17Sniklas
4833fbc94a17Sniklas /* How to see if there is any pending input. */
4834fbc94a17Sniklas unsigned char
info_input_pending_p(void)4835*1076333cSespie info_input_pending_p (void)
4836fbc94a17Sniklas {
4837fbc94a17Sniklas return (pending_input_character);
4838fbc94a17Sniklas }
4839fbc94a17Sniklas
4840fbc94a17Sniklas /* Largest number of characters that we can read in advance. */
4841fbc94a17Sniklas #define MAX_INFO_INPUT_BUFFERING 512
4842fbc94a17Sniklas
4843fbc94a17Sniklas static int pop_index = 0, push_index = 0;
4844fbc94a17Sniklas static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
4845fbc94a17Sniklas
4846fbc94a17Sniklas /* Add KEY to the buffer of characters to be read. */
4847fbc94a17Sniklas static void
info_push_typeahead(unsigned char key)4848*1076333cSespie info_push_typeahead (unsigned char key)
4849fbc94a17Sniklas {
4850fbc94a17Sniklas /* Flush all pending input in the case of C-g pressed. */
4851fbc94a17Sniklas if (key == Control ('g'))
4852fbc94a17Sniklas {
4853fbc94a17Sniklas push_index = pop_index;
4854fbc94a17Sniklas info_set_pending_input (Control ('g'));
4855fbc94a17Sniklas }
4856fbc94a17Sniklas else
4857fbc94a17Sniklas {
4858fbc94a17Sniklas info_input_buffer[push_index++] = key;
4859*1076333cSespie if ((unsigned int) push_index >= sizeof (info_input_buffer))
4860fbc94a17Sniklas push_index = 0;
4861fbc94a17Sniklas }
4862fbc94a17Sniklas }
4863fbc94a17Sniklas
4864fbc94a17Sniklas /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4865fbc94a17Sniklas static int
info_input_buffer_space_available(void)4866*1076333cSespie info_input_buffer_space_available (void)
4867fbc94a17Sniklas {
4868fbc94a17Sniklas if (pop_index > push_index)
4869fbc94a17Sniklas return (pop_index - push_index);
4870fbc94a17Sniklas else
487140248eceSdownsj return (sizeof (info_input_buffer) - (push_index - pop_index));
4872fbc94a17Sniklas }
4873fbc94a17Sniklas
4874fbc94a17Sniklas /* Get a key from the buffer of characters to be read.
4875fbc94a17Sniklas Return the key in KEY.
4876fbc94a17Sniklas Result is non-zero if there was a key, or 0 if there wasn't. */
4877fbc94a17Sniklas static int
info_get_key_from_typeahead(unsigned char * key)4878*1076333cSespie info_get_key_from_typeahead (unsigned char *key)
4879fbc94a17Sniklas {
4880fbc94a17Sniklas if (push_index == pop_index)
4881fbc94a17Sniklas return (0);
4882fbc94a17Sniklas
4883fbc94a17Sniklas *key = info_input_buffer[pop_index++];
4884fbc94a17Sniklas
4885*1076333cSespie if ((unsigned int) pop_index >= sizeof (info_input_buffer))
4886fbc94a17Sniklas pop_index = 0;
4887fbc94a17Sniklas
4888fbc94a17Sniklas return (1);
4889fbc94a17Sniklas }
4890fbc94a17Sniklas
4891fbc94a17Sniklas int
info_any_buffered_input_p(void)4892*1076333cSespie info_any_buffered_input_p (void)
4893fbc94a17Sniklas {
4894fbc94a17Sniklas info_gather_typeahead ();
4895fbc94a17Sniklas return (push_index != pop_index);
4896fbc94a17Sniklas }
4897fbc94a17Sniklas
4898fbc94a17Sniklas /* If characters are available to be read, then read them and stuff them into
4899fbc94a17Sniklas info_input_buffer. Otherwise, do nothing. */
4900fbc94a17Sniklas void
info_gather_typeahead(void)4901*1076333cSespie info_gather_typeahead (void)
4902fbc94a17Sniklas {
490340248eceSdownsj register int i = 0;
4904fbc94a17Sniklas int tty, space_avail;
4905fbc94a17Sniklas long chars_avail;
4906fbc94a17Sniklas unsigned char input[MAX_INFO_INPUT_BUFFERING];
4907fbc94a17Sniklas
4908fbc94a17Sniklas tty = fileno (info_input_stream);
4909fbc94a17Sniklas chars_avail = 0;
4910fbc94a17Sniklas
4911fbc94a17Sniklas space_avail = info_input_buffer_space_available ();
4912fbc94a17Sniklas
4913fbc94a17Sniklas /* If we can just find out how many characters there are to read, do so. */
4914fbc94a17Sniklas #if defined (FIONREAD)
4915fbc94a17Sniklas {
4916fbc94a17Sniklas ioctl (tty, FIONREAD, &chars_avail);
4917fbc94a17Sniklas
4918fbc94a17Sniklas if (chars_avail > space_avail)
4919fbc94a17Sniklas chars_avail = space_avail;
4920fbc94a17Sniklas
4921fbc94a17Sniklas if (chars_avail)
4922840175f0Skstailey chars_avail = read (tty, &input[0], chars_avail);
4923fbc94a17Sniklas }
4924fbc94a17Sniklas #else /* !FIONREAD */
4925fbc94a17Sniklas # if defined (O_NDELAY)
4926fbc94a17Sniklas {
4927fbc94a17Sniklas int flags;
4928fbc94a17Sniklas
4929fbc94a17Sniklas flags = fcntl (tty, F_GETFL, 0);
4930fbc94a17Sniklas
4931fbc94a17Sniklas fcntl (tty, F_SETFL, (flags | O_NDELAY));
4932fbc94a17Sniklas chars_avail = read (tty, &input[0], space_avail);
4933fbc94a17Sniklas fcntl (tty, F_SETFL, flags);
4934fbc94a17Sniklas
4935fbc94a17Sniklas if (chars_avail == -1)
4936fbc94a17Sniklas chars_avail = 0;
4937fbc94a17Sniklas }
4938672dff93Sespie # else /* !O_NDELAY */
4939672dff93Sespie # ifdef __DJGPP__
4940672dff93Sespie {
4941672dff93Sespie extern long pc_term_chars_avail (void);
4942672dff93Sespie
4943672dff93Sespie if (isatty (tty))
4944672dff93Sespie chars_avail = pc_term_chars_avail ();
4945672dff93Sespie else
4946672dff93Sespie {
4947672dff93Sespie /* We could be more accurate by calling ltell, but we have no idea
4948672dff93Sespie whether tty is buffered by stdio functions, and if so, how many
4949672dff93Sespie characters are already waiting in the buffer. So we punt. */
4950672dff93Sespie struct stat st;
4951672dff93Sespie
4952672dff93Sespie if (fstat (tty, &st) < 0)
4953672dff93Sespie chars_avail = 1;
4954672dff93Sespie else
4955672dff93Sespie chars_avail = st.st_size;
4956672dff93Sespie }
4957672dff93Sespie if (chars_avail > space_avail)
4958672dff93Sespie chars_avail = space_avail;
4959672dff93Sespie if (chars_avail)
4960672dff93Sespie chars_avail = read (tty, &input[0], chars_avail);
4961672dff93Sespie }
4962672dff93Sespie # endif/* __DJGPP__ */
4963fbc94a17Sniklas # endif /* O_NDELAY */
4964fbc94a17Sniklas #endif /* !FIONREAD */
4965fbc94a17Sniklas
496640248eceSdownsj while (i < chars_avail)
4967fbc94a17Sniklas {
4968fbc94a17Sniklas info_push_typeahead (input[i]);
496940248eceSdownsj i++;
4970fbc94a17Sniklas }
4971fbc94a17Sniklas }
4972fbc94a17Sniklas
4973fbc94a17Sniklas /* How to read a single character. */
4974fbc94a17Sniklas unsigned char
info_get_input_char(void)4975*1076333cSespie info_get_input_char (void)
4976fbc94a17Sniklas {
4977fbc94a17Sniklas unsigned char keystroke;
4978fbc94a17Sniklas
4979fbc94a17Sniklas info_gather_typeahead ();
4980fbc94a17Sniklas
4981fbc94a17Sniklas if (pending_input_character)
4982fbc94a17Sniklas {
4983fbc94a17Sniklas keystroke = pending_input_character;
4984fbc94a17Sniklas pending_input_character = 0;
4985fbc94a17Sniklas }
4986fbc94a17Sniklas else if (info_get_key_from_typeahead (&keystroke) == 0)
4987fbc94a17Sniklas {
4988fbc94a17Sniklas int rawkey;
4989840175f0Skstailey unsigned char c;
4990840175f0Skstailey int tty = fileno (info_input_stream);
4991fbc94a17Sniklas
4992840175f0Skstailey /* Using stream I/O causes FIONREAD etc to fail to work
4993840175f0Skstailey so unless someone can find a portable way of finding
4994840175f0Skstailey out how many characters are currently buffered, we
4995840175f0Skstailey should stay with away from stream I/O.
4996840175f0Skstailey --Egil Kvaleberg <egilk@sn.no>, January 1997. */
499728ea187bSespie #ifdef EINTR
499828ea187bSespie /* Keep reading if we got EINTR, so that we don't just exit.
499928ea187bSespie --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
500028ea187bSespie 22 Dec 1997. */
500128ea187bSespie {
500228ea187bSespie int n;
500328ea187bSespie do
500428ea187bSespie n = read (tty, &c, 1);
500528ea187bSespie while (n == -1 && errno == EINTR);
500628ea187bSespie rawkey = n == 1 ? c : EOF;
500728ea187bSespie }
500828ea187bSespie #else
5009840175f0Skstailey rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
501028ea187bSespie #endif
5011840175f0Skstailey
5012fbc94a17Sniklas keystroke = rawkey;
5013fbc94a17Sniklas
5014fbc94a17Sniklas if (rawkey == EOF)
5015fbc94a17Sniklas {
5016fbc94a17Sniklas if (info_input_stream != stdin)
5017fbc94a17Sniklas {
5018fbc94a17Sniklas fclose (info_input_stream);
5019fbc94a17Sniklas info_input_stream = stdin;
5020672dff93Sespie tty = fileno (info_input_stream);
5021fbc94a17Sniklas display_inhibited = 0;
5022fbc94a17Sniklas display_update_display (windows);
5023fbc94a17Sniklas display_cursor_at_point (active_window);
5024840175f0Skstailey rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5025fbc94a17Sniklas keystroke = rawkey;
5026fbc94a17Sniklas }
5027fbc94a17Sniklas
5028fbc94a17Sniklas if (rawkey == EOF)
5029fbc94a17Sniklas {
5030fbc94a17Sniklas terminal_unprep_terminal ();
5031fbc94a17Sniklas close_dribble_file ();
5032672dff93Sespie xexit (0);
5033fbc94a17Sniklas }
5034fbc94a17Sniklas }
5035fbc94a17Sniklas }
5036fbc94a17Sniklas
5037fbc94a17Sniklas if (info_dribble_file)
5038fbc94a17Sniklas dribble (keystroke);
5039fbc94a17Sniklas
5040840175f0Skstailey return keystroke;
5041fbc94a17Sniklas }
5042