1 /* $FreeBSD: src/contrib/texinfo/info/session.c,v 1.4.2.1 2002/03/30 17:09:19 ru Exp $ */
2 /* $DragonFly: src/contrib/texinfo/info/Attic/session.c,v 1.2 2003/06/17 04:24:07 dillon Exp $ */
3 /* session.c -- user windowing interface to Info.
4 $Id: session.c,v 1.45 2002/03/02 15:05:04 karl Exp $
6 Copyright (C) 1993, 96, 97, 98, 99, 2000, 01, 02
7 Free Software Foundation, Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 Written by Brian Fox (bfox@ai.mit.edu). */
26 #include <sys/ioctl.h>
28 #if defined (HAVE_SYS_TIME_H)
29 # include <sys/time.h>
30 # define HAVE_STRUCT_TIMEVAL
31 #endif /* HAVE_SYS_TIME_H */
33 #if defined (HANDLE_MAN_PAGES)
38 /* SCO 3.2v5.0.2 defines but does not correctly declare strncasecmp.
39 Since we use it as a symbol, have to get it right. --gildea, 1jul99. */
40 extern int strncasecmp (const char *, const char *, size_t);
43 static void info_clear_pending_input (), info_set_pending_input ();
44 static void info_handle_pointer ();
46 /* **************************************************************** */
48 /* Running an Info Session */
50 /* **************************************************************** */
52 /* The place that we are reading input from. */
53 static FILE *info_input_stream = NULL;
55 /* The last executed command. */
56 VFunction *info_last_executed_command = NULL;
58 /* Becomes non-zero when 'q' is typed to an Info window. */
59 int quit_info_immediately = 0;
61 /* Array of structures describing for each window which nodes have been
62 visited in that window. */
63 INFO_WINDOW **info_windows = NULL;
65 /* Where to add the next window, if we need to add one. */
66 static int info_windows_index = 0;
68 /* Number of slots allocated to `info_windows'. */
69 static int info_windows_slots = 0;
71 void remember_window_and_node (), forget_window_and_nodes ();
72 void initialize_info_session (), info_session ();
73 void display_startup_message_and_start ();
75 /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
76 For each loaded node, create a new window. Always split the largest of the
79 begin_multiple_window_info_session (filename, nodenames)
84 WINDOW *window = (WINDOW *)NULL;
86 for (i = 0; nodenames[i]; i++)
90 node = info_get_node (filename, nodenames[i]);
95 /* If this is the first node, initialize the info session. */
98 initialize_info_session (node, 1);
99 window = active_window;
103 /* Find the largest window in WINDOWS, and make that be the active
104 one. Then split it and add our window and node to the list
105 of remembered windows and nodes. Then tile the windows. */
106 WINDOW *win, *largest = NULL;
109 for (win = windows; win; win = win->next)
110 if (win->height > max_height)
112 max_height = win->height;
118 display_update_display (windows);
119 info_error (msg_cant_find_window);
124 active_window = largest;
125 window = window_make_window (node);
128 window_tile_windows (TILE_INTERNALS);
129 remember_window_and_node (window, node);
133 display_update_display (windows);
134 info_error (msg_win_too_small);
140 display_startup_message_and_start ();
143 /* Start an info session with INITIAL_NODE, and an error message in the echo
144 area made from FORMAT and ARG. */
146 begin_info_session_with_error (initial_node, format, arg1, arg2)
152 initialize_info_session (initial_node, 1);
153 info_error (format, arg1, arg2);
157 /* Start an info session with INITIAL_NODE. */
159 begin_info_session (initial_node)
162 initialize_info_session (initial_node, 1);
163 display_startup_message_and_start ();
167 display_startup_message_and_start ()
171 format = replace_in_documentation
172 (_("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."));
174 window_message_in_echo_area (format, VERSION);
178 /* Run an info session with an already initialized window and node. */
182 display_update_display (windows);
183 info_last_executed_command = NULL;
184 info_read_and_dispatch ();
185 /* On program exit, leave the cursor at the bottom of the window, and
186 restore the terminal I/O. */
187 terminal_goto_xy (0, screenheight - 1);
188 terminal_clear_to_eol ();
190 terminal_unprep_terminal ();
191 close_dribble_file ();
194 /* Here is a window-location dependent event loop. Called from the
195 functions info_session (), and from read_xxx_in_echo_area (). */
197 info_read_and_dispatch ()
203 while (!done && !quit_info_immediately)
207 /* If we haven't just gone up or down a line, there is no
208 goal column for this window. */
209 if ((info_last_executed_command != info_next_line) &&
210 (info_last_executed_command != info_prev_line))
211 active_window->goal_column = -1;
213 if (echo_area_is_active)
215 lk = echo_area_last_command_was_kill;
216 echo_area_prep_read ();
219 if (!info_any_buffered_input_p ())
220 display_update_display (windows);
222 display_cursor_at_point (active_window);
223 info_initialize_numeric_arg ();
225 initialize_keyseq ();
226 key = info_get_input_char ();
228 /* No errors yet. We just read a character, that's all. Only clear
229 the echo_area if it is not currently active. */
230 if (!echo_area_is_active)
231 window_clear_echo_area ();
233 info_error_was_printed = 0;
235 /* Do the selected command. */
236 info_dispatch_on_key (key, active_window->keymap);
238 if (echo_area_is_active)
240 /* Echo area commands that do killing increment the value of
241 ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no
242 change in the value of this variable, the last command
243 executed was not a kill command. */
244 if (lk == echo_area_last_command_was_kill)
245 echo_area_last_command_was_kill = 0;
247 if (ea_last_executed_command == ea_newline ||
248 info_aborted_echo_area)
250 ea_last_executed_command = (VFunction *)NULL;
254 if (info_last_executed_command == info_quit)
255 quit_info_immediately = 1;
257 else if (info_last_executed_command == info_quit)
262 /* Found in signals.c */
263 extern void initialize_info_signal_handler ();
265 /* Initialize the first info session by starting the terminal, window,
266 and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
268 initialize_info_session (node, clear_screen)
272 char *term_name = getenv ("TERM");
273 terminal_initialize_terminal (term_name);
275 if (terminal_is_dumb_p)
280 info_error (msg_term_too_dumb, term_name);
286 terminal_prep_terminal ();
287 terminal_clear_screen ();
290 initialize_info_keymaps ();
291 window_initialize_windows (screenwidth, screenheight);
292 initialize_info_signal_handler ();
293 display_initialize_display (screenwidth, screenheight);
294 info_set_node_of_window (0, active_window, node);
296 /* Tell the window system how to notify us when a window needs to be
297 asynchronously deleted (e.g., user resizes window very small). */
298 window_deletion_notifier = forget_window_and_nodes;
300 /* If input has not been redirected yet, make it come from unbuffered
302 if (!info_input_stream)
304 setbuf (stdin, NULL);
305 info_input_stream = stdin;
308 info_windows_initialized_p = 1;
311 /* Tell Info that input is coming from the file FILENAME. */
313 info_set_input_from_file (filename)
318 /* Input may include binary characters. */
319 stream = fopen (filename, FOPEN_RBIN);
324 if ((info_input_stream != (FILE *)NULL) &&
325 (info_input_stream != stdin))
326 fclose (info_input_stream);
328 info_input_stream = stream;
331 display_inhibited = 1;
334 /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
336 get_info_window_of_window (window)
340 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
342 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
343 if (info_win->window == window)
349 /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
350 values if the window and node are the same as the current one being
353 set_remembered_pagetop_and_point (window)
356 INFO_WINDOW *info_win;
358 info_win = get_info_window_of_window (window);
363 if (info_win->nodes_index &&
364 (info_win->nodes[info_win->current] == window->node))
366 info_win->pagetops[info_win->current] = window->pagetop;
367 info_win->points[info_win->current] = window->point;
372 remember_window_and_node (window, node)
376 /* See if we already have this window in our list. */
377 INFO_WINDOW *info_win = get_info_window_of_window (window);
379 /* If the window wasn't already on our list, then make a new entry. */
382 info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
383 info_win->window = window;
384 info_win->nodes = (NODE **)NULL;
385 info_win->pagetops = (int *)NULL;
386 info_win->points = (long *)NULL;
387 info_win->current = 0;
388 info_win->nodes_index = 0;
389 info_win->nodes_slots = 0;
391 add_pointer_to_array (info_win, info_windows_index, info_windows,
392 info_windows_slots, 10, INFO_WINDOW *);
395 /* If this node, the current pagetop, and the current point are the
396 same as the current saved node and pagetop, don't really add this to
397 the list of history nodes. This may happen only at the very
398 beginning of the program, I'm not sure. --karl */
400 && info_win->current >= 0
401 && info_win->nodes[info_win->current]->contents == node->contents
402 && info_win->pagetops[info_win->current] == window->pagetop
403 && info_win->points[info_win->current] == window->point)
406 /* Remember this node, the currently displayed pagetop, and the current
407 location of point in this window. Because we are updating pagetops
408 and points as well as nodes, it is more efficient to avoid the
409 add_pointer_to_array macro here. */
410 if (info_win->nodes_index + 2 >= info_win->nodes_slots)
412 info_win->nodes_slots += 20;
413 info_win->nodes = (NODE **) xrealloc (info_win->nodes,
414 info_win->nodes_slots * sizeof (NODE *));
415 info_win->pagetops = (int *) xrealloc (info_win->pagetops,
416 info_win->nodes_slots * sizeof (int));
417 info_win->points = (long *) xrealloc (info_win->points,
418 info_win->nodes_slots * sizeof (long));
421 info_win->nodes[info_win->nodes_index] = node;
422 info_win->pagetops[info_win->nodes_index] = window->pagetop;
423 info_win->points[info_win->nodes_index] = window->point;
424 info_win->current = info_win->nodes_index++;
425 info_win->nodes[info_win->nodes_index] = NULL;
426 info_win->pagetops[info_win->nodes_index] = 0;
427 info_win->points[info_win->nodes_index] = 0;
430 #define DEBUG_FORGET_WINDOW_AND_NODES
431 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
433 consistency_check_info_windows ()
437 for (i = 0; i < info_windows_index; i++)
441 for (win = windows; win; win = win->next)
442 if (win == info_windows[i]->window)
449 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
451 /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
453 forget_window_and_nodes (window)
457 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
459 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
460 if (info_win->window == window)
463 /* If we found the window to forget, then do so. */
466 while (i < info_windows_index)
468 info_windows[i] = info_windows[i + 1];
472 info_windows_index--;
473 info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
477 /* Free the node structures which held onto internal node contents
478 here. This doesn't free the contents; we have a garbage collector
480 for (i = 0; info_win->nodes[i]; i++)
481 if (internal_info_node_p (info_win->nodes[i]))
482 free (info_win->nodes[i]);
483 free (info_win->nodes);
485 maybe_free (info_win->pagetops);
486 maybe_free (info_win->points);
491 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
492 consistency_check_info_windows ();
493 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
496 /* Set WINDOW to show NODE. Remember the new window in our list of Info
497 windows. If we are doing automatic footnote display, also try to display
498 the footnotes for this window. If REMEMBER is nonzero, first call
499 set_remembered_pagetop_and_point. */
501 info_set_node_of_window (remember, window, node)
507 set_remembered_pagetop_and_point (window);
509 /* Put this node into the window. */
510 window_set_node_of_window (window, node);
512 /* Remember this node and window in our list of info windows. */
513 remember_window_and_node (window, node);
515 /* If doing auto-footnote display/undisplay, show the footnotes belonging
516 to this window's node. */
517 if (auto_footnotes_p)
518 info_get_or_remove_footnotes (window);
522 /* **************************************************************** */
524 /* Info Movement Commands */
526 /* **************************************************************** */
528 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
531 set_window_pagetop (window, desired_top)
535 int point_line, old_pagetop;
539 else if (desired_top > window->line_count)
540 desired_top = window->line_count - 1;
542 if (window->pagetop == desired_top)
545 old_pagetop = window->pagetop;
546 window->pagetop = desired_top;
548 /* Make sure that point appears in this window. */
549 point_line = window_line_of_point (window);
550 if ((point_line < window->pagetop) ||
551 ((point_line - window->pagetop) > window->height - 1))
553 window->line_starts[window->pagetop] - window->node->contents;
555 window->flags |= W_UpdateWindow;
557 /* Find out which direction to scroll, and scroll the window in that
558 direction. Do this only if there would be a savings in redisplay
559 time. This is true if the amount to scroll is less than the height
560 of the window, and if the number of lines scrolled would be greater
561 than 10 % of the window's height. */
562 if (old_pagetop < desired_top)
564 int start, end, amount;
566 amount = desired_top - old_pagetop;
568 if ((amount >= window->height) ||
569 (((window->height - amount) * 10) < window->height))
572 start = amount + window->first_row;
573 end = window->height + window->first_row;
575 display_scroll_display (start, end, -amount);
579 int start, end, amount;
581 amount = old_pagetop - desired_top;
583 if ((amount >= window->height) ||
584 (((window->height - amount) * 10) < window->height))
587 start = window->first_row;
588 end = (window->first_row + window->height) - amount;
589 display_scroll_display (start, end, amount);
593 /* Immediately make WINDOW->point visible on the screen, and move the
594 terminal cursor there. */
596 info_show_point (window)
601 old_pagetop = window->pagetop;
602 window_adjust_pagetop (window);
603 if (old_pagetop != window->pagetop)
607 new_pagetop = window->pagetop;
608 window->pagetop = old_pagetop;
609 set_window_pagetop (window, new_pagetop);
612 if (window->flags & W_UpdateWindow)
613 display_update_one_window (window);
615 display_cursor_at_point (window);
618 /* Move WINDOW->point from OLD line index to NEW line index. */
620 move_to_new_line (old, new, window)
626 info_error (msg_cant_find_point);
632 if (new >= window->line_count || new < 0)
635 goal = window_get_goal_column (window);
636 window->goal_column = goal;
638 window->point = window->line_starts[new] - window->node->contents;
639 window->point += window_chars_to_goal (window->line_starts[new], goal);
640 info_show_point (window);
644 /* Move WINDOW's point down to the next line if possible. */
645 DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
647 int old_line, new_line;
650 info_prev_line (window, -count, key);
653 old_line = window_line_of_point (window);
654 new_line = old_line + count;
655 move_to_new_line (old_line, new_line, window);
659 /* Move WINDOW's point up to the previous line if possible. */
660 DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
662 int old_line, new_line;
665 info_next_line (window, -count, key);
668 old_line = window_line_of_point (window);
669 new_line = old_line - count;
670 move_to_new_line (old_line, new_line, window);
674 /* Move WINDOW's point to the end of the true line. */
675 DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
677 register int point, len;
678 register char *buffer;
680 buffer = window->node->contents;
681 len = window->node->nodelen;
683 for (point = window->point;
684 (point < len) && (buffer[point] != '\n');
687 if (point != window->point)
689 window->point = point;
690 info_show_point (window);
694 /* Move WINDOW's point to the beginning of the true line. */
695 DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
698 register char *buffer;
700 buffer = window->node->contents;
701 point = window->point;
703 for (; (point) && (buffer[point - 1] != '\n'); point--);
705 /* If at a line start already, do nothing. */
706 if (point != window->point)
708 window->point = point;
709 info_show_point (window);
713 /* Move point forward in the node. */
714 DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
717 info_backward_char (window, -count, key);
720 window->point += count;
722 if (window->point >= window->node->nodelen)
723 window->point = window->node->nodelen - 1;
725 info_show_point (window);
729 /* Move point backward in the node. */
730 DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
733 info_forward_char (window, -count, key);
736 window->point -= count;
738 if (window->point < 0)
741 info_show_point (window);
745 #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
747 /* Move forward a word in this node. */
748 DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
756 info_backward_word (window, -count, key);
760 point = window->point;
761 buffer = window->node->contents;
762 end = window->node->nodelen;
766 if (point + 1 >= end)
769 /* If we are not in a word, move forward until we are in one.
770 Then, move forward until we hit a non-alphabetic character. */
775 while (++point < end)
783 if (point >= end) return;
785 while (++point < end)
793 window->point = point;
794 info_show_point (window);
797 DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
805 info_forward_word (window, -count, key);
809 buffer = window->node->contents;
810 point = window->point;
817 /* Like info_forward_word (), except that we look at the
818 characters just before point. */
820 c = buffer[point - 1];
826 c = buffer[point - 1];
834 c = buffer[point - 1];
842 window->point = point;
843 info_show_point (window);
846 /* Variable controlling the behaviour of default scrolling when you are
847 already at the bottom of a node. Possible values are defined in session.h.
850 IS_Continuous Try to get first menu item, or failing that, the
851 "Next:" pointer, or failing that, the "Up:" and
853 IS_NextOnly Try to get "Next:" menu item.
854 IS_PageOnly Simply give up at the bottom of a node. */
856 int info_scroll_behaviour = IS_Continuous;
858 /* Choices used by the completer when reading a value for the user-visible
859 variable "scroll-behaviour". */
860 char *info_scroll_choices[] = {
861 "Continuous", "Next Only", "Page Only", (char *)NULL
864 /* Default window sizes for scrolling commands. */
865 int default_window_size = -1; /* meaning 1 window-full */
866 int default_scroll_size = -1; /* meaning half screen size */
868 #define INFO_LABEL_FOUND() \
869 (info_parsed_nodename || (info_parsed_filename \
870 && !is_dir_name (info_parsed_filename)))
872 /* Move to 1st menu item, Next, Up/Next, or error in this window. */
874 forward_move_node_structure (window, behaviour)
881 info_error (msg_at_node_bottom);
885 info_next_label_of_node (window->node);
886 if (!info_parsed_nodename && !info_parsed_filename)
887 info_error (msg_no_pointer, _("Next"));
890 window_message_in_echo_area (_("Following Next node..."));
891 info_handle_pointer ("Next", window);
897 /* First things first. If this node contains a menu, move down
902 menu = info_menu_of_node (window->node);
906 info_free_references (menu);
907 window_message_in_echo_area (_("Selecting first menu item..."));
908 info_menu_digit (window, 1, '1');
913 /* Okay, this node does not contain a menu. If it contains a
914 "Next:" pointer, use that. */
915 info_next_label_of_node (window->node);
916 if (INFO_LABEL_FOUND ())
918 window_message_in_echo_area (_("Selecting Next node..."));
919 info_handle_pointer ("Next", window);
923 /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we
924 can move "Next:". If that isn't possible, complain that there
925 are no more nodes. */
927 int up_counter, old_current;
928 INFO_WINDOW *info_win;
930 /* Remember the current node and location. */
931 info_win = get_info_window_of_window (window);
932 old_current = info_win->current;
934 /* Back up through the "Up:" pointers until we have found a "Next:"
935 that isn't the same as the first menu item found in that node. */
937 while (!info_error_was_printed)
939 info_up_label_of_node (window->node);
940 if (INFO_LABEL_FOUND ())
942 info_handle_pointer ("Up", window);
943 if (info_error_was_printed)
948 info_next_label_of_node (window->node);
950 /* If no "Next" pointer, keep backing up. */
951 if (!INFO_LABEL_FOUND ())
954 /* If this node's first menu item is the same as this node's
955 Next pointer, keep backing up. */
956 if (!info_parsed_filename)
961 /* Remember the name of the Next node, since reading
962 the menu can overwrite the contents of the
963 info_parsed_xxx strings. */
964 next_nodename = xstrdup (info_parsed_nodename);
966 menu = info_menu_of_node (window->node);
969 (menu[0]->nodename, next_nodename) == 0))
971 info_free_references (menu);
972 free (next_nodename);
977 /* Restore the world to where it was before
978 reading the menu contents. */
979 info_free_references (menu);
980 free (next_nodename);
981 info_next_label_of_node (window->node);
985 /* This node has a "Next" pointer, and it is not the
986 same as the first menu item found in this node. */
987 window_message_in_echo_area
988 (_("Moving Up %d time(s), then Next."),
991 info_handle_pointer ("Next", window);
996 /* No more "Up" pointers. Print an error, and call it
1000 for (i = 0; i < up_counter; i++)
1002 info_win->nodes_index--;
1003 free (info_win->nodes[info_win->nodes_index]);
1004 info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
1006 info_win->current = old_current;
1007 window->node = info_win->nodes[old_current];
1008 window->pagetop = info_win->pagetops[old_current];
1009 window->point = info_win->points[old_current];
1010 recalculate_line_starts (window);
1011 window->flags |= W_UpdateWindow;
1012 info_error (_("No more nodes within this document."));
1021 /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
1023 backward_move_node_structure (window, behaviour)
1030 info_error (msg_at_node_top);
1034 info_prev_label_of_node (window->node);
1035 if (!info_parsed_nodename && !info_parsed_filename)
1036 info_error (_("No `Prev' for this node."));
1039 window_message_in_echo_area (_("Moving Prev in this window."));
1040 info_handle_pointer ("Prev", window);
1045 info_prev_label_of_node (window->node);
1047 if (!info_parsed_nodename && (!info_parsed_filename
1048 || is_dir_name (info_parsed_filename)))
1050 info_up_label_of_node (window->node);
1051 if (!info_parsed_nodename && (!info_parsed_filename
1052 || is_dir_name (info_parsed_filename)))
1053 info_error (_("No `Prev' or `Up' for this node within this document."));
1056 window_message_in_echo_area (_("Moving Up in this window."));
1057 info_handle_pointer ("Up", window);
1063 int inhibit_menu_traversing = 0;
1065 /* Watch out! If this node's Prev is the same as the Up, then
1066 move Up. Otherwise, we could move Prev, and then to the last
1067 menu item in the Prev. This would cause the user to loop
1068 through a subsection of the info file. */
1069 if (!info_parsed_filename && info_parsed_nodename)
1073 pnode = xstrdup (info_parsed_nodename);
1074 info_up_label_of_node (window->node);
1076 if (!info_parsed_filename && info_parsed_nodename &&
1077 strcmp (info_parsed_nodename, pnode) == 0)
1079 /* The nodes are the same. Inhibit moving to the last
1082 inhibit_menu_traversing = 1;
1087 info_prev_label_of_node (window->node);
1091 /* Move to the previous node. If this node now contains a menu,
1092 and we have not inhibited movement to it, move to the node
1093 corresponding to the last menu item. */
1094 window_message_in_echo_area (_("Moving Prev in this window."));
1095 info_handle_pointer ("Prev", window);
1097 if (!inhibit_menu_traversing)
1099 while (!info_error_was_printed &&
1100 (menu = info_menu_of_node (window->node)))
1102 info_free_references (menu);
1103 window_message_in_echo_area
1104 (_("Moving to `Prev's last menu item."));
1105 info_menu_digit (window, 1, '0');
1113 /* Move continuously forward through the node structure of this info file. */
1114 DECLARE_INFO_COMMAND (info_global_next_node,
1115 _("Move forwards or down through node structure"))
1118 info_global_prev_node (window, -count, key);
1121 while (count && !info_error_was_printed)
1123 forward_move_node_structure (window, IS_Continuous);
1129 /* Move continuously backward through the node structure of this info file. */
1130 DECLARE_INFO_COMMAND (info_global_prev_node,
1131 _("Move backwards or up through node structure"))
1134 info_global_next_node (window, -count, key);
1137 while (count && !info_error_was_printed)
1139 backward_move_node_structure (window, IS_Continuous);
1145 static void _scroll_forward();
1146 static void _scroll_backward();
1149 _scroll_forward(window, count, key, behaviour)
1156 _scroll_backward (window, -count, key, behaviour);
1161 /* Without an explicit numeric argument, scroll the bottom two
1162 lines to the top of this window, Or, if at bottom of window,
1163 and the chosen behaviour is to scroll through nodes get the
1164 "Next" node for this window. */
1165 if (default_window_size > 0)
1166 desired_top = window->pagetop + default_window_size;
1167 else if (!info_explicit_arg && count == 1)
1169 desired_top = window->pagetop + (window->height - 2);
1171 /* If there are no more lines to scroll here, error, or get
1172 another node, depending on BEHAVIOUR. */
1173 if (desired_top > window->line_count)
1175 forward_move_node_structure (window, behaviour);
1180 desired_top = window->pagetop + count;
1182 if (desired_top >= window->line_count)
1183 desired_top = window->line_count - 2;
1185 if (window->pagetop > desired_top)
1188 set_window_pagetop (window, desired_top);
1193 _scroll_backward(window, count, key, behaviour)
1200 _scroll_forward (window, -count, key, behaviour);
1205 /* Without an explicit numeric argument, scroll the top two lines
1206 to the bottom of this window, or, depending on the selected
1207 behaviour, move to the previous, or Up'th node. */
1208 if (default_window_size > 0)
1209 desired_top = window->pagetop - default_window_size;
1210 else if (!info_explicit_arg && count == 1)
1212 desired_top = window->pagetop - (window->height - 2);
1214 if ((desired_top < 0) && (window->pagetop == 0))
1216 backward_move_node_structure (window, behaviour);
1221 desired_top = window->pagetop - count;
1223 if (desired_top < 0)
1226 set_window_pagetop (window, desired_top);
1230 /* Show the next screen of WINDOW's node. */
1231 DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
1233 _scroll_forward (window, count, key, info_scroll_behaviour);
1236 /* Like info_scroll_forward, but sets default_window_size as a side
1238 DECLARE_INFO_COMMAND (info_scroll_forward_set_window,
1239 _("Scroll forward in this window and set default window size"))
1241 if (info_explicit_arg)
1242 default_window_size = count;
1243 _scroll_forward (window, count, key, info_scroll_behaviour);
1246 /* Show the next screen of WINDOW's node but never advance to next node. */
1247 DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))
1249 _scroll_forward (window, count, key, IS_PageOnly);
1252 /* Like info_scroll_forward_page_only, but sets default_window_size as a side
1254 DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,
1255 _("Scroll forward in this window staying within node and set default window size"))
1257 if (info_explicit_arg)
1258 default_window_size = count;
1259 _scroll_forward (window, count, key, IS_PageOnly);
1262 /* Show the previous screen of WINDOW's node. */
1263 DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
1265 _scroll_backward (window, count, key, info_scroll_behaviour);
1268 /* Like info_scroll_backward, but sets default_window_size as a side
1270 DECLARE_INFO_COMMAND (info_scroll_backward_set_window,
1271 _("Scroll backward in this window and set default window size"))
1273 if (info_explicit_arg)
1274 default_window_size = count;
1275 _scroll_backward (window, count, key, info_scroll_behaviour);
1278 /* Show the previous screen of WINDOW's node but never move to previous
1280 DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))
1282 _scroll_backward (window, count, key, IS_PageOnly);
1285 /* Like info_scroll_backward_page_only, but sets default_window_size as a side
1287 DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,
1288 _("Scroll backward in this window staying within node and set default window size"))
1290 if (info_explicit_arg)
1291 default_window_size = count;
1292 _scroll_backward (window, count, key, IS_PageOnly);
1295 /* Move to the beginning of the node. */
1296 DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1298 window->pagetop = window->point = 0;
1299 window->flags |= W_UpdateWindow;
1302 /* Move to the end of the node. */
1303 DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1305 window->point = window->node->nodelen - 1;
1306 info_show_point (window);
1309 /* Scroll the window forward by N lines. */
1310 DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
1313 info_up_line (window, -count, key);
1316 int desired_top = window->pagetop + count;
1318 if (desired_top >= window->line_count)
1319 desired_top = window->line_count - 2;
1321 if (window->pagetop <= desired_top)
1322 set_window_pagetop (window, desired_top);
1326 /* Scroll the window backward by N lines. */
1327 DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
1330 info_down_line (window, -count, key);
1333 int desired_top = window->pagetop - count;
1335 if (desired_top < 0)
1338 set_window_pagetop (window, desired_top);
1342 /* Scroll the window forward by N lines and remember N as default for
1343 subsequent commands. */
1344 DECLARE_INFO_COMMAND (info_scroll_half_screen_down,
1345 _("Scroll down by half screen size"))
1348 info_scroll_half_screen_up (window, -count, key);
1351 int scroll_size = (the_screen->height + 1) / 2;
1354 if (info_explicit_arg)
1355 default_scroll_size = count;
1356 if (default_scroll_size > 0)
1357 scroll_size = default_scroll_size;
1359 desired_top = window->pagetop + scroll_size;
1360 if (desired_top >= window->line_count)
1361 desired_top = window->line_count - 2;
1363 if (window->pagetop <= desired_top)
1364 set_window_pagetop (window, desired_top);
1368 /* Scroll the window backward by N lines and remember N as default for
1369 subsequent commands. */
1370 DECLARE_INFO_COMMAND (info_scroll_half_screen_up,
1371 _("Scroll up by half screen size"))
1374 info_scroll_half_screen_down (window, -count, key);
1377 int scroll_size = (the_screen->height + 1) / 2;
1380 if (info_explicit_arg)
1381 default_scroll_size = count;
1382 if (default_scroll_size > 0)
1383 scroll_size = default_scroll_size;
1385 desired_top = window->pagetop - scroll_size;
1386 if (desired_top < 0)
1389 set_window_pagetop (window, desired_top);
1393 /* **************************************************************** */
1395 /* Commands for Manipulating Windows */
1397 /* **************************************************************** */
1399 /* Make the next window in the chain be the active window. */
1400 DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1404 info_prev_window (window, -count, key);
1408 /* If no other window, error now. */
1409 if (!windows->next && !echo_area_is_active)
1411 info_error (msg_one_window);
1418 window = window->next;
1421 if (window == the_echo_area || !echo_area_is_active)
1424 window = the_echo_area;
1428 if (active_window != window)
1430 if (auto_footnotes_p)
1431 info_get_or_remove_footnotes (window);
1433 window->flags |= W_UpdateWindow;
1434 active_window = window;
1438 /* Make the previous window in the chain be the active window. */
1439 DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1443 info_next_window (window, -count, key);
1447 /* Only one window? */
1449 if (!windows->next && !echo_area_is_active)
1451 info_error (msg_one_window);
1457 /* If we are in the echo area, or if the echo area isn't active and we
1458 are in the first window, find the last window in the chain. */
1459 if (window == the_echo_area ||
1460 (window == windows && !echo_area_is_active))
1462 register WINDOW *win, *last;
1464 for (win = windows; win; win = win->next)
1471 if (window == windows)
1472 window = the_echo_area;
1474 window = window->prev;
1478 if (active_window != window)
1480 if (auto_footnotes_p)
1481 info_get_or_remove_footnotes (window);
1483 window->flags |= W_UpdateWindow;
1484 active_window = window;
1488 /* Split WINDOW into two windows, both showing the same node. If we
1489 are automatically tiling windows, re-tile after the split. */
1490 DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
1492 WINDOW *split, *old_active;
1495 /* Remember the current pagetop of the window being split. If it doesn't
1496 change, we can scroll its contents around after the split. */
1497 pagetop = window->pagetop;
1499 /* Make the new window. */
1500 old_active = active_window;
1501 active_window = window;
1502 split = window_make_window (window->node);
1503 active_window = old_active;
1507 info_error (msg_win_too_small);
1511 #if defined (SPLIT_BEFORE_ACTIVE)
1512 /* Try to scroll the old window into its new postion. */
1513 if (pagetop == window->pagetop)
1515 int start, end, amount;
1517 start = split->first_row;
1518 end = start + window->height;
1519 amount = split->height + 1;
1520 display_scroll_display (start, end, amount);
1522 #else /* !SPLIT_BEFORE_ACTIVE */
1523 /* Make sure point still appears in the active window. */
1524 info_show_point (window);
1525 #endif /* !SPLIT_BEFORE_ACTIVE */
1527 /* If the window just split was one internal to Info, try to display
1528 something else in it. */
1529 if (internal_info_node_p (split->node))
1533 NODE *node = (NODE *)NULL;
1536 for (i = 0; (iw = info_windows[i]); i++)
1538 for (j = 0; j < iw->nodes_index; j++)
1539 if (!internal_info_node_p (iw->nodes[j]))
1541 if (iw->nodes[j]->parent)
1542 filename = iw->nodes[j]->parent;
1544 filename = iw->nodes[j]->filename;
1546 node = info_get_node (filename, iw->nodes[j]->nodename);
1549 window_set_node_of_window (split, node);
1550 i = info_windows_index - 1;
1556 split->pagetop = window->pagetop;
1559 window_tile_windows (DONT_TILE_INTERNALS);
1561 window_adjust_pagetop (split);
1563 remember_window_and_node (split, split->node);
1567 /* Delete WINDOW, forgetting the list of last visited nodes. If we are
1568 automatically displaying footnotes, show or remove the footnotes
1569 window. If we are automatically tiling windows, re-tile after the
1571 DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
1575 info_error (msg_cant_kill_last);
1577 else if (window->flags & W_WindowIsPerm)
1579 info_error (_("Cannot delete a permanent window"));
1583 info_delete_window_internal (window);
1585 if (auto_footnotes_p)
1586 info_get_or_remove_footnotes (active_window);
1589 window_tile_windows (DONT_TILE_INTERNALS);
1593 /* Do the physical deletion of WINDOW, and forget this window and
1594 associated nodes. */
1596 info_delete_window_internal (window)
1599 if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
1601 /* We not only delete the window from the display, we forget it from
1602 our list of remembered windows. */
1603 forget_window_and_nodes (window);
1604 window_delete_window (window);
1606 if (echo_area_is_active)
1607 echo_area_inform_of_deleted_window (window);
1611 /* Just keep WINDOW, deleting all others. */
1612 DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1614 int num_deleted; /* The number of windows we deleted. */
1615 int pagetop, start, end;
1617 /* Remember a few things about this window. We may be able to speed up
1618 redisplay later by scrolling its contents. */
1619 pagetop = window->pagetop;
1620 start = window->first_row;
1621 end = start + window->height;
1629 /* Find an eligible window and delete it. If no eligible windows
1630 are found, we are done. A window is eligible for deletion if
1631 is it not permanent, and it is not WINDOW. */
1632 for (win = windows; win; win = win->next)
1633 if (win != window && ((win->flags & W_WindowIsPerm) == 0))
1639 info_delete_window_internal (win);
1643 /* Scroll the contents of this window into the right place so that the
1644 user doesn't have to wait any longer than necessary for redisplay. */
1649 amount = (window->first_row - start);
1650 amount -= (window->pagetop - pagetop);
1651 display_scroll_display (start, end, amount);
1654 window->flags |= W_UpdateWindow;
1657 /* Scroll the "other" window of WINDOW. */
1658 DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1662 /* If only one window, give up. */
1665 info_error (msg_one_window);
1669 other = window->next;
1672 other = window->prev;
1674 info_scroll_forward (other, count, key);
1677 /* Scroll the "other" window of WINDOW. */
1678 DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
1679 _("Scroll the other window backward"))
1681 info_scroll_other_window (window, -count, key);
1684 /* Change the size of WINDOW by AMOUNT. */
1685 DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1687 window_change_window_height (window, count);
1690 /* When non-zero, tiling takes place automatically when info_split_window
1692 int auto_tiling_p = 0;
1694 /* Tile all of the visible windows. */
1695 DECLARE_INFO_COMMAND (info_tile_windows,
1696 _("Divide the available screen space among the visible windows"))
1698 window_tile_windows (TILE_INTERNALS);
1701 /* Toggle the state of this window's wrapping of lines. */
1702 DECLARE_INFO_COMMAND (info_toggle_wrap,
1703 _("Toggle the state of line wrapping in the current window"))
1705 window_toggle_wrap (window);
1708 /* **************************************************************** */
1710 /* Info Node Commands */
1712 /* **************************************************************** */
1714 /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
1715 filename is not set. */
1717 node_printed_rep (node)
1725 = filename_non_directory (node->parent ? node->parent : node->filename);
1726 rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
1727 sprintf (rep, "(%s)%s", filename, node->nodename);
1730 rep = node->nodename;
1736 /* Using WINDOW for various defaults, select the node referenced by ENTRY
1737 in it. If the node is selected, the window and node are remembered. */
1739 info_select_reference (window, entry)
1744 char *filename, *nodename, *file_system_error;
1746 file_system_error = (char *)NULL;
1748 filename = entry->filename;
1750 filename = window->node->parent;
1752 filename = window->node->filename;
1755 filename = xstrdup (filename);
1757 if (entry->nodename)
1758 nodename = xstrdup (entry->nodename);
1760 nodename = xstrdup ("Top");
1762 node = info_get_node (filename, nodename);
1764 /* Try something a little weird. If the node couldn't be found, and the
1765 reference was of the form "foo::", see if the entry->label can be found
1766 as a file, with a node of "Top". */
1769 if (info_recent_file_error)
1770 file_system_error = xstrdup (info_recent_file_error);
1772 if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
1774 node = info_get_node (entry->label, "Top");
1775 if (!node && info_recent_file_error)
1777 maybe_free (file_system_error);
1778 file_system_error = xstrdup (info_recent_file_error);
1785 if (file_system_error)
1786 info_error (file_system_error);
1788 info_error (msg_cant_find_node, nodename);
1791 maybe_free (file_system_error);
1792 maybe_free (filename);
1793 maybe_free (nodename);
1796 info_set_node_of_window (1, window, node);
1799 /* Parse the node specification in LINE using WINDOW to default the filename.
1800 Select the parsed node in WINDOW and remember it, or error if the node
1801 couldn't be found. */
1803 info_parse_and_select (line, window)
1809 info_parse_node (line, DONT_SKIP_NEWLINES);
1811 entry.nodename = info_parsed_nodename;
1812 entry.filename = info_parsed_filename;
1813 entry.label = "*info-parse-and-select*";
1815 info_select_reference (window, &entry);
1818 /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
1819 are previously filled, try to get the node represented by them into
1820 WINDOW. The node should have been pointed to by the LABEL pointer of
1823 info_handle_pointer (label, window)
1827 if (info_parsed_filename || info_parsed_nodename)
1829 char *filename, *nodename;
1832 filename = nodename = (char *)NULL;
1834 if (info_parsed_filename)
1835 filename = xstrdup (info_parsed_filename);
1838 if (window->node->parent)
1839 filename = xstrdup (window->node->parent);
1840 else if (window->node->filename)
1841 filename = xstrdup (window->node->filename);
1844 if (info_parsed_nodename)
1845 nodename = xstrdup (info_parsed_nodename);
1847 nodename = xstrdup ("Top");
1849 node = info_get_node (filename, nodename);
1853 INFO_WINDOW *info_win;
1855 info_win = get_info_window_of_window (window);
1858 info_win->pagetops[info_win->current] = window->pagetop;
1859 info_win->points[info_win->current] = window->point;
1861 info_set_node_of_window (1, window, node);
1865 if (info_recent_file_error)
1866 info_error (info_recent_file_error);
1868 info_error (msg_cant_file_node, filename, nodename);
1876 info_error (msg_no_pointer, label);
1880 /* Make WINDOW display the "Next:" node of the node currently being
1882 DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
1884 info_next_label_of_node (window->node);
1885 info_handle_pointer ("Next", window);
1888 /* Make WINDOW display the "Prev:" node of the node currently being
1890 DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
1892 info_prev_label_of_node (window->node);
1893 info_handle_pointer ("Prev", window);
1896 /* Make WINDOW display the "Up:" node of the node currently being
1898 DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
1900 info_up_label_of_node (window->node);
1901 info_handle_pointer ("Up", window);
1904 /* Make WINDOW display the last node of this info file. */
1905 DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
1908 FILE_BUFFER *fb = file_buffer_of_window (window);
1909 NODE *node = (NODE *)NULL;
1913 int last_node_tag_idx = -1;
1915 /* If no explicit argument, or argument of zero, default to the
1917 if (count == 0 || (count == 1 && !info_explicit_arg))
1919 for (i = 0; count && fb->tags[i]; i++)
1920 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1923 last_node_tag_idx = i;
1926 i = last_node_tag_idx + 1;
1928 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1932 info_error (_("This window has no additional nodes"));
1934 info_set_node_of_window (1, window, node);
1937 /* Make WINDOW display the first node of this info file. */
1938 DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
1940 FILE_BUFFER *fb = file_buffer_of_window (window);
1941 NODE *node = (NODE *)NULL;
1943 /* If no explicit argument, or argument of zero, default to the
1950 int last_node_tag_idx = -1;
1952 for (i = 0; count && fb->tags[i]; i++)
1953 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1956 last_node_tag_idx = i;
1959 i = last_node_tag_idx + 1;
1961 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1965 info_error (_("This window has no additional nodes"));
1967 info_set_node_of_window (1, window, node);
1970 /* Select the last menu item in WINDOW->node. */
1971 DECLARE_INFO_COMMAND (info_last_menu_item,
1972 _("Select the last item in this node's menu"))
1974 info_menu_digit (window, 1, '0');
1977 /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
1978 DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
1980 register int i, item;
1981 register REFERENCE *entry, **menu;
1983 menu = info_menu_of_node (window->node);
1987 info_error (msg_no_menu_node);
1991 /* We have the menu. See if there are this many items in it. */
1994 /* Special case. Item "0" is the last item in this menu. */
1996 for (i = 0; menu[i + 1]; i++);
1999 for (i = 0; (entry = menu[i]); i++)
2005 info_select_reference (window, menu[i]);
2007 info_error (_("There aren't %d items in this menu."), item);
2009 info_free_references (menu);
2013 /* Read a menu or followed reference from the user defaulting to the
2014 reference found on the current line, and select that node. The
2015 reading is done with completion. BUILDER is the function used
2016 to build the list of references. ASK_P is non-zero if the user
2017 should be prompted, or zero to select the default item. */
2019 info_menu_or_ref_item (window, count, key, builder, ask_p)
2023 REFERENCE **(*builder) ();
2026 REFERENCE **menu, *entry, *defentry = (REFERENCE *)NULL;
2029 menu = (*builder) (window->node);
2033 if (builder == info_menu_of_node)
2034 info_error (msg_no_menu_node);
2036 info_error (msg_no_xref_node);
2040 /* Default the selected reference to the one which is on the line that
2043 REFERENCE **refs = (REFERENCE **)NULL;
2046 point_line = window_line_of_point (window);
2048 if (point_line != -1)
2050 SEARCH_BINDING binding;
2052 binding.buffer = window->node->contents;
2053 binding.start = window->line_starts[point_line] - binding.buffer;
2054 if (window->line_starts[point_line + 1])
2055 binding.end = window->line_starts[point_line + 1] - binding.buffer;
2057 binding.end = window->node->nodelen;
2060 if (builder == info_menu_of_node)
2065 refs = info_menu_items (&binding);
2070 #if defined (HANDLE_MAN_PAGES)
2071 if (window->node->flags & N_IsManPage)
2072 refs = manpage_xrefs_in_binding (window->node, &binding);
2074 #endif /* HANDLE_MAN_PAGES */
2076 refs = info_xrefs (&binding);
2077 if (!refs && point_line > 0)
2079 /* People get annoyed that Info cannot find an xref
2080 which starts on a previous line and ends on this
2081 one. So if we fail to find a reference on this
2082 line, let's try the one before. */
2084 window->line_starts[point_line - 1] - binding.buffer;
2085 refs = info_xrefs (&binding);
2092 if ((strcmp (refs[0]->label, "Menu") != 0) ||
2093 (builder == info_xrefs_of_node))
2097 /* Find the closest reference to point. */
2098 if (builder == info_xrefs_of_node)
2102 for (; refs[which]; which++)
2104 if ((window->point >= refs[which]->start) &&
2105 (window->point <= refs[which]->end))
2110 else if (window->point < refs[which]->start)
2121 defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2122 defentry->label = xstrdup (refs[which]->label);
2123 defentry->filename = refs[which]->filename;
2124 defentry->nodename = refs[which]->nodename;
2126 if (defentry->filename)
2127 defentry->filename = xstrdup (defentry->filename);
2128 if (defentry->nodename)
2129 defentry->nodename = xstrdup (defentry->nodename);
2131 info_free_references (refs);
2136 /* If we are going to ask the user a question, do it now. */
2141 /* Build the prompt string. */
2143 prompt = (char *)xmalloc (20 + strlen (defentry->label));
2145 prompt = (char *)xmalloc (20);
2147 if (builder == info_menu_of_node)
2150 sprintf (prompt, _("Menu item (%s): "), defentry->label);
2152 sprintf (prompt, _("Menu item: "));
2157 sprintf (prompt, _("Follow xref (%s): "), defentry->label);
2159 sprintf (prompt, _("Follow xref: "));
2162 line = info_read_completing_in_echo_area (window, prompt, menu);
2165 window = active_window;
2167 /* User aborts, just quit. */
2170 maybe_free (defentry);
2171 info_free_references (menu);
2172 info_abort_key (window, 0, 0);
2176 /* If we had a default and the user accepted it, use that. */
2181 line = xstrdup (defentry->label);
2183 line = (char *)NULL;
2188 /* Not going to ask any questions. If we have a default entry, use
2189 that, otherwise return. */
2193 line = xstrdup (defentry->label);
2198 /* It is possible that the references have more than a single
2199 entry with the same label, and also LINE is down-cased, which
2200 complicates matters even more. Try to be as accurate as we
2201 can: if they've chosen the default, use defentry directly. */
2202 if (defentry && strcmp (line, defentry->label) == 0)
2205 /* Find the selected label in the references. If there are
2206 more than one label which matches, find the one that's
2207 closest to point. */
2210 int best = -1, min_dist = window->node->nodelen;
2213 for (i = 0; menu && (ref = menu[i]); i++)
2215 /* Need to use strcasecmp because LINE is downcased
2216 inside info_read_completing_in_echo_area. */
2217 if (strcasecmp (line, ref->label) == 0)
2219 /* ref->end is more accurate estimate of position
2220 for menus than ref->start. Go figure. */
2221 int dist = abs (window->point - ref->end);
2223 if (dist < min_dist)
2233 entry = (REFERENCE *)NULL;
2236 if (!entry && defentry)
2237 info_error (_("The reference disappeared! (%s)."), line);
2240 NODE *orig = window->node;
2241 info_select_reference (window, entry);
2242 if (builder == info_xrefs_of_node && window->node != orig
2243 && !(window->node->flags & N_FromAnchor))
2244 { /* Search for this reference in the node. */
2248 if (window->line_count > 0)
2249 start = window->line_starts[1] - window->node->contents;
2254 info_target_search_node (window->node, entry->label, start);
2258 window->point = offset;
2259 window_adjust_pagetop (window);
2267 free (defentry->label);
2268 maybe_free (defentry->filename);
2269 maybe_free (defentry->nodename);
2274 info_free_references (menu);
2276 if (!info_error_was_printed)
2277 window_clear_echo_area ();
2280 /* Read a line (with completion) which is the name of a menu item,
2281 and select that item. */
2282 DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
2284 info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2287 /* Read a line (with completion) which is the name of a reference to
2288 follow, and select the node. */
2289 DECLARE_INFO_COMMAND
2290 (info_xref_item, _("Read a footnote or cross reference and select its node"))
2292 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2295 /* Position the cursor at the start of this node's menu. */
2296 DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
2298 SEARCH_BINDING binding;
2301 binding.buffer = window->node->contents;
2303 binding.end = window->node->nodelen;
2304 binding.flags = S_FoldCase | S_SkipDest;
2306 position = search (INFO_MENU_LABEL, &binding);
2309 info_error (msg_no_menu_node);
2312 window->point = position;
2313 window_adjust_pagetop (window);
2314 window->flags |= W_UpdateWindow;
2318 /* Visit as many menu items as is possible, each in a separate window. */
2319 DECLARE_INFO_COMMAND (info_visit_menu,
2320 _("Visit as many menu items at once as possible"))
2323 REFERENCE *entry, **menu;
2325 menu = info_menu_of_node (window->node);
2328 info_error (msg_no_menu_node);
2330 for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2334 new = window_make_window (window->node);
2335 window_tile_windows (TILE_INTERNALS);
2338 info_error (msg_win_too_small);
2341 active_window = new;
2342 info_select_reference (new, entry);
2347 /* Read a line of input which is a node name, and go to that node. */
2348 DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
2352 #define GOTO_COMPLETES
2353 #if defined (GOTO_COMPLETES)
2354 /* Build a completion list of all of the known nodes. */
2356 register int fbi, i;
2357 FILE_BUFFER *current;
2358 REFERENCE **items = (REFERENCE **)NULL;
2359 int items_index = 0;
2360 int items_slots = 0;
2362 current = file_buffer_of_window (window);
2364 for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2368 int this_is_the_current_fb;
2370 fb = info_loaded_files[fbi];
2371 this_is_the_current_fb = (current == fb);
2373 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2374 entry->filename = entry->nodename = (char *)NULL;
2375 entry->label = (char *)xmalloc (4 + strlen (fb->filename));
2376 sprintf (entry->label, "(%s)*", fb->filename);
2378 add_pointer_to_array
2379 (entry, items_index, items, items_slots, 10, REFERENCE *);
2383 for (i = 0; fb->tags[i]; i++)
2385 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2386 entry->filename = entry->nodename = (char *)NULL;
2387 if (this_is_the_current_fb)
2388 entry->label = xstrdup (fb->tags[i]->nodename);
2391 entry->label = (char *) xmalloc
2392 (4 + strlen (fb->filename) +
2393 strlen (fb->tags[i]->nodename));
2394 sprintf (entry->label, "(%s)%s",
2395 fb->filename, fb->tags[i]->nodename);
2398 add_pointer_to_array
2399 (entry, items_index, items, items_slots, 100, REFERENCE *);
2403 line = info_read_maybe_completing (window, _("Goto node: "), items);
2404 info_free_references (items);
2406 #else /* !GOTO_COMPLETES */
2407 line = info_read_in_echo_area (window, _("Goto node: "));
2408 #endif /* !GOTO_COMPLETES */
2410 /* If the user aborted, quit now. */
2413 info_abort_key (window, 0, 0);
2417 canonicalize_whitespace (line);
2420 info_parse_and_select (line, window);
2423 if (!info_error_was_printed)
2424 window_clear_echo_area ();
2427 /* Follow the menu list in MENUS (list of strings terminated by a NULL
2428 entry) from INITIAL_NODE. If can't continue at any point (no menu or
2429 no menu entry for the next item), return the node so far -- that
2430 might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will
2431 be set to the error message and argument for message, otherwise they
2435 info_follow_menus (initial_node, menus, errstr, errarg1, errarg2)
2438 char **errstr, **errarg1, **errarg2;
2441 *errstr = *errarg1 = *errarg2 = NULL;
2443 for (; *menus; menus++)
2445 static char *first_arg = NULL;
2448 char *arg = *menus; /* Remember the name of the menu entry we want. */
2450 /* A leading space is certainly NOT part of a node name. Most
2451 probably, they typed a space after the separating comma. The
2452 strings in menus[] have their whitespace canonicalized, so
2453 there's at most one space to ignore. */
2459 /* Build and return a list of the menu items in this node. */
2460 menu = info_menu_of_node (initial_node);
2462 /* If no menu item in this node, stop here, but let the user
2463 continue to use Info. Perhaps they wanted this node and didn't
2467 if (arg == first_arg)
2469 node = make_manpage_node (first_arg);
2471 goto maybe_got_node;
2473 *errstr = _("No menu in node `%s'.");
2474 *errarg1 = node_printed_rep (initial_node);
2475 return initial_node;
2478 /* Find the specified menu item. */
2479 entry = info_get_labeled_reference (arg, menu);
2481 /* If the item wasn't found, search the list sloppily. Perhaps this
2482 user typed "buffer" when they really meant "Buffers". */
2486 int best_guess = -1;
2488 for (i = 0; (entry = menu[i]); i++)
2490 if (strcasecmp (entry->label, arg) == 0)
2493 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2497 if (!entry && best_guess != -1)
2498 entry = menu[best_guess];
2501 /* If we still failed to find the reference, start Info with the current
2502 node anyway. It is probably a misspelling. */
2505 if (arg == first_arg)
2507 /* Maybe they typed "info foo" instead of "info -f foo". */
2508 node = info_get_node (first_arg, 0);
2510 add_file_directory_to_path (first_arg);
2512 node = make_manpage_node (first_arg);
2514 goto maybe_got_node;
2517 info_free_references (menu);
2518 *errstr = _("No menu item `%s' in node `%s'.");
2520 *errarg2 = node_printed_rep (initial_node);
2521 return initial_node;
2524 /* We have found the reference that the user specified. If no
2525 filename in this reference, define it. */
2526 if (!entry->filename)
2527 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2528 : initial_node->filename);
2530 /* Try to find this node. */
2531 node = info_get_node (entry->filename, entry->nodename);
2532 if (!node && arg == first_arg)
2534 node = make_manpage_node (first_arg);
2536 goto maybe_got_node;
2539 /* Since we cannot find it, try using the label of the entry as a
2540 file, i.e., "(LABEL)Top". */
2541 if (!node && entry->nodename
2542 && strcmp (entry->label, entry->nodename) == 0)
2543 node = info_get_node (entry->label, "Top");
2548 *errstr = _("Unable to find node referenced by `%s' in `%s'.");
2549 *errarg1 = xstrdup (entry->label);
2550 *errarg2 = node_printed_rep (initial_node);
2551 info_free_references (menu);
2552 return initial_node;
2555 info_free_references (menu);
2557 /* Success. Go round the loop again. */
2558 free (initial_node);
2559 initial_node = node;
2562 return initial_node;
2565 /* Split STR into individual node names by writing null bytes in wherever
2566 there are commas and constructing a list of the resulting pointers.
2567 (We can do this since STR has had canonicalize_whitespace called on it.)
2568 Return array terminated with NULL. */
2571 split_list_of_nodenames (str)
2575 char **nodes = xmalloc (len * sizeof (char *));
2577 nodes[len - 2] = str;
2583 *str++ = 0; /* get past the null byte */
2585 nodes = xrealloc (nodes, len * sizeof (char *));
2586 nodes[len - 2] = str;
2590 nodes[len - 1] = NULL;
2596 /* Read a line of input which is a sequence of menus (starting from
2597 dir), and follow them. */
2598 DECLARE_INFO_COMMAND (info_menu_sequence,
2599 _("Read a list of menus starting from dir and follow them"))
2601 char *line = info_read_in_echo_area (window, _("Follow menus: "));
2603 /* If the user aborted, quit now. */
2606 info_abort_key (window, 0, 0);
2610 canonicalize_whitespace (line);
2614 char *errstr, *errarg1, *errarg2;
2615 NODE *dir_node = info_get_node (NULL, NULL);
2616 char **nodes = split_list_of_nodenames (line);
2619 /* If DIR_NODE is NULL, they might be reading a file directly,
2620 like in "info -d . -f ./foo". Try using "Top" instead. */
2623 char *file_name = window->node->parent;
2626 file_name = window->node->filename;
2627 dir_node = info_get_node (file_name, NULL);
2630 /* If we still cannot find the starting point, give up.
2631 We cannot allow a NULL pointer inside info_follow_menus. */
2633 info_error (msg_cant_find_node, "Top");
2636 = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
2640 info_set_node_of_window (1, window, node);
2642 info_error (errstr, errarg1, errarg2);
2646 if (!info_error_was_printed)
2647 window_clear_echo_area ();
2650 /* Search the menu MENU for a (possibly mis-spelled) entry ARG.
2651 Return the menu entry, or the best guess for what they meant by ARG,
2652 or NULL if there's nothing in this menu seems to fit the bill.
2653 If EXACT is non-zero, allow only exact matches. */
2655 entry_in_menu (arg, menu, exact)
2662 /* First, try to find the specified menu item verbatim. */
2663 entry = info_get_labeled_reference (arg, menu);
2665 /* If the item wasn't found, search the list sloppily. Perhaps we
2666 have "Option Summary", but ARG is "option". */
2667 if (!entry && !exact)
2670 int best_guess = -1;
2672 for (i = 0; (entry = menu[i]); i++)
2674 if (strcasecmp (entry->label, arg) == 0)
2677 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2681 if (!entry && best_guess != -1)
2682 entry = menu[best_guess];
2688 /* Find the node that is the best candidate to list the PROGRAM's
2689 invocation info and its command-line options, by looking for menu
2690 items and chains of menu items with characteristic names. */
2692 info_intuit_options_node (window, initial_node, program)
2697 /* The list of node names typical for GNU manuals where the program
2698 usage and specifically the command-line arguments are described.
2699 This is pure heuristics. I gathered these node names by looking
2700 at all the Info files I could put my hands on. If you are
2701 looking for evidence to complain to the GNU project about
2702 non-uniform style of documentation, here you have your case! */
2703 static const char *invocation_nodes[] = {
2706 "Preliminaries", /* m4 has Invoking under Preliminaries! */
2708 "Command Arguments",/* Emacs */
2712 "Option ", /* e.g. "Option Summary" */
2714 "All options", /* tar, paxutils */
2716 "%s cmdline", /* ar */
2717 "%s", /* last resort */
2722 const char **try_node;
2724 /* We keep looking deeper and deeper in the menu structure until
2725 there are no more menus or no menu items from the above list.
2726 Some manuals have the invocation node sitting 3 or 4 levels deep
2727 in the menu hierarchy... */
2728 for (node = initial_node; node; initial_node = node)
2732 /* Build and return a list of the menu items in this node. */
2733 menu = info_menu_of_node (initial_node);
2735 /* If no menu item in this node, stop here. Perhaps this node
2736 is the one they need. */
2740 /* Look for node names typical for usage nodes in this menu. */
2741 for (try_node = invocation_nodes; *try_node; try_node++)
2745 sprintf (nodename, *try_node, program);
2746 /* The last resort "%s" is dangerous, so we restrict it
2747 to exact matches here. */
2748 entry = entry_in_menu (nodename, menu,
2749 strcmp (*try_node, "%s") == 0);
2757 if (!entry->filename)
2758 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2759 : initial_node->filename);
2760 /* Try to find this node. */
2761 node = info_get_node (entry->filename, entry->nodename);
2762 info_free_references (menu);
2767 /* We've got our best shot at the invocation node. Now select it. */
2769 info_set_node_of_window (1, window, initial_node);
2770 if (!info_error_was_printed)
2771 window_clear_echo_area ();
2774 /* Given a name of an Info file, find the name of the package it
2775 describes by removing the leading directories and extensions. */
2777 program_name_from_file_name (file_name)
2781 char *program_name = xstrdup (filename_non_directory (file_name));
2783 for (i = strlen (program_name) - 1; i > 0; i--)
2784 if (program_name[i] == '.'
2785 && (FILENAME_CMPN (program_name + i, ".info", 5) == 0
2786 || FILENAME_CMPN (program_name + i, ".inf", 4) == 0
2788 || FILENAME_CMPN (program_name + i, ".i", 2) == 0
2790 || isdigit (program_name[i + 1]))) /* a man page foo.1 */
2792 program_name[i] = 0;
2795 return program_name;
2798 DECLARE_INFO_COMMAND (info_goto_invocation_node,
2799 _("Find the node describing program invocation"))
2801 char *invocation_prompt = _("Find Invocation node of [%s]: ");
2802 char *program_name, *line;
2803 char *default_program_name, *prompt, *file_name;
2806 /* Intuit the name of the program they are likely to want.
2807 We use the file name of the current Info file as a hint. */
2808 file_name = window->node->parent ? window->node->parent
2809 : window->node->filename;
2810 default_program_name = program_name_from_file_name (file_name);
2812 prompt = (char *)xmalloc (strlen (default_program_name) +
2813 strlen (invocation_prompt));
2814 sprintf (prompt, invocation_prompt, default_program_name);
2815 line = info_read_in_echo_area (window, prompt);
2823 program_name = line;
2825 program_name = default_program_name;
2827 /* In interactive usage they'd probably expect us to begin looking
2828 from the Top node. */
2829 top_node = info_get_node (file_name, NULL);
2831 info_error (msg_cant_find_node, "Top");
2833 info_intuit_options_node (window, top_node, program_name);
2835 free (default_program_name);
2838 #if defined (HANDLE_MAN_PAGES)
2839 DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
2843 line = info_read_in_echo_area (window, _("Get Manpage: "));
2847 info_abort_key (window, 0, 0);
2851 canonicalize_whitespace (line);
2857 goto_command = (char *)xmalloc
2858 (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
2860 sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
2862 info_parse_and_select (goto_command, window);
2863 free (goto_command);
2867 if (!info_error_was_printed)
2868 window_clear_echo_area ();
2870 #endif /* HANDLE_MAN_PAGES */
2872 /* Move to the "Top" node in this file. */
2873 DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
2875 info_parse_and_select ("Top", window);
2878 /* Move to the node "(dir)Top". */
2879 DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
2881 info_parse_and_select ("(dir)Top", window);
2885 /* Read the name of a node to kill. The list of available nodes comes
2886 from the nodes appearing in the current window configuration. */
2888 read_nodename_to_kill (window)
2893 INFO_WINDOW *info_win;
2894 REFERENCE **menu = NULL;
2895 int menu_index = 0, menu_slots = 0;
2896 char *default_nodename = xstrdup (active_window->node->nodename);
2897 char *prompt = xmalloc (40 + strlen (default_nodename));
2899 sprintf (prompt, _("Kill node (%s): "), default_nodename);
2901 for (iw = 0; (info_win = info_windows[iw]); iw++)
2903 REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2904 entry->label = xstrdup (info_win->window->node->nodename);
2905 entry->filename = entry->nodename = (char *)NULL;
2907 add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
2911 nodename = info_read_completing_in_echo_area (window, prompt, menu);
2913 info_free_references (menu);
2914 if (nodename && !*nodename)
2917 nodename = default_nodename;
2920 free (default_nodename);
2926 /* Delete NODENAME from this window, showing the most
2927 recently selected node in this window. */
2929 kill_node (window, nodename)
2934 INFO_WINDOW *info_win;
2937 /* If there is no nodename to kill, quit now. */
2940 info_abort_key (window, 0, 0);
2944 /* If there is a nodename, find it in our window list. */
2945 for (iw = 0; (info_win = info_windows[iw]); iw++)
2946 if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
2947 && info_win->window == window)
2953 info_error (_("Cannot kill node `%s'"), nodename);
2955 window_clear_echo_area ();
2960 /* If there are no more nodes left anywhere to view, complain and exit. */
2961 if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
2963 info_error (_("Cannot kill the last node"));
2967 /* INFO_WIN contains the node that the user wants to stop viewing. Delete
2968 this node from the list of nodes previously shown in this window. */
2969 for (i = info_win->current; i < info_win->nodes_index; i++)
2970 info_win->nodes[i] = info_win->nodes[i + 1];
2972 /* There is one less node in this window's history list. */
2973 info_win->nodes_index--;
2975 /* Make this window show the most recent history node. */
2976 info_win->current = info_win->nodes_index - 1;
2978 /* If there aren't any nodes left in this window, steal one from the
2980 if (info_win->current < 0)
2982 INFO_WINDOW *stealer;
2986 if (info_windows[iw + 1])
2987 stealer = info_windows[iw + 1];
2989 stealer = info_windows[0];
2991 /* If the node being displayed in the next window is not the most
2992 recently loaded one, get the most recently loaded one. */
2993 if ((stealer->nodes_index - 1) != stealer->current)
2994 which = stealer->nodes_index - 1;
2996 /* Else, if there is another node behind the stealers current node,
2998 else if (stealer->current > 0)
2999 which = stealer->current - 1;
3001 /* Else, just use the node appearing in STEALER's window. */
3003 which = stealer->current;
3005 /* Copy this node. */
3007 NODE *copy = xmalloc (sizeof (NODE));
3009 temp = stealer->nodes[which];
3010 point = stealer->points[which];
3011 pagetop = stealer->pagetops[which];
3013 copy->filename = temp->filename;
3014 copy->parent = temp->parent;
3015 copy->nodename = temp->nodename;
3016 copy->contents = temp->contents;
3017 copy->nodelen = temp->nodelen;
3018 copy->flags = temp->flags;
3019 copy->display_pos = temp->display_pos;
3024 window_set_node_of_window (info_win->window, temp);
3025 window->point = point;
3026 window->pagetop = pagetop;
3027 remember_window_and_node (info_win->window, temp);
3031 temp = info_win->nodes[info_win->current];
3032 temp->display_pos = info_win->points[info_win->current];
3033 window_set_node_of_window (info_win->window, temp);
3036 if (!info_error_was_printed)
3037 window_clear_echo_area ();
3039 if (auto_footnotes_p)
3040 info_get_or_remove_footnotes (window);
3043 /* Kill current node, thus going back one in the node history. I (karl)
3044 do not think this is completely correct yet, because of the
3045 window-changing stuff in kill_node, but it's a lot better than the
3046 previous implementation, which did not account for nodes being
3047 visited twice at all. */
3048 DECLARE_INFO_COMMAND (info_history_node,
3049 _("Select the most recently selected node"))
3051 kill_node (window, active_window->node->nodename);
3054 /* Kill named node. */
3055 DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
3057 char *nodename = read_nodename_to_kill (window);
3058 kill_node (window, nodename);
3062 /* Read the name of a file and select the entire file. */
3063 DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
3067 line = info_read_in_echo_area (window, _("Find file: "));
3070 info_abort_key (active_window, 1, 0);
3078 node = info_get_node (line, "*");
3081 if (info_recent_file_error)
3082 info_error (info_recent_file_error);
3084 info_error (_("Cannot find `%s'."), line);
3087 info_set_node_of_window (1, window, node);
3092 if (!info_error_was_printed)
3093 window_clear_echo_area ();
3096 /* **************************************************************** */
3098 /* Dumping and Printing Nodes */
3100 /* **************************************************************** */
3102 #define VERBOSE_NODE_DUMPING
3103 static void write_node_to_stream ();
3104 static void dump_node_to_stream ();
3105 static void initialize_dumping ();
3107 /* Dump the nodes specified by FILENAME and NODENAMES to the file named
3108 in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3109 the nodes which appear in the menu of each node dumped. */
3111 dump_nodes_to_file (filename, nodenames, output_filename, dump_subnodes)
3114 char *output_filename;
3118 FILE *output_stream;
3120 /* Get the stream to print the nodes to. Special case of an output
3121 filename of "-" means to dump the nodes to stdout. */
3122 if (strcmp (output_filename, "-") == 0)
3123 output_stream = stdout;
3125 output_stream = fopen (output_filename, "w");
3129 info_error (_("Could not create output file `%s'."), output_filename);
3133 /* Print each node to stream. */
3134 initialize_dumping ();
3135 for (i = 0; nodenames[i]; i++)
3136 dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
3138 if (output_stream != stdout)
3139 fclose (output_stream);
3141 #if defined (VERBOSE_NODE_DUMPING)
3142 info_error (_("Done."));
3143 #endif /* VERBOSE_NODE_DUMPING */
3146 /* A place to remember already dumped nodes. */
3147 static char **dumped_already = (char **)NULL;
3148 static int dumped_already_index = 0;
3149 static int dumped_already_slots = 0;
3152 initialize_dumping ()
3154 dumped_already_index = 0;
3157 /* Get and print the node specified by FILENAME and NODENAME to STREAM.
3158 If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
3159 in the menu of each node dumped. */
3161 dump_node_to_stream (filename, nodename, stream, dump_subnodes)
3162 char *filename, *nodename;
3169 node = info_get_node (filename, nodename);
3173 if (info_recent_file_error)
3174 info_error (info_recent_file_error);
3177 if (filename && *nodename != '(')
3178 info_error (msg_cant_file_node, filename_non_directory (filename),
3181 info_error (msg_cant_find_node, nodename);
3186 /* If we have already dumped this node, don't dump it again. */
3187 for (i = 0; i < dumped_already_index; i++)
3188 if (strcmp (node->nodename, dumped_already[i]) == 0)
3193 add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
3194 dumped_already_slots, 50, char *);
3196 #if defined (VERBOSE_NODE_DUMPING)
3197 /* Maybe we should print some information about the node being output. */
3198 info_error (_("Writing node %s..."), node_printed_rep (node));
3199 #endif /* VERBOSE_NODE_DUMPING */
3201 write_node_to_stream (node, stream);
3203 /* If we are dumping subnodes, get the list of menu items in this node,
3204 and dump each one recursively. */
3207 REFERENCE **menu = (REFERENCE **)NULL;
3209 /* If this node is an Index, do not dump the menu references. */
3210 if (string_in_line ("Index", node->nodename) == -1)
3211 menu = info_menu_of_node (node);
3215 for (i = 0; menu[i]; i++)
3217 /* We don't dump Info files which are different than the
3219 if (!menu[i]->filename)
3221 (filename, menu[i]->nodename, stream, dump_subnodes);
3223 info_free_references (menu);
3230 /* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3231 the nodes which appear in the menu of each node dumped. */
3233 dump_node_to_file (node, filename, dump_subnodes)
3238 FILE *output_stream;
3239 char *nodes_filename;
3241 /* Get the stream to print this node to. Special case of an output
3242 filename of "-" means to dump the nodes to stdout. */
3243 if (strcmp (filename, "-") == 0)
3244 output_stream = stdout;
3246 output_stream = fopen (filename, "w");
3250 info_error (_("Could not create output file `%s'."), filename);
3255 nodes_filename = node->parent;
3257 nodes_filename = node->filename;
3259 initialize_dumping ();
3261 (nodes_filename, node->nodename, output_stream, dump_subnodes);
3263 if (output_stream != stdout)
3264 fclose (output_stream);
3266 #if defined (VERBOSE_NODE_DUMPING)
3267 info_error (_("Done."));
3268 #endif /* VERBOSE_NODE_DUMPING */
3271 #if !defined (DEFAULT_INFO_PRINT_COMMAND)
3272 # define DEFAULT_INFO_PRINT_COMMAND "lpr"
3273 #endif /* !DEFAULT_INFO_PRINT_COMMAND */
3275 DECLARE_INFO_COMMAND (info_print_node,
3276 _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
3278 print_node (window->node);
3281 /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3287 char *print_command = getenv ("INFO_PRINT_COMMAND");
3290 if (!print_command || !*print_command)
3291 print_command = DEFAULT_INFO_PRINT_COMMAND;
3293 /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
3294 (default) text mode, since the printer drivers there need to see
3295 DOS-style CRLF pairs at the end of each line.
3297 FIXME: if we are to support Mac-style text files, we might need
3298 to convert the text here. */
3300 /* INFO_PRINT_COMMAND which says ">file" means write to that file.
3301 Presumably, the name of the file is the local printer device. */
3302 if (*print_command == '>')
3303 printer_pipe = fopen (++print_command, "w");
3306 printer_pipe = popen (print_command, "w");
3312 info_error (_("Cannot open pipe to `%s'."), print_command);
3316 #if defined (VERBOSE_NODE_DUMPING)
3317 /* Maybe we should print some information about the node being output. */
3318 info_error (_("Printing node %s..."), node_printed_rep (node));
3319 #endif /* VERBOSE_NODE_DUMPING */
3321 write_node_to_stream (node, printer_pipe);
3323 pclose (printer_pipe);
3325 fclose (printer_pipe);
3327 #if defined (VERBOSE_NODE_DUMPING)
3328 info_error (_("Done."));
3329 #endif /* VERBOSE_NODE_DUMPING */
3333 write_node_to_stream (node, stream)
3337 fwrite (node->contents, 1, node->nodelen, stream);
3340 /* **************************************************************** */
3342 /* Info Searching Commands */
3344 /* **************************************************************** */
3346 /* Variable controlling the garbage collection of files briefly visited
3347 during searches. Such files are normally gc'ed, unless they were
3348 compressed to begin with. If this variable is non-zero, it says
3349 to gc even those file buffer contents which had to be uncompressed. */
3350 int gc_compressed_files = 0;
3352 static void info_gc_file_buffers ();
3353 static void info_search_1 ();
3355 static char *search_string = (char *)NULL;
3356 static int search_string_index = 0;
3357 static int search_string_size = 0;
3358 static int isearch_is_active = 0;
3360 static int last_search_direction = 0;
3361 static int last_search_case_sensitive = 0;
3363 /* Return the file buffer which belongs to WINDOW's node. */
3365 file_buffer_of_window (window)
3368 /* If this window has no node, then it has no file buffer. */
3370 return ((FILE_BUFFER *)NULL);
3372 if (window->node->parent)
3373 return (info_find_file (window->node->parent));
3375 if (window->node->filename)
3376 return (info_find_file (window->node->filename));
3378 return ((FILE_BUFFER *)NULL);
3381 /* Search for STRING in NODE starting at START. Return -1 if the string
3382 was not found, or the location of the string if it was. If WINDOW is
3383 passed as non-null, set the window's node to be NODE, its point to be
3384 the found string, and readjust the window's pagetop. Final argument
3385 DIR says which direction to search in. If it is positive, search
3386 forward, else backwards. */
3388 info_search_in_node (string, node, start, window, dir, case_sensitive)
3393 int dir, case_sensitive;
3395 SEARCH_BINDING binding;
3398 binding.buffer = node->contents;
3399 binding.start = start;
3400 binding.end = node->nodelen;
3402 if (!case_sensitive)
3403 binding.flags |= S_FoldCase;
3408 binding.flags |= S_SkipDest;
3411 if (binding.start < 0)
3414 /* For incremental searches, we always wish to skip past the string. */
3415 if (isearch_is_active)
3416 binding.flags |= S_SkipDest;
3418 offset = search (string, &binding);
3420 if (offset != -1 && window)
3422 set_remembered_pagetop_and_point (window);
3423 if (window->node != node)
3424 window_set_node_of_window (window, node);
3425 window->point = offset;
3426 window_adjust_pagetop (window);
3431 /* Search NODE, looking for the largest possible match of STRING. Start the
3432 search at START. Return the absolute position of the match, or -1, if
3433 no part of the string could be found. */
3435 info_target_search_node (node, string, start)
3444 target = xstrdup (string);
3445 i = strlen (target);
3447 /* Try repeatedly searching for this string while removing words from
3452 offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
3457 /* Delete the last word from TARGET. */
3458 for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
3464 /* Search for STRING starting in WINDOW at point. If the string is found
3465 in this node, set point to that position. Otherwise, get the file buffer
3466 associated with WINDOW's node, and search through each node in that file.
3467 If the search fails, return non-zero, else zero. Side-effect window
3468 leaving the node and point where the string was found current. */
3470 info_search_internal (string, window, dir, case_sensitive)
3473 int dir, case_sensitive;
3476 FILE_BUFFER *file_buffer;
3477 char *initial_nodename;
3478 long ret, start = 0;
3480 file_buffer = file_buffer_of_window (window);
3481 initial_nodename = window->node->nodename;
3483 /* This used to begin from window->point, unless this was a repeated
3484 search command. But invoking search with an argument loses with
3485 that logic, since info_last_executed_command is then set to
3486 info_add_digit_to_numeric_arg. I think there's no sense in
3487 ``finding'' a string that is already under the cursor, anyway. */
3488 ret = info_search_in_node
3489 (string, window->node, window->point + dir, window, dir,
3495 if (!echo_area_is_active && !isearch_is_active)
3496 window_clear_echo_area ();
3500 /* The string wasn't found in the current node. Search through the
3501 window's file buffer, iff the current node is not "*". */
3502 if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
3505 /* If this file has tags, search through every subfile, starting at
3506 this node's subfile and node. Otherwise, search through the
3507 file's node list. */
3508 if (file_buffer->tags)
3510 register int current_tag, number_of_tags;
3514 /* Find number of tags and current tag. */
3515 last_subfile = (char *)NULL;
3516 for (i = 0; file_buffer->tags[i]; i++)
3517 if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
3520 last_subfile = file_buffer->tags[i]->filename;
3525 /* If there is no last_subfile, our tag wasn't found. */
3529 /* Search through subsequent nodes, wrapping around to the top
3530 of the info file until we find the string or return to this
3531 window's node and point. */
3536 /* Allow C-g to quit the search, failing it if pressed. */
3537 return_if_control_g (-1);
3539 /* Find the next tag that isn't an anchor. */
3540 for (i = current_tag + dir; i != current_tag; i += dir)
3543 i = number_of_tags - 1;
3544 else if (i == number_of_tags)
3547 tag = file_buffer->tags[i];
3548 if (tag->nodelen != 0)
3552 /* If we got past out starting point, bail out. */
3553 if (i == current_tag)
3557 if (!echo_area_is_active && (last_subfile != tag->filename))
3559 window_message_in_echo_area
3560 (_("Searching subfile %s ..."),
3561 filename_non_directory (tag->filename));
3563 last_subfile = tag->filename;
3566 node = info_get_node (file_buffer->filename, tag->nodename);
3570 /* If not doing i-search... */
3571 if (!echo_area_is_active)
3573 if (info_recent_file_error)
3574 info_error (info_recent_file_error);
3576 info_error (msg_cant_file_node,
3577 filename_non_directory (file_buffer->filename),
3584 start = tag->nodelen;
3587 info_search_in_node (string, node, start, window, dir,
3590 /* Did we find the string in this node? */
3594 remember_window_and_node (window, node);
3595 if (!echo_area_is_active)
3596 window_clear_echo_area ();
3600 /* No. Free this node, and make sure that we haven't passed
3601 our starting point. */
3604 if (strcmp (initial_nodename, tag->nodename) == 0)
3611 DECLARE_INFO_COMMAND (info_search_case_sensitively,
3612 _("Read a string and search for it case-sensitively"))
3614 last_search_direction = count > 0 ? 1 : -1;
3615 last_search_case_sensitive = 1;
3616 info_search_1 (window, count, key, 1, 1);
3619 DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
3621 last_search_direction = count > 0 ? 1 : -1;
3622 last_search_case_sensitive = 0;
3623 info_search_1 (window, count, key, 0, 1);
3626 DECLARE_INFO_COMMAND (info_search_backward,
3627 _("Read a string and search backward for it"))
3629 last_search_direction = count > 0 ? -1 : 1;
3630 last_search_case_sensitive = 0;
3631 info_search_1 (window, -count, key, 0, 1);
3635 info_search_1 (window, count, key, case_sensitive, ask_for_string)
3642 char *line, *prompt;
3643 int result, old_pagetop;
3655 count = 1; /* for backward compatibility */
3658 /* Read a string from the user, defaulting the search to SEARCH_STRING. */
3661 search_string = (char *)xmalloc (search_string_size = 100);
3662 search_string[0] = '\0';
3667 prompt = (char *)xmalloc (50 + strlen (search_string));
3669 sprintf (prompt, _("%s%sfor string [%s]: "),
3670 direction < 0 ? _("Search backward") : _("Search"),
3671 case_sensitive ? _(" case-sensitively ") : _(" "),
3674 line = info_read_in_echo_area (window, prompt);
3685 if (strlen (line) + 1 > search_string_size)
3686 search_string = (char *) xrealloc
3687 (search_string, (search_string_size += 50 + strlen (line)));
3689 strcpy (search_string, line);
3690 search_string_index = strlen (line);
3695 /* If the search string includes upper-case letters, make the search
3697 if (case_sensitive == 0)
3698 for (line = search_string; *line; line++)
3699 if (isupper (*line))
3705 old_pagetop = active_window->pagetop;
3706 for (result = 0; result == 0 && count--; )
3707 result = info_search_internal (search_string,
3708 active_window, direction, case_sensitive);
3710 if (result != 0 && !info_error_was_printed)
3711 info_error (_("Search failed."));
3712 else if (old_pagetop != active_window->pagetop)
3716 new_pagetop = active_window->pagetop;
3717 active_window->pagetop = old_pagetop;
3718 set_window_pagetop (active_window, new_pagetop);
3719 if (auto_footnotes_p)
3720 info_get_or_remove_footnotes (active_window);
3723 /* Perhaps free the unreferenced file buffers that were searched, but
3725 info_gc_file_buffers ();
3728 DECLARE_INFO_COMMAND (info_search_next,
3729 _("Repeat last search in the same direction"))
3731 if (!last_search_direction)
3732 info_error (_("No previous search string"));
3734 info_search_1 (window, last_search_direction * count,
3735 key, last_search_case_sensitive, 0);
3738 DECLARE_INFO_COMMAND (info_search_previous,
3739 _("Repeat last search in the reverse direction"))
3741 if (!last_search_direction)
3742 info_error (_("No previous search string"));
3744 info_search_1 (window, -last_search_direction * count,
3745 key, last_search_case_sensitive, 0);
3748 /* **************************************************************** */
3750 /* Incremental Searching */
3752 /* **************************************************************** */
3754 static void incremental_search ();
3756 DECLARE_INFO_COMMAND (isearch_forward,
3757 _("Search interactively for a string as you type it"))
3759 incremental_search (window, count, key);
3762 DECLARE_INFO_COMMAND (isearch_backward,
3763 _("Search interactively for a string as you type it"))
3765 incremental_search (window, -count, key);
3768 /* Incrementally search for a string as it is typed. */
3769 /* The last accepted incremental search string. */
3770 static char *last_isearch_accepted = (char *)NULL;
3772 /* The current incremental search string. */
3773 static char *isearch_string = (char *)NULL;
3774 static int isearch_string_index = 0;
3775 static int isearch_string_size = 0;
3776 static unsigned char isearch_terminate_search_key = ESC;
3778 /* Structure defining the current state of an incremental search. */
3780 WINDOW_STATE_DECL; /* The node, pagetop and point. */
3781 int search_index; /* Offset of the last char in the search string. */
3782 int direction; /* The direction that this search is heading in. */
3783 int failing; /* Whether or not this search failed. */
3786 /* Array of search states. */
3787 static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
3788 static int isearch_states_index = 0;
3789 static int isearch_states_slots = 0;
3791 /* Push the state of this search. */
3793 push_isearch (window, search_index, direction, failing)
3795 int search_index, direction, failing;
3797 SEARCH_STATE *state;
3799 state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
3800 window_get_state (window, state);
3801 state->search_index = search_index;
3802 state->direction = direction;
3803 state->failing = failing;
3805 add_pointer_to_array (state, isearch_states_index, isearch_states,
3806 isearch_states_slots, 20, SEARCH_STATE *);
3809 /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3811 pop_isearch (window, search_index, direction, failing)
3813 int *search_index, *direction, *failing;
3815 SEARCH_STATE *state;
3817 if (isearch_states_index)
3819 isearch_states_index--;
3820 state = isearch_states[isearch_states_index];
3821 window_set_state (window, state);
3822 *search_index = state->search_index;
3823 *direction = state->direction;
3824 *failing = state->failing;
3827 isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
3831 /* Free the memory used by isearch_states. */
3833 free_isearch_states ()
3837 for (i = 0; i < isearch_states_index; i++)
3839 free (isearch_states[i]);
3840 isearch_states[i] = (SEARCH_STATE *)NULL;
3842 isearch_states_index = 0;
3845 /* Display the current search in the echo area. */
3847 show_isearch_prompt (dir, string, failing_p)
3849 unsigned char *string;
3853 char *prefix, *prompt, *p_rep;
3854 int prompt_len, p_rep_index, p_rep_size;
3857 prefix = _("I-search backward: ");
3859 prefix = _("I-search: ");
3861 p_rep_index = p_rep_size = 0;
3862 p_rep = (char *)NULL;
3863 for (i = 0; string[i]; i++)
3869 case ' ': rep = " "; break;
3870 case LFD: rep = "\\n"; break;
3871 case TAB: rep = "\\t"; break;
3873 rep = pretty_keyname (string[i]);
3875 if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3876 p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3878 strcpy (p_rep + p_rep_index, rep);
3879 p_rep_index += strlen (rep);
3882 prompt_len = strlen (prefix) + p_rep_index + 20;
3883 prompt = (char *)xmalloc (prompt_len);
3884 sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
3885 p_rep ? p_rep : "");
3887 window_message_in_echo_area ("%s", prompt);
3890 display_cursor_at_point (active_window);
3894 incremental_search (window, count, ignore)
3897 unsigned char ignore;
3900 int last_search_result, search_result, dir;
3901 SEARCH_STATE mystate, orig_state;
3903 int case_sensitive = 0;
3910 last_search_result = search_result = 0;
3912 window_get_state (window, &orig_state);
3914 isearch_string_index = 0;
3915 if (!isearch_string_size)
3916 isearch_string = (char *)xmalloc (isearch_string_size = 50);
3918 /* Show the search string in the echo area. */
3919 isearch_string[isearch_string_index] = '\0';
3920 show_isearch_prompt (dir, isearch_string, search_result);
3922 isearch_is_active = 1;
3924 while (isearch_is_active)
3926 VFunction *func = (VFunction *)NULL;
3929 /* If a recent display was interrupted, then do the redisplay now if
3930 it is convenient. */
3931 if (!info_any_buffered_input_p () && display_was_interrupted_p)
3933 display_update_one_window (window);
3934 display_cursor_at_point (active_window);
3937 /* Read a character and dispatch on it. */
3938 key = info_get_input_char ();
3939 window_get_state (window, &mystate);
3943 /* User wants to delete one level of search? */
3944 if (!isearch_states_index)
3946 terminal_ring_bell ();
3952 (window, &isearch_string_index, &dir, &search_result);
3953 isearch_string[isearch_string_index] = '\0';
3954 show_isearch_prompt (dir, isearch_string, search_result);
3958 else if (key == Control ('q'))
3960 key = info_get_input_char ();
3964 /* We are about to search again, or quit. Save the current search. */
3965 push_isearch (window, isearch_string_index, dir, search_result);
3968 goto insert_and_search;
3970 if (!Meta_p (key) || key > 32)
3972 func = InfoFunction(window->keymap[key].function);
3974 if (isprint (key) || func == (VFunction *)NULL)
3978 if (isearch_string_index + 2 >= isearch_string_size)
3979 isearch_string = (char *)xrealloc
3980 (isearch_string, isearch_string_size += 100);
3982 isearch_string[isearch_string_index++] = key;
3983 isearch_string[isearch_string_index] = '\0';
3986 else if (func == isearch_forward || func == isearch_backward)
3988 /* If this key invokes an incremental search, then this
3989 means that we will either search again in the same
3990 direction, search again in the reverse direction, or
3991 insert the last search string that was accepted through
3992 incremental searching. */
3993 if ((func == isearch_forward && dir > 0) ||
3994 (func == isearch_backward && dir < 0))
3996 /* If the user has typed no characters, then insert the
3997 last successful search into the current search string. */
3998 if (isearch_string_index == 0)
4000 /* Of course, there must be something to insert. */
4001 if (last_isearch_accepted)
4003 if (strlen (last_isearch_accepted) + 1 >=
4004 isearch_string_size)
4005 isearch_string = (char *)
4006 xrealloc (isearch_string,
4007 isearch_string_size += 10 +
4008 strlen (last_isearch_accepted));
4009 strcpy (isearch_string, last_isearch_accepted);
4010 isearch_string_index = strlen (isearch_string);
4018 /* Search again in the same direction. This means start
4019 from a new place if the last search was successful. */
4020 if (search_result == 0)
4021 window->point += dir;
4026 /* Reverse the direction of the search. */
4030 else if (func == info_abort_key)
4032 /* If C-g pressed, and the search is failing, pop the search
4033 stack back to the last unfailed search. */
4034 if (isearch_states_index && (search_result != 0))
4036 terminal_ring_bell ();
4037 while (isearch_states_index && (search_result != 0))
4039 (window, &isearch_string_index, &dir, &search_result);
4040 isearch_string[isearch_string_index] = '\0';
4041 show_isearch_prompt (dir, isearch_string, search_result);
4053 /* The character is not printable, or it has a function which is
4054 non-null. Exit the search, remembering the search string. If
4055 the key is not the same as the isearch_terminate_search_key,
4056 then push it into pending input. */
4057 if (isearch_string_index && func != info_abort_key)
4059 maybe_free (last_isearch_accepted);
4060 last_isearch_accepted = xstrdup (isearch_string);
4063 /* If the key is the isearch_terminate_search_key, but some buffered
4064 input is pending, it is almost invariably because the ESC key is
4065 actually the beginning of an escape sequence, like in case they
4066 pressed an arrow key. So don't gobble the ESC key, push it back
4067 into pending input. */
4068 /* FIXME: this seems like a kludge! We need a more reliable
4069 mechanism to know when ESC is a separate key and when it is
4070 part of an escape sequence. */
4071 if (key != RET /* Emacs addicts want RET to get lost */
4072 && (key != isearch_terminate_search_key
4073 || info_any_buffered_input_p ()))
4074 info_set_pending_input (key);
4076 if (func == info_abort_key)
4078 if (isearch_states_index)
4079 window_set_state (window, &orig_state);
4082 if (!echo_area_is_active)
4083 window_clear_echo_area ();
4085 if (auto_footnotes_p)
4086 info_get_or_remove_footnotes (active_window);
4088 isearch_is_active = 0;
4092 /* Search for the contents of isearch_string. */
4094 show_isearch_prompt (dir, isearch_string, search_result);
4096 /* If the search string includes upper-case letters, make the
4097 search case-sensitive. */
4098 for (p = isearch_string; *p; p++)
4106 if (search_result == 0)
4108 /* Check to see if the current search string is right here. If
4109 we are looking at it, then don't bother calling the search
4112 ((case_sensitive ? strncmp : strncasecmp)
4113 (window->node->contents + window->point,
4114 isearch_string, isearch_string_index) == 0)) ||
4116 ((window->point - isearch_string_index) >= 0) &&
4117 ((case_sensitive ? strncmp : strncasecmp)
4118 (window->node->contents +
4119 (window->point - (isearch_string_index - 1)),
4120 isearch_string, isearch_string_index) == 0)))
4126 search_result = info_search_internal (isearch_string,
4127 window, dir, case_sensitive);
4130 /* If this search failed, and we didn't already have a failed search,
4131 then ring the terminal bell. */
4132 if (search_result != 0 && last_search_result == 0)
4133 terminal_ring_bell ();
4136 show_isearch_prompt (dir, isearch_string, search_result);
4138 if (search_result == 0)
4140 if ((mystate.node == window->node) &&
4141 (mystate.pagetop != window->pagetop))
4143 int newtop = window->pagetop;
4144 window->pagetop = mystate.pagetop;
4145 set_window_pagetop (window, newtop);
4147 display_update_one_window (window);
4148 display_cursor_at_point (window);
4151 last_search_result = search_result;
4154 /* Free the memory used to remember each search state. */
4155 free_isearch_states ();
4157 /* Perhaps GC some file buffers. */
4158 info_gc_file_buffers ();
4160 /* After searching, leave the window in the correct state. */
4161 if (!echo_area_is_active)
4162 window_clear_echo_area ();
4165 /* GC some file buffers. A file buffer can be gc-ed if there we have
4166 no nodes in INFO_WINDOWS that reference this file buffer's contents.
4167 Garbage collecting a file buffer means to free the file buffers
4170 info_gc_file_buffers ()
4172 register int fb_index, iw_index, i;
4173 register FILE_BUFFER *fb;
4174 register INFO_WINDOW *iw;
4176 if (!info_loaded_files)
4179 for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4181 int fb_referenced_p = 0;
4183 /* If already gc-ed, do nothing. */
4187 /* If this file had to be uncompressed, check to see if we should
4188 gc it. This means that the user-variable "gc-compressed-files"
4190 if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
4193 /* If this file's contents are not gc-able, move on. */
4194 if (fb->flags & N_CannotGC)
4197 /* Check each INFO_WINDOW to see if it has any nodes which reference
4199 for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
4201 for (i = 0; iw->nodes && iw->nodes[i]; i++)
4203 if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4204 (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
4206 fb_referenced_p = 1;
4212 /* If this file buffer wasn't referenced, free its contents. */
4213 if (!fb_referenced_p)
4215 free (fb->contents);
4216 fb->contents = (char *)NULL;
4221 /* **************************************************************** */
4223 /* Traversing and Selecting References */
4225 /* **************************************************************** */
4227 /* Move to the next or previous cross reference in this node. */
4229 info_move_to_xref (window, count, key, dir)
4235 long firstmenu, firstxref;
4236 long nextmenu, nextxref;
4237 long placement = -1;
4239 NODE *node = window->node;
4242 start = node->nodelen;
4244 /* This search is only allowed to fail if there is no menu or cross
4245 reference in the current node. Otherwise, the first menu or xref
4246 found is moved to. */
4248 firstmenu = info_search_in_node
4249 (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4251 /* FIRSTMENU may point directly to the line defining the menu. Skip that
4252 and go directly to the first item. */
4254 if (firstmenu != -1)
4256 char *text = node->contents + firstmenu;
4258 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4259 firstmenu = info_search_in_node
4260 (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0);
4264 info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4266 #if defined (HANDLE_MAN_PAGES)
4267 if ((firstxref == -1) && (node->flags & N_IsManPage))
4269 firstxref = locate_manpage_xref (node, start, dir);
4271 #endif /* HANDLE_MAN_PAGES */
4273 if (firstmenu == -1 && firstxref == -1)
4275 info_error (msg_no_xref_node);
4279 /* There is at least one cross reference or menu entry in this node.
4280 Try hard to find the next available one. */
4282 nextmenu = info_search_in_node
4283 (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4285 nextxref = info_search_in_node
4286 (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4288 #if defined (HANDLE_MAN_PAGES)
4289 if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
4290 nextxref = locate_manpage_xref (node, window->point + dir, dir);
4291 #endif /* HANDLE_MAN_PAGES */
4293 /* Ignore "Menu:" as a menu item. */
4296 char *text = node->contents + nextmenu;
4298 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4299 nextmenu = info_search_in_node
4300 (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
4303 /* If there is both a next menu entry, and a next xref entry, choose the
4304 one which occurs first. Otherwise, select the one which actually
4305 appears in this node following point. */
4306 if (nextmenu != -1 && nextxref != -1)
4308 if (((dir == 1) && (nextmenu < nextxref)) ||
4309 ((dir == -1) && (nextmenu > nextxref)))
4310 placement = nextmenu + 1;
4312 placement = nextxref;
4314 else if (nextmenu != -1)
4315 placement = nextmenu + 1;
4316 else if (nextxref != -1)
4317 placement = nextxref;
4319 /* If there was neither a menu or xref entry appearing in this node after
4320 point, choose the first menu or xref entry appearing in this node. */
4321 if (placement == -1)
4323 if (firstmenu != -1 && firstxref != -1)
4325 if (((dir == 1) && (firstmenu < firstxref)) ||
4326 ((dir == -1) && (firstmenu > firstxref)))
4327 placement = firstmenu + 1;
4329 placement = firstxref;
4331 else if (firstmenu != -1)
4332 placement = firstmenu + 1;
4334 placement = firstxref;
4336 window->point = placement;
4337 window_adjust_pagetop (window);
4338 window->flags |= W_UpdateWindow;
4341 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
4342 _("Move to the previous cross reference"))
4345 info_move_to_prev_xref (window, -count, key);
4347 info_move_to_xref (window, count, key, -1);
4350 DECLARE_INFO_COMMAND (info_move_to_next_xref,
4351 _("Move to the next cross reference"))
4354 info_move_to_next_xref (window, -count, key);
4356 info_move_to_xref (window, count, key, 1);
4359 /* Select the menu item or reference that appears on this line. */
4360 DECLARE_INFO_COMMAND (info_select_reference_this_line,
4361 _("Select reference or menu item appearing on this line"))
4366 line = window->line_starts[window_line_of_point (window)];
4367 orig = window->node;
4369 /* If this line contains a menu item, select that one. */
4370 if (strncmp ("* ", line, 2) == 0)
4371 info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
4373 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
4376 /* **************************************************************** */
4378 /* Miscellaneous Info Commands */
4380 /* **************************************************************** */
4382 /* What to do when C-g is pressed in a window. */
4383 DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
4385 /* If error printing doesn't oridinarily ring the bell, do it now,
4386 since C-g always rings the bell. Otherwise, let the error printer
4388 if (!info_error_rings_bell_p)
4389 terminal_ring_bell ();
4390 info_error (_("Quit"));
4392 info_initialize_numeric_arg ();
4393 info_clear_pending_input ();
4394 info_last_executed_command = (VFunction *)NULL;
4397 /* Move the cursor to the desired line of the window. */
4398 DECLARE_INFO_COMMAND (info_move_to_window_line,
4399 _("Move the cursor to a specific line of the window"))
4403 /* With no numeric argument of any kind, default to the center line. */
4404 if (!info_explicit_arg && count == 1)
4405 line = (window->height / 2) + window->pagetop;
4409 line = (window->height + count) + window->pagetop;
4411 line = window->pagetop + count;
4414 /* If the line doesn't appear in this window, make it do so. */
4415 if ((line - window->pagetop) >= window->height)
4416 line = window->pagetop + (window->height - 1);
4418 /* If the line is too small, make it fit. */
4419 if (line < window->pagetop)
4420 line = window->pagetop;
4422 /* If the selected line is past the bottom of the node, force it back. */
4423 if (line >= window->line_count)
4424 line = window->line_count - 1;
4426 window->point = (window->line_starts[line] - window->node->contents);
4429 /* Clear the screen and redraw its contents. Given a numeric argument,
4430 move the line the cursor is on to the COUNT'th line of the window. */
4431 DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
4433 if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4435 terminal_clear_screen ();
4436 display_clear_display (the_display);
4437 window_mark_chain (windows, W_UpdateWindow);
4438 display_update_display (windows);
4442 int desired_line, point_line;
4445 point_line = window_line_of_point (window) - window->pagetop;
4448 desired_line = window->height + count;
4450 desired_line = count;
4452 if (desired_line < 0)
4455 if (desired_line >= window->height)
4456 desired_line = window->height - 1;
4458 if (desired_line == point_line)
4461 new_pagetop = window->pagetop + (point_line - desired_line);
4463 set_window_pagetop (window, new_pagetop);
4466 /* This command does nothing. It is the fact that a key is bound to it
4467 that has meaning. See the code at the top of info_session (). */
4468 DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
4472 /* **************************************************************** */
4474 /* Reading Keys and Dispatching on Them */
4476 /* **************************************************************** */
4478 /* Declaration only. Special cased in info_dispatch_on_key ().
4479 Doc string is to avoid ugly results with describe_key etc. */
4480 DECLARE_INFO_COMMAND (info_do_lowercase_version,
4481 _("Run command bound to this key's lowercase variant"))
4485 dispatch_error (keyseq)
4490 rep = pretty_keyseq (keyseq);
4492 if (!echo_area_is_active)
4493 info_error (_("Unknown command (%s)."), rep);
4496 char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid")));
4497 sprintf (temp, _("\"%s\" is invalid"), rep);
4498 terminal_ring_bell ();
4499 inform_in_echo_area (temp);
4504 /* Keeping track of key sequences. */
4505 static char *info_keyseq = (char *)NULL;
4506 static int info_keyseq_index = 0;
4507 static int info_keyseq_size = 0;
4508 static int info_keyseq_displayed_p = 0;
4510 /* Initialize the length of the current key sequence. */
4512 initialize_keyseq ()
4514 info_keyseq_index = 0;
4515 info_keyseq_displayed_p = 0;
4518 /* Add CHARACTER to the current key sequence. */
4520 add_char_to_keyseq (character)
4523 if (info_keyseq_index + 2 >= info_keyseq_size)
4524 info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
4526 info_keyseq[info_keyseq_index++] = character;
4527 info_keyseq[info_keyseq_index] = '\0';
4530 /* Display the current value of info_keyseq. If argument EXPECTING is
4531 non-zero, input is expected to be read after the key sequence is
4532 displayed, so add an additional prompting character to the sequence. */
4534 display_info_keyseq (expecting_future_input)
4535 int expecting_future_input;
4539 rep = pretty_keyseq (info_keyseq);
4540 if (expecting_future_input)
4543 if (echo_area_is_active)
4544 inform_in_echo_area (rep);
4547 window_message_in_echo_area (rep);
4548 display_cursor_at_point (active_window);
4550 info_keyseq_displayed_p = 1;
4553 /* Called by interactive commands to read a keystroke. */
4555 info_get_another_input_char ()
4557 int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
4559 /* If there isn't any input currently available, then wait a
4560 moment looking for input. If we don't get it fast enough,
4561 prompt a little bit with the current key sequence. */
4562 if (!info_keyseq_displayed_p)
4565 if (!info_any_buffered_input_p () &&
4566 !info_input_pending_p ())
4568 #if defined (FD_SET)
4569 struct timeval timer;
4573 FD_SET (fileno (info_input_stream), &readfds);
4575 timer.tv_usec = 750;
4576 ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
4584 display_info_keyseq (1);
4586 return (info_get_input_char ());
4589 /* Do the command associated with KEY in MAP. If the associated command is
4590 really a keymap, then read another key, and dispatch into that map. */
4592 info_dispatch_on_key (key, map)
4596 #if !defined(INFOKEY)
4597 if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
4599 if (map[ESC].type == ISKMAP)
4601 map = (Keymap)map[ESC].function;
4602 add_char_to_keyseq (ESC);
4604 info_dispatch_on_key (key, map);
4608 dispatch_error (info_keyseq);
4612 #endif /* INFOKEY */
4614 switch (map[key].type)
4620 func = InfoFunction(map[key].function);
4621 if (func != (VFunction *)NULL)
4623 /* Special case info_do_lowercase_version (). */
4624 if (func == info_do_lowercase_version)
4626 #if defined(INFOKEY)
4627 unsigned char lowerkey;
4629 lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
4630 if (lowerkey == key)
4632 add_char_to_keyseq (key);
4633 dispatch_error (info_keyseq);
4636 info_dispatch_on_key (lowerkey, map);
4637 #else /* !INFOKEY */
4638 info_dispatch_on_key (tolower (key), map);
4639 #endif /* INFOKEY */
4643 add_char_to_keyseq (key);
4645 if (info_keyseq_displayed_p)
4646 display_info_keyseq (0);
4651 where = active_window;
4652 (*InfoFunction(map[key].function))
4653 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
4655 /* If we have input pending, then the last command was a prefix
4656 command. Don't change the value of the last function vars.
4657 Otherwise, remember the last command executed in the var
4658 appropriate to the window in which it was executed. */
4659 if (!info_input_pending_p ())
4661 if (where == the_echo_area)
4662 ea_last_executed_command = InfoFunction(map[key].function);
4664 info_last_executed_command = InfoFunction(map[key].function);
4670 add_char_to_keyseq (key);
4671 dispatch_error (info_keyseq);
4678 add_char_to_keyseq (key);
4679 if (map[key].function != (InfoCommand *)NULL)
4681 unsigned char newkey;
4683 newkey = info_get_another_input_char ();
4684 info_dispatch_on_key (newkey, (Keymap)map[key].function);
4688 dispatch_error (info_keyseq);
4695 /* **************************************************************** */
4697 /* Numeric Arguments */
4699 /* **************************************************************** */
4701 /* Handle C-u style numeric args, as well as M--, and M-digits. */
4703 /* Non-zero means that an explicit argument has been passed to this
4704 command, as in C-u C-v. */
4705 int info_explicit_arg = 0;
4707 /* The sign of the numeric argument. */
4708 int info_numeric_arg_sign = 1;
4710 /* The value of the argument itself. */
4711 int info_numeric_arg = 1;
4713 /* Add the current digit to the argument in progress. */
4714 DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
4715 _("Add this digit to the current numeric argument"))
4717 info_numeric_arg_digit_loop (window, 0, key);
4720 /* C-u, universal argument. Multiply the current argument by 4.
4721 Read a key. If the key has nothing to do with arguments, then
4722 dispatch on it. If the key is the abort character then abort. */
4723 DECLARE_INFO_COMMAND (info_universal_argument,
4724 _("Start (or multiply by 4) the current numeric argument"))
4726 info_numeric_arg *= 4;
4727 info_numeric_arg_digit_loop (window, 0, 0);
4730 /* Create a default argument. */
4732 info_initialize_numeric_arg ()
4734 info_numeric_arg = info_numeric_arg_sign = 1;
4735 info_explicit_arg = 0;
4738 DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
4739 _("Internally used by \\[universal-argument]"))
4741 unsigned char pure_key;
4742 Keymap keymap = window->keymap;
4750 if (display_was_interrupted_p && !info_any_buffered_input_p ())
4751 display_update_display (windows);
4753 if (active_window != the_echo_area)
4754 display_cursor_at_point (active_window);
4756 pure_key = key = info_get_another_input_char ();
4758 #if !defined(INFOKEY)
4760 add_char_to_keyseq (ESC);
4762 add_char_to_keyseq (UnMeta (key));
4763 #else /* defined(INFOKEY) */
4764 add_char_to_keyseq (key);
4765 #endif /* defined(INFOKEY) */
4768 #if !defined(INFOKEY)
4771 #endif /* !defined(INFOKEY) */
4773 if (keymap[key].type == ISFUNC &&
4774 InfoFunction(keymap[key].function) == info_universal_argument)
4776 info_numeric_arg *= 4;
4781 #if defined(INFOKEY)
4784 #endif /* !defined(INFOKEY) */
4789 if (info_explicit_arg)
4790 info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
4792 info_numeric_arg = (key - '0');
4793 info_explicit_arg = 1;
4797 if (key == '-' && !info_explicit_arg)
4799 info_numeric_arg_sign = -1;
4800 info_numeric_arg = 1;
4804 info_keyseq_index--;
4805 info_dispatch_on_key (pure_key, keymap);
4813 /* **************************************************************** */
4815 /* Input Character Buffering */
4817 /* **************************************************************** */
4819 /* Character waiting to be read next. */
4820 static int pending_input_character = 0;
4822 /* How to make there be no pending input. */
4824 info_clear_pending_input ()
4826 pending_input_character = 0;
4829 /* How to set the pending input character. */
4831 info_set_pending_input (key)
4834 pending_input_character = key;
4837 /* How to see if there is any pending input. */
4839 info_input_pending_p ()
4841 return (pending_input_character);
4844 /* Largest number of characters that we can read in advance. */
4845 #define MAX_INFO_INPUT_BUFFERING 512
4847 static int pop_index = 0, push_index = 0;
4848 static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
4850 /* Add KEY to the buffer of characters to be read. */
4852 info_push_typeahead (key)
4855 /* Flush all pending input in the case of C-g pressed. */
4856 if (key == Control ('g'))
4858 push_index = pop_index;
4859 info_set_pending_input (Control ('g'));
4863 info_input_buffer[push_index++] = key;
4864 if (push_index >= sizeof (info_input_buffer))
4869 /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4871 info_input_buffer_space_available ()
4873 if (pop_index > push_index)
4874 return (pop_index - push_index);
4876 return (sizeof (info_input_buffer) - (push_index - pop_index));
4879 /* Get a key from the buffer of characters to be read.
4880 Return the key in KEY.
4881 Result is non-zero if there was a key, or 0 if there wasn't. */
4883 info_get_key_from_typeahead (key)
4886 if (push_index == pop_index)
4889 *key = info_input_buffer[pop_index++];
4891 if (pop_index >= sizeof (info_input_buffer))
4898 info_any_buffered_input_p ()
4900 info_gather_typeahead ();
4901 return (push_index != pop_index);
4904 /* If characters are available to be read, then read them and stuff them into
4905 info_input_buffer. Otherwise, do nothing. */
4907 info_gather_typeahead ()
4910 int tty, space_avail;
4912 unsigned char input[MAX_INFO_INPUT_BUFFERING];
4914 tty = fileno (info_input_stream);
4917 space_avail = info_input_buffer_space_available ();
4919 /* If we can just find out how many characters there are to read, do so. */
4920 #if defined (FIONREAD)
4922 ioctl (tty, FIONREAD, &chars_avail);
4924 if (chars_avail > space_avail)
4925 chars_avail = space_avail;
4928 chars_avail = read (tty, &input[0], chars_avail);
4930 #else /* !FIONREAD */
4931 # if defined (O_NDELAY)
4935 flags = fcntl (tty, F_GETFL, 0);
4937 fcntl (tty, F_SETFL, (flags | O_NDELAY));
4938 chars_avail = read (tty, &input[0], space_avail);
4939 fcntl (tty, F_SETFL, flags);
4941 if (chars_avail == -1)
4944 # else /* !O_NDELAY */
4947 extern long pc_term_chars_avail (void);
4950 chars_avail = pc_term_chars_avail ();
4953 /* We could be more accurate by calling ltell, but we have no idea
4954 whether tty is buffered by stdio functions, and if so, how many
4955 characters are already waiting in the buffer. So we punt. */
4958 if (fstat (tty, &st) < 0)
4961 chars_avail = st.st_size;
4963 if (chars_avail > space_avail)
4964 chars_avail = space_avail;
4966 chars_avail = read (tty, &input[0], chars_avail);
4968 # endif/* __DJGPP__ */
4969 # endif /* O_NDELAY */
4970 #endif /* !FIONREAD */
4972 while (i < chars_avail)
4974 info_push_typeahead (input[i]);
4979 /* How to read a single character. */
4981 info_get_input_char ()
4983 unsigned char keystroke;
4985 info_gather_typeahead ();
4987 if (pending_input_character)
4989 keystroke = pending_input_character;
4990 pending_input_character = 0;
4992 else if (info_get_key_from_typeahead (&keystroke) == 0)
4996 int tty = fileno (info_input_stream);
4998 /* Using stream I/O causes FIONREAD etc to fail to work
4999 so unless someone can find a portable way of finding
5000 out how many characters are currently buffered, we
5001 should stay with away from stream I/O.
5002 --Egil Kvaleberg <egilk@sn.no>, January 1997. */
5004 /* Keep reading if we got EINTR, so that we don't just exit.
5005 --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
5010 n = read (tty, &c, 1);
5011 while (n == -1 && errno == EINTR);
5012 rawkey = n == 1 ? c : EOF;
5015 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5022 if (info_input_stream != stdin)
5024 fclose (info_input_stream);
5025 info_input_stream = stdin;
5026 tty = fileno (info_input_stream);
5027 display_inhibited = 0;
5028 display_update_display (windows);
5029 display_cursor_at_point (active_window);
5030 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5036 terminal_unprep_terminal ();
5037 close_dribble_file ();
5043 if (info_dribble_file)
5044 dribble (keystroke);