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