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