xref: /openbsd-src/gnu/usr.bin/texinfo/info/session.c (revision 1076333c323c9f213f0d653fc52002328f47dbe9)
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