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