Back to index

tetex-bin  3.0
session.c
Go to the documentation of this file.
00001 /* session.c -- user windowing interface to Info.
00002    $Id: session.c,v 1.16 2004/12/14 00:15:36 karl Exp $
00003 
00004    Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
00005    Free Software Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021    Originally written by Brian Fox (bfox@ai.mit.edu). */
00022 
00023 #include "info.h"
00024 #include "search.h"
00025 #include <sys/ioctl.h>
00026 
00027 #if defined (HAVE_SYS_TIME_H)
00028 #  include <sys/time.h>
00029 #  define HAVE_STRUCT_TIMEVAL
00030 #endif /* HAVE_SYS_TIME_H */
00031 
00032 #if defined (HANDLE_MAN_PAGES)
00033 #  include "man.h"
00034 #endif
00035 
00036 static void info_clear_pending_input (void);
00037 static void info_set_pending_input (unsigned char key);
00038 static void info_handle_pointer (char *label, WINDOW *window);
00039 static void display_info_keyseq (int expecting_future_input);
00040 char *node_printed_rep (NODE *node);
00041 
00042 /* **************************************************************** */
00043 /*                                                                  */
00044 /*                   Running an Info Session                        */
00045 /*                                                                  */
00046 /* **************************************************************** */
00047 
00048 /* The place that we are reading input from. */
00049 static FILE *info_input_stream = NULL;
00050 
00051 /* The last executed command. */
00052 VFunction *info_last_executed_command = NULL;
00053 
00054 /* Becomes non-zero when 'q' is typed to an Info window. */
00055 int quit_info_immediately = 0;
00056 
00057 /* Array of structures describing for each window which nodes have been
00058    visited in that window. */
00059 INFO_WINDOW **info_windows = NULL;
00060 
00061 /* Where to add the next window, if we need to add one. */
00062 static int info_windows_index = 0;
00063 
00064 /* Number of slots allocated to `info_windows'. */
00065 static int info_windows_slots = 0;
00066 
00067 void remember_window_and_node (WINDOW *window, NODE *node);
00068 void forget_window_and_nodes (WINDOW *window);
00069 void display_startup_message_and_start (void);
00070 
00071 /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
00072    For each loaded node, create a new window.  Always split the largest of the
00073    available windows. */
00074 void
00075 begin_multiple_window_info_session (char *filename, char **nodenames)
00076 {
00077   register int i;
00078   WINDOW *window = (WINDOW *)NULL;
00079 
00080   for (i = 0; nodenames[i]; i++)
00081     {
00082       NODE *node;
00083 
00084       node = info_get_node (filename, nodenames[i]);
00085 
00086       if (!node)
00087         break;
00088 
00089       /* If this is the first node, initialize the info session. */
00090       if (!window)
00091         {
00092           initialize_info_session (node, 1);
00093           window = active_window;
00094         }
00095       else
00096         {
00097           /* Find the largest window in WINDOWS, and make that be the active
00098              one.  Then split it and add our window and node to the list
00099              of remembered windows and nodes.  Then tile the windows. */
00100           WINDOW *win, *largest = NULL;
00101           int max_height = 0;
00102 
00103           for (win = windows; win; win = win->next)
00104             if (win->height > max_height)
00105               {
00106                 max_height = win->height;
00107                 largest = win;
00108               }
00109 
00110           if (!largest)
00111             {
00112               display_update_display (windows);
00113               info_error ((char *) msg_cant_find_window, NULL, NULL);
00114               info_session ();
00115               xexit (0);
00116             }
00117 
00118           active_window = largest;
00119           window = window_make_window (node);
00120           if (window)
00121             {
00122               window_tile_windows (TILE_INTERNALS);
00123               remember_window_and_node (window, node);
00124             }
00125           else
00126             {
00127               display_update_display (windows);
00128               info_error ((char *) msg_win_too_small, NULL, NULL);
00129               info_session ();
00130               xexit (0);
00131             }
00132         }
00133     }
00134   display_startup_message_and_start ();
00135 }
00136 
00137 /* Start an info session with INITIAL_NODE, and an error message in the echo
00138    area made from FORMAT and ARG. */
00139 void
00140 begin_info_session_with_error (NODE *initial_node, char *format,
00141     void *arg1, void *arg2)
00142 {
00143   initialize_info_session (initial_node, 1);
00144   info_error (format, arg1, arg2);
00145   info_session ();
00146 }
00147 
00148 /* Start an info session with INITIAL_NODE. */
00149 void
00150 begin_info_session (NODE *initial_node)
00151 {
00152   initialize_info_session (initial_node, 1);
00153   display_startup_message_and_start ();
00154 }
00155 
00156 void
00157 display_startup_message_and_start (void)
00158 {
00159   char *format;
00160 
00161   format = replace_in_documentation
00162     ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."),
00163      0);
00164 
00165   window_message_in_echo_area (format, VERSION, NULL);
00166   info_session ();
00167 }
00168 
00169 /* Run an info session with an already initialized window and node. */
00170 void
00171 info_session (void)
00172 {
00173   display_update_display (windows);
00174   info_last_executed_command = NULL;
00175   info_read_and_dispatch ();
00176   /* On program exit, leave the cursor at the bottom of the window, and
00177      restore the terminal I/O. */
00178   terminal_goto_xy (0, screenheight - 1);
00179   terminal_clear_to_eol ();
00180   fflush (stdout);
00181   terminal_unprep_terminal ();
00182   close_dribble_file ();
00183 }
00184 
00185 /* Here is a window-location dependent event loop.  Called from the
00186    functions info_session (), and from read_xxx_in_echo_area (). */
00187 void
00188 info_read_and_dispatch (void)
00189 {
00190   unsigned char key;
00191   int done;
00192   done = 0;
00193 
00194   while (!done && !quit_info_immediately)
00195     {
00196       int lk = 0;
00197 
00198       /* If we haven't just gone up or down a line, there is no
00199          goal column for this window. */
00200       if ((info_last_executed_command != (VFunction *) info_next_line) &&
00201           (info_last_executed_command != (VFunction *) info_prev_line))
00202         active_window->goal_column = -1;
00203 
00204       if (echo_area_is_active)
00205         {
00206           lk = echo_area_last_command_was_kill;
00207           echo_area_prep_read ();
00208         }
00209 
00210       if (!info_any_buffered_input_p ())
00211         display_update_display (windows);
00212 
00213       display_cursor_at_point (active_window);
00214       info_initialize_numeric_arg ();
00215 
00216       initialize_keyseq ();
00217       key = info_get_input_char ();
00218 
00219       /* No errors yet.  We just read a character, that's all.  Only clear
00220          the echo_area if it is not currently active. */
00221       if (!echo_area_is_active)
00222         window_clear_echo_area ();
00223 
00224       info_error_was_printed = 0;
00225 
00226       /* Do the selected command. */
00227       info_dispatch_on_key (key, active_window->keymap);
00228 
00229       if (echo_area_is_active)
00230         {
00231           /* Echo area commands that do killing increment the value of
00232              ECHO_AREA_LAST_COMMAND_WAS_KILL.  Thus, if there is no
00233              change in the value of this variable, the last command
00234              executed was not a kill command. */
00235           if (lk == echo_area_last_command_was_kill)
00236             echo_area_last_command_was_kill = 0;
00237 
00238           if (ea_last_executed_command == (VFunction *) ea_newline ||
00239               info_aborted_echo_area)
00240             {
00241               ea_last_executed_command = (VFunction *)NULL;
00242               done = 1;
00243             }
00244 
00245           if (info_last_executed_command == (VFunction *) info_quit)
00246             quit_info_immediately = 1;
00247         }
00248       else if (info_last_executed_command == (VFunction *) info_quit)
00249         done = 1;
00250     }
00251 }
00252 
00253 /* Found in signals.c */
00254 extern void initialize_info_signal_handler (void );
00255 
00256 /* Initialize the first info session by starting the terminal, window,
00257    and display systems.  If CLEAR_SCREEN is 0, don't clear the screen.  */
00258 void
00259 initialize_info_session (NODE *node, int clear_screen)
00260 {
00261   char *term_name = getenv ("TERM");
00262   terminal_initialize_terminal (term_name);
00263 
00264   if (terminal_is_dumb_p)
00265     {
00266       if (!term_name)
00267         term_name = "dumb";
00268 
00269       info_error ((char *) msg_term_too_dumb, term_name, NULL);
00270       xexit (1);
00271     }
00272 
00273   if (clear_screen)
00274     {
00275       terminal_prep_terminal ();
00276       terminal_clear_screen ();
00277     }
00278 
00279   initialize_info_keymaps ();
00280   window_initialize_windows (screenwidth, screenheight);
00281   initialize_info_signal_handler ();
00282   display_initialize_display (screenwidth, screenheight);
00283   info_set_node_of_window (0, active_window, node);
00284 
00285   /* Tell the window system how to notify us when a window needs to be
00286      asynchronously deleted (e.g., user resizes window very small). */
00287   window_deletion_notifier = (VFunction *) forget_window_and_nodes;
00288 
00289   /* If input has not been redirected yet, make it come from unbuffered
00290      standard input. */
00291   if (!info_input_stream)
00292     {
00293       setbuf (stdin, NULL);
00294       info_input_stream = stdin;
00295     }
00296 
00297   info_windows_initialized_p = 1;
00298 }
00299 
00300 /* Tell Info that input is coming from the file FILENAME. */
00301 void
00302 info_set_input_from_file (char *filename)
00303 {
00304   FILE *stream;
00305 
00306   /* Input may include binary characters.  */
00307   stream = fopen (filename, FOPEN_RBIN);
00308 
00309   if (!stream)
00310     return;
00311 
00312   if ((info_input_stream != (FILE *)NULL) &&
00313       (info_input_stream != stdin))
00314     fclose (info_input_stream);
00315 
00316   info_input_stream = stream;
00317 
00318   if (stream != stdin)
00319     display_inhibited = 1;
00320 }
00321 
00322 /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
00323 static INFO_WINDOW *
00324 get_info_window_of_window (WINDOW *window)
00325 {
00326   register int i;
00327   INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
00328 
00329   for (i = 0; info_windows && (info_win = info_windows[i]); i++)
00330     if (info_win->window == window)
00331       break;
00332 
00333   return (info_win);
00334 }
00335 
00336 /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
00337    values if the window and node are the same as the current one being
00338    displayed. */
00339 void
00340 set_remembered_pagetop_and_point (WINDOW *window)
00341 {
00342   INFO_WINDOW *info_win;
00343 
00344   info_win = get_info_window_of_window (window);
00345 
00346   if (!info_win)
00347     return;
00348 
00349   if (info_win->nodes_index &&
00350       (info_win->nodes[info_win->current] == window->node))
00351     {
00352       info_win->pagetops[info_win->current] = window->pagetop;
00353       info_win->points[info_win->current] = window->point;
00354     }
00355 }
00356 
00357 void
00358 remember_window_and_node (WINDOW *window, NODE *node)
00359 {
00360   /* See if we already have this window in our list. */
00361   INFO_WINDOW *info_win = get_info_window_of_window (window);
00362 
00363   /* If the window wasn't already on our list, then make a new entry. */
00364   if (!info_win)
00365     {
00366       info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
00367       info_win->window = window;
00368       info_win->nodes = (NODE **)NULL;
00369       info_win->pagetops = (int *)NULL;
00370       info_win->points = (long *)NULL;
00371       info_win->current = 0;
00372       info_win->nodes_index = 0;
00373       info_win->nodes_slots = 0;
00374 
00375       add_pointer_to_array (info_win, info_windows_index, info_windows,
00376                             info_windows_slots, 10, INFO_WINDOW *);
00377     }
00378 
00379   /* If this node, the current pagetop, and the current point are the
00380      same as the current saved node and pagetop, don't really add this to
00381      the list of history nodes.  This may happen only at the very
00382      beginning of the program, I'm not sure.  --karl  */
00383   if (info_win->nodes
00384       && info_win->current >= 0
00385       && info_win->nodes[info_win->current]->contents == node->contents
00386       && info_win->pagetops[info_win->current] == window->pagetop
00387       && info_win->points[info_win->current] == window->point)
00388   return;
00389 
00390   /* Remember this node, the currently displayed pagetop, and the current
00391      location of point in this window.  Because we are updating pagetops
00392      and points as well as nodes, it is more efficient to avoid the
00393      add_pointer_to_array macro here. */
00394   if (info_win->nodes_index + 2 >= info_win->nodes_slots)
00395     {
00396       info_win->nodes_slots += 20;
00397       info_win->nodes = (NODE **) xrealloc (info_win->nodes,
00398                                       info_win->nodes_slots * sizeof (NODE *));
00399       info_win->pagetops = (int *) xrealloc (info_win->pagetops,
00400                                       info_win->nodes_slots * sizeof (int));
00401       info_win->points = (long *) xrealloc (info_win->points,
00402                                       info_win->nodes_slots * sizeof (long));
00403     }
00404 
00405   info_win->nodes[info_win->nodes_index] = node;
00406   info_win->pagetops[info_win->nodes_index] = window->pagetop;
00407   info_win->points[info_win->nodes_index] = window->point;
00408   info_win->current = info_win->nodes_index++;
00409   info_win->nodes[info_win->nodes_index] = NULL;
00410   info_win->pagetops[info_win->nodes_index] = 0;
00411   info_win->points[info_win->nodes_index] = 0;
00412 }
00413 
00414 #define DEBUG_FORGET_WINDOW_AND_NODES
00415 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
00416 static void
00417 consistency_check_info_windows (void)
00418 {
00419   register int i;
00420 
00421   for (i = 0; i < info_windows_index; i++)
00422     {
00423       WINDOW *win;
00424 
00425       for (win = windows; win; win = win->next)
00426         if (win == info_windows[i]->window)
00427           break;
00428 
00429       if (!win)
00430         abort ();
00431     }
00432 }
00433 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
00434 
00435 /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
00436 void
00437 forget_window_and_nodes (WINDOW *window)
00438 {
00439   register int i;
00440   INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
00441 
00442   for (i = 0; info_windows && (info_win = info_windows[i]); i++)
00443     if (info_win->window == window)
00444       break;
00445 
00446   /* If we found the window to forget, then do so. */
00447   if (info_win)
00448     {
00449       while (i < info_windows_index)
00450         {
00451           info_windows[i] = info_windows[i + 1];
00452           i++;
00453         }
00454 
00455       info_windows_index--;
00456       info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
00457 
00458       if (info_win->nodes)
00459         {
00460           /* Free the node structures which held onto internal node contents
00461              here.  This doesn't free the contents; we have a garbage collector
00462              which does that. */
00463           for (i = 0; info_win->nodes[i]; i++)
00464             if (internal_info_node_p (info_win->nodes[i]))
00465               free (info_win->nodes[i]);
00466           free (info_win->nodes);
00467 
00468           maybe_free (info_win->pagetops);
00469           maybe_free (info_win->points);
00470         }
00471 
00472       free (info_win);
00473     }
00474 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
00475   consistency_check_info_windows ();
00476 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
00477 }
00478 
00479 /* Set WINDOW to show NODE.  Remember the new window in our list of Info
00480    windows.  If we are doing automatic footnote display, also try to display
00481    the footnotes for this window.  If REMEMBER is nonzero, first call
00482    set_remembered_pagetop_and_point.  */
00483 void
00484 info_set_node_of_window (int remember, WINDOW *window, NODE *node)
00485 {
00486   if (remember)
00487     set_remembered_pagetop_and_point (window);
00488 
00489   /* Put this node into the window. */
00490   window_set_node_of_window (window, node);
00491 
00492   /* Remember this node and window in our list of info windows. */
00493   remember_window_and_node (window, node);
00494 
00495   /* If doing auto-footnote display/undisplay, show the footnotes belonging
00496      to this window's node. */
00497   if (auto_footnotes_p)
00498     info_get_or_remove_footnotes (window);
00499 }
00500 
00501 
00502 /* **************************************************************** */
00503 /*                                                                  */
00504 /*                     Info Movement Commands                       */
00505 /*                                                                  */
00506 /* **************************************************************** */
00507 
00508 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
00509    to do so. */
00510 void
00511 set_window_pagetop (WINDOW *window, int desired_top)
00512 {
00513   int point_line, old_pagetop;
00514 
00515   if (desired_top < 0)
00516     desired_top = 0;
00517   else if (desired_top > window->line_count)
00518     desired_top = window->line_count - 1;
00519 
00520   if (window->pagetop == desired_top)
00521     return;
00522 
00523   old_pagetop = window->pagetop;
00524   window->pagetop = desired_top;
00525 
00526   /* Make sure that point appears in this window. */
00527   point_line = window_line_of_point (window);
00528   if ((point_line < window->pagetop) ||
00529       ((point_line - window->pagetop) > window->height - 1))
00530     window->point =
00531       window->line_starts[window->pagetop] - window->node->contents;
00532 
00533   window->flags |= W_UpdateWindow;
00534 
00535   /* Find out which direction to scroll, and scroll the window in that
00536      direction.  Do this only if there would be a savings in redisplay
00537      time.  This is true if the amount to scroll is less than the height
00538      of the window, and if the number of lines scrolled would be greater
00539      than 10 % of the window's height. */
00540   if (old_pagetop < desired_top)
00541     {
00542       int start, end, amount;
00543 
00544       amount = desired_top - old_pagetop;
00545 
00546       if ((amount >= window->height) ||
00547           (((window->height - amount) * 10) < window->height))
00548         return;
00549 
00550       start = amount + window->first_row;
00551       end = window->height + window->first_row;
00552 
00553       display_scroll_display (start, end, -amount);
00554     }
00555   else
00556     {
00557       int start, end, amount;
00558 
00559       amount = old_pagetop - desired_top;
00560 
00561       if ((amount >= window->height) ||
00562           (((window->height - amount) * 10) < window->height))
00563         return;
00564 
00565       start = window->first_row;
00566       end = (window->first_row + window->height) - amount;
00567       display_scroll_display (start, end, amount);
00568     }
00569 }
00570 
00571 /* Immediately make WINDOW->point visible on the screen, and move the
00572    terminal cursor there. */
00573 static void
00574 info_show_point (WINDOW *window)
00575 {
00576   int old_pagetop;
00577 
00578   old_pagetop = window->pagetop;
00579   window_adjust_pagetop (window);
00580   if (old_pagetop != window->pagetop)
00581     {
00582       int new_pagetop;
00583 
00584       new_pagetop = window->pagetop;
00585       window->pagetop = old_pagetop;
00586       set_window_pagetop (window, new_pagetop);
00587     }
00588 
00589   if (window->flags & W_UpdateWindow)
00590     display_update_one_window (window);
00591 
00592   display_cursor_at_point (window);
00593 }
00594 
00595 /* Move WINDOW->point from OLD line index to NEW line index. */
00596 static void
00597 move_to_new_line (int old, int new, WINDOW *window)
00598 {
00599   if (old == -1)
00600     {
00601       info_error ((char *) msg_cant_find_point, NULL, NULL);
00602     }
00603   else
00604     {
00605       int goal;
00606 
00607       if (new >= window->line_count || new < 0)
00608         return;
00609 
00610       goal = window_get_goal_column (window);
00611       window->goal_column = goal;
00612 
00613       window->point = window->line_starts[new] - window->node->contents;
00614       window->point += window_chars_to_goal (window->line_starts[new], goal);
00615       info_show_point (window);
00616     }
00617 }
00618 
00619 /* Move WINDOW's point down to the next line if possible. */
00620 DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
00621 {
00622   int old_line, new_line;
00623 
00624   if (count < 0)
00625     info_prev_line (window, -count, key);
00626   else
00627     {
00628       old_line = window_line_of_point (window);
00629       new_line = old_line + count;
00630       move_to_new_line (old_line, new_line, window);
00631     }
00632 }
00633 
00634 /* Move WINDOW's point up to the previous line if possible. */
00635 DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
00636 {
00637   int old_line, new_line;
00638 
00639   if (count < 0)
00640     info_next_line (window, -count, key);
00641   else
00642     {
00643       old_line = window_line_of_point (window);
00644       new_line = old_line - count;
00645       move_to_new_line (old_line, new_line, window);
00646     }
00647 }
00648 
00649 /* Move WINDOW's point to the end of the true line. */
00650 DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
00651 {
00652   register int point, len;
00653   register char *buffer;
00654 
00655   buffer = window->node->contents;
00656   len = window->node->nodelen;
00657 
00658   for (point = window->point;
00659        (point < len) && (buffer[point] != '\n');
00660        point++);
00661 
00662   if (point != window->point)
00663     {
00664       window->point = point;
00665       info_show_point (window);
00666     }
00667 }
00668 
00669 /* Move WINDOW's point to the beginning of the true line. */
00670 DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
00671 {
00672   register int point;
00673   register char *buffer;
00674 
00675   buffer = window->node->contents;
00676   point = window->point;
00677 
00678   for (; (point) && (buffer[point - 1] != '\n'); point--);
00679 
00680   /* If at a line start already, do nothing. */
00681   if (point != window->point)
00682     {
00683       window->point = point;
00684       info_show_point (window);
00685     }
00686 }
00687 
00688 /* Move point forward in the node. */
00689 DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
00690 {
00691   if (count < 0)
00692     info_backward_char (window, -count, key);
00693   else
00694     {
00695       window->point += count;
00696 
00697       if (window->point >= window->node->nodelen)
00698         window->point = window->node->nodelen - 1;
00699 
00700       info_show_point (window);
00701     }
00702 }
00703 
00704 /* Move point backward in the node. */
00705 DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
00706 {
00707   if (count < 0)
00708     info_forward_char (window, -count, key);
00709   else
00710     {
00711       window->point -= count;
00712 
00713       if (window->point < 0)
00714         window->point = 0;
00715 
00716       info_show_point (window);
00717     }
00718 }
00719 
00720 #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
00721 
00722 /* Move forward a word in this node. */
00723 DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
00724 {
00725   long point;
00726   char *buffer;
00727   int end, c;
00728 
00729   if (count < 0)
00730     {
00731       info_backward_word (window, -count, key);
00732       return;
00733     }
00734 
00735   point = window->point;
00736   buffer = window->node->contents;
00737   end = window->node->nodelen;
00738 
00739   while (count)
00740     {
00741       if (point + 1 >= end)
00742         return;
00743 
00744       /* If we are not in a word, move forward until we are in one.
00745          Then, move forward until we hit a non-alphabetic character. */
00746       c = buffer[point];
00747 
00748       if (!alphabetic (c))
00749         {
00750           while (++point < end)
00751             {
00752               c = buffer[point];
00753               if (alphabetic (c))
00754                 break;
00755             }
00756         }
00757 
00758       if (point >= end) return;
00759 
00760       while (++point < end)
00761         {
00762           c = buffer[point];
00763           if (!alphabetic (c))
00764             break;
00765         }
00766       --count;
00767     }
00768   window->point = point;
00769   info_show_point (window);
00770 }
00771 
00772 DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
00773 {
00774   long point;
00775   char *buffer;
00776   int c;
00777 
00778   if (count < 0)
00779     {
00780       info_forward_word (window, -count, key);
00781       return;
00782     }
00783 
00784   buffer = window->node->contents;
00785   point = window->point;
00786 
00787   while (count)
00788     {
00789       if (point == 0)
00790         break;
00791 
00792       /* Like info_forward_word (), except that we look at the
00793          characters just before point. */
00794 
00795       c = buffer[point - 1];
00796 
00797       if (!alphabetic (c))
00798         {
00799           while (--point)
00800             {
00801               c = buffer[point - 1];
00802               if (alphabetic (c))
00803                 break;
00804             }
00805         }
00806 
00807       while (point)
00808         {
00809           c = buffer[point - 1];
00810           if (!alphabetic (c))
00811             break;
00812           else
00813             --point;
00814         }
00815       --count;
00816     }
00817   window->point = point;
00818   info_show_point (window);
00819 }
00820 
00821 /* Variable controlling the behaviour of default scrolling when you are
00822    already at the bottom of a node.  Possible values are defined in session.h.
00823    The meanings are:
00824 
00825    IS_Continuous        Try to get first menu item, or failing that, the
00826                         "Next:" pointer, or failing that, the "Up:" and
00827                         "Next:" of the up.
00828    IS_NextOnly          Try to get "Next:" menu item.
00829    IS_PageOnly          Simply give up at the bottom of a node. */
00830 
00831 int info_scroll_behaviour = IS_Continuous;
00832 
00833 /* Choices used by the completer when reading a value for the user-visible
00834    variable "scroll-behaviour". */
00835 char *info_scroll_choices[] = {
00836   "Continuous", "Next Only", "Page Only", (char *)NULL
00837 };
00838 
00839 /* Default window sizes for scrolling commands.  */
00840 int default_window_size = -1;      /* meaning 1 window-full */
00841 int default_scroll_size = -1;      /* meaning half screen size */
00842 
00843 #define INFO_LABEL_FOUND() \
00844   (info_parsed_nodename || (info_parsed_filename \
00845                             && !is_dir_name (info_parsed_filename)))
00846 
00847 /* Move to 1st menu item, Next, Up/Next, or error in this window. */
00848 static void
00849 forward_move_node_structure (WINDOW *window, int behaviour)
00850 {
00851   switch (behaviour)
00852     {
00853     case IS_PageOnly:
00854       info_error ((char *) msg_at_node_bottom, NULL, NULL);
00855       break;
00856 
00857     case IS_NextOnly:
00858       info_next_label_of_node (window->node);
00859       if (!info_parsed_nodename && !info_parsed_filename)
00860         info_error ((char *) msg_no_pointer, (char *) _("Next"), NULL);
00861       else
00862         {
00863           window_message_in_echo_area ((char *) _("Following Next node..."),
00864               NULL, NULL);
00865           info_handle_pointer ("Next", window);
00866         }
00867       break;
00868 
00869     case IS_Continuous:
00870       {
00871         /* First things first.  If this node contains a menu, move down
00872            into the menu. */
00873         {
00874           REFERENCE **menu;
00875 
00876           menu = info_menu_of_node (window->node);
00877 
00878           if (menu)
00879             {
00880               info_free_references (menu);
00881               window_message_in_echo_area ((char *) _("Selecting first menu item..."),
00882                   NULL, NULL);
00883               info_menu_digit (window, 1, '1');
00884               return;
00885             }
00886         }
00887 
00888         /* Okay, this node does not contain a menu.  If it contains a
00889            "Next:" pointer, use that. */
00890         info_next_label_of_node (window->node);
00891         if (INFO_LABEL_FOUND ())
00892           {
00893             window_message_in_echo_area ((char *) _("Selecting Next node..."),
00894                 NULL, NULL);
00895             info_handle_pointer ("Next", window);
00896             return;
00897           }
00898 
00899         /* Okay, there wasn't a "Next:" for this node.  Move "Up:" until we
00900            can move "Next:".  If that isn't possible, complain that there
00901            are no more nodes. */
00902         {
00903           int up_counter, old_current;
00904           INFO_WINDOW *info_win;
00905 
00906           /* Remember the current node and location. */
00907           info_win = get_info_window_of_window (window);
00908           old_current = info_win->current;
00909 
00910           /* Back up through the "Up:" pointers until we have found a "Next:"
00911              that isn't the same as the first menu item found in that node. */
00912           up_counter = 0;
00913           while (!info_error_was_printed)
00914             {
00915               info_up_label_of_node (window->node);
00916               if (INFO_LABEL_FOUND ())
00917                 {
00918                   info_handle_pointer ("Up", window);
00919                   if (info_error_was_printed)
00920                     continue;
00921 
00922                   up_counter++;
00923 
00924                   info_next_label_of_node (window->node);
00925 
00926                   /* If no "Next" pointer, keep backing up. */
00927                   if (!INFO_LABEL_FOUND ())
00928                     continue;
00929 
00930                   /* If this node's first menu item is the same as this node's
00931                      Next pointer, keep backing up. */
00932                   if (!info_parsed_filename)
00933                     {
00934                       REFERENCE **menu;
00935                       char *next_nodename;
00936 
00937                       /* Remember the name of the Next node, since reading
00938                          the menu can overwrite the contents of the
00939                          info_parsed_xxx strings. */
00940                       next_nodename = xstrdup (info_parsed_nodename);
00941 
00942                       menu = info_menu_of_node (window->node);
00943                       if (menu &&
00944                           (strcmp
00945                            (menu[0]->nodename, next_nodename) == 0))
00946                         {
00947                           info_free_references (menu);
00948                           free (next_nodename);
00949                           continue;
00950                         }
00951                       else
00952                         {
00953                           /* Restore the world to where it was before
00954                              reading the menu contents. */
00955                           info_free_references (menu);
00956                           free (next_nodename);
00957                           info_next_label_of_node (window->node);
00958                         }
00959                     }
00960 
00961                   /* This node has a "Next" pointer, and it is not the
00962                      same as the first menu item found in this node. */
00963                   window_message_in_echo_area
00964                     ((char *) _("Moving Up %d time(s), then Next."),
00965                      (void *) (long) up_counter, NULL);
00966 
00967                   info_handle_pointer ("Next", window);
00968                   return;
00969                 }
00970               else
00971                 {
00972                   /* No more "Up" pointers.  Print an error, and call it
00973                      quits. */
00974                   register int i;
00975 
00976                   for (i = 0; i < up_counter; i++)
00977                     {
00978                       info_win->nodes_index--;
00979                       free (info_win->nodes[info_win->nodes_index]);
00980                       info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
00981                     }
00982                   info_win->current = old_current;
00983                   window->node = info_win->nodes[old_current];
00984                   window->pagetop = info_win->pagetops[old_current];
00985                   window->point = info_win->points[old_current];
00986                   recalculate_line_starts (window);
00987                   window->flags |= W_UpdateWindow;
00988                   info_error ((char *) _("No more nodes within this document."),
00989                       NULL, NULL);
00990                 }
00991             }
00992         }
00993         break;
00994       }
00995     }
00996 }
00997 
00998 /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
00999 static void
01000 backward_move_node_structure (WINDOW *window, int behaviour)
01001 {
01002   switch (behaviour)
01003     {
01004     case IS_PageOnly:
01005       info_error ((char *) msg_at_node_top, NULL, NULL);
01006       break;
01007 
01008     case IS_NextOnly:
01009       info_prev_label_of_node (window->node);
01010       if (!info_parsed_nodename && !info_parsed_filename)
01011         info_error ((char *) _("No `Prev' for this node."), NULL, NULL);
01012       else
01013         {
01014           window_message_in_echo_area ((char *) _("Moving Prev in this window."),
01015               NULL, NULL);
01016           info_handle_pointer ("Prev", window);
01017         }
01018       break;
01019 
01020     case IS_Continuous:
01021       info_prev_label_of_node (window->node);
01022 
01023       if (!info_parsed_nodename && (!info_parsed_filename
01024                                     || is_dir_name (info_parsed_filename)))
01025         {
01026           info_up_label_of_node (window->node);
01027           if (!info_parsed_nodename && (!info_parsed_filename
01028                                         || is_dir_name (info_parsed_filename)))
01029             info_error ((char *)
01030                 _("No `Prev' or `Up' for this node within this document."),
01031                 NULL, NULL);
01032           else
01033             {
01034               window_message_in_echo_area ((char *) _("Moving Up in this window."),
01035                   NULL, NULL);
01036               info_handle_pointer ("Up", window);
01037             }
01038         }
01039       else
01040         {
01041           REFERENCE **menu;
01042           int inhibit_menu_traversing = 0;
01043 
01044           /* Watch out!  If this node's Prev is the same as the Up, then
01045              move Up.  Otherwise, we could move Prev, and then to the last
01046              menu item in the Prev.  This would cause the user to loop
01047              through a subsection of the info file. */
01048           if (!info_parsed_filename && info_parsed_nodename)
01049             {
01050               char *pnode;
01051 
01052               pnode = xstrdup (info_parsed_nodename);
01053               info_up_label_of_node (window->node);
01054 
01055               if (!info_parsed_filename && info_parsed_nodename &&
01056                   strcmp (info_parsed_nodename, pnode) == 0)
01057                 {
01058                   /* The nodes are the same.  Inhibit moving to the last
01059                      menu item. */
01060                   free (pnode);
01061                   inhibit_menu_traversing = 1;
01062                 }
01063               else
01064                 {
01065                   free (pnode);
01066                   info_prev_label_of_node (window->node);
01067                 }
01068             }
01069 
01070           /* Move to the previous node.  If this node now contains a menu,
01071              and we have not inhibited movement to it, move to the node
01072              corresponding to the last menu item. */
01073           window_message_in_echo_area ((char *) _("Moving Prev in this window."),
01074               NULL, NULL);
01075           info_handle_pointer ("Prev", window);
01076 
01077           if (!inhibit_menu_traversing)
01078             {
01079               while (!info_error_was_printed &&
01080                      (menu = info_menu_of_node (window->node)))
01081                 {
01082                   info_free_references (menu);
01083                   window_message_in_echo_area
01084                     ((char *) _("Moving to `Prev's last menu item."), NULL, NULL);
01085                   info_menu_digit (window, 1, '0');
01086                 }
01087             }
01088         }
01089       break;
01090     }
01091 }
01092 
01093 /* Move continuously forward through the node structure of this info file. */
01094 DECLARE_INFO_COMMAND (info_global_next_node,
01095                       _("Move forwards or down through node structure"))
01096 {
01097   if (count < 0)
01098     info_global_prev_node (window, -count, key);
01099   else
01100     {
01101       while (count && !info_error_was_printed)
01102         {
01103           forward_move_node_structure (window, IS_Continuous);
01104           count--;
01105         }
01106     }
01107 }
01108 
01109 /* Move continuously backward through the node structure of this info file. */
01110 DECLARE_INFO_COMMAND (info_global_prev_node,
01111                       _("Move backwards or up through node structure"))
01112 {
01113   if (count < 0)
01114     info_global_next_node (window, -count, key);
01115   else
01116     {
01117       while (count && !info_error_was_printed)
01118         {
01119           backward_move_node_structure (window, IS_Continuous);
01120           count--;
01121         }
01122     }
01123 }
01124 
01125 static void _scroll_forward(WINDOW *window, int count,
01126     unsigned char key, int behaviour);
01127 static void _scroll_backward(WINDOW *window, int count,
01128     unsigned char key, int behaviour);
01129 
01130 static void
01131 _scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour)
01132 {
01133   if (count < 0)
01134     _scroll_backward (window, -count, key, behaviour);
01135   else
01136     {
01137       int desired_top;
01138 
01139       /* Without an explicit numeric argument, scroll the bottom two
01140          lines to the top of this window,  Or, if at bottom of window,
01141          and the chosen behaviour is to scroll through nodes get the
01142         "Next" node for this window. */
01143       if (default_window_size > 0)
01144         desired_top = window->pagetop + default_window_size;
01145       else if (!info_explicit_arg && count == 1)
01146         {
01147           desired_top = window->pagetop + (window->height - 2);
01148 
01149           /* If there are no more lines to scroll here, error, or get
01150              another node, depending on BEHAVIOUR. */
01151           if (desired_top > window->line_count)
01152             {
01153               forward_move_node_structure (window, behaviour);
01154               return;
01155             }
01156         }
01157       else
01158         desired_top = window->pagetop + count;
01159 
01160       if (desired_top >= window->line_count)
01161         desired_top = window->line_count - 2;
01162 
01163       if (window->pagetop > desired_top)
01164         return;
01165       else
01166         set_window_pagetop (window, desired_top);
01167     }
01168 }
01169 
01170 static void
01171 _scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour)
01172 {
01173   if (count < 0)
01174     _scroll_forward (window, -count, key, behaviour);
01175   else
01176     {
01177       int desired_top;
01178 
01179       /* Without an explicit numeric argument, scroll the top two lines
01180          to the bottom of this window, or, depending on the selected
01181         behaviour, move to the previous, or Up'th node. */
01182       if (default_window_size > 0)
01183         desired_top = window->pagetop - default_window_size;
01184       else if (!info_explicit_arg && count == 1)
01185         {
01186           desired_top = window->pagetop - (window->height - 2);
01187 
01188           if ((desired_top < 0) && (window->pagetop == 0))
01189             {
01190               backward_move_node_structure (window, behaviour);
01191               return;
01192             }
01193         }
01194       else
01195         desired_top = window->pagetop - count;
01196 
01197       if (desired_top < 0)
01198         desired_top = 0;
01199 
01200       set_window_pagetop (window, desired_top);
01201     }
01202 }
01203 
01204 /* Show the next screen of WINDOW's node. */
01205 DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
01206 {
01207   _scroll_forward (window, count, key, info_scroll_behaviour);
01208 }
01209 
01210 /* Like info_scroll_forward, but sets default_window_size as a side
01211    effect.  */
01212 DECLARE_INFO_COMMAND (info_scroll_forward_set_window,
01213                     _("Scroll forward in this window and set default window size"))
01214 {
01215   if (info_explicit_arg)
01216     default_window_size = count;
01217   _scroll_forward (window, count, key, info_scroll_behaviour);
01218 }
01219 
01220 /* Show the next screen of WINDOW's node but never advance to next node. */
01221 DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))
01222 {
01223   _scroll_forward (window, count, key, IS_PageOnly);
01224 }
01225 
01226 /* Like info_scroll_forward_page_only, but sets default_window_size as a side
01227    effect.  */
01228 DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,
01229                     _("Scroll forward in this window staying within node and set default window size"))
01230 {
01231   if (info_explicit_arg)
01232     default_window_size = count;
01233   _scroll_forward (window, count, key, IS_PageOnly);
01234 }
01235 
01236 /* Show the previous screen of WINDOW's node. */
01237 DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
01238 {
01239   _scroll_backward (window, count, key, info_scroll_behaviour);
01240 }
01241 
01242 /* Like info_scroll_backward, but sets default_window_size as a side
01243    effect.  */
01244 DECLARE_INFO_COMMAND (info_scroll_backward_set_window,
01245                     _("Scroll backward in this window and set default window size"))
01246 {
01247   if (info_explicit_arg)
01248     default_window_size = count;
01249   _scroll_backward (window, count, key, info_scroll_behaviour);
01250 }
01251 
01252 /* Show the previous screen of WINDOW's node but never move to previous
01253    node. */
01254 DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))
01255 {
01256   _scroll_backward (window, count, key, IS_PageOnly);
01257 }
01258 
01259 /* Like info_scroll_backward_page_only, but sets default_window_size as a side
01260    effect.  */
01261 DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,
01262                     _("Scroll backward in this window staying within node and set default window size"))
01263 {
01264   if (info_explicit_arg)
01265     default_window_size = count;
01266   _scroll_backward (window, count, key, IS_PageOnly);
01267 }
01268 
01269 /* Move to the beginning of the node. */
01270 DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
01271 {
01272   window->pagetop = window->point = 0;
01273   window->flags |= W_UpdateWindow;
01274 }
01275 
01276 /* Move to the end of the node. */
01277 DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
01278 {
01279   window->point = window->node->nodelen - 1;
01280   info_show_point (window);
01281 }
01282 
01283 /* Scroll the window forward by N lines.  */
01284 DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
01285 {
01286   if (count < 0)
01287     info_up_line (window, -count, key);
01288   else
01289     {
01290       int desired_top = window->pagetop + count;
01291 
01292       if (desired_top >= window->line_count)
01293        desired_top = window->line_count - 2;
01294 
01295       if (window->pagetop <= desired_top)
01296        set_window_pagetop (window, desired_top);
01297     }
01298 }
01299 
01300 /* Scroll the window backward by N lines.  */
01301 DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
01302 {
01303   if (count < 0)
01304     info_down_line (window, -count, key);
01305   else
01306     {
01307       int desired_top = window->pagetop - count;
01308 
01309       if (desired_top < 0)
01310        desired_top = 0;
01311 
01312       set_window_pagetop (window, desired_top);
01313     }
01314 }
01315 
01316 /* Scroll the window forward by N lines and remember N as default for
01317    subsequent commands.  */
01318 DECLARE_INFO_COMMAND (info_scroll_half_screen_down,
01319                     _("Scroll down by half screen size"))
01320 {
01321   if (count < 0)
01322     info_scroll_half_screen_up (window, -count, key);
01323   else
01324     {
01325       int scroll_size = (the_screen->height + 1) / 2;
01326       int desired_top;
01327 
01328       if (info_explicit_arg)
01329        default_scroll_size = count;
01330       if (default_scroll_size > 0)
01331        scroll_size = default_scroll_size;
01332 
01333       desired_top = window->pagetop + scroll_size;
01334       if (desired_top >= window->line_count)
01335        desired_top = window->line_count - 2;
01336 
01337       if (window->pagetop <= desired_top)
01338        set_window_pagetop (window, desired_top);
01339     }
01340 }
01341 
01342 /* Scroll the window backward by N lines and remember N as default for
01343    subsequent commands.  */
01344 DECLARE_INFO_COMMAND (info_scroll_half_screen_up,
01345                     _("Scroll up by half screen size"))
01346 {
01347   if (count < 0)
01348     info_scroll_half_screen_down (window, -count, key);
01349   else
01350     {
01351       int scroll_size = (the_screen->height + 1) / 2;
01352       int desired_top;
01353 
01354       if (info_explicit_arg)
01355        default_scroll_size = count;
01356       if (default_scroll_size > 0)
01357        scroll_size = default_scroll_size;
01358 
01359       desired_top = window->pagetop - scroll_size;
01360       if (desired_top < 0)
01361        desired_top = 0;
01362 
01363       set_window_pagetop (window, desired_top);
01364     }
01365 }
01366 
01367 /* **************************************************************** */
01368 /*                                                                  */
01369 /*                 Commands for Manipulating Windows                */
01370 /*                                                                  */
01371 /* **************************************************************** */
01372 
01373 /* Make the next window in the chain be the active window. */
01374 DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
01375 {
01376   if (count < 0)
01377     {
01378       info_prev_window (window, -count, key);
01379       return;
01380     }
01381 
01382   /* If no other window, error now. */
01383   if (!windows->next && !echo_area_is_active)
01384     {
01385       info_error ((char *) msg_one_window, NULL, NULL);
01386       return;
01387     }
01388 
01389   while (count--)
01390     {
01391       if (window->next)
01392         window = window->next;
01393       else
01394         {
01395           if (window == the_echo_area || !echo_area_is_active)
01396             window = windows;
01397           else
01398             window = the_echo_area;
01399         }
01400     }
01401 
01402   if (active_window != window)
01403     {
01404       if (auto_footnotes_p)
01405         info_get_or_remove_footnotes (window);
01406 
01407       window->flags |= W_UpdateWindow;
01408       active_window = window;
01409     }
01410 }
01411 
01412 /* Make the previous window in the chain be the active window. */
01413 DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
01414 {
01415   if (count < 0)
01416     {
01417       info_next_window (window, -count, key);
01418       return;
01419     }
01420 
01421   /* Only one window? */
01422 
01423   if (!windows->next && !echo_area_is_active)
01424     {
01425       info_error ((char *) msg_one_window, NULL, NULL);
01426       return;
01427     }
01428 
01429   while (count--)
01430     {
01431       /* If we are in the echo area, or if the echo area isn't active and we
01432          are in the first window, find the last window in the chain. */
01433       if (window == the_echo_area ||
01434           (window == windows && !echo_area_is_active))
01435         {
01436           register WINDOW *win, *last = NULL;
01437 
01438           for (win = windows; win; win = win->next)
01439             last = win;
01440 
01441           window = last;
01442         }
01443       else
01444         {
01445           if (window == windows)
01446             window = the_echo_area;
01447           else
01448             window = window->prev;
01449         }
01450     }
01451 
01452   if (active_window != window)
01453     {
01454       if (auto_footnotes_p)
01455         info_get_or_remove_footnotes (window);
01456 
01457       window->flags |= W_UpdateWindow;
01458       active_window = window;
01459     }
01460 }
01461 
01462 /* Split WINDOW into two windows, both showing the same node.  If we
01463    are automatically tiling windows, re-tile after the split. */
01464 DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
01465 {
01466   WINDOW *split, *old_active;
01467   int pagetop;
01468 
01469   /* Remember the current pagetop of the window being split.  If it doesn't
01470      change, we can scroll its contents around after the split. */
01471   pagetop = window->pagetop;
01472 
01473   /* Make the new window. */
01474   old_active = active_window;
01475   active_window = window;
01476   split = window_make_window (window->node);
01477   active_window = old_active;
01478 
01479   if (!split)
01480     {
01481       info_error ((char *) msg_win_too_small, NULL, NULL);
01482     }
01483   else
01484     {
01485 #if defined (SPLIT_BEFORE_ACTIVE)
01486       /* Try to scroll the old window into its new postion. */
01487       if (pagetop == window->pagetop)
01488         {
01489           int start, end, amount;
01490 
01491           start = split->first_row;
01492           end = start + window->height;
01493           amount = split->height + 1;
01494           display_scroll_display (start, end, amount);
01495         }
01496 #else /* !SPLIT_BEFORE_ACTIVE */
01497       /* Make sure point still appears in the active window. */
01498       info_show_point (window);
01499 #endif /* !SPLIT_BEFORE_ACTIVE */
01500 
01501       /* If the window just split was one internal to Info, try to display
01502          something else in it. */
01503       if (internal_info_node_p (split->node))
01504         {
01505           register int i, j;
01506           INFO_WINDOW *iw;
01507           NODE *node = (NODE *)NULL;
01508           char *filename;
01509 
01510           for (i = 0; (iw = info_windows[i]); i++)
01511             {
01512               for (j = 0; j < iw->nodes_index; j++)
01513                 if (!internal_info_node_p (iw->nodes[j]))
01514                   {
01515                     if (iw->nodes[j]->parent)
01516                       filename = iw->nodes[j]->parent;
01517                     else
01518                       filename = iw->nodes[j]->filename;
01519 
01520                     node = info_get_node (filename, iw->nodes[j]->nodename);
01521                     if (node)
01522                       {
01523                         window_set_node_of_window (split, node);
01524                         i = info_windows_index - 1;
01525                         break;
01526                       }
01527                   }
01528             }
01529         }
01530       split->pagetop = window->pagetop;
01531 
01532       if (auto_tiling_p)
01533         window_tile_windows (DONT_TILE_INTERNALS);
01534       else
01535         window_adjust_pagetop (split);
01536 
01537       remember_window_and_node (split, split->node);
01538     }
01539 }
01540 
01541 /* Delete WINDOW, forgetting the list of last visited nodes.  If we are
01542    automatically displaying footnotes, show or remove the footnotes
01543    window.  If we are automatically tiling windows, re-tile after the
01544    deletion. */
01545 DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
01546 {
01547   if (!windows->next)
01548     {
01549       info_error ((char *) msg_cant_kill_last, NULL, NULL);
01550     }
01551   else if (window->flags & W_WindowIsPerm)
01552     {
01553       info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL);
01554     }
01555   else
01556     {
01557       info_delete_window_internal (window);
01558 
01559       if (auto_footnotes_p)
01560         info_get_or_remove_footnotes (active_window);
01561 
01562       if (auto_tiling_p)
01563         window_tile_windows (DONT_TILE_INTERNALS);
01564     }
01565 }
01566 
01567 /* Do the physical deletion of WINDOW, and forget this window and
01568    associated nodes. */
01569 void
01570 info_delete_window_internal (WINDOW *window)
01571 {
01572   if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
01573     {
01574       /* We not only delete the window from the display, we forget it from
01575          our list of remembered windows. */
01576       forget_window_and_nodes (window);
01577       window_delete_window (window);
01578 
01579       if (echo_area_is_active)
01580         echo_area_inform_of_deleted_window (window);
01581     }
01582 }
01583 
01584 /* Just keep WINDOW, deleting all others. */
01585 DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
01586 {
01587   int num_deleted;              /* The number of windows we deleted. */
01588   int pagetop, start, end;
01589 
01590   /* Remember a few things about this window.  We may be able to speed up
01591      redisplay later by scrolling its contents. */
01592   pagetop = window->pagetop;
01593   start = window->first_row;
01594   end = start + window->height;
01595 
01596   num_deleted = 0;
01597 
01598   while (1)
01599     {
01600       WINDOW *win;
01601 
01602       /* Find an eligible window and delete it.  If no eligible windows
01603          are found, we are done.  A window is eligible for deletion if
01604          is it not permanent, and it is not WINDOW. */
01605       for (win = windows; win; win = win->next)
01606         if (win != window && ((win->flags & W_WindowIsPerm) == 0))
01607           break;
01608 
01609       if (!win)
01610         break;
01611 
01612       info_delete_window_internal (win);
01613       num_deleted++;
01614     }
01615 
01616   /* Scroll the contents of this window into the right place so that the
01617      user doesn't have to wait any longer than necessary for redisplay. */
01618   if (num_deleted)
01619     {
01620       int amount;
01621 
01622       amount = (window->first_row - start);
01623       amount -= (window->pagetop - pagetop);
01624       display_scroll_display (start, end, amount);
01625     }
01626 
01627   window->flags |= W_UpdateWindow;
01628 }
01629 
01630 /* Scroll the "other" window of WINDOW. */
01631 DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
01632 {
01633   WINDOW *other;
01634 
01635   /* If only one window, give up. */
01636   if (!windows->next)
01637     {
01638       info_error ((char *) msg_one_window, NULL, NULL);
01639       return;
01640     }
01641 
01642   other = window->next;
01643 
01644   if (!other)
01645     other = window->prev;
01646 
01647   info_scroll_forward (other, count, key);
01648 }
01649 
01650 /* Scroll the "other" window of WINDOW. */
01651 DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
01652                       _("Scroll the other window backward"))
01653 {
01654   info_scroll_other_window (window, -count, key);
01655 }
01656 
01657 /* Change the size of WINDOW by AMOUNT. */
01658 DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
01659 {
01660   window_change_window_height (window, count);
01661 }
01662 
01663 /* When non-zero, tiling takes place automatically when info_split_window
01664    is called. */
01665 int auto_tiling_p = 0;
01666 
01667 /* Tile all of the visible windows. */
01668 DECLARE_INFO_COMMAND (info_tile_windows,
01669     _("Divide the available screen space among the visible windows"))
01670 {
01671   window_tile_windows (TILE_INTERNALS);
01672 }
01673 
01674 /* Toggle the state of this window's wrapping of lines. */
01675 DECLARE_INFO_COMMAND (info_toggle_wrap,
01676               _("Toggle the state of line wrapping in the current window"))
01677 {
01678   window_toggle_wrap (window);
01679 }
01680 
01681 /* **************************************************************** */
01682 /*                                                                  */
01683 /*                      Info Node Commands                          */
01684 /*                                                                  */
01685 /* **************************************************************** */
01686 
01687 /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
01688    filename is not set. */
01689 char *
01690 node_printed_rep (NODE *node)
01691 {
01692   char *rep;
01693 
01694   if (node->filename)
01695     {
01696       char *filename
01697        = filename_non_directory (node->parent ? node->parent : node->filename);
01698       rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
01699       sprintf (rep, "(%s)%s", filename, node->nodename);
01700     }
01701   else
01702     rep = node->nodename;
01703 
01704   return rep;
01705 }
01706 
01707 
01708 /* Using WINDOW for various defaults, select the node referenced by ENTRY
01709    in it.  If the node is selected, the window and node are remembered. */
01710 void
01711 info_select_reference (WINDOW *window, REFERENCE *entry)
01712 {
01713   NODE *node;
01714   char *filename, *nodename, *file_system_error;
01715 
01716   file_system_error = (char *)NULL;
01717 
01718   filename = entry->filename;
01719   if (!filename)
01720     filename = window->node->parent;
01721   if (!filename)
01722     filename = window->node->filename;
01723 
01724   if (filename)
01725     filename = xstrdup (filename);
01726 
01727   if (entry->nodename)
01728     nodename = xstrdup (entry->nodename);
01729   else
01730     nodename = xstrdup ("Top");
01731 
01732   node = info_get_node (filename, nodename);
01733 
01734   /* Try something a little weird.  If the node couldn't be found, and the
01735      reference was of the form "foo::", see if the entry->label can be found
01736      as a file, with a node of "Top". */
01737   if (!node)
01738     {
01739       if (info_recent_file_error)
01740         file_system_error = xstrdup (info_recent_file_error);
01741 
01742       if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
01743         {
01744           node = info_get_node (entry->label, "Top");
01745           if (!node && info_recent_file_error)
01746             {
01747               maybe_free (file_system_error);
01748               file_system_error = xstrdup (info_recent_file_error);
01749             }
01750         }
01751     }
01752 
01753   if (!node)
01754     {
01755       if (file_system_error)
01756         info_error (file_system_error, NULL, NULL);
01757       else
01758         info_error ((char *) msg_cant_find_node, nodename, NULL);
01759     }
01760 
01761   maybe_free (file_system_error);
01762   maybe_free (filename);
01763   maybe_free (nodename);
01764 
01765   if (node)
01766     info_set_node_of_window (1, window, node);
01767 }
01768 
01769 /* Parse the node specification in LINE using WINDOW to default the filename.
01770    Select the parsed node in WINDOW and remember it, or error if the node
01771    couldn't be found. */
01772 static void
01773 info_parse_and_select (char *line, WINDOW *window)
01774 {
01775   REFERENCE entry;
01776 
01777   info_parse_node (line, DONT_SKIP_NEWLINES);
01778 
01779   entry.nodename = info_parsed_nodename;
01780   entry.filename = info_parsed_filename;
01781   entry.label = "*info-parse-and-select*";
01782 
01783   info_select_reference (window, &entry);
01784 }
01785 
01786 /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
01787    are previously filled, try to get the node represented by them into
01788    WINDOW.  The node should have been pointed to by the LABEL pointer of
01789    WINDOW->node. */
01790 static void
01791 info_handle_pointer (char *label, WINDOW *window)
01792 {
01793   if (info_parsed_filename || info_parsed_nodename)
01794     {
01795       char *filename, *nodename;
01796       NODE *node;
01797 
01798       filename = nodename = (char *)NULL;
01799 
01800       if (info_parsed_filename)
01801         filename = xstrdup (info_parsed_filename);
01802       else
01803         {
01804           if (window->node->parent)
01805             filename = xstrdup (window->node->parent);
01806           else if (window->node->filename)
01807             filename = xstrdup (window->node->filename);
01808         }
01809 
01810       if (info_parsed_nodename)
01811         nodename = xstrdup (info_parsed_nodename);
01812       else
01813         nodename = xstrdup ("Top");
01814 
01815       node = info_get_node (filename, nodename);
01816 
01817       if (node)
01818         {
01819           INFO_WINDOW *info_win;
01820 
01821           info_win = get_info_window_of_window (window);
01822           if (info_win)
01823             {
01824               info_win->pagetops[info_win->current] = window->pagetop;
01825               info_win->points[info_win->current] = window->point;
01826             }
01827           info_set_node_of_window (1, window, node);
01828         }
01829       else
01830         {
01831           if (info_recent_file_error)
01832             info_error (info_recent_file_error, NULL, NULL);
01833           else
01834             info_error ((char *) msg_cant_file_node, filename, nodename);
01835         }
01836 
01837       free (filename);
01838       free (nodename);
01839     }
01840   else
01841     {
01842       info_error ((char *) msg_no_pointer, label, NULL);
01843     }
01844 }
01845 
01846 /* Make WINDOW display the "Next:" node of the node currently being
01847    displayed. */
01848 DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
01849 {
01850   info_next_label_of_node (window->node);
01851   info_handle_pointer ("Next", window);
01852 }
01853 
01854 /* Make WINDOW display the "Prev:" node of the node currently being
01855    displayed. */
01856 DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
01857 {
01858   info_prev_label_of_node (window->node);
01859   info_handle_pointer ("Prev", window);
01860 }
01861 
01862 /* Make WINDOW display the "Up:" node of the node currently being
01863    displayed. */
01864 DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
01865 {
01866   info_up_label_of_node (window->node);
01867   info_handle_pointer ("Up", window);
01868 }
01869 
01870 /* Make WINDOW display the last node of this info file. */
01871 DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
01872 {
01873   register int i;
01874   FILE_BUFFER *fb = file_buffer_of_window (window);
01875   NODE *node = (NODE *)NULL;
01876 
01877   if (fb && fb->tags)
01878     {
01879       int last_node_tag_idx = -1;
01880 
01881       /* If no explicit argument, or argument of zero, default to the
01882          last node.  */
01883       if (count == 0 || (count == 1 && !info_explicit_arg))
01884         count = -1;
01885       for (i = 0; count && fb->tags[i]; i++)
01886         if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
01887           {
01888             count--;
01889             last_node_tag_idx = i;
01890           }
01891       if (count > 0)
01892         i = last_node_tag_idx + 1;
01893       if (i > 0)
01894         node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
01895     }
01896 
01897   if (!node)
01898     info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
01899   else
01900     info_set_node_of_window (1, window, node);
01901 }
01902 
01903 /* Make WINDOW display the first node of this info file. */
01904 DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
01905 {
01906   FILE_BUFFER *fb = file_buffer_of_window (window);
01907   NODE *node = (NODE *)NULL;
01908 
01909   /* If no explicit argument, or argument of zero, default to the
01910      first node.  */
01911   if (count == 0)
01912     count = 1;
01913   if (fb && fb->tags)
01914     {
01915       register int i;
01916       int last_node_tag_idx = -1;
01917 
01918       for (i = 0; count && fb->tags[i]; i++)
01919         if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
01920           {
01921             count--;
01922             last_node_tag_idx = i;
01923           }
01924       if (count > 0)
01925         i = last_node_tag_idx + 1;
01926       if (i > 0)
01927         node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
01928     }
01929 
01930   if (!node)
01931     info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
01932   else
01933     info_set_node_of_window (1, window, node);
01934 }
01935 
01936 /* Select the last menu item in WINDOW->node. */
01937 DECLARE_INFO_COMMAND (info_last_menu_item,
01938    _("Select the last item in this node's menu"))
01939 {
01940   info_menu_digit (window, 1, '0');
01941 }
01942 
01943 /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
01944 DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
01945 {
01946   register int i, item;
01947   register REFERENCE **menu;
01948 
01949   menu = info_menu_of_node (window->node);
01950 
01951   if (!menu)
01952     {
01953       info_error ((char *) msg_no_menu_node, NULL, NULL);
01954       return;
01955     }
01956 
01957   /* We have the menu.  See if there are this many items in it. */
01958   item = key - '0';
01959 
01960   /* Special case.  Item "0" is the last item in this menu. */
01961   if (item == 0)
01962     for (i = 0; menu[i + 1]; i++);
01963   else
01964     {
01965       for (i = 0; menu[i]; i++)
01966         if (i == item - 1)
01967           break;
01968     }
01969 
01970   if (menu[i])
01971     {
01972       info_select_reference (window, menu[i]);
01973       if (menu[i]->line_number > 0)
01974         info_next_line (window, menu[i]->line_number - 1, key);
01975     }
01976   else
01977     info_error ((char *) _("There aren't %d items in this menu."),
01978                 (void *) (long) item, NULL);
01979 
01980   info_free_references (menu);
01981   return;
01982 }
01983 
01984 
01985 
01986 /* Return a pointer to the xref in XREF_LIST that is nearest to POS, or
01987    NULL if XREF_LIST is empty.  That is, if POS is within any of the
01988    given xrefs, return that one.  Otherwise, return the one with the
01989    nearest beginning or end.  If there are two that are equidistant,
01990    prefer the one forward.  The return is in newly-allocated memory,
01991    since the caller frees it.
01992    
01993    This is called from info_menu_or_ref_item with XREF_LIST being all
01994    the xrefs in the node, and POS being point.  The ui function that
01995    starts it all off is select-reference-this-line.
01996 
01997    This is not the same logic as in info.el.  Info-get-token prefers
01998    searching backwards to searching forwards, and has a hardwired search
01999    limit of 200 chars (in Emacs 21.2).  */
02000 
02001 static REFERENCE **
02002 nearest_xref (REFERENCE **xref_list, long int pos)
02003 {
02004   int this_xref;
02005   int nearest = -1;
02006   long best_delta = -1;
02007   
02008   for (this_xref = 0; xref_list[this_xref]; this_xref++)
02009     {
02010       long delta;
02011       REFERENCE *xref = xref_list[this_xref];
02012       if (xref->start <= pos && pos <= xref->end)
02013         { /* POS is within this xref, we're done */
02014           nearest = this_xref;
02015           break;
02016         }
02017       
02018       /* See how far POS is from this xref.  Take into account the
02019          `*Note' that begins the xref, since as far as the user is
02020          concerned, that's where it starts.  */
02021       delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))),
02022                    labs (pos - xref->end));
02023       
02024       /* It's the <= instead of < that makes us choose the forward xref
02025          of POS if two are equidistant.  Of course, because of all the
02026          punctuation surrounding xrefs, it's not necessarily obvious
02027          where one ends.  */
02028       if (delta <= best_delta || best_delta < 0)
02029         {
02030           nearest = this_xref;
02031           best_delta = delta;
02032         }
02033     }
02034   
02035   /* Maybe there was no list to search through.  */
02036   if (nearest < 0)
02037     return NULL;
02038   
02039   /* Ok, we have a nearest xref, make a list of it.  */
02040   {
02041     REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2);
02042     ret[0] = info_copy_reference (xref_list[nearest]);
02043     ret[1] = NULL;
02044     return ret;
02045   }
02046 }
02047 
02048 
02049 /* Read a menu or followed reference from the user defaulting to the
02050    reference found on the current line, and select that node.  The
02051    reading is done with completion.  BUILDER is the function used
02052    to build the list of references.  ASK_P is non-zero if the user
02053    should be prompted, or zero to select the default item. */
02054 static void
02055 info_menu_or_ref_item (WINDOW *window, int count,
02056     unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p)
02057 {
02058   char *line;
02059   REFERENCE *entry;
02060   REFERENCE *defentry = NULL;
02061   REFERENCE **menu = (*builder) (window->node);
02062 
02063   if (!menu)
02064     {
02065       if (builder == info_menu_of_node)
02066         info_error ((char *) msg_no_menu_node, NULL, NULL);
02067       else
02068         info_error ((char *) msg_no_xref_node, NULL, NULL);
02069       return;
02070     }
02071 
02072   /* Default the selected reference to the one which is on the line that
02073      point is in.  */
02074   {
02075     REFERENCE **refs = NULL;
02076     int point_line = window_line_of_point (window);
02077 
02078     if (point_line != -1)
02079       {
02080         SEARCH_BINDING binding;
02081 
02082         binding.buffer = window->node->contents;
02083         binding.start = window->line_starts[point_line] - binding.buffer;
02084         if (window->line_starts[point_line + 1])
02085           binding.end = window->line_starts[point_line + 1] - binding.buffer;
02086         else
02087           binding.end = window->node->nodelen;
02088         binding.flags = 0;
02089 
02090         if (builder == info_menu_of_node)
02091           {
02092             if (point_line)
02093               {
02094                 binding.start--;
02095                 refs = info_menu_items (&binding);
02096               }
02097           }
02098         else
02099           {
02100 #if defined (HANDLE_MAN_PAGES)
02101             if (window->node->flags & N_IsManPage)
02102               refs = manpage_xrefs_in_binding (window->node, &binding);
02103             else
02104 #endif /* HANDLE_MAN_PAGES */
02105               refs = nearest_xref (menu, window->point);
02106           }
02107 
02108         if (refs && refs[0])
02109           {
02110             if (strcmp (refs[0]->label, "Menu") != 0
02111                 || builder == info_xrefs_of_node)
02112               {
02113                 int which = 0;
02114 
02115                 /* For xrefs, find the closest reference to point,
02116                    unless we only have one reference (as we will if
02117                    we've called nearest_xref above).  It would be better
02118                    to have only one piece of code, but the conditions
02119                    when we call this are tangled.  */
02120                 if (builder == info_xrefs_of_node && refs[1])
02121                   {
02122                     int closest = -1;
02123 
02124                     for (; refs[which]; which++)
02125                       {
02126                         if (window->point >= refs[which]->start
02127                             && window->point <= refs[which]->end)
02128                           {
02129                             closest = which;
02130                             break;
02131                           }
02132                         else if (window->point < refs[which]->start)
02133                           break;
02134                       }
02135                   if (which > 0)
02136                     {
02137                      if (closest == -1)
02138                        which--;
02139                      else
02140                        which = closest;
02141                     }
02142                   }
02143 
02144                 defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
02145                 defentry->label = xstrdup (refs[which]->label);
02146                 defentry->filename = refs[which]->filename;
02147                 defentry->nodename = refs[which]->nodename;
02148                 defentry->line_number = refs[which]->line_number;
02149 
02150                 if (defentry->filename)
02151                   defentry->filename = xstrdup (defentry->filename);
02152                 if (defentry->nodename)
02153                   defentry->nodename = xstrdup (defentry->nodename);
02154               }
02155             info_free_references (refs);
02156           }
02157       }
02158   }
02159 
02160   /* If we are going to ask the user a question, do it now. */
02161   if (ask_p)
02162     {
02163       char *prompt;
02164 
02165       /* Build the prompt string. */
02166       if (builder == info_menu_of_node)
02167         {
02168           if (defentry)
02169            {
02170              prompt = xmalloc (strlen (defentry->label)
02171                             + strlen (_("Menu item (%s): ")));
02172              sprintf (prompt, _("Menu item (%s): "), defentry->label);
02173            }
02174           else
02175            prompt = xstrdup (_("Menu item: "));
02176         }
02177       else
02178         {
02179           if (defentry)
02180            {
02181              prompt = xmalloc (strlen (defentry->label)
02182                             + strlen (_("Follow xref (%s): ")));
02183              sprintf (prompt, _("Follow xref (%s): "), defentry->label);
02184            }
02185           else
02186            prompt = xstrdup (_("Follow xref: "));
02187         }
02188 
02189       line = info_read_completing_in_echo_area (window, prompt, menu);
02190       free (prompt);
02191 
02192       window = active_window;
02193 
02194       /* User aborts, just quit. */
02195       if (!line)
02196         {
02197           maybe_free (defentry);
02198           info_free_references (menu);
02199           info_abort_key (window, 0, 0);
02200           return;
02201         }
02202 
02203       /* If we had a default and the user accepted it, use that. */
02204       if (!*line)
02205         {
02206           free (line);
02207           if (defentry)
02208             line = xstrdup (defentry->label);
02209           else
02210             line = (char *)NULL;
02211         }
02212     }
02213   else
02214     {
02215       /* Not going to ask any questions.  If we have a default entry, use
02216          that, otherwise return. */
02217       if (!defentry)
02218         return;
02219       else
02220         line = xstrdup (defentry->label);
02221     }
02222 
02223   if (line)
02224     {
02225       /* It is possible that the references have more than a single
02226          entry with the same label, and also LINE is down-cased, which
02227          complicates matters even more.  Try to be as accurate as we
02228          can: if they've chosen the default, use defentry directly. */
02229       if (defentry && strcmp (line, defentry->label) == 0)
02230         entry = defentry;
02231       else
02232         /* Find the selected label in the references.  If there are
02233            more than one label which matches, find the one that's
02234            closest to point.  */
02235         {
02236           register int i;
02237           int best = -1, min_dist = window->node->nodelen;
02238           REFERENCE *ref;
02239 
02240           for (i = 0; menu && (ref = menu[i]); i++)
02241             {
02242               /* Need to use strcasecmp because LINE is downcased
02243                  inside info_read_completing_in_echo_area.  */
02244               if (strcasecmp (line, ref->label) == 0)
02245                 {
02246                   /* ref->end is more accurate estimate of position
02247                      for menus than ref->start.  Go figure.  */
02248                   int dist = abs (window->point - ref->end);
02249 
02250                   if (dist < min_dist)
02251                     {
02252                       min_dist = dist;
02253                       best = i;
02254                     }
02255                 }
02256             }
02257           if (best != -1)
02258             entry = menu[best];
02259           else
02260             entry = (REFERENCE *)NULL;
02261         }
02262 
02263       if (!entry && defentry)
02264         info_error ((char *) _("The reference disappeared! (%s)."), line, NULL);
02265       else
02266         {
02267           NODE *orig = window->node;
02268           info_select_reference (window, entry);
02269 
02270           if (builder == info_xrefs_of_node && window->node != orig
02271               && !(window->node->flags & N_FromAnchor))
02272             { /* Search for this reference in the node.  */
02273               long offset;
02274               long start;
02275 
02276               if (window->line_count > 0)
02277                 start = window->line_starts[1] - window->node->contents;
02278               else
02279                 start = 0;
02280 
02281               offset =
02282                 info_target_search_node (window->node, entry->label, start);
02283 
02284               if (offset != -1)
02285                 {
02286                   window->point = offset;
02287                   window_adjust_pagetop (window);
02288                 }
02289             }
02290 
02291             if (entry->line_number > 0)
02292               /* next_line starts at line 1?  Anyway, the -1 makes it
02293                  move to the right line.  */
02294               info_next_line (window, entry->line_number - 1, key);
02295         }
02296 
02297       free (line);
02298       if (defentry)
02299         {
02300           free (defentry->label);
02301           maybe_free (defentry->filename);
02302           maybe_free (defentry->nodename);
02303           free (defentry);
02304         }
02305     }
02306 
02307   info_free_references (menu);
02308 
02309   if (!info_error_was_printed)
02310     window_clear_echo_area ();
02311 }
02312 
02313 /* Read a line (with completion) which is the name of a menu item,
02314    and select that item. */
02315 DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
02316 {
02317   info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
02318 }
02319 
02320 /* Read a line (with completion) which is the name of a reference to
02321    follow, and select the node. */
02322 DECLARE_INFO_COMMAND
02323   (info_xref_item, _("Read a footnote or cross reference and select its node"))
02324 {
02325   info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
02326 }
02327 
02328 /* Position the cursor at the start of this node's menu. */
02329 DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
02330 {
02331   SEARCH_BINDING binding;
02332   long position;
02333 
02334   binding.buffer = window->node->contents;
02335   binding.start  = 0;
02336   binding.end = window->node->nodelen;
02337   binding.flags = S_FoldCase | S_SkipDest;
02338 
02339   position = search (INFO_MENU_LABEL, &binding);
02340 
02341   if (position == -1)
02342     info_error ((char *) msg_no_menu_node, NULL, NULL);
02343   else
02344     {
02345       window->point = position;
02346       window_adjust_pagetop (window);
02347       window->flags |= W_UpdateWindow;
02348     }
02349 }
02350 
02351 /* Visit as many menu items as is possible, each in a separate window. */
02352 DECLARE_INFO_COMMAND (info_visit_menu,
02353   _("Visit as many menu items at once as possible"))
02354 {
02355   register int i;
02356   REFERENCE *entry, **menu;
02357 
02358   menu = info_menu_of_node (window->node);
02359 
02360   if (!menu)
02361     info_error ((char *) msg_no_menu_node, NULL, NULL);
02362 
02363   for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
02364     {
02365       WINDOW *new;
02366 
02367       new = window_make_window (window->node);
02368       window_tile_windows (TILE_INTERNALS);
02369 
02370       if (!new)
02371         info_error ((char *) msg_win_too_small, NULL, NULL);
02372       else
02373         {
02374           active_window = new;
02375           info_select_reference (new, entry);
02376         }
02377     }
02378 }
02379 
02380 /* Read a line of input which is a node name, and go to that node. */
02381 DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
02382 {
02383   char *line;
02384 
02385 #define GOTO_COMPLETES
02386 #if defined (GOTO_COMPLETES)
02387   /* Build a completion list of all of the known nodes. */
02388   {
02389     register int fbi, i;
02390     FILE_BUFFER *current;
02391     REFERENCE **items = (REFERENCE **)NULL;
02392     int items_index = 0;
02393     int items_slots = 0;
02394 
02395     current = file_buffer_of_window (window);
02396 
02397     for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
02398       {
02399         FILE_BUFFER *fb;
02400         REFERENCE *entry;
02401         int this_is_the_current_fb;
02402 
02403         fb = info_loaded_files[fbi];
02404         this_is_the_current_fb = (current == fb);
02405 
02406         entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
02407         entry->filename = entry->nodename = (char *)NULL;
02408         entry->label = (char *)xmalloc (4 + strlen (fb->filename));
02409         sprintf (entry->label, "(%s)*", fb->filename);
02410 
02411         add_pointer_to_array
02412           (entry, items_index, items, items_slots, 10, REFERENCE *);
02413 
02414         if (fb->tags)
02415           {
02416             for (i = 0; fb->tags[i]; i++)
02417               {
02418                 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
02419                 entry->filename = entry->nodename = (char *)NULL;
02420               if (this_is_the_current_fb)
02421                 entry->label = xstrdup (fb->tags[i]->nodename);
02422               else
02423                 {
02424                   entry->label = (char *) xmalloc
02425                     (4 + strlen (fb->filename) +
02426                      strlen (fb->tags[i]->nodename));
02427                   sprintf (entry->label, "(%s)%s",
02428                           fb->filename, fb->tags[i]->nodename);
02429                 }
02430 
02431                 add_pointer_to_array
02432                   (entry, items_index, items, items_slots, 100, REFERENCE *);
02433               }
02434           }
02435       }
02436     line = info_read_maybe_completing (window, (char *) _("Goto node: "),
02437         items);
02438     info_free_references (items);
02439   }
02440 #else /* !GOTO_COMPLETES */
02441   line = info_read_in_echo_area (window, (char *) _("Goto node: "));
02442 #endif /* !GOTO_COMPLETES */
02443 
02444   /* If the user aborted, quit now. */
02445   if (!line)
02446     {
02447       info_abort_key (window, 0, 0);
02448       return;
02449     }
02450 
02451   canonicalize_whitespace (line);
02452 
02453   if (*line)
02454     info_parse_and_select (line, window);
02455 
02456   free (line);
02457   if (!info_error_was_printed)
02458     window_clear_echo_area ();
02459 }
02460 
02461 /* Follow the menu list in MENUS (list of strings terminated by a NULL
02462    entry) from INITIAL_NODE.  If can't continue at any point (no menu or
02463    no menu entry for the next item), return the node so far -- that
02464    might be INITIAL_NODE itself.  If error, *ERRSTR and *ERRARG[12] will
02465    be set to the error message and argument for message, otherwise they
02466    will be NULL.  */
02467 
02468 NODE *
02469 info_follow_menus (NODE *initial_node, char **menus,
02470     const char **errstr, char **errarg1, char **errarg2)
02471 {
02472   NODE *node = NULL;
02473   *errstr = *errarg1 = *errarg2 = NULL;
02474 
02475   for (; *menus; menus++)
02476     {
02477       static char *first_arg = NULL;
02478       REFERENCE **menu;
02479       REFERENCE *entry;
02480       char *arg = *menus; /* Remember the name of the menu entry we want. */
02481 
02482       /* A leading space is certainly NOT part of a node name.  Most
02483         probably, they typed a space after the separating comma.  The
02484         strings in menus[] have their whitespace canonicalized, so
02485         there's at most one space to ignore.  */
02486       if (*arg == ' ')
02487        arg++;
02488       if (!first_arg)
02489         first_arg = arg;
02490 
02491       /* Build and return a list of the menu items in this node. */
02492       menu = info_menu_of_node (initial_node);
02493 
02494       /* If no menu item in this node, stop here, but let the user
02495          continue to use Info.  Perhaps they wanted this node and didn't
02496          realize it. */
02497       if (!menu)
02498         {
02499           if (arg == first_arg)
02500             {
02501               node = make_manpage_node (first_arg);
02502               if (node)
02503                 goto maybe_got_node;
02504             }
02505           *errstr = _("No menu in node `%s'.");
02506           *errarg1 = node_printed_rep (initial_node);
02507           return initial_node;
02508         }
02509 
02510       /* Find the specified menu item. */
02511       entry = info_get_labeled_reference (arg, menu);
02512 
02513       /* If the item wasn't found, search the list sloppily.  Perhaps this
02514          user typed "buffer" when they really meant "Buffers". */
02515       if (!entry)
02516         {
02517           int i;
02518           int best_guess = -1;
02519 
02520           for (i = 0; (entry = menu[i]); i++)
02521             {
02522               if (strcasecmp (entry->label, arg) == 0)
02523                 break;
02524               else
02525                 if ((best_guess == -1)
02526                     && (strncasecmp (entry->label, arg, strlen (arg)) == 0))
02527                   best_guess = i;
02528             }
02529 
02530           if (!entry && best_guess != -1)
02531             entry = menu[best_guess];
02532         }
02533 
02534       /* If we still failed to find the reference, start Info with the current
02535          node anyway.  It is probably a misspelling. */
02536       if (!entry)
02537         {
02538           if (arg == first_arg)
02539             {
02540              /* Maybe they typed "info foo" instead of "info -f foo".  */
02541              node = info_get_node (first_arg, 0);
02542              if (node)
02543               add_file_directory_to_path (first_arg);
02544              else
02545               node = make_manpage_node (first_arg);
02546               if (node)
02547                 goto maybe_got_node;
02548             }
02549 
02550           info_free_references (menu);
02551           *errstr = _("No menu item `%s' in node `%s'.");
02552           *errarg1 = arg;
02553           *errarg2 = node_printed_rep (initial_node);
02554           return initial_node;
02555         }
02556 
02557       /* We have found the reference that the user specified.  If no
02558          filename in this reference, define it. */
02559       if (!entry->filename)
02560         entry->filename = xstrdup (initial_node->parent ? initial_node->parent
02561                                                      : initial_node->filename);
02562 
02563       /* Try to find this node.  */
02564       node = info_get_node (entry->filename, entry->nodename);
02565       if (!node && arg == first_arg)
02566        {
02567          node = make_manpage_node (first_arg);
02568          if (node)
02569            goto maybe_got_node;
02570        }
02571 
02572       /* Since we cannot find it, try using the label of the entry as a
02573          file, i.e., "(LABEL)Top".  */
02574       if (!node && entry->nodename
02575           && strcmp (entry->label, entry->nodename) == 0)
02576         node = info_get_node (entry->label, "Top");
02577 
02578     maybe_got_node:
02579       if (!node)
02580         {
02581           *errstr = _("Unable to find node referenced by `%s' in `%s'.");
02582           *errarg1 = xstrdup (entry->label);
02583           *errarg2 = node_printed_rep (initial_node);
02584           info_free_references (menu);
02585           return initial_node;
02586         }
02587 
02588       info_free_references (menu);
02589 
02590       /* Success.  Go round the loop again.  */
02591       free (initial_node);
02592       initial_node = node;
02593     }
02594 
02595   return initial_node;
02596 }
02597 
02598 /* Split STR into individual node names by writing null bytes in wherever
02599    there are commas and constructing a list of the resulting pointers.
02600    (We can do this since STR has had canonicalize_whitespace called on it.)
02601    Return array terminated with NULL.  */
02602 
02603 static char **
02604 split_list_of_nodenames (char *str)
02605 {
02606   unsigned len = 2;
02607   char **nodes = xmalloc (len * sizeof (char *));
02608 
02609   nodes[len - 2] = str;
02610 
02611   while (*str++)
02612     {
02613       if (*str == ',')
02614         {
02615           *str++ = 0;              /* get past the null byte */
02616           len++;
02617           nodes = xrealloc (nodes, len * sizeof (char *));
02618           nodes[len - 2] = str;
02619         }
02620     }
02621 
02622   nodes[len - 1] = NULL;
02623 
02624   return nodes;
02625 }
02626 
02627 
02628 /* Read a line of input which is a sequence of menus (starting from
02629    dir), and follow them.  */
02630 DECLARE_INFO_COMMAND (info_menu_sequence,
02631    _("Read a list of menus starting from dir and follow them"))
02632 {
02633   char *line = info_read_in_echo_area (window, (char *) _("Follow menus: "));
02634 
02635   /* If the user aborted, quit now. */
02636   if (!line)
02637     {
02638       info_abort_key (window, 0, 0);
02639       return;
02640     }
02641 
02642   canonicalize_whitespace (line);
02643 
02644   if (*line)
02645     {
02646       const char *errstr;
02647       char *errarg1, *errarg2;
02648       NODE *dir_node = info_get_node (NULL, NULL);
02649       char **nodes = split_list_of_nodenames (line);
02650       NODE *node = NULL;
02651 
02652       /* If DIR_NODE is NULL, they might be reading a file directly,
02653         like in "info -d . -f ./foo".  Try using "Top" instead.  */
02654       if (!dir_node)
02655        {
02656          char *file_name = window->node->parent;
02657 
02658          if (!file_name)
02659            file_name = window->node->filename;
02660          dir_node = info_get_node (file_name, NULL);
02661        }
02662 
02663       /* If we still cannot find the starting point, give up.
02664         We cannot allow a NULL pointer inside info_follow_menus.  */
02665       if (!dir_node)
02666        info_error ((char *) msg_cant_find_node, "Top", NULL);
02667       else
02668        node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
02669 
02670       free (nodes);
02671       if (!errstr)
02672         info_set_node_of_window (1, window, node);
02673       else
02674         info_error ((char *) errstr, errarg1, errarg2);
02675     }
02676 
02677   free (line);
02678   if (!info_error_was_printed)
02679     window_clear_echo_area ();
02680 }
02681 
02682 /* Search the menu MENU for a (possibly mis-spelled) entry ARG.
02683    Return the menu entry, or the best guess for what they meant by ARG,
02684    or NULL if there's nothing in this menu seems to fit the bill.
02685    If EXACT is non-zero, allow only exact matches.  */
02686 static REFERENCE *
02687 entry_in_menu (char *arg, REFERENCE **menu, int exact)
02688 {
02689   REFERENCE *entry;
02690 
02691   /* First, try to find the specified menu item verbatim.  */
02692   entry = info_get_labeled_reference (arg, menu);
02693 
02694   /* If the item wasn't found, search the list sloppily.  Perhaps we
02695      have "Option Summary", but ARG is "option".  */
02696   if (!entry && !exact)
02697     {
02698       int i;
02699       int best_guess = -1;
02700 
02701       for (i = 0; (entry = menu[i]); i++)
02702        {
02703          if (strcasecmp (entry->label, arg) == 0)
02704            break;
02705          else
02706            if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
02707              best_guess = i;
02708        }
02709 
02710       if (!entry && best_guess != -1)
02711        entry = menu[best_guess];
02712     }
02713 
02714   return entry;
02715 }
02716 
02717 /* Find the node that is the best candidate to list the PROGRAM's
02718    invocation info and its command-line options, by looking for menu
02719    items and chains of menu items with characteristic names.  */
02720 void
02721 info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program)
02722 {
02723   /* The list of node names typical for GNU manuals where the program
02724      usage and specifically the command-line arguments are described.
02725      This is pure heuristics.  I gathered these node names by looking
02726      at all the Info files I could put my hands on.  If you are
02727      looking for evidence to complain to the GNU project about
02728      non-uniform style of documentation, here you have your case!  */
02729   static const char *invocation_nodes[] = {
02730     "%s invocation",
02731     "Invoking %s",
02732     "Preliminaries", /* m4 has Invoking under Preliminaries! */
02733     "Invocation",
02734     "Command Arguments",/* Emacs */
02735     "Invoking `%s'",
02736     "%s options",
02737     "Options",
02738     "Option ",              /* e.g. "Option Summary" */
02739     "Invoking",
02740     "All options",   /* tar, paxutils */
02741     "Arguments",
02742     "%s cmdline",    /* ar */
02743     "%s",            /* last resort */
02744     (const char *)0
02745   };
02746   NODE *node = NULL;
02747   REFERENCE **menu;
02748   const char **try_node;
02749 
02750   /* We keep looking deeper and deeper in the menu structure until
02751      there are no more menus or no menu items from the above list.
02752      Some manuals have the invocation node sitting 3 or 4 levels deep
02753      in the menu hierarchy...  */
02754   for (node = initial_node; node; initial_node = node)
02755     {
02756       REFERENCE *entry = NULL;
02757 
02758       /* Build and return a list of the menu items in this node. */
02759       menu = info_menu_of_node (initial_node);
02760 
02761       /* If no menu item in this node, stop here.  Perhaps this node
02762         is the one they need.  */
02763       if (!menu)
02764        break;
02765 
02766       /* Look for node names typical for usage nodes in this menu.  */
02767       for (try_node = invocation_nodes; *try_node; try_node++)
02768        {
02769          char *nodename;
02770 
02771          nodename = xmalloc (strlen (program) + strlen (*try_node));
02772          sprintf (nodename, *try_node, program);
02773          /* The last resort "%s" is dangerous, so we restrict it
02774              to exact matches here.  */
02775          entry = entry_in_menu (nodename, menu,
02776                              strcmp (*try_node, "%s") == 0);
02777          free (nodename);
02778          if (entry)
02779            break;
02780        }
02781 
02782       if (!entry)
02783        break;
02784 
02785       if (!entry->filename)
02786        entry->filename = xstrdup (initial_node->parent ? initial_node->parent
02787                                : initial_node->filename);
02788       /* Try to find this node.  */
02789       node = info_get_node (entry->filename, entry->nodename);
02790       info_free_references (menu);
02791       if (!node)
02792        break;
02793     }
02794 
02795   /* We've got our best shot at the invocation node.  Now select it.  */
02796   if (initial_node)
02797     info_set_node_of_window (1, window, initial_node);
02798   if (!info_error_was_printed)
02799     window_clear_echo_area ();
02800 }
02801 
02802 /* Given a name of an Info file, find the name of the package it
02803    describes by removing the leading directories and extensions.  */
02804 char *
02805 program_name_from_file_name (char *file_name)
02806 {
02807   int i;
02808   char *program_name = xstrdup (filename_non_directory (file_name));
02809 
02810   for (i = strlen (program_name) - 1; i > 0; i--)
02811     if (program_name[i] == '.'
02812        && (FILENAME_CMPN (program_name + i, ".info", 5) == 0
02813            || FILENAME_CMPN (program_name + i, ".inf", 4) == 0
02814 #ifdef __MSDOS__
02815            || FILENAME_CMPN (program_name + i, ".i", 2) == 0
02816 #endif
02817            || isdigit (program_name[i + 1]))) /* a man page foo.1 */
02818       {
02819        program_name[i] = 0;
02820        break;
02821       }
02822   return program_name;
02823 }
02824 
02825 DECLARE_INFO_COMMAND (info_goto_invocation_node,
02826                     _("Find the node describing program invocation"))
02827 {
02828   const char *invocation_prompt = _("Find Invocation node of [%s]: ");
02829   char *program_name, *line;
02830   char *default_program_name, *prompt, *file_name;
02831   NODE *top_node;
02832 
02833   /* Intuit the name of the program they are likely to want.
02834      We use the file name of the current Info file as a hint.  */
02835   file_name = window->node->parent ? window->node->parent
02836                                : window->node->filename;
02837   default_program_name = program_name_from_file_name (file_name);
02838 
02839   prompt = (char *)xmalloc (strlen (default_program_name) +
02840                          strlen (invocation_prompt));
02841   sprintf (prompt, invocation_prompt, default_program_name);
02842   line = info_read_in_echo_area (window, prompt);
02843   free (prompt);
02844   if (!line)
02845     {
02846       info_abort_key (window, 0, 0);
02847       return;
02848     }
02849   if (*line)
02850     program_name = line;
02851   else
02852     program_name = default_program_name;
02853 
02854   /* In interactive usage they'd probably expect us to begin looking
02855      from the Top node.  */
02856   top_node = info_get_node (file_name, NULL);
02857   if (!top_node)
02858     info_error ((char *) msg_cant_find_node, "Top", NULL);
02859 
02860   info_intuit_options_node (window, top_node, program_name);
02861   free (line);
02862   free (default_program_name);
02863 }
02864 
02865 #if defined (HANDLE_MAN_PAGES)
02866 DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
02867 {
02868   char *line;
02869 
02870   line = info_read_in_echo_area (window, (char *) _("Get Manpage: "));
02871 
02872   if (!line)
02873     {
02874       info_abort_key (window, 0, 0);
02875       return;
02876     }
02877 
02878   canonicalize_whitespace (line);
02879 
02880   if (*line)
02881     {
02882       char *goto_command;
02883 
02884       goto_command = (char *)xmalloc
02885         (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
02886 
02887       sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
02888 
02889       info_parse_and_select (goto_command, window);
02890       free (goto_command);
02891     }
02892 
02893   free (line);
02894   if (!info_error_was_printed)
02895     window_clear_echo_area ();
02896 }
02897 #endif /* HANDLE_MAN_PAGES */
02898 
02899 /* Move to the "Top" node in this file. */
02900 DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
02901 {
02902   info_parse_and_select ("Top", window);
02903 }
02904 
02905 /* Move to the node "(dir)Top". */
02906 DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
02907 {
02908   info_parse_and_select ("(dir)Top", window);
02909 }
02910 
02911 
02912 /* Read the name of a node to kill.  The list of available nodes comes
02913    from the nodes appearing in the current window configuration. */
02914 static char *
02915 read_nodename_to_kill (WINDOW *window)
02916 {
02917   int iw;
02918   char *nodename;
02919   INFO_WINDOW *info_win;
02920   REFERENCE **menu = NULL;
02921   int menu_index = 0, menu_slots = 0;
02922   char *default_nodename = xstrdup (active_window->node->nodename);
02923   char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename));
02924 
02925   sprintf (prompt, _("Kill node (%s): "), default_nodename);
02926 
02927   for (iw = 0; (info_win = info_windows[iw]); iw++)
02928     {
02929       REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
02930       entry->label = xstrdup (info_win->window->node->nodename);
02931       entry->filename = entry->nodename = (char *)NULL;
02932 
02933       add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
02934                             REFERENCE *);
02935     }
02936 
02937   nodename = info_read_completing_in_echo_area (window, prompt, menu);
02938   free (prompt);
02939   info_free_references (menu);
02940   if (nodename && !*nodename)
02941     {
02942       free (nodename);
02943       nodename = default_nodename;
02944     }
02945   else
02946     free (default_nodename);
02947 
02948   return nodename;
02949 }
02950 
02951 
02952 /* Delete NODENAME from this window, showing the most
02953    recently selected node in this window. */
02954 static void
02955 kill_node (WINDOW *window, char *nodename)
02956 {
02957   int iw, i;
02958   INFO_WINDOW *info_win;
02959   NODE *temp;
02960 
02961   /* If there is no nodename to kill, quit now. */
02962   if (!nodename)
02963     {
02964       info_abort_key (window, 0, 0);
02965       return;
02966     }
02967 
02968   /* If there is a nodename, find it in our window list. */
02969   for (iw = 0; (info_win = info_windows[iw]); iw++)
02970     if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
02971        && info_win->window == window)
02972       break;
02973 
02974   if (!info_win)
02975     {
02976       if (*nodename)
02977         info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL);
02978       else
02979         window_clear_echo_area ();
02980 
02981       return;
02982     }
02983 
02984   /* If there are no more nodes left anywhere to view, complain and exit. */
02985   if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
02986     {
02987       info_error ((char *) _("Cannot kill the last node"), NULL, NULL);
02988       return;
02989     }
02990 
02991   /* INFO_WIN contains the node that the user wants to stop viewing.  Delete
02992      this node from the list of nodes previously shown in this window. */
02993   for (i = info_win->current; i < info_win->nodes_index; i++)
02994     info_win->nodes[i] = info_win->nodes[i + 1];
02995 
02996   /* There is one less node in this window's history list. */
02997   info_win->nodes_index--;
02998 
02999   /* Make this window show the most recent history node. */
03000   info_win->current = info_win->nodes_index - 1;
03001 
03002   /* If there aren't any nodes left in this window, steal one from the
03003      next window. */
03004   if (info_win->current < 0)
03005     {
03006       INFO_WINDOW *stealer;
03007       int which, pagetop;
03008       long point;
03009 
03010       if (info_windows[iw + 1])
03011         stealer = info_windows[iw + 1];
03012       else
03013         stealer = info_windows[0];
03014 
03015       /* If the node being displayed in the next window is not the most
03016          recently loaded one, get the most recently loaded one. */
03017       if ((stealer->nodes_index - 1) != stealer->current)
03018         which = stealer->nodes_index - 1;
03019 
03020       /* Else, if there is another node behind the stealers current node,
03021          use that one. */
03022       else if (stealer->current > 0)
03023         which = stealer->current - 1;
03024 
03025       /* Else, just use the node appearing in STEALER's window. */
03026       else
03027         which = stealer->current;
03028 
03029       /* Copy this node. */
03030       {
03031         NODE *copy = xmalloc (sizeof (NODE));
03032 
03033         temp = stealer->nodes[which];
03034         point = stealer->points[which];
03035         pagetop = stealer->pagetops[which];
03036 
03037         copy->filename = temp->filename;
03038         copy->parent = temp->parent;
03039         copy->nodename = temp->nodename;
03040         copy->contents = temp->contents;
03041         copy->nodelen = temp->nodelen;
03042         copy->flags = temp->flags;
03043         copy->display_pos = temp->display_pos;
03044 
03045         temp = copy;
03046       }
03047 
03048       window_set_node_of_window (info_win->window, temp);
03049       window->point = point;
03050       window->pagetop = pagetop;
03051       remember_window_and_node (info_win->window, temp);
03052     }
03053   else
03054     {
03055       temp = info_win->nodes[info_win->current];
03056       temp->display_pos = info_win->points[info_win->current];
03057       window_set_node_of_window (info_win->window, temp);
03058     }
03059 
03060   if (!info_error_was_printed)
03061     window_clear_echo_area ();
03062 
03063   if (auto_footnotes_p)
03064     info_get_or_remove_footnotes (window);
03065 }
03066 
03067 /* Kill current node, thus going back one in the node history.  I (karl)
03068    do not think this is completely correct yet, because of the
03069    window-changing stuff in kill_node, but it's a lot better than the
03070    previous implementation, which did not account for nodes being
03071    visited twice at all.  */
03072 DECLARE_INFO_COMMAND (info_history_node,
03073                       _("Select the most recently selected node"))
03074 {
03075   kill_node (window, active_window->node->nodename);
03076 }
03077 
03078 /* Kill named node.  */
03079 DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
03080 {
03081   char *nodename = read_nodename_to_kill (window);
03082   kill_node (window, nodename);
03083 }
03084 
03085 
03086 /* Read the name of a file and select the entire file. */
03087 DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
03088 {
03089   char *line;
03090 
03091   line = info_read_in_echo_area (window, (char *) _("Find file: "));
03092   if (!line)
03093     {
03094       info_abort_key (active_window, 1, 0);
03095       return;
03096     }
03097 
03098   if (*line)
03099     {
03100       NODE *node;
03101 
03102       node = info_get_node (line, "*");
03103       if (!node)
03104         {
03105           if (info_recent_file_error)
03106             info_error (info_recent_file_error, NULL, NULL);
03107           else
03108             info_error ((char *) _("Cannot find `%s'."), line, NULL);
03109         }
03110       else
03111         info_set_node_of_window (1, window, node);
03112 
03113       free (line);
03114     }
03115 
03116   if (!info_error_was_printed)
03117     window_clear_echo_area ();
03118 }
03119 
03120 /* **************************************************************** */
03121 /*                                                                  */
03122 /*                 Dumping and Printing Nodes                       */
03123 /*                                                                  */
03124 /* **************************************************************** */
03125 
03126 #define VERBOSE_NODE_DUMPING
03127 static void write_node_to_stream (NODE *node, FILE *stream);
03128 static void dump_node_to_stream (char *filename, char *nodename,
03129     FILE *stream, int dump_subnodes);
03130 static void initialize_dumping (void);
03131 
03132 /* Dump the nodes specified by FILENAME and NODENAMES to the file named
03133    in OUTPUT_FILENAME.  If DUMP_SUBNODES is non-zero, recursively dump
03134    the nodes which appear in the menu of each node dumped. */
03135 void
03136 dump_nodes_to_file (char *filename, char **nodenames,
03137     char *output_filename, int dump_subnodes)
03138 {
03139   register int i;
03140   FILE *output_stream;
03141 
03142   /* Get the stream to print the nodes to.  Special case of an output
03143      filename of "-" means to dump the nodes to stdout. */
03144   if (strcmp (output_filename, "-") == 0)
03145     output_stream = stdout;
03146   else
03147     output_stream = fopen (output_filename, "w");
03148 
03149   if (!output_stream)
03150     {
03151       info_error ((char *) _("Could not create output file `%s'."),
03152           output_filename, NULL);
03153       return;
03154     }
03155 
03156   /* Print each node to stream. */
03157   initialize_dumping ();
03158   for (i = 0; nodenames[i]; i++)
03159     dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
03160 
03161   if (output_stream != stdout)
03162     fclose (output_stream);
03163 
03164 #if defined (VERBOSE_NODE_DUMPING)
03165   info_error ((char *) _("Done."), NULL, NULL);
03166 #endif /* VERBOSE_NODE_DUMPING */
03167 }
03168 
03169 /* A place to remember already dumped nodes. */
03170 static char **dumped_already = (char **)NULL;
03171 static int dumped_already_index = 0;
03172 static int dumped_already_slots = 0;
03173 
03174 static void
03175 initialize_dumping (void)
03176 {
03177   dumped_already_index = 0;
03178 }
03179 
03180 /* Get and print the node specified by FILENAME and NODENAME to STREAM.
03181    If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
03182    in the menu of each node dumped. */
03183 static void
03184 dump_node_to_stream (char *filename, char *nodename,
03185     FILE *stream, int dump_subnodes)
03186 {
03187   register int i;
03188   NODE *node;
03189 
03190   node = info_get_node (filename, nodename);
03191 
03192   if (!node)
03193     {
03194       if (info_recent_file_error)
03195         info_error (info_recent_file_error, NULL, NULL);
03196       else
03197         {
03198           if (filename && *nodename != '(')
03199             info_error ((char *) msg_cant_file_node,
03200                 filename_non_directory (filename),
03201                 nodename);
03202           else
03203             info_error ((char *) msg_cant_find_node, nodename, NULL);
03204         }
03205       return;
03206     }
03207 
03208   /* If we have already dumped this node, don't dump it again. */
03209   for (i = 0; i < dumped_already_index; i++)
03210     if (strcmp (node->nodename, dumped_already[i]) == 0)
03211       {
03212         free (node);
03213         return;
03214       }
03215   add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
03216                         dumped_already_slots, 50, char *);
03217 
03218 #if defined (VERBOSE_NODE_DUMPING)
03219   /* Maybe we should print some information about the node being output. */
03220   info_error ((char *) _("Writing node %s..."), node_printed_rep (node), NULL);
03221 #endif /* VERBOSE_NODE_DUMPING */
03222 
03223   write_node_to_stream (node, stream);
03224 
03225   /* If we are dumping subnodes, get the list of menu items in this node,
03226      and dump each one recursively. */
03227   if (dump_subnodes)
03228     {
03229       REFERENCE **menu = (REFERENCE **)NULL;
03230 
03231       /* If this node is an Index, do not dump the menu references. */
03232       if (string_in_line ("Index", node->nodename) == -1)
03233         menu = info_menu_of_node (node);
03234 
03235       if (menu)
03236         {
03237           for (i = 0; menu[i]; i++)
03238             {
03239               /* We don't dump Info files which are different than the
03240                  current one. */
03241               if (!menu[i]->filename)
03242                 dump_node_to_stream
03243                   (filename, menu[i]->nodename, stream, dump_subnodes);
03244             }
03245           info_free_references (menu);
03246         }
03247     }
03248 
03249   free (node);
03250 }
03251 
03252 /* Dump NODE to FILENAME.  If DUMP_SUBNODES is non-zero, recursively dump
03253    the nodes which appear in the menu of each node dumped. */
03254 void
03255 dump_node_to_file (NODE *node, char *filename, int dump_subnodes)
03256 {
03257   FILE *output_stream;
03258   char *nodes_filename;
03259 
03260   /* Get the stream to print this node to.  Special case of an output
03261      filename of "-" means to dump the nodes to stdout. */
03262   if (strcmp (filename, "-") == 0)
03263     output_stream = stdout;
03264   else
03265     output_stream = fopen (filename, "w");
03266 
03267   if (!output_stream)
03268     {
03269       info_error ((char *) _("Could not create output file `%s'."), filename,
03270           NULL);
03271       return;
03272     }
03273 
03274   if (node->parent)
03275     nodes_filename = node->parent;
03276   else
03277     nodes_filename = node->filename;
03278 
03279   initialize_dumping ();
03280   dump_node_to_stream
03281     (nodes_filename, node->nodename, output_stream, dump_subnodes);
03282 
03283   if (output_stream != stdout)
03284     fclose (output_stream);
03285 
03286 #if defined (VERBOSE_NODE_DUMPING)
03287   info_error ((char *) _("Done."), NULL, NULL);
03288 #endif /* VERBOSE_NODE_DUMPING */
03289 }
03290 
03291 #if !defined (DEFAULT_INFO_PRINT_COMMAND)
03292 #  define DEFAULT_INFO_PRINT_COMMAND "lpr"
03293 #endif /* !DEFAULT_INFO_PRINT_COMMAND */
03294 
03295 DECLARE_INFO_COMMAND (info_print_node,
03296  _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
03297 {
03298   print_node (window->node);
03299 }
03300 
03301 /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
03302 void
03303 print_node (NODE *node)
03304 {
03305   FILE *printer_pipe;
03306   char *print_command = getenv ("INFO_PRINT_COMMAND");
03307   int piping = 0;
03308 
03309   if (!print_command || !*print_command)
03310     print_command = DEFAULT_INFO_PRINT_COMMAND;
03311 
03312   /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
03313      (default) text mode, since the printer drivers there need to see
03314      DOS-style CRLF pairs at the end of each line.
03315 
03316      FIXME: if we are to support Mac-style text files, we might need
03317      to convert the text here.  */
03318 
03319   /* INFO_PRINT_COMMAND which says ">file" means write to that file.
03320      Presumably, the name of the file is the local printer device.  */
03321   if (*print_command == '>')
03322     printer_pipe = fopen (++print_command, "w");
03323   else
03324     {
03325       printer_pipe = popen (print_command, "w");
03326       piping = 1;
03327     }
03328 
03329   if (!printer_pipe)
03330     {
03331       info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL);
03332       return;
03333     }
03334 
03335 #if defined (VERBOSE_NODE_DUMPING)
03336   /* Maybe we should print some information about the node being output. */
03337   info_error ((char *) _("Printing node %s..."), node_printed_rep (node), NULL);
03338 #endif /* VERBOSE_NODE_DUMPING */
03339 
03340   write_node_to_stream (node, printer_pipe);
03341   if (piping)
03342     pclose (printer_pipe);
03343   else
03344     fclose (printer_pipe);
03345 
03346 #if defined (VERBOSE_NODE_DUMPING)
03347   info_error ((char *) _("Done."), NULL, NULL);
03348 #endif /* VERBOSE_NODE_DUMPING */
03349 }
03350 
03351 static void
03352 write_node_to_stream (NODE *node, FILE *stream)
03353 {
03354   fwrite (node->contents, 1, node->nodelen, stream);
03355 }
03356 
03357 /* **************************************************************** */
03358 /*                                                                  */
03359 /*                    Info Searching Commands                       */
03360 /*                                                                  */
03361 /* **************************************************************** */
03362 
03363 /* Variable controlling the garbage collection of files briefly visited
03364    during searches.  Such files are normally gc'ed, unless they were
03365    compressed to begin with.  If this variable is non-zero, it says
03366    to gc even those file buffer contents which had to be uncompressed. */
03367 int gc_compressed_files = 0;
03368 
03369 static void info_gc_file_buffers (void);
03370 static void info_search_1 (WINDOW *window, int count,
03371     unsigned char key, int case_sensitive, int ask_for_string);
03372 
03373 static char *search_string = (char *)NULL;
03374 static int search_string_size = 0;
03375 static int isearch_is_active = 0;
03376 
03377 static int last_search_direction = 0;
03378 static int last_search_case_sensitive = 0;
03379 
03380 /* Return the file buffer which belongs to WINDOW's node. */
03381 FILE_BUFFER *
03382 file_buffer_of_window (WINDOW *window)
03383 {
03384   /* If this window has no node, then it has no file buffer. */
03385   if (!window->node)
03386     return ((FILE_BUFFER *)NULL);
03387 
03388   if (window->node->parent)
03389     return (info_find_file (window->node->parent));
03390 
03391   if (window->node->filename)
03392     return (info_find_file (window->node->filename));
03393 
03394   return ((FILE_BUFFER *)NULL);
03395 }
03396 
03397 /* Search for STRING in NODE starting at START.  Return -1 if the string
03398    was not found, or the location of the string if it was.  If WINDOW is
03399    passed as non-null, set the window's node to be NODE, its point to be
03400    the found string, and readjust the window's pagetop.  Final argument
03401    DIR says which direction to search in.  If it is positive, search
03402    forward, else backwards. */
03403 long
03404 info_search_in_node (char *string, NODE *node, long int start,
03405     WINDOW *window, int dir, int case_sensitive)
03406 {
03407   SEARCH_BINDING binding;
03408   long offset;
03409 
03410   binding.buffer = node->contents;
03411   binding.start = start;
03412   binding.end = node->nodelen;
03413   binding.flags = 0;
03414   if (!case_sensitive)
03415     binding.flags |= S_FoldCase;
03416 
03417   if (dir < 0)
03418     {
03419       binding.end = 0;
03420       binding.flags |= S_SkipDest;
03421     }
03422 
03423   if (binding.start < 0)
03424     return (-1);
03425 
03426   /* For incremental searches, we always wish to skip past the string. */
03427   if (isearch_is_active)
03428     binding.flags |= S_SkipDest;
03429 
03430   offset = search (string, &binding);
03431 
03432   if (offset != -1 && window)
03433     {
03434       set_remembered_pagetop_and_point (window);
03435       if (window->node != node)
03436         window_set_node_of_window (window, node);
03437       window->point = offset;
03438       window_adjust_pagetop (window);
03439     }
03440   return (offset);
03441 }
03442 
03443 /* Search NODE, looking for the largest possible match of STRING.  Start the
03444    search at START.  Return the absolute position of the match, or -1, if
03445    no part of the string could be found. */
03446 long
03447 info_target_search_node (NODE *node, char *string, long int start)
03448 {
03449   register int i;
03450   long offset = 0;
03451   char *target;
03452 
03453   target = xstrdup (string);
03454   i = strlen (target);
03455 
03456   /* Try repeatedly searching for this string while removing words from
03457      the end of it. */
03458   while (i)
03459     {
03460       target[i] = '\0';
03461       offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
03462 
03463       if (offset != -1)
03464         break;
03465 
03466       /* Delete the last word from TARGET. */
03467       for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
03468     }
03469   free (target);
03470   return (offset);
03471 }
03472 
03473 /* Search for STRING starting in WINDOW at point.  If the string is found
03474    in this node, set point to that position.  Otherwise, get the file buffer
03475    associated with WINDOW's node, and search through each node in that file.
03476    If the search fails, return non-zero, else zero.  Side-effect window
03477    leaving the node and point where the string was found current. */
03478 static int
03479 info_search_internal (char *string, WINDOW *window,
03480     int dir, int case_sensitive)
03481 {
03482   register int i;
03483   FILE_BUFFER *file_buffer;
03484   char *initial_nodename;
03485   long ret, start = 0;
03486 
03487   file_buffer = file_buffer_of_window (window);
03488   initial_nodename = window->node->nodename;
03489 
03490   /* This used to begin from window->point, unless this was a repeated
03491      search command.  But invoking search with an argument loses with
03492      that logic, since info_last_executed_command is then set to
03493      info_add_digit_to_numeric_arg.  I think there's no sense in
03494      ``finding'' a string that is already under the cursor, anyway.  */
03495   ret = info_search_in_node
03496         (string, window->node, window->point + dir, window, dir,
03497          case_sensitive);
03498 
03499   if (ret != -1)
03500     {
03501       /* We won! */
03502       if (!echo_area_is_active && !isearch_is_active)
03503         window_clear_echo_area ();
03504       return (0);
03505     }
03506 
03507   /* The string wasn't found in the current node.  Search through the
03508      window's file buffer, iff the current node is not "*". */
03509   if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
03510     return (-1);
03511 
03512   /* If this file has tags, search through every subfile, starting at
03513      this node's subfile and node.  Otherwise, search through the
03514      file's node list. */
03515   if (file_buffer->tags)
03516     {
03517       register int current_tag = 0, number_of_tags;
03518       char *last_subfile;
03519       TAG *tag;
03520 
03521       /* Find number of tags and current tag. */
03522       last_subfile = (char *)NULL;
03523       for (i = 0; file_buffer->tags[i]; i++)
03524         if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
03525           {
03526             current_tag = i;
03527             last_subfile = file_buffer->tags[i]->filename;
03528           }
03529 
03530       number_of_tags = i;
03531 
03532       /* If there is no last_subfile, our tag wasn't found. */
03533       if (!last_subfile)
03534         return (-1);
03535 
03536       /* Search through subsequent nodes, wrapping around to the top
03537          of the info file until we find the string or return to this
03538          window's node and point. */
03539       while (1)
03540         {
03541           NODE *node;
03542 
03543           /* Allow C-g to quit the search, failing it if pressed. */
03544           return_if_control_g (-1);
03545 
03546           /* Find the next tag that isn't an anchor.  */
03547           for (i = current_tag + dir; i != current_tag; i += dir)
03548             {
03549               if (i < 0)
03550                 i = number_of_tags - 1;
03551               else if (i == number_of_tags)
03552                 i = 0;
03553 
03554               tag = file_buffer->tags[i];
03555               if (tag->nodelen != 0)
03556                 break;
03557             }
03558 
03559           /* If we got past out starting point, bail out.  */
03560           if (i == current_tag)
03561             return (-1);
03562           current_tag = i;
03563 
03564           if (!echo_area_is_active && (last_subfile != tag->filename))
03565             {
03566               window_message_in_echo_area
03567                 ((char *) _("Searching subfile %s ..."),
03568                  filename_non_directory (tag->filename), NULL);
03569 
03570               last_subfile = tag->filename;
03571             }
03572 
03573           node = info_get_node (file_buffer->filename, tag->nodename);
03574 
03575           if (!node)
03576             {
03577               /* If not doing i-search... */
03578               if (!echo_area_is_active)
03579                 {
03580                   if (info_recent_file_error)
03581                     info_error (info_recent_file_error, NULL, NULL);
03582                   else
03583                     info_error ((char *) msg_cant_file_node,
03584                                 filename_non_directory (file_buffer->filename),
03585                                 tag->nodename);
03586                 }
03587               return (-1);
03588             }
03589 
03590           if (dir < 0)
03591             start = tag->nodelen;
03592 
03593           ret =
03594             info_search_in_node (string, node, start, window, dir,
03595                                  case_sensitive);
03596 
03597           /* Did we find the string in this node? */
03598           if (ret != -1)
03599             {
03600               /* Yes!  We win. */
03601               remember_window_and_node (window, node);
03602               if (!echo_area_is_active)
03603                 window_clear_echo_area ();
03604               return (0);
03605             }
03606 
03607           /* No.  Free this node, and make sure that we haven't passed
03608              our starting point. */
03609           free (node);
03610 
03611           if (strcmp (initial_nodename, tag->nodename) == 0)
03612             return (-1);
03613         }
03614     }
03615   return (-1);
03616 }
03617 
03618 DECLARE_INFO_COMMAND (info_search_case_sensitively,
03619                       _("Read a string and search for it case-sensitively"))
03620 {
03621   last_search_direction = count > 0 ? 1 : -1;
03622   last_search_case_sensitive = 1;
03623   info_search_1 (window, count, key, 1, 1);
03624 }
03625 
03626 DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
03627 {
03628   last_search_direction = count > 0 ? 1 : -1;
03629   last_search_case_sensitive = 0;
03630   info_search_1 (window, count, key, 0, 1);
03631 }
03632 
03633 DECLARE_INFO_COMMAND (info_search_backward,
03634                     _("Read a string and search backward for it"))
03635 {
03636   last_search_direction = count > 0 ? -1 : 1;
03637   last_search_case_sensitive = 0;
03638   info_search_1 (window, -count, key, 0, 1);
03639 }
03640 
03641 static void
03642 info_search_1 (WINDOW *window, int count, unsigned char key,
03643     int case_sensitive, int ask_for_string)
03644 {
03645   char *line, *prompt;
03646   int result, old_pagetop;
03647   int direction;
03648 
03649   if (count < 0)
03650     {
03651       direction = -1;
03652       count = -count;
03653     }
03654   else
03655     {
03656       direction = 1;
03657       if (count == 0)
03658         count = 1;   /* for backward compatibility */
03659     }
03660 
03661   /* Read a string from the user, defaulting the search to SEARCH_STRING. */
03662   if (!search_string)
03663     {
03664       search_string = (char *)xmalloc (search_string_size = 100);
03665       search_string[0] = '\0';
03666     }
03667 
03668   if (ask_for_string)
03669     {
03670       prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: "))
03671                             + strlen (_("Search backward"))
03672                             + strlen (_("Search"))
03673                             + strlen (_(" case-sensitively "))
03674                             + strlen (_(" "))
03675                             + strlen (search_string));
03676 
03677       sprintf (prompt, _("%s%sfor string [%s]: "),
03678                direction < 0 ? _("Search backward") : _("Search"),
03679                case_sensitive ? _(" case-sensitively ") : _(" "),
03680                search_string);
03681 
03682       line = info_read_in_echo_area (window, prompt);
03683       free (prompt);
03684 
03685       if (!line)
03686         {
03687           info_abort_key (window, 0, 0);
03688           return;
03689         }
03690 
03691       if (*line)
03692         {
03693           if (strlen (line) + 1 > (unsigned int) search_string_size)
03694             search_string = (char *) xrealloc
03695               (search_string, (search_string_size += 50 + strlen (line)));
03696 
03697           strcpy (search_string, line);
03698           free (line);
03699         }
03700     }
03701 
03702   /* If the search string includes upper-case letters, make the search
03703      case-sensitive.  */
03704   if (case_sensitive == 0)
03705     for (line = search_string; *line; line++)
03706       if (isupper (*line))
03707         {
03708           case_sensitive = 1;
03709           break;
03710         }
03711 
03712   old_pagetop = active_window->pagetop;
03713   for (result = 0; result == 0 && count--; )
03714     result = info_search_internal (search_string,
03715                                    active_window, direction, case_sensitive);
03716 
03717   if (result != 0 && !info_error_was_printed)
03718     info_error ((char *) _("Search failed."), NULL, NULL);
03719   else if (old_pagetop != active_window->pagetop)
03720     {
03721       int new_pagetop;
03722 
03723       new_pagetop = active_window->pagetop;
03724       active_window->pagetop = old_pagetop;
03725       set_window_pagetop (active_window, new_pagetop);
03726       if (auto_footnotes_p)
03727         info_get_or_remove_footnotes (active_window);
03728     }
03729 
03730   /* Perhaps free the unreferenced file buffers that were searched, but
03731      not retained. */
03732   info_gc_file_buffers ();
03733 }
03734 
03735 DECLARE_INFO_COMMAND (info_search_next,
03736                     _("Repeat last search in the same direction"))
03737 {
03738   if (!last_search_direction)
03739     info_error ((char *) _("No previous search string"), NULL, NULL);
03740   else
03741     info_search_1 (window, last_search_direction * count,
03742                  key, last_search_case_sensitive, 0);
03743 }
03744 
03745 DECLARE_INFO_COMMAND (info_search_previous,
03746                     _("Repeat last search in the reverse direction"))
03747 {
03748   if (!last_search_direction)
03749     info_error ((char *) _("No previous search string"), NULL, NULL);
03750   else
03751     info_search_1 (window, -last_search_direction * count,
03752                  key, last_search_case_sensitive, 0);
03753 }
03754 
03755 /* **************************************************************** */
03756 /*                                                                  */
03757 /*                      Incremental Searching                       */
03758 /*                                                                  */
03759 /* **************************************************************** */
03760 
03761 static void incremental_search (WINDOW *window, int count,
03762     unsigned char ignore);
03763 
03764 DECLARE_INFO_COMMAND (isearch_forward,
03765                       _("Search interactively for a string as you type it"))
03766 {
03767   incremental_search (window, count, key);
03768 }
03769 
03770 DECLARE_INFO_COMMAND (isearch_backward,
03771                       _("Search interactively for a string as you type it"))
03772 {
03773   incremental_search (window, -count, key);
03774 }
03775 
03776 /* Incrementally search for a string as it is typed. */
03777 /* The last accepted incremental search string. */
03778 static char *last_isearch_accepted = (char *)NULL;
03779 
03780 /* The current incremental search string. */
03781 static char *isearch_string = (char *)NULL;
03782 static int isearch_string_index = 0;
03783 static int isearch_string_size = 0;
03784 static unsigned char isearch_terminate_search_key = ESC;
03785 
03786 /* Array of search states. */
03787 static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
03788 static int isearch_states_index = 0;
03789 static int isearch_states_slots = 0;
03790 
03791 /* Push the state of this search. */
03792 static void
03793 push_isearch (WINDOW *window, int search_index, int direction, int failing)
03794 {
03795   SEARCH_STATE *state;
03796 
03797   state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
03798   window_get_state (window, state);
03799   state->search_index = search_index;
03800   state->direction = direction;
03801   state->failing = failing;
03802 
03803   add_pointer_to_array (state, isearch_states_index, isearch_states,
03804                         isearch_states_slots, 20, SEARCH_STATE *);
03805 }
03806 
03807 /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
03808 static void
03809 pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing)
03810 {
03811   SEARCH_STATE *state;
03812 
03813   if (isearch_states_index)
03814     {
03815       isearch_states_index--;
03816       state = isearch_states[isearch_states_index];
03817       window_set_state (window, state);
03818       *search_index = state->search_index;
03819       *direction = state->direction;
03820       *failing = state->failing;
03821 
03822       free (state);
03823       isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
03824     }
03825 }
03826 
03827 /* Free the memory used by isearch_states. */
03828 static void
03829 free_isearch_states (void)
03830 {
03831   register int i;
03832 
03833   for (i = 0; i < isearch_states_index; i++)
03834     {
03835       free (isearch_states[i]);
03836       isearch_states[i] = (SEARCH_STATE *)NULL;
03837     }
03838   isearch_states_index = 0;
03839 }
03840 
03841 /* Display the current search in the echo area. */
03842 static void
03843 show_isearch_prompt (int dir, unsigned char *string, int failing_p)
03844 {
03845   register int i;
03846   const char *prefix;
03847   char *prompt, *p_rep;
03848   unsigned int prompt_len, p_rep_index, p_rep_size;
03849 
03850   if (dir < 0)
03851     prefix = _("I-search backward: ");
03852   else
03853     prefix = _("I-search: ");
03854 
03855   p_rep_index = p_rep_size = 0;
03856   p_rep = (char *)NULL;
03857   for (i = 0; string[i]; i++)
03858     {
03859       char *rep;
03860 
03861       switch (string[i])
03862         {
03863         case ' ': rep = " "; break;
03864         case LFD: rep = "\\n"; break;
03865         case TAB: rep = "\\t"; break;
03866         default:
03867           rep = pretty_keyname (string[i]);
03868         }
03869       if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
03870         p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
03871 
03872       strcpy (p_rep + p_rep_index, rep);
03873       p_rep_index += strlen (rep);
03874     }
03875 
03876   prompt_len = strlen (prefix) + p_rep_index + 1;
03877   if (failing_p)
03878     prompt_len += strlen (_("Failing "));
03879   prompt = xmalloc (prompt_len);
03880   sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
03881            p_rep ? p_rep : "");
03882 
03883   window_message_in_echo_area ("%s", prompt, NULL);
03884   maybe_free (p_rep);
03885   free (prompt);
03886   display_cursor_at_point (active_window);
03887 }
03888 
03889 static void
03890 incremental_search (WINDOW *window, int count, unsigned char ignore)
03891 {
03892   unsigned char key;
03893   int last_search_result, search_result, dir;
03894   SEARCH_STATE mystate, orig_state;
03895   char *p;
03896   int case_sensitive = 0;
03897 
03898   if (count < 0)
03899     dir = -1;
03900   else
03901     dir = 1;
03902 
03903   last_search_result = search_result = 0;
03904 
03905   window_get_state (window, &orig_state);
03906 
03907   isearch_string_index = 0;
03908   if (!isearch_string_size)
03909     isearch_string = (char *)xmalloc (isearch_string_size = 50);
03910 
03911   /* Show the search string in the echo area. */
03912   isearch_string[isearch_string_index] = '\0';
03913   show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
03914 
03915   isearch_is_active = 1;
03916 
03917   while (isearch_is_active)
03918     {
03919       VFunction *func = (VFunction *)NULL;
03920       int quoted = 0;
03921 
03922       /* If a recent display was interrupted, then do the redisplay now if
03923          it is convenient. */
03924       if (!info_any_buffered_input_p () && display_was_interrupted_p)
03925         {
03926           display_update_one_window (window);
03927           display_cursor_at_point (active_window);
03928         }
03929 
03930       /* Read a character and dispatch on it. */
03931       key = info_get_input_char ();
03932       window_get_state (window, &mystate);
03933 
03934       if (key == DEL || key == Control ('h'))
03935         {
03936           /* User wants to delete one level of search? */
03937           if (!isearch_states_index)
03938             {
03939               terminal_ring_bell ();
03940               continue;
03941             }
03942           else
03943             {
03944               pop_isearch
03945                 (window, &isearch_string_index, &dir, &search_result);
03946               isearch_string[isearch_string_index] = '\0';
03947               show_isearch_prompt (dir, (unsigned char *) isearch_string,
03948                   search_result);
03949               goto after_search;
03950             }
03951         }
03952       else if (key == Control ('q'))
03953         {
03954           key = info_get_input_char ();
03955           quoted = 1;
03956         }
03957 
03958       /* We are about to search again, or quit.  Save the current search. */
03959       push_isearch (window, isearch_string_index, dir, search_result);
03960 
03961       if (quoted)
03962         goto insert_and_search;
03963 
03964       if (!Meta_p (key) || key > 32)
03965         {
03966           /* If this key is not a keymap, get its associated function,
03967              if any.  If it is a keymap, then it's probably ESC from an
03968              arrow key, and we handle that case below.  */
03969           char type = window->keymap[key].type;
03970           func = type == ISFUNC
03971                  ? InfoFunction(window->keymap[key].function)
03972                  : NULL;  /* function member is a Keymap if ISKMAP */
03973 
03974           if (isprint (key) || (type == ISFUNC && func == NULL))
03975             {
03976             insert_and_search:
03977 
03978               if (isearch_string_index + 2 >= isearch_string_size)
03979                 isearch_string = (char *)xrealloc
03980                   (isearch_string, isearch_string_size += 100);
03981 
03982               isearch_string[isearch_string_index++] = key;
03983               isearch_string[isearch_string_index] = '\0';
03984               goto search_now;
03985             }
03986           else if (func == (VFunction *) isearch_forward
03987               || func == (VFunction *) isearch_backward)
03988             {
03989              /* If this key invokes an incremental search, then this
03990                means that we will either search again in the same
03991                direction, search again in the reverse direction, or
03992                insert the last search string that was accepted through
03993                incremental searching. */
03994               if ((func == (VFunction *) isearch_forward && dir > 0) ||
03995                   (func == (VFunction *) isearch_backward && dir < 0))
03996                 {
03997                   /* If the user has typed no characters, then insert the
03998                      last successful search into the current search string. */
03999                   if (isearch_string_index == 0)
04000                     {
04001                       /* Of course, there must be something to insert. */
04002                       if (last_isearch_accepted)
04003                         {
04004                           if (strlen ((char *) last_isearch_accepted) + 1
04005                               >= (unsigned int) isearch_string_size)
04006                             isearch_string = (char *)
04007                               xrealloc (isearch_string,
04008                                         isearch_string_size += 10 +
04009                                         strlen (last_isearch_accepted));
04010                           strcpy (isearch_string, last_isearch_accepted);
04011                           isearch_string_index = strlen (isearch_string);
04012                           goto search_now;
04013                         }
04014                       else
04015                         continue;
04016                     }
04017                   else
04018                     {
04019                       /* Search again in the same direction.  This means start
04020                          from a new place if the last search was successful. */
04021                       if (search_result == 0)
04022                         window->point += dir;
04023                     }
04024                 }
04025               else
04026                 {
04027                   /* Reverse the direction of the search. */
04028                   dir = -dir;
04029                 }
04030             }
04031           else if (func == (VFunction *) info_abort_key)
04032             {
04033               /* If C-g pressed, and the search is failing, pop the search
04034                  stack back to the last unfailed search. */
04035               if (isearch_states_index && (search_result != 0))
04036                 {
04037                   terminal_ring_bell ();
04038                   while (isearch_states_index && (search_result != 0))
04039                     pop_isearch
04040                       (window, &isearch_string_index, &dir, &search_result);
04041                   isearch_string[isearch_string_index] = '\0';
04042                   show_isearch_prompt (dir, (unsigned char *) isearch_string,
04043                       search_result);
04044                   continue;
04045                 }
04046               else
04047                 goto exit_search;
04048             }
04049           else
04050             goto exit_search;
04051         }
04052       else
04053         {
04054         exit_search:
04055           /* The character is not printable, or it has a function which is
04056              non-null.  Exit the search, remembering the search string.  If
04057              the key is not the same as the isearch_terminate_search_key,
04058              then push it into pending input. */
04059           if (isearch_string_index && func != (VFunction *) info_abort_key)
04060             {
04061               maybe_free (last_isearch_accepted);
04062               last_isearch_accepted = xstrdup (isearch_string);
04063             }
04064 
04065          /* If the key is the isearch_terminate_search_key, but some buffered
04066             input is pending, it is almost invariably because the ESC key is
04067             actually the beginning of an escape sequence, like in case they
04068             pressed an arrow key.  So don't gobble the ESC key, push it back
04069             into pending input.  */
04070          /* FIXME: this seems like a kludge!  We need a more reliable
04071             mechanism to know when ESC is a separate key and when it is
04072             part of an escape sequence.  */
04073           if (key != RET  /* Emacs addicts want RET to get lost */
04074              && (key != isearch_terminate_search_key
04075                 || info_any_buffered_input_p ()))
04076             info_set_pending_input (key);
04077 
04078           if (func == (VFunction *) info_abort_key)
04079             {
04080               if (isearch_states_index)
04081                 window_set_state (window, &orig_state);
04082             }
04083 
04084           if (!echo_area_is_active)
04085             window_clear_echo_area ();
04086 
04087           if (auto_footnotes_p)
04088             info_get_or_remove_footnotes (active_window);
04089 
04090           isearch_is_active = 0;
04091           continue;
04092         }
04093 
04094       /* Search for the contents of isearch_string. */
04095     search_now:
04096       show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
04097 
04098       /* If the search string includes upper-case letters, make the
04099          search case-sensitive.  */
04100       for (p = isearch_string; *p; p++)
04101         if (isupper (*p))
04102           {
04103             case_sensitive = 1;
04104             break;
04105           }
04106       
04107 
04108       if (search_result == 0)
04109         {
04110           /* Check to see if the current search string is right here.  If
04111              we are looking at it, then don't bother calling the search
04112              function. */
04113           if (((dir < 0) &&
04114               ((case_sensitive ? strncmp : strncasecmp)
04115                             (window->node->contents + window->point,
04116                              isearch_string, isearch_string_index) == 0)) ||
04117               ((dir > 0) &&
04118                ((window->point - isearch_string_index) >= 0) &&
04119               ((case_sensitive ? strncmp : strncasecmp)
04120                             (window->node->contents +
04121                              (window->point - (isearch_string_index - 1)),
04122                              isearch_string, isearch_string_index) == 0)))
04123             {
04124               if (dir > 0)
04125                 window->point++;
04126             }
04127           else
04128             search_result = info_search_internal (isearch_string,
04129                                             window, dir, case_sensitive);
04130         }
04131 
04132       /* If this search failed, and we didn't already have a failed search,
04133          then ring the terminal bell. */
04134       if (search_result != 0 && last_search_result == 0)
04135         terminal_ring_bell ();
04136 
04137     after_search:
04138       show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
04139 
04140       if (search_result == 0)
04141         {
04142           if ((mystate.node == window->node) &&
04143               (mystate.pagetop != window->pagetop))
04144             {
04145               int newtop = window->pagetop;
04146               window->pagetop = mystate.pagetop;
04147               set_window_pagetop (window, newtop);
04148             }
04149           display_update_one_window (window);
04150           display_cursor_at_point (window);
04151         }
04152 
04153       last_search_result = search_result;
04154     }
04155 
04156   /* Free the memory used to remember each search state. */
04157   free_isearch_states ();
04158 
04159   /* Perhaps GC some file buffers. */
04160   info_gc_file_buffers ();
04161 
04162   /* After searching, leave the window in the correct state. */
04163   if (!echo_area_is_active)
04164     window_clear_echo_area ();
04165 }
04166 
04167 /* GC some file buffers.  A file buffer can be gc-ed if there we have
04168    no nodes in INFO_WINDOWS that reference this file buffer's contents.
04169    Garbage collecting a file buffer means to free the file buffers
04170    contents. */
04171 static void
04172 info_gc_file_buffers (void)
04173 {
04174   register int fb_index, iw_index, i;
04175   register FILE_BUFFER *fb;
04176   register INFO_WINDOW *iw;
04177 
04178   if (!info_loaded_files)
04179     return;
04180 
04181   for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
04182     {
04183       int fb_referenced_p = 0;
04184 
04185       /* If already gc-ed, do nothing. */
04186       if (!fb->contents)
04187         continue;
04188 
04189       /* If this file had to be uncompressed, check to see if we should
04190          gc it.  This means that the user-variable "gc-compressed-files"
04191          is non-zero. */
04192       if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
04193         continue;
04194 
04195       /* If this file's contents are not gc-able, move on. */
04196       if (fb->flags & N_CannotGC)
04197         continue;
04198 
04199       /* Check each INFO_WINDOW to see if it has any nodes which reference
04200          this file. */
04201       for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
04202         {
04203           for (i = 0; iw->nodes && iw->nodes[i]; i++)
04204             {
04205               if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
04206                   (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
04207                 {
04208                   fb_referenced_p = 1;
04209                   break;
04210                 }
04211             }
04212         }
04213 
04214       /* If this file buffer wasn't referenced, free its contents. */
04215       if (!fb_referenced_p)
04216         {
04217           free (fb->contents);
04218           fb->contents = (char *)NULL;
04219         }
04220     }
04221 }
04222 
04223 /* **************************************************************** */
04224 /*                                                                  */
04225 /*                Traversing and Selecting References               */
04226 /*                                                                  */
04227 /* **************************************************************** */
04228 
04229 /* Move to the next or previous cross reference in this node. */
04230 static void
04231 info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir)
04232 {
04233   long firstmenu, firstxref;
04234   long nextmenu, nextxref;
04235   long placement = -1;
04236   long start = 0;
04237   NODE *node = window->node;
04238 
04239   if (dir < 0)
04240     start = node->nodelen;
04241 
04242   /* This search is only allowed to fail if there is no menu or cross
04243      reference in the current node.  Otherwise, the first menu or xref
04244      found is moved to. */
04245 
04246   firstmenu = info_search_in_node
04247     (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
04248 
04249   /* FIRSTMENU may point directly to the line defining the menu.  Skip that
04250      and go directly to the first item. */
04251 
04252   if (firstmenu != -1)
04253     {
04254       char *text = node->contents + firstmenu;
04255 
04256       if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
04257         firstmenu = info_search_in_node
04258           (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0);
04259     }
04260 
04261   firstxref =
04262     info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
04263 
04264 #if defined (HANDLE_MAN_PAGES)
04265   if ((firstxref == -1) && (node->flags & N_IsManPage))
04266     {
04267       firstxref = locate_manpage_xref (node, start, dir);
04268     }
04269 #endif /* HANDLE_MAN_PAGES */
04270 
04271   if (firstmenu == -1 && firstxref == -1)
04272     {
04273       info_error ((char *) msg_no_xref_node, NULL, NULL);
04274       return;
04275     }
04276 
04277   /* There is at least one cross reference or menu entry in this node.
04278      Try hard to find the next available one. */
04279 
04280   nextmenu = info_search_in_node
04281     (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
04282 
04283   nextxref = info_search_in_node
04284     (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
04285 
04286 #if defined (HANDLE_MAN_PAGES)
04287   if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
04288     nextxref = locate_manpage_xref (node, window->point + dir, dir);
04289 #endif /* HANDLE_MAN_PAGES */
04290 
04291   /* Ignore "Menu:" as a menu item. */
04292   if (nextmenu != -1)
04293     {
04294       char *text = node->contents + nextmenu;
04295 
04296       if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
04297         nextmenu = info_search_in_node
04298           (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
04299     }
04300 
04301   /* If there is both a next menu entry, and a next xref entry, choose the
04302      one which occurs first.  Otherwise, select the one which actually
04303      appears in this node following point. */
04304   if (nextmenu != -1 && nextxref != -1)
04305     {
04306       if (((dir == 1) && (nextmenu < nextxref)) ||
04307           ((dir == -1) && (nextmenu > nextxref)))
04308         placement = nextmenu + 1;
04309       else
04310         placement = nextxref;
04311     }
04312   else if (nextmenu != -1)
04313     placement = nextmenu + 1;
04314   else if (nextxref != -1)
04315     placement = nextxref;
04316 
04317   /* If there was neither a menu or xref entry appearing in this node after
04318      point, choose the first menu or xref entry appearing in this node. */
04319   if (placement == -1)
04320     {
04321       if (firstmenu != -1 && firstxref != -1)
04322         {
04323           if (((dir == 1) && (firstmenu < firstxref)) ||
04324               ((dir == -1) && (firstmenu > firstxref)))
04325             placement = firstmenu + 1;
04326           else
04327             placement = firstxref;
04328         }
04329       else if (firstmenu != -1)
04330         placement = firstmenu + 1;
04331       else
04332         placement = firstxref;
04333     }
04334   window->point = placement;
04335   window_adjust_pagetop (window);
04336   window->flags |= W_UpdateWindow;
04337 }
04338 
04339 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
04340                       _("Move to the previous cross reference"))
04341 {
04342   if (count < 0)
04343     info_move_to_prev_xref (window, -count, key);
04344   else
04345     info_move_to_xref (window, count, key, -1);
04346 }
04347 
04348 DECLARE_INFO_COMMAND (info_move_to_next_xref,
04349                       _("Move to the next cross reference"))
04350 {
04351   if (count < 0)
04352     info_move_to_next_xref (window, -count, key);
04353   else
04354     info_move_to_xref (window, count, key, 1);
04355 }
04356 
04357 /* Select the menu item or reference that appears on this line. */
04358 DECLARE_INFO_COMMAND (info_select_reference_this_line,
04359                       _("Select reference or menu item appearing on this line"))
04360 {
04361   char *line;
04362 
04363   if (window->line_starts)
04364     line = window->line_starts[window_line_of_point (window)];
04365   else
04366     line = "";
04367 
04368   /* If this line contains a menu item, select that one. */
04369   if (strncmp ("* ", line, 2) == 0)
04370     info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
04371   else
04372     info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
04373 }
04374 
04375 /* **************************************************************** */
04376 /*                                                                  */
04377 /*                  Miscellaneous Info Commands                     */
04378 /*                                                                  */
04379 /* **************************************************************** */
04380 
04381 /* What to do when C-g is pressed in a window. */
04382 DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
04383 {
04384   /* If error printing doesn't oridinarily ring the bell, do it now,
04385      since C-g always rings the bell.  Otherwise, let the error printer
04386      do it. */
04387   if (!info_error_rings_bell_p)
04388     terminal_ring_bell ();
04389   info_error ((char *) _("Quit"), NULL, NULL);
04390 
04391   info_initialize_numeric_arg ();
04392   info_clear_pending_input ();
04393   info_last_executed_command = (VFunction *)NULL;
04394 }
04395 
04396 /* Move the cursor to the desired line of the window. */
04397 DECLARE_INFO_COMMAND (info_move_to_window_line,
04398    _("Move the cursor to a specific line of the window"))
04399 {
04400   int line;
04401 
04402   /* With no numeric argument of any kind, default to the center line. */
04403   if (!info_explicit_arg && count == 1)
04404     line = (window->height / 2) + window->pagetop;
04405   else
04406     {
04407       if (count < 0)
04408         line = (window->height + count) + window->pagetop;
04409       else
04410         line = window->pagetop + count;
04411     }
04412 
04413   /* If the line doesn't appear in this window, make it do so. */
04414   if ((line - window->pagetop) >= window->height)
04415     line = window->pagetop + (window->height - 1);
04416 
04417   /* If the line is too small, make it fit. */
04418   if (line < window->pagetop)
04419     line = window->pagetop;
04420 
04421   /* If the selected line is past the bottom of the node, force it back. */
04422   if (line >= window->line_count)
04423     line = window->line_count - 1;
04424 
04425   window->point = (window->line_starts[line] - window->node->contents);
04426 }
04427 
04428 /* Clear the screen and redraw its contents.  Given a numeric argument,
04429    move the line the cursor is on to the COUNT'th line of the window. */
04430 DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
04431 {
04432   if ((!info_explicit_arg && count == 1) || echo_area_is_active)
04433     {
04434       terminal_clear_screen ();
04435       display_clear_display (the_display);
04436       window_mark_chain (windows, W_UpdateWindow);
04437       display_update_display (windows);
04438     }
04439   else
04440     {
04441       int desired_line, point_line;
04442       int new_pagetop;
04443 
04444       point_line = window_line_of_point (window) - window->pagetop;
04445 
04446       if (count < 0)
04447         desired_line = window->height + count;
04448       else
04449         desired_line = count;
04450 
04451       if (desired_line < 0)
04452         desired_line = 0;
04453 
04454       if (desired_line >= window->height)
04455         desired_line = window->height - 1;
04456 
04457       if (desired_line == point_line)
04458         return;
04459 
04460       new_pagetop = window->pagetop + (point_line - desired_line);
04461 
04462       set_window_pagetop (window, new_pagetop);
04463     }
04464 }
04465 /* This command does nothing.  It is the fact that a key is bound to it
04466    that has meaning.  See the code at the top of info_session (). */
04467 DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
04468 {}
04469 
04470 
04471 /* **************************************************************** */
04472 /*                                                                  */
04473 /*               Reading Keys and Dispatching on Them               */
04474 /*                                                                  */
04475 /* **************************************************************** */
04476 
04477 /* Declaration only.  Special cased in info_dispatch_on_key ().
04478    Doc string is to avoid ugly results with describe_key etc.  */
04479 DECLARE_INFO_COMMAND (info_do_lowercase_version,
04480                     _("Run command bound to this key's lowercase variant"))
04481 {}
04482 
04483 static void
04484 dispatch_error (char *keyseq)
04485 {
04486   char *rep;
04487 
04488   rep = pretty_keyseq (keyseq);
04489 
04490   if (!echo_area_is_active)
04491     info_error ((char *) _("Unknown command (%s)."), rep, NULL);
04492   else
04493     {
04494       char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid")));
04495       sprintf (temp, _("`%s' is invalid"), rep);
04496       terminal_ring_bell ();
04497       inform_in_echo_area (temp);
04498       free (temp);
04499     }
04500 }
04501 
04502 /* Keeping track of key sequences. */
04503 static char *info_keyseq = (char *)NULL;
04504 static int info_keyseq_index = 0;
04505 static int info_keyseq_size = 0;
04506 static int info_keyseq_displayed_p = 0;
04507 
04508 /* Initialize the length of the current key sequence. */
04509 void
04510 initialize_keyseq (void)
04511 {
04512   info_keyseq_index = 0;
04513   info_keyseq_displayed_p = 0;
04514 }
04515 
04516 /* Add CHARACTER to the current key sequence. */
04517 void
04518 add_char_to_keyseq (char character)
04519 {
04520   if (info_keyseq_index + 2 >= info_keyseq_size)
04521     info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
04522 
04523   info_keyseq[info_keyseq_index++] = character;
04524   info_keyseq[info_keyseq_index] = '\0';
04525 }
04526 
04527 /* Display the current value of info_keyseq.  If argument EXPECTING is
04528    non-zero, input is expected to be read after the key sequence is
04529    displayed, so add an additional prompting character to the sequence. */
04530 static void
04531 display_info_keyseq (int expecting_future_input)
04532 {
04533   char *rep;
04534 
04535   rep = pretty_keyseq (info_keyseq);
04536   if (expecting_future_input)
04537     strcat (rep, "-");
04538 
04539   if (echo_area_is_active)
04540     inform_in_echo_area (rep);
04541   else
04542     {
04543       window_message_in_echo_area (rep, NULL, NULL);
04544       display_cursor_at_point (active_window);
04545     }
04546   info_keyseq_displayed_p = 1;
04547 }
04548 
04549 /* Called by interactive commands to read a keystroke. */
04550 unsigned char
04551 info_get_another_input_char (void)
04552 {
04553   int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
04554 
04555   /* If there isn't any input currently available, then wait a
04556      moment looking for input.  If we don't get it fast enough,
04557      prompt a little bit with the current key sequence. */
04558   if (!info_keyseq_displayed_p)
04559     {
04560       ready = 1;
04561       if (!info_any_buffered_input_p () &&
04562           !info_input_pending_p ())
04563         {
04564 #if defined (FD_SET)
04565           struct timeval timer;
04566           fd_set readfds;
04567 
04568           FD_ZERO (&readfds);
04569           FD_SET (fileno (info_input_stream), &readfds);
04570           timer.tv_sec = 1;
04571           timer.tv_usec = 750;
04572           ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
04573 #else
04574           ready = 0;
04575 #endif /* FD_SET */
04576       }
04577     }
04578 
04579   if (!ready)
04580     display_info_keyseq (1);
04581 
04582   return (info_get_input_char ());
04583 }
04584 
04585 /* Do the command associated with KEY in MAP.  If the associated command is
04586    really a keymap, then read another key, and dispatch into that map. */
04587 void
04588 info_dispatch_on_key (unsigned char key, Keymap map)
04589 {
04590 #if !defined(INFOKEY)
04591   if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
04592     {
04593       if (map[ESC].type == ISKMAP)
04594         {
04595           map = (Keymap)map[ESC].function;
04596           add_char_to_keyseq (ESC);
04597           key = UnMeta (key);
04598           info_dispatch_on_key (key, map);
04599         }
04600       else
04601         {
04602           dispatch_error (info_keyseq);
04603         }
04604       return;
04605     }
04606 #endif /* INFOKEY */
04607 
04608   switch (map[key].type)
04609     {
04610     case ISFUNC:
04611       {
04612         VFunction *func;
04613 
04614         func = InfoFunction(map[key].function);
04615         if (func != (VFunction *)NULL)
04616           {
04617             /* Special case info_do_lowercase_version (). */
04618             if (func == (VFunction *) info_do_lowercase_version)
04619               {
04620 #if defined(INFOKEY)
04621               unsigned char lowerkey;
04622 
04623               lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
04624               if (lowerkey == key)
04625                 {
04626                   add_char_to_keyseq (key);
04627                   dispatch_error (info_keyseq);
04628                   return;
04629                 }
04630                 info_dispatch_on_key (lowerkey, map);
04631 #else /* !INFOKEY */
04632                 info_dispatch_on_key (tolower (key), map);
04633 #endif /* INFOKEY */
04634                 return;
04635               }
04636 
04637             add_char_to_keyseq (key);
04638 
04639             if (info_keyseq_displayed_p)
04640               display_info_keyseq (0);
04641 
04642             {
04643               WINDOW *where;
04644 
04645               where = active_window;
04646               (*InfoFunction(map[key].function))
04647                 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
04648 
04649               /* If we have input pending, then the last command was a prefix
04650                  command.  Don't change the value of the last function vars.
04651                  Otherwise, remember the last command executed in the var
04652                  appropriate to the window in which it was executed. */
04653               if (!info_input_pending_p ())
04654                 {
04655                   if (where == the_echo_area)
04656                     ea_last_executed_command = InfoFunction(map[key].function);
04657                   else
04658                     info_last_executed_command = InfoFunction(map[key].function);
04659                 }
04660             }
04661           }
04662         else
04663           {
04664             add_char_to_keyseq (key);
04665             dispatch_error (info_keyseq);
04666             return;
04667           }
04668       }
04669       break;
04670 
04671     case ISKMAP:
04672       add_char_to_keyseq (key);
04673       if (map[key].function != (InfoCommand *)NULL)
04674         {
04675           unsigned char newkey;
04676 
04677           newkey = info_get_another_input_char ();
04678           info_dispatch_on_key (newkey, (Keymap)map[key].function);
04679         }
04680       else
04681         {
04682           dispatch_error (info_keyseq);
04683           return;
04684         }
04685       break;
04686     }
04687 }
04688 
04689 /* **************************************************************** */
04690 /*                                                                  */
04691 /*                      Numeric Arguments                           */
04692 /*                                                                  */
04693 /* **************************************************************** */
04694 
04695 /* Handle C-u style numeric args, as well as M--, and M-digits. */
04696 
04697 /* Non-zero means that an explicit argument has been passed to this
04698    command, as in C-u C-v. */
04699 int info_explicit_arg = 0;
04700 
04701 /* The sign of the numeric argument. */
04702 int info_numeric_arg_sign = 1;
04703 
04704 /* The value of the argument itself. */
04705 int info_numeric_arg = 1;
04706 
04707 /* Add the current digit to the argument in progress. */
04708 DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
04709                       _("Add this digit to the current numeric argument"))
04710 {
04711   info_numeric_arg_digit_loop (window, 0, key);
04712 }
04713 
04714 /* C-u, universal argument.  Multiply the current argument by 4.
04715    Read a key.  If the key has nothing to do with arguments, then
04716    dispatch on it.  If the key is the abort character then abort. */
04717 DECLARE_INFO_COMMAND (info_universal_argument,
04718                       _("Start (or multiply by 4) the current numeric argument"))
04719 {
04720   info_numeric_arg *= 4;
04721   info_numeric_arg_digit_loop (window, 0, 0);
04722 }
04723 
04724 /* Create a default argument. */
04725 void
04726 info_initialize_numeric_arg (void)
04727 {
04728   info_numeric_arg = info_numeric_arg_sign = 1;
04729   info_explicit_arg = 0;
04730 }
04731 
04732 DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
04733                       _("Internally used by \\[universal-argument]"))
04734 {
04735   unsigned char pure_key;
04736   Keymap keymap = window->keymap;
04737 
04738   while (1)
04739     {
04740       if (key)
04741         pure_key = key;
04742       else
04743         {
04744           if (display_was_interrupted_p && !info_any_buffered_input_p ())
04745             display_update_display (windows);
04746 
04747           if (active_window != the_echo_area)
04748             display_cursor_at_point (active_window);
04749 
04750           pure_key = key = info_get_another_input_char ();
04751 
04752 #if !defined(INFOKEY)
04753           if (Meta_p (key))
04754             add_char_to_keyseq (ESC);
04755 
04756           add_char_to_keyseq (UnMeta (key));
04757 #else /* defined(INFOKEY) */
04758           add_char_to_keyseq (key);
04759 #endif /* defined(INFOKEY) */
04760         }
04761 
04762 #if !defined(INFOKEY)
04763       if (Meta_p (key))
04764         key = UnMeta (key);
04765 #endif /* !defined(INFOKEY) */
04766 
04767       if (keymap[key].type == ISFUNC
04768           && InfoFunction(keymap[key].function)
04769               == (VFunction *) info_universal_argument)
04770         {
04771           info_numeric_arg *= 4;
04772           key = 0;
04773           continue;
04774         }
04775 
04776 #if defined(INFOKEY)
04777       if (Meta_p (key))
04778         key = UnMeta (key);
04779 #endif /* !defined(INFOKEY) */
04780 
04781 
04782       if (isdigit (key))
04783         {
04784           if (info_explicit_arg)
04785             info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
04786           else
04787             info_numeric_arg = (key - '0');
04788           info_explicit_arg = 1;
04789         }
04790       else
04791         {
04792           if (key == '-' && !info_explicit_arg)
04793             {
04794               info_numeric_arg_sign = -1;
04795               info_numeric_arg = 1;
04796             }
04797           else
04798             {
04799               info_keyseq_index--;
04800               info_dispatch_on_key (pure_key, keymap);
04801               return;
04802             }
04803         }
04804       key = 0;
04805     }
04806 }
04807 
04808 /* **************************************************************** */
04809 /*                                                                  */
04810 /*                      Input Character Buffering                   */
04811 /*                                                                  */
04812 /* **************************************************************** */
04813 
04814 /* Character waiting to be read next. */
04815 static int pending_input_character = 0;
04816 
04817 /* How to make there be no pending input. */
04818 static void
04819 info_clear_pending_input (void)
04820 {
04821   pending_input_character = 0;
04822 }
04823 
04824 /* How to set the pending input character. */
04825 static void
04826 info_set_pending_input (unsigned char key)
04827 {
04828   pending_input_character = key;
04829 }
04830 
04831 /* How to see if there is any pending input. */
04832 unsigned char
04833 info_input_pending_p (void)
04834 {
04835   return (pending_input_character);
04836 }
04837 
04838 /* Largest number of characters that we can read in advance. */
04839 #define MAX_INFO_INPUT_BUFFERING 512
04840 
04841 static int pop_index = 0, push_index = 0;
04842 static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
04843 
04844 /* Add KEY to the buffer of characters to be read. */
04845 static void
04846 info_push_typeahead (unsigned char key)
04847 {
04848   /* Flush all pending input in the case of C-g pressed. */
04849   if (key == Control ('g'))
04850     {
04851       push_index = pop_index;
04852       info_set_pending_input (Control ('g'));
04853     }
04854   else
04855     {
04856       info_input_buffer[push_index++] = key;
04857       if ((unsigned int) push_index >= sizeof (info_input_buffer))
04858         push_index = 0;
04859     }
04860 }
04861 
04862 /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
04863 static int
04864 info_input_buffer_space_available (void)
04865 {
04866   if (pop_index > push_index)
04867     return (pop_index - push_index);
04868   else
04869     return (sizeof (info_input_buffer) - (push_index - pop_index));
04870 }
04871 
04872 /* Get a key from the buffer of characters to be read.
04873    Return the key in KEY.
04874    Result is non-zero if there was a key, or 0 if there wasn't. */
04875 static int
04876 info_get_key_from_typeahead (unsigned char *key)
04877 {
04878   if (push_index == pop_index)
04879     return (0);
04880 
04881   *key = info_input_buffer[pop_index++];
04882 
04883   if ((unsigned int) pop_index >= sizeof (info_input_buffer))
04884     pop_index = 0;
04885 
04886   return (1);
04887 }
04888 
04889 int
04890 info_any_buffered_input_p (void)
04891 {
04892   info_gather_typeahead ();
04893   return (push_index != pop_index);
04894 }
04895 
04896 /* If characters are available to be read, then read them and stuff them into
04897    info_input_buffer.  Otherwise, do nothing. */
04898 void
04899 info_gather_typeahead (void)
04900 {
04901   register int i = 0;
04902   int tty, space_avail;
04903   long chars_avail;
04904   unsigned char input[MAX_INFO_INPUT_BUFFERING];
04905 
04906   tty = fileno (info_input_stream);
04907   chars_avail = 0;
04908 
04909   space_avail = info_input_buffer_space_available ();
04910 
04911   /* If we can just find out how many characters there are to read, do so. */
04912 #if defined (FIONREAD)
04913   {
04914     ioctl (tty, FIONREAD, &chars_avail);
04915 
04916     if (chars_avail > space_avail)
04917       chars_avail = space_avail;
04918 
04919     if (chars_avail)
04920       chars_avail = read (tty, &input[0], chars_avail);
04921   }
04922 #else /* !FIONREAD */
04923 #  if defined (O_NDELAY)
04924   {
04925     int flags;
04926 
04927     flags = fcntl (tty, F_GETFL, 0);
04928 
04929     fcntl (tty, F_SETFL, (flags | O_NDELAY));
04930       chars_avail = read (tty, &input[0], space_avail);
04931     fcntl (tty, F_SETFL, flags);
04932 
04933     if (chars_avail == -1)
04934       chars_avail = 0;
04935   }
04936 #  else  /* !O_NDELAY */
04937 #   ifdef __DJGPP__
04938   {
04939     extern long pc_term_chars_avail (void);
04940 
04941     if (isatty (tty))
04942       chars_avail = pc_term_chars_avail ();
04943     else
04944       {
04945        /* We could be more accurate by calling ltell, but we have no idea
04946           whether tty is buffered by stdio functions, and if so, how many
04947           characters are already waiting in the buffer.  So we punt.  */
04948        struct stat st;
04949 
04950        if (fstat (tty, &st) < 0)
04951          chars_avail = 1;
04952        else
04953          chars_avail = st.st_size;
04954       }
04955     if (chars_avail > space_avail)
04956       chars_avail = space_avail;
04957     if (chars_avail)
04958       chars_avail = read (tty, &input[0], chars_avail);
04959   }
04960 #   endif/* __DJGPP__ */
04961 #  endif /* O_NDELAY */
04962 #endif /* !FIONREAD */
04963 
04964   while (i < chars_avail)
04965     {
04966       info_push_typeahead (input[i]);
04967       i++;
04968     }
04969 }
04970 
04971 /* How to read a single character. */
04972 unsigned char
04973 info_get_input_char (void)
04974 {
04975   unsigned char keystroke;
04976 
04977   info_gather_typeahead ();
04978 
04979   if (pending_input_character)
04980     {
04981       keystroke = pending_input_character;
04982       pending_input_character = 0;
04983     }
04984   else if (info_get_key_from_typeahead (&keystroke) == 0)
04985     {
04986       int rawkey;
04987       unsigned char c;
04988       int tty = fileno (info_input_stream);
04989 
04990       /* Using stream I/O causes FIONREAD etc to fail to work
04991          so unless someone can find a portable way of finding
04992          out how many characters are currently buffered, we
04993          should stay with away from stream I/O.
04994          --Egil Kvaleberg <egilk@sn.no>, January 1997.  */
04995 #ifdef EINTR
04996       /* Keep reading if we got EINTR, so that we don't just exit.
04997          --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
04998          22 Dec 1997.  */
04999       {
05000         int n;
05001         do
05002          n = read (tty, &c, 1);
05003         while (n == -1 && errno == EINTR);
05004         rawkey = n == 1 ? c : EOF;
05005       }
05006 #else
05007       rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
05008 #endif
05009 
05010       keystroke = rawkey;
05011 
05012       if (rawkey == EOF)
05013         {
05014           if (info_input_stream != stdin)
05015             {
05016               fclose (info_input_stream);
05017               info_input_stream = stdin;
05018              tty = fileno (info_input_stream);
05019               display_inhibited = 0;
05020               display_update_display (windows);
05021               display_cursor_at_point (active_window);
05022               rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
05023               keystroke = rawkey;
05024             }
05025 
05026           if (rawkey == EOF)
05027             {
05028               terminal_unprep_terminal ();
05029               close_dribble_file ();
05030               xexit (0);
05031             }
05032         }
05033     }
05034 
05035   if (info_dribble_file)
05036     dribble (keystroke);
05037 
05038   return keystroke;
05039 }