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