home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / OS2 / gnuinfo.zip / info / session.c < prev    next >
C/C++ Source or Header  |  1997-12-27  |  126KB  |  4,253 lines

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