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