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