1 /* $FreeBSD: src/contrib/texinfo/info/session.c,v 1.4.2.1 2002/03/30 17:09:19 ru Exp $ */
2 /* session.c -- user windowing interface to Info.
3 $Id: session.c,v 1.45 2002/03/02 15:05:04 karl Exp $
5 Copyright (C) 1993, 96, 97, 98, 99, 2000, 01, 02
6 Free Software Foundation, Inc.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 Written by Brian Fox (bfox@ai.mit.edu). */
25 #include <sys/ioctl.h>
27 #if defined (HAVE_SYS_TIME_H)
28 # include <sys/time.h>
29 # define HAVE_STRUCT_TIMEVAL
30 #endif /* HAVE_SYS_TIME_H */
32 #if defined (HANDLE_MAN_PAGES)
37 /* SCO 3.2v5.0.2 defines but does not correctly declare strncasecmp.
38 Since we use it as a symbol, have to get it right. --gildea, 1jul99. */
39 extern int strncasecmp (const char *, const char *, size_t);
42 static void info_clear_pending_input (), info_set_pending_input ();
43 static void info_handle_pointer ();
45 /* **************************************************************** */
47 /* Running an Info Session */
49 /* **************************************************************** */
51 /* The place that we are reading input from. */
52 static FILE *info_input_stream = NULL;
54 /* The last executed command. */
55 VFunction *info_last_executed_command = NULL;
57 /* Becomes non-zero when 'q' is typed to an Info window. */
58 int quit_info_immediately = 0;
60 /* Array of structures describing for each window which nodes have been
61 visited in that window. */
62 INFO_WINDOW **info_windows = NULL;
64 /* Where to add the next window, if we need to add one. */
65 static int info_windows_index = 0;
67 /* Number of slots allocated to `info_windows'. */
68 static int info_windows_slots = 0;
70 void remember_window_and_node (), forget_window_and_nodes ();
71 void initialize_info_session (), info_session ();
72 void display_startup_message_and_start ();
74 /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
75 For each loaded node, create a new window. Always split the largest of the
78 begin_multiple_window_info_session (filename, nodenames)
83 WINDOW *window = (WINDOW *)NULL;
85 for (i = 0; nodenames[i]; i++)
89 node = info_get_node (filename, nodenames[i]);
94 /* If this is the first node, initialize the info session. */
97 initialize_info_session (node, 1);
98 window = active_window;
102 /* Find the largest window in WINDOWS, and make that be the active
103 one. Then split it and add our window and node to the list
104 of remembered windows and nodes. Then tile the windows. */
105 WINDOW *win, *largest = NULL;
108 for (win = windows; win; win = win->next)
109 if (win->height > max_height)
111 max_height = win->height;
117 display_update_display (windows);
118 info_error (msg_cant_find_window);
123 active_window = largest;
124 window = window_make_window (node);
127 window_tile_windows (TILE_INTERNALS);
128 remember_window_and_node (window, node);
132 display_update_display (windows);
133 info_error (msg_win_too_small);
139 display_startup_message_and_start ();
142 /* Start an info session with INITIAL_NODE, and an error message in the echo
143 area made from FORMAT and ARG. */
145 begin_info_session_with_error (initial_node, format, arg1, arg2)
151 initialize_info_session (initial_node, 1);
152 info_error (format, arg1, arg2);
156 /* Start an info session with INITIAL_NODE. */
158 begin_info_session (initial_node)
161 initialize_info_session (initial_node, 1);
162 display_startup_message_and_start ();
166 display_startup_message_and_start ()
170 format = replace_in_documentation
171 (_("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."));
173 window_message_in_echo_area (format, VERSION);
177 /* Run an info session with an already initialized window and node. */
181 display_update_display (windows);
182 info_last_executed_command = NULL;
183 info_read_and_dispatch ();
184 /* On program exit, leave the cursor at the bottom of the window, and
185 restore the terminal I/O. */
186 terminal_goto_xy (0, screenheight - 1);
187 terminal_clear_to_eol ();
189 terminal_unprep_terminal ();
190 close_dribble_file ();
193 /* Here is a window-location dependent event loop. Called from the
194 functions info_session (), and from read_xxx_in_echo_area (). */
196 info_read_and_dispatch ()
202 while (!done && !quit_info_immediately)
206 /* If we haven't just gone up or down a line, there is no
207 goal column for this window. */
208 if ((info_last_executed_command != info_next_line) &&
209 (info_last_executed_command != info_prev_line))
210 active_window->goal_column = -1;
212 if (echo_area_is_active)
214 lk = echo_area_last_command_was_kill;
215 echo_area_prep_read ();
218 if (!info_any_buffered_input_p ())
219 display_update_display (windows);
221 display_cursor_at_point (active_window);
222 info_initialize_numeric_arg ();
224 initialize_keyseq ();
225 key = info_get_input_char ();
227 /* No errors yet. We just read a character, that's all. Only clear
228 the echo_area if it is not currently active. */
229 if (!echo_area_is_active)
230 window_clear_echo_area ();
232 info_error_was_printed = 0;
234 /* Do the selected command. */
235 info_dispatch_on_key (key, active_window->keymap);
237 if (echo_area_is_active)
239 /* Echo area commands that do killing increment the value of
240 ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no
241 change in the value of this variable, the last command
242 executed was not a kill command. */
243 if (lk == echo_area_last_command_was_kill)
244 echo_area_last_command_was_kill = 0;
246 if (ea_last_executed_command == ea_newline ||
247 info_aborted_echo_area)
249 ea_last_executed_command = (VFunction *)NULL;
253 if (info_last_executed_command == info_quit)
254 quit_info_immediately = 1;
256 else if (info_last_executed_command == info_quit)
261 /* Found in signals.c */
262 extern void initialize_info_signal_handler ();
264 /* Initialize the first info session by starting the terminal, window,
265 and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
267 initialize_info_session (node, clear_screen)
271 char *term_name = getenv ("TERM");
272 terminal_initialize_terminal (term_name);
274 if (terminal_is_dumb_p)
279 info_error (msg_term_too_dumb, term_name);
285 terminal_prep_terminal ();
286 terminal_clear_screen ();
289 initialize_info_keymaps ();
290 window_initialize_windows (screenwidth, screenheight);
291 initialize_info_signal_handler ();
292 display_initialize_display (screenwidth, screenheight);
293 info_set_node_of_window (0, active_window, node);
295 /* Tell the window system how to notify us when a window needs to be
296 asynchronously deleted (e.g., user resizes window very small). */
297 window_deletion_notifier = forget_window_and_nodes;
299 /* If input has not been redirected yet, make it come from unbuffered
301 if (!info_input_stream)
303 setbuf (stdin, NULL);
304 info_input_stream = stdin;
307 info_windows_initialized_p = 1;
310 /* Tell Info that input is coming from the file FILENAME. */
312 info_set_input_from_file (filename)
317 /* Input may include binary characters. */
318 stream = fopen (filename, FOPEN_RBIN);
323 if ((info_input_stream != (FILE *)NULL) &&
324 (info_input_stream != stdin))
325 fclose (info_input_stream);
327 info_input_stream = stream;
330 display_inhibited = 1;
333 /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
335 get_info_window_of_window (window)
339 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
341 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
342 if (info_win->window == window)
348 /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
349 values if the window and node are the same as the current one being
352 set_remembered_pagetop_and_point (window)
355 INFO_WINDOW *info_win;
357 info_win = get_info_window_of_window (window);
362 if (info_win->nodes_index &&
363 (info_win->nodes[info_win->current] == window->node))
365 info_win->pagetops[info_win->current] = window->pagetop;
366 info_win->points[info_win->current] = window->point;
371 remember_window_and_node (window, node)
375 /* See if we already have this window in our list. */
376 INFO_WINDOW *info_win = get_info_window_of_window (window);
378 /* If the window wasn't already on our list, then make a new entry. */
381 info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
382 info_win->window = window;
383 info_win->nodes = (NODE **)NULL;
384 info_win->pagetops = (int *)NULL;
385 info_win->points = (long *)NULL;
386 info_win->current = 0;
387 info_win->nodes_index = 0;
388 info_win->nodes_slots = 0;
390 add_pointer_to_array (info_win, info_windows_index, info_windows,
391 info_windows_slots, 10, INFO_WINDOW *);
394 /* If this node, the current pagetop, and the current point are the
395 same as the current saved node and pagetop, don't really add this to
396 the list of history nodes. This may happen only at the very
397 beginning of the program, I'm not sure. --karl */
399 && info_win->current >= 0
400 && info_win->nodes[info_win->current]->contents == node->contents
401 && info_win->pagetops[info_win->current] == window->pagetop
402 && info_win->points[info_win->current] == window->point)
405 /* Remember this node, the currently displayed pagetop, and the current
406 location of point in this window. Because we are updating pagetops
407 and points as well as nodes, it is more efficient to avoid the
408 add_pointer_to_array macro here. */
409 if (info_win->nodes_index + 2 >= info_win->nodes_slots)
411 info_win->nodes_slots += 20;
412 info_win->nodes = (NODE **) xrealloc (info_win->nodes,
413 info_win->nodes_slots * sizeof (NODE *));
414 info_win->pagetops = (int *) xrealloc (info_win->pagetops,
415 info_win->nodes_slots * sizeof (int));
416 info_win->points = (long *) xrealloc (info_win->points,
417 info_win->nodes_slots * sizeof (long));
420 info_win->nodes[info_win->nodes_index] = node;
421 info_win->pagetops[info_win->nodes_index] = window->pagetop;
422 info_win->points[info_win->nodes_index] = window->point;
423 info_win->current = info_win->nodes_index++;
424 info_win->nodes[info_win->nodes_index] = NULL;
425 info_win->pagetops[info_win->nodes_index] = 0;
426 info_win->points[info_win->nodes_index] = 0;
429 #define DEBUG_FORGET_WINDOW_AND_NODES
430 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
432 consistency_check_info_windows ()
436 for (i = 0; i < info_windows_index; i++)
440 for (win = windows; win; win = win->next)
441 if (win == info_windows[i]->window)
448 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
450 /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
452 forget_window_and_nodes (window)
456 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
458 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
459 if (info_win->window == window)
462 /* If we found the window to forget, then do so. */
465 while (i < info_windows_index)
467 info_windows[i] = info_windows[i + 1];
471 info_windows_index--;
472 info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
476 /* Free the node structures which held onto internal node contents
477 here. This doesn't free the contents; we have a garbage collector
479 for (i = 0; info_win->nodes[i]; i++)
480 if (internal_info_node_p (info_win->nodes[i]))
481 free (info_win->nodes[i]);
482 free (info_win->nodes);
484 maybe_free (info_win->pagetops);
485 maybe_free (info_win->points);
490 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
491 consistency_check_info_windows ();
492 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
495 /* Set WINDOW to show NODE. Remember the new window in our list of Info
496 windows. If we are doing automatic footnote display, also try to display
497 the footnotes for this window. If REMEMBER is nonzero, first call
498 set_remembered_pagetop_and_point. */
500 info_set_node_of_window (remember, window, node)
506 set_remembered_pagetop_and_point (window);
508 /* Put this node into the window. */
509 window_set_node_of_window (window, node);
511 /* Remember this node and window in our list of info windows. */
512 remember_window_and_node (window, node);
514 /* If doing auto-footnote display/undisplay, show the footnotes belonging
515 to this window's node. */
516 if (auto_footnotes_p)
517 info_get_or_remove_footnotes (window);
521 /* **************************************************************** */
523 /* Info Movement Commands */
525 /* **************************************************************** */
527 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
530 set_window_pagetop (window, desired_top)
534 int point_line, old_pagetop;
538 else if (desired_top > window->line_count)
539 desired_top = window->line_count - 1;
541 if (window->pagetop == desired_top)
544 old_pagetop = window->pagetop;
545 window->pagetop = desired_top;
547 /* Make sure that point appears in this window. */
548 point_line = window_line_of_point (window);
549 if ((point_line < window->pagetop) ||
550 ((point_line - window->pagetop) > window->height - 1))
552 window->line_starts[window->pagetop] - window->node->contents;
554 window->flags |= W_UpdateWindow;
556 /* Find out which direction to scroll, and scroll the window in that
557 direction. Do this only if there would be a savings in redisplay
558 time. This is true if the amount to scroll is less than the height
559 of the window, and if the number of lines scrolled would be greater
560 than 10 % of the window's height. */
561 if (old_pagetop < desired_top)
563 int start, end, amount;
565 amount = desired_top - old_pagetop;
567 if ((amount >= window->height) ||
568 (((window->height - amount) * 10) < window->height))
571 start = amount + window->first_row;
572 end = window->height + window->first_row;
574 display_scroll_display (start, end, -amount);
578 int start, end, amount;
580 amount = old_pagetop - desired_top;
582 if ((amount >= window->height) ||
583 (((window->height - amount) * 10) < window->height))
586 start = window->first_row;
587 end = (window->first_row + window->height) - amount;
588 display_scroll_display (start, end, amount);
592 /* Immediately make WINDOW->point visible on the screen, and move the
593 terminal cursor there. */
595 info_show_point (window)
600 old_pagetop = window->pagetop;
601 window_adjust_pagetop (window);
602 if (old_pagetop != window->pagetop)
606 new_pagetop = window->pagetop;
607 window->pagetop = old_pagetop;
608 set_window_pagetop (window, new_pagetop);
611 if (window->flags & W_UpdateWindow)
612 display_update_one_window (window);
614 display_cursor_at_point (window);
617 /* Move WINDOW->point from OLD line index to NEW line index. */
619 move_to_new_line (old, new, window)
625 info_error (msg_cant_find_point);
631 if (new >= window->line_count || new < 0)
634 goal = window_get_goal_column (window);
635 window->goal_column = goal;
637 window->point = window->line_starts[new] - window->node->contents;
638 window->point += window_chars_to_goal (window->line_starts[new], goal);
639 info_show_point (window);
643 /* Move WINDOW's point down to the next line if possible. */
644 DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
646 int old_line, new_line;
649 info_prev_line (window, -count, key);
652 old_line = window_line_of_point (window);
653 new_line = old_line + count;
654 move_to_new_line (old_line, new_line, window);
658 /* Move WINDOW's point up to the previous line if possible. */
659 DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
661 int old_line, new_line;
664 info_next_line (window, -count, key);
667 old_line = window_line_of_point (window);
668 new_line = old_line - count;
669 move_to_new_line (old_line, new_line, window);
673 /* Move WINDOW's point to the end of the true line. */
674 DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
676 register int point, len;
677 register char *buffer;
679 buffer = window->node->contents;
680 len = window->node->nodelen;
682 for (point = window->point;
683 (point < len) && (buffer[point] != '\n');
686 if (point != window->point)
688 window->point = point;
689 info_show_point (window);
693 /* Move WINDOW's point to the beginning of the true line. */
694 DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
697 register char *buffer;
699 buffer = window->node->contents;
700 point = window->point;
702 for (; (point) && (buffer[point - 1] != '\n'); point--);
704 /* If at a line start already, do nothing. */
705 if (point != window->point)
707 window->point = point;
708 info_show_point (window);
712 /* Move point forward in the node. */
713 DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
716 info_backward_char (window, -count, key);
719 window->point += count;
721 if (window->point >= window->node->nodelen)
722 window->point = window->node->nodelen - 1;
724 info_show_point (window);
728 /* Move point backward in the node. */
729 DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
732 info_forward_char (window, -count, key);
735 window->point -= count;
737 if (window->point < 0)
740 info_show_point (window);
744 #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
746 /* Move forward a word in this node. */
747 DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
755 info_backward_word (window, -count, key);
759 point = window->point;
760 buffer = window->node->contents;
761 end = window->node->nodelen;
765 if (point + 1 >= end)
768 /* If we are not in a word, move forward until we are in one.
769 Then, move forward until we hit a non-alphabetic character. */
774 while (++point < end)
782 if (point >= end) return;
784 while (++point < end)
792 window->point = point;
793 info_show_point (window);
796 DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
804 info_forward_word (window, -count, key);
808 buffer = window->node->contents;
809 point = window->point;
816 /* Like info_forward_word (), except that we look at the
817 characters just before point. */
819 c = buffer[point - 1];
825 c = buffer[point - 1];
833 c = buffer[point - 1];
841 window->point = point;
842 info_show_point (window);
845 /* Variable controlling the behaviour of default scrolling when you are
846 already at the bottom of a node. Possible values are defined in session.h.
849 IS_Continuous Try to get first menu item, or failing that, the
850 "Next:" pointer, or failing that, the "Up:" and
852 IS_NextOnly Try to get "Next:" menu item.
853 IS_PageOnly Simply give up at the bottom of a node. */
855 int info_scroll_behaviour = IS_Continuous;
857 /* Choices used by the completer when reading a value for the user-visible
858 variable "scroll-behaviour". */
859 char *info_scroll_choices[] = {
860 "Continuous", "Next Only", "Page Only", (char *)NULL
863 /* Default window sizes for scrolling commands. */
864 int default_window_size = -1; /* meaning 1 window-full */
865 int default_scroll_size = -1; /* meaning half screen size */
867 #define INFO_LABEL_FOUND() \
868 (info_parsed_nodename || (info_parsed_filename \
869 && !is_dir_name (info_parsed_filename)))
871 /* Move to 1st menu item, Next, Up/Next, or error in this window. */
873 forward_move_node_structure (window, behaviour)
880 info_error (msg_at_node_bottom);
884 info_next_label_of_node (window->node);
885 if (!info_parsed_nodename && !info_parsed_filename)
886 info_error (msg_no_pointer, _("Next"));
889 window_message_in_echo_area (_("Following Next node..."));
890 info_handle_pointer ("Next", window);
896 /* First things first. If this node contains a menu, move down
901 menu = info_menu_of_node (window->node);
905 info_free_references (menu);
906 window_message_in_echo_area (_("Selecting first menu item..."));
907 info_menu_digit (window, 1, '1');
912 /* Okay, this node does not contain a menu. If it contains a
913 "Next:" pointer, use that. */
914 info_next_label_of_node (window->node);
915 if (INFO_LABEL_FOUND ())
917 window_message_in_echo_area (_("Selecting Next node..."));
918 info_handle_pointer ("Next", window);
922 /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we
923 can move "Next:". If that isn't possible, complain that there
924 are no more nodes. */
926 int up_counter, old_current;
927 INFO_WINDOW *info_win;
929 /* Remember the current node and location. */
930 info_win = get_info_window_of_window (window);
931 old_current = info_win->current;
933 /* Back up through the "Up:" pointers until we have found a "Next:"
934 that isn't the same as the first menu item found in that node. */
936 while (!info_error_was_printed)
938 info_up_label_of_node (window->node);
939 if (INFO_LABEL_FOUND ())
941 info_handle_pointer ("Up", window);
942 if (info_error_was_printed)
947 info_next_label_of_node (window->node);
949 /* If no "Next" pointer, keep backing up. */
950 if (!INFO_LABEL_FOUND ())
953 /* If this node's first menu item is the same as this node's
954 Next pointer, keep backing up. */
955 if (!info_parsed_filename)
960 /* Remember the name of the Next node, since reading
961 the menu can overwrite the contents of the
962 info_parsed_xxx strings. */
963 next_nodename = xstrdup (info_parsed_nodename);
965 menu = info_menu_of_node (window->node);
968 (menu[0]->nodename, next_nodename) == 0))
970 info_free_references (menu);
971 free (next_nodename);
976 /* Restore the world to where it was before
977 reading the menu contents. */
978 info_free_references (menu);
979 free (next_nodename);
980 info_next_label_of_node (window->node);
984 /* This node has a "Next" pointer, and it is not the
985 same as the first menu item found in this node. */
986 window_message_in_echo_area
987 (_("Moving Up %d time(s), then Next."),
990 info_handle_pointer ("Next", window);
995 /* No more "Up" pointers. Print an error, and call it
999 for (i = 0; i < up_counter; i++)
1001 info_win->nodes_index--;
1002 free (info_win->nodes[info_win->nodes_index]);
1003 info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
1005 info_win->current = old_current;
1006 window->node = info_win->nodes[old_current];
1007 window->pagetop = info_win->pagetops[old_current];
1008 window->point = info_win->points[old_current];
1009 recalculate_line_starts (window);
1010 window->flags |= W_UpdateWindow;
1011 info_error (_("No more nodes within this document."));
1020 /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
1022 backward_move_node_structure (window, behaviour)
1029 info_error (msg_at_node_top);
1033 info_prev_label_of_node (window->node);
1034 if (!info_parsed_nodename && !info_parsed_filename)
1035 info_error (_("No `Prev' for this node."));
1038 window_message_in_echo_area (_("Moving Prev in this window."));
1039 info_handle_pointer ("Prev", window);
1044 info_prev_label_of_node (window->node);
1046 if (!info_parsed_nodename && (!info_parsed_filename
1047 || is_dir_name (info_parsed_filename)))
1049 info_up_label_of_node (window->node);
1050 if (!info_parsed_nodename && (!info_parsed_filename
1051 || is_dir_name (info_parsed_filename)))
1052 info_error (_("No `Prev' or `Up' for this node within this document."));
1055 window_message_in_echo_area (_("Moving Up in this window."));
1056 info_handle_pointer ("Up", window);
1062 int inhibit_menu_traversing = 0;
1064 /* Watch out! If this node's Prev is the same as the Up, then
1065 move Up. Otherwise, we could move Prev, and then to the last
1066 menu item in the Prev. This would cause the user to loop
1067 through a subsection of the info file. */
1068 if (!info_parsed_filename && info_parsed_nodename)
1072 pnode = xstrdup (info_parsed_nodename);
1073 info_up_label_of_node (window->node);
1075 if (!info_parsed_filename && info_parsed_nodename &&
1076 strcmp (info_parsed_nodename, pnode) == 0)
1078 /* The nodes are the same. Inhibit moving to the last
1081 inhibit_menu_traversing = 1;
1086 info_prev_label_of_node (window->node);
1090 /* Move to the previous node. If this node now contains a menu,
1091 and we have not inhibited movement to it, move to the node
1092 corresponding to the last menu item. */
1093 window_message_in_echo_area (_("Moving Prev in this window."));
1094 info_handle_pointer ("Prev", window);
1096 if (!inhibit_menu_traversing)
1098 while (!info_error_was_printed &&
1099 (menu = info_menu_of_node (window->node)))
1101 info_free_references (menu);
1102 window_message_in_echo_area
1103 (_("Moving to `Prev's last menu item."));
1104 info_menu_digit (window, 1, '0');
1112 /* Move continuously forward through the node structure of this info file. */
1113 DECLARE_INFO_COMMAND (info_global_next_node,
1114 _("Move forwards or down through node structure"))
1117 info_global_prev_node (window, -count, key);
1120 while (count && !info_error_was_printed)
1122 forward_move_node_structure (window, IS_Continuous);
1128 /* Move continuously backward through the node structure of this info file. */
1129 DECLARE_INFO_COMMAND (info_global_prev_node,
1130 _("Move backwards or up through node structure"))
1133 info_global_next_node (window, -count, key);
1136 while (count && !info_error_was_printed)
1138 backward_move_node_structure (window, IS_Continuous);
1144 static void _scroll_forward();
1145 static void _scroll_backward();
1148 _scroll_forward(window, count, key, behaviour)
1155 _scroll_backward (window, -count, key, behaviour);
1160 /* Without an explicit numeric argument, scroll the bottom two
1161 lines to the top of this window, Or, if at bottom of window,
1162 and the chosen behaviour is to scroll through nodes get the
1163 "Next" node for this window. */
1164 if (default_window_size > 0)
1165 desired_top = window->pagetop + default_window_size;
1166 else if (!info_explicit_arg && count == 1)
1168 desired_top = window->pagetop + (window->height - 2);
1170 /* If there are no more lines to scroll here, error, or get
1171 another node, depending on BEHAVIOUR. */
1172 if (desired_top > window->line_count)
1174 forward_move_node_structure (window, behaviour);
1179 desired_top = window->pagetop + count;
1181 if (desired_top >= window->line_count)
1182 desired_top = window->line_count - 2;
1184 if (window->pagetop > desired_top)
1187 set_window_pagetop (window, desired_top);
1192 _scroll_backward(window, count, key, behaviour)
1199 _scroll_forward (window, -count, key, behaviour);
1204 /* Without an explicit numeric argument, scroll the top two lines
1205 to the bottom of this window, or, depending on the selected
1206 behaviour, move to the previous, or Up'th node. */
1207 if (default_window_size > 0)
1208 desired_top = window->pagetop - default_window_size;
1209 else if (!info_explicit_arg && count == 1)
1211 desired_top = window->pagetop - (window->height - 2);
1213 if ((desired_top < 0) && (window->pagetop == 0))
1215 backward_move_node_structure (window, behaviour);
1220 desired_top = window->pagetop - count;
1222 if (desired_top < 0)
1225 set_window_pagetop (window, desired_top);
1229 /* Show the next screen of WINDOW's node. */
1230 DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
1232 _scroll_forward (window, count, key, info_scroll_behaviour);
1235 /* Like info_scroll_forward, but sets default_window_size as a side
1237 DECLARE_INFO_COMMAND (info_scroll_forward_set_window,
1238 _("Scroll forward in this window and set default window size"))
1240 if (info_explicit_arg)
1241 default_window_size = count;
1242 _scroll_forward (window, count, key, info_scroll_behaviour);
1245 /* Show the next screen of WINDOW's node but never advance to next node. */
1246 DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))
1248 _scroll_forward (window, count, key, IS_PageOnly);
1251 /* Like info_scroll_forward_page_only, but sets default_window_size as a side
1253 DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,
1254 _("Scroll forward in this window staying within node and set default window size"))
1256 if (info_explicit_arg)
1257 default_window_size = count;
1258 _scroll_forward (window, count, key, IS_PageOnly);
1261 /* Show the previous screen of WINDOW's node. */
1262 DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
1264 _scroll_backward (window, count, key, info_scroll_behaviour);
1267 /* Like info_scroll_backward, but sets default_window_size as a side
1269 DECLARE_INFO_COMMAND (info_scroll_backward_set_window,
1270 _("Scroll backward in this window and set default window size"))
1272 if (info_explicit_arg)
1273 default_window_size = count;
1274 _scroll_backward (window, count, key, info_scroll_behaviour);
1277 /* Show the previous screen of WINDOW's node but never move to previous
1279 DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))
1281 _scroll_backward (window, count, key, IS_PageOnly);
1284 /* Like info_scroll_backward_page_only, but sets default_window_size as a side
1286 DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,
1287 _("Scroll backward in this window staying within node and set default window size"))
1289 if (info_explicit_arg)
1290 default_window_size = count;
1291 _scroll_backward (window, count, key, IS_PageOnly);
1294 /* Move to the beginning of the node. */
1295 DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1297 window->pagetop = window->point = 0;
1298 window->flags |= W_UpdateWindow;
1301 /* Move to the end of the node. */
1302 DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1304 window->point = window->node->nodelen - 1;
1305 info_show_point (window);
1308 /* Scroll the window forward by N lines. */
1309 DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
1312 info_up_line (window, -count, key);
1315 int desired_top = window->pagetop + count;
1317 if (desired_top >= window->line_count)
1318 desired_top = window->line_count - 2;
1320 if (window->pagetop <= desired_top)
1321 set_window_pagetop (window, desired_top);
1325 /* Scroll the window backward by N lines. */
1326 DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
1329 info_down_line (window, -count, key);
1332 int desired_top = window->pagetop - count;
1334 if (desired_top < 0)
1337 set_window_pagetop (window, desired_top);
1341 /* Scroll the window forward by N lines and remember N as default for
1342 subsequent commands. */
1343 DECLARE_INFO_COMMAND (info_scroll_half_screen_down,
1344 _("Scroll down by half screen size"))
1347 info_scroll_half_screen_up (window, -count, key);
1350 int scroll_size = (the_screen->height + 1) / 2;
1353 if (info_explicit_arg)
1354 default_scroll_size = count;
1355 if (default_scroll_size > 0)
1356 scroll_size = default_scroll_size;
1358 desired_top = window->pagetop + scroll_size;
1359 if (desired_top >= window->line_count)
1360 desired_top = window->line_count - 2;
1362 if (window->pagetop <= desired_top)
1363 set_window_pagetop (window, desired_top);
1367 /* Scroll the window backward by N lines and remember N as default for
1368 subsequent commands. */
1369 DECLARE_INFO_COMMAND (info_scroll_half_screen_up,
1370 _("Scroll up by half screen size"))
1373 info_scroll_half_screen_down (window, -count, key);
1376 int scroll_size = (the_screen->height + 1) / 2;
1379 if (info_explicit_arg)
1380 default_scroll_size = count;
1381 if (default_scroll_size > 0)
1382 scroll_size = default_scroll_size;
1384 desired_top = window->pagetop - scroll_size;
1385 if (desired_top < 0)
1388 set_window_pagetop (window, desired_top);
1392 /* **************************************************************** */
1394 /* Commands for Manipulating Windows */
1396 /* **************************************************************** */
1398 /* Make the next window in the chain be the active window. */
1399 DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1403 info_prev_window (window, -count, key);
1407 /* If no other window, error now. */
1408 if (!windows->next && !echo_area_is_active)
1410 info_error (msg_one_window);
1417 window = window->next;
1420 if (window == the_echo_area || !echo_area_is_active)
1423 window = the_echo_area;
1427 if (active_window != window)
1429 if (auto_footnotes_p)
1430 info_get_or_remove_footnotes (window);
1432 window->flags |= W_UpdateWindow;
1433 active_window = window;
1437 /* Make the previous window in the chain be the active window. */
1438 DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1442 info_next_window (window, -count, key);
1446 /* Only one window? */
1448 if (!windows->next && !echo_area_is_active)
1450 info_error (msg_one_window);
1456 /* If we are in the echo area, or if the echo area isn't active and we
1457 are in the first window, find the last window in the chain. */
1458 if (window == the_echo_area ||
1459 (window == windows && !echo_area_is_active))
1461 register WINDOW *win, *last;
1463 for (win = windows; win; win = win->next)
1470 if (window == windows)
1471 window = the_echo_area;
1473 window = window->prev;
1477 if (active_window != window)
1479 if (auto_footnotes_p)
1480 info_get_or_remove_footnotes (window);
1482 window->flags |= W_UpdateWindow;
1483 active_window = window;
1487 /* Split WINDOW into two windows, both showing the same node. If we
1488 are automatically tiling windows, re-tile after the split. */
1489 DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
1491 WINDOW *split, *old_active;
1494 /* Remember the current pagetop of the window being split. If it doesn't
1495 change, we can scroll its contents around after the split. */
1496 pagetop = window->pagetop;
1498 /* Make the new window. */
1499 old_active = active_window;
1500 active_window = window;
1501 split = window_make_window (window->node);
1502 active_window = old_active;
1506 info_error (msg_win_too_small);
1510 #if defined (SPLIT_BEFORE_ACTIVE)
1511 /* Try to scroll the old window into its new postion. */
1512 if (pagetop == window->pagetop)
1514 int start, end, amount;
1516 start = split->first_row;
1517 end = start + window->height;
1518 amount = split->height + 1;
1519 display_scroll_display (start, end, amount);
1521 #else /* !SPLIT_BEFORE_ACTIVE */
1522 /* Make sure point still appears in the active window. */
1523 info_show_point (window);
1524 #endif /* !SPLIT_BEFORE_ACTIVE */
1526 /* If the window just split was one internal to Info, try to display
1527 something else in it. */
1528 if (internal_info_node_p (split->node))
1532 NODE *node = (NODE *)NULL;
1535 for (i = 0; (iw = info_windows[i]); i++)
1537 for (j = 0; j < iw->nodes_index; j++)
1538 if (!internal_info_node_p (iw->nodes[j]))
1540 if (iw->nodes[j]->parent)
1541 filename = iw->nodes[j]->parent;
1543 filename = iw->nodes[j]->filename;
1545 node = info_get_node (filename, iw->nodes[j]->nodename);
1548 window_set_node_of_window (split, node);
1549 i = info_windows_index - 1;
1555 split->pagetop = window->pagetop;
1558 window_tile_windows (DONT_TILE_INTERNALS);
1560 window_adjust_pagetop (split);
1562 remember_window_and_node (split, split->node);
1566 /* Delete WINDOW, forgetting the list of last visited nodes. If we are
1567 automatically displaying footnotes, show or remove the footnotes
1568 window. If we are automatically tiling windows, re-tile after the
1570 DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
1574 info_error (msg_cant_kill_last);
1576 else if (window->flags & W_WindowIsPerm)
1578 info_error (_("Cannot delete a permanent window"));
1582 info_delete_window_internal (window);
1584 if (auto_footnotes_p)
1585 info_get_or_remove_footnotes (active_window);
1588 window_tile_windows (DONT_TILE_INTERNALS);
1592 /* Do the physical deletion of WINDOW, and forget this window and
1593 associated nodes. */
1595 info_delete_window_internal (window)
1598 if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
1600 /* We not only delete the window from the display, we forget it from
1601 our list of remembered windows. */
1602 forget_window_and_nodes (window);
1603 window_delete_window (window);
1605 if (echo_area_is_active)
1606 echo_area_inform_of_deleted_window (window);
1610 /* Just keep WINDOW, deleting all others. */
1611 DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1613 int num_deleted; /* The number of windows we deleted. */
1614 int pagetop, start, end;
1616 /* Remember a few things about this window. We may be able to speed up
1617 redisplay later by scrolling its contents. */
1618 pagetop = window->pagetop;
1619 start = window->first_row;
1620 end = start + window->height;
1628 /* Find an eligible window and delete it. If no eligible windows
1629 are found, we are done. A window is eligible for deletion if
1630 is it not permanent, and it is not WINDOW. */
1631 for (win = windows; win; win = win->next)
1632 if (win != window && ((win->flags & W_WindowIsPerm) == 0))
1638 info_delete_window_internal (win);
1642 /* Scroll the contents of this window into the right place so that the
1643 user doesn't have to wait any longer than necessary for redisplay. */
1648 amount = (window->first_row - start);
1649 amount -= (window->pagetop - pagetop);
1650 display_scroll_display (start, end, amount);
1653 window->flags |= W_UpdateWindow;
1656 /* Scroll the "other" window of WINDOW. */
1657 DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1661 /* If only one window, give up. */
1664 info_error (msg_one_window);
1668 other = window->next;
1671 other = window->prev;
1673 info_scroll_forward (other, count, key);
1676 /* Scroll the "other" window of WINDOW. */
1677 DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
1678 _("Scroll the other window backward"))
1680 info_scroll_other_window (window, -count, key);
1683 /* Change the size of WINDOW by AMOUNT. */
1684 DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1686 window_change_window_height (window, count);
1689 /* When non-zero, tiling takes place automatically when info_split_window
1691 int auto_tiling_p = 0;
1693 /* Tile all of the visible windows. */
1694 DECLARE_INFO_COMMAND (info_tile_windows,
1695 _("Divide the available screen space among the visible windows"))
1697 window_tile_windows (TILE_INTERNALS);
1700 /* Toggle the state of this window's wrapping of lines. */
1701 DECLARE_INFO_COMMAND (info_toggle_wrap,
1702 _("Toggle the state of line wrapping in the current window"))
1704 window_toggle_wrap (window);
1707 /* **************************************************************** */
1709 /* Info Node Commands */
1711 /* **************************************************************** */
1713 /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
1714 filename is not set. */
1716 node_printed_rep (node)
1724 = filename_non_directory (node->parent ? node->parent : node->filename);
1725 rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
1726 sprintf (rep, "(%s)%s", filename, node->nodename);
1729 rep = node->nodename;
1735 /* Using WINDOW for various defaults, select the node referenced by ENTRY
1736 in it. If the node is selected, the window and node are remembered. */
1738 info_select_reference (window, entry)
1743 char *filename, *nodename, *file_system_error;
1745 file_system_error = (char *)NULL;
1747 filename = entry->filename;
1749 filename = window->node->parent;
1751 filename = window->node->filename;
1754 filename = xstrdup (filename);
1756 if (entry->nodename)
1757 nodename = xstrdup (entry->nodename);
1759 nodename = xstrdup ("Top");
1761 node = info_get_node (filename, nodename);
1763 /* Try something a little weird. If the node couldn't be found, and the
1764 reference was of the form "foo::", see if the entry->label can be found
1765 as a file, with a node of "Top". */
1768 if (info_recent_file_error)
1769 file_system_error = xstrdup (info_recent_file_error);
1771 if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
1773 node = info_get_node (entry->label, "Top");
1774 if (!node && info_recent_file_error)
1776 maybe_free (file_system_error);
1777 file_system_error = xstrdup (info_recent_file_error);
1784 if (file_system_error)
1785 info_error (file_system_error);
1787 info_error (msg_cant_find_node, nodename);
1790 maybe_free (file_system_error);
1791 maybe_free (filename);
1792 maybe_free (nodename);
1795 info_set_node_of_window (1, window, node);
1798 /* Parse the node specification in LINE using WINDOW to default the filename.
1799 Select the parsed node in WINDOW and remember it, or error if the node
1800 couldn't be found. */
1802 info_parse_and_select (line, window)
1808 info_parse_node (line, DONT_SKIP_NEWLINES);
1810 entry.nodename = info_parsed_nodename;
1811 entry.filename = info_parsed_filename;
1812 entry.label = "*info-parse-and-select*";
1814 info_select_reference (window, &entry);
1817 /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
1818 are previously filled, try to get the node represented by them into
1819 WINDOW. The node should have been pointed to by the LABEL pointer of
1822 info_handle_pointer (label, window)
1826 if (info_parsed_filename || info_parsed_nodename)
1828 char *filename, *nodename;
1831 filename = nodename = (char *)NULL;
1833 if (info_parsed_filename)
1834 filename = xstrdup (info_parsed_filename);
1837 if (window->node->parent)
1838 filename = xstrdup (window->node->parent);
1839 else if (window->node->filename)
1840 filename = xstrdup (window->node->filename);
1843 if (info_parsed_nodename)
1844 nodename = xstrdup (info_parsed_nodename);
1846 nodename = xstrdup ("Top");
1848 node = info_get_node (filename, nodename);
1852 INFO_WINDOW *info_win;
1854 info_win = get_info_window_of_window (window);
1857 info_win->pagetops[info_win->current] = window->pagetop;
1858 info_win->points[info_win->current] = window->point;
1860 info_set_node_of_window (1, window, node);
1864 if (info_recent_file_error)
1865 info_error (info_recent_file_error);
1867 info_error (msg_cant_file_node, filename, nodename);
1875 info_error (msg_no_pointer, label);
1879 /* Make WINDOW display the "Next:" node of the node currently being
1881 DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
1883 info_next_label_of_node (window->node);
1884 info_handle_pointer ("Next", window);
1887 /* Make WINDOW display the "Prev:" node of the node currently being
1889 DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
1891 info_prev_label_of_node (window->node);
1892 info_handle_pointer ("Prev", window);
1895 /* Make WINDOW display the "Up:" node of the node currently being
1897 DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
1899 info_up_label_of_node (window->node);
1900 info_handle_pointer ("Up", window);
1903 /* Make WINDOW display the last node of this info file. */
1904 DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
1907 FILE_BUFFER *fb = file_buffer_of_window (window);
1908 NODE *node = (NODE *)NULL;
1912 int last_node_tag_idx = -1;
1914 /* If no explicit argument, or argument of zero, default to the
1916 if (count == 0 || (count == 1 && !info_explicit_arg))
1918 for (i = 0; count && fb->tags[i]; i++)
1919 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1922 last_node_tag_idx = i;
1925 i = last_node_tag_idx + 1;
1927 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1931 info_error (_("This window has no additional nodes"));
1933 info_set_node_of_window (1, window, node);
1936 /* Make WINDOW display the first node of this info file. */
1937 DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
1939 FILE_BUFFER *fb = file_buffer_of_window (window);
1940 NODE *node = (NODE *)NULL;
1942 /* If no explicit argument, or argument of zero, default to the
1949 int last_node_tag_idx = -1;
1951 for (i = 0; count && fb->tags[i]; i++)
1952 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1955 last_node_tag_idx = i;
1958 i = last_node_tag_idx + 1;
1960 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1964 info_error (_("This window has no additional nodes"));
1966 info_set_node_of_window (1, window, node);
1969 /* Select the last menu item in WINDOW->node. */
1970 DECLARE_INFO_COMMAND (info_last_menu_item,
1971 _("Select the last item in this node's menu"))
1973 info_menu_digit (window, 1, '0');
1976 /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
1977 DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
1979 register int i, item;
1980 register REFERENCE *entry, **menu;
1982 menu = info_menu_of_node (window->node);
1986 info_error (msg_no_menu_node);
1990 /* We have the menu. See if there are this many items in it. */
1993 /* Special case. Item "0" is the last item in this menu. */
1995 for (i = 0; menu[i + 1]; i++);
1998 for (i = 0; (entry = menu[i]); i++)
2004 info_select_reference (window, menu[i]);
2006 info_error (_("There aren't %d items in this menu."), item);
2008 info_free_references (menu);
2012 /* Read a menu or followed reference from the user defaulting to the
2013 reference found on the current line, and select that node. The
2014 reading is done with completion. BUILDER is the function used
2015 to build the list of references. ASK_P is non-zero if the user
2016 should be prompted, or zero to select the default item. */
2018 info_menu_or_ref_item (window, count, key, builder, ask_p)
2022 REFERENCE **(*builder) ();
2025 REFERENCE **menu, *entry, *defentry = (REFERENCE *)NULL;
2028 menu = (*builder) (window->node);
2032 if (builder == info_menu_of_node)
2033 info_error (msg_no_menu_node);
2035 info_error (msg_no_xref_node);
2039 /* Default the selected reference to the one which is on the line that
2042 REFERENCE **refs = (REFERENCE **)NULL;
2045 point_line = window_line_of_point (window);
2047 if (point_line != -1)
2049 SEARCH_BINDING binding;
2051 binding.buffer = window->node->contents;
2052 binding.start = window->line_starts[point_line] - binding.buffer;
2053 if (window->line_starts[point_line + 1])
2054 binding.end = window->line_starts[point_line + 1] - binding.buffer;
2056 binding.end = window->node->nodelen;
2059 if (builder == info_menu_of_node)
2064 refs = info_menu_items (&binding);
2069 #if defined (HANDLE_MAN_PAGES)
2070 if (window->node->flags & N_IsManPage)
2071 refs = manpage_xrefs_in_binding (window->node, &binding);
2073 #endif /* HANDLE_MAN_PAGES */
2075 refs = info_xrefs (&binding);
2076 if (!refs && point_line > 0)
2078 /* People get annoyed that Info cannot find an xref
2079 which starts on a previous line and ends on this
2080 one. So if we fail to find a reference on this
2081 line, let's try the one before. */
2083 window->line_starts[point_line - 1] - binding.buffer;
2084 refs = info_xrefs (&binding);
2091 if ((strcmp (refs[0]->label, "Menu") != 0) ||
2092 (builder == info_xrefs_of_node))
2096 /* Find the closest reference to point. */
2097 if (builder == info_xrefs_of_node)
2101 for (; refs[which]; which++)
2103 if ((window->point >= refs[which]->start) &&
2104 (window->point <= refs[which]->end))
2109 else if (window->point < refs[which]->start)
2120 defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2121 defentry->label = xstrdup (refs[which]->label);
2122 defentry->filename = refs[which]->filename;
2123 defentry->nodename = refs[which]->nodename;
2125 if (defentry->filename)
2126 defentry->filename = xstrdup (defentry->filename);
2127 if (defentry->nodename)
2128 defentry->nodename = xstrdup (defentry->nodename);
2130 info_free_references (refs);
2135 /* If we are going to ask the user a question, do it now. */
2140 /* Build the prompt string. */
2142 prompt = (char *)xmalloc (20 + strlen (defentry->label));
2144 prompt = (char *)xmalloc (20);
2146 if (builder == info_menu_of_node)
2149 sprintf (prompt, _("Menu item (%s): "), defentry->label);
2151 sprintf (prompt, _("Menu item: "));
2156 sprintf (prompt, _("Follow xref (%s): "), defentry->label);
2158 sprintf (prompt, _("Follow xref: "));
2161 line = info_read_completing_in_echo_area (window, prompt, menu);
2164 window = active_window;
2166 /* User aborts, just quit. */
2169 maybe_free (defentry);
2170 info_free_references (menu);
2171 info_abort_key (window, 0, 0);
2175 /* If we had a default and the user accepted it, use that. */
2180 line = xstrdup (defentry->label);
2182 line = (char *)NULL;
2187 /* Not going to ask any questions. If we have a default entry, use
2188 that, otherwise return. */
2192 line = xstrdup (defentry->label);
2197 /* It is possible that the references have more than a single
2198 entry with the same label, and also LINE is down-cased, which
2199 complicates matters even more. Try to be as accurate as we
2200 can: if they've chosen the default, use defentry directly. */
2201 if (defentry && strcmp (line, defentry->label) == 0)
2204 /* Find the selected label in the references. If there are
2205 more than one label which matches, find the one that's
2206 closest to point. */
2209 int best = -1, min_dist = window->node->nodelen;
2212 for (i = 0; menu && (ref = menu[i]); i++)
2214 /* Need to use strcasecmp because LINE is downcased
2215 inside info_read_completing_in_echo_area. */
2216 if (strcasecmp (line, ref->label) == 0)
2218 /* ref->end is more accurate estimate of position
2219 for menus than ref->start. Go figure. */
2220 int dist = abs (window->point - ref->end);
2222 if (dist < min_dist)
2232 entry = (REFERENCE *)NULL;
2235 if (!entry && defentry)
2236 info_error (_("The reference disappeared! (%s)."), line);
2239 NODE *orig = window->node;
2240 info_select_reference (window, entry);
2241 if (builder == info_xrefs_of_node && window->node != orig
2242 && !(window->node->flags & N_FromAnchor))
2243 { /* Search for this reference in the node. */
2247 if (window->line_count > 0)
2248 start = window->line_starts[1] - window->node->contents;
2253 info_target_search_node (window->node, entry->label, start);
2257 window->point = offset;
2258 window_adjust_pagetop (window);
2266 free (defentry->label);
2267 maybe_free (defentry->filename);
2268 maybe_free (defentry->nodename);
2273 info_free_references (menu);
2275 if (!info_error_was_printed)
2276 window_clear_echo_area ();
2279 /* Read a line (with completion) which is the name of a menu item,
2280 and select that item. */
2281 DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
2283 info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2286 /* Read a line (with completion) which is the name of a reference to
2287 follow, and select the node. */
2288 DECLARE_INFO_COMMAND
2289 (info_xref_item, _("Read a footnote or cross reference and select its node"))
2291 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2294 /* Position the cursor at the start of this node's menu. */
2295 DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
2297 SEARCH_BINDING binding;
2300 binding.buffer = window->node->contents;
2302 binding.end = window->node->nodelen;
2303 binding.flags = S_FoldCase | S_SkipDest;
2305 position = search (INFO_MENU_LABEL, &binding);
2308 info_error (msg_no_menu_node);
2311 window->point = position;
2312 window_adjust_pagetop (window);
2313 window->flags |= W_UpdateWindow;
2317 /* Visit as many menu items as is possible, each in a separate window. */
2318 DECLARE_INFO_COMMAND (info_visit_menu,
2319 _("Visit as many menu items at once as possible"))
2322 REFERENCE *entry, **menu;
2324 menu = info_menu_of_node (window->node);
2327 info_error (msg_no_menu_node);
2329 for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2333 new = window_make_window (window->node);
2334 window_tile_windows (TILE_INTERNALS);
2337 info_error (msg_win_too_small);
2340 active_window = new;
2341 info_select_reference (new, entry);
2346 /* Read a line of input which is a node name, and go to that node. */
2347 DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
2351 #define GOTO_COMPLETES
2352 #if defined (GOTO_COMPLETES)
2353 /* Build a completion list of all of the known nodes. */
2355 register int fbi, i;
2356 FILE_BUFFER *current;
2357 REFERENCE **items = (REFERENCE **)NULL;
2358 int items_index = 0;
2359 int items_slots = 0;
2361 current = file_buffer_of_window (window);
2363 for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2367 int this_is_the_current_fb;
2369 fb = info_loaded_files[fbi];
2370 this_is_the_current_fb = (current == fb);
2372 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2373 entry->filename = entry->nodename = (char *)NULL;
2374 entry->label = (char *)xmalloc (4 + strlen (fb->filename));
2375 sprintf (entry->label, "(%s)*", fb->filename);
2377 add_pointer_to_array
2378 (entry, items_index, items, items_slots, 10, REFERENCE *);
2382 for (i = 0; fb->tags[i]; i++)
2384 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2385 entry->filename = entry->nodename = (char *)NULL;
2386 if (this_is_the_current_fb)
2387 entry->label = xstrdup (fb->tags[i]->nodename);
2390 entry->label = (char *) xmalloc
2391 (4 + strlen (fb->filename) +
2392 strlen (fb->tags[i]->nodename));
2393 sprintf (entry->label, "(%s)%s",
2394 fb->filename, fb->tags[i]->nodename);
2397 add_pointer_to_array
2398 (entry, items_index, items, items_slots, 100, REFERENCE *);
2402 line = info_read_maybe_completing (window, _("Goto node: "), items);
2403 info_free_references (items);
2405 #else /* !GOTO_COMPLETES */
2406 line = info_read_in_echo_area (window, _("Goto node: "));
2407 #endif /* !GOTO_COMPLETES */
2409 /* If the user aborted, quit now. */
2412 info_abort_key (window, 0, 0);
2416 canonicalize_whitespace (line);
2419 info_parse_and_select (line, window);
2422 if (!info_error_was_printed)
2423 window_clear_echo_area ();
2426 /* Follow the menu list in MENUS (list of strings terminated by a NULL
2427 entry) from INITIAL_NODE. If can't continue at any point (no menu or
2428 no menu entry for the next item), return the node so far -- that
2429 might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will
2430 be set to the error message and argument for message, otherwise they
2434 info_follow_menus (initial_node, menus, errstr, errarg1, errarg2)
2437 char **errstr, **errarg1, **errarg2;
2440 *errstr = *errarg1 = *errarg2 = NULL;
2442 for (; *menus; menus++)
2444 static char *first_arg = NULL;
2447 char *arg = *menus; /* Remember the name of the menu entry we want. */
2449 /* A leading space is certainly NOT part of a node name. Most
2450 probably, they typed a space after the separating comma. The
2451 strings in menus[] have their whitespace canonicalized, so
2452 there's at most one space to ignore. */
2458 /* Build and return a list of the menu items in this node. */
2459 menu = info_menu_of_node (initial_node);
2461 /* If no menu item in this node, stop here, but let the user
2462 continue to use Info. Perhaps they wanted this node and didn't
2466 if (arg == first_arg)
2468 node = make_manpage_node (first_arg);
2470 goto maybe_got_node;
2472 *errstr = _("No menu in node `%s'.");
2473 *errarg1 = node_printed_rep (initial_node);
2474 return initial_node;
2477 /* Find the specified menu item. */
2478 entry = info_get_labeled_reference (arg, menu);
2480 /* If the item wasn't found, search the list sloppily. Perhaps this
2481 user typed "buffer" when they really meant "Buffers". */
2485 int best_guess = -1;
2487 for (i = 0; (entry = menu[i]); i++)
2489 if (strcasecmp (entry->label, arg) == 0)
2492 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2496 if (!entry && best_guess != -1)
2497 entry = menu[best_guess];
2500 /* If we still failed to find the reference, start Info with the current
2501 node anyway. It is probably a misspelling. */
2504 if (arg == first_arg)
2506 /* Maybe they typed "info foo" instead of "info -f foo". */
2507 node = info_get_node (first_arg, 0);
2509 add_file_directory_to_path (first_arg);
2511 node = make_manpage_node (first_arg);
2513 goto maybe_got_node;
2516 info_free_references (menu);
2517 *errstr = _("No menu item `%s' in node `%s'.");
2519 *errarg2 = node_printed_rep (initial_node);
2520 return initial_node;
2523 /* We have found the reference that the user specified. If no
2524 filename in this reference, define it. */
2525 if (!entry->filename)
2526 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2527 : initial_node->filename);
2529 /* Try to find this node. */
2530 node = info_get_node (entry->filename, entry->nodename);
2531 if (!node && arg == first_arg)
2533 node = make_manpage_node (first_arg);
2535 goto maybe_got_node;
2538 /* Since we cannot find it, try using the label of the entry as a
2539 file, i.e., "(LABEL)Top". */
2540 if (!node && entry->nodename
2541 && strcmp (entry->label, entry->nodename) == 0)
2542 node = info_get_node (entry->label, "Top");
2547 *errstr = _("Unable to find node referenced by `%s' in `%s'.");
2548 *errarg1 = xstrdup (entry->label);
2549 *errarg2 = node_printed_rep (initial_node);
2550 info_free_references (menu);
2551 return initial_node;
2554 info_free_references (menu);
2556 /* Success. Go round the loop again. */
2557 free (initial_node);
2558 initial_node = node;
2561 return initial_node;
2564 /* Split STR into individual node names by writing null bytes in wherever
2565 there are commas and constructing a list of the resulting pointers.
2566 (We can do this since STR has had canonicalize_whitespace called on it.)
2567 Return array terminated with NULL. */
2570 split_list_of_nodenames (str)
2574 char **nodes = xmalloc (len * sizeof (char *));
2576 nodes[len - 2] = str;
2582 *str++ = 0; /* get past the null byte */
2584 nodes = xrealloc (nodes, len * sizeof (char *));
2585 nodes[len - 2] = str;
2589 nodes[len - 1] = NULL;
2595 /* Read a line of input which is a sequence of menus (starting from
2596 dir), and follow them. */
2597 DECLARE_INFO_COMMAND (info_menu_sequence,
2598 _("Read a list of menus starting from dir and follow them"))
2600 char *line = info_read_in_echo_area (window, _("Follow menus: "));
2602 /* If the user aborted, quit now. */
2605 info_abort_key (window, 0, 0);
2609 canonicalize_whitespace (line);
2613 char *errstr, *errarg1, *errarg2;
2614 NODE *dir_node = info_get_node (NULL, NULL);
2615 char **nodes = split_list_of_nodenames (line);
2618 /* If DIR_NODE is NULL, they might be reading a file directly,
2619 like in "info -d . -f ./foo". Try using "Top" instead. */
2622 char *file_name = window->node->parent;
2625 file_name = window->node->filename;
2626 dir_node = info_get_node (file_name, NULL);
2629 /* If we still cannot find the starting point, give up.
2630 We cannot allow a NULL pointer inside info_follow_menus. */
2632 info_error (msg_cant_find_node, "Top");
2635 = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
2639 info_set_node_of_window (1, window, node);
2641 info_error (errstr, errarg1, errarg2);
2645 if (!info_error_was_printed)
2646 window_clear_echo_area ();
2649 /* Search the menu MENU for a (possibly mis-spelled) entry ARG.
2650 Return the menu entry, or the best guess for what they meant by ARG,
2651 or NULL if there's nothing in this menu seems to fit the bill.
2652 If EXACT is non-zero, allow only exact matches. */
2654 entry_in_menu (arg, menu, exact)
2661 /* First, try to find the specified menu item verbatim. */
2662 entry = info_get_labeled_reference (arg, menu);
2664 /* If the item wasn't found, search the list sloppily. Perhaps we
2665 have "Option Summary", but ARG is "option". */
2666 if (!entry && !exact)
2669 int best_guess = -1;
2671 for (i = 0; (entry = menu[i]); i++)
2673 if (strcasecmp (entry->label, arg) == 0)
2676 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2680 if (!entry && best_guess != -1)
2681 entry = menu[best_guess];
2687 /* Find the node that is the best candidate to list the PROGRAM's
2688 invocation info and its command-line options, by looking for menu
2689 items and chains of menu items with characteristic names. */
2691 info_intuit_options_node (window, initial_node, program)
2696 /* The list of node names typical for GNU manuals where the program
2697 usage and specifically the command-line arguments are described.
2698 This is pure heuristics. I gathered these node names by looking
2699 at all the Info files I could put my hands on. If you are
2700 looking for evidence to complain to the GNU project about
2701 non-uniform style of documentation, here you have your case! */
2702 static const char *invocation_nodes[] = {
2705 "Preliminaries", /* m4 has Invoking under Preliminaries! */
2707 "Command Arguments",/* Emacs */
2711 "Option ", /* e.g. "Option Summary" */
2713 "All options", /* tar, paxutils */
2715 "%s cmdline", /* ar */
2716 "%s", /* last resort */
2721 const char **try_node;
2723 /* We keep looking deeper and deeper in the menu structure until
2724 there are no more menus or no menu items from the above list.
2725 Some manuals have the invocation node sitting 3 or 4 levels deep
2726 in the menu hierarchy... */
2727 for (node = initial_node; node; initial_node = node)
2731 /* Build and return a list of the menu items in this node. */
2732 menu = info_menu_of_node (initial_node);
2734 /* If no menu item in this node, stop here. Perhaps this node
2735 is the one they need. */
2739 /* Look for node names typical for usage nodes in this menu. */
2740 for (try_node = invocation_nodes; *try_node; try_node++)
2744 sprintf (nodename, *try_node, program);
2745 /* The last resort "%s" is dangerous, so we restrict it
2746 to exact matches here. */
2747 entry = entry_in_menu (nodename, menu,
2748 strcmp (*try_node, "%s") == 0);
2756 if (!entry->filename)
2757 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2758 : initial_node->filename);
2759 /* Try to find this node. */
2760 node = info_get_node (entry->filename, entry->nodename);
2761 info_free_references (menu);
2766 /* We've got our best shot at the invocation node. Now select it. */
2768 info_set_node_of_window (1, window, initial_node);
2769 if (!info_error_was_printed)
2770 window_clear_echo_area ();
2773 /* Given a name of an Info file, find the name of the package it
2774 describes by removing the leading directories and extensions. */
2776 program_name_from_file_name (file_name)
2780 char *program_name = xstrdup (filename_non_directory (file_name));
2782 for (i = strlen (program_name) - 1; i > 0; i--)
2783 if (program_name[i] == '.'
2784 && (FILENAME_CMPN (program_name + i, ".info", 5) == 0
2785 || FILENAME_CMPN (program_name + i, ".inf", 4) == 0
2787 || FILENAME_CMPN (program_name + i, ".i", 2) == 0
2789 || isdigit (program_name[i + 1]))) /* a man page foo.1 */
2791 program_name[i] = 0;
2794 return program_name;
2797 DECLARE_INFO_COMMAND (info_goto_invocation_node,
2798 _("Find the node describing program invocation"))
2800 char *invocation_prompt = _("Find Invocation node of [%s]: ");
2801 char *program_name, *line;
2802 char *default_program_name, *prompt, *file_name;
2805 /* Intuit the name of the program they are likely to want.
2806 We use the file name of the current Info file as a hint. */
2807 file_name = window->node->parent ? window->node->parent
2808 : window->node->filename;
2809 default_program_name = program_name_from_file_name (file_name);
2811 prompt = (char *)xmalloc (strlen (default_program_name) +
2812 strlen (invocation_prompt));
2813 sprintf (prompt, invocation_prompt, default_program_name);
2814 line = info_read_in_echo_area (window, prompt);
2822 program_name = line;
2824 program_name = default_program_name;
2826 /* In interactive usage they'd probably expect us to begin looking
2827 from the Top node. */
2828 top_node = info_get_node (file_name, NULL);
2830 info_error (msg_cant_find_node, "Top");
2832 info_intuit_options_node (window, top_node, program_name);
2834 free (default_program_name);
2837 #if defined (HANDLE_MAN_PAGES)
2838 DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
2842 line = info_read_in_echo_area (window, _("Get Manpage: "));
2846 info_abort_key (window, 0, 0);
2850 canonicalize_whitespace (line);
2856 goto_command = (char *)xmalloc
2857 (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
2859 sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
2861 info_parse_and_select (goto_command, window);
2862 free (goto_command);
2866 if (!info_error_was_printed)
2867 window_clear_echo_area ();
2869 #endif /* HANDLE_MAN_PAGES */
2871 /* Move to the "Top" node in this file. */
2872 DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
2874 info_parse_and_select ("Top", window);
2877 /* Move to the node "(dir)Top". */
2878 DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
2880 info_parse_and_select ("(dir)Top", window);
2884 /* Read the name of a node to kill. The list of available nodes comes
2885 from the nodes appearing in the current window configuration. */
2887 read_nodename_to_kill (window)
2892 INFO_WINDOW *info_win;
2893 REFERENCE **menu = NULL;
2894 int menu_index = 0, menu_slots = 0;
2895 char *default_nodename = xstrdup (active_window->node->nodename);
2896 char *prompt = xmalloc (40 + strlen (default_nodename));
2898 sprintf (prompt, _("Kill node (%s): "), default_nodename);
2900 for (iw = 0; (info_win = info_windows[iw]); iw++)
2902 REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2903 entry->label = xstrdup (info_win->window->node->nodename);
2904 entry->filename = entry->nodename = (char *)NULL;
2906 add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
2910 nodename = info_read_completing_in_echo_area (window, prompt, menu);
2912 info_free_references (menu);
2913 if (nodename && !*nodename)
2916 nodename = default_nodename;
2919 free (default_nodename);
2925 /* Delete NODENAME from this window, showing the most
2926 recently selected node in this window. */
2928 kill_node (window, nodename)
2933 INFO_WINDOW *info_win;
2936 /* If there is no nodename to kill, quit now. */
2939 info_abort_key (window, 0, 0);
2943 /* If there is a nodename, find it in our window list. */
2944 for (iw = 0; (info_win = info_windows[iw]); iw++)
2945 if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
2946 && info_win->window == window)
2952 info_error (_("Cannot kill node `%s'"), nodename);
2954 window_clear_echo_area ();
2959 /* If there are no more nodes left anywhere to view, complain and exit. */
2960 if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
2962 info_error (_("Cannot kill the last node"));
2966 /* INFO_WIN contains the node that the user wants to stop viewing. Delete
2967 this node from the list of nodes previously shown in this window. */
2968 for (i = info_win->current; i < info_win->nodes_index; i++)
2969 info_win->nodes[i] = info_win->nodes[i + 1];
2971 /* There is one less node in this window's history list. */
2972 info_win->nodes_index--;
2974 /* Make this window show the most recent history node. */
2975 info_win->current = info_win->nodes_index - 1;
2977 /* If there aren't any nodes left in this window, steal one from the
2979 if (info_win->current < 0)
2981 INFO_WINDOW *stealer;
2985 if (info_windows[iw + 1])
2986 stealer = info_windows[iw + 1];
2988 stealer = info_windows[0];
2990 /* If the node being displayed in the next window is not the most
2991 recently loaded one, get the most recently loaded one. */
2992 if ((stealer->nodes_index - 1) != stealer->current)
2993 which = stealer->nodes_index - 1;
2995 /* Else, if there is another node behind the stealers current node,
2997 else if (stealer->current > 0)
2998 which = stealer->current - 1;
3000 /* Else, just use the node appearing in STEALER's window. */
3002 which = stealer->current;
3004 /* Copy this node. */
3006 NODE *copy = xmalloc (sizeof (NODE));
3008 temp = stealer->nodes[which];
3009 point = stealer->points[which];
3010 pagetop = stealer->pagetops[which];
3012 copy->filename = temp->filename;
3013 copy->parent = temp->parent;
3014 copy->nodename = temp->nodename;
3015 copy->contents = temp->contents;
3016 copy->nodelen = temp->nodelen;
3017 copy->flags = temp->flags;
3018 copy->display_pos = temp->display_pos;
3023 window_set_node_of_window (info_win->window, temp);
3024 window->point = point;
3025 window->pagetop = pagetop;
3026 remember_window_and_node (info_win->window, temp);
3030 temp = info_win->nodes[info_win->current];
3031 temp->display_pos = info_win->points[info_win->current];
3032 window_set_node_of_window (info_win->window, temp);
3035 if (!info_error_was_printed)
3036 window_clear_echo_area ();
3038 if (auto_footnotes_p)
3039 info_get_or_remove_footnotes (window);
3042 /* Kill current node, thus going back one in the node history. I (karl)
3043 do not think this is completely correct yet, because of the
3044 window-changing stuff in kill_node, but it's a lot better than the
3045 previous implementation, which did not account for nodes being
3046 visited twice at all. */
3047 DECLARE_INFO_COMMAND (info_history_node,
3048 _("Select the most recently selected node"))
3050 kill_node (window, active_window->node->nodename);
3053 /* Kill named node. */
3054 DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
3056 char *nodename = read_nodename_to_kill (window);
3057 kill_node (window, nodename);
3061 /* Read the name of a file and select the entire file. */
3062 DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
3066 line = info_read_in_echo_area (window, _("Find file: "));
3069 info_abort_key (active_window, 1, 0);
3077 node = info_get_node (line, "*");
3080 if (info_recent_file_error)
3081 info_error (info_recent_file_error);
3083 info_error (_("Cannot find `%s'."), line);
3086 info_set_node_of_window (1, window, node);
3091 if (!info_error_was_printed)
3092 window_clear_echo_area ();
3095 /* **************************************************************** */
3097 /* Dumping and Printing Nodes */
3099 /* **************************************************************** */
3101 #define VERBOSE_NODE_DUMPING
3102 static void write_node_to_stream ();
3103 static void dump_node_to_stream ();
3104 static void initialize_dumping ();
3106 /* Dump the nodes specified by FILENAME and NODENAMES to the file named
3107 in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3108 the nodes which appear in the menu of each node dumped. */
3110 dump_nodes_to_file (filename, nodenames, output_filename, dump_subnodes)
3113 char *output_filename;
3117 FILE *output_stream;
3119 /* Get the stream to print the nodes to. Special case of an output
3120 filename of "-" means to dump the nodes to stdout. */
3121 if (strcmp (output_filename, "-") == 0)
3122 output_stream = stdout;
3124 output_stream = fopen (output_filename, "w");
3128 info_error (_("Could not create output file `%s'."), output_filename);
3132 /* Print each node to stream. */
3133 initialize_dumping ();
3134 for (i = 0; nodenames[i]; i++)
3135 dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
3137 if (output_stream != stdout)
3138 fclose (output_stream);
3140 #if defined (VERBOSE_NODE_DUMPING)
3141 info_error (_("Done."));
3142 #endif /* VERBOSE_NODE_DUMPING */
3145 /* A place to remember already dumped nodes. */
3146 static char **dumped_already = (char **)NULL;
3147 static int dumped_already_index = 0;
3148 static int dumped_already_slots = 0;
3151 initialize_dumping ()
3153 dumped_already_index = 0;
3156 /* Get and print the node specified by FILENAME and NODENAME to STREAM.
3157 If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
3158 in the menu of each node dumped. */
3160 dump_node_to_stream (filename, nodename, stream, dump_subnodes)
3161 char *filename, *nodename;
3168 node = info_get_node (filename, nodename);
3172 if (info_recent_file_error)
3173 info_error (info_recent_file_error);
3176 if (filename && *nodename != '(')
3177 info_error (msg_cant_file_node, filename_non_directory (filename),
3180 info_error (msg_cant_find_node, nodename);
3185 /* If we have already dumped this node, don't dump it again. */
3186 for (i = 0; i < dumped_already_index; i++)
3187 if (strcmp (node->nodename, dumped_already[i]) == 0)
3192 add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
3193 dumped_already_slots, 50, char *);
3195 #if defined (VERBOSE_NODE_DUMPING)
3196 /* Maybe we should print some information about the node being output. */
3197 info_error (_("Writing node %s..."), node_printed_rep (node));
3198 #endif /* VERBOSE_NODE_DUMPING */
3200 write_node_to_stream (node, stream);
3202 /* If we are dumping subnodes, get the list of menu items in this node,
3203 and dump each one recursively. */
3206 REFERENCE **menu = (REFERENCE **)NULL;
3208 /* If this node is an Index, do not dump the menu references. */
3209 if (string_in_line ("Index", node->nodename) == -1)
3210 menu = info_menu_of_node (node);
3214 for (i = 0; menu[i]; i++)
3216 /* We don't dump Info files which are different than the
3218 if (!menu[i]->filename)
3220 (filename, menu[i]->nodename, stream, dump_subnodes);
3222 info_free_references (menu);
3229 /* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3230 the nodes which appear in the menu of each node dumped. */
3232 dump_node_to_file (node, filename, dump_subnodes)
3237 FILE *output_stream;
3238 char *nodes_filename;
3240 /* Get the stream to print this node to. Special case of an output
3241 filename of "-" means to dump the nodes to stdout. */
3242 if (strcmp (filename, "-") == 0)
3243 output_stream = stdout;
3245 output_stream = fopen (filename, "w");
3249 info_error (_("Could not create output file `%s'."), filename);
3254 nodes_filename = node->parent;
3256 nodes_filename = node->filename;
3258 initialize_dumping ();
3260 (nodes_filename, node->nodename, output_stream, dump_subnodes);
3262 if (output_stream != stdout)
3263 fclose (output_stream);
3265 #if defined (VERBOSE_NODE_DUMPING)
3266 info_error (_("Done."));
3267 #endif /* VERBOSE_NODE_DUMPING */
3270 #if !defined (DEFAULT_INFO_PRINT_COMMAND)
3271 # define DEFAULT_INFO_PRINT_COMMAND "lpr"
3272 #endif /* !DEFAULT_INFO_PRINT_COMMAND */
3274 DECLARE_INFO_COMMAND (info_print_node,
3275 _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
3277 print_node (window->node);
3280 /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3286 char *print_command = getenv ("INFO_PRINT_COMMAND");
3289 if (!print_command || !*print_command)
3290 print_command = DEFAULT_INFO_PRINT_COMMAND;
3292 /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
3293 (default) text mode, since the printer drivers there need to see
3294 DOS-style CRLF pairs at the end of each line.
3296 FIXME: if we are to support Mac-style text files, we might need
3297 to convert the text here. */
3299 /* INFO_PRINT_COMMAND which says ">file" means write to that file.
3300 Presumably, the name of the file is the local printer device. */
3301 if (*print_command == '>')
3302 printer_pipe = fopen (++print_command, "w");
3305 printer_pipe = popen (print_command, "w");
3311 info_error (_("Cannot open pipe to `%s'."), print_command);
3315 #if defined (VERBOSE_NODE_DUMPING)
3316 /* Maybe we should print some information about the node being output. */
3317 info_error (_("Printing node %s..."), node_printed_rep (node));
3318 #endif /* VERBOSE_NODE_DUMPING */
3320 write_node_to_stream (node, printer_pipe);
3322 pclose (printer_pipe);
3324 fclose (printer_pipe);
3326 #if defined (VERBOSE_NODE_DUMPING)
3327 info_error (_("Done."));
3328 #endif /* VERBOSE_NODE_DUMPING */
3332 write_node_to_stream (node, stream)
3336 fwrite (node->contents, 1, node->nodelen, stream);
3339 /* **************************************************************** */
3341 /* Info Searching Commands */
3343 /* **************************************************************** */
3345 /* Variable controlling the garbage collection of files briefly visited
3346 during searches. Such files are normally gc'ed, unless they were
3347 compressed to begin with. If this variable is non-zero, it says
3348 to gc even those file buffer contents which had to be uncompressed. */
3349 int gc_compressed_files = 0;
3351 static void info_gc_file_buffers ();
3352 static void info_search_1 ();
3354 static char *search_string = (char *)NULL;
3355 static int search_string_index = 0;
3356 static int search_string_size = 0;
3357 static int isearch_is_active = 0;
3359 static int last_search_direction = 0;
3360 static int last_search_case_sensitive = 0;
3362 /* Return the file buffer which belongs to WINDOW's node. */
3364 file_buffer_of_window (window)
3367 /* If this window has no node, then it has no file buffer. */
3369 return ((FILE_BUFFER *)NULL);
3371 if (window->node->parent)
3372 return (info_find_file (window->node->parent));
3374 if (window->node->filename)
3375 return (info_find_file (window->node->filename));
3377 return ((FILE_BUFFER *)NULL);
3380 /* Search for STRING in NODE starting at START. Return -1 if the string
3381 was not found, or the location of the string if it was. If WINDOW is
3382 passed as non-null, set the window's node to be NODE, its point to be
3383 the found string, and readjust the window's pagetop. Final argument
3384 DIR says which direction to search in. If it is positive, search
3385 forward, else backwards. */
3387 info_search_in_node (string, node, start, window, dir, case_sensitive)
3392 int dir, case_sensitive;
3394 SEARCH_BINDING binding;
3397 binding.buffer = node->contents;
3398 binding.start = start;
3399 binding.end = node->nodelen;
3401 if (!case_sensitive)
3402 binding.flags |= S_FoldCase;
3407 binding.flags |= S_SkipDest;
3410 if (binding.start < 0)
3413 /* For incremental searches, we always wish to skip past the string. */
3414 if (isearch_is_active)
3415 binding.flags |= S_SkipDest;
3417 offset = search (string, &binding);
3419 if (offset != -1 && window)
3421 set_remembered_pagetop_and_point (window);
3422 if (window->node != node)
3423 window_set_node_of_window (window, node);
3424 window->point = offset;
3425 window_adjust_pagetop (window);
3430 /* Search NODE, looking for the largest possible match of STRING. Start the
3431 search at START. Return the absolute position of the match, or -1, if
3432 no part of the string could be found. */
3434 info_target_search_node (node, string, start)
3443 target = xstrdup (string);
3444 i = strlen (target);
3446 /* Try repeatedly searching for this string while removing words from
3451 offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
3456 /* Delete the last word from TARGET. */
3457 for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
3463 /* Search for STRING starting in WINDOW at point. If the string is found
3464 in this node, set point to that position. Otherwise, get the file buffer
3465 associated with WINDOW's node, and search through each node in that file.
3466 If the search fails, return non-zero, else zero. Side-effect window
3467 leaving the node and point where the string was found current. */
3469 info_search_internal (string, window, dir, case_sensitive)
3472 int dir, case_sensitive;
3475 FILE_BUFFER *file_buffer;
3476 char *initial_nodename;
3477 long ret, start = 0;
3479 file_buffer = file_buffer_of_window (window);
3480 initial_nodename = window->node->nodename;
3482 /* This used to begin from window->point, unless this was a repeated
3483 search command. But invoking search with an argument loses with
3484 that logic, since info_last_executed_command is then set to
3485 info_add_digit_to_numeric_arg. I think there's no sense in
3486 ``finding'' a string that is already under the cursor, anyway. */
3487 ret = info_search_in_node
3488 (string, window->node, window->point + dir, window, dir,
3494 if (!echo_area_is_active && !isearch_is_active)
3495 window_clear_echo_area ();
3499 /* The string wasn't found in the current node. Search through the
3500 window's file buffer, iff the current node is not "*". */
3501 if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
3504 /* If this file has tags, search through every subfile, starting at
3505 this node's subfile and node. Otherwise, search through the
3506 file's node list. */
3507 if (file_buffer->tags)
3509 register int current_tag, number_of_tags;
3513 /* Find number of tags and current tag. */
3514 last_subfile = (char *)NULL;
3515 for (i = 0; file_buffer->tags[i]; i++)
3516 if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
3519 last_subfile = file_buffer->tags[i]->filename;
3524 /* If there is no last_subfile, our tag wasn't found. */
3528 /* Search through subsequent nodes, wrapping around to the top
3529 of the info file until we find the string or return to this
3530 window's node and point. */
3535 /* Allow C-g to quit the search, failing it if pressed. */
3536 return_if_control_g (-1);
3538 /* Find the next tag that isn't an anchor. */
3539 for (i = current_tag + dir; i != current_tag; i += dir)
3542 i = number_of_tags - 1;
3543 else if (i == number_of_tags)
3546 tag = file_buffer->tags[i];
3547 if (tag->nodelen != 0)
3551 /* If we got past out starting point, bail out. */
3552 if (i == current_tag)
3556 if (!echo_area_is_active && (last_subfile != tag->filename))
3558 window_message_in_echo_area
3559 (_("Searching subfile %s ..."),
3560 filename_non_directory (tag->filename));
3562 last_subfile = tag->filename;
3565 node = info_get_node (file_buffer->filename, tag->nodename);
3569 /* If not doing i-search... */
3570 if (!echo_area_is_active)
3572 if (info_recent_file_error)
3573 info_error (info_recent_file_error);
3575 info_error (msg_cant_file_node,
3576 filename_non_directory (file_buffer->filename),
3583 start = tag->nodelen;
3586 info_search_in_node (string, node, start, window, dir,
3589 /* Did we find the string in this node? */
3593 remember_window_and_node (window, node);
3594 if (!echo_area_is_active)
3595 window_clear_echo_area ();
3599 /* No. Free this node, and make sure that we haven't passed
3600 our starting point. */
3603 if (strcmp (initial_nodename, tag->nodename) == 0)
3610 DECLARE_INFO_COMMAND (info_search_case_sensitively,
3611 _("Read a string and search for it case-sensitively"))
3613 last_search_direction = count > 0 ? 1 : -1;
3614 last_search_case_sensitive = 1;
3615 info_search_1 (window, count, key, 1, 1);
3618 DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
3620 last_search_direction = count > 0 ? 1 : -1;
3621 last_search_case_sensitive = 0;
3622 info_search_1 (window, count, key, 0, 1);
3625 DECLARE_INFO_COMMAND (info_search_backward,
3626 _("Read a string and search backward for it"))
3628 last_search_direction = count > 0 ? -1 : 1;
3629 last_search_case_sensitive = 0;
3630 info_search_1 (window, -count, key, 0, 1);
3634 info_search_1 (window, count, key, case_sensitive, ask_for_string)
3641 char *line, *prompt;
3642 int result, old_pagetop;
3654 count = 1; /* for backward compatibility */
3657 /* Read a string from the user, defaulting the search to SEARCH_STRING. */
3660 search_string = (char *)xmalloc (search_string_size = 100);
3661 search_string[0] = '\0';
3666 prompt = (char *)xmalloc (50 + strlen (search_string));
3668 sprintf (prompt, _("%s%sfor string [%s]: "),
3669 direction < 0 ? _("Search backward") : _("Search"),
3670 case_sensitive ? _(" case-sensitively ") : _(" "),
3673 line = info_read_in_echo_area (window, prompt);
3684 if (strlen (line) + 1 > search_string_size)
3685 search_string = (char *) xrealloc
3686 (search_string, (search_string_size += 50 + strlen (line)));
3688 strcpy (search_string, line);
3689 search_string_index = strlen (line);
3694 /* If the search string includes upper-case letters, make the search
3696 if (case_sensitive == 0)
3697 for (line = search_string; *line; line++)
3698 if (isupper (*line))
3704 old_pagetop = active_window->pagetop;
3705 for (result = 0; result == 0 && count--; )
3706 result = info_search_internal (search_string,
3707 active_window, direction, case_sensitive);
3709 if (result != 0 && !info_error_was_printed)
3710 info_error (_("Search failed."));
3711 else if (old_pagetop != active_window->pagetop)
3715 new_pagetop = active_window->pagetop;
3716 active_window->pagetop = old_pagetop;
3717 set_window_pagetop (active_window, new_pagetop);
3718 if (auto_footnotes_p)
3719 info_get_or_remove_footnotes (active_window);
3722 /* Perhaps free the unreferenced file buffers that were searched, but
3724 info_gc_file_buffers ();
3727 DECLARE_INFO_COMMAND (info_search_next,
3728 _("Repeat last search in the same direction"))
3730 if (!last_search_direction)
3731 info_error (_("No previous search string"));
3733 info_search_1 (window, last_search_direction * count,
3734 key, last_search_case_sensitive, 0);
3737 DECLARE_INFO_COMMAND (info_search_previous,
3738 _("Repeat last search in the reverse direction"))
3740 if (!last_search_direction)
3741 info_error (_("No previous search string"));
3743 info_search_1 (window, -last_search_direction * count,
3744 key, last_search_case_sensitive, 0);
3747 /* **************************************************************** */
3749 /* Incremental Searching */
3751 /* **************************************************************** */
3753 static void incremental_search ();
3755 DECLARE_INFO_COMMAND (isearch_forward,
3756 _("Search interactively for a string as you type it"))
3758 incremental_search (window, count, key);
3761 DECLARE_INFO_COMMAND (isearch_backward,
3762 _("Search interactively for a string as you type it"))
3764 incremental_search (window, -count, key);
3767 /* Incrementally search for a string as it is typed. */
3768 /* The last accepted incremental search string. */
3769 static char *last_isearch_accepted = (char *)NULL;
3771 /* The current incremental search string. */
3772 static char *isearch_string = (char *)NULL;
3773 static int isearch_string_index = 0;
3774 static int isearch_string_size = 0;
3775 static unsigned char isearch_terminate_search_key = ESC;
3777 /* Structure defining the current state of an incremental search. */
3779 WINDOW_STATE_DECL; /* The node, pagetop and point. */
3780 int search_index; /* Offset of the last char in the search string. */
3781 int direction; /* The direction that this search is heading in. */
3782 int failing; /* Whether or not this search failed. */
3785 /* Array of search states. */
3786 static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
3787 static int isearch_states_index = 0;
3788 static int isearch_states_slots = 0;
3790 /* Push the state of this search. */
3792 push_isearch (window, search_index, direction, failing)
3794 int search_index, direction, failing;
3796 SEARCH_STATE *state;
3798 state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
3799 window_get_state (window, state);
3800 state->search_index = search_index;
3801 state->direction = direction;
3802 state->failing = failing;
3804 add_pointer_to_array (state, isearch_states_index, isearch_states,
3805 isearch_states_slots, 20, SEARCH_STATE *);
3808 /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3810 pop_isearch (window, search_index, direction, failing)
3812 int *search_index, *direction, *failing;
3814 SEARCH_STATE *state;
3816 if (isearch_states_index)
3818 isearch_states_index--;
3819 state = isearch_states[isearch_states_index];
3820 window_set_state (window, state);
3821 *search_index = state->search_index;
3822 *direction = state->direction;
3823 *failing = state->failing;
3826 isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
3830 /* Free the memory used by isearch_states. */
3832 free_isearch_states ()
3836 for (i = 0; i < isearch_states_index; i++)
3838 free (isearch_states[i]);
3839 isearch_states[i] = (SEARCH_STATE *)NULL;
3841 isearch_states_index = 0;
3844 /* Display the current search in the echo area. */
3846 show_isearch_prompt (dir, string, failing_p)
3848 unsigned char *string;
3852 char *prefix, *prompt, *p_rep;
3853 int prompt_len, p_rep_index, p_rep_size;
3856 prefix = _("I-search backward: ");
3858 prefix = _("I-search: ");
3860 p_rep_index = p_rep_size = 0;
3861 p_rep = (char *)NULL;
3862 for (i = 0; string[i]; i++)
3868 case ' ': rep = " "; break;
3869 case LFD: rep = "\\n"; break;
3870 case TAB: rep = "\\t"; break;
3872 rep = pretty_keyname (string[i]);
3874 if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3875 p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3877 strcpy (p_rep + p_rep_index, rep);
3878 p_rep_index += strlen (rep);
3881 prompt_len = strlen (prefix) + p_rep_index + 20;
3882 prompt = (char *)xmalloc (prompt_len);
3883 sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
3884 p_rep ? p_rep : "");
3886 window_message_in_echo_area ("%s", prompt);
3889 display_cursor_at_point (active_window);
3893 incremental_search (window, count, ignore)
3896 unsigned char ignore;
3899 int last_search_result, search_result, dir;
3900 SEARCH_STATE mystate, orig_state;
3902 int case_sensitive = 0;
3909 last_search_result = search_result = 0;
3911 window_get_state (window, &orig_state);
3913 isearch_string_index = 0;
3914 if (!isearch_string_size)
3915 isearch_string = (char *)xmalloc (isearch_string_size = 50);
3917 /* Show the search string in the echo area. */
3918 isearch_string[isearch_string_index] = '\0';
3919 show_isearch_prompt (dir, isearch_string, search_result);
3921 isearch_is_active = 1;
3923 while (isearch_is_active)
3925 VFunction *func = (VFunction *)NULL;
3928 /* If a recent display was interrupted, then do the redisplay now if
3929 it is convenient. */
3930 if (!info_any_buffered_input_p () && display_was_interrupted_p)
3932 display_update_one_window (window);
3933 display_cursor_at_point (active_window);
3936 /* Read a character and dispatch on it. */
3937 key = info_get_input_char ();
3938 window_get_state (window, &mystate);
3942 /* User wants to delete one level of search? */
3943 if (!isearch_states_index)
3945 terminal_ring_bell ();
3951 (window, &isearch_string_index, &dir, &search_result);
3952 isearch_string[isearch_string_index] = '\0';
3953 show_isearch_prompt (dir, isearch_string, search_result);
3957 else if (key == Control ('q'))
3959 key = info_get_input_char ();
3963 /* We are about to search again, or quit. Save the current search. */
3964 push_isearch (window, isearch_string_index, dir, search_result);
3967 goto insert_and_search;
3969 if (!Meta_p (key) || key > 32)
3971 func = InfoFunction(window->keymap[key].function);
3973 if (isprint (key) || func == (VFunction *)NULL)
3977 if (isearch_string_index + 2 >= isearch_string_size)
3978 isearch_string = (char *)xrealloc
3979 (isearch_string, isearch_string_size += 100);
3981 isearch_string[isearch_string_index++] = key;
3982 isearch_string[isearch_string_index] = '\0';
3985 else if (func == isearch_forward || func == isearch_backward)
3987 /* If this key invokes an incremental search, then this
3988 means that we will either search again in the same
3989 direction, search again in the reverse direction, or
3990 insert the last search string that was accepted through
3991 incremental searching. */
3992 if ((func == isearch_forward && dir > 0) ||
3993 (func == isearch_backward && dir < 0))
3995 /* If the user has typed no characters, then insert the
3996 last successful search into the current search string. */
3997 if (isearch_string_index == 0)
3999 /* Of course, there must be something to insert. */
4000 if (last_isearch_accepted)
4002 if (strlen (last_isearch_accepted) + 1 >=
4003 isearch_string_size)
4004 isearch_string = (char *)
4005 xrealloc (isearch_string,
4006 isearch_string_size += 10 +
4007 strlen (last_isearch_accepted));
4008 strcpy (isearch_string, last_isearch_accepted);
4009 isearch_string_index = strlen (isearch_string);
4017 /* Search again in the same direction. This means start
4018 from a new place if the last search was successful. */
4019 if (search_result == 0)
4020 window->point += dir;
4025 /* Reverse the direction of the search. */
4029 else if (func == info_abort_key)
4031 /* If C-g pressed, and the search is failing, pop the search
4032 stack back to the last unfailed search. */
4033 if (isearch_states_index && (search_result != 0))
4035 terminal_ring_bell ();
4036 while (isearch_states_index && (search_result != 0))
4038 (window, &isearch_string_index, &dir, &search_result);
4039 isearch_string[isearch_string_index] = '\0';
4040 show_isearch_prompt (dir, isearch_string, search_result);
4052 /* The character is not printable, or it has a function which is
4053 non-null. Exit the search, remembering the search string. If
4054 the key is not the same as the isearch_terminate_search_key,
4055 then push it into pending input. */
4056 if (isearch_string_index && func != info_abort_key)
4058 maybe_free (last_isearch_accepted);
4059 last_isearch_accepted = xstrdup (isearch_string);
4062 /* If the key is the isearch_terminate_search_key, but some buffered
4063 input is pending, it is almost invariably because the ESC key is
4064 actually the beginning of an escape sequence, like in case they
4065 pressed an arrow key. So don't gobble the ESC key, push it back
4066 into pending input. */
4067 /* FIXME: this seems like a kludge! We need a more reliable
4068 mechanism to know when ESC is a separate key and when it is
4069 part of an escape sequence. */
4070 if (key != RET /* Emacs addicts want RET to get lost */
4071 && (key != isearch_terminate_search_key
4072 || info_any_buffered_input_p ()))
4073 info_set_pending_input (key);
4075 if (func == info_abort_key)
4077 if (isearch_states_index)
4078 window_set_state (window, &orig_state);
4081 if (!echo_area_is_active)
4082 window_clear_echo_area ();
4084 if (auto_footnotes_p)
4085 info_get_or_remove_footnotes (active_window);
4087 isearch_is_active = 0;
4091 /* Search for the contents of isearch_string. */
4093 show_isearch_prompt (dir, isearch_string, search_result);
4095 /* If the search string includes upper-case letters, make the
4096 search case-sensitive. */
4097 for (p = isearch_string; *p; p++)
4105 if (search_result == 0)
4107 /* Check to see if the current search string is right here. If
4108 we are looking at it, then don't bother calling the search
4111 ((case_sensitive ? strncmp : strncasecmp)
4112 (window->node->contents + window->point,
4113 isearch_string, isearch_string_index) == 0)) ||
4115 ((window->point - isearch_string_index) >= 0) &&
4116 ((case_sensitive ? strncmp : strncasecmp)
4117 (window->node->contents +
4118 (window->point - (isearch_string_index - 1)),
4119 isearch_string, isearch_string_index) == 0)))
4125 search_result = info_search_internal (isearch_string,
4126 window, dir, case_sensitive);
4129 /* If this search failed, and we didn't already have a failed search,
4130 then ring the terminal bell. */
4131 if (search_result != 0 && last_search_result == 0)
4132 terminal_ring_bell ();
4135 show_isearch_prompt (dir, isearch_string, search_result);
4137 if (search_result == 0)
4139 if ((mystate.node == window->node) &&
4140 (mystate.pagetop != window->pagetop))
4142 int newtop = window->pagetop;
4143 window->pagetop = mystate.pagetop;
4144 set_window_pagetop (window, newtop);
4146 display_update_one_window (window);
4147 display_cursor_at_point (window);
4150 last_search_result = search_result;
4153 /* Free the memory used to remember each search state. */
4154 free_isearch_states ();
4156 /* Perhaps GC some file buffers. */
4157 info_gc_file_buffers ();
4159 /* After searching, leave the window in the correct state. */
4160 if (!echo_area_is_active)
4161 window_clear_echo_area ();
4164 /* GC some file buffers. A file buffer can be gc-ed if there we have
4165 no nodes in INFO_WINDOWS that reference this file buffer's contents.
4166 Garbage collecting a file buffer means to free the file buffers
4169 info_gc_file_buffers ()
4171 register int fb_index, iw_index, i;
4172 register FILE_BUFFER *fb;
4173 register INFO_WINDOW *iw;
4175 if (!info_loaded_files)
4178 for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4180 int fb_referenced_p = 0;
4182 /* If already gc-ed, do nothing. */
4186 /* If this file had to be uncompressed, check to see if we should
4187 gc it. This means that the user-variable "gc-compressed-files"
4189 if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
4192 /* If this file's contents are not gc-able, move on. */
4193 if (fb->flags & N_CannotGC)
4196 /* Check each INFO_WINDOW to see if it has any nodes which reference
4198 for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
4200 for (i = 0; iw->nodes && iw->nodes[i]; i++)
4202 if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4203 (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
4205 fb_referenced_p = 1;
4211 /* If this file buffer wasn't referenced, free its contents. */
4212 if (!fb_referenced_p)
4214 free (fb->contents);
4215 fb->contents = (char *)NULL;
4220 /* **************************************************************** */
4222 /* Traversing and Selecting References */
4224 /* **************************************************************** */
4226 /* Move to the next or previous cross reference in this node. */
4228 info_move_to_xref (window, count, key, dir)
4234 long firstmenu, firstxref;
4235 long nextmenu, nextxref;
4236 long placement = -1;
4238 NODE *node = window->node;
4241 start = node->nodelen;
4243 /* This search is only allowed to fail if there is no menu or cross
4244 reference in the current node. Otherwise, the first menu or xref
4245 found is moved to. */
4247 firstmenu = info_search_in_node
4248 (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4250 /* FIRSTMENU may point directly to the line defining the menu. Skip that
4251 and go directly to the first item. */
4253 if (firstmenu != -1)
4255 char *text = node->contents + firstmenu;
4257 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4258 firstmenu = info_search_in_node
4259 (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0);
4263 info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4265 #if defined (HANDLE_MAN_PAGES)
4266 if ((firstxref == -1) && (node->flags & N_IsManPage))
4268 firstxref = locate_manpage_xref (node, start, dir);
4270 #endif /* HANDLE_MAN_PAGES */
4272 if (firstmenu == -1 && firstxref == -1)
4274 info_error (msg_no_xref_node);
4278 /* There is at least one cross reference or menu entry in this node.
4279 Try hard to find the next available one. */
4281 nextmenu = info_search_in_node
4282 (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4284 nextxref = info_search_in_node
4285 (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4287 #if defined (HANDLE_MAN_PAGES)
4288 if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
4289 nextxref = locate_manpage_xref (node, window->point + dir, dir);
4290 #endif /* HANDLE_MAN_PAGES */
4292 /* Ignore "Menu:" as a menu item. */
4295 char *text = node->contents + nextmenu;
4297 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4298 nextmenu = info_search_in_node
4299 (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
4302 /* If there is both a next menu entry, and a next xref entry, choose the
4303 one which occurs first. Otherwise, select the one which actually
4304 appears in this node following point. */
4305 if (nextmenu != -1 && nextxref != -1)
4307 if (((dir == 1) && (nextmenu < nextxref)) ||
4308 ((dir == -1) && (nextmenu > nextxref)))
4309 placement = nextmenu + 1;
4311 placement = nextxref;
4313 else if (nextmenu != -1)
4314 placement = nextmenu + 1;
4315 else if (nextxref != -1)
4316 placement = nextxref;
4318 /* If there was neither a menu or xref entry appearing in this node after
4319 point, choose the first menu or xref entry appearing in this node. */
4320 if (placement == -1)
4322 if (firstmenu != -1 && firstxref != -1)
4324 if (((dir == 1) && (firstmenu < firstxref)) ||
4325 ((dir == -1) && (firstmenu > firstxref)))
4326 placement = firstmenu + 1;
4328 placement = firstxref;
4330 else if (firstmenu != -1)
4331 placement = firstmenu + 1;
4333 placement = firstxref;
4335 window->point = placement;
4336 window_adjust_pagetop (window);
4337 window->flags |= W_UpdateWindow;
4340 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
4341 _("Move to the previous cross reference"))
4344 info_move_to_prev_xref (window, -count, key);
4346 info_move_to_xref (window, count, key, -1);
4349 DECLARE_INFO_COMMAND (info_move_to_next_xref,
4350 _("Move to the next cross reference"))
4353 info_move_to_next_xref (window, -count, key);
4355 info_move_to_xref (window, count, key, 1);
4358 /* Select the menu item or reference that appears on this line. */
4359 DECLARE_INFO_COMMAND (info_select_reference_this_line,
4360 _("Select reference or menu item appearing on this line"))
4365 line = window->line_starts[window_line_of_point (window)];
4366 orig = window->node;
4368 /* If this line contains a menu item, select that one. */
4369 if (strncmp ("* ", line, 2) == 0)
4370 info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
4372 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
4375 /* **************************************************************** */
4377 /* Miscellaneous Info Commands */
4379 /* **************************************************************** */
4381 /* What to do when C-g is pressed in a window. */
4382 DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
4384 /* If error printing doesn't oridinarily ring the bell, do it now,
4385 since C-g always rings the bell. Otherwise, let the error printer
4387 if (!info_error_rings_bell_p)
4388 terminal_ring_bell ();
4389 info_error (_("Quit"));
4391 info_initialize_numeric_arg ();
4392 info_clear_pending_input ();
4393 info_last_executed_command = (VFunction *)NULL;
4396 /* Move the cursor to the desired line of the window. */
4397 DECLARE_INFO_COMMAND (info_move_to_window_line,
4398 _("Move the cursor to a specific line of the window"))
4402 /* With no numeric argument of any kind, default to the center line. */
4403 if (!info_explicit_arg && count == 1)
4404 line = (window->height / 2) + window->pagetop;
4408 line = (window->height + count) + window->pagetop;
4410 line = window->pagetop + count;
4413 /* If the line doesn't appear in this window, make it do so. */
4414 if ((line - window->pagetop) >= window->height)
4415 line = window->pagetop + (window->height - 1);
4417 /* If the line is too small, make it fit. */
4418 if (line < window->pagetop)
4419 line = window->pagetop;
4421 /* If the selected line is past the bottom of the node, force it back. */
4422 if (line >= window->line_count)
4423 line = window->line_count - 1;
4425 window->point = (window->line_starts[line] - window->node->contents);
4428 /* Clear the screen and redraw its contents. Given a numeric argument,
4429 move the line the cursor is on to the COUNT'th line of the window. */
4430 DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
4432 if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4434 terminal_clear_screen ();
4435 display_clear_display (the_display);
4436 window_mark_chain (windows, W_UpdateWindow);
4437 display_update_display (windows);
4441 int desired_line, point_line;
4444 point_line = window_line_of_point (window) - window->pagetop;
4447 desired_line = window->height + count;
4449 desired_line = count;
4451 if (desired_line < 0)
4454 if (desired_line >= window->height)
4455 desired_line = window->height - 1;
4457 if (desired_line == point_line)
4460 new_pagetop = window->pagetop + (point_line - desired_line);
4462 set_window_pagetop (window, new_pagetop);
4465 /* This command does nothing. It is the fact that a key is bound to it
4466 that has meaning. See the code at the top of info_session (). */
4467 DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
4471 /* **************************************************************** */
4473 /* Reading Keys and Dispatching on Them */
4475 /* **************************************************************** */
4477 /* Declaration only. Special cased in info_dispatch_on_key ().
4478 Doc string is to avoid ugly results with describe_key etc. */
4479 DECLARE_INFO_COMMAND (info_do_lowercase_version,
4480 _("Run command bound to this key's lowercase variant"))
4484 dispatch_error (keyseq)
4489 rep = pretty_keyseq (keyseq);
4491 if (!echo_area_is_active)
4492 info_error (_("Unknown command (%s)."), rep);
4495 char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid")));
4496 sprintf (temp, _("\"%s\" is invalid"), rep);
4497 terminal_ring_bell ();
4498 inform_in_echo_area (temp);
4503 /* Keeping track of key sequences. */
4504 static char *info_keyseq = (char *)NULL;
4505 static int info_keyseq_index = 0;
4506 static int info_keyseq_size = 0;
4507 static int info_keyseq_displayed_p = 0;
4509 /* Initialize the length of the current key sequence. */
4511 initialize_keyseq ()
4513 info_keyseq_index = 0;
4514 info_keyseq_displayed_p = 0;
4517 /* Add CHARACTER to the current key sequence. */
4519 add_char_to_keyseq (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. */
4533 display_info_keyseq (expecting_future_input)
4534 int expecting_future_input;
4538 rep = pretty_keyseq (info_keyseq);
4539 if (expecting_future_input)
4542 if (echo_area_is_active)
4543 inform_in_echo_area (rep);
4546 window_message_in_echo_area (rep);
4547 display_cursor_at_point (active_window);
4549 info_keyseq_displayed_p = 1;
4552 /* Called by interactive commands to read a keystroke. */
4554 info_get_another_input_char ()
4556 int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
4558 /* If there isn't any input currently available, then wait a
4559 moment looking for input. If we don't get it fast enough,
4560 prompt a little bit with the current key sequence. */
4561 if (!info_keyseq_displayed_p)
4564 if (!info_any_buffered_input_p () &&
4565 !info_input_pending_p ())
4567 #if defined (FD_SET)
4568 struct timeval timer;
4572 FD_SET (fileno (info_input_stream), &readfds);
4574 timer.tv_usec = 750;
4575 ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
4583 display_info_keyseq (1);
4585 return (info_get_input_char ());
4588 /* Do the command associated with KEY in MAP. If the associated command is
4589 really a keymap, then read another key, and dispatch into that map. */
4591 info_dispatch_on_key (key, map)
4595 #if !defined(INFOKEY)
4596 if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
4598 if (map[ESC].type == ISKMAP)
4600 map = (Keymap)map[ESC].function;
4601 add_char_to_keyseq (ESC);
4603 info_dispatch_on_key (key, map);
4607 dispatch_error (info_keyseq);
4611 #endif /* INFOKEY */
4613 switch (map[key].type)
4619 func = InfoFunction(map[key].function);
4620 if (func != (VFunction *)NULL)
4622 /* Special case info_do_lowercase_version (). */
4623 if (func == info_do_lowercase_version)
4625 #if defined(INFOKEY)
4626 unsigned char lowerkey;
4628 lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
4629 if (lowerkey == key)
4631 add_char_to_keyseq (key);
4632 dispatch_error (info_keyseq);
4635 info_dispatch_on_key (lowerkey, map);
4636 #else /* !INFOKEY */
4637 info_dispatch_on_key (tolower (key), map);
4638 #endif /* INFOKEY */
4642 add_char_to_keyseq (key);
4644 if (info_keyseq_displayed_p)
4645 display_info_keyseq (0);
4650 where = active_window;
4651 (*InfoFunction(map[key].function))
4652 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
4654 /* If we have input pending, then the last command was a prefix
4655 command. Don't change the value of the last function vars.
4656 Otherwise, remember the last command executed in the var
4657 appropriate to the window in which it was executed. */
4658 if (!info_input_pending_p ())
4660 if (where == the_echo_area)
4661 ea_last_executed_command = InfoFunction(map[key].function);
4663 info_last_executed_command = InfoFunction(map[key].function);
4669 add_char_to_keyseq (key);
4670 dispatch_error (info_keyseq);
4677 add_char_to_keyseq (key);
4678 if (map[key].function != (InfoCommand *)NULL)
4680 unsigned char newkey;
4682 newkey = info_get_another_input_char ();
4683 info_dispatch_on_key (newkey, (Keymap)map[key].function);
4687 dispatch_error (info_keyseq);
4694 /* **************************************************************** */
4696 /* Numeric Arguments */
4698 /* **************************************************************** */
4700 /* Handle C-u style numeric args, as well as M--, and M-digits. */
4702 /* Non-zero means that an explicit argument has been passed to this
4703 command, as in C-u C-v. */
4704 int info_explicit_arg = 0;
4706 /* The sign of the numeric argument. */
4707 int info_numeric_arg_sign = 1;
4709 /* The value of the argument itself. */
4710 int info_numeric_arg = 1;
4712 /* Add the current digit to the argument in progress. */
4713 DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
4714 _("Add this digit to the current numeric argument"))
4716 info_numeric_arg_digit_loop (window, 0, key);
4719 /* C-u, universal argument. Multiply the current argument by 4.
4720 Read a key. If the key has nothing to do with arguments, then
4721 dispatch on it. If the key is the abort character then abort. */
4722 DECLARE_INFO_COMMAND (info_universal_argument,
4723 _("Start (or multiply by 4) the current numeric argument"))
4725 info_numeric_arg *= 4;
4726 info_numeric_arg_digit_loop (window, 0, 0);
4729 /* Create a default argument. */
4731 info_initialize_numeric_arg ()
4733 info_numeric_arg = info_numeric_arg_sign = 1;
4734 info_explicit_arg = 0;
4737 DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
4738 _("Internally used by \\[universal-argument]"))
4740 unsigned char pure_key;
4741 Keymap keymap = window->keymap;
4749 if (display_was_interrupted_p && !info_any_buffered_input_p ())
4750 display_update_display (windows);
4752 if (active_window != the_echo_area)
4753 display_cursor_at_point (active_window);
4755 pure_key = key = info_get_another_input_char ();
4757 #if !defined(INFOKEY)
4759 add_char_to_keyseq (ESC);
4761 add_char_to_keyseq (UnMeta (key));
4762 #else /* defined(INFOKEY) */
4763 add_char_to_keyseq (key);
4764 #endif /* defined(INFOKEY) */
4767 #if !defined(INFOKEY)
4770 #endif /* !defined(INFOKEY) */
4772 if (keymap[key].type == ISFUNC &&
4773 InfoFunction(keymap[key].function) == info_universal_argument)
4775 info_numeric_arg *= 4;
4780 #if defined(INFOKEY)
4783 #endif /* !defined(INFOKEY) */
4788 if (info_explicit_arg)
4789 info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
4791 info_numeric_arg = (key - '0');
4792 info_explicit_arg = 1;
4796 if (key == '-' && !info_explicit_arg)
4798 info_numeric_arg_sign = -1;
4799 info_numeric_arg = 1;
4803 info_keyseq_index--;
4804 info_dispatch_on_key (pure_key, keymap);
4812 /* **************************************************************** */
4814 /* Input Character Buffering */
4816 /* **************************************************************** */
4818 /* Character waiting to be read next. */
4819 static int pending_input_character = 0;
4821 /* How to make there be no pending input. */
4823 info_clear_pending_input ()
4825 pending_input_character = 0;
4828 /* How to set the pending input character. */
4830 info_set_pending_input (key)
4833 pending_input_character = key;
4836 /* How to see if there is any pending input. */
4838 info_input_pending_p ()
4840 return (pending_input_character);
4843 /* Largest number of characters that we can read in advance. */
4844 #define MAX_INFO_INPUT_BUFFERING 512
4846 static int pop_index = 0, push_index = 0;
4847 static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
4849 /* Add KEY to the buffer of characters to be read. */
4851 info_push_typeahead (key)
4854 /* Flush all pending input in the case of C-g pressed. */
4855 if (key == Control ('g'))
4857 push_index = pop_index;
4858 info_set_pending_input (Control ('g'));
4862 info_input_buffer[push_index++] = key;
4863 if (push_index >= sizeof (info_input_buffer))
4868 /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4870 info_input_buffer_space_available ()
4872 if (pop_index > push_index)
4873 return (pop_index - push_index);
4875 return (sizeof (info_input_buffer) - (push_index - pop_index));
4878 /* Get a key from the buffer of characters to be read.
4879 Return the key in KEY.
4880 Result is non-zero if there was a key, or 0 if there wasn't. */
4882 info_get_key_from_typeahead (key)
4885 if (push_index == pop_index)
4888 *key = info_input_buffer[pop_index++];
4890 if (pop_index >= sizeof (info_input_buffer))
4897 info_any_buffered_input_p ()
4899 info_gather_typeahead ();
4900 return (push_index != pop_index);
4903 /* If characters are available to be read, then read them and stuff them into
4904 info_input_buffer. Otherwise, do nothing. */
4906 info_gather_typeahead ()
4909 int tty, space_avail;
4911 unsigned char input[MAX_INFO_INPUT_BUFFERING];
4913 tty = fileno (info_input_stream);
4916 space_avail = info_input_buffer_space_available ();
4918 /* If we can just find out how many characters there are to read, do so. */
4919 #if defined (FIONREAD)
4921 ioctl (tty, FIONREAD, &chars_avail);
4923 if (chars_avail > space_avail)
4924 chars_avail = space_avail;
4927 chars_avail = read (tty, &input[0], chars_avail);
4929 #else /* !FIONREAD */
4930 # if defined (O_NDELAY)
4934 flags = fcntl (tty, F_GETFL, 0);
4936 fcntl (tty, F_SETFL, (flags | O_NDELAY));
4937 chars_avail = read (tty, &input[0], space_avail);
4938 fcntl (tty, F_SETFL, flags);
4940 if (chars_avail == -1)
4943 # else /* !O_NDELAY */
4946 extern long pc_term_chars_avail (void);
4949 chars_avail = pc_term_chars_avail ();
4952 /* We could be more accurate by calling ltell, but we have no idea
4953 whether tty is buffered by stdio functions, and if so, how many
4954 characters are already waiting in the buffer. So we punt. */
4957 if (fstat (tty, &st) < 0)
4960 chars_avail = st.st_size;
4962 if (chars_avail > space_avail)
4963 chars_avail = space_avail;
4965 chars_avail = read (tty, &input[0], chars_avail);
4967 # endif/* __DJGPP__ */
4968 # endif /* O_NDELAY */
4969 #endif /* !FIONREAD */
4971 while (i < chars_avail)
4973 info_push_typeahead (input[i]);
4978 /* How to read a single character. */
4980 info_get_input_char ()
4982 unsigned char keystroke;
4984 info_gather_typeahead ();
4986 if (pending_input_character)
4988 keystroke = pending_input_character;
4989 pending_input_character = 0;
4991 else if (info_get_key_from_typeahead (&keystroke) == 0)
4995 int tty = fileno (info_input_stream);
4997 /* Using stream I/O causes FIONREAD etc to fail to work
4998 so unless someone can find a portable way of finding
4999 out how many characters are currently buffered, we
5000 should stay with away from stream I/O.
5001 --Egil Kvaleberg <egilk@sn.no>, January 1997. */
5003 /* Keep reading if we got EINTR, so that we don't just exit.
5004 --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
5009 n = read (tty, &c, 1);
5010 while (n == -1 && errno == EINTR);
5011 rawkey = n == 1 ? c : EOF;
5014 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5021 if (info_input_stream != stdin)
5023 fclose (info_input_stream);
5024 info_input_stream = stdin;
5025 tty = fileno (info_input_stream);
5026 display_inhibited = 0;
5027 display_update_display (windows);
5028 display_cursor_at_point (active_window);
5029 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5035 terminal_unprep_terminal ();
5036 close_dribble_file ();
5042 if (info_dribble_file)
5043 dribble (keystroke);