OSDN Git Service

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