No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / texinfo / info / session.c
blob4dfa4cb22bd172652071cb05f848eece17b23539
1 /* $NetBSD: session.c,v 1.4 2008/09/02 08:00:24 christos Exp $ */
3 /* session.c -- user windowing interface to Info.
4 Id: session.c,v 1.16 2004/12/14 00:15:36 karl Exp
6 Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
7 Free Software Foundation, Inc.
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.
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.
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.
23 Originally written by Brian Fox (bfox@ai.mit.edu). */
25 #include "info.h"
26 #include "search.h"
27 #include <sys/ioctl.h>
29 #if defined (HAVE_SYS_TIME_H)
30 # include <sys/time.h>
31 # define HAVE_STRUCT_TIMEVAL
32 #endif /* HAVE_SYS_TIME_H */
34 #if defined (HANDLE_MAN_PAGES)
35 # include "man.h"
36 #endif
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);
44 /* **************************************************************** */
45 /* */
46 /* Running an Info Session */
47 /* */
48 /* **************************************************************** */
50 /* The place that we are reading input from. */
51 static FILE *info_input_stream = NULL;
53 /* The last executed command. */
54 VFunction *info_last_executed_command = NULL;
56 /* Becomes non-zero when 'q' is typed to an Info window. */
57 int quit_info_immediately = 0;
59 /* Array of structures describing for each window which nodes have been
60 visited in that window. */
61 INFO_WINDOW **info_windows = NULL;
63 /* Where to add the next window, if we need to add one. */
64 static int info_windows_index = 0;
66 /* Number of slots allocated to `info_windows'. */
67 static int info_windows_slots = 0;
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);
73 /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
74 For each loaded node, create a new window. Always split the largest of the
75 available windows. */
76 void
77 begin_multiple_window_info_session (char *filename, char **nodenames)
79 register int i;
80 WINDOW *window = (WINDOW *)NULL;
82 for (i = 0; nodenames[i]; i++)
84 NODE *node;
86 node = info_get_node (filename, nodenames[i]);
88 if (!node)
89 break;
91 /* If this is the first node, initialize the info session. */
92 if (!window)
94 initialize_info_session (node, 1);
95 window = active_window;
97 else
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;
105 for (win = windows; win; win = win->next)
106 if (win->height > max_height)
108 max_height = win->height;
109 largest = win;
112 if (!largest)
114 display_update_display (windows);
115 info_error ((char *) msg_cant_find_window, NULL, NULL);
116 info_session ();
117 xexit (0);
120 active_window = largest;
121 window = window_make_window (node);
122 if (window)
124 window_tile_windows (TILE_INTERNALS);
125 remember_window_and_node (window, node);
127 else
129 display_update_display (windows);
130 info_error ((char *) msg_win_too_small, NULL, NULL);
131 info_session ();
132 xexit (0);
136 display_startup_message_and_start ();
139 /* Start an info session with INITIAL_NODE, and an error message in the echo
140 area made from FORMAT and ARG. */
141 void
142 begin_info_session_with_error (NODE *initial_node, char *format,
143 void *arg1, void *arg2)
145 initialize_info_session (initial_node, 1);
146 info_error (format, arg1, arg2);
147 info_session ();
150 /* Start an info session with INITIAL_NODE. */
151 void
152 begin_info_session (NODE *initial_node)
154 initialize_info_session (initial_node, 1);
155 display_startup_message_and_start ();
158 void
159 display_startup_message_and_start (void)
161 char *format;
163 format = replace_in_documentation
164 ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."),
167 window_message_in_echo_area (format, VERSION, NULL);
168 info_session ();
171 /* Run an info session with an already initialized window and node. */
172 void
173 info_session (void)
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 ();
187 /* Here is a window-location dependent event loop. Called from the
188 functions info_session (), and from read_xxx_in_echo_area (). */
189 void
190 info_read_and_dispatch (void)
192 unsigned char key;
193 int done;
194 done = 0;
196 while (!done && !quit_info_immediately)
198 int lk = 0;
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;
206 if (echo_area_is_active)
208 lk = echo_area_last_command_was_kill;
209 echo_area_prep_read ();
212 if (!info_any_buffered_input_p ())
213 display_update_display (windows);
215 display_cursor_at_point (active_window);
216 info_initialize_numeric_arg ();
218 initialize_keyseq ();
219 key = info_get_input_char ();
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 ();
226 info_error_was_printed = 0;
228 /* Do the selected command. */
229 info_dispatch_on_key (key, active_window->keymap);
231 if (echo_area_is_active)
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;
240 if (ea_last_executed_command == (VFunction *) ea_newline ||
241 info_aborted_echo_area)
243 ea_last_executed_command = (VFunction *)NULL;
244 done = 1;
247 if (info_last_executed_command == (VFunction *) info_quit)
248 quit_info_immediately = 1;
250 else if (info_last_executed_command == (VFunction *) info_quit)
251 done = 1;
255 /* Found in signals.c */
256 extern void initialize_info_signal_handler (void );
258 /* Initialize the first info session by starting the terminal, window,
259 and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
260 void
261 initialize_info_session (NODE *node, int clear_screen)
263 char *term_name = getenv ("TERM");
264 terminal_initialize_terminal (term_name);
266 if (terminal_is_dumb_p)
268 if (!term_name)
269 term_name = "dumb";
271 info_error ((char *) msg_term_too_dumb, term_name, NULL);
272 xexit (1);
275 if (clear_screen)
277 terminal_prep_terminal ();
278 terminal_clear_screen ();
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);
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;
291 /* If input has not been redirected yet, make it come from unbuffered
292 standard input. */
293 if (!info_input_stream)
295 setbuf (stdin, NULL);
296 info_input_stream = stdin;
299 info_windows_initialized_p = 1;
302 /* Tell Info that input is coming from the file FILENAME. */
303 void
304 info_set_input_from_file (char *filename)
306 FILE *stream;
308 /* Input may include binary characters. */
309 stream = fopen (filename, FOPEN_RBIN);
311 if (!stream)
312 return;
314 if ((info_input_stream != (FILE *)NULL) &&
315 (info_input_stream != stdin))
316 fclose (info_input_stream);
318 info_input_stream = stream;
320 if (stream != stdin)
321 display_inhibited = 1;
324 /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
325 static INFO_WINDOW *
326 get_info_window_of_window (WINDOW *window)
328 register int i;
329 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
331 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
332 if (info_win->window == window)
333 break;
335 return (info_win);
338 /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
339 values if the window and node are the same as the current one being
340 displayed. */
341 void
342 set_remembered_pagetop_and_point (WINDOW *window)
344 INFO_WINDOW *info_win;
346 info_win = get_info_window_of_window (window);
348 if (!info_win)
349 return;
351 if (info_win->nodes_index &&
352 (info_win->nodes[info_win->current] == window->node))
354 info_win->pagetops[info_win->current] = window->pagetop;
355 info_win->points[info_win->current] = window->point;
359 void
360 remember_window_and_node (WINDOW *window, NODE *node)
362 /* See if we already have this window in our list. */
363 INFO_WINDOW *info_win = get_info_window_of_window (window);
365 /* If the window wasn't already on our list, then make a new entry. */
366 if (!info_win)
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;
377 add_pointer_to_array (info_win, info_windows_index, info_windows,
378 info_windows_slots, 10, INFO_WINDOW *);
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;
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)
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));
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;
416 #define DEBUG_FORGET_WINDOW_AND_NODES
417 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
418 static void
419 consistency_check_info_windows (void)
421 register int i;
423 for (i = 0; i < info_windows_index; i++)
425 WINDOW *win;
427 for (win = windows; win; win = win->next)
428 if (win == info_windows[i]->window)
429 break;
431 if (!win)
432 abort ();
435 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
437 /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
438 void
439 forget_window_and_nodes (WINDOW *window)
441 register int i;
442 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
444 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
445 if (info_win->window == window)
446 break;
448 /* If we found the window to forget, then do so. */
449 if (info_win)
451 while (i < info_windows_index)
453 info_windows[i] = info_windows[i + 1];
454 i++;
457 info_windows_index--;
458 info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
460 if (info_win->nodes)
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);
470 maybe_free (info_win->pagetops);
471 maybe_free (info_win->points);
474 free (info_win);
476 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
477 consistency_check_info_windows ();
478 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
481 /* Set WINDOW to show NODE. Remember the new window in our list of Info
482 windows. If we are doing automatic footnote display, also try to display
483 the footnotes for this window. If REMEMBER is nonzero, first call
484 set_remembered_pagetop_and_point. */
485 void
486 info_set_node_of_window (int remember, WINDOW *window, NODE *node)
488 if (remember)
489 set_remembered_pagetop_and_point (window);
491 /* Put this node into the window. */
492 window_set_node_of_window (window, node);
494 /* Remember this node and window in our list of info windows. */
495 remember_window_and_node (window, node);
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);
504 /* **************************************************************** */
505 /* */
506 /* Info Movement Commands */
507 /* */
508 /* **************************************************************** */
510 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
511 to do so. */
512 void
513 set_window_pagetop (WINDOW *window, int desired_top)
515 int point_line, old_pagetop;
517 if (desired_top < 0)
518 desired_top = 0;
519 else if (desired_top > window->line_count)
520 desired_top = window->line_count - 1;
522 if (window->pagetop == desired_top)
523 return;
525 old_pagetop = window->pagetop;
526 window->pagetop = desired_top;
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;
535 window->flags |= W_UpdateWindow;
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)
544 int start, end, amount;
546 amount = desired_top - old_pagetop;
548 if ((amount >= window->height) ||
549 (((window->height - amount) * 10) < window->height))
550 return;
552 start = amount + window->first_row;
553 end = window->height + window->first_row;
555 display_scroll_display (start, end, -amount);
557 else
559 int start, end, amount;
561 amount = old_pagetop - desired_top;
563 if ((amount >= window->height) ||
564 (((window->height - amount) * 10) < window->height))
565 return;
567 start = window->first_row;
568 end = (window->first_row + window->height) - amount;
569 display_scroll_display (start, end, amount);
573 /* Immediately make WINDOW->point visible on the screen, and move the
574 terminal cursor there. */
575 static void
576 info_show_point (WINDOW *window)
578 int old_pagetop;
580 old_pagetop = window->pagetop;
581 window_adjust_pagetop (window);
582 if (old_pagetop != window->pagetop)
584 int new_pagetop;
586 new_pagetop = window->pagetop;
587 window->pagetop = old_pagetop;
588 set_window_pagetop (window, new_pagetop);
591 if (window->flags & W_UpdateWindow)
592 display_update_one_window (window);
594 display_cursor_at_point (window);
597 /* Move WINDOW->point from OLD line index to NEW line index. */
598 static void
599 move_to_new_line (int old, int new, WINDOW *window)
601 if (old == -1)
603 info_error ((char *) msg_cant_find_point, NULL, NULL);
605 else
607 int goal;
609 if (new >= window->line_count || new < 0)
610 return;
612 goal = window_get_goal_column (window);
613 window->goal_column = goal;
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);
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"))
624 int old_line, new_line;
626 if (count < 0)
627 info_prev_line (window, -count, key);
628 else
630 old_line = window_line_of_point (window);
631 new_line = old_line + count;
632 move_to_new_line (old_line, new_line, window);
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"))
639 int old_line, new_line;
641 if (count < 0)
642 info_next_line (window, -count, key);
643 else
645 old_line = window_line_of_point (window);
646 new_line = old_line - count;
647 move_to_new_line (old_line, new_line, window);
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"))
654 register int point, len;
655 register char *buffer;
657 buffer = window->node->contents;
658 len = window->node->nodelen;
660 for (point = window->point;
661 (point < len) && (buffer[point] != '\n');
662 point++);
664 if (point != window->point)
666 window->point = point;
667 info_show_point (window);
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"))
674 register int point;
675 register char *buffer;
677 buffer = window->node->contents;
678 point = window->point;
680 for (; (point) && (buffer[point - 1] != '\n'); point--);
682 /* If at a line start already, do nothing. */
683 if (point != window->point)
685 window->point = point;
686 info_show_point (window);
690 /* Move point forward in the node. */
691 DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
693 if (count < 0)
694 info_backward_char (window, -count, key);
695 else
697 window->point += count;
699 if (window->point >= window->node->nodelen)
700 window->point = window->node->nodelen - 1;
702 info_show_point (window);
706 /* Move point backward in the node. */
707 DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
709 if (count < 0)
710 info_forward_char (window, -count, key);
711 else
713 window->point -= count;
715 if (window->point < 0)
716 window->point = 0;
718 info_show_point (window);
722 #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
724 /* Move forward a word in this node. */
725 DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
727 long point;
728 char *buffer;
729 int end, c;
731 if (count < 0)
733 info_backward_word (window, -count, key);
734 return;
737 point = window->point;
738 buffer = window->node->contents;
739 end = window->node->nodelen;
741 while (count)
743 if (point + 1 >= end)
744 return;
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];
750 if (!alphabetic (c))
752 while (++point < end)
754 c = buffer[point];
755 if (alphabetic (c))
756 break;
760 if (point >= end) return;
762 while (++point < end)
764 c = buffer[point];
765 if (!alphabetic (c))
766 break;
768 --count;
770 window->point = point;
771 info_show_point (window);
774 DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
776 long point;
777 char *buffer;
778 int c;
780 if (count < 0)
782 info_forward_word (window, -count, key);
783 return;
786 buffer = window->node->contents;
787 point = window->point;
789 while (count)
791 if (point == 0)
792 break;
794 /* Like info_forward_word (), except that we look at the
795 characters just before point. */
797 c = buffer[point - 1];
799 if (!alphabetic (c))
801 while (--point)
803 c = buffer[point - 1];
804 if (alphabetic (c))
805 break;
809 while (point)
811 c = buffer[point - 1];
812 if (!alphabetic (c))
813 break;
814 else
815 --point;
817 --count;
819 window->point = point;
820 info_show_point (window);
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:
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. */
833 int info_scroll_behaviour = IS_Continuous;
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
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 */
845 #define INFO_LABEL_FOUND() \
846 (info_parsed_nodename || (info_parsed_filename \
847 && !is_dir_name (info_parsed_filename)))
849 /* Move to 1st menu item, Next, Up/Next, or error in this window. */
850 static void
851 forward_move_node_structure (WINDOW *window, int behaviour)
853 switch (behaviour)
855 case IS_PageOnly:
856 info_error ((char *) msg_at_node_bottom, NULL, NULL);
857 break;
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
865 window_message_in_echo_area ((char *) _("Following Next node..."),
866 NULL, NULL);
867 info_handle_pointer ("Next", window);
869 break;
871 case IS_Continuous:
873 /* First things first. If this node contains a menu, move down
874 into the menu. */
876 REFERENCE **menu;
878 menu = info_menu_of_node (window->node);
880 if (menu)
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;
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 ())
895 window_message_in_echo_area ((char *) _("Selecting Next node..."),
896 NULL, NULL);
897 info_handle_pointer ("Next", window);
898 return;
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. */
905 int up_counter, old_current;
906 INFO_WINDOW *info_win;
908 /* Remember the current node and location. */
909 info_win = get_info_window_of_window (window);
910 old_current = info_win->current;
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)
917 info_up_label_of_node (window->node);
918 if (INFO_LABEL_FOUND ())
920 info_handle_pointer ("Up", window);
921 if (info_error_was_printed)
922 continue;
924 up_counter++;
926 info_next_label_of_node (window->node);
928 /* If no "Next" pointer, keep backing up. */
929 if (!INFO_LABEL_FOUND ())
930 continue;
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)
936 REFERENCE **menu;
937 char *next_nodename;
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);
944 menu = info_menu_of_node (window->node);
945 if (menu &&
946 (strcmp
947 (menu[0]->nodename, next_nodename) == 0))
949 info_free_references (menu);
950 free (next_nodename);
951 continue;
953 else
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);
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);
969 info_handle_pointer ("Next", window);
970 return;
972 else
974 /* No more "Up" pointers. Print an error, and call it
975 quits. */
976 register int i;
978 for (i = 0; i < up_counter; i++)
980 info_win->nodes_index--;
981 free (info_win->nodes[info_win->nodes_index]);
982 info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
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);
995 break;
1000 /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
1001 static void
1002 backward_move_node_structure (WINDOW *window, int behaviour)
1004 switch (behaviour)
1006 case IS_PageOnly:
1007 info_error ((char *) msg_at_node_top, NULL, NULL);
1008 break;
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
1016 window_message_in_echo_area ((char *) _("Moving Prev in this window."),
1017 NULL, NULL);
1018 info_handle_pointer ("Prev", window);
1020 break;
1022 case IS_Continuous:
1023 info_prev_label_of_node (window->node);
1025 if (!info_parsed_nodename && (!info_parsed_filename
1026 || is_dir_name (info_parsed_filename)))
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
1036 window_message_in_echo_area ((char *) _("Moving Up in this window."),
1037 NULL, NULL);
1038 info_handle_pointer ("Up", window);
1041 else
1043 REFERENCE **menu;
1044 int inhibit_menu_traversing = 0;
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)
1052 char *pnode;
1054 pnode = xstrdup (info_parsed_nodename);
1055 info_up_label_of_node (window->node);
1057 if (!info_parsed_filename && info_parsed_nodename &&
1058 strcmp (info_parsed_nodename, pnode) == 0)
1060 /* The nodes are the same. Inhibit moving to the last
1061 menu item. */
1062 free (pnode);
1063 inhibit_menu_traversing = 1;
1065 else
1067 free (pnode);
1068 info_prev_label_of_node (window->node);
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);
1079 if (!inhibit_menu_traversing)
1081 while (!info_error_was_printed &&
1082 (menu = info_menu_of_node (window->node)))
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');
1091 break;
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"))
1099 if (count < 0)
1100 info_global_prev_node (window, -count, key);
1101 else
1103 while (count && !info_error_was_printed)
1105 forward_move_node_structure (window, IS_Continuous);
1106 count--;
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"))
1115 if (count < 0)
1116 info_global_next_node (window, -count, key);
1117 else
1119 while (count && !info_error_was_printed)
1121 backward_move_node_structure (window, IS_Continuous);
1122 count--;
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);
1132 static void
1133 _scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour)
1135 if (count < 0)
1136 _scroll_backward (window, -count, key, behaviour);
1137 else
1139 int desired_top;
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)
1149 desired_top = window->pagetop + (window->height - 2);
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)
1155 forward_move_node_structure (window, behaviour);
1156 return;
1159 else
1160 desired_top = window->pagetop + count;
1162 if (desired_top >= window->line_count)
1163 desired_top = window->line_count - 2;
1165 if (window->pagetop > desired_top)
1166 return;
1167 else
1168 set_window_pagetop (window, desired_top);
1172 static void
1173 _scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour)
1175 if (count < 0)
1176 _scroll_forward (window, -count, key, behaviour);
1177 else
1179 int desired_top;
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)
1188 desired_top = window->pagetop - (window->height - 2);
1190 if ((desired_top < 0) && (window->pagetop == 0))
1192 backward_move_node_structure (window, behaviour);
1193 return;
1196 else
1197 desired_top = window->pagetop - count;
1199 if (desired_top < 0)
1200 desired_top = 0;
1202 set_window_pagetop (window, desired_top);
1206 /* Show the next screen of WINDOW's node. */
1207 DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
1209 _scroll_forward (window, count, key, info_scroll_behaviour);
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"))
1217 if (info_explicit_arg)
1218 default_window_size = count;
1219 _scroll_forward (window, count, key, info_scroll_behaviour);
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"))
1225 _scroll_forward (window, count, key, IS_PageOnly);
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"))
1233 if (info_explicit_arg)
1234 default_window_size = count;
1235 _scroll_forward (window, count, key, IS_PageOnly);
1238 /* Show the previous screen of WINDOW's node. */
1239 DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
1241 _scroll_backward (window, count, key, info_scroll_behaviour);
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"))
1249 if (info_explicit_arg)
1250 default_window_size = count;
1251 _scroll_backward (window, count, key, info_scroll_behaviour);
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"))
1258 _scroll_backward (window, count, key, IS_PageOnly);
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"))
1266 if (info_explicit_arg)
1267 default_window_size = count;
1268 _scroll_backward (window, count, key, IS_PageOnly);
1271 /* Move to the beginning of the node. */
1272 DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1274 window->pagetop = window->point = 0;
1275 window->flags |= W_UpdateWindow;
1278 /* Move to the end of the node. */
1279 DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1281 window->point = window->node->nodelen - 1;
1282 info_show_point (window);
1285 /* Scroll the window forward by N lines. */
1286 DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
1288 if (count < 0)
1289 info_up_line (window, -count, key);
1290 else
1292 int desired_top = window->pagetop + count;
1294 if (desired_top >= window->line_count)
1295 desired_top = window->line_count - 2;
1297 if (window->pagetop <= desired_top)
1298 set_window_pagetop (window, desired_top);
1302 /* Scroll the window backward by N lines. */
1303 DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
1305 if (count < 0)
1306 info_down_line (window, -count, key);
1307 else
1309 int desired_top = window->pagetop - count;
1311 if (desired_top < 0)
1312 desired_top = 0;
1314 set_window_pagetop (window, desired_top);
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"))
1323 if (count < 0)
1324 info_scroll_half_screen_up (window, -count, key);
1325 else
1327 int scroll_size = (the_screen->height + 1) / 2;
1328 int desired_top;
1330 if (info_explicit_arg)
1331 default_scroll_size = count;
1332 if (default_scroll_size > 0)
1333 scroll_size = default_scroll_size;
1335 desired_top = window->pagetop + scroll_size;
1336 if (desired_top >= window->line_count)
1337 desired_top = window->line_count - 2;
1339 if (window->pagetop <= desired_top)
1340 set_window_pagetop (window, desired_top);
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"))
1349 if (count < 0)
1350 info_scroll_half_screen_down (window, -count, key);
1351 else
1353 int scroll_size = (the_screen->height + 1) / 2;
1354 int desired_top;
1356 if (info_explicit_arg)
1357 default_scroll_size = count;
1358 if (default_scroll_size > 0)
1359 scroll_size = default_scroll_size;
1361 desired_top = window->pagetop - scroll_size;
1362 if (desired_top < 0)
1363 desired_top = 0;
1365 set_window_pagetop (window, desired_top);
1369 /* **************************************************************** */
1370 /* */
1371 /* Commands for Manipulating Windows */
1372 /* */
1373 /* **************************************************************** */
1375 /* Make the next window in the chain be the active window. */
1376 DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1378 if (count < 0)
1380 info_prev_window (window, -count, key);
1381 return;
1384 /* If no other window, error now. */
1385 if (!windows->next && !echo_area_is_active)
1387 info_error ((char *) msg_one_window, NULL, NULL);
1388 return;
1391 while (count--)
1393 if (window->next)
1394 window = window->next;
1395 else
1397 if (window == the_echo_area || !echo_area_is_active)
1398 window = windows;
1399 else
1400 window = the_echo_area;
1404 if (active_window != window)
1406 if (auto_footnotes_p)
1407 info_get_or_remove_footnotes (window);
1409 window->flags |= W_UpdateWindow;
1410 active_window = window;
1414 /* Make the previous window in the chain be the active window. */
1415 DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1417 if (count < 0)
1419 info_next_window (window, -count, key);
1420 return;
1423 /* Only one window? */
1425 if (!windows->next && !echo_area_is_active)
1427 info_error ((char *) msg_one_window, NULL, NULL);
1428 return;
1431 while (count--)
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))
1438 register WINDOW *win, *last = NULL;
1440 for (win = windows; win; win = win->next)
1441 last = win;
1443 window = last;
1445 else
1447 if (window == windows)
1448 window = the_echo_area;
1449 else
1450 window = window->prev;
1454 if (active_window != window)
1456 if (auto_footnotes_p)
1457 info_get_or_remove_footnotes (window);
1459 window->flags |= W_UpdateWindow;
1460 active_window = window;
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"))
1468 WINDOW *split, *old_active;
1469 int pagetop;
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;
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;
1481 if (!split)
1483 info_error ((char *) msg_win_too_small, NULL, NULL);
1485 else
1487 #if defined (SPLIT_BEFORE_ACTIVE)
1488 /* Try to scroll the old window into its new postion. */
1489 if (pagetop == window->pagetop)
1491 int start, end, amount;
1493 start = split->first_row;
1494 end = start + window->height;
1495 amount = split->height + 1;
1496 display_scroll_display (start, end, amount);
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 */
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))
1507 register int i, j;
1508 INFO_WINDOW *iw;
1509 NODE *node = (NODE *)NULL;
1510 char *filename;
1512 for (i = 0; (iw = info_windows[i]); i++)
1514 for (j = 0; j < iw->nodes_index; j++)
1515 if (!internal_info_node_p (iw->nodes[j]))
1517 if (iw->nodes[j]->parent)
1518 filename = iw->nodes[j]->parent;
1519 else
1520 filename = iw->nodes[j]->filename;
1522 node = info_get_node (filename, iw->nodes[j]->nodename);
1523 if (node)
1525 window_set_node_of_window (split, node);
1526 i = info_windows_index - 1;
1527 break;
1532 split->pagetop = window->pagetop;
1534 if (auto_tiling_p)
1535 window_tile_windows (DONT_TILE_INTERNALS);
1536 else
1537 window_adjust_pagetop (split);
1539 remember_window_and_node (split, split->node);
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"))
1549 if (!windows->next)
1551 info_error ((char *) msg_cant_kill_last, NULL, NULL);
1553 else if (window->flags & W_WindowIsPerm)
1555 info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL);
1557 else
1559 info_delete_window_internal (window);
1561 if (auto_footnotes_p)
1562 info_get_or_remove_footnotes (active_window);
1564 if (auto_tiling_p)
1565 window_tile_windows (DONT_TILE_INTERNALS);
1569 /* Do the physical deletion of WINDOW, and forget this window and
1570 associated nodes. */
1571 void
1572 info_delete_window_internal (WINDOW *window)
1574 if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
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);
1581 if (echo_area_is_active)
1582 echo_area_inform_of_deleted_window (window);
1586 /* Just keep WINDOW, deleting all others. */
1587 DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1589 int num_deleted; /* The number of windows we deleted. */
1590 int pagetop, start, end;
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;
1598 num_deleted = 0;
1600 while (1)
1602 WINDOW *win;
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;
1611 if (!win)
1612 break;
1614 info_delete_window_internal (win);
1615 num_deleted++;
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)
1622 int amount;
1624 amount = (window->first_row - start);
1625 amount -= (window->pagetop - pagetop);
1626 display_scroll_display (start, end, amount);
1629 window->flags |= W_UpdateWindow;
1632 /* Scroll the "other" window of WINDOW. */
1633 DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1635 WINDOW *other;
1637 /* If only one window, give up. */
1638 if (!windows->next)
1640 info_error ((char *) msg_one_window, NULL, NULL);
1641 return;
1644 other = window->next;
1646 if (!other)
1647 other = window->prev;
1649 info_scroll_forward (other, count, key);
1652 /* Scroll the "other" window of WINDOW. */
1653 DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
1654 _("Scroll the other window backward"))
1656 info_scroll_other_window (window, -count, key);
1659 /* Change the size of WINDOW by AMOUNT. */
1660 DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1662 window_change_window_height (window, count);
1665 /* When non-zero, tiling takes place automatically when info_split_window
1666 is called. */
1667 int auto_tiling_p = 0;
1669 /* Tile all of the visible windows. */
1670 DECLARE_INFO_COMMAND (info_tile_windows,
1671 _("Divide the available screen space among the visible windows"))
1673 window_tile_windows (TILE_INTERNALS);
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"))
1680 window_toggle_wrap (window);
1683 /* **************************************************************** */
1684 /* */
1685 /* Info Node Commands */
1686 /* */
1687 /* **************************************************************** */
1689 /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
1690 filename is not set. */
1691 char *
1692 node_printed_rep (NODE *node)
1694 char *rep;
1696 if (node->filename)
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);
1703 else
1704 rep = node->nodename;
1706 return rep;
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
1713 info_select_reference (WINDOW *window, REFERENCE *entry)
1715 NODE *node;
1716 char *filename, *nodename, *file_system_error;
1718 file_system_error = (char *)NULL;
1720 filename = entry->filename;
1721 if (!filename)
1722 filename = window->node->parent;
1723 if (!filename)
1724 filename = window->node->filename;
1726 if (filename)
1727 filename = xstrdup (filename);
1729 if (entry->nodename)
1730 nodename = xstrdup (entry->nodename);
1731 else
1732 nodename = xstrdup ("Top");
1734 node = info_get_node (filename, nodename);
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)
1741 if (info_recent_file_error)
1742 file_system_error = xstrdup (info_recent_file_error);
1744 if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
1746 node = info_get_node (entry->label, "Top");
1747 if (!node && info_recent_file_error)
1749 maybe_free (file_system_error);
1750 file_system_error = xstrdup (info_recent_file_error);
1755 if (!node)
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);
1763 maybe_free (file_system_error);
1764 maybe_free (filename);
1765 maybe_free (nodename);
1767 if (node)
1768 info_set_node_of_window (1, window, node);
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
1775 info_parse_and_select (char *line, WINDOW *window)
1777 REFERENCE entry;
1779 info_parse_node (line, DONT_SKIP_NEWLINES);
1781 entry.nodename = info_parsed_nodename;
1782 entry.filename = info_parsed_filename;
1783 entry.label = "*info-parse-and-select*";
1785 info_select_reference (window, &entry);
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
1793 info_handle_pointer (char *label, WINDOW *window)
1795 if (info_parsed_filename || info_parsed_nodename)
1797 char *filename, *nodename;
1798 NODE *node;
1800 filename = nodename = (char *)NULL;
1802 if (info_parsed_filename)
1803 filename = xstrdup (info_parsed_filename);
1804 else
1806 if (window->node->parent)
1807 filename = xstrdup (window->node->parent);
1808 else if (window->node->filename)
1809 filename = xstrdup (window->node->filename);
1812 if (info_parsed_nodename)
1813 nodename = xstrdup (info_parsed_nodename);
1814 else
1815 nodename = xstrdup ("Top");
1817 node = info_get_node (filename, nodename);
1819 if (node)
1821 INFO_WINDOW *info_win;
1823 info_win = get_info_window_of_window (window);
1824 if (info_win)
1826 info_win->pagetops[info_win->current] = window->pagetop;
1827 info_win->points[info_win->current] = window->point;
1829 info_set_node_of_window (1, window, node);
1831 else
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);
1839 free (filename);
1840 free (nodename);
1842 else
1844 info_error ((char *) msg_no_pointer, label, NULL);
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"))
1852 info_next_label_of_node (window->node);
1853 info_handle_pointer ("Next", window);
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"))
1860 info_prev_label_of_node (window->node);
1861 info_handle_pointer ("Prev", window);
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"))
1868 info_up_label_of_node (window->node);
1869 info_handle_pointer ("Up", window);
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"))
1875 register int i;
1876 FILE_BUFFER *fb = file_buffer_of_window (window);
1877 NODE *node = (NODE *)NULL;
1879 if (fb && fb->tags)
1881 int last_node_tag_idx = -1;
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 */
1890 count--;
1891 last_node_tag_idx = i;
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);
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);
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"))
1908 FILE_BUFFER *fb = file_buffer_of_window (window);
1909 NODE *node = (NODE *)NULL;
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)
1917 register int i;
1918 int last_node_tag_idx = -1;
1920 for (i = 0; count && fb->tags[i]; i++)
1921 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1923 count--;
1924 last_node_tag_idx = i;
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);
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);
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"))
1942 info_menu_digit (window, 1, '0');
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"))
1948 register int i, item;
1949 register REFERENCE **menu;
1951 menu = info_menu_of_node (window->node);
1953 if (!menu)
1955 info_error ((char *) msg_no_menu_node, NULL, NULL);
1956 return;
1959 /* We have the menu. See if there are this many items in it. */
1960 item = key - '0';
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
1967 for (i = 0; menu[i]; i++)
1968 if (i == item - 1)
1969 break;
1972 if (menu[i])
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);
1978 else
1979 info_error ((char *) _("There aren't %d items in this menu."),
1980 (void *)((intptr_t)item), NULL);
1982 info_free_references (menu);
1983 return;
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.
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.
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). */
2003 static REFERENCE **
2004 nearest_xref (REFERENCE **xref_list, long int pos)
2006 int this_xref;
2007 int nearest = -1;
2008 long best_delta = -1;
2010 for (this_xref = 0; xref_list[this_xref]; this_xref++)
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;
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 - strlen (INFO_XREF_LABEL))),
2024 labs (pos - xref->end));
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)
2032 nearest = this_xref;
2033 best_delta = delta;
2037 /* Maybe there was no list to search through. */
2038 if (nearest < 0)
2039 return NULL;
2041 /* Ok, we have a nearest xref, make a list of it. */
2043 REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2);
2044 ret[0] = info_copy_reference (xref_list[nearest]);
2045 ret[1] = NULL;
2046 return ret;
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
2057 info_menu_or_ref_item (WINDOW *window, int count,
2058 unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p)
2060 char *line;
2061 REFERENCE *entry;
2062 REFERENCE *defentry = NULL;
2063 REFERENCE **menu = (*builder) (window->node);
2065 if (!menu)
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;
2074 /* Default the selected reference to the one which is on the line that
2075 point is in. */
2077 REFERENCE **refs = NULL;
2078 int point_line = window_line_of_point (window);
2080 if (point_line != -1)
2082 SEARCH_BINDING binding;
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;
2092 if (builder == info_menu_of_node)
2094 if (point_line)
2096 binding.start--;
2097 refs = info_menu_items (&binding);
2100 else
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);
2110 if (refs && refs[0])
2112 if (strcmp (refs[0]->label, "Menu") != 0
2113 || builder == info_xrefs_of_node)
2115 int which = 0;
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])
2124 int closest = -1;
2126 for (; refs[which]; which++)
2128 if (window->point >= refs[which]->start
2129 && window->point <= refs[which]->end)
2131 closest = which;
2132 break;
2134 else if (window->point < refs[which]->start)
2135 break;
2137 if (which > 0)
2139 if (closest == -1)
2140 which--;
2141 else
2142 which = closest;
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;
2152 if (defentry->filename)
2153 defentry->filename = xstrdup (defentry->filename);
2154 if (defentry->nodename)
2155 defentry->nodename = xstrdup (defentry->nodename);
2157 info_free_references (refs);
2162 /* If we are going to ask the user a question, do it now. */
2163 if (ask_p)
2165 char *prompt;
2167 /* Build the prompt string. */
2168 if (builder == info_menu_of_node)
2170 if (defentry)
2172 prompt = xmalloc (strlen (defentry->label)
2173 + strlen (_("Menu item (%s): ")));
2174 sprintf (prompt, _("Menu item (%s): "), defentry->label);
2176 else
2177 prompt = xstrdup (_("Menu item: "));
2179 else
2181 if (defentry)
2183 prompt = xmalloc (strlen (defentry->label)
2184 + strlen (_("Follow xref (%s): ")));
2185 sprintf (prompt, _("Follow xref (%s): "), defentry->label);
2187 else
2188 prompt = xstrdup (_("Follow xref: "));
2191 line = info_read_completing_in_echo_area (window, prompt, menu);
2192 free (prompt);
2194 window = active_window;
2196 /* User aborts, just quit. */
2197 if (!line)
2199 maybe_free (defentry);
2200 info_free_references (menu);
2201 info_abort_key (window, 0, 0);
2202 return;
2205 /* If we had a default and the user accepted it, use that. */
2206 if (!*line)
2208 free (line);
2209 if (defentry)
2210 line = xstrdup (defentry->label);
2211 else
2212 line = (char *)NULL;
2215 else
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);
2225 if (line)
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. */
2238 register int i;
2239 int best = -1, min_dist = window->node->nodelen;
2240 REFERENCE *ref;
2242 for (i = 0; menu && (ref = menu[i]); i++)
2244 /* Need to use strcasecmp because LINE is downcased
2245 inside info_read_completing_in_echo_area. */
2246 if (strcasecmp (line, ref->label) == 0)
2248 /* ref->end is more accurate estimate of position
2249 for menus than ref->start. Go figure. */
2250 int dist = abs (window->point - ref->end);
2252 if (dist < min_dist)
2254 min_dist = dist;
2255 best = i;
2259 if (best != -1)
2260 entry = menu[best];
2261 else
2262 entry = (REFERENCE *)NULL;
2265 if (!entry && defentry)
2266 info_error ((char *) _("The reference disappeared! (%s)."), line, NULL);
2267 else
2269 NODE *orig = window->node;
2270 info_select_reference (window, entry);
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;
2278 if (window->line_count > 0)
2279 start = window->line_starts[1] - window->node->contents;
2280 else
2281 start = 0;
2283 offset =
2284 info_target_search_node (window->node, entry->label, start);
2286 if (offset != -1)
2288 window->point = offset;
2289 window_adjust_pagetop (window);
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);
2299 free (line);
2300 if (defentry)
2302 free (defentry->label);
2303 maybe_free (defentry->filename);
2304 maybe_free (defentry->nodename);
2305 free (defentry);
2309 info_free_references (menu);
2311 if (!info_error_was_printed)
2312 window_clear_echo_area ();
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"))
2319 info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
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"))
2327 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
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"))
2333 SEARCH_BINDING binding;
2334 long position;
2336 binding.buffer = window->node->contents;
2337 binding.start = 0;
2338 binding.end = window->node->nodelen;
2339 binding.flags = S_FoldCase | S_SkipDest;
2341 position = search (INFO_MENU_LABEL, &binding);
2343 if (position == -1)
2344 info_error ((char *) msg_no_menu_node, NULL, NULL);
2345 else
2347 window->point = position;
2348 window_adjust_pagetop (window);
2349 window->flags |= W_UpdateWindow;
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"))
2357 register int i;
2358 REFERENCE *entry, **menu;
2360 menu = info_menu_of_node (window->node);
2362 if (!menu)
2363 info_error ((char *) msg_no_menu_node, NULL, NULL);
2365 for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2367 WINDOW *new;
2369 new = window_make_window (window->node);
2370 window_tile_windows (TILE_INTERNALS);
2372 if (!new)
2373 info_error ((char *) msg_win_too_small, NULL, NULL);
2374 else
2376 active_window = new;
2377 info_select_reference (new, entry);
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"))
2385 char *line;
2387 #define GOTO_COMPLETES
2388 #if defined (GOTO_COMPLETES)
2389 /* Build a completion list of all of the known nodes. */
2391 register int fbi, i;
2392 FILE_BUFFER *current;
2393 REFERENCE **items = (REFERENCE **)NULL;
2394 int items_index = 0;
2395 int items_slots = 0;
2397 current = file_buffer_of_window (window);
2399 for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2401 FILE_BUFFER *fb;
2402 REFERENCE *entry;
2403 int this_is_the_current_fb;
2405 fb = info_loaded_files[fbi];
2406 this_is_the_current_fb = (current == fb);
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);
2413 add_pointer_to_array
2414 (entry, items_index, items, items_slots, 10, REFERENCE *);
2416 if (fb->tags)
2418 for (i = 0; fb->tags[i]; i++)
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
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);
2433 add_pointer_to_array
2434 (entry, items_index, items, items_slots, 100, REFERENCE *);
2438 line = info_read_maybe_completing (window, (char *) _("Goto node: "),
2439 items);
2440 info_free_references (items);
2442 #else /* !GOTO_COMPLETES */
2443 line = info_read_in_echo_area (window, (char *) _("Goto node: "));
2444 #endif /* !GOTO_COMPLETES */
2446 /* If the user aborted, quit now. */
2447 if (!line)
2449 info_abort_key (window, 0, 0);
2450 return;
2453 canonicalize_whitespace (line);
2455 if (*line)
2456 info_parse_and_select (line, window);
2458 free (line);
2459 if (!info_error_was_printed)
2460 window_clear_echo_area ();
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. */
2470 NODE *
2471 info_follow_menus (NODE *initial_node, char **menus,
2472 const char **errstr, char **errarg1, char **errarg2)
2474 NODE *node = NULL;
2475 *errstr = *errarg1 = *errarg2 = NULL;
2477 for (; *menus; menus++)
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. */
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;
2493 /* Build and return a list of the menu items in this node. */
2494 menu = info_menu_of_node (initial_node);
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)
2501 if (arg == first_arg)
2503 node = make_manpage_node (first_arg);
2504 if (node)
2505 goto maybe_got_node;
2507 *errstr = _("No menu in node `%s'.");
2508 *errarg1 = node_printed_rep (initial_node);
2509 return initial_node;
2512 /* Find the specified menu item. */
2513 entry = info_get_labeled_reference (arg, menu);
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)
2519 int i;
2520 int best_guess = -1;
2522 for (i = 0; (entry = menu[i]); i++)
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;
2532 if (!entry && best_guess != -1)
2533 entry = menu[best_guess];
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)
2540 if (arg == first_arg)
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;
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;
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);
2565 /* Try to find this node. */
2566 node = info_get_node (entry->filename, entry->nodename);
2567 if (!node && arg == first_arg)
2569 node = make_manpage_node (first_arg);
2570 if (node)
2571 goto maybe_got_node;
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");
2580 maybe_got_node:
2581 if (!node)
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;
2590 info_free_references (menu);
2592 /* Success. Go round the loop again. */
2593 free (initial_node);
2594 initial_node = node;
2597 return initial_node;
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. */
2605 static char **
2606 split_list_of_nodenames (char *str)
2608 unsigned len = 2;
2609 char **nodes = xmalloc (len * sizeof (char *));
2611 nodes[len - 2] = str;
2613 while (*str++)
2615 if (*str == ',')
2617 *str++ = 0; /* get past the null byte */
2618 len++;
2619 nodes = xrealloc (nodes, len * sizeof (char *));
2620 nodes[len - 2] = str;
2624 nodes[len - 1] = NULL;
2626 return nodes;
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"))
2635 char *line = info_read_in_echo_area (window, (char *) _("Follow menus: "));
2637 /* If the user aborted, quit now. */
2638 if (!line)
2640 info_abort_key (window, 0, 0);
2641 return;
2644 canonicalize_whitespace (line);
2646 if (*line)
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;
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)
2658 char *file_name = window->node->parent;
2660 if (!file_name)
2661 file_name = window->node->filename;
2662 dir_node = info_get_node (file_name, NULL);
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);
2672 free (nodes);
2673 if (!errstr)
2674 info_set_node_of_window (1, window, node);
2675 else
2676 info_error ((char *) errstr, errarg1, errarg2);
2679 free (line);
2680 if (!info_error_was_printed)
2681 window_clear_echo_area ();
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 *
2689 entry_in_menu (char *arg, REFERENCE **menu, int exact)
2691 REFERENCE *entry;
2693 /* First, try to find the specified menu item verbatim. */
2694 entry = info_get_labeled_reference (arg, menu);
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)
2700 int i;
2701 int best_guess = -1;
2703 for (i = 0; (entry = menu[i]); i++)
2705 if (strcasecmp (entry->label, arg) == 0)
2706 break;
2707 else
2708 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2709 best_guess = i;
2712 if (!entry && best_guess != -1)
2713 entry = menu[best_guess];
2716 return entry;
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
2723 info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program)
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
2748 NODE *node = NULL;
2749 REFERENCE **menu;
2750 const char **try_node;
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)
2758 REFERENCE *entry = NULL;
2760 /* Build and return a list of the menu items in this node. */
2761 menu = info_menu_of_node (initial_node);
2763 /* If no menu item in this node, stop here. Perhaps this node
2764 is the one they need. */
2765 if (!menu)
2766 break;
2768 /* Look for node names typical for usage nodes in this menu. */
2769 for (try_node = invocation_nodes; *try_node; try_node++)
2771 char *nodename;
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;
2784 if (!entry)
2785 break;
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;
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 ();
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 *
2807 program_name_from_file_name (char *file_name)
2809 int i;
2810 char *program_name = xstrdup (filename_non_directory (file_name));
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 */
2821 program_name[i] = 0;
2822 break;
2824 return program_name;
2827 DECLARE_INFO_COMMAND (info_goto_invocation_node,
2828 _("Find the node describing program invocation"))
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;
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);
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)
2848 info_abort_key (window, 0, 0);
2849 return;
2851 if (*line)
2852 program_name = line;
2853 else
2854 program_name = default_program_name;
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);
2862 info_intuit_options_node (window, top_node, program_name);
2863 free (line);
2864 free (default_program_name);
2867 #if defined (HANDLE_MAN_PAGES)
2868 DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
2870 char *line;
2872 line = info_read_in_echo_area (window, (char *) _("Get Manpage: "));
2874 if (!line)
2876 info_abort_key (window, 0, 0);
2877 return;
2880 canonicalize_whitespace (line);
2882 if (*line)
2884 char *goto_command;
2886 goto_command = (char *)xmalloc
2887 (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
2889 sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
2891 info_parse_and_select (goto_command, window);
2892 free (goto_command);
2895 free (line);
2896 if (!info_error_was_printed)
2897 window_clear_echo_area ();
2899 #endif /* HANDLE_MAN_PAGES */
2901 /* Move to the "Top" node in this file. */
2902 DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
2904 info_parse_and_select ("Top", window);
2907 /* Move to the node "(dir)Top". */
2908 DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
2910 info_parse_and_select ("(dir)Top", window);
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 *
2917 read_nodename_to_kill (WINDOW *window)
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));
2927 sprintf (prompt, _("Kill node (%s): "), default_nodename);
2929 for (iw = 0; (info_win = info_windows[iw]); iw++)
2931 REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2932 entry->label = xstrdup (info_win->window->node->nodename);
2933 entry->filename = entry->nodename = (char *)NULL;
2935 add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
2936 REFERENCE *);
2939 nodename = info_read_completing_in_echo_area (window, prompt, menu);
2940 free (prompt);
2941 info_free_references (menu);
2942 if (nodename && !*nodename)
2944 free (nodename);
2945 nodename = default_nodename;
2947 else
2948 free (default_nodename);
2950 return nodename;
2954 /* Delete NODENAME from this window, showing the most
2955 recently selected node in this window. */
2956 static void
2957 kill_node (WINDOW *window, char *nodename)
2959 int iw, i;
2960 INFO_WINDOW *info_win;
2961 NODE *temp;
2963 /* If there is no nodename to kill, quit now. */
2964 if (!nodename)
2966 info_abort_key (window, 0, 0);
2967 return;
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;
2976 if (!info_win)
2978 if (*nodename)
2979 info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL);
2980 else
2981 window_clear_echo_area ();
2983 return;
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)
2989 info_error ((char *) _("Cannot kill the last node"), NULL, NULL);
2990 return;
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];
2998 /* There is one less node in this window's history list. */
2999 info_win->nodes_index--;
3001 /* Make this window show the most recent history node. */
3002 info_win->current = info_win->nodes_index - 1;
3004 /* If there aren't any nodes left in this window, steal one from the
3005 next window. */
3006 if (info_win->current < 0)
3008 INFO_WINDOW *stealer;
3009 int which, pagetop;
3010 long point;
3012 if (info_windows[iw + 1])
3013 stealer = info_windows[iw + 1];
3014 else
3015 stealer = info_windows[0];
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;
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;
3027 /* Else, just use the node appearing in STEALER's window. */
3028 else
3029 which = stealer->current;
3031 /* Copy this node. */
3033 NODE *copy = xmalloc (sizeof (NODE));
3035 temp = stealer->nodes[which];
3036 point = stealer->points[which];
3037 pagetop = stealer->pagetops[which];
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;
3047 temp = copy;
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);
3055 else
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);
3062 if (!info_error_was_printed)
3063 window_clear_echo_area ();
3065 if (auto_footnotes_p)
3066 info_get_or_remove_footnotes (window);
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"))
3077 kill_node (window, active_window->node->nodename);
3080 /* Kill named node. */
3081 DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
3083 char *nodename = read_nodename_to_kill (window);
3084 kill_node (window, nodename);
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"))
3091 char *line;
3093 line = info_read_in_echo_area (window, (char *) _("Find file: "));
3094 if (!line)
3096 info_abort_key (active_window, 1, 0);
3097 return;
3100 if (*line)
3102 NODE *node;
3104 node = info_get_node (line, "*");
3105 if (!node)
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);
3112 else
3113 info_set_node_of_window (1, window, node);
3115 free (line);
3118 if (!info_error_was_printed)
3119 window_clear_echo_area ();
3122 /* **************************************************************** */
3123 /* */
3124 /* Dumping and Printing Nodes */
3125 /* */
3126 /* **************************************************************** */
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);
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
3138 dump_nodes_to_file (char *filename, char **nodenames,
3139 char *output_filename, int dump_subnodes)
3141 register int i;
3142 FILE *output_stream;
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");
3151 if (!output_stream)
3153 info_error ((char *) _("Could not create output file `%s'."),
3154 output_filename, NULL);
3155 return;
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);
3163 if (output_stream != stdout)
3164 fclose (output_stream);
3166 #if defined (VERBOSE_NODE_DUMPING)
3167 info_error ((char *) _("Done."), NULL, NULL);
3168 #endif /* VERBOSE_NODE_DUMPING */
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;
3176 static void
3177 initialize_dumping (void)
3179 dumped_already_index = 0;
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
3186 dump_node_to_stream (char *filename, char *nodename,
3187 FILE *stream, int dump_subnodes)
3189 register int i;
3190 NODE *node;
3192 node = info_get_node (filename, nodename);
3194 if (!node)
3196 if (info_recent_file_error)
3197 info_error (info_recent_file_error, NULL, NULL);
3198 else
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);
3207 return;
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)
3214 free (node);
3215 return;
3217 add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
3218 dumped_already_slots, 50, char *);
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 */
3225 write_node_to_stream (node, stream);
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)
3231 REFERENCE **menu = (REFERENCE **)NULL;
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);
3237 if (menu)
3239 for (i = 0; menu[i]; i++)
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);
3247 info_free_references (menu);
3251 free (node);
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
3257 dump_node_to_file (NODE *node, char *filename, int dump_subnodes)
3259 FILE *output_stream;
3260 char *nodes_filename;
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");
3269 if (!output_stream)
3271 info_error ((char *) _("Could not create output file `%s'."), filename,
3272 NULL);
3273 return;
3276 if (node->parent)
3277 nodes_filename = node->parent;
3278 else
3279 nodes_filename = node->filename;
3281 initialize_dumping ();
3282 dump_node_to_stream
3283 (nodes_filename, node->nodename, output_stream, dump_subnodes);
3285 if (output_stream != stdout)
3286 fclose (output_stream);
3288 #if defined (VERBOSE_NODE_DUMPING)
3289 info_error ((char *) _("Done."), NULL, NULL);
3290 #endif /* VERBOSE_NODE_DUMPING */
3293 #if !defined (DEFAULT_INFO_PRINT_COMMAND)
3294 # define DEFAULT_INFO_PRINT_COMMAND "lpr"
3295 #endif /* !DEFAULT_INFO_PRINT_COMMAND */
3297 DECLARE_INFO_COMMAND (info_print_node,
3298 _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
3300 print_node (window->node);
3303 /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3304 void
3305 print_node (NODE *node)
3307 FILE *printer_pipe;
3308 char *print_command = getenv ("INFO_PRINT_COMMAND");
3309 int piping = 0;
3311 if (!print_command || !*print_command)
3312 print_command = DEFAULT_INFO_PRINT_COMMAND;
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.
3318 FIXME: if we are to support Mac-style text files, we might need
3319 to convert the text here. */
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
3327 printer_pipe = popen (print_command, "w");
3328 piping = 1;
3331 if (!printer_pipe)
3333 info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL);
3334 return;
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 */
3342 write_node_to_stream (node, printer_pipe);
3343 if (piping)
3344 pclose (printer_pipe);
3345 else
3346 fclose (printer_pipe);
3348 #if defined (VERBOSE_NODE_DUMPING)
3349 info_error ((char *) _("Done."), NULL, NULL);
3350 #endif /* VERBOSE_NODE_DUMPING */
3353 static void
3354 write_node_to_stream (NODE *node, FILE *stream)
3356 fwrite (node->contents, 1, node->nodelen, stream);
3359 /* **************************************************************** */
3360 /* */
3361 /* Info Searching Commands */
3362 /* */
3363 /* **************************************************************** */
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;
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);
3375 static char *search_string = (char *)NULL;
3376 static int search_string_size = 0;
3377 static int isearch_is_active = 0;
3379 static int last_search_direction = 0;
3380 static int last_search_case_sensitive = 0;
3382 /* Return the file buffer which belongs to WINDOW's node. */
3383 FILE_BUFFER *
3384 file_buffer_of_window (WINDOW *window)
3386 /* If this window has no node, then it has no file buffer. */
3387 if (!window->node)
3388 return ((FILE_BUFFER *)NULL);
3390 if (window->node->parent)
3391 return (info_find_file (window->node->parent));
3393 if (window->node->filename)
3394 return (info_find_file (window->node->filename));
3396 return ((FILE_BUFFER *)NULL);
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
3406 info_search_in_node (char *string, NODE *node, long int start,
3407 WINDOW *window, int dir, int case_sensitive)
3409 SEARCH_BINDING binding;
3410 long offset;
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;
3419 if (dir < 0)
3421 binding.end = 0;
3422 binding.flags |= S_SkipDest;
3425 if (binding.start < 0)
3426 return (-1);
3428 /* For incremental searches, we always wish to skip past the string. */
3429 if (isearch_is_active)
3430 binding.flags |= S_SkipDest;
3432 offset = search (string, &binding);
3434 if (offset != -1 && window)
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);
3442 return (offset);
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
3449 info_target_search_node (NODE *node, char *string, long int start)
3451 register int i;
3452 long offset = 0;
3453 char *target;
3455 target = xstrdup (string);
3456 i = strlen (target);
3458 /* Try repeatedly searching for this string while removing words from
3459 the end of it. */
3460 while (i)
3462 target[i] = '\0';
3463 offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
3465 if (offset != -1)
3466 break;
3468 /* Delete the last word from TARGET. */
3469 for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
3471 free (target);
3472 return (offset);
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
3481 info_search_internal (char *string, WINDOW *window,
3482 int dir, int case_sensitive)
3484 register int i;
3485 FILE_BUFFER *file_buffer;
3486 char *initial_nodename;
3487 long ret, start = 0;
3489 file_buffer = file_buffer_of_window (window);
3490 initial_nodename = window->node->nodename;
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);
3501 if (ret != -1)
3503 /* We won! */
3504 if (!echo_area_is_active && !isearch_is_active)
3505 window_clear_echo_area ();
3506 return (0);
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);
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)
3519 register int current_tag = 0, number_of_tags;
3520 char *last_subfile;
3521 TAG *tag;
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)
3528 current_tag = i;
3529 last_subfile = file_buffer->tags[i]->filename;
3532 number_of_tags = i;
3534 /* If there is no last_subfile, our tag wasn't found. */
3535 if (!last_subfile)
3536 return (-1);
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)
3543 NODE *node;
3545 /* Allow C-g to quit the search, failing it if pressed. */
3546 return_if_control_g (-1);
3548 /* Find the next tag that isn't an anchor. */
3549 for (i = current_tag + dir; i != current_tag; i += dir)
3551 if (i < 0)
3552 i = number_of_tags - 1;
3553 else if (i == number_of_tags)
3554 i = 0;
3556 tag = file_buffer->tags[i];
3557 if (tag->nodelen != 0)
3558 break;
3561 /* If we got past out starting point, bail out. */
3562 if (i == current_tag)
3563 return (-1);
3564 current_tag = i;
3566 if (!echo_area_is_active && (last_subfile != tag->filename))
3568 window_message_in_echo_area
3569 ((char *) _("Searching subfile %s ..."),
3570 filename_non_directory (tag->filename), NULL);
3572 last_subfile = tag->filename;
3575 node = info_get_node (file_buffer->filename, tag->nodename);
3577 if (!node)
3579 /* If not doing i-search... */
3580 if (!echo_area_is_active)
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);
3589 return (-1);
3592 if (dir < 0)
3593 start = tag->nodelen;
3595 ret =
3596 info_search_in_node (string, node, start, window, dir,
3597 case_sensitive);
3599 /* Did we find the string in this node? */
3600 if (ret != -1)
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);
3609 /* No. Free this node, and make sure that we haven't passed
3610 our starting point. */
3611 free (node);
3613 if (strcmp (initial_nodename, tag->nodename) == 0)
3614 return (-1);
3617 return (-1);
3620 DECLARE_INFO_COMMAND (info_search_case_sensitively,
3621 _("Read a string and search for it case-sensitively"))
3623 last_search_direction = count > 0 ? 1 : -1;
3624 last_search_case_sensitive = 1;
3625 info_search_1 (window, count, key, 1, 1);
3628 DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
3630 last_search_direction = count > 0 ? 1 : -1;
3631 last_search_case_sensitive = 0;
3632 info_search_1 (window, count, key, 0, 1);
3635 DECLARE_INFO_COMMAND (info_search_backward,
3636 _("Read a string and search backward for it"))
3638 last_search_direction = count > 0 ? -1 : 1;
3639 last_search_case_sensitive = 0;
3640 info_search_1 (window, -count, key, 0, 1);
3643 static void
3644 info_search_1 (WINDOW *window, int count, unsigned char key,
3645 int case_sensitive, int ask_for_string)
3647 char *line, *prompt;
3648 int result, old_pagetop;
3649 int direction;
3651 if (count < 0)
3653 direction = -1;
3654 count = -count;
3656 else
3658 direction = 1;
3659 if (count == 0)
3660 count = 1; /* for backward compatibility */
3663 /* Read a string from the user, defaulting the search to SEARCH_STRING. */
3664 if (!search_string)
3666 search_string = (char *)xmalloc (search_string_size = 100);
3667 search_string[0] = '\0';
3670 if (ask_for_string)
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));
3679 sprintf (prompt, _("%s%sfor string [%s]: "),
3680 direction < 0 ? _("Search backward") : _("Search"),
3681 case_sensitive ? _(" case-sensitively ") : _(" "),
3682 search_string);
3684 line = info_read_in_echo_area (window, prompt);
3685 free (prompt);
3687 if (!line)
3689 info_abort_key (window, 0, 0);
3690 return;
3693 if (*line)
3695 if (strlen (line) + 1 > (unsigned int) search_string_size)
3696 search_string = (char *) xrealloc
3697 (search_string, (search_string_size += 50 + strlen (line)));
3699 strcpy (search_string, line);
3700 free (line);
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))
3710 case_sensitive = 1;
3711 break;
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);
3719 if (result != 0 && !info_error_was_printed)
3720 info_error ((char *) _("Search failed."), NULL, NULL);
3721 else if (old_pagetop != active_window->pagetop)
3723 int new_pagetop;
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);
3732 /* Perhaps free the unreferenced file buffers that were searched, but
3733 not retained. */
3734 info_gc_file_buffers ();
3737 DECLARE_INFO_COMMAND (info_search_next,
3738 _("Repeat last search in the same direction"))
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);
3747 DECLARE_INFO_COMMAND (info_search_previous,
3748 _("Repeat last search in the reverse direction"))
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);
3757 /* **************************************************************** */
3758 /* */
3759 /* Incremental Searching */
3760 /* */
3761 /* **************************************************************** */
3763 static void incremental_search (WINDOW *window, int count,
3764 unsigned char ignore);
3766 DECLARE_INFO_COMMAND (isearch_forward,
3767 _("Search interactively for a string as you type it"))
3769 incremental_search (window, count, key);
3772 DECLARE_INFO_COMMAND (isearch_backward,
3773 _("Search interactively for a string as you type it"))
3775 incremental_search (window, -count, key);
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;
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;
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;
3793 /* Push the state of this search. */
3794 static void
3795 push_isearch (WINDOW *window, int search_index, int direction, int failing)
3797 SEARCH_STATE *state;
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;
3805 add_pointer_to_array (state, isearch_states_index, isearch_states,
3806 isearch_states_slots, 20, SEARCH_STATE *);
3809 /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3810 static void
3811 pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing)
3813 SEARCH_STATE *state;
3815 if (isearch_states_index)
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;
3824 free (state);
3825 isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
3829 /* Free the memory used by isearch_states. */
3830 static void
3831 free_isearch_states (void)
3833 register int i;
3835 for (i = 0; i < isearch_states_index; i++)
3837 free (isearch_states[i]);
3838 isearch_states[i] = (SEARCH_STATE *)NULL;
3840 isearch_states_index = 0;
3843 /* Display the current search in the echo area. */
3844 static void
3845 show_isearch_prompt (int dir, unsigned char *string, int failing_p)
3847 register int i;
3848 const char *prefix;
3849 char *prompt, *p_rep;
3850 unsigned int prompt_len, p_rep_index, p_rep_size;
3852 if (dir < 0)
3853 prefix = _("I-search backward: ");
3854 else
3855 prefix = _("I-search: ");
3857 p_rep_index = p_rep_size = 0;
3858 p_rep = (char *)NULL;
3859 for (i = 0; string[i]; i++)
3861 char *rep;
3863 switch (string[i])
3865 case ' ': rep = " "; break;
3866 case LFD: rep = "\\n"; break;
3867 case TAB: rep = "\\t"; break;
3868 default:
3869 rep = pretty_keyname (string[i]);
3871 if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3872 p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3874 strcpy (p_rep + p_rep_index, rep);
3875 p_rep_index += strlen (rep);
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 : "");
3885 window_message_in_echo_area ("%s", prompt, NULL);
3886 maybe_free (p_rep);
3887 free (prompt);
3888 display_cursor_at_point (active_window);
3891 static void
3892 incremental_search (WINDOW *window, int count, unsigned char ignore)
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;
3900 if (count < 0)
3901 dir = -1;
3902 else
3903 dir = 1;
3905 last_search_result = search_result = 0;
3907 window_get_state (window, &orig_state);
3909 isearch_string_index = 0;
3910 if (!isearch_string_size)
3911 isearch_string = (char *)xmalloc (isearch_string_size = 50);
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);
3917 isearch_is_active = 1;
3919 while (isearch_is_active)
3921 VFunction *func = (VFunction *)NULL;
3922 int quoted = 0;
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)
3928 display_update_one_window (window);
3929 display_cursor_at_point (active_window);
3932 /* Read a character and dispatch on it. */
3933 key = info_get_input_char ();
3934 window_get_state (window, &mystate);
3936 if (key == DEL || key == Control ('h'))
3938 /* User wants to delete one level of search? */
3939 if (!isearch_states_index)
3941 terminal_ring_bell ();
3942 continue;
3944 else
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;
3954 else if (key == Control ('q'))
3956 key = info_get_input_char ();
3957 quoted = 1;
3960 /* We are about to search again, or quit. Save the current search. */
3961 push_isearch (window, isearch_string_index, dir, search_result);
3963 if (quoted)
3964 goto insert_and_search;
3966 if (!Meta_p (key) || key > 32)
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 */
3976 if (isprint (key) || (type == ISFUNC && func == NULL))
3978 insert_and_search:
3980 if (isearch_string_index + 2 >= isearch_string_size)
3981 isearch_string = (char *)xrealloc
3982 (isearch_string, isearch_string_size += 100);
3984 isearch_string[isearch_string_index++] = key;
3985 isearch_string[isearch_string_index] = '\0';
3986 goto search_now;
3988 else if (func == (VFunction *) isearch_forward
3989 || func == (VFunction *) isearch_backward)
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))
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)
4003 /* Of course, there must be something to insert. */
4004 if (last_isearch_accepted)
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;
4016 else
4017 continue;
4019 else
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;
4027 else
4029 /* Reverse the direction of the search. */
4030 dir = -dir;
4033 else if (func == (VFunction *) info_abort_key)
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))
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;
4048 else
4049 goto exit_search;
4051 else
4052 goto exit_search;
4054 else
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)
4063 maybe_free (last_isearch_accepted);
4064 last_isearch_accepted = xstrdup (isearch_string);
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);
4080 if (func == (VFunction *) info_abort_key)
4082 if (isearch_states_index)
4083 window_set_state (window, &orig_state);
4086 if (!echo_area_is_active)
4087 window_clear_echo_area ();
4089 if (auto_footnotes_p)
4090 info_get_or_remove_footnotes (active_window);
4092 isearch_is_active = 0;
4093 continue;
4096 /* Search for the contents of isearch_string. */
4097 search_now:
4098 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
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))
4105 case_sensitive = 1;
4106 break;
4110 if (search_result == 0)
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)))
4126 if (dir > 0)
4127 window->point++;
4129 else
4130 search_result = info_search_internal (isearch_string,
4131 window, dir, case_sensitive);
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 ();
4139 after_search:
4140 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4142 if (search_result == 0)
4144 if ((mystate.node == window->node) &&
4145 (mystate.pagetop != window->pagetop))
4147 int newtop = window->pagetop;
4148 window->pagetop = mystate.pagetop;
4149 set_window_pagetop (window, newtop);
4151 display_update_one_window (window);
4152 display_cursor_at_point (window);
4155 last_search_result = search_result;
4158 /* Free the memory used to remember each search state. */
4159 free_isearch_states ();
4161 /* Perhaps GC some file buffers. */
4162 info_gc_file_buffers ();
4164 /* After searching, leave the window in the correct state. */
4165 if (!echo_area_is_active)
4166 window_clear_echo_area ();
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
4174 info_gc_file_buffers (void)
4176 register int fb_index, iw_index, i;
4177 register FILE_BUFFER *fb;
4178 register INFO_WINDOW *iw;
4180 if (!info_loaded_files)
4181 return;
4183 for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4185 int fb_referenced_p = 0;
4187 /* If already gc-ed, do nothing. */
4188 if (!fb->contents)
4189 continue;
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;
4197 /* If this file's contents are not gc-able, move on. */
4198 if (fb->flags & N_CannotGC)
4199 continue;
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++)
4205 for (i = 0; iw->nodes && iw->nodes[i]; i++)
4207 if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4208 (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
4210 fb_referenced_p = 1;
4211 break;
4216 /* If this file buffer wasn't referenced, free its contents. */
4217 if (!fb_referenced_p)
4219 free (fb->contents);
4220 fb->contents = (char *)NULL;
4225 /* **************************************************************** */
4226 /* */
4227 /* Traversing and Selecting References */
4228 /* */
4229 /* **************************************************************** */
4231 /* Move to the next or previous cross reference in this node. */
4232 static void
4233 info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir)
4235 long firstmenu, firstxref;
4236 long nextmenu, nextxref;
4237 long placement = -1;
4238 long start = 0;
4239 NODE *node = window->node;
4241 if (dir < 0)
4242 start = node->nodelen;
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. */
4248 firstmenu = info_search_in_node
4249 (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4251 /* FIRSTMENU may point directly to the line defining the menu. Skip that
4252 and go directly to the first item. */
4254 if (firstmenu != -1)
4256 char *text = node->contents + firstmenu;
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);
4263 firstxref =
4264 info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4266 #if defined (HANDLE_MAN_PAGES)
4267 if ((firstxref == -1) && (node->flags & N_IsManPage))
4269 firstxref = locate_manpage_xref (node, start, dir);
4271 #endif /* HANDLE_MAN_PAGES */
4273 if (firstmenu == -1 && firstxref == -1)
4275 info_error ((char *) msg_no_xref_node, NULL, NULL);
4276 return;
4279 /* There is at least one cross reference or menu entry in this node.
4280 Try hard to find the next available one. */
4282 nextmenu = info_search_in_node
4283 (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4285 nextxref = info_search_in_node
4286 (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
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 */
4293 /* Ignore "Menu:" as a menu item. */
4294 if (nextmenu != -1)
4296 char *text = node->contents + nextmenu;
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);
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)
4308 if (((dir == 1) && (nextmenu < nextxref)) ||
4309 ((dir == -1) && (nextmenu > nextxref)))
4310 placement = nextmenu + 1;
4311 else
4312 placement = nextxref;
4314 else if (nextmenu != -1)
4315 placement = nextmenu + 1;
4316 else if (nextxref != -1)
4317 placement = nextxref;
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)
4323 if (firstmenu != -1 && firstxref != -1)
4325 if (((dir == 1) && (firstmenu < firstxref)) ||
4326 ((dir == -1) && (firstmenu > firstxref)))
4327 placement = firstmenu + 1;
4328 else
4329 placement = firstxref;
4331 else if (firstmenu != -1)
4332 placement = firstmenu + 1;
4333 else
4334 placement = firstxref;
4336 window->point = placement;
4337 window_adjust_pagetop (window);
4338 window->flags |= W_UpdateWindow;
4341 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
4342 _("Move to the previous cross reference"))
4344 if (count < 0)
4345 info_move_to_prev_xref (window, -count, key);
4346 else
4347 info_move_to_xref (window, count, key, -1);
4350 DECLARE_INFO_COMMAND (info_move_to_next_xref,
4351 _("Move to the next cross reference"))
4353 if (count < 0)
4354 info_move_to_next_xref (window, -count, key);
4355 else
4356 info_move_to_xref (window, count, key, 1);
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"))
4363 char *line;
4365 if (window->line_starts)
4366 line = window->line_starts[window_line_of_point (window)];
4367 else
4368 line = "";
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);
4377 /* **************************************************************** */
4378 /* */
4379 /* Miscellaneous Info Commands */
4380 /* */
4381 /* **************************************************************** */
4383 /* What to do when C-g is pressed in a window. */
4384 DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
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);
4393 info_initialize_numeric_arg ();
4394 info_clear_pending_input ();
4395 info_last_executed_command = (VFunction *)NULL;
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"))
4402 int line;
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
4409 if (count < 0)
4410 line = (window->height + count) + window->pagetop;
4411 else
4412 line = window->pagetop + count;
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);
4419 /* If the line is too small, make it fit. */
4420 if (line < window->pagetop)
4421 line = window->pagetop;
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;
4427 window->point = (window->line_starts[line] - window->node->contents);
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"))
4434 if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4436 terminal_clear_screen ();
4437 display_clear_display (the_display);
4438 window_mark_chain (windows, W_UpdateWindow);
4439 display_update_display (windows);
4441 else
4443 int desired_line, point_line;
4444 int new_pagetop;
4446 point_line = window_line_of_point (window) - window->pagetop;
4448 if (count < 0)
4449 desired_line = window->height + count;
4450 else
4451 desired_line = count;
4453 if (desired_line < 0)
4454 desired_line = 0;
4456 if (desired_line >= window->height)
4457 desired_line = window->height - 1;
4459 if (desired_line == point_line)
4460 return;
4462 new_pagetop = window->pagetop + (point_line - desired_line);
4464 set_window_pagetop (window, new_pagetop);
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"))
4473 /* **************************************************************** */
4474 /* */
4475 /* Reading Keys and Dispatching on Them */
4476 /* */
4477 /* **************************************************************** */
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"))
4485 static void
4486 dispatch_error (char *keyseq)
4488 char *rep;
4490 rep = pretty_keyseq (keyseq);
4492 if (!echo_area_is_active)
4493 info_error ((char *) _("Unknown command (%s)."), rep, NULL);
4494 else
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);
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;
4510 /* Initialize the length of the current key sequence. */
4511 void
4512 initialize_keyseq (void)
4514 info_keyseq_index = 0;
4515 info_keyseq_displayed_p = 0;
4518 /* Add CHARACTER to the current key sequence. */
4519 void
4520 add_char_to_keyseq (char character)
4522 if (info_keyseq_index + 2 >= info_keyseq_size)
4523 info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
4525 info_keyseq[info_keyseq_index++] = character;
4526 info_keyseq[info_keyseq_index] = '\0';
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
4533 display_info_keyseq (int expecting_future_input)
4535 char *rep;
4537 rep = pretty_keyseq (info_keyseq);
4538 if (expecting_future_input)
4539 strcat (rep, "-");
4541 if (echo_area_is_active)
4542 inform_in_echo_area (rep);
4543 else
4545 window_message_in_echo_area (rep, NULL, NULL);
4546 display_cursor_at_point (active_window);
4548 info_keyseq_displayed_p = 1;
4551 /* Called by interactive commands to read a keystroke. */
4552 unsigned char
4553 info_get_another_input_char (void)
4555 int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
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)
4562 ready = 1;
4563 if (!info_any_buffered_input_p () &&
4564 !info_input_pending_p ())
4566 #if defined (FD_SET)
4567 struct timeval timer;
4568 fd_set readfds;
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 */
4581 if (!ready)
4582 display_info_keyseq (1);
4584 return (info_get_input_char ());
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
4590 info_dispatch_on_key (unsigned char key, Keymap map)
4592 #if !defined(INFOKEY)
4593 if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
4595 if (map[ESC].type == ISKMAP)
4597 map = (Keymap)map[ESC].function;
4598 add_char_to_keyseq (ESC);
4599 key = UnMeta (key);
4600 info_dispatch_on_key (key, map);
4602 else
4604 dispatch_error (info_keyseq);
4606 return;
4608 #endif /* INFOKEY */
4610 switch (map[key].type)
4612 case ISFUNC:
4614 VFunction *func;
4616 func = InfoFunction(map[key].function);
4617 if (func != (VFunction *)NULL)
4619 /* Special case info_do_lowercase_version (). */
4620 if (func == (VFunction *) info_do_lowercase_version)
4622 #if defined(INFOKEY)
4623 unsigned char lowerkey;
4625 lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
4626 if (lowerkey == key)
4628 add_char_to_keyseq (key);
4629 dispatch_error (info_keyseq);
4630 return;
4632 info_dispatch_on_key (lowerkey, map);
4633 #else /* !INFOKEY */
4634 info_dispatch_on_key (tolower (key), map);
4635 #endif /* INFOKEY */
4636 return;
4639 add_char_to_keyseq (key);
4641 if (info_keyseq_displayed_p)
4642 display_info_keyseq (0);
4645 WINDOW *where;
4647 where = active_window;
4648 (*InfoFunction(map[key].function))
4649 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
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 ())
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);
4664 else
4666 add_char_to_keyseq (key);
4667 dispatch_error (info_keyseq);
4668 return;
4671 break;
4673 case ISKMAP:
4674 add_char_to_keyseq (key);
4675 if (map[key].function != (InfoCommand *)NULL)
4677 unsigned char newkey;
4679 newkey = info_get_another_input_char ();
4680 info_dispatch_on_key (newkey, (Keymap)map[key].function);
4682 else
4684 dispatch_error (info_keyseq);
4685 return;
4687 break;
4691 /* **************************************************************** */
4692 /* */
4693 /* Numeric Arguments */
4694 /* */
4695 /* **************************************************************** */
4697 /* Handle C-u style numeric args, as well as M--, and M-digits. */
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;
4703 /* The sign of the numeric argument. */
4704 int info_numeric_arg_sign = 1;
4706 /* The value of the argument itself. */
4707 int info_numeric_arg = 1;
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"))
4713 info_numeric_arg_digit_loop (window, 0, key);
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"))
4722 info_numeric_arg *= 4;
4723 info_numeric_arg_digit_loop (window, 0, 0);
4726 /* Create a default argument. */
4727 void
4728 info_initialize_numeric_arg (void)
4730 info_numeric_arg = info_numeric_arg_sign = 1;
4731 info_explicit_arg = 0;
4734 DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
4735 _("Internally used by \\[universal-argument]"))
4737 unsigned char pure_key;
4738 Keymap keymap = window->keymap;
4740 while (1)
4742 if (key)
4743 pure_key = key;
4744 else
4746 if (display_was_interrupted_p && !info_any_buffered_input_p ())
4747 display_update_display (windows);
4749 if (active_window != the_echo_area)
4750 display_cursor_at_point (active_window);
4752 pure_key = key = info_get_another_input_char ();
4754 #if !defined(INFOKEY)
4755 if (Meta_p (key))
4756 add_char_to_keyseq (ESC);
4758 add_char_to_keyseq (UnMeta (key));
4759 #else /* defined(INFOKEY) */
4760 add_char_to_keyseq (key);
4761 #endif /* defined(INFOKEY) */
4764 #if !defined(INFOKEY)
4765 if (Meta_p (key))
4766 key = UnMeta (key);
4767 #endif /* !defined(INFOKEY) */
4769 if (keymap[key].type == ISFUNC
4770 && InfoFunction(keymap[key].function)
4771 == (VFunction *) info_universal_argument)
4773 info_numeric_arg *= 4;
4774 key = 0;
4775 continue;
4778 #if defined(INFOKEY)
4779 if (Meta_p (key))
4780 key = UnMeta (key);
4781 #endif /* !defined(INFOKEY) */
4784 if (isdigit (key))
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;
4792 else
4794 if (key == '-' && !info_explicit_arg)
4796 info_numeric_arg_sign = -1;
4797 info_numeric_arg = 1;
4799 else
4801 info_keyseq_index--;
4802 info_dispatch_on_key (pure_key, keymap);
4803 return;
4806 key = 0;
4810 /* **************************************************************** */
4811 /* */
4812 /* Input Character Buffering */
4813 /* */
4814 /* **************************************************************** */
4816 /* Character waiting to be read next. */
4817 static int pending_input_character = 0;
4819 /* How to make there be no pending input. */
4820 static void
4821 info_clear_pending_input (void)
4823 pending_input_character = 0;
4826 /* How to set the pending input character. */
4827 static void
4828 info_set_pending_input (unsigned char key)
4830 pending_input_character = key;
4833 /* How to see if there is any pending input. */
4834 unsigned char
4835 info_input_pending_p (void)
4837 return (pending_input_character);
4840 /* Largest number of characters that we can read in advance. */
4841 #define MAX_INFO_INPUT_BUFFERING 512
4843 static int pop_index = 0, push_index = 0;
4844 static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
4846 /* Add KEY to the buffer of characters to be read. */
4847 static void
4848 info_push_typeahead (unsigned char key)
4850 /* Flush all pending input in the case of C-g pressed. */
4851 if (key == Control ('g'))
4853 push_index = pop_index;
4854 info_set_pending_input (Control ('g'));
4856 else
4858 info_input_buffer[push_index++] = key;
4859 if ((unsigned int) push_index >= sizeof (info_input_buffer))
4860 push_index = 0;
4864 /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4865 static int
4866 info_input_buffer_space_available (void)
4868 if (pop_index > push_index)
4869 return (pop_index - push_index);
4870 else
4871 return (sizeof (info_input_buffer) - (push_index - pop_index));
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
4878 info_get_key_from_typeahead (unsigned char *key)
4880 if (push_index == pop_index)
4881 return (0);
4883 *key = info_input_buffer[pop_index++];
4885 if ((unsigned int) pop_index >= sizeof (info_input_buffer))
4886 pop_index = 0;
4888 return (1);
4892 info_any_buffered_input_p (void)
4894 info_gather_typeahead ();
4895 return (push_index != pop_index);
4898 /* If characters are available to be read, then read them and stuff them into
4899 info_input_buffer. Otherwise, do nothing. */
4900 void
4901 info_gather_typeahead (void)
4903 register int i = 0;
4904 int tty, space_avail;
4905 long chars_avail;
4906 unsigned char input[MAX_INFO_INPUT_BUFFERING];
4908 tty = fileno (info_input_stream);
4909 chars_avail = 0;
4911 space_avail = info_input_buffer_space_available ();
4913 /* If we can just find out how many characters there are to read, do so. */
4914 #if defined (FIONREAD)
4916 ioctl (tty, FIONREAD, &chars_avail);
4918 if (chars_avail > space_avail)
4919 chars_avail = space_avail;
4921 if (chars_avail)
4922 chars_avail = read (tty, &input[0], chars_avail);
4924 #else /* !FIONREAD */
4925 # if defined (O_NDELAY)
4927 int flags;
4929 flags = fcntl (tty, F_GETFL, 0);
4931 fcntl (tty, F_SETFL, (flags | O_NDELAY));
4932 chars_avail = read (tty, &input[0], space_avail);
4933 fcntl (tty, F_SETFL, flags);
4935 if (chars_avail == -1)
4936 chars_avail = 0;
4938 # else /* !O_NDELAY */
4939 # ifdef __DJGPP__
4941 extern long pc_term_chars_avail (void);
4943 if (isatty (tty))
4944 chars_avail = pc_term_chars_avail ();
4945 else
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;
4952 if (fstat (tty, &st) < 0)
4953 chars_avail = 1;
4954 else
4955 chars_avail = st.st_size;
4957 if (chars_avail > space_avail)
4958 chars_avail = space_avail;
4959 if (chars_avail)
4960 chars_avail = read (tty, &input[0], chars_avail);
4962 # endif/* __DJGPP__ */
4963 # endif /* O_NDELAY */
4964 #endif /* !FIONREAD */
4966 while (i < chars_avail)
4968 info_push_typeahead (input[i]);
4969 i++;
4973 /* How to read a single character. */
4974 unsigned char
4975 info_get_input_char (void)
4977 unsigned char keystroke;
4979 info_gather_typeahead ();
4981 if (pending_input_character)
4983 keystroke = pending_input_character;
4984 pending_input_character = 0;
4986 else if (info_get_key_from_typeahead (&keystroke) == 0)
4988 int rawkey;
4989 unsigned char c;
4990 int tty = fileno (info_input_stream);
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. */
5002 int n;
5004 n = read (tty, &c, 1);
5005 while (n == -1 && errno == EINTR);
5006 rawkey = n == 1 ? c : EOF;
5008 #else
5009 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5010 #endif
5012 keystroke = rawkey;
5014 if (rawkey == EOF)
5016 if (info_input_stream != stdin)
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;
5028 if (rawkey == EOF)
5030 terminal_unprep_terminal ();
5031 close_dribble_file ();
5032 xexit (0);
5037 if (info_dribble_file)
5038 dribble (keystroke);
5040 return keystroke;