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