Merge branch 'vendor/OPENSSL'
[dragonfly.git] / contrib / texinfo / info / session.c
1 /* session.c -- user windowing interface to Info.
2    $Id: session.c,v 1.43 2008/06/11 17:38:33 gray Exp $
3
4    Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
5    2004, 2007, 2008 Free Software Foundation, Inc.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20    Originally written by Brian Fox (bfox@ai.mit.edu). */
21
22 #include "info.h"
23 #include "search.h"
24 #include <sys/ioctl.h>
25
26 #if defined (HAVE_SYS_TIME_H)
27 #  include <sys/time.h>
28 #  define HAVE_STRUCT_TIMEVAL
29 #endif /* HAVE_SYS_TIME_H */
30
31 #if defined (HANDLE_MAN_PAGES)
32 #  include "man.h"
33 #endif
34
35 static void info_clear_pending_input (void);
36 static void info_set_pending_input (unsigned char key);
37 static void info_handle_pointer (char *label, WINDOW *window);
38 static void display_info_keyseq (int expecting_future_input);
39 char *node_printed_rep (NODE *node);
40
41 /* **************************************************************** */
42 /*                                                                  */
43 /*                   Running an Info Session                        */
44 /*                                                                  */
45 /* **************************************************************** */
46
47 /* The place that we are reading input from. */
48 static FILE *info_input_stream = NULL;
49
50 /* The last executed command. */
51 VFunction *info_last_executed_command = NULL;
52
53 /* Becomes non-zero when 'q' is typed to an Info window. */
54 int quit_info_immediately = 0;
55
56 /* Array of structures describing for each window which nodes have been
57    visited in that window. */
58 INFO_WINDOW **info_windows = NULL;
59
60 /* Where to add the next window, if we need to add one. */
61 static int info_windows_index = 0;
62
63 /* Number of slots allocated to `info_windows'. */
64 static int info_windows_slots = 0;
65
66 /* Whether to use regexps or not for search.  */
67 static int use_regex = 1;
68
69 void remember_window_and_node (WINDOW *window, NODE *node);
70 void forget_window_and_nodes (WINDOW *window);
71 void display_startup_message_and_start (void);
72
73 /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
74    For each loaded node, create a new window.  Always split the largest of the
75    available windows. */
76 void
77 begin_multiple_window_info_session (char *filename, char **nodenames)
78 {
79   register int i;
80   WINDOW *window = NULL;
81
82   for (i = 0; nodenames[i]; i++)
83     {
84       NODE *node;
85
86       node = info_get_node (filename, nodenames[i]);
87
88       if (!node)
89         break;
90
91       /* If this is the first node, initialize the info session. */
92       if (!window)
93         {
94           initialize_info_session (node, 1);
95           window = active_window;
96         }
97       else
98         {
99           /* Find the largest window in WINDOWS, and make that be the active
100              one.  Then split it and add our window and node to the list
101              of remembered windows and nodes.  Then tile the windows. */
102           WINDOW *win, *largest = NULL;
103           int max_height = 0;
104
105           for (win = windows; win; win = win->next)
106             if (win->height > max_height)
107               {
108                 max_height = win->height;
109                 largest = win;
110               }
111
112           if (!largest)
113             {
114               display_update_display (windows);
115               info_error (msg_cant_find_window, NULL, NULL);
116               info_session ();
117               xexit (0);
118             }
119
120           active_window = largest;
121           window = window_make_window (node);
122           if (window)
123             {
124               window_tile_windows (TILE_INTERNALS);
125               remember_window_and_node (window, node);
126             }
127           else
128             {
129               display_update_display (windows);
130               info_error (msg_win_too_small, NULL, NULL);
131               info_session ();
132               xexit (0);
133             }
134         }
135     }
136   display_startup_message_and_start ();
137 }
138
139 /* Start an info session with INITIAL_NODE, and an error message in the echo
140    area made from FORMAT and ARG. */
141 void
142 begin_info_session_with_error (NODE *initial_node, const char *format,
143     void *arg1, void *arg2)
144 {
145   initialize_info_session (initial_node, 1);
146   info_error (format, arg1, arg2);
147   info_session ();
148 }
149
150 /* Start an info session with INITIAL_NODE. */
151 void
152 begin_info_session (NODE *initial_node)
153 {
154   initialize_info_session (initial_node, 1);
155   display_startup_message_and_start ();
156 }
157
158 void
159 display_startup_message_and_start (void)
160 {
161   char *format;
162
163   format = replace_in_documentation
164     (_("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."),
165      0);
166
167   window_message_in_echo_area (format, VERSION, NULL);
168   info_session ();
169 }
170
171 /* Run an info session with an already initialized window and node. */
172 void
173 info_session (void)
174 {
175   display_update_display (windows);
176   info_last_executed_command = NULL;
177   info_read_and_dispatch ();
178   /* On program exit, leave the cursor at the bottom of the window, and
179      restore the terminal I/O. */
180   terminal_goto_xy (0, screenheight - 1);
181   terminal_clear_to_eol ();
182   fflush (stdout);
183   terminal_unprep_terminal ();
184   close_dribble_file ();
185 }
186
187 /* Here is a window-location dependent event loop.  Called from the
188    functions info_session (), and from read_xxx_in_echo_area (). */
189 void
190 info_read_and_dispatch (void)
191 {
192   unsigned char key;
193   int done;
194   done = 0;
195
196   while (!done && !quit_info_immediately)
197     {
198       int lk = 0;
199
200       /* If we haven't just gone up or down a line, there is no
201          goal column for this window. */
202       if ((info_last_executed_command != (VFunction *) info_next_line) &&
203           (info_last_executed_command != (VFunction *) info_prev_line))
204         active_window->goal_column = -1;
205
206       if (echo_area_is_active)
207         {
208           lk = echo_area_last_command_was_kill;
209           echo_area_prep_read ();
210         }
211
212       if (!info_any_buffered_input_p ())
213         display_update_display (windows);
214
215       display_cursor_at_point (active_window);
216       info_initialize_numeric_arg ();
217
218       initialize_keyseq ();
219       key = info_get_input_char ();
220
221       /* No errors yet.  We just read a character, that's all.  Only clear
222          the echo_area if it is not currently active. */
223       if (!echo_area_is_active)
224         window_clear_echo_area ();
225
226       info_error_was_printed = 0;
227
228       /* Do the selected command. */
229       info_dispatch_on_key (key, active_window->keymap);
230
231       if (echo_area_is_active)
232         {
233           /* Echo area commands that do killing increment the value of
234              ECHO_AREA_LAST_COMMAND_WAS_KILL.  Thus, if there is no
235              change in the value of this variable, the last command
236              executed was not a kill command. */
237           if (lk == echo_area_last_command_was_kill)
238             echo_area_last_command_was_kill = 0;
239
240           if (ea_last_executed_command == (VFunction *) ea_newline ||
241               info_aborted_echo_area)
242             {
243               ea_last_executed_command = NULL;
244               done = 1;
245             }
246
247           if (info_last_executed_command == (VFunction *) info_quit)
248             quit_info_immediately = 1;
249         }
250       else if (info_last_executed_command == (VFunction *) info_quit)
251         done = 1;
252     }
253 }
254
255 /* Found in signals.c */
256 extern void initialize_info_signal_handler (void );
257
258 /* Initialize the first info session by starting the terminal, window,
259    and display systems.  If CLEAR_SCREEN is 0, don't clear the screen.  */
260 void
261 initialize_info_session (NODE *node, int clear_screen)
262 {
263   char *term_name = getenv ("TERM");
264   terminal_initialize_terminal (term_name);
265
266   if (terminal_is_dumb_p)
267     {
268       if (!term_name)
269         term_name = "dumb";
270
271       info_error (msg_term_too_dumb, term_name, NULL);
272       xexit (1);
273     }
274
275   if (clear_screen)
276     {
277       terminal_prep_terminal ();
278       terminal_clear_screen ();
279     }
280
281   initialize_info_keymaps ();
282   window_initialize_windows (screenwidth, screenheight);
283   initialize_info_signal_handler ();
284   display_initialize_display (screenwidth, screenheight);
285   info_set_node_of_window (0, active_window, node);
286
287   /* Tell the window system how to notify us when a window needs to be
288      asynchronously deleted (e.g., user resizes window very small). */
289   window_deletion_notifier = (VFunction *) forget_window_and_nodes;
290
291   /* If input has not been redirected yet, make it come from unbuffered
292      standard input. */
293   if (!info_input_stream)
294     {
295       setbuf (stdin, NULL);
296       info_input_stream = stdin;
297     }
298
299   info_windows_initialized_p = 1;
300 }
301
302 /* Tell Info that input is coming from the file FILENAME. */
303 void
304 info_set_input_from_file (char *filename)
305 {
306   FILE *stream;
307
308   /* Input may include binary characters.  */
309   stream = fopen (filename, FOPEN_RBIN);
310
311   if (!stream)
312     return;
313
314   if ((info_input_stream != NULL) &&
315       (info_input_stream != stdin))
316     fclose (info_input_stream);
317
318   info_input_stream = stream;
319
320   if (stream != stdin)
321     display_inhibited = 1;
322 }
323
324 /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
325 static INFO_WINDOW *
326 get_info_window_of_window (WINDOW *window)
327 {
328   register int i;
329   INFO_WINDOW *info_win = NULL;
330
331   for (i = 0; info_windows && (info_win = info_windows[i]); i++)
332     if (info_win->window == window)
333       break;
334
335   return info_win;
336 }
337
338 /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
339    values if the window and node are the same as the current one being
340    displayed. */
341 void
342 set_remembered_pagetop_and_point (WINDOW *window)
343 {
344   INFO_WINDOW *info_win;
345
346   info_win = get_info_window_of_window (window);
347
348   if (!info_win)
349     return;
350
351   if (info_win->nodes_index &&
352       (info_win->nodes[info_win->current] == window->node))
353     {
354       info_win->pagetops[info_win->current] = window->pagetop;
355       info_win->points[info_win->current] = window->point;
356     }
357 }
358
359 void
360 remember_window_and_node (WINDOW *window, NODE *node)
361 {
362   /* See if we already have this window in our list. */
363   INFO_WINDOW *info_win = get_info_window_of_window (window);
364
365   /* If the window wasn't already on our list, then make a new entry. */
366   if (!info_win)
367     {
368       info_win = xmalloc (sizeof (INFO_WINDOW));
369       info_win->window = window;
370       info_win->nodes = NULL;
371       info_win->pagetops = NULL;
372       info_win->points = NULL;
373       info_win->current = 0;
374       info_win->nodes_index = 0;
375       info_win->nodes_slots = 0;
376
377       add_pointer_to_array (info_win, info_windows_index, info_windows,
378                             info_windows_slots, 10, INFO_WINDOW *);
379     }
380
381   /* If this node, the current pagetop, and the current point are the
382      same as the current saved node and pagetop, don't really add this to
383      the list of history nodes.  This may happen only at the very
384      beginning of the program, I'm not sure.  --karl  */
385   if (info_win->nodes
386       && info_win->current >= 0
387       && info_win->nodes[info_win->current]->contents == node->contents
388       && info_win->pagetops[info_win->current] == window->pagetop
389       && info_win->points[info_win->current] == window->point)
390   return;
391
392   /* Remember this node, the currently displayed pagetop, and the current
393      location of point in this window.  Because we are updating pagetops
394      and points as well as nodes, it is more efficient to avoid the
395      add_pointer_to_array macro here. */
396   if (info_win->nodes_index + 2 >= info_win->nodes_slots)
397     {
398       info_win->nodes_slots += 20;
399       info_win->nodes = (NODE **) xrealloc (info_win->nodes,
400                                       info_win->nodes_slots * sizeof (NODE *));
401       info_win->pagetops = (int *) xrealloc (info_win->pagetops,
402                                       info_win->nodes_slots * sizeof (int));
403       info_win->points = (long *) xrealloc (info_win->points,
404                                       info_win->nodes_slots * sizeof (long));
405     }
406
407   info_win->nodes[info_win->nodes_index] = node;
408   info_win->pagetops[info_win->nodes_index] = window->pagetop;
409   info_win->points[info_win->nodes_index] = window->point;
410   info_win->current = info_win->nodes_index++;
411   info_win->nodes[info_win->nodes_index] = NULL;
412   info_win->pagetops[info_win->nodes_index] = 0;
413   info_win->points[info_win->nodes_index] = 0;
414 }
415
416 #define DEBUG_FORGET_WINDOW_AND_NODES
417 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
418 static void
419 consistency_check_info_windows (void)
420 {
421   register int i;
422
423   for (i = 0; i < info_windows_index; i++)
424     {
425       WINDOW *win;
426
427       for (win = windows; win; win = win->next)
428         if (win == info_windows[i]->window)
429           break;
430
431       if (!win)
432         abort ();
433     }
434 }
435 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
436
437 /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
438 void
439 forget_window_and_nodes (WINDOW *window)
440 {
441   register int i;
442   INFO_WINDOW *info_win = NULL;
443
444   for (i = 0; info_windows && (info_win = info_windows[i]); i++)
445     if (info_win->window == window)
446       break;
447
448   /* If we found the window to forget, then do so. */
449   if (info_win)
450     {
451       while (i < info_windows_index)
452         {
453           info_windows[i] = info_windows[i + 1];
454           i++;
455         }
456
457       info_windows_index--;
458       info_windows[info_windows_index] = NULL;
459
460       if (info_win->nodes)
461         {
462           /* Free the node structures which held onto internal node contents
463              here.  This doesn't free the contents; we have a garbage collector
464              which does that. */
465           for (i = 0; info_win->nodes[i]; i++)
466             if (internal_info_node_p (info_win->nodes[i]))
467               free (info_win->nodes[i]);
468           free (info_win->nodes);
469
470           maybe_free (info_win->pagetops);
471           maybe_free (info_win->points);
472         }
473
474       free (info_win);
475     }
476 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
477   consistency_check_info_windows ();
478 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
479 }
480
481 /* Set WINDOW to show NODE.  Remember the new window in our list of Info
482    windows.  If we are doing automatic footnote display, also try to display
483    the footnotes for this window.  If REMEMBER is nonzero, first call
484    set_remembered_pagetop_and_point.  */
485 void
486 info_set_node_of_window (int remember, WINDOW *window, NODE *node)
487 {
488   if (remember)
489     set_remembered_pagetop_and_point (window);
490
491   /* Put this node into the window. */
492   window_set_node_of_window (window, node);
493
494   /* Remember this node and window in our list of info windows. */
495   remember_window_and_node (window, node);
496
497   /* If doing auto-footnote display/undisplay, show the footnotes belonging
498      to this window's node. */
499   if (auto_footnotes_p)
500     info_get_or_remove_footnotes (window);
501 }
502
503 \f
504 /* **************************************************************** */
505 /*                                                                  */
506 /*                     Info Movement Commands                       */
507 /*                                                                  */
508 /* **************************************************************** */
509
510 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
511    to do so. */
512 void
513 set_window_pagetop (WINDOW *window, int desired_top)
514 {
515   int point_line, old_pagetop;
516
517   if (desired_top < 0)
518     desired_top = 0;
519   else if (desired_top > window->line_count)
520     desired_top = window->line_count - 1;
521
522   if (window->pagetop == desired_top)
523     return;
524
525   old_pagetop = window->pagetop;
526   window->pagetop = desired_top;
527
528   /* Make sure that point appears in this window. */
529   point_line = window_line_of_point (window);
530   if ((point_line < window->pagetop) ||
531       ((point_line - window->pagetop) > window->height - 1))
532     window->point =
533       window->line_starts[window->pagetop] - window->node->contents;
534
535   window->flags |= W_UpdateWindow;
536
537   /* Find out which direction to scroll, and scroll the window in that
538      direction.  Do this only if there would be a savings in redisplay
539      time.  This is true if the amount to scroll is less than the height
540      of the window, and if the number of lines scrolled would be greater
541      than 10 % of the window's height. */
542   if (old_pagetop < desired_top)
543     {
544       int start, end, amount;
545
546       amount = desired_top - old_pagetop;
547
548       if ((amount >= window->height) ||
549           (((window->height - amount) * 10) < window->height))
550         return;
551
552       start = amount + window->first_row;
553       end = window->height + window->first_row;
554
555       display_scroll_display (start, end, -amount);
556     }
557   else
558     {
559       int start, end, amount;
560
561       amount = old_pagetop - desired_top;
562
563       if ((amount >= window->height) ||
564           (((window->height - amount) * 10) < window->height))
565         return;
566
567       start = window->first_row;
568       end = (window->first_row + window->height) - amount;
569       display_scroll_display (start, end, amount);
570     }
571 }
572
573 /* Immediately make WINDOW->point visible on the screen, and move the
574    terminal cursor there. */
575 static void
576 info_show_point (WINDOW *window)
577 {
578   int old_pagetop;
579
580   old_pagetop = window->pagetop;
581   window_adjust_pagetop (window);
582   if (old_pagetop != window->pagetop)
583     {
584       int new_pagetop;
585
586       new_pagetop = window->pagetop;
587       window->pagetop = old_pagetop;
588       set_window_pagetop (window, new_pagetop);
589     }
590
591   if (window->flags & W_UpdateWindow)
592     display_update_one_window (window);
593
594   display_cursor_at_point (window);
595 }
596
597 /* Move WINDOW->point from OLD line index to NEW line index. */
598 static void
599 move_to_new_line (int old, int new, WINDOW *window)
600 {
601   if (old == -1)
602     {
603       info_error (msg_cant_find_point, NULL, NULL);
604     }
605   else
606     {
607       int goal;
608
609       if (new >= window->line_count || new < 0)
610         return;
611       
612       goal = window_get_goal_column (window);
613       window->goal_column = goal;
614
615       window->point = window->line_starts[new] - window->node->contents;
616       window->point += window_chars_to_goal (window, goal);
617       info_show_point (window);
618     }
619 }
620
621 static int forward_move_node_structure (WINDOW *window, int behaviour);
622 static int backward_move_node_structure (WINDOW *window, int behaviour);
623
624 /* Move WINDOW's point down to the next line if possible. */
625 DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
626 {
627   int old_line, new_line;
628
629   if (count < 0)
630     info_prev_line (window, -count, key);
631   else
632     while (count)
633       {
634         int diff;
635
636         old_line = window_line_of_point (window);
637         diff = window->line_count - old_line;
638         if (diff > count)
639           diff = count;
640
641         count -= diff;
642         new_line = old_line + diff;
643         if (new_line >= window->line_count)
644           {
645             if (cursor_movement_scrolls_p)
646               {
647                 if (forward_move_node_structure (window,
648                                                  info_scroll_behaviour))
649                   break;
650                 move_to_new_line (0, 0, window);
651               }
652             else
653               break;
654           }
655         else
656           move_to_new_line (old_line, new_line, window);
657       }
658 }
659
660 /* Move WINDOW's point up to the previous line if possible. */
661 DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
662 {
663   int old_line, new_line;
664
665   if (count < 0)
666     info_next_line (window, -count, key);
667   else
668     while (count)
669       {
670         int diff;
671         
672         old_line = window_line_of_point (window);
673         diff = old_line + 1;
674         if (diff > count)
675           diff = count;
676         
677         count -= diff;
678         new_line = old_line - diff;
679         
680         if (new_line < 0
681             && cursor_movement_scrolls_p)
682           {
683             if (backward_move_node_structure (window, info_scroll_behaviour))
684               break;
685             if (window->line_count > window->height)
686               set_window_pagetop (window, window->line_count - window->height);
687             move_to_new_line (window->line_count,
688                               window->line_count - 1, window);
689           }
690         else
691           move_to_new_line (old_line, new_line, window);
692       }
693 }
694
695 /* Return true if POINT sits on a newline character. */
696 static int
697 _looking_at_newline (WINDOW *win, long point)
698 {
699   mbi_iterator_t iter;
700
701   mbi_init (iter, win->node->contents + point,
702             win->node->nodelen - point);
703   mbi_avail (iter);
704   return mbi_cur (iter).wc_valid && mbi_cur (iter).wc == '\n';
705 }
706
707 /* Advance point of WIN to the beginning of the next logical line.
708    Return 1 if there is no next line. */
709 static int
710 point_next_line (WINDOW *win)
711 {
712   int line = window_line_of_point (win);
713   if (line + 1 >= win->line_count)
714     return 1;
715   win->point = win->line_starts[line + 1] - win->node->contents;
716   window_compute_line_map (win);
717   return 0;
718 }
719
720 /* Move point of WIN to the beginning of the previous logical
721    line.
722    Return 1 if there is no previous line. */
723 static int
724 point_prev_line (WINDOW *win)
725 {
726   int line = window_line_of_point (win);
727   if (line == 0)
728     return 1;
729   win->point = win->line_starts[line - 1] - win->node->contents;
730   window_compute_line_map (win);
731   return 0;
732 }
733
734 /* Advance point to the next multibyte character.  Return 1 if this would
735    cause pointing past the end of node buffer. */
736 static int
737 point_forward_char (WINDOW *win)
738 {
739   long point = win->point;
740   int col;
741
742   window_compute_line_map (win);
743   col = window_point_to_column (win, point, &point) + 1;
744   if (col >= win->line_map.used)
745     {
746       if (point_next_line (win))
747         return 1;
748       col = 0;
749     }
750   win->point = win->line_map.map[col];
751   return 0;
752 }
753
754 /* Set point to the previous multibyte character.
755    Return 1 if already on the beginning of node buffer. */
756 static int
757 point_backward_char (WINDOW *win)
758 {
759   long point = win->point;
760   int col;
761
762   window_compute_line_map (win);
763   col = window_point_to_column (win, point, &point);
764   for (; col >= 0 && win->line_map.map[col] == point; col--)
765     ;
766   if (col < 0)
767     {
768       if (point_prev_line (win))
769         return 1;
770       col = win->line_map.used - 1;
771     }
772   win->point = win->line_map.map[col];
773   return 0;
774 }
775
776 /* Skip forward any white space characters starting from column *PCOL in
777    the current line, advancing line if necessary.  Return 1 if going past
778    the end of node buffer. */
779 static int
780 point_skip_ws_forward (WINDOW *win, int *pcol)
781 {
782   mbi_iterator_t iter;
783   int col = *pcol;
784
785   while (1)
786     {
787       char *buffer = win->node->contents;
788       size_t buflen = win->node->nodelen;
789
790       for (; col < win->line_map.used; col++)
791         {
792           mbi_init (iter, buffer + win->line_map.map[col],
793                     buflen - win->line_map.map[col]);
794           mbi_avail (iter);
795           if (!mbi_cur (iter).wc_valid || iswalnum (mbi_cur (iter).wc))
796             {
797               *pcol = col;
798               return 0;
799             }
800         }
801       if (point_next_line (win))
802         return 1;
803       col = 0;
804     }
805   return 1;
806 }
807
808 /* Skip backward any white space characters starting from column *PCOL in
809    the current line, retracting line if necessary.  Return 1 if going
810    before the beginning of node buffer. */
811 static int
812 point_skip_ws_backward (WINDOW *win, int *pcol)
813 {
814   mbi_iterator_t iter;
815   int col = *pcol;
816
817   while (1)
818     {
819       char *buffer = win->node->contents;
820       size_t buflen = win->node->nodelen;
821
822       for (; col > 0; col--)
823         {
824           mbi_init (iter, buffer + win->line_map.map[col],
825                     buflen - win->line_map.map[col]);
826           mbi_avail (iter);
827           if (!mbi_cur (iter).wc_valid || iswalnum (mbi_cur (iter).wc))
828             {
829               *pcol = col;
830               return 0;
831             }
832         }
833       if (point_prev_line (win))
834         return 1;
835       col = win->line_map.used - 1;
836     }
837   return 1;
838 }
839
840 /* Advance window point to the beginning of the next word.  Return 1
841    if there are no more words in the buffer. */
842 static int
843 point_forward_word (WINDOW *win)
844 {
845   mbi_iterator_t iter;
846   int col;
847
848   window_compute_line_map (win);
849   col = window_point_to_column (win, win->point, &win->point);
850
851   if (point_skip_ws_forward (win, &col))
852     return 1;
853
854   while (1)
855     {
856       char *buffer = win->node->contents;
857       size_t buflen = win->node->nodelen;
858       
859       for (; col < win->line_map.used; col++)
860         {
861           mbi_init (iter, buffer + win->line_map.map[col],
862                     buflen - win->line_map.map[col]);
863           mbi_avail (iter);
864           if (!(mbi_cur (iter).wc_valid && iswalnum (mbi_cur (iter).wc)))
865             {
866               if (point_skip_ws_forward (win, &col))
867                 return 1;
868               win->point = win->line_map.map[col];
869               return 0;
870             }
871         }
872       if (point_next_line (win))
873         return 1;
874       col = 0;
875     }
876   return 1;
877 }
878
879 /* Set window point to the beginning of the previous word.  Return 1
880    if looking at the very first word in the buffer. */
881 static int
882 point_backward_word (WINDOW *win)
883 {
884   mbi_iterator_t iter;
885   int col;
886
887   window_compute_line_map (win);
888   col = window_point_to_column (win, win->point, &win->point);
889
890   while (1)
891     {
892       long point;
893       char *buffer;
894       size_t buflen;
895
896       if (col <= 0)
897         {
898           if (point_prev_line (win))
899             return 1;
900           col = win->line_map.used;
901         }
902       col--;
903       if (point_skip_ws_backward (win, &col))
904         return 1;
905
906       buffer = win->node->contents;
907       buflen = win->node->nodelen;
908
909       for (; col >= 0; col--)
910         {
911           mbi_init (iter, buffer + win->line_map.map[col],
912                     buflen - win->line_map.map[col]);
913           mbi_avail (iter);
914           if (!(mbi_cur (iter).wc_valid && iswalnum (mbi_cur (iter).wc)))
915             {
916               win->point = win->line_map.map[col+1];
917               return 0;
918             }
919         }
920       point = win->line_map.map[0] - 1;
921       if (point > 0 && _looking_at_newline (win, point))
922         {
923           win->point = win->line_map.map[0];
924           return 0;
925         }
926     }
927   return 1;
928 }
929
930 /* Move WINDOW's point to the end of the true line. */
931 DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
932 {
933   int point = window_end_of_line (window);
934   if (point != window->point)
935     {
936       window->point = point;
937       info_show_point (window);
938     }
939 }
940
941 /* Move WINDOW's point to the beginning of the true line. */
942 DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
943 {
944   int old_point = window->point;
945   int point;
946   
947   while (1)
948     {
949       window_compute_line_map (window);
950       point = window->line_map.map[0];
951       if (point == 0 || _looking_at_newline (window, point-1))
952         break;
953       point_prev_line (window);
954     }
955
956   if (point != old_point)
957     {
958       window->point = point;
959       info_show_point (window);
960     }
961   else
962     window->point = old_point;
963 }
964
965 /* Move point forward in the node. */
966 DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
967 {
968   if (count < 0)
969     info_backward_char (window, -count, key);
970   else
971     {
972       while (count)
973         {
974           if (point_forward_char (window))
975             {
976               if (cursor_movement_scrolls_p
977                   && forward_move_node_structure (window,
978                                                   info_scroll_behaviour) == 0)
979                 window->point = 0;
980               else
981                 {
982                   window->point = window->node->nodelen - 1;
983                   break;
984                 }
985             }
986           count--;
987         }
988       info_show_point (window);
989     }
990 }
991
992 /* Move point backward in the node. */
993 DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
994 {
995   if (count < 0)
996     info_forward_char (window, -count, key);
997   else
998     {
999       while (count)
1000         {
1001           if (point_backward_char (window))
1002             {
1003               if (cursor_movement_scrolls_p
1004                   && backward_move_node_structure (window,
1005                                                    info_scroll_behaviour) == 0)
1006                 {
1007                   window->point = window->node->nodelen - 1;
1008                   if (window->line_count > window->height)
1009                     set_window_pagetop (window,
1010                                         window->line_count - window->height);
1011                 }
1012               else
1013                 {
1014                   window->point = 0;
1015                   break;
1016                 }
1017             }
1018           count--;
1019         }
1020       info_show_point (window);
1021     }
1022 }
1023
1024 /* Move forward a word in this node. */
1025 DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
1026 {
1027   if (count < 0)
1028     {
1029       info_backward_word (window, -count, key);
1030       return;
1031     }
1032
1033   while (count)
1034     {
1035       if (point_forward_word (window))
1036         {
1037           if (cursor_movement_scrolls_p
1038               && forward_move_node_structure (window,
1039                                               info_scroll_behaviour) == 0)
1040             window->point = 0;
1041           else
1042             return;
1043         }
1044       --count;
1045     }
1046   info_show_point (window);
1047 }
1048
1049 DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
1050 {
1051   if (count < 0)
1052     {
1053       info_forward_word (window, -count, key);
1054       return;
1055     }
1056
1057   while (count)
1058     {
1059       if (point_backward_word (window))
1060         {
1061           if (cursor_movement_scrolls_p
1062               && backward_move_node_structure (window,
1063                                                info_scroll_behaviour) == 0)
1064             {
1065               if (window->line_count > window->height)
1066                 set_window_pagetop (window,
1067                                     window->line_count - window->height);
1068               window->point = window->node->nodelen;
1069             }
1070           else
1071             break;
1072         }
1073       --count;
1074     }
1075   info_show_point (window);
1076 }
1077
1078 /* Variable controlling the behaviour of default scrolling when you are
1079    already at the bottom of a node.  Possible values are defined in session.h.
1080    The meanings are:
1081
1082    IS_Continuous        Try to get first menu item, or failing that, the
1083                         "Next:" pointer, or failing that, the "Up:" and
1084                         "Next:" of the up.
1085    IS_NextOnly          Try to get "Next:" menu item.
1086    IS_PageOnly          Simply give up at the bottom of a node. */
1087
1088 int info_scroll_behaviour = IS_Continuous;
1089
1090 /* Choices used by the completer when reading a value for the user-visible
1091    variable "scroll-behaviour". */
1092 char *info_scroll_choices[] = {
1093   "Continuous", "Next Only", "Page Only", NULL
1094 };
1095
1096 /* Controls whether scroll-behavior affects line movement commands */
1097 int cursor_movement_scrolls_p = 1;
1098
1099 /* Choices for the scroll-last-node variable */
1100 char *scroll_last_node_choices[] = {
1101   "Stop", "Scroll", "Top", NULL
1102 };
1103
1104 /* Controls what to do when a scrolling command is issued at the end of the
1105    last node. */
1106 int scroll_last_node = SLN_Stop;
1107
1108 /* Default window sizes for scrolling commands.  */
1109 int default_window_size = -1;   /* meaning 1 window-full */
1110 int default_scroll_size = -1;   /* meaning half screen size */
1111
1112 #define INFO_LABEL_FOUND() \
1113   (info_parsed_nodename || (info_parsed_filename \
1114                             && !is_dir_name (info_parsed_filename)))
1115
1116 static int
1117 last_node_p (NODE *node)
1118 {
1119   info_next_label_of_node (node);
1120   if (!INFO_LABEL_FOUND ())
1121     {
1122       info_up_label_of_node (node);
1123       return !INFO_LABEL_FOUND () || strcmp (info_parsed_nodename, "Top") == 0;
1124     }
1125   return 0;
1126 }
1127
1128 /* Move to 1st menu item, Next, Up/Next, or error in this window. */
1129 static int
1130 forward_move_node_structure (WINDOW *window, int behaviour)
1131 {
1132   switch (behaviour)
1133     {
1134     case IS_PageOnly:
1135       info_error (msg_at_node_bottom, NULL, NULL);
1136       return 1;
1137
1138     case IS_NextOnly:
1139       info_next_label_of_node (window->node);
1140       if (!info_parsed_nodename && !info_parsed_filename)
1141         {
1142           info_error (msg_no_pointer, (char *) _("Next"), NULL);
1143           return 1;
1144         }
1145       else
1146         {
1147           info_handle_pointer ("Next", window);
1148         }
1149       break;
1150
1151     case IS_Continuous:
1152       {
1153         if (last_node_p (window->node))
1154           {
1155             switch (scroll_last_node)
1156               {
1157               case SLN_Stop:
1158                 info_error (_("No more nodes within this document."),
1159                             NULL, NULL);
1160                 return 1;
1161                 
1162               case SLN_Scroll:
1163                 break;
1164                 
1165               case SLN_Top:
1166                 info_top_node (window, 1, 0);
1167                 return 0;
1168                 
1169               default:
1170                 abort ();
1171               }
1172           }
1173         
1174         /* First things first.  If this node contains a menu, move down
1175            into the menu. */
1176         {
1177           REFERENCE **menu;
1178
1179           menu = info_menu_of_node (window->node);
1180           
1181           if (menu)
1182             {
1183               info_free_references (menu);
1184               info_menu_digit (window, 1, '1');
1185               return 0;
1186             }
1187         }
1188
1189         /* Okay, this node does not contain a menu.  If it contains a
1190            "Next:" pointer, use that. */
1191         info_next_label_of_node (window->node);
1192         if (INFO_LABEL_FOUND ())
1193           {
1194             info_handle_pointer ("Next", window);
1195             return 0;
1196           }
1197
1198         /* Okay, there wasn't a "Next:" for this node.  Move "Up:" until we
1199            can move "Next:".  If that isn't possible, complain that there
1200            are no more nodes. */
1201         {
1202           int up_counter, old_current;
1203           INFO_WINDOW *info_win;
1204
1205           /* Remember the current node and location. */
1206           info_win = get_info_window_of_window (window);
1207           old_current = info_win->current;
1208
1209           /* Back up through the "Up:" pointers until we have found a "Next:"
1210              that isn't the same as the first menu item found in that node. */
1211           up_counter = 0;
1212           while (!info_error_was_printed)
1213             {
1214               info_up_label_of_node (window->node);
1215               if (INFO_LABEL_FOUND ())
1216                 {
1217                   info_handle_pointer ("Up", window);
1218                   if (info_error_was_printed)
1219                     continue;
1220
1221                   up_counter++;
1222
1223                   info_next_label_of_node (window->node);
1224
1225                   /* If no "Next" pointer, keep backing up. */
1226                   if (!INFO_LABEL_FOUND ())
1227                     continue;
1228
1229                   /* If this node's first menu item is the same as this node's
1230                      Next pointer, keep backing up. */
1231                   if (!info_parsed_filename)
1232                     {
1233                       REFERENCE **menu;
1234                       char *next_nodename;
1235
1236                       /* Remember the name of the Next node, since reading
1237                          the menu can overwrite the contents of the
1238                          info_parsed_xxx strings. */
1239                       next_nodename = xstrdup (info_parsed_nodename);
1240
1241                       menu = info_menu_of_node (window->node);
1242                       if (menu &&
1243                           (strcmp
1244                            (menu[0]->nodename, next_nodename) == 0))
1245                         {
1246                           info_free_references (menu);
1247                           free (next_nodename);
1248                           continue;
1249                         }
1250                       else
1251                         {
1252                           /* Restore the world to where it was before
1253                              reading the menu contents. */
1254                           info_free_references (menu);
1255                           free (next_nodename);
1256                           info_next_label_of_node (window->node);
1257                         }
1258                     }
1259
1260                   /* This node has a "Next" pointer, and it is not the
1261                      same as the first menu item found in this node. */
1262                   info_handle_pointer ("Next", window);
1263                   return 0;
1264                 }
1265               else
1266                 {
1267                   /* No more "Up" pointers.  Print an error, and call it
1268                      quits. */
1269                   register int i;
1270
1271                   for (i = 0; i < up_counter; i++)
1272                     {
1273                       info_win->nodes_index--;
1274                       free (info_win->nodes[info_win->nodes_index]);
1275                       info_win->nodes[info_win->nodes_index] = NULL;
1276                     }
1277                   info_win->current = old_current;
1278                   window->node = info_win->nodes[old_current];
1279                   window->pagetop = info_win->pagetops[old_current];
1280                   window->point = info_win->points[old_current];
1281                   recalculate_line_starts (window);
1282                   window->flags |= W_UpdateWindow;
1283                   info_error (_("No more nodes within this document."),
1284                       NULL, NULL);
1285                   return 1;
1286                 }
1287             }
1288         }
1289         break;
1290       }
1291     }
1292   return info_error_was_printed; /*FIXME*/
1293 }
1294
1295 /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
1296 static int
1297 backward_move_node_structure (WINDOW *window, int behaviour)
1298 {
1299   switch (behaviour)
1300     {
1301     case IS_PageOnly:
1302       info_error (msg_at_node_top, NULL, NULL);
1303       return 1;
1304
1305     case IS_NextOnly:
1306       info_prev_label_of_node (window->node);
1307       if (!info_parsed_nodename && !info_parsed_filename)
1308         {
1309           info_error (_("No `Prev' for this node."), NULL, NULL);
1310           return 1;
1311         }
1312       else
1313         {
1314           info_handle_pointer ("Prev", window);
1315         }
1316       break;
1317
1318     case IS_Continuous:
1319       info_prev_label_of_node (window->node);
1320
1321       if (!info_parsed_nodename && (!info_parsed_filename
1322                                     || is_dir_name (info_parsed_filename)))
1323         {
1324           info_up_label_of_node (window->node);
1325           if (!info_parsed_nodename && (!info_parsed_filename
1326                                         || is_dir_name (info_parsed_filename)))
1327             {
1328               info_error (
1329                     _("No `Prev' or `Up' for this node within this document."),
1330                           NULL, NULL);
1331               return 1;
1332             }
1333           else
1334             {
1335               info_handle_pointer ("Up", window);
1336             }
1337         }
1338       else
1339         {
1340           REFERENCE **menu;
1341           int inhibit_menu_traversing = 0;
1342
1343           /* Watch out!  If this node's Prev is the same as the Up, then
1344              move Up.  Otherwise, we could move Prev, and then to the last
1345              menu item in the Prev.  This would cause the user to loop
1346              through a subsection of the info file. */
1347           if (!info_parsed_filename && info_parsed_nodename)
1348             {
1349               char *pnode;
1350
1351               pnode = xstrdup (info_parsed_nodename);
1352               info_up_label_of_node (window->node);
1353
1354               if (!info_parsed_filename && info_parsed_nodename &&
1355                   strcmp (info_parsed_nodename, pnode) == 0)
1356                 {
1357                   /* The nodes are the same.  Inhibit moving to the last
1358                      menu item. */
1359                   free (pnode);
1360                   inhibit_menu_traversing = 1;
1361                 }
1362               else
1363                 {
1364                   free (pnode);
1365                   info_prev_label_of_node (window->node);
1366                 }
1367             }
1368
1369           /* Move to the previous node.  If this node now contains a menu,
1370              and we have not inhibited movement to it, move to the node
1371              corresponding to the last menu item. */
1372           info_handle_pointer ("Prev", window);
1373
1374           if (!inhibit_menu_traversing)
1375             {
1376               while (!info_error_was_printed &&
1377                      (menu = info_menu_of_node (window->node)))
1378                 {
1379                   info_free_references (menu);
1380                   info_menu_digit (window, 1, '0');
1381                 }
1382             }
1383         }
1384       break;
1385     }
1386   return 0;
1387 }
1388
1389 /* Move continuously forward through the node structure of this info file. */
1390 DECLARE_INFO_COMMAND (info_global_next_node,
1391                       _("Move forwards or down through node structure"))
1392 {
1393   if (count < 0)
1394     info_global_prev_node (window, -count, key);
1395   else
1396     {
1397       while (count && !info_error_was_printed)
1398         {
1399           forward_move_node_structure (window, IS_Continuous);
1400           count--;
1401         }
1402     }
1403 }
1404
1405 /* Move continuously backward through the node structure of this info file. */
1406 DECLARE_INFO_COMMAND (info_global_prev_node,
1407                       _("Move backwards or up through node structure"))
1408 {
1409   if (count < 0)
1410     info_global_next_node (window, -count, key);
1411   else
1412     {
1413       while (count && !info_error_was_printed)
1414         {
1415           backward_move_node_structure (window, IS_Continuous);
1416           count--;
1417         }
1418     }
1419 }
1420
1421 static void _scroll_forward(WINDOW *window, int count,
1422     unsigned char key, int behaviour);
1423 static void _scroll_backward(WINDOW *window, int count,
1424     unsigned char key, int behaviour);
1425
1426 static void
1427 _scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour)
1428 {
1429   if (count < 0)
1430     _scroll_backward (window, -count, key, behaviour);
1431   else
1432     {
1433       int desired_top;
1434
1435       /* Without an explicit numeric argument, scroll the bottom two
1436          lines to the top of this window,  Or, if at bottom of window,
1437          and the chosen behaviour is to scroll through nodes get the
1438          "Next" node for this window. */
1439       if (default_window_size > 0)
1440         desired_top = window->pagetop + default_window_size;
1441       else if (!info_explicit_arg && count == 1)
1442         {
1443           desired_top = window->pagetop + (window->height - 2);
1444
1445           /* If there are no more lines to scroll here, error, or get
1446              another node, depending on BEHAVIOUR. */
1447           if (desired_top > window->line_count)
1448             {
1449               if (forward_move_node_structure (window, behaviour))
1450                 info_end_of_node (window, 1, 0);
1451               return;
1452             }
1453         }
1454       else
1455         desired_top = window->pagetop + count;
1456
1457       if (desired_top >= window->line_count)
1458         desired_top = window->line_count - 2;
1459
1460       if (window->pagetop > desired_top)
1461         return;
1462       else
1463         set_window_pagetop (window, desired_top);
1464     }
1465 }
1466
1467 static void
1468 _scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour)
1469 {
1470   if (count < 0)
1471     _scroll_forward (window, -count, key, behaviour);
1472   else
1473     {
1474       int desired_top;
1475
1476       /* Without an explicit numeric argument, scroll the top two lines
1477          to the bottom of this window, or, depending on the selected
1478          behaviour, move to the previous, or Up'th node. */
1479       if (default_window_size > 0)
1480         desired_top = window->pagetop - default_window_size;
1481       else if (!info_explicit_arg && count == 1)
1482         {
1483           desired_top = window->pagetop - (window->height - 2);
1484           
1485           if ((desired_top < 0) && (window->pagetop == 0))
1486             {
1487               if ((backward_move_node_structure (window, behaviour) == 0)
1488                   && (cursor_movement_scrolls_p))
1489                 info_end_of_node (window, 1, 0);
1490               window->point = (window->line_starts[window->pagetop]
1491                                - window->node->contents);
1492               return;
1493             }
1494         }
1495       else
1496         desired_top = window->pagetop - count;
1497
1498       if (desired_top < 0)
1499         desired_top = 0;
1500
1501       set_window_pagetop (window, desired_top);
1502       window->point = (window->line_starts[window->pagetop]
1503                        - window->node->contents);
1504     }
1505 }
1506
1507 /* Show the next screen of WINDOW's node. */
1508 DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
1509 {
1510   _scroll_forward (window, count, key, info_scroll_behaviour);
1511 }
1512
1513 /* Like info_scroll_forward, but sets default_window_size as a side
1514    effect.  */
1515 DECLARE_INFO_COMMAND (info_scroll_forward_set_window,
1516                       _("Scroll forward in this window and set default window size"))
1517 {
1518   if (info_explicit_arg)
1519     default_window_size = count;
1520   _scroll_forward (window, count, key, info_scroll_behaviour);
1521 }
1522
1523 /* Show the next screen of WINDOW's node but never advance to next node. */
1524 DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))
1525 {
1526   _scroll_forward (window, count, key, IS_PageOnly);
1527 }
1528
1529 /* Like info_scroll_forward_page_only, but sets default_window_size as a side
1530    effect.  */
1531 DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,
1532                       _("Scroll forward in this window staying within node and set default window size"))
1533 {
1534   if (info_explicit_arg)
1535     default_window_size = count;
1536   _scroll_forward (window, count, key, IS_PageOnly);
1537 }
1538
1539 /* Show the previous screen of WINDOW's node. */
1540 DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
1541 {
1542   _scroll_backward (window, count, key, info_scroll_behaviour);
1543 }
1544
1545 /* Like info_scroll_backward, but sets default_window_size as a side
1546    effect.  */
1547 DECLARE_INFO_COMMAND (info_scroll_backward_set_window,
1548                       _("Scroll backward in this window and set default window size"))
1549 {
1550   if (info_explicit_arg)
1551     default_window_size = count;
1552   _scroll_backward (window, count, key, info_scroll_behaviour);
1553 }
1554
1555 /* Show the previous screen of WINDOW's node but never move to previous
1556    node. */
1557 DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))
1558 {
1559   _scroll_backward (window, count, key, IS_PageOnly);
1560 }
1561
1562 /* Like info_scroll_backward_page_only, but sets default_window_size as a side
1563    effect.  */
1564 DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,
1565                       _("Scroll backward in this window staying within node and set default window size"))
1566 {
1567   if (info_explicit_arg)
1568     default_window_size = count;
1569   _scroll_backward (window, count, key, IS_PageOnly);
1570 }
1571
1572 /* Move to the beginning of the node. */
1573 DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1574 {
1575   window->pagetop = window->point = 0;
1576   window->flags |= W_UpdateWindow;
1577 }
1578
1579 /* Move to the end of the node. */
1580 DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1581 {
1582   window->point = window->node->nodelen - 1;
1583   info_show_point (window);
1584 }
1585
1586 /* Scroll the window forward by N lines.  */
1587 DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
1588 {
1589   if (count < 0)
1590     info_up_line (window, -count, key);
1591   else
1592     {
1593       int desired_top = window->pagetop + count;
1594
1595       if (desired_top >= window->line_count)
1596         desired_top = window->line_count - 2;
1597
1598       if (window->pagetop <= desired_top)
1599         set_window_pagetop (window, desired_top);
1600     }
1601 }
1602
1603 /* Scroll the window backward by N lines.  */
1604 DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
1605 {
1606   if (count < 0)
1607     info_down_line (window, -count, key);
1608   else
1609     {
1610       int desired_top = window->pagetop - count;
1611
1612       if (desired_top < 0)
1613         desired_top = 0;
1614
1615       set_window_pagetop (window, desired_top);
1616     }
1617 }
1618
1619 /* Scroll the window forward by N lines and remember N as default for
1620    subsequent commands.  */
1621 DECLARE_INFO_COMMAND (info_scroll_half_screen_down,
1622                       _("Scroll down by half screen size"))
1623 {
1624   if (count < 0)
1625     info_scroll_half_screen_up (window, -count, key);
1626   else
1627     {
1628       int scroll_size = (the_screen->height + 1) / 2;
1629       int desired_top;
1630
1631       if (info_explicit_arg)
1632         default_scroll_size = count;
1633       if (default_scroll_size > 0)
1634         scroll_size = default_scroll_size;
1635
1636       desired_top = window->pagetop + scroll_size;
1637       if (desired_top >= window->line_count)
1638         desired_top = window->line_count - 2;
1639
1640       if (window->pagetop <= desired_top)
1641         set_window_pagetop (window, desired_top);
1642     }
1643 }
1644
1645 /* Scroll the window backward by N lines and remember N as default for
1646    subsequent commands.  */
1647 DECLARE_INFO_COMMAND (info_scroll_half_screen_up,
1648                       _("Scroll up by half screen size"))
1649 {
1650   if (count < 0)
1651     info_scroll_half_screen_down (window, -count, key);
1652   else
1653     {
1654       int scroll_size = (the_screen->height + 1) / 2;
1655       int desired_top;
1656
1657       if (info_explicit_arg)
1658         default_scroll_size = count;
1659       if (default_scroll_size > 0)
1660         scroll_size = default_scroll_size;
1661
1662       desired_top = window->pagetop - scroll_size;
1663       if (desired_top < 0)
1664         desired_top = 0;
1665
1666       set_window_pagetop (window, desired_top);
1667     }
1668 }
1669 \f
1670 /* **************************************************************** */
1671 /*                                                                  */
1672 /*                 Commands for Manipulating Windows                */
1673 /*                                                                  */
1674 /* **************************************************************** */
1675
1676 /* Make the next window in the chain be the active window. */
1677 DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1678 {
1679   if (count < 0)
1680     {
1681       info_prev_window (window, -count, key);
1682       return;
1683     }
1684
1685   /* If no other window, error now. */
1686   if (!windows->next && !echo_area_is_active)
1687     {
1688       info_error (msg_one_window, NULL, NULL);
1689       return;
1690     }
1691
1692   while (count--)
1693     {
1694       if (window->next)
1695         window = window->next;
1696       else
1697         {
1698           if (window == the_echo_area || !echo_area_is_active)
1699             window = windows;
1700           else
1701             window = the_echo_area;
1702         }
1703     }
1704
1705   if (active_window != window)
1706     {
1707       if (auto_footnotes_p)
1708         info_get_or_remove_footnotes (window);
1709
1710       window->flags |= W_UpdateWindow;
1711       active_window = window;
1712     }
1713 }
1714
1715 /* Make the previous window in the chain be the active window. */
1716 DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1717 {
1718   if (count < 0)
1719     {
1720       info_next_window (window, -count, key);
1721       return;
1722     }
1723
1724   /* Only one window? */
1725
1726   if (!windows->next && !echo_area_is_active)
1727     {
1728       info_error (msg_one_window, NULL, NULL);
1729       return;
1730     }
1731
1732   while (count--)
1733     {
1734       /* If we are in the echo area, or if the echo area isn't active and we
1735          are in the first window, find the last window in the chain. */
1736       if (window == the_echo_area ||
1737           (window == windows && !echo_area_is_active))
1738         {
1739           register WINDOW *win, *last = NULL;
1740
1741           for (win = windows; win; win = win->next)
1742             last = win;
1743
1744           window = last;
1745         }
1746       else
1747         {
1748           if (window == windows)
1749             window = the_echo_area;
1750           else
1751             window = window->prev;
1752         }
1753     }
1754
1755   if (active_window != window)
1756     {
1757       if (auto_footnotes_p)
1758         info_get_or_remove_footnotes (window);
1759
1760       window->flags |= W_UpdateWindow;
1761       active_window = window;
1762     }
1763 }
1764
1765 /* Split WINDOW into two windows, both showing the same node.  If we
1766    are automatically tiling windows, re-tile after the split. */
1767 DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
1768 {
1769   WINDOW *split, *old_active;
1770   int pagetop;
1771
1772   /* Remember the current pagetop of the window being split.  If it doesn't
1773      change, we can scroll its contents around after the split. */
1774   pagetop = window->pagetop;
1775
1776   /* Make the new window. */
1777   old_active = active_window;
1778   active_window = window;
1779   split = window_make_window (window->node);
1780   active_window = old_active;
1781
1782   if (!split)
1783     {
1784       info_error (msg_win_too_small, NULL, NULL);
1785     }
1786   else
1787     {
1788 #if defined (SPLIT_BEFORE_ACTIVE)
1789       /* Try to scroll the old window into its new postion. */
1790       if (pagetop == window->pagetop)
1791         {
1792           int start, end, amount;
1793
1794           start = split->first_row;
1795           end = start + window->height;
1796           amount = split->height + 1;
1797           display_scroll_display (start, end, amount);
1798         }
1799 #else /* !SPLIT_BEFORE_ACTIVE */
1800       /* Make sure point still appears in the active window. */
1801       info_show_point (window);
1802 #endif /* !SPLIT_BEFORE_ACTIVE */
1803
1804       /* If the window just split was one internal to Info, try to display
1805          something else in it. */
1806       if (internal_info_node_p (split->node))
1807         {
1808           register int i, j;
1809           INFO_WINDOW *iw;
1810           NODE *node = NULL;
1811           char *filename;
1812
1813           for (i = 0; (iw = info_windows[i]); i++)
1814             {
1815               for (j = 0; j < iw->nodes_index; j++)
1816                 if (!internal_info_node_p (iw->nodes[j]))
1817                   {
1818                     if (iw->nodes[j]->parent)
1819                       filename = iw->nodes[j]->parent;
1820                     else
1821                       filename = iw->nodes[j]->filename;
1822
1823                     node = info_get_node (filename, iw->nodes[j]->nodename);
1824                     if (node)
1825                       {
1826                         window_set_node_of_window (split, node);
1827                         i = info_windows_index - 1;
1828                         break;
1829                       }
1830                   }
1831             }
1832         }
1833       split->pagetop = window->pagetop;
1834
1835       if (auto_tiling_p)
1836         window_tile_windows (DONT_TILE_INTERNALS);
1837       else
1838         window_adjust_pagetop (split);
1839
1840       remember_window_and_node (split, split->node);
1841     }
1842 }
1843
1844 /* Delete WINDOW, forgetting the list of last visited nodes.  If we are
1845    automatically displaying footnotes, show or remove the footnotes
1846    window.  If we are automatically tiling windows, re-tile after the
1847    deletion. */
1848 DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
1849 {
1850   if (!windows->next)
1851     {
1852       info_error (msg_cant_kill_last, NULL, NULL);
1853     }
1854   else if (window->flags & W_WindowIsPerm)
1855     {
1856       info_error (_("Cannot delete a permanent window"), NULL, NULL);
1857     }
1858   else
1859     {
1860       info_delete_window_internal (window);
1861
1862       if (auto_footnotes_p)
1863         info_get_or_remove_footnotes (active_window);
1864
1865       if (auto_tiling_p)
1866         window_tile_windows (DONT_TILE_INTERNALS);
1867     }
1868 }
1869
1870 /* Do the physical deletion of WINDOW, and forget this window and
1871    associated nodes. */
1872 void
1873 info_delete_window_internal (WINDOW *window)
1874 {
1875   if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
1876     {
1877       /* We not only delete the window from the display, we forget it from
1878          our list of remembered windows. */
1879       forget_window_and_nodes (window);
1880       window_delete_window (window);
1881
1882       if (echo_area_is_active)
1883         echo_area_inform_of_deleted_window (window);
1884     }
1885 }
1886
1887 /* Just keep WINDOW, deleting all others. */
1888 DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1889 {
1890   int num_deleted;              /* The number of windows we deleted. */
1891   int pagetop, start, end;
1892
1893   /* Remember a few things about this window.  We may be able to speed up
1894      redisplay later by scrolling its contents. */
1895   pagetop = window->pagetop;
1896   start = window->first_row;
1897   end = start + window->height;
1898
1899   num_deleted = 0;
1900
1901   while (1)
1902     {
1903       WINDOW *win;
1904
1905       /* Find an eligible window and delete it.  If no eligible windows
1906          are found, we are done.  A window is eligible for deletion if
1907          is it not permanent, and it is not WINDOW. */
1908       for (win = windows; win; win = win->next)
1909         if (win != window && ((win->flags & W_WindowIsPerm) == 0))
1910           break;
1911
1912       if (!win)
1913         break;
1914
1915       info_delete_window_internal (win);
1916       num_deleted++;
1917     }
1918
1919   /* Scroll the contents of this window into the right place so that the
1920      user doesn't have to wait any longer than necessary for redisplay. */
1921   if (num_deleted)
1922     {
1923       int amount;
1924
1925       amount = (window->first_row - start);
1926       amount -= (window->pagetop - pagetop);
1927       display_scroll_display (start, end, amount);
1928     }
1929
1930   window->flags |= W_UpdateWindow;
1931 }
1932
1933 /* Scroll the "other" window of WINDOW. */
1934 DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1935 {
1936   WINDOW *other;
1937
1938   /* If only one window, give up. */
1939   if (!windows->next)
1940     {
1941       info_error (msg_one_window, NULL, NULL);
1942       return;
1943     }
1944
1945   other = window->next;
1946
1947   if (!other)
1948     other = window->prev;
1949
1950   info_scroll_forward (other, count, key);
1951 }
1952
1953 /* Scroll the "other" window of WINDOW. */
1954 DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
1955                       _("Scroll the other window backward"))
1956 {
1957   info_scroll_other_window (window, -count, key);
1958 }
1959
1960 /* Change the size of WINDOW by AMOUNT. */
1961 DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1962 {
1963   window_change_window_height (window, count);
1964 }
1965
1966 /* When non-zero, tiling takes place automatically when info_split_window
1967    is called. */
1968 int auto_tiling_p = 0;
1969
1970 /* Tile all of the visible windows. */
1971 DECLARE_INFO_COMMAND (info_tile_windows,
1972     _("Divide the available screen space among the visible windows"))
1973 {
1974   window_tile_windows (TILE_INTERNALS);
1975 }
1976
1977 /* Toggle the state of this window's wrapping of lines. */
1978 DECLARE_INFO_COMMAND (info_toggle_wrap,
1979               _("Toggle the state of line wrapping in the current window"))
1980 {
1981   window_toggle_wrap (window);
1982 }
1983
1984 /* Toggle the usage of regular expressions in searches. */
1985 DECLARE_INFO_COMMAND (info_toggle_regexp,
1986               _("Toggle the usage of regular expressions in searches"))
1987 {
1988   use_regex = 1 - use_regex;
1989   window_message_in_echo_area (use_regex
1990                                ? _("Using regular expressions for searches.")
1991                                : _("Using literal strings for searches."),
1992                                NULL, NULL);
1993 }
1994 \f
1995 /* **************************************************************** */
1996 /*                                                                  */
1997 /*                      Info Node Commands                          */
1998 /*                                                                  */
1999 /* **************************************************************** */
2000
2001 /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
2002    filename is not set. */
2003 char *
2004 node_printed_rep (NODE *node)
2005 {
2006   char *rep;
2007
2008   if (node->filename)
2009     {
2010       char *filename
2011        = filename_non_directory (node->parent ? node->parent : node->filename);
2012       rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
2013       sprintf (rep, "(%s)%s", filename, node->nodename);
2014     }
2015   else
2016     rep = node->nodename;
2017
2018   return rep;
2019 }
2020
2021
2022 /* Using WINDOW for various defaults, select the node referenced by ENTRY
2023    in it.  If the node is selected, the window and node are remembered. */
2024 void
2025 info_select_reference (WINDOW *window, REFERENCE *entry)
2026 {
2027   NODE *node;
2028   char *filename, *nodename, *file_system_error;
2029
2030   file_system_error = NULL;
2031
2032   filename = entry->filename;
2033   if (!filename)
2034     filename = window->node->parent;
2035   if (!filename)
2036     filename = window->node->filename;
2037
2038   if (filename)
2039     filename = xstrdup (filename);
2040
2041   if (entry->nodename)
2042     nodename = xstrdup (entry->nodename);
2043   else
2044     nodename = xstrdup ("Top");
2045
2046   node = info_get_node (filename, nodename);
2047
2048   /* Try something a little weird.  If the node couldn't be found, and the
2049      reference was of the form "foo::", see if the entry->label can be found
2050      as a file, with a node of "Top". */
2051   if (!node)
2052     {
2053       if (info_recent_file_error)
2054         file_system_error = xstrdup (info_recent_file_error);
2055
2056       if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
2057         {
2058           node = info_get_node (entry->label, "Top");
2059           if (!node && info_recent_file_error)
2060             {
2061               maybe_free (file_system_error);
2062               file_system_error = xstrdup (info_recent_file_error);
2063             }
2064         }
2065     }
2066
2067   if (!node)
2068     {
2069       if (file_system_error)
2070         info_error (file_system_error, NULL, NULL);
2071       else
2072         info_error (msg_cant_find_node, nodename, NULL);
2073     }
2074
2075   maybe_free (file_system_error);
2076   maybe_free (filename);
2077   maybe_free (nodename);
2078
2079   if (node)
2080     info_set_node_of_window (1, window, node);
2081 }
2082
2083 /* Parse the node specification in LINE using WINDOW to default the filename.
2084    Select the parsed node in WINDOW and remember it, or error if the node
2085    couldn't be found. */
2086 static void
2087 info_parse_and_select (char *line, WINDOW *window)
2088 {
2089   REFERENCE entry;
2090
2091   info_parse_node (line, DONT_SKIP_NEWLINES);
2092
2093   entry.nodename = info_parsed_nodename;
2094   entry.filename = info_parsed_filename;
2095   entry.label = "*info-parse-and-select*";
2096
2097   info_select_reference (window, &entry);
2098 }
2099
2100 /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
2101    are previously filled, try to get the node represented by them into
2102    WINDOW.  The node should have been pointed to by the LABEL pointer of
2103    WINDOW->node. */
2104 static void
2105 info_handle_pointer (char *label, WINDOW *window)
2106 {
2107   if (info_parsed_filename || info_parsed_nodename)
2108     {
2109       char *filename, *nodename;
2110       NODE *node;
2111
2112       filename = nodename = NULL;
2113
2114       if (info_parsed_filename)
2115         filename = xstrdup (info_parsed_filename);
2116       else
2117         {
2118           if (window->node->parent)
2119             filename = xstrdup (window->node->parent);
2120           else if (window->node->filename)
2121             filename = xstrdup (window->node->filename);
2122         }
2123
2124       if (info_parsed_nodename)
2125         nodename = xstrdup (info_parsed_nodename);
2126       else
2127         nodename = xstrdup ("Top");
2128
2129       node = info_get_node (filename, nodename);
2130
2131       if (node)
2132         {
2133           INFO_WINDOW *info_win;
2134
2135           info_win = get_info_window_of_window (window);
2136           if (info_win)
2137             {
2138               info_win->pagetops[info_win->current] = window->pagetop;
2139               info_win->points[info_win->current] = window->point;
2140             }
2141           info_set_node_of_window (1, window, node);
2142         }
2143       else
2144         {
2145           if (info_recent_file_error)
2146             info_error (info_recent_file_error, NULL, NULL);
2147           else
2148             info_error (msg_cant_file_node, filename, nodename);
2149         }
2150
2151       free (filename);
2152       free (nodename);
2153     }
2154   else
2155     {
2156       info_error (msg_no_pointer, label, NULL);
2157     }
2158 }
2159
2160 /* Make WINDOW display the "Next:" node of the node currently being
2161    displayed. */
2162 DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
2163 {
2164   info_next_label_of_node (window->node);
2165   info_handle_pointer ("Next", window);
2166 }
2167
2168 /* Make WINDOW display the "Prev:" node of the node currently being
2169    displayed. */
2170 DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
2171 {
2172   info_prev_label_of_node (window->node);
2173   info_handle_pointer ("Prev", window);
2174 }
2175
2176 /* Make WINDOW display the "Up:" node of the node currently being
2177    displayed. */
2178 DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
2179 {
2180   info_up_label_of_node (window->node);
2181   info_handle_pointer ("Up", window);
2182 }
2183
2184 /* Make WINDOW display the last node of this info file. */
2185 DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
2186 {
2187   register int i;
2188   FILE_BUFFER *fb = file_buffer_of_window (window);
2189   NODE *node = NULL;
2190
2191   if (fb && fb->tags)
2192     {
2193       int last_node_tag_idx = -1;
2194
2195       /* If no explicit argument, or argument of zero, default to the
2196          last node.  */
2197       if (count == 0 || (count == 1 && !info_explicit_arg))
2198         count = -1;
2199       for (i = 0; count && fb->tags[i]; i++)
2200         if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
2201           {
2202             count--;
2203             last_node_tag_idx = i;
2204           }
2205       if (count > 0)
2206         i = last_node_tag_idx + 1;
2207       if (i > 0)
2208         node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
2209     }
2210
2211   if (!node)
2212     info_error (_("This window has no additional nodes"), NULL, NULL);
2213   else
2214     info_set_node_of_window (1, window, node);
2215 }
2216
2217 /* Make WINDOW display the first node of this info file. */
2218 DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
2219 {
2220   FILE_BUFFER *fb = file_buffer_of_window (window);
2221   NODE *node = NULL;
2222
2223   /* If no explicit argument, or argument of zero, default to the
2224      first node.  */
2225   if (count == 0)
2226     count = 1;
2227   if (fb && fb->tags)
2228     {
2229       register int i;
2230       int last_node_tag_idx = -1;
2231
2232       for (i = 0; count && fb->tags[i]; i++)
2233         if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
2234           {
2235             count--;
2236             last_node_tag_idx = i;
2237           }
2238       if (count > 0)
2239         i = last_node_tag_idx + 1;
2240       if (i > 0)
2241         node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
2242     }
2243
2244   if (!node)
2245     info_error (_("This window has no additional nodes"), NULL, NULL);
2246   else
2247     info_set_node_of_window (1, window, node);
2248 }
2249
2250 /* Select the last menu item in WINDOW->node. */
2251 DECLARE_INFO_COMMAND (info_last_menu_item,
2252    _("Select the last item in this node's menu"))
2253 {
2254   info_menu_digit (window, 1, '0');
2255 }
2256
2257 /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
2258 DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
2259 {
2260   register int i, item;
2261   register REFERENCE **menu;
2262
2263   menu = info_menu_of_node (window->node);
2264
2265   if (!menu)
2266     {
2267       info_error (msg_no_menu_node, NULL, NULL);
2268       return;
2269     }
2270
2271   /* We have the menu.  See if there are this many items in it. */
2272   item = key - '0';
2273
2274   /* Special case.  Item "0" is the last item in this menu. */
2275   if (item == 0)
2276     for (i = 0; menu[i + 1]; i++);
2277   else
2278     {
2279       for (i = 0; menu[i]; i++)
2280         if (i == item - 1)
2281           break;
2282     }
2283
2284   if (menu[i])
2285     {
2286       info_select_reference (window, menu[i]);
2287       if (menu[i]->line_number > 0)
2288         info_next_line (window, menu[i]->line_number - 1, key);
2289     }
2290   else
2291     info_error (_("There aren't %d items in this menu."),
2292                 (void *) (long) item, NULL);
2293
2294   info_free_references (menu);
2295   return;
2296 }
2297
2298
2299 \f
2300 /* Return a pointer to the xref in XREF_LIST that is nearest to POS, or
2301    NULL if XREF_LIST is empty.  That is, if POS is within any of the
2302    given xrefs, return that one.  Otherwise, return the one with the
2303    nearest beginning or end.  If there are two that are equidistant,
2304    prefer the one forward.  The return is in newly-allocated memory,
2305    since the caller frees it.
2306    
2307    This is called from info_menu_or_ref_item with XREF_LIST being all
2308    the xrefs in the node, and POS being point.  The ui function that
2309    starts it all off is select-reference-this-line.
2310
2311    This is not the same logic as in info.el.  Info-get-token prefers
2312    searching backwards to searching forwards, and has a hardwired search
2313    limit of 200 chars (in Emacs 21.2).  */
2314
2315 static REFERENCE **
2316 nearest_xref (REFERENCE **xref_list, long int pos)
2317 {
2318   int this_xref;
2319   int nearest = -1;
2320   long best_delta = -1;
2321   
2322   for (this_xref = 0; xref_list[this_xref]; this_xref++)
2323     {
2324       long delta;
2325       REFERENCE *xref = xref_list[this_xref];
2326       if (xref->start <= pos && pos <= xref->end)
2327         { /* POS is within this xref, we're done */
2328           nearest = this_xref;
2329           break;
2330         }
2331       
2332       /* See how far POS is from this xref.  Take into account the
2333          `*Note' that begins the xref, since as far as the user is
2334          concerned, that's where it starts.  */
2335       delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))),
2336                    labs (pos - xref->end));
2337       
2338       /* It's the <= instead of < that makes us choose the forward xref
2339          of POS if two are equidistant.  Of course, because of all the
2340          punctuation surrounding xrefs, it's not necessarily obvious
2341          where one ends.  */
2342       if (delta <= best_delta || best_delta < 0)
2343         {
2344           nearest = this_xref;
2345           best_delta = delta;
2346         }
2347     }
2348   
2349   /* Maybe there was no list to search through.  */
2350   if (nearest < 0)
2351     return NULL;
2352   
2353   /* Ok, we have a nearest xref, make a list of it.  */
2354   {
2355     REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2);
2356     ret[0] = info_copy_reference (xref_list[nearest]);
2357     ret[1] = NULL;
2358     return ret;
2359   }
2360 }
2361
2362
2363 /* Read a menu or followed reference from the user defaulting to the
2364    reference found on the current line, and select that node.  The
2365    reading is done with completion.  BUILDER is the function used
2366    to build the list of references.  ASK_P is non-zero if the user
2367    should be prompted, or zero to select the default item. */
2368 static void
2369 info_menu_or_ref_item (WINDOW *window, int count,
2370     unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p)
2371 {
2372   char *line;
2373   REFERENCE *entry;
2374   REFERENCE *defentry = NULL;
2375   REFERENCE **menu = (*builder) (window->node);
2376
2377   if (!menu)
2378     {
2379       if (builder == info_menu_of_node)
2380         info_error (msg_no_menu_node, NULL, NULL);
2381       else
2382         info_error (msg_no_xref_node, NULL, NULL);
2383       return;
2384     }
2385
2386   /* Default the selected reference to the one which is on the line that
2387      point is in.  */
2388   {
2389     REFERENCE **refs = NULL;
2390     int point_line = window_line_of_point (window);
2391
2392     if (point_line != -1)
2393       {
2394         SEARCH_BINDING binding;
2395
2396         binding.buffer = window->node->contents;
2397         binding.start = window->line_starts[point_line] - binding.buffer;
2398         if (window->line_starts[point_line + 1])
2399           binding.end = window->line_starts[point_line + 1] - binding.buffer;
2400         else
2401           binding.end = window->node->nodelen;
2402         binding.flags = 0;
2403
2404         if (builder == info_menu_of_node)
2405           {
2406             if (point_line)
2407               {
2408                 binding.start--;
2409                 refs = info_menu_items (&binding);
2410               }
2411           }
2412         else
2413           {
2414 #if defined (HANDLE_MAN_PAGES)
2415             if (window->node->flags & N_IsManPage)
2416               refs = manpage_xrefs_in_binding (window->node, &binding);
2417             else
2418 #endif /* HANDLE_MAN_PAGES */
2419               refs = nearest_xref (menu, window->point);
2420           }
2421
2422         if (refs && refs[0])
2423           {
2424             if (strcmp (refs[0]->label, "Menu") != 0
2425                 || builder == info_xrefs_of_node)
2426               {
2427                 int which = 0;
2428
2429                 /* For xrefs, find the closest reference to point,
2430                    unless we only have one reference (as we will if
2431                    we've called nearest_xref above).  It would be better
2432                    to have only one piece of code, but the conditions
2433                    when we call this are tangled.  */
2434                 if (builder == info_xrefs_of_node && refs[1])
2435                   {
2436                     int closest = -1;
2437
2438                     for (; refs[which]; which++)
2439                       {
2440                         if (window->point >= refs[which]->start
2441                             && window->point <= refs[which]->end)
2442                           {
2443                             closest = which;
2444                             break;
2445                           }
2446                         else if (window->point < refs[which]->start)
2447                           break;
2448                       }
2449                     if (which > 0)
2450                       {
2451                         if (closest == -1)
2452                           which--;
2453                         else
2454                           which = closest;
2455                       }
2456                   }
2457
2458                 defentry = xmalloc (sizeof (REFERENCE));
2459                 defentry->label = xstrdup (refs[which]->label);
2460                 defentry->filename = refs[which]->filename;
2461                 defentry->nodename = refs[which]->nodename;
2462                 defentry->line_number = refs[which]->line_number;
2463
2464                 if (defentry->filename)
2465                   defentry->filename = xstrdup (defentry->filename);
2466                 if (defentry->nodename)
2467                   defentry->nodename = xstrdup (defentry->nodename);
2468               }
2469             info_free_references (refs);
2470           }
2471       }
2472   }
2473
2474   /* If we are going to ask the user a question, do it now. */
2475   if (ask_p)
2476     {
2477       char *prompt;
2478
2479       /* Build the prompt string. */
2480       if (builder == info_menu_of_node)
2481         {
2482           if (defentry)
2483             {
2484               prompt = xmalloc (strlen (defentry->label)
2485                                 + strlen (_("Menu item (%s): ")));
2486               sprintf (prompt, _("Menu item (%s): "), defentry->label);
2487             }
2488           else
2489             prompt = xstrdup (_("Menu item: "));
2490         }
2491       else
2492         {
2493           if (defentry)
2494             {
2495               prompt = xmalloc (strlen (defentry->label)
2496                                 + strlen (_("Follow xref (%s): ")));
2497               sprintf (prompt, _("Follow xref (%s): "), defentry->label);
2498             }
2499           else
2500             prompt = xstrdup (_("Follow xref: "));
2501         }
2502
2503       line = info_read_completing_in_echo_area (window, prompt, menu);
2504       free (prompt);
2505
2506       window = active_window;
2507
2508       /* User aborts, just quit. */
2509       if (!line)
2510         {
2511           maybe_free (defentry);
2512           info_free_references (menu);
2513           info_abort_key (window, 0, 0);
2514           return;
2515         }
2516
2517       /* If we had a default and the user accepted it, use that. */
2518       if (!*line)
2519         {
2520           free (line);
2521           if (defentry)
2522             line = xstrdup (defentry->label);
2523           else
2524             line = NULL;
2525         }
2526     }
2527   else
2528     {
2529       /* Not going to ask any questions.  If we have a default entry, use
2530          that, otherwise return. */
2531       if (!defentry)
2532         return;
2533       else
2534         line = xstrdup (defentry->label);
2535     }
2536
2537   if (line)
2538     {
2539       /* It is possible that the references have more than a single
2540          entry with the same label, and also LINE is down-cased, which
2541          complicates matters even more.  Try to be as accurate as we
2542          can: if they've chosen the default, use defentry directly. */
2543       if (defentry && strcmp (line, defentry->label) == 0)
2544         entry = defentry;
2545       else
2546         /* Find the selected label in the references.  If there are
2547            more than one label which matches, find the one that's
2548            closest to point.  */
2549         {
2550           register int i;
2551           int best = -1, min_dist = window->node->nodelen;
2552           REFERENCE *ref;
2553
2554           for (i = 0; menu && (ref = menu[i]); i++)
2555             {
2556               /* Need to use mbscasecmp because LINE is downcased
2557                  inside info_read_completing_in_echo_area.  */
2558               if (mbscasecmp (line, ref->label) == 0)
2559                 {
2560                   /* ref->end is more accurate estimate of position
2561                      for menus than ref->start.  Go figure.  */
2562                   int dist = abs (window->point - ref->end);
2563
2564                   if (dist < min_dist)
2565                     {
2566                       min_dist = dist;
2567                       best = i;
2568                     }
2569                 }
2570             }
2571           if (best != -1)
2572             entry = menu[best];
2573           else
2574             entry = NULL;
2575         }
2576
2577       if (!entry && defentry)
2578         info_error (_("The reference disappeared! (%s)."), line, NULL);
2579       else
2580         {
2581           NODE *orig = window->node;
2582           info_select_reference (window, entry);
2583
2584           if (builder == info_xrefs_of_node && window->node != orig
2585               && !(window->node->flags & N_FromAnchor))
2586             { /* Search for this reference in the node.  */
2587               long offset;
2588               long start;
2589
2590               if (window->line_count > 0)
2591                 start = window->line_starts[1] - window->node->contents;
2592               else
2593                 start = 0;
2594
2595               offset =
2596                 info_target_search_node (window->node, entry->label, start);
2597
2598               if (offset != -1)
2599                 {
2600                   window->point = offset;
2601                   window_adjust_pagetop (window);
2602                 }
2603             }
2604
2605             if (entry->line_number > 0)
2606               /* next_line starts at line 1?  Anyway, the -1 makes it
2607                  move to the right line.  */
2608               info_next_line (window, entry->line_number - 1, key);
2609         }
2610
2611       free (line);
2612       if (defentry)
2613         {
2614           free (defentry->label);
2615           maybe_free (defentry->filename);
2616           maybe_free (defentry->nodename);
2617           free (defentry);
2618         }
2619     }
2620
2621   info_free_references (menu);
2622
2623   if (!info_error_was_printed)
2624     window_clear_echo_area ();
2625 }
2626
2627 /* Read a line (with completion) which is the name of a menu item,
2628    and select that item. */
2629 DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
2630 {
2631   info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2632 }
2633
2634 /* Read a line (with completion) which is the name of a reference to
2635    follow, and select the node. */
2636 DECLARE_INFO_COMMAND
2637   (info_xref_item, _("Read a footnote or cross reference and select its node"))
2638 {
2639   info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2640 }
2641
2642 /* Position the cursor at the start of this node's menu. */
2643 DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
2644 {
2645   SEARCH_BINDING binding;
2646   long position;
2647
2648   binding.buffer = window->node->contents;
2649   binding.start  = 0;
2650   binding.end = window->node->nodelen;
2651   binding.flags = S_FoldCase | S_SkipDest;
2652
2653   position = search (INFO_MENU_LABEL, &binding);
2654
2655   if (position == -1)
2656     info_error (msg_no_menu_node, NULL, NULL);
2657   else
2658     {
2659       window->point = position;
2660       window_adjust_pagetop (window);
2661       window->flags |= W_UpdateWindow;
2662     }
2663 }
2664
2665 /* Visit as many menu items as is possible, each in a separate window. */
2666 DECLARE_INFO_COMMAND (info_visit_menu,
2667   _("Visit as many menu items at once as possible"))
2668 {
2669   register int i;
2670   REFERENCE *entry, **menu;
2671
2672   menu = info_menu_of_node (window->node);
2673
2674   if (!menu)
2675     info_error (msg_no_menu_node, NULL, NULL);
2676
2677   for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2678     {
2679       WINDOW *new;
2680
2681       new = window_make_window (window->node);
2682       window_tile_windows (TILE_INTERNALS);
2683
2684       if (!new)
2685         info_error (msg_win_too_small, NULL, NULL);
2686       else
2687         {
2688           active_window = new;
2689           info_select_reference (new, entry);
2690         }
2691     }
2692 }
2693
2694 /* Read a line of input which is a node name, and go to that node. */
2695 DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
2696 {
2697   char *line;
2698
2699 #define GOTO_COMPLETES
2700 #if defined (GOTO_COMPLETES)
2701   /* Build a completion list of all of the known nodes. */
2702   {
2703     register int fbi, i;
2704     FILE_BUFFER *current;
2705     REFERENCE **items = NULL;
2706     int items_index = 0;
2707     int items_slots = 0;
2708
2709     current = file_buffer_of_window (window);
2710
2711     for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2712       {
2713         FILE_BUFFER *fb;
2714         REFERENCE *entry;
2715         int this_is_the_current_fb;
2716
2717         fb = info_loaded_files[fbi];
2718         this_is_the_current_fb = (current == fb);
2719
2720         entry = xmalloc (sizeof (REFERENCE));
2721         entry->filename = entry->nodename = NULL;
2722         entry->label = xmalloc (4 + strlen (fb->filename));
2723         sprintf (entry->label, "(%s)*", fb->filename);
2724
2725         add_pointer_to_array
2726           (entry, items_index, items, items_slots, 10, REFERENCE *);
2727
2728         if (fb->tags)
2729           {
2730             for (i = 0; fb->tags[i]; i++)
2731               {
2732                 entry = xmalloc (sizeof (REFERENCE));
2733                 entry->filename = entry->nodename = NULL;
2734                 if (this_is_the_current_fb)
2735                   entry->label = xstrdup (fb->tags[i]->nodename);
2736                 else
2737                   {
2738                     entry->label = xmalloc
2739                       (4 + strlen (fb->filename) +
2740                        strlen (fb->tags[i]->nodename));
2741                     sprintf (entry->label, "(%s)%s",
2742                              fb->filename, fb->tags[i]->nodename);
2743                   }
2744
2745                 add_pointer_to_array
2746                   (entry, items_index, items, items_slots, 100, REFERENCE *);
2747               }
2748           }
2749       }
2750     line = info_read_maybe_completing (window, _("Goto node: "),
2751         items);
2752     info_free_references (items);
2753   }
2754 #else /* !GOTO_COMPLETES */
2755   line = info_read_in_echo_area (window, _("Goto node: "));
2756 #endif /* !GOTO_COMPLETES */
2757
2758   /* If the user aborted, quit now. */
2759   if (!line)
2760     {
2761       info_abort_key (window, 0, 0);
2762       return;
2763     }
2764
2765   canonicalize_whitespace (line);
2766
2767   if (*line)
2768     info_parse_and_select (line, window);
2769
2770   free (line);
2771   if (!info_error_was_printed)
2772     window_clear_echo_area ();
2773 }
2774 \f
2775 /* Follow the menu list in MENUS (list of strings terminated by a NULL
2776    entry) from INITIAL_NODE.  If can't continue at any point (no menu or
2777    no menu entry for the next item), return the node so far -- that
2778    might be INITIAL_NODE itself.  If error, *ERRSTR and *ERRARG[12] will
2779    be set to the error message and argument for message, otherwise they
2780    will be NULL.  */
2781
2782 NODE *
2783 info_follow_menus (NODE *initial_node, char **menus,
2784     const char **errstr, char **errarg1, char **errarg2)
2785 {
2786   NODE *node = NULL;
2787   *errstr = *errarg1 = *errarg2 = NULL;
2788
2789   for (; *menus; menus++)
2790     {
2791       static char *first_arg = NULL;
2792       REFERENCE **menu;
2793       REFERENCE *entry;
2794       char *arg = *menus; /* Remember the name of the menu entry we want. */
2795
2796       /* A leading space is certainly NOT part of a node name.  Most
2797          probably, they typed a space after the separating comma.  The
2798          strings in menus[] have their whitespace canonicalized, so
2799          there's at most one space to ignore.  */
2800       if (*arg == ' ')
2801         arg++;
2802       if (!first_arg)
2803         first_arg = arg;
2804
2805       /* Build and return a list of the menu items in this node. */
2806       menu = info_menu_of_node (initial_node);
2807
2808       /* If no menu item in this node, stop here, but let the user
2809          continue to use Info.  Perhaps they wanted this node and didn't
2810          realize it. */
2811       if (!menu)
2812         {
2813           if (arg == first_arg)
2814             {
2815               node = make_manpage_node (first_arg);
2816               if (node)
2817                 goto maybe_got_node;
2818             }
2819           *errstr = _("No menu in node `%s'.");
2820           *errarg1 = node_printed_rep (initial_node);
2821           return initial_node;
2822         }
2823
2824       /* Find the specified menu item. */
2825       entry = info_get_labeled_reference (arg, menu);
2826
2827       /* If the item wasn't found, search the list sloppily.  Perhaps this
2828          user typed "buffer" when they really meant "Buffers". */
2829       if (!entry)
2830         {
2831           int i;
2832           int best_guess = -1;
2833
2834           for (i = 0; (entry = menu[i]); i++)
2835             {
2836               if (mbscasecmp (entry->label, arg) == 0)
2837                 break;
2838               else
2839                 if ((best_guess == -1)
2840                     && (mbsncasecmp (entry->label, arg, strlen (arg)) == 0))
2841                   best_guess = i;
2842             }
2843
2844           if (!entry && best_guess != -1)
2845             entry = menu[best_guess];
2846         }
2847
2848       /* If we still failed to find the reference, start Info with the current
2849          node anyway.  It is probably a misspelling. */
2850       if (!entry)
2851         {
2852           if (arg == first_arg)
2853             {
2854               /* Maybe they typed "info foo" instead of "info -f foo".  */
2855               node = info_get_node (first_arg, 0);
2856               if (node)
2857                 add_file_directory_to_path (first_arg);
2858               else
2859                 node = make_manpage_node (first_arg);
2860               if (node)
2861                 goto maybe_got_node;
2862             }
2863
2864           info_free_references (menu);
2865           *errstr = _("No menu item `%s' in node `%s'.");
2866           *errarg1 = arg;
2867           *errarg2 = node_printed_rep (initial_node);
2868           return initial_node;
2869         }
2870
2871       /* We have found the reference that the user specified.  If no
2872          filename in this reference, define it. */
2873       if (!entry->filename)
2874         entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2875                                                      : initial_node->filename);
2876
2877       /* Try to find this node.  */
2878       node = info_get_node (entry->filename, entry->nodename);
2879       if (!node && arg == first_arg)
2880         {
2881           node = make_manpage_node (first_arg);
2882           if (node)
2883             goto maybe_got_node;
2884         }
2885
2886       /* Since we cannot find it, try using the label of the entry as a
2887          file, i.e., "(LABEL)Top".  */
2888       if (!node && entry->nodename
2889           && strcmp (entry->label, entry->nodename) == 0)
2890         node = info_get_node (entry->label, "Top");
2891
2892     maybe_got_node:
2893       if (!node)
2894         {
2895           *errstr = _("Unable to find node referenced by `%s' in `%s'.");
2896           *errarg1 = xstrdup (entry->label);
2897           *errarg2 = node_printed_rep (initial_node);
2898           info_free_references (menu);
2899           return initial_node;
2900         }
2901
2902       info_free_references (menu);
2903
2904       /* Success.  Go round the loop again.  */
2905       free (initial_node);
2906       initial_node = node;
2907     }
2908
2909   return initial_node;
2910 }
2911
2912 /* Split STR into individual node names by writing null bytes in wherever
2913    there are commas and constructing a list of the resulting pointers.
2914    (We can do this since STR has had canonicalize_whitespace called on it.)
2915    Return array terminated with NULL.  */
2916
2917 static char **
2918 split_list_of_nodenames (char *str)
2919 {
2920   unsigned len = 2;
2921   char **nodes = xmalloc (len * sizeof (char *));
2922
2923   nodes[len - 2] = str;
2924
2925   while (*str++)
2926     {
2927       if (*str == ',')
2928         {
2929           *str++ = 0;           /* get past the null byte */
2930           len++;
2931           nodes = xrealloc (nodes, len * sizeof (char *));
2932           nodes[len - 2] = str;
2933         }
2934     }
2935
2936   nodes[len - 1] = NULL;
2937
2938   return nodes;
2939 }
2940
2941
2942 /* Read a line of input which is a sequence of menus (starting from
2943    dir), and follow them.  */
2944 DECLARE_INFO_COMMAND (info_menu_sequence,
2945    _("Read a list of menus starting from dir and follow them"))
2946 {
2947   char *line = info_read_in_echo_area (window, _("Follow menus: "));
2948
2949   /* If the user aborted, quit now. */
2950   if (!line)
2951     {
2952       info_abort_key (window, 0, 0);
2953       return;
2954     }
2955
2956   canonicalize_whitespace (line);
2957
2958   if (*line)
2959     {
2960       const char *errstr;
2961       char *errarg1, *errarg2;
2962       NODE *dir_node = info_get_node (NULL, NULL);
2963       char **nodes = split_list_of_nodenames (line);
2964       NODE *node = NULL;
2965
2966       /* If DIR_NODE is NULL, they might be reading a file directly,
2967          like in "info -d . -f ./foo".  Try using "Top" instead.  */
2968       if (!dir_node)
2969         {
2970           char *file_name = window->node->parent;
2971
2972           if (!file_name)
2973             file_name = window->node->filename;
2974           dir_node = info_get_node (file_name, NULL);
2975         }
2976
2977       /* If we still cannot find the starting point, give up.
2978          We cannot allow a NULL pointer inside info_follow_menus.  */
2979       if (!dir_node)
2980         info_error (msg_cant_find_node, "Top", NULL);
2981       else
2982         node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
2983
2984       free (nodes);
2985       if (!errstr)
2986         info_set_node_of_window (1, window, node);
2987       else
2988         info_error (errstr, errarg1, errarg2);
2989     }
2990
2991   free (line);
2992   if (!info_error_was_printed)
2993     window_clear_echo_area ();
2994 }
2995
2996 /* Search the menu MENU for a (possibly mis-spelled) entry ARG.
2997    Return the menu entry, or the best guess for what they meant by ARG,
2998    or NULL if there's nothing in this menu seems to fit the bill.
2999    If EXACT is non-zero, allow only exact matches.  */
3000 static REFERENCE *
3001 entry_in_menu (char *arg, REFERENCE **menu, int exact)
3002 {
3003   REFERENCE *entry;
3004
3005   /* First, try to find the specified menu item verbatim.  */
3006   entry = info_get_labeled_reference (arg, menu);
3007
3008   /* If the item wasn't found, search the list sloppily.  Perhaps we
3009      have "Option Summary", but ARG is "option".  */
3010   if (!entry && !exact)
3011     {
3012       int i;
3013       int best_guess = -1;
3014
3015       for (i = 0; (entry = menu[i]); i++)
3016         {
3017           if (mbscasecmp (entry->label, arg) == 0)
3018             break;
3019           else
3020             if (mbsncasecmp (entry->label, arg, strlen (arg)) == 0)
3021               best_guess = i;
3022         }
3023
3024       if (!entry && best_guess != -1)
3025         entry = menu[best_guess];
3026     }
3027
3028   return entry;
3029 }
3030
3031 /* Find the node that is the best candidate to list the PROGRAM's
3032    invocation info and its command-line options, by looking for menu
3033    items and chains of menu items with characteristic names.  */
3034 void
3035 info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program)
3036 {
3037   /* The list of node names typical for GNU manuals where the program
3038      usage and specifically the command-line arguments are described.
3039      This is pure heuristics.  I gathered these node names by looking
3040      at all the Info files I could put my hands on.  If you are
3041      looking for evidence to complain to the GNU project about
3042      non-uniform style of documentation, here you have your case!  */
3043   static const char *invocation_nodes[] = {
3044     "%s invocation",
3045     "Invoking %s",
3046     "Preliminaries",    /* m4 has Invoking under Preliminaries! */
3047     "Invocation",
3048     "Command Arguments",/* Emacs */
3049     "Invoking `%s'",
3050     "%s options",
3051     "Options",
3052     "Option ",          /* e.g. "Option Summary" */
3053     "Invoking",
3054     "All options",      /* tar, paxutils */
3055     "Arguments",
3056     "%s cmdline",       /* ar */
3057     "%s",               /* last resort */
3058     (const char *)0
3059   };
3060   NODE *node = NULL;
3061   REFERENCE **menu;
3062   const char **try_node;
3063
3064   /* We keep looking deeper and deeper in the menu structure until
3065      there are no more menus or no menu items from the above list.
3066      Some manuals have the invocation node sitting 3 or 4 levels deep
3067      in the menu hierarchy...  */
3068   for (node = initial_node; node; initial_node = node)
3069     {
3070       REFERENCE *entry = NULL;
3071
3072       /* Build and return a list of the menu items in this node. */
3073       menu = info_menu_of_node (initial_node);
3074
3075       /* If no menu item in this node, stop here.  Perhaps this node
3076          is the one they need.  */
3077       if (!menu)
3078         break;
3079
3080       /* Look for node names typical for usage nodes in this menu.  */
3081       for (try_node = invocation_nodes; *try_node; try_node++)
3082         {
3083           char *nodename;
3084
3085           nodename = xmalloc (strlen (program) + strlen (*try_node));
3086           sprintf (nodename, *try_node, program);
3087           /* The last resort "%s" is dangerous, so we restrict it
3088              to exact matches here.  */
3089           entry = entry_in_menu (nodename, menu,
3090                                  strcmp (*try_node, "%s") == 0);
3091           free (nodename);
3092           if (entry)
3093             break;
3094         }
3095
3096       if (!entry)
3097         break;
3098
3099       if (!entry->filename)
3100         entry->filename = xstrdup (initial_node->parent ? initial_node->parent
3101                                    : initial_node->filename);
3102       /* Try to find this node.  */
3103       node = info_get_node (entry->filename, entry->nodename);
3104       info_free_references (menu);
3105       if (!node)
3106         break;
3107     }
3108
3109   /* We've got our best shot at the invocation node.  Now select it.  */
3110   if (initial_node)
3111     info_set_node_of_window (1, window, initial_node);
3112   if (!info_error_was_printed)
3113     window_clear_echo_area ();
3114 }
3115
3116 /* Given a name of an Info file, find the name of the package it
3117    describes by removing the leading directories and extensions.  */
3118 char *
3119 program_name_from_file_name (char *file_name)
3120 {
3121   int i;
3122   char *program_name = xstrdup (filename_non_directory (file_name));
3123
3124   for (i = strlen (program_name) - 1; i > 0; i--)
3125     if (program_name[i] == '.'
3126         && (FILENAME_CMPN (program_name + i, ".info", 5) == 0
3127             || FILENAME_CMPN (program_name + i, ".inf", 4) == 0
3128 #ifdef __MSDOS__
3129             || FILENAME_CMPN (program_name + i, ".i", 2) == 0
3130 #endif
3131             || isdigit (program_name[i + 1]))) /* a man page foo.1 */
3132       {
3133         program_name[i] = 0;
3134         break;
3135       }
3136   return program_name;
3137 }
3138
3139 DECLARE_INFO_COMMAND (info_goto_invocation_node,
3140                       _("Find the node describing program invocation"))
3141 {
3142   const char *invocation_prompt = _("Find Invocation node of [%s]: ");
3143   char *program_name, *line;
3144   char *default_program_name, *prompt, *file_name;
3145   NODE *top_node;
3146
3147   /* Intuit the name of the program they are likely to want.
3148      We use the file name of the current Info file as a hint.  */
3149   file_name = window->node->parent ? window->node->parent
3150                                    : window->node->filename;
3151   default_program_name = program_name_from_file_name (file_name);
3152
3153   prompt = xmalloc (strlen (default_program_name) +
3154                     strlen (invocation_prompt));
3155   sprintf (prompt, invocation_prompt, default_program_name);
3156   line = info_read_in_echo_area (window, prompt);
3157   free (prompt);
3158   if (!line)
3159     {
3160       info_abort_key (window, 0, 0);
3161       return;
3162     }
3163   if (*line)
3164     program_name = line;
3165   else
3166     program_name = default_program_name;
3167
3168   /* In interactive usage they'd probably expect us to begin looking
3169      from the Top node.  */
3170   top_node = info_get_node (file_name, NULL);
3171   if (!top_node)
3172     info_error (msg_cant_find_node, "Top", NULL);
3173
3174   info_intuit_options_node (window, top_node, program_name);
3175   free (line);
3176   free (default_program_name);
3177 }
3178 \f
3179 #if defined (HANDLE_MAN_PAGES)
3180 DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
3181 {
3182   char *line;
3183
3184   line = info_read_in_echo_area (window, _("Get Manpage: "));
3185
3186   if (!line)
3187     {
3188       info_abort_key (window, 0, 0);
3189       return;
3190     }
3191
3192   canonicalize_whitespace (line);
3193
3194   if (*line)
3195     {
3196       char *goto_command;
3197
3198       goto_command = xmalloc
3199         (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
3200
3201       sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
3202
3203       info_parse_and_select (goto_command, window);
3204       free (goto_command);
3205     }
3206
3207   free (line);
3208   if (!info_error_was_printed)
3209     window_clear_echo_area ();
3210 }
3211 #endif /* HANDLE_MAN_PAGES */
3212
3213 /* Move to the "Top" node in this file. */
3214 DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
3215 {
3216   info_parse_and_select ("Top", window);
3217 }
3218
3219 /* Move to the node "(dir)Top". */
3220 DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
3221 {
3222   info_parse_and_select ("(dir)Top", window);
3223 }
3224
3225 \f
3226 /* Read the name of a node to kill.  The list of available nodes comes
3227    from the nodes appearing in the current window configuration. */
3228 static char *
3229 read_nodename_to_kill (WINDOW *window)
3230 {
3231   int iw;
3232   char *nodename;
3233   INFO_WINDOW *info_win;
3234   REFERENCE **menu = NULL;
3235   int menu_index = 0, menu_slots = 0;
3236   char *default_nodename = xstrdup (active_window->node->nodename);
3237   char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename));
3238
3239   sprintf (prompt, _("Kill node (%s): "), default_nodename);
3240
3241   for (iw = 0; (info_win = info_windows[iw]); iw++)
3242     {
3243       REFERENCE *entry = xmalloc (sizeof (REFERENCE));
3244       entry->label = xstrdup (info_win->window->node->nodename);
3245       entry->filename = entry->nodename = NULL;
3246
3247       add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
3248                             REFERENCE *);
3249     }
3250
3251   nodename = info_read_completing_in_echo_area (window, prompt, menu);
3252   free (prompt);
3253   info_free_references (menu);
3254   if (nodename && !*nodename)
3255     {
3256       free (nodename);
3257       nodename = default_nodename;
3258     }
3259   else
3260     free (default_nodename);
3261
3262   return nodename;
3263 }
3264
3265
3266 /* Delete NODENAME from this window, showing the most
3267    recently selected node in this window. */
3268 static void
3269 kill_node (WINDOW *window, char *nodename)
3270 {
3271   int iw, i;
3272   INFO_WINDOW *info_win;
3273   NODE *temp;
3274
3275   /* If there is no nodename to kill, quit now. */
3276   if (!nodename)
3277     {
3278       info_abort_key (window, 0, 0);
3279       return;
3280     }
3281
3282   /* If there is a nodename, find it in our window list. */
3283   for (iw = 0; (info_win = info_windows[iw]); iw++)
3284     if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
3285         && info_win->window == window)
3286       break;
3287
3288   if (!info_win)
3289     {
3290       if (*nodename)
3291         info_error (_("Cannot kill node `%s'"), nodename, NULL);
3292       else
3293         window_clear_echo_area ();
3294
3295       return;
3296     }
3297
3298   /* If there are no more nodes left anywhere to view, complain and exit. */
3299   if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
3300     {
3301       info_error (_("Cannot kill the last node"), NULL, NULL);
3302       return;
3303     }
3304
3305   /* INFO_WIN contains the node that the user wants to stop viewing.  Delete
3306      this node from the list of nodes previously shown in this window. */
3307   for (i = info_win->current; i < info_win->nodes_index; i++)
3308     info_win->nodes[i] = info_win->nodes[i + 1];
3309
3310   /* There is one less node in this window's history list. */
3311   info_win->nodes_index--;
3312
3313   /* Make this window show the most recent history node. */
3314   info_win->current = info_win->nodes_index - 1;
3315
3316   /* If there aren't any nodes left in this window, steal one from the
3317      next window. */
3318   if (info_win->current < 0)
3319     {
3320       INFO_WINDOW *stealer;
3321       int which, pagetop;
3322       long point;
3323
3324       if (info_windows[iw + 1])
3325         stealer = info_windows[iw + 1];
3326       else
3327         stealer = info_windows[0];
3328
3329       /* If the node being displayed in the next window is not the most
3330          recently loaded one, get the most recently loaded one. */
3331       if ((stealer->nodes_index - 1) != stealer->current)
3332         which = stealer->nodes_index - 1;
3333
3334       /* Else, if there is another node behind the stealers current node,
3335          use that one. */
3336       else if (stealer->current > 0)
3337         which = stealer->current - 1;
3338
3339       /* Else, just use the node appearing in STEALER's window. */
3340       else
3341         which = stealer->current;
3342
3343       /* Copy this node. */
3344       {
3345         NODE *copy = xmalloc (sizeof (NODE));
3346
3347         temp = stealer->nodes[which];
3348         point = stealer->points[which];
3349         pagetop = stealer->pagetops[which];
3350
3351         copy->filename = temp->filename;
3352         copy->parent = temp->parent;
3353         copy->nodename = temp->nodename;
3354         copy->contents = temp->contents;
3355         copy->nodelen = temp->nodelen;
3356         copy->flags = temp->flags;
3357         copy->display_pos = temp->display_pos;
3358
3359         temp = copy;
3360       }
3361
3362       window_set_node_of_window (info_win->window, temp);
3363       window->point = point;
3364       window->pagetop = pagetop;
3365       remember_window_and_node (info_win->window, temp);
3366     }
3367   else
3368     {
3369       temp = info_win->nodes[info_win->current];
3370       temp->display_pos = info_win->points[info_win->current];
3371       window_set_node_of_window (info_win->window, temp);
3372     }
3373
3374   if (!info_error_was_printed)
3375     window_clear_echo_area ();
3376
3377   if (auto_footnotes_p)
3378     info_get_or_remove_footnotes (window);
3379 }
3380
3381 /* Kill current node, thus going back one in the node history.  I (karl)
3382    do not think this is completely correct yet, because of the
3383    window-changing stuff in kill_node, but it's a lot better than the
3384    previous implementation, which did not account for nodes being
3385    visited twice at all.  */
3386 DECLARE_INFO_COMMAND (info_history_node,
3387                       _("Select the most recently selected node"))
3388 {
3389   kill_node (window, active_window->node->nodename);
3390 }
3391
3392 /* Kill named node.  */
3393 DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
3394 {
3395   char *nodename = read_nodename_to_kill (window);
3396   kill_node (window, nodename);
3397 }
3398
3399 \f
3400 /* Read the name of a file and select the entire file. */
3401 DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
3402 {
3403   char *line;
3404
3405   line = info_read_in_echo_area (window, _("Find file: "));
3406   if (!line)
3407     {
3408       info_abort_key (active_window, 1, 0);
3409       return;
3410     }
3411
3412   if (*line)
3413     {
3414       NODE *node;
3415
3416       node = info_get_node (line, "*");
3417       if (!node)
3418         {
3419           if (info_recent_file_error)
3420             info_error (info_recent_file_error, NULL, NULL);
3421           else
3422             info_error (_("Cannot find `%s'."), line, NULL);
3423         }
3424       else
3425         info_set_node_of_window (1, window, node);
3426
3427       free (line);
3428     }
3429
3430   if (!info_error_was_printed)
3431     window_clear_echo_area ();
3432 }
3433 \f
3434 /* **************************************************************** */
3435 /*                                                                  */
3436 /*                 Dumping and Printing Nodes                       */
3437 /*                                                                  */
3438 /* **************************************************************** */
3439
3440 #define VERBOSE_NODE_DUMPING
3441 static void write_node_to_stream (NODE *node, FILE *stream);
3442 static void dump_node_to_stream (char *filename, char *nodename,
3443     FILE *stream, int dump_subnodes);
3444 static void initialize_dumping (void);
3445
3446 /* Dump the nodes specified by FILENAME and NODENAMES to the file named
3447    in OUTPUT_FILENAME.  If DUMP_SUBNODES is non-zero, recursively dump
3448    the nodes which appear in the menu of each node dumped. */
3449 void
3450 dump_nodes_to_file (char *filename, char **nodenames,
3451     char *output_filename, int dump_subnodes)
3452 {
3453   register int i;
3454   FILE *output_stream;
3455
3456   /* Get the stream to print the nodes to.  Special case of an output
3457      filename of "-" means to dump the nodes to stdout. */
3458   if (strcmp (output_filename, "-") == 0)
3459     output_stream = stdout;
3460   else
3461     output_stream = fopen (output_filename, "w");
3462
3463   if (!output_stream)
3464     {
3465       info_error (_("Could not create output file `%s'."),
3466           output_filename, NULL);
3467       return;
3468     }
3469
3470   /* Print each node to stream. */
3471   initialize_dumping ();
3472   for (i = 0; nodenames[i]; i++)
3473     dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
3474
3475   if (output_stream != stdout)
3476     fclose (output_stream);
3477
3478 #if defined (VERBOSE_NODE_DUMPING)
3479   info_error (_("Done."), NULL, NULL);
3480 #endif /* VERBOSE_NODE_DUMPING */
3481 }
3482
3483 /* A place to remember already dumped nodes. */
3484 static char **dumped_already = NULL;
3485 static int dumped_already_index = 0;
3486 static int dumped_already_slots = 0;
3487
3488 static void
3489 initialize_dumping (void)
3490 {
3491   dumped_already_index = 0;
3492 }
3493
3494 /* Get and print the node specified by FILENAME and NODENAME to STREAM.
3495    If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
3496    in the menu of each node dumped. */
3497 static void
3498 dump_node_to_stream (char *filename, char *nodename,
3499     FILE *stream, int dump_subnodes)
3500 {
3501   register int i;
3502   NODE *node;
3503
3504   node = info_get_node (filename, nodename);
3505
3506   if (!node)
3507     {
3508       if (info_recent_file_error)
3509         info_error (info_recent_file_error, NULL, NULL);
3510       else
3511         {
3512           if (filename && *nodename != '(')
3513             info_error (msg_cant_file_node,
3514                 filename_non_directory (filename),
3515                 nodename);
3516           else
3517             info_error (msg_cant_find_node, nodename, NULL);
3518         }
3519       return;
3520     }
3521
3522   /* If we have already dumped this node, don't dump it again. */
3523   for (i = 0; i < dumped_already_index; i++)
3524     if (strcmp (node->nodename, dumped_already[i]) == 0)
3525       {
3526         free (node);
3527         return;
3528       }
3529   add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
3530                         dumped_already_slots, 50, char *);
3531
3532 #if defined (VERBOSE_NODE_DUMPING)
3533   /* Maybe we should print some information about the node being output. */
3534   info_error (_("Writing node %s..."), node_printed_rep (node), NULL);
3535 #endif /* VERBOSE_NODE_DUMPING */
3536
3537   write_node_to_stream (node, stream);
3538
3539   /* If we are dumping subnodes, get the list of menu items in this node,
3540      and dump each one recursively. */
3541   if (dump_subnodes)
3542     {
3543       REFERENCE **menu = NULL;
3544
3545       /* If this node is an Index, do not dump the menu references. */
3546       if (string_in_line ("Index", node->nodename) == -1)
3547         menu = info_menu_of_node (node);
3548
3549       if (menu)
3550         {
3551           for (i = 0; menu[i]; i++)
3552             {
3553               /* We don't dump Info files which are different than the
3554                  current one. */
3555               if (!menu[i]->filename)
3556                 dump_node_to_stream
3557                   (filename, menu[i]->nodename, stream, dump_subnodes);
3558             }
3559           info_free_references (menu);
3560         }
3561     }
3562
3563   free (node);
3564 }
3565
3566 /* Dump NODE to FILENAME.  If DUMP_SUBNODES is non-zero, recursively dump
3567    the nodes which appear in the menu of each node dumped. */
3568 void
3569 dump_node_to_file (NODE *node, char *filename, int dump_subnodes)
3570 {
3571   FILE *output_stream;
3572   char *nodes_filename;
3573
3574   /* Get the stream to print this node to.  Special case of an output
3575      filename of "-" means to dump the nodes to stdout. */
3576   if (strcmp (filename, "-") == 0)
3577     output_stream = stdout;
3578   else
3579     output_stream = fopen (filename, "w");
3580
3581   if (!output_stream)
3582     {
3583       info_error (_("Could not create output file `%s'."), filename,
3584           NULL);
3585       return;
3586     }
3587
3588   if (node->parent)
3589     nodes_filename = node->parent;
3590   else
3591     nodes_filename = node->filename;
3592
3593   initialize_dumping ();
3594   dump_node_to_stream
3595     (nodes_filename, node->nodename, output_stream, dump_subnodes);
3596
3597   if (output_stream != stdout)
3598     fclose (output_stream);
3599
3600 #if defined (VERBOSE_NODE_DUMPING)
3601   info_error (_("Done."), NULL, NULL);
3602 #endif /* VERBOSE_NODE_DUMPING */
3603 }
3604
3605 #if !defined (DEFAULT_INFO_PRINT_COMMAND)
3606 #  define DEFAULT_INFO_PRINT_COMMAND "lpr"
3607 #endif /* !DEFAULT_INFO_PRINT_COMMAND */
3608
3609 DECLARE_INFO_COMMAND (info_print_node,
3610  _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
3611 {
3612   print_node (window->node);
3613 }
3614
3615 /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3616 void
3617 print_node (NODE *node)
3618 {
3619   FILE *printer_pipe;
3620   char *print_command = getenv ("INFO_PRINT_COMMAND");
3621   int piping = 0;
3622
3623   if (!print_command || !*print_command)
3624     print_command = DEFAULT_INFO_PRINT_COMMAND;
3625
3626   /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
3627      (default) text mode, since the printer drivers there need to see
3628      DOS-style CRLF pairs at the end of each line.
3629
3630      FIXME: if we are to support Mac-style text files, we might need
3631      to convert the text here.  */
3632
3633   /* INFO_PRINT_COMMAND which says ">file" means write to that file.
3634      Presumably, the name of the file is the local printer device.  */
3635   if (*print_command == '>')
3636     printer_pipe = fopen (++print_command, "w");
3637   else
3638     {
3639       printer_pipe = popen (print_command, "w");
3640       piping = 1;
3641     }
3642
3643   if (!printer_pipe)
3644     {
3645       info_error (_("Cannot open pipe to `%s'."), print_command, NULL);
3646       return;
3647     }
3648
3649 #if defined (VERBOSE_NODE_DUMPING)
3650   /* Maybe we should print some information about the node being output. */
3651   info_error (_("Printing node %s..."), node_printed_rep (node), NULL);
3652 #endif /* VERBOSE_NODE_DUMPING */
3653
3654   write_node_to_stream (node, printer_pipe);
3655   if (piping)
3656     pclose (printer_pipe);
3657   else
3658     fclose (printer_pipe);
3659
3660 #if defined (VERBOSE_NODE_DUMPING)
3661   info_error (_("Done."), NULL, NULL);
3662 #endif /* VERBOSE_NODE_DUMPING */
3663 }
3664
3665 static void
3666 write_node_to_stream (NODE *node, FILE *stream)
3667 {
3668   fwrite (node->contents, 1, node->nodelen, stream);
3669 }
3670 \f
3671 /* **************************************************************** */
3672 /*                                                                  */
3673 /*                    Info Searching Commands                       */
3674 /*                                                                  */
3675 /* **************************************************************** */
3676
3677 /* Variable controlling the garbage collection of files briefly visited
3678    during searches.  Such files are normally gc'ed, unless they were
3679    compressed to begin with.  If this variable is non-zero, it says
3680    to gc even those file buffer contents which had to be uncompressed. */
3681 int gc_compressed_files = 0;
3682
3683 static void info_gc_file_buffers (void);
3684 static void info_search_1 (WINDOW *window, int count,
3685     unsigned char key, int case_sensitive, int ask_for_string);
3686
3687 static char *search_string = NULL;
3688 static int search_string_size = 0;
3689 static int isearch_is_active = 0;
3690
3691 static int last_search_direction = 0;
3692 static int last_search_case_sensitive = 0;
3693
3694 /* Return the file buffer which belongs to WINDOW's node. */
3695 FILE_BUFFER *
3696 file_buffer_of_window (WINDOW *window)
3697 {
3698   /* If this window has no node, then it has no file buffer. */
3699   if (!window->node)
3700     return NULL;
3701
3702   if (window->node->parent)
3703     return info_find_file (window->node->parent);
3704
3705   if (window->node->filename)
3706     return info_find_file (window->node->filename);
3707
3708   return NULL;
3709 }
3710
3711 /* Search for STRING in NODE starting at START.  Return -1 if the string
3712    was not found, or the location of the string if it was.  If WINDOW is
3713    passed as non-null, set the window's node to be NODE, its point to be
3714    the found string, and readjust the window's pagetop.  The DIR argument
3715    says which direction to search in.  If it is positive, search
3716    forward, else backwards.
3717
3718    The last argument, RESBND, makes sense only when USE_REGEX is set.
3719    If the regexp search succeeds, RESBND is filled with the final state
3720    of the search binding.  In particular, its START and END fields contain
3721    bounds of the found string instance.
3722 */
3723 static long
3724 info_search_in_node_internal (char *string, NODE *node, long int start,
3725                               WINDOW *window, int dir, int case_sensitive,
3726                               SEARCH_BINDING *resbnd)
3727 {
3728   SEARCH_BINDING binding;
3729   long offset;
3730
3731   binding.buffer = node->contents;
3732   binding.start = start;
3733   binding.end = node->nodelen;
3734   binding.flags = 0;
3735   if (!case_sensitive)
3736     binding.flags |= S_FoldCase;
3737
3738   if (dir < 0)
3739     {
3740       binding.end = 0;
3741       binding.flags |= S_SkipDest;
3742     }
3743
3744   if (binding.start < 0)
3745     return -1;
3746
3747   /* For incremental searches, we always wish to skip past the string. */
3748   if (isearch_is_active)
3749     binding.flags |= S_SkipDest;
3750
3751   offset = (use_regex ? 
3752             regexp_search (string, &binding, node->nodelen, resbnd):
3753             search (string, &binding));
3754
3755   if (offset != -1 && window)
3756     {
3757       set_remembered_pagetop_and_point (window);
3758       if (window->node != node)
3759         window_set_node_of_window (window, node);
3760       window->point = offset;
3761       window_adjust_pagetop (window);
3762     }
3763   return offset;
3764 }
3765
3766 long
3767 info_search_in_node (char *string, NODE *node, long int start,
3768                      WINDOW *window, int dir, int case_sensitive)
3769 {
3770   return info_search_in_node_internal (string, node, start,
3771                                        window, dir, case_sensitive, NULL);
3772 }
3773
3774 /* Search NODE, looking for the largest possible match of STRING.  Start the
3775    search at START.  Return the absolute position of the match, or -1, if
3776    no part of the string could be found. */
3777 long
3778 info_target_search_node (NODE *node, char *string, long int start)
3779 {
3780   register int i;
3781   long offset = 0;
3782   char *target;
3783
3784   target = xstrdup (string);
3785   i = strlen (target);
3786
3787   /* Try repeatedly searching for this string while removing words from
3788      the end of it. */
3789   while (i)
3790     {
3791       target[i] = '\0';
3792       offset = info_search_in_node (target, node, start, NULL, 1, 0);
3793
3794       if (offset != -1)
3795         break;
3796
3797       /* Delete the last word from TARGET. */
3798       for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
3799     }
3800   free (target);
3801   return offset;
3802 }
3803
3804 /* Search for STRING starting in WINDOW.  The starting position is determined
3805    by DIR and RESBND argument.  If the latter is given, and its START field
3806    is not -1, it gives starting position.  Otherwise, the search begins at
3807    window point + DIR.
3808
3809    If the string is found in this node, set point to that position.
3810    Otherwise, get the file buffer associated with WINDOW's node, and search
3811    through each node in that file.
3812
3813    If the search succeeds and RESBND is given, its START and END fields
3814    contain bounds of the found string instance (only for regexp searches).
3815    
3816    If the search fails, return non-zero, else zero.  Side-effect window
3817    leaving the node and point where the string was found current. */
3818 static int
3819 info_search_internal (char *string, WINDOW *window,
3820                       int dir, int case_sensitive,
3821                       SEARCH_BINDING *resbnd)
3822 {
3823   register int i;
3824   FILE_BUFFER *file_buffer;
3825   char *initial_nodename;
3826   long ret, start;
3827
3828   file_buffer = file_buffer_of_window (window);
3829   initial_nodename = window->node->nodename;
3830
3831   if (resbnd && resbnd->start != -1)
3832     start = resbnd->start;
3833   else
3834     /* This used to begin from window->point, unless this was a repeated
3835        search command.  But invoking search with an argument loses with
3836        that logic, since info_last_executed_command is then set to
3837        info_add_digit_to_numeric_arg.  I think there's no sense in
3838        ``finding'' a string that is already under the cursor, anyway.  */
3839     start = window->point + dir;
3840   
3841   ret = info_search_in_node_internal
3842         (string, window->node, start, window, dir,
3843          case_sensitive, resbnd);
3844   
3845   if (ret != -1)
3846     {
3847       /* We won! */
3848       if (!echo_area_is_active && !isearch_is_active)
3849         window_clear_echo_area ();
3850       return 0;
3851     }
3852   
3853   start = 0;
3854   
3855   /* The string wasn't found in the current node.  Search through the
3856      window's file buffer, iff the current node is not "*". */
3857   if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
3858     return -1;
3859
3860   /* If this file has tags, search through every subfile, starting at
3861      this node's subfile and node.  Otherwise, search through the
3862      file's node list. */
3863   if (file_buffer->tags)
3864     {
3865       register int current_tag = 0, number_of_tags;
3866       char *last_subfile;
3867       TAG *tag;
3868       char *msg = NULL;
3869
3870       /* Find number of tags and current tag. */
3871       last_subfile = NULL;
3872       for (i = 0; file_buffer->tags[i]; i++)
3873         if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
3874           {
3875             current_tag = i;
3876             last_subfile = file_buffer->tags[i]->filename;
3877           }
3878
3879       number_of_tags = i;
3880
3881       /* If there is no last_subfile, our tag wasn't found. */
3882       if (!last_subfile)
3883         return -1;
3884
3885       /* Search through subsequent nodes, wrapping around to the top
3886          of the info file until we find the string or return to this
3887          window's node and point. */
3888       while (1)
3889         {
3890           NODE *node;
3891           
3892           /* Allow C-g to quit the search, failing it if pressed. */
3893           return_if_control_g (-1);
3894
3895           /* Find the next tag that isn't an anchor.  */
3896           for (i = current_tag + dir; i != current_tag; i += dir)
3897             {
3898               if (i < 0)
3899                 {
3900                   msg = N_("Search continued from the end of the document.");
3901                   i = number_of_tags - 1;
3902                 }
3903               else if (i == number_of_tags)
3904                 {
3905                   msg = N_("Search continued from the beginning of the document.");
3906                   i = 0;
3907                 }
3908               
3909               tag = file_buffer->tags[i];
3910               if (tag->nodelen != 0)
3911                 break;
3912             }
3913
3914           /* If we got past out starting point, bail out.  */
3915           if (i == current_tag)
3916             return -1;
3917           current_tag = i;
3918
3919           if (!echo_area_is_active && (last_subfile != tag->filename))
3920             {
3921               window_message_in_echo_area
3922                 (_("Searching subfile %s ..."),
3923                  filename_non_directory (tag->filename), NULL);
3924
3925               last_subfile = tag->filename;
3926             }
3927
3928           node = info_get_node (file_buffer->filename, tag->nodename);
3929
3930           if (!node)
3931             {
3932               /* If not doing i-search... */
3933               if (!echo_area_is_active)
3934                 {
3935                   if (info_recent_file_error)
3936                     info_error (info_recent_file_error, NULL, NULL);
3937                   else
3938                     info_error (msg_cant_file_node,
3939                                 filename_non_directory (file_buffer->filename),
3940                                 tag->nodename);
3941                 }
3942               return -1;
3943             }
3944
3945           if (dir < 0)
3946             start = tag->nodelen;
3947
3948           ret =
3949             info_search_in_node_internal (string, node, start, window, dir,
3950                                           case_sensitive, resbnd);
3951
3952           /* Did we find the string in this node? */
3953           if (ret != -1)
3954             {
3955               /* Yes!  We win. */
3956               remember_window_and_node (window, node);
3957               if (!echo_area_is_active)
3958                 {
3959                   if (msg)
3960                     window_message_in_echo_area ("%s", (char *) _(msg), NULL);
3961                   else
3962                     window_clear_echo_area ();
3963                 }
3964               return 0;
3965             }
3966
3967           /* No.  Free this node, and make sure that we haven't passed
3968              our starting point. */
3969           free (node);
3970
3971           if (strcmp (initial_nodename, tag->nodename) == 0)
3972             return -1;
3973         }
3974     }
3975   return -1;
3976 }
3977
3978 DECLARE_INFO_COMMAND (info_search_case_sensitively,
3979                       _("Read a string and search for it case-sensitively"))
3980 {
3981   last_search_direction = count > 0 ? 1 : -1;
3982   last_search_case_sensitive = 1;
3983   info_search_1 (window, count, key, 1, 1);
3984 }
3985
3986 DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
3987 {
3988   last_search_direction = count > 0 ? 1 : -1;
3989   last_search_case_sensitive = 0;
3990   info_search_1 (window, count, key, 0, 1);
3991 }
3992
3993 DECLARE_INFO_COMMAND (info_search_backward,
3994                       _("Read a string and search backward for it"))
3995 {
3996   last_search_direction = count > 0 ? -1 : 1;
3997   last_search_case_sensitive = 0;
3998   info_search_1 (window, -count, key, 0, 1);
3999 }
4000
4001 static void
4002 info_search_1 (WINDOW *window, int count, unsigned char key,
4003     int case_sensitive, int ask_for_string)
4004 {
4005   char *line, *prompt;
4006   int result, old_pagetop;
4007   int direction;
4008
4009   if (count < 0)
4010     {
4011       direction = -1;
4012       count = -count;
4013     }
4014   else
4015     {
4016       direction = 1;
4017       if (count == 0)
4018         count = 1;      /* for backward compatibility */
4019     }
4020
4021   /* Read a string from the user, defaulting the search to SEARCH_STRING. */
4022   if (!search_string)
4023     {
4024       search_string = xmalloc (search_string_size = 100);
4025       search_string[0] = '\0';
4026     }
4027
4028   if (ask_for_string)
4029     {
4030       prompt = xmalloc (strlen (_("%s%s%s [%s]: "))
4031                         + strlen (_("Regexp search"))
4032                         + strlen (_(" case-sensitively"))
4033                         + strlen (_(" backward"))
4034                         + strlen (search_string));
4035
4036       sprintf (prompt, _("%s%s%s [%s]: "),
4037                use_regex ? _("Regexp search") : _("Search"),
4038                case_sensitive ? _(" case-sensitively") : "",
4039                direction < 0 ? _(" backward") : "",
4040                search_string);
4041
4042       line = info_read_in_echo_area (window, prompt);
4043       free (prompt);
4044
4045       if (!line)
4046         {
4047           info_abort_key (window, 0, 0);
4048           return;
4049         }
4050
4051       if (*line)
4052         {
4053           if (strlen (line) + 1 > (unsigned int) search_string_size)
4054             search_string = xrealloc
4055               (search_string, (search_string_size += 50 + strlen (line)));
4056
4057           strcpy (search_string, line);
4058           free (line);
4059         }
4060     }
4061
4062   /* If the search string includes upper-case letters, make the search
4063      case-sensitive.  */
4064   if (case_sensitive == 0)
4065     for (line = search_string; *line; line++)
4066       if (isupper (*line))
4067         {
4068           case_sensitive = 1;
4069           break;
4070         }
4071
4072   old_pagetop = active_window->pagetop;
4073   for (result = 0; result == 0 && count--; )
4074     result = info_search_internal (search_string,
4075                                    active_window, direction, case_sensitive,
4076                                    NULL);
4077
4078   if (result != 0 && !info_error_was_printed)
4079     info_error (_("Search failed."), NULL, NULL);
4080   else if (old_pagetop != active_window->pagetop)
4081     {
4082       int new_pagetop;
4083
4084       new_pagetop = active_window->pagetop;
4085       active_window->pagetop = old_pagetop;
4086       set_window_pagetop (active_window, new_pagetop);
4087       if (auto_footnotes_p)
4088         info_get_or_remove_footnotes (active_window);
4089     }
4090
4091   /* Perhaps free the unreferenced file buffers that were searched, but
4092      not retained. */
4093   info_gc_file_buffers ();
4094 }
4095
4096 DECLARE_INFO_COMMAND (info_search_next,
4097                       _("Repeat last search in the same direction"))
4098 {
4099   if (!last_search_direction)
4100     info_error (_("No previous search string"), NULL, NULL);
4101   else
4102     info_search_1 (window, last_search_direction * count,
4103                    key, last_search_case_sensitive, 0);
4104 }
4105
4106 DECLARE_INFO_COMMAND (info_search_previous,
4107                       _("Repeat last search in the reverse direction"))
4108 {
4109   if (!last_search_direction)
4110     info_error (_("No previous search string"), NULL, NULL);
4111   else
4112     info_search_1 (window, -last_search_direction * count,
4113                    key, last_search_case_sensitive, 0);
4114 }
4115
4116 /* **************************************************************** */
4117 /*                                                                  */
4118 /*                      Incremental Searching                       */
4119 /*                                                                  */
4120 /* **************************************************************** */
4121
4122 static void incremental_search (WINDOW *window, int count,
4123     unsigned char ignore);
4124
4125 DECLARE_INFO_COMMAND (isearch_forward,
4126                       _("Search interactively for a string as you type it"))
4127 {
4128   incremental_search (window, count, key);
4129 }
4130
4131 DECLARE_INFO_COMMAND (isearch_backward,
4132                       _("Search interactively for a string as you type it"))
4133 {
4134   incremental_search (window, -count, key);
4135 }
4136
4137 /* Incrementally search for a string as it is typed. */
4138 /* The last accepted incremental search string. */
4139 static char *last_isearch_accepted = NULL;
4140
4141 /* The current incremental search string. */
4142 static char *isearch_string = NULL;
4143 static int isearch_string_index = 0;
4144 static int isearch_string_size = 0;
4145 static unsigned char isearch_terminate_search_key = ESC;
4146
4147 /* Array of search states. */
4148 static SEARCH_STATE **isearch_states = NULL;
4149 static int isearch_states_index = 0;
4150 static int isearch_states_slots = 0;
4151
4152 /* Push the state of this search. */
4153 static void
4154 push_isearch (WINDOW *window, int search_index, int direction, int failing)
4155 {
4156   SEARCH_STATE *state;
4157
4158   state = xmalloc (sizeof (SEARCH_STATE));
4159   window_get_state (window, state);
4160   state->search_index = search_index;
4161   state->direction = direction;
4162   state->failing = failing;
4163
4164   add_pointer_to_array (state, isearch_states_index, isearch_states,
4165                         isearch_states_slots, 20, SEARCH_STATE *);
4166 }
4167
4168 /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
4169 static void
4170 pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing)
4171 {
4172   SEARCH_STATE *state;
4173
4174   if (isearch_states_index)
4175     {
4176       isearch_states_index--;
4177       state = isearch_states[isearch_states_index];
4178       window_set_state (window, state);
4179       *search_index = state->search_index;
4180       *direction = state->direction;
4181       *failing = state->failing;
4182
4183       free (state);
4184       isearch_states[isearch_states_index] = NULL;
4185     }
4186 }
4187
4188 /* Free the memory used by isearch_states. */
4189 static void
4190 free_isearch_states (void)
4191 {
4192   register int i;
4193
4194   for (i = 0; i < isearch_states_index; i++)
4195     {
4196       free (isearch_states[i]);
4197       isearch_states[i] = NULL;
4198     }
4199   isearch_states_index = 0;
4200 }
4201
4202 /* Display the current search in the echo area. */
4203 static void
4204 show_isearch_prompt (int dir, unsigned char *string, int failing_p)
4205 {
4206   register int i;
4207   const char *prefix;
4208   char *prompt, *p_rep;
4209   unsigned int prompt_len, p_rep_index, p_rep_size;
4210
4211   if (dir < 0)
4212     prefix = use_regex ? _("Regexp I-search backward: ")
4213                        : _("I-search backward: ");
4214   else
4215     prefix = use_regex ? _("Regexp I-search: ")
4216                        : _("I-search: ");
4217
4218   p_rep_index = p_rep_size = 0;
4219   p_rep = NULL;
4220   for (i = 0; string[i]; i++)
4221     {
4222       char *rep;
4223
4224       switch (string[i])
4225         {
4226         case ' ': rep = " "; break;
4227         case LFD: rep = "\\n"; break;
4228         case TAB: rep = "\\t"; break;
4229         default:
4230           rep = pretty_keyname (string[i]);
4231         }
4232       if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
4233         p_rep = xrealloc (p_rep, p_rep_size += 100);
4234
4235       strcpy (p_rep + p_rep_index, rep);
4236       p_rep_index += strlen (rep);
4237     }
4238
4239   prompt_len = strlen (prefix) + p_rep_index + 1;
4240   if (failing_p)
4241     prompt_len += strlen (_("Failing "));
4242   prompt = xmalloc (prompt_len);
4243   sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
4244            p_rep ? p_rep : "");
4245
4246   window_message_in_echo_area ("%s", prompt, NULL);
4247   maybe_free (p_rep);
4248   free (prompt);
4249   display_cursor_at_point (active_window);
4250 }
4251
4252 static void
4253 incremental_search (WINDOW *window, int count, unsigned char ignore)
4254 {
4255   unsigned char key;
4256   int last_search_result, search_result, dir;
4257   SEARCH_STATE mystate, orig_state;
4258   char *p;
4259   int case_sensitive = 0;
4260   SEARCH_BINDING bnd;
4261
4262   bnd.start = -1;
4263
4264   if (count < 0)
4265     dir = -1;
4266   else
4267     dir = 1;
4268
4269   last_search_result = search_result = 0;
4270
4271   window_get_state (window, &orig_state);
4272
4273   isearch_string_index = 0;
4274   if (!isearch_string_size)
4275     isearch_string = xmalloc (isearch_string_size = 50);
4276
4277   /* Show the search string in the echo area. */
4278   isearch_string[isearch_string_index] = '\0';
4279   show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4280
4281   isearch_is_active = 1;
4282
4283   while (isearch_is_active)
4284     {
4285       VFunction *func = NULL;
4286       int quoted = 0;
4287
4288       /* If a recent display was interrupted, then do the redisplay now if
4289          it is convenient. */
4290       if (!info_any_buffered_input_p () && display_was_interrupted_p)
4291         {
4292           display_update_one_window (window);
4293           display_cursor_at_point (active_window);
4294         }
4295
4296       /* Read a character and dispatch on it. */
4297       key = info_get_input_char ();
4298       window_get_state (window, &mystate);
4299
4300       if (key == DEL || key == Control ('h'))
4301         {
4302           /* User wants to delete one level of search? */
4303           if (!isearch_states_index)
4304             {
4305               terminal_ring_bell ();
4306               continue;
4307             }
4308           else
4309             {
4310               pop_isearch
4311                 (window, &isearch_string_index, &dir, &search_result);
4312               isearch_string[isearch_string_index] = '\0';
4313               show_isearch_prompt (dir, (unsigned char *) isearch_string,
4314                   search_result);
4315               goto after_search;
4316             }
4317         }
4318       else if (key == Control ('q'))
4319         {
4320           key = info_get_input_char ();
4321           quoted = 1;
4322         }
4323
4324       /* We are about to search again, or quit.  Save the current search. */
4325       push_isearch (window, isearch_string_index, dir, search_result);
4326
4327       if (quoted)
4328         goto insert_and_search;
4329
4330       if (!Meta_p (key) || key > 32)
4331         {
4332           /* If this key is not a keymap, get its associated function,
4333              if any.  If it is a keymap, then it's probably ESC from an
4334              arrow key, and we handle that case below.  */
4335           char type = window->keymap[key].type;
4336           func = type == ISFUNC
4337                  ? InfoFunction(window->keymap[key].function)
4338                  : NULL;  /* function member is a Keymap if ISKMAP */
4339
4340           if (isprint (key) || (type == ISFUNC && func == NULL))
4341             {
4342             insert_and_search:
4343
4344               if (isearch_string_index + 2 >= isearch_string_size)
4345                 isearch_string = xrealloc
4346                   (isearch_string, isearch_string_size += 100);
4347
4348               isearch_string[isearch_string_index++] = key;
4349               isearch_string[isearch_string_index] = '\0';
4350               goto search_now;
4351             }
4352           else if (func == (VFunction *) isearch_forward
4353               || func == (VFunction *) isearch_backward)
4354             {
4355               /* If this key invokes an incremental search, then this
4356                  means that we will either search again in the same
4357                  direction, search again in the reverse direction, or
4358                  insert the last search string that was accepted through
4359                  incremental searching. */
4360               if ((func == (VFunction *) isearch_forward && dir > 0) ||
4361                   (func == (VFunction *) isearch_backward && dir < 0))
4362                 {
4363                   /* If the user has typed no characters, then insert the
4364                      last successful search into the current search string. */
4365                   if (isearch_string_index == 0)
4366                     {
4367                       /* Of course, there must be something to insert. */
4368                       if (last_isearch_accepted)
4369                         {
4370                           if (strlen ((char *) last_isearch_accepted) + 1
4371                               >= (unsigned int) isearch_string_size)
4372                             isearch_string = (char *)
4373                               xrealloc (isearch_string,
4374                                         isearch_string_size += 10 +
4375                                         strlen (last_isearch_accepted));
4376                           strcpy (isearch_string, last_isearch_accepted);
4377                           isearch_string_index = strlen (isearch_string);
4378                           goto search_now;
4379                         }
4380                       else
4381                         continue;
4382                     }
4383                   else
4384                     {
4385                       /* Search again in the same direction.  This means start
4386                          from a new place if the last search was successful. */
4387                       if (search_result == 0)
4388                         {
4389                           window->point += dir;
4390                           bnd.start = -1;
4391                         }
4392                     }
4393                 }
4394               else
4395                 {
4396                   /* Reverse the direction of the search. */
4397                   dir = -dir;
4398                 }
4399             }
4400           else if (func == (VFunction *) info_abort_key)
4401             {
4402               /* If C-g pressed, and the search is failing, pop the search
4403                  stack back to the last unfailed search. */
4404               if (isearch_states_index && (search_result != 0))
4405                 {
4406                   terminal_ring_bell ();
4407                   while (isearch_states_index && (search_result != 0))
4408                     pop_isearch
4409                       (window, &isearch_string_index, &dir, &search_result);
4410                   isearch_string[isearch_string_index] = '\0';
4411                   show_isearch_prompt (dir, (unsigned char *) isearch_string,
4412                       search_result);
4413                   continue;
4414                 }
4415               else
4416                 goto exit_search;
4417             }
4418           else
4419             goto exit_search;
4420         }
4421       else
4422         {
4423         exit_search:
4424           /* The character is not printable, or it has a function which is
4425              non-null.  Exit the search, remembering the search string.  If
4426              the key is not the same as the isearch_terminate_search_key,
4427              then push it into pending input. */
4428           if (isearch_string_index && func != (VFunction *) info_abort_key)
4429             {
4430               maybe_free (last_isearch_accepted);
4431               last_isearch_accepted = xstrdup (isearch_string);
4432             }
4433
4434           /* If the key is the isearch_terminate_search_key, but some buffered
4435              input is pending, it is almost invariably because the ESC key is
4436              actually the beginning of an escape sequence, like in case they
4437              pressed an arrow key.  So don't gobble the ESC key, push it back
4438              into pending input.  */
4439           /* FIXME: this seems like a kludge!  We need a more reliable
4440              mechanism to know when ESC is a separate key and when it is
4441              part of an escape sequence.  */
4442           if (key != RET  /* Emacs addicts want RET to get lost */
4443               && (key != isearch_terminate_search_key
4444                   || info_any_buffered_input_p ()))
4445             info_set_pending_input (key);
4446
4447           if (func == (VFunction *) info_abort_key)
4448             {
4449               if (isearch_states_index)
4450                 window_set_state (window, &orig_state);
4451             }
4452
4453           if (!echo_area_is_active)
4454             window_clear_echo_area ();
4455
4456           if (auto_footnotes_p)
4457             info_get_or_remove_footnotes (active_window);
4458
4459           isearch_is_active = 0;
4460           continue;
4461         }
4462
4463       /* Search for the contents of isearch_string. */
4464     search_now:
4465       show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4466
4467       /* If the search string includes upper-case letters, make the
4468          search case-sensitive.  */
4469       for (p = isearch_string; *p; p++)
4470         if (isupper (*p))
4471           {
4472             case_sensitive = 1;
4473             break;
4474           }
4475       
4476       /* Regex isearch means we better search again every time.  We
4477          might have had a failed search for "\", for example, but now we
4478          have "\.".  */
4479       if (use_regex)
4480         {
4481           search_result = info_search_internal (isearch_string,
4482                                                 window, dir, case_sensitive,
4483                                                 &bnd);
4484         }
4485       else if (search_result == 0)
4486         { /* We test for search_result being zero because a non-zero
4487              value means the string was not found in entire document. */
4488           /* Check to see if the current search string is right here.  If
4489              we are looking at it, then don't bother calling the search
4490              function. */
4491           if (((dir < 0) &&
4492                ((case_sensitive ? strncmp : mbsncasecmp)
4493                             (window->node->contents + window->point,
4494                              isearch_string, isearch_string_index) == 0)) ||
4495               ((dir > 0) &&
4496                ((window->point - isearch_string_index) >= 0) &&
4497                ((case_sensitive ? strncmp : mbsncasecmp)
4498                             (window->node->contents +
4499                              (window->point - (isearch_string_index - 1)),
4500                              isearch_string, isearch_string_index) == 0)))
4501             {
4502               if (dir > 0)
4503                 window->point++;
4504             }
4505           else
4506             search_result = info_search_internal (isearch_string,
4507                                                   window, dir, case_sensitive,
4508                                                   NULL);
4509         }
4510
4511       /* If this search failed, and we didn't already have a failed search,
4512          then ring the terminal bell. */
4513       if (search_result != 0 && last_search_result == 0)
4514         terminal_ring_bell ();
4515
4516     after_search:
4517       show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4518
4519       if (search_result == 0)
4520         {
4521           if ((mystate.node == window->node) &&
4522               (mystate.pagetop != window->pagetop))
4523             {
4524               int newtop = window->pagetop;
4525               window->pagetop = mystate.pagetop;
4526               set_window_pagetop (window, newtop);
4527             }
4528           display_update_one_window (window);
4529           display_cursor_at_point (window);
4530         }
4531
4532       last_search_result = search_result;
4533     }
4534
4535   /* Free the memory used to remember each search state. */
4536   free_isearch_states ();
4537
4538   /* Perhaps GC some file buffers. */
4539   info_gc_file_buffers ();
4540
4541   /* After searching, leave the window in the correct state. */
4542   if (!echo_area_is_active)
4543     window_clear_echo_area ();
4544 }
4545
4546 /* GC some file buffers.  A file buffer can be gc-ed if there we have
4547    no nodes in INFO_WINDOWS that reference this file buffer's contents.
4548    Garbage collecting a file buffer means to free the file buffers
4549    contents. */
4550 static void
4551 info_gc_file_buffers (void)
4552 {
4553   register int fb_index, iw_index, i;
4554   register FILE_BUFFER *fb;
4555   register INFO_WINDOW *iw;
4556
4557   if (!info_loaded_files)
4558     return;
4559
4560   for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4561     {
4562       int fb_referenced_p = 0;
4563
4564       /* If already gc-ed, do nothing. */
4565       if (!fb->contents)
4566         continue;
4567
4568       /* If this file had to be uncompressed, check to see if we should
4569          gc it.  This means that the user-variable "gc-compressed-files"
4570          is non-zero. */
4571       if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
4572         continue;
4573
4574       /* If this file's contents are not gc-able, move on. */
4575       if (fb->flags & N_CannotGC)
4576         continue;
4577
4578       /* Check each INFO_WINDOW to see if it has any nodes which reference
4579          this file. */
4580       for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
4581         {
4582           for (i = 0; iw->nodes && iw->nodes[i]; i++)
4583             {
4584               if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4585                   (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
4586                 {
4587                   fb_referenced_p = 1;
4588                   break;
4589                 }
4590             }
4591         }
4592
4593       /* If this file buffer wasn't referenced, free its contents. */
4594       if (!fb_referenced_p)
4595         {
4596           free (fb->contents);
4597           fb->contents = NULL;
4598         }
4599     }
4600 }
4601 \f
4602 /* **************************************************************** */
4603 /*                                                                  */
4604 /*                Traversing and Selecting References               */
4605 /*                                                                  */
4606 /* **************************************************************** */
4607
4608 /* Move to the next or previous cross reference in this node. */
4609 static int
4610 info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir)
4611 {
4612   long firstmenu, firstxref;
4613   long nextmenu, nextxref;
4614   long placement = -1;
4615   long start = 0;
4616   NODE *node = window->node;
4617   int save_use_regex = use_regex;
4618
4619   /* Most of our keywords contain * characters; don't use regexes.  */
4620   use_regex = 0;
4621
4622   if (dir < 0)
4623     start = node->nodelen;
4624
4625   /* This search is only allowed to fail if there is no menu or cross
4626      reference in the current node.  Otherwise, the first menu or xref
4627      found is moved to. */
4628
4629   firstmenu = info_search_in_node
4630     (INFO_MENU_ENTRY_LABEL, node, start, NULL, dir, 0);
4631
4632   /* FIRSTMENU may point directly to the line defining the menu.  Skip that
4633      and go directly to the first item. */
4634
4635   if (firstmenu != -1)
4636     {
4637       char *text = node->contents + firstmenu;
4638
4639       if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4640         firstmenu = info_search_in_node
4641           (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, NULL, dir, 0);
4642     }
4643
4644   firstxref =
4645     info_search_in_node (INFO_XREF_LABEL, node, start, NULL, dir, 0);
4646
4647 #if defined (HANDLE_MAN_PAGES)
4648   if ((firstxref == -1) && (node->flags & N_IsManPage))
4649     {
4650       firstxref = locate_manpage_xref (node, start, dir);
4651     }
4652 #endif /* HANDLE_MAN_PAGES */
4653
4654   if (firstmenu == -1 && firstxref == -1)
4655     {
4656       if (!cursor_movement_scrolls_p)
4657         info_error (msg_no_xref_node, NULL, NULL);
4658       use_regex = save_use_regex;
4659       return cursor_movement_scrolls_p;
4660     }
4661
4662   /* There is at least one cross reference or menu entry in this node.
4663      Try hard to find the next available one. */
4664
4665   nextmenu = info_search_in_node
4666     (INFO_MENU_ENTRY_LABEL, node, window->point + dir, NULL, dir, 0);
4667
4668   nextxref = info_search_in_node
4669     (INFO_XREF_LABEL, node, window->point + dir, NULL, dir, 0);
4670
4671 #if defined (HANDLE_MAN_PAGES)
4672   if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
4673     nextxref = locate_manpage_xref (node, window->point + dir, dir);
4674 #endif /* HANDLE_MAN_PAGES */
4675
4676   /* Ignore "Menu:" as a menu item. */
4677   if (nextmenu != -1)
4678     {
4679       char *text = node->contents + nextmenu;
4680
4681       if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4682         nextmenu = info_search_in_node
4683           (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, NULL, dir, 0);
4684     }
4685
4686   /* No more searches, back to whatever the user wanted.  */
4687   use_regex = save_use_regex;
4688
4689   /* If there is both a next menu entry, and a next xref entry, choose the
4690      one which occurs first.  Otherwise, select the one which actually
4691      appears in this node following point. */
4692   if (nextmenu != -1 && nextxref != -1)
4693     {
4694       if (((dir == 1) && (nextmenu < nextxref)) ||
4695           ((dir == -1) && (nextmenu > nextxref)))
4696         placement = nextmenu + 1;
4697       else
4698         placement = nextxref;
4699     }
4700   else if (nextmenu != -1)
4701     placement = nextmenu + 1;
4702   else if (nextxref != -1)
4703     placement = nextxref;
4704
4705   /* If there was neither a menu or xref entry appearing in this node after
4706      point, choose the first menu or xref entry appearing in this node. */
4707   if (placement == -1)
4708     {
4709       if (cursor_movement_scrolls_p)
4710         return 1;
4711       else
4712         {
4713           if (firstmenu != -1 && firstxref != -1)
4714             {
4715               if (((dir == 1) && (firstmenu < firstxref)) ||
4716                   ((dir == -1) && (firstmenu > firstxref)))
4717                 placement = firstmenu + 1;
4718               else
4719                 placement = firstxref;
4720             }
4721           else if (firstmenu != -1)
4722             placement = firstmenu + 1;
4723           else
4724             placement = firstxref;
4725         }
4726     }
4727   window->point = placement;
4728   window_adjust_pagetop (window);
4729   window->flags |= W_UpdateWindow;
4730   return 0;
4731 }
4732
4733 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
4734                       _("Move to the previous cross reference"))
4735 {
4736   if (count < 0)
4737     info_move_to_prev_xref (window, -count, key);
4738   else
4739     {
4740       while (info_move_to_xref (window, count, key, -1))
4741         {
4742           info_error_was_printed = 0;
4743           if (backward_move_node_structure (window, info_scroll_behaviour))
4744             break;
4745           move_to_new_line (window->line_count, window->line_count - 1,
4746                             window);
4747         }
4748     }
4749 }
4750
4751 DECLARE_INFO_COMMAND (info_move_to_next_xref,
4752                       _("Move to the next cross reference"))
4753 {
4754   if (count < 0)
4755     info_move_to_next_xref (window, -count, key);
4756   else
4757     {
4758       /* Note: This can cause some blinking when the next cross reference is
4759          located several nodes further. This effect can be easily suppressed
4760          by setting display_inhibited to 1, however this will also make
4761          error messages to be dumped on stderr, instead on the echo area. */ 
4762       while (info_move_to_xref (window, count, key, 1))
4763         {
4764           info_error_was_printed = 0;
4765           if (forward_move_node_structure (window, info_scroll_behaviour))
4766             break;
4767           move_to_new_line (0, 0, window);
4768         }
4769     }
4770 }
4771
4772 /* Select the menu item or reference that appears on this line. */
4773 DECLARE_INFO_COMMAND (info_select_reference_this_line,
4774                       _("Select reference or menu item appearing on this line"))
4775 {
4776   char *line;
4777
4778   if (window->line_starts)
4779     line = window->line_starts[window_line_of_point (window)];
4780   else
4781     line = "";
4782
4783   /* If this line contains a menu item, select that one. */
4784   if (strncmp ("* ", line, 2) == 0)
4785     info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
4786   else
4787     info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
4788 }
4789 \f
4790 /* **************************************************************** */
4791 /*                                                                  */
4792 /*                  Miscellaneous Info Commands                     */
4793 /*                                                                  */
4794 /* **************************************************************** */
4795
4796 /* What to do when C-g is pressed in a window. */
4797 DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
4798 {
4799   /* If error printing doesn't oridinarily ring the bell, do it now,
4800      since C-g always rings the bell.  Otherwise, let the error printer
4801      do it. */
4802   if (!info_error_rings_bell_p)
4803     terminal_ring_bell ();
4804   info_error (_("Quit"), NULL, NULL);
4805
4806   info_initialize_numeric_arg ();
4807   info_clear_pending_input ();
4808   info_last_executed_command = NULL;
4809 }
4810
4811 /* Move the cursor to the desired line of the window. */
4812 DECLARE_INFO_COMMAND (info_move_to_window_line,
4813    _("Move the cursor to a specific line of the window"))
4814 {
4815   int line;
4816
4817   /* With no numeric argument of any kind, default to the center line. */
4818   if (!info_explicit_arg && count == 1)
4819     line = (window->height / 2) + window->pagetop;
4820   else
4821     {
4822       if (count < 0)
4823         line = (window->height + count) + window->pagetop;
4824       else
4825         line = window->pagetop + count;
4826     }
4827
4828   /* If the line doesn't appear in this window, make it do so. */
4829   if ((line - window->pagetop) >= window->height)
4830     line = window->pagetop + (window->height - 1);
4831
4832   /* If the line is too small, make it fit. */
4833   if (line < window->pagetop)
4834     line = window->pagetop;
4835
4836   /* If the selected line is past the bottom of the node, force it back. */
4837   if (line >= window->line_count)
4838     line = window->line_count - 1;
4839
4840   window->point = (window->line_starts[line] - window->node->contents);
4841 }
4842
4843 /* Clear the screen and redraw its contents.  Given a numeric argument,
4844    move the line the cursor is on to the COUNT'th line of the window. */
4845 DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
4846 {
4847   if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4848     {
4849       terminal_clear_screen ();
4850       display_clear_display (the_display);
4851       window_mark_chain (windows, W_UpdateWindow);
4852       display_update_display (windows);
4853     }
4854   else
4855     {
4856       int desired_line, point_line;
4857       int new_pagetop;
4858
4859       point_line = window_line_of_point (window) - window->pagetop;
4860
4861       if (count < 0)
4862         desired_line = window->height + count;
4863       else
4864         desired_line = count;
4865
4866       if (desired_line < 0)
4867         desired_line = 0;
4868
4869       if (desired_line >= window->height)
4870         desired_line = window->height - 1;
4871
4872       if (desired_line == point_line)
4873         return;
4874
4875       new_pagetop = window->pagetop + (point_line - desired_line);
4876
4877       set_window_pagetop (window, new_pagetop);
4878     }
4879 }
4880 /* This command does nothing.  It is the fact that a key is bound to it
4881    that has meaning.  See the code at the top of info_session (). */
4882 DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
4883 {}
4884
4885 \f
4886 /* **************************************************************** */
4887 /*                                                                  */
4888 /*               Reading Keys and Dispatching on Them               */
4889 /*                                                                  */
4890 /* **************************************************************** */
4891
4892 /* Declaration only.  Special cased in info_dispatch_on_key ().
4893    Doc string is to avoid ugly results with describe_key etc.  */
4894 DECLARE_INFO_COMMAND (info_do_lowercase_version,
4895                       _("Run command bound to this key's lowercase variant"))
4896 {}
4897
4898 static void
4899 dispatch_error (char *keyseq)
4900 {
4901   char *rep;
4902
4903   rep = pretty_keyseq (keyseq);
4904
4905   if (!echo_area_is_active)
4906     info_error (_("Unknown command (%s)."), rep, NULL);
4907   else
4908     {
4909       char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid")));
4910       sprintf (temp, _("`%s' is invalid"), rep);
4911       terminal_ring_bell ();
4912       inform_in_echo_area (temp);
4913       free (temp);
4914     }
4915 }
4916
4917 /* Keeping track of key sequences. */
4918 static char *info_keyseq = NULL;
4919 static int info_keyseq_index = 0;
4920 static int info_keyseq_size = 0;
4921 static int info_keyseq_displayed_p = 0;
4922
4923 /* Initialize the length of the current key sequence. */
4924 void
4925 initialize_keyseq (void)
4926 {
4927   info_keyseq_index = 0;
4928   info_keyseq_displayed_p = 0;
4929 }
4930
4931 /* Add CHARACTER to the current key sequence. */
4932 void
4933 add_char_to_keyseq (char character)
4934 {
4935   if (info_keyseq_index + 2 >= info_keyseq_size)
4936     info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
4937
4938   info_keyseq[info_keyseq_index++] = character;
4939   info_keyseq[info_keyseq_index] = '\0';
4940 }
4941
4942 /* Display the current value of info_keyseq.  If argument EXPECTING is
4943    non-zero, input is expected to be read after the key sequence is
4944    displayed, so add an additional prompting character to the sequence. */
4945 static void
4946 display_info_keyseq (int expecting_future_input)
4947 {
4948   char *rep;
4949
4950   rep = pretty_keyseq (info_keyseq);
4951   if (expecting_future_input)
4952     strcat (rep, "-");
4953
4954   if (echo_area_is_active)
4955     inform_in_echo_area (rep);
4956   else
4957     {
4958       window_message_in_echo_area (rep, NULL, NULL);
4959       display_cursor_at_point (active_window);
4960     }
4961   info_keyseq_displayed_p = 1;
4962 }
4963
4964 /* Called by interactive commands to read a keystroke. */
4965 unsigned char
4966 info_get_another_input_char (void)
4967 {
4968   int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
4969
4970   /* If there isn't any input currently available, then wait a
4971      moment looking for input.  If we don't get it fast enough,
4972      prompt a little bit with the current key sequence. */
4973   if (!info_keyseq_displayed_p)
4974     {
4975       ready = 1;
4976       if (!info_any_buffered_input_p () &&
4977           !info_input_pending_p ())
4978         {
4979 #if defined (FD_SET)
4980           struct timeval timer;
4981           fd_set readfds;
4982
4983           FD_ZERO (&readfds);
4984           FD_SET (fileno (info_input_stream), &readfds);
4985           timer.tv_sec = 1;
4986           timer.tv_usec = 750;
4987           ready = select (fileno(info_input_stream)+1, &readfds,
4988                           NULL, NULL, &timer);
4989 #else
4990           ready = 0;
4991 #endif /* FD_SET */
4992       }
4993     }
4994
4995   if (!ready)
4996     display_info_keyseq (1);
4997
4998   return info_get_input_char ();
4999 }
5000
5001 /* Do the command associated with KEY in MAP.  If the associated command is
5002    really a keymap, then read another key, and dispatch into that map. */
5003 void
5004 info_dispatch_on_key (unsigned char key, Keymap map)
5005 {
5006 #if !defined(INFOKEY)
5007   if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
5008     {
5009       if (map[ESC].type == ISKMAP)
5010         {
5011           map = (Keymap)map[ESC].function;
5012           add_char_to_keyseq (ESC);
5013           key = UnMeta (key);
5014           info_dispatch_on_key (key, map);
5015         }
5016       else
5017         {
5018           dispatch_error (info_keyseq);
5019         }
5020       return;
5021     }
5022 #endif /* INFOKEY */
5023
5024   switch (map[key].type)
5025     {
5026     case ISFUNC:
5027       {
5028         VFunction *func;
5029
5030         func = InfoFunction(map[key].function);
5031         if (func != NULL)
5032           {
5033             /* Special case info_do_lowercase_version (). */
5034             if (func == (VFunction *) info_do_lowercase_version)
5035               {
5036 #if defined(INFOKEY)
5037                 unsigned char lowerkey;
5038
5039                 lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
5040                 if (lowerkey == key)
5041                   {
5042                     add_char_to_keyseq (key);
5043                     dispatch_error (info_keyseq);
5044                     return;
5045                   }
5046                 info_dispatch_on_key (lowerkey, map);
5047 #else /* !INFOKEY */
5048                 info_dispatch_on_key (tolower (key), map);
5049 #endif /* INFOKEY */
5050                 return;
5051               }
5052
5053             add_char_to_keyseq (key);
5054
5055             if (info_keyseq_displayed_p)
5056               display_info_keyseq (0);
5057
5058             {
5059               WINDOW *where;
5060
5061               where = active_window;
5062               (*InfoFunction(map[key].function))
5063                 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
5064
5065               /* If we have input pending, then the last command was a prefix
5066                  command.  Don't change the value of the last function vars.
5067                  Otherwise, remember the last command executed in the var
5068                  appropriate to the window in which it was executed. */
5069               if (!info_input_pending_p ())
5070                 {
5071                   if (where == the_echo_area)
5072                     ea_last_executed_command = InfoFunction(map[key].function);
5073                   else
5074                     info_last_executed_command = InfoFunction(map[key].function);
5075                 }
5076             }
5077           }
5078         else
5079           {
5080             add_char_to_keyseq (key);
5081             dispatch_error (info_keyseq);
5082             return;
5083           }
5084       }
5085       break;
5086
5087     case ISKMAP:
5088       add_char_to_keyseq (key);
5089       if (map[key].function != NULL)
5090         {
5091           unsigned char newkey;
5092
5093           newkey = info_get_another_input_char ();
5094           info_dispatch_on_key (newkey, (Keymap)map[key].function);
5095         }
5096       else
5097         {
5098           dispatch_error (info_keyseq);
5099           return;
5100         }
5101       break;
5102     }
5103 }
5104 \f
5105 /* **************************************************************** */
5106 /*                                                                  */
5107 /*                      Numeric Arguments                           */
5108 /*                                                                  */
5109 /* **************************************************************** */
5110
5111 /* Handle C-u style numeric args, as well as M--, and M-digits. */
5112
5113 /* Non-zero means that an explicit argument has been passed to this
5114    command, as in C-u C-v. */
5115 int info_explicit_arg = 0;
5116
5117 /* The sign of the numeric argument. */
5118 int info_numeric_arg_sign = 1;
5119
5120 /* The value of the argument itself. */
5121 int info_numeric_arg = 1;
5122
5123 /* Add the current digit to the argument in progress. */
5124 DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
5125                       _("Add this digit to the current numeric argument"))
5126 {
5127   info_numeric_arg_digit_loop (window, 0, key);
5128 }
5129
5130 /* C-u, universal argument.  Multiply the current argument by 4.
5131    Read a key.  If the key has nothing to do with arguments, then
5132    dispatch on it.  If the key is the abort character then abort. */
5133 DECLARE_INFO_COMMAND (info_universal_argument,
5134                       _("Start (or multiply by 4) the current numeric argument"))
5135 {
5136   info_numeric_arg *= 4;
5137   info_numeric_arg_digit_loop (window, 0, 0);
5138 }
5139
5140 /* Create a default argument. */
5141 void
5142 info_initialize_numeric_arg (void)
5143 {
5144   info_numeric_arg = info_numeric_arg_sign = 1;
5145   info_explicit_arg = 0;
5146 }
5147
5148 DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
5149                       _("Internally used by \\[universal-argument]"))
5150 {
5151   unsigned char pure_key;
5152   Keymap keymap = window->keymap;
5153
5154   while (1)
5155     {
5156       if (key)
5157         pure_key = key;
5158       else
5159         {
5160           if (display_was_interrupted_p && !info_any_buffered_input_p ())
5161             display_update_display (windows);
5162
5163           if (active_window != the_echo_area)
5164             display_cursor_at_point (active_window);
5165
5166           pure_key = key = info_get_another_input_char ();
5167
5168 #if !defined(INFOKEY)
5169           if (Meta_p (key))
5170             add_char_to_keyseq (ESC);
5171
5172           add_char_to_keyseq (UnMeta (key));
5173 #else /* defined(INFOKEY) */
5174           add_char_to_keyseq (key);
5175 #endif /* defined(INFOKEY) */
5176         }
5177
5178 #if !defined(INFOKEY)
5179       if (Meta_p (key))
5180         key = UnMeta (key);
5181 #endif /* !defined(INFOKEY) */
5182
5183       if (keymap[key].type == ISFUNC
5184           && InfoFunction(keymap[key].function)
5185               == (VFunction *) info_universal_argument)
5186         {
5187           info_numeric_arg *= 4;
5188           key = 0;
5189           continue;
5190         }
5191
5192 #if defined(INFOKEY)
5193       if (Meta_p (key))
5194         key = UnMeta (key);
5195 #endif /* !defined(INFOKEY) */
5196
5197
5198       if (isdigit (key))
5199         {
5200           if (info_explicit_arg)
5201             info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
5202           else
5203             info_numeric_arg = (key - '0');
5204           info_explicit_arg = 1;
5205         }
5206       else
5207         {
5208           if (key == '-' && !info_explicit_arg)
5209             {
5210               info_numeric_arg_sign = -1;
5211               info_numeric_arg = 1;
5212             }
5213           else
5214             {
5215               info_keyseq_index--;
5216               info_dispatch_on_key (pure_key, keymap);
5217               return;
5218             }
5219         }
5220       key = 0;
5221     }
5222 }
5223 \f
5224 /* **************************************************************** */
5225 /*                                                                  */
5226 /*                      Input Character Buffering                   */
5227 /*                                                                  */
5228 /* **************************************************************** */
5229
5230 /* Character waiting to be read next. */
5231 static int pending_input_character = 0;
5232
5233 /* How to make there be no pending input. */
5234 static void
5235 info_clear_pending_input (void)
5236 {
5237   pending_input_character = 0;
5238 }
5239
5240 /* How to set the pending input character. */
5241 static void
5242 info_set_pending_input (unsigned char key)
5243 {
5244   pending_input_character = key;
5245 }
5246
5247 /* How to see if there is any pending input. */
5248 unsigned char
5249 info_input_pending_p (void)
5250 {
5251   return pending_input_character;
5252 }
5253
5254 /* Largest number of characters that we can read in advance. */
5255 #define MAX_INFO_INPUT_BUFFERING 512
5256
5257 static int pop_index = 0, push_index = 0;
5258 static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
5259
5260 /* Add KEY to the buffer of characters to be read. */
5261 static void
5262 info_push_typeahead (unsigned char key)
5263 {
5264   /* Flush all pending input in the case of C-g pressed. */
5265   if (key == Control ('g'))
5266     {
5267       push_index = pop_index;
5268       info_set_pending_input (Control ('g'));
5269     }
5270   else
5271     {
5272       info_input_buffer[push_index++] = key;
5273       if ((unsigned int) push_index >= sizeof (info_input_buffer))
5274         push_index = 0;
5275     }
5276 }
5277
5278 /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
5279 static int
5280 info_input_buffer_space_available (void)
5281 {
5282   if (pop_index > push_index)
5283     return pop_index - push_index;
5284   else
5285     return sizeof (info_input_buffer) - (push_index - pop_index);
5286 }
5287
5288 /* Get a key from the buffer of characters to be read.
5289    Return the key in KEY.
5290    Result is non-zero if there was a key, or 0 if there wasn't. */
5291 static int
5292 info_get_key_from_typeahead (unsigned char *key)
5293 {
5294   if (push_index == pop_index)
5295     return 0;
5296
5297   *key = info_input_buffer[pop_index++];
5298
5299   if ((unsigned int) pop_index >= sizeof (info_input_buffer))
5300     pop_index = 0;
5301
5302   return 1;
5303 }
5304
5305 int
5306 info_any_buffered_input_p (void)
5307 {
5308   info_gather_typeahead ();
5309   return push_index != pop_index;
5310 }
5311
5312 /* If characters are available to be read, then read them and stuff them into
5313    info_input_buffer.  Otherwise, do nothing. */
5314 void
5315 info_gather_typeahead (void)
5316 {
5317   register int i = 0;
5318   int tty, space_avail;
5319   long chars_avail;
5320   unsigned char input[MAX_INFO_INPUT_BUFFERING];
5321
5322   tty = fileno (info_input_stream);
5323   chars_avail = 0;
5324
5325   space_avail = info_input_buffer_space_available ();
5326
5327   /* If we can just find out how many characters there are to read, do so. */
5328 #if defined (FIONREAD)
5329   {
5330     ioctl (tty, FIONREAD, &chars_avail);
5331
5332     if (chars_avail > space_avail)
5333       chars_avail = space_avail;
5334
5335     if (chars_avail)
5336       chars_avail = read (tty, &input[0], chars_avail);
5337   }
5338 #else /* !FIONREAD */
5339 #  if defined (O_NDELAY)
5340   {
5341     int flags;
5342
5343     flags = fcntl (tty, F_GETFL, 0);
5344
5345     fcntl (tty, F_SETFL, (flags | O_NDELAY));
5346       chars_avail = read (tty, &input[0], space_avail);
5347     fcntl (tty, F_SETFL, flags);
5348
5349     if (chars_avail == -1)
5350       chars_avail = 0;
5351   }
5352 #  else  /* !O_NDELAY */
5353 #   ifdef __DJGPP__
5354   {
5355     extern long pc_term_chars_avail (void);
5356
5357     if (isatty (tty))
5358       chars_avail = pc_term_chars_avail ();
5359     else
5360       {
5361         /* We could be more accurate by calling ltell, but we have no idea
5362            whether tty is buffered by stdio functions, and if so, how many
5363            characters are already waiting in the buffer.  So we punt.  */
5364         struct stat st;
5365
5366         if (fstat (tty, &st) < 0)
5367           chars_avail = 1;
5368         else
5369           chars_avail = st.st_size;
5370       }
5371     if (chars_avail > space_avail)
5372       chars_avail = space_avail;
5373     if (chars_avail)
5374       chars_avail = read (tty, &input[0], chars_avail);
5375   }
5376 #   endif/* __DJGPP__ */
5377 #  endif /* O_NDELAY */
5378 #endif /* !FIONREAD */
5379
5380   while (i < chars_avail)
5381     {
5382       info_push_typeahead (input[i]);
5383       i++;
5384     }
5385 }
5386
5387 /* How to read a single character. */
5388 unsigned char
5389 info_get_input_char (void)
5390 {
5391   unsigned char keystroke;
5392
5393   info_gather_typeahead ();
5394
5395   if (pending_input_character)
5396     {
5397       keystroke = pending_input_character;
5398       pending_input_character = 0;
5399     }
5400   else if (info_get_key_from_typeahead (&keystroke) == 0)
5401     {
5402       int rawkey;
5403       unsigned char c;
5404       int tty = fileno (info_input_stream);
5405
5406       /* Using stream I/O causes FIONREAD etc to fail to work
5407          so unless someone can find a portable way of finding
5408          out how many characters are currently buffered, we
5409          should stay with away from stream I/O.
5410          --Egil Kvaleberg <egilk@sn.no>, January 1997.  */
5411 #ifdef EINTR
5412       /* Keep reading if we got EINTR, so that we don't just exit.
5413          --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
5414          22 Dec 1997.  */
5415       {
5416         int n;
5417         do
5418           n = read (tty, &c, 1);
5419         while (n == -1 && errno == EINTR);
5420         rawkey = n == 1 ? c : EOF;
5421       }
5422 #else
5423       rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5424 #endif
5425
5426       keystroke = rawkey;
5427
5428       if (rawkey == EOF)
5429         {
5430           if (info_input_stream != stdin)
5431             {
5432               fclose (info_input_stream);
5433               info_input_stream = stdin;
5434               tty = fileno (info_input_stream);
5435               display_inhibited = 0;
5436               display_update_display (windows);
5437               display_cursor_at_point (active_window);
5438               rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5439               keystroke = rawkey;
5440             }
5441
5442           if (rawkey == EOF)
5443             {
5444               terminal_unprep_terminal ();
5445               close_dribble_file ();
5446               xexit (0);
5447             }
5448         }
5449     }
5450
5451   if (info_dribble_file)
5452     dribble (keystroke);
5453
5454   return keystroke;
5455 }