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