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