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