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