1 /* session.c -- user windowing interface to Info.
2 $Id: session.c,v 1.8 2003/03/22 17:41:16 karl Exp $
4 Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
5 Free Software Foundation, Inc.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 Written by Brian Fox (bfox@ai.mit.edu). */
24 #include <sys/ioctl.h>
26 #if defined (HAVE_SYS_TIME_H)
27 # include <sys/time.h>
28 # define HAVE_STRUCT_TIMEVAL
29 #endif /* HAVE_SYS_TIME_H */
31 #if defined (HANDLE_MAN_PAGES)
36 /* SCO 3.2v5.0.2 defines but does not correctly declare strncasecmp.
37 Since we use it as a symbol, have to get it right. --gildea, 1jul99. */
38 extern int strncasecmp (const char *, const char *, size_t);
41 static void info_clear_pending_input (), info_set_pending_input ();
42 static void info_handle_pointer ();
44 /* **************************************************************** */
46 /* Running an Info Session */
48 /* **************************************************************** */
50 /* The place that we are reading input from. */
51 static FILE *info_input_stream = NULL;
53 /* The last executed command. */
54 VFunction *info_last_executed_command = NULL;
56 /* Becomes non-zero when 'q' is typed to an Info window. */
57 int quit_info_immediately = 0;
59 /* Array of structures describing for each window which nodes have been
60 visited in that window. */
61 INFO_WINDOW **info_windows = NULL;
63 /* Where to add the next window, if we need to add one. */
64 static int info_windows_index = 0;
66 /* Number of slots allocated to `info_windows'. */
67 static int info_windows_slots = 0;
69 void remember_window_and_node (), forget_window_and_nodes ();
70 void initialize_info_session (), info_session ();
71 void display_startup_message_and_start ();
73 /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
74 For each loaded node, create a new window. Always split the largest of the
77 begin_multiple_window_info_session (filename, nodenames)
82 WINDOW *window = (WINDOW *)NULL;
84 for (i = 0; nodenames[i]; i++)
88 node = info_get_node (filename, nodenames[i]);
93 /* If this is the first node, initialize the info session. */
96 initialize_info_session (node, 1);
97 window = active_window;
101 /* Find the largest window in WINDOWS, and make that be the active
102 one. Then split it and add our window and node to the list
103 of remembered windows and nodes. Then tile the windows. */
104 WINDOW *win, *largest = NULL;
107 for (win = windows; win; win = win->next)
108 if (win->height > max_height)
110 max_height = win->height;
116 display_update_display (windows);
117 info_error (msg_cant_find_window);
122 active_window = largest;
123 window = window_make_window (node);
126 window_tile_windows (TILE_INTERNALS);
127 remember_window_and_node (window, node);
131 display_update_display (windows);
132 info_error (msg_win_too_small);
138 display_startup_message_and_start ();
141 /* Start an info session with INITIAL_NODE, and an error message in the echo
142 area made from FORMAT and ARG. */
144 begin_info_session_with_error (initial_node, format, arg1, arg2)
150 initialize_info_session (initial_node, 1);
151 info_error (format, arg1, arg2);
155 /* Start an info session with INITIAL_NODE. */
157 begin_info_session (initial_node)
160 initialize_info_session (initial_node, 1);
161 display_startup_message_and_start ();
165 display_startup_message_and_start ()
169 format = replace_in_documentation
170 (_("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."));
172 window_message_in_echo_area (format, VERSION);
176 /* Run an info session with an already initialized window and node. */
180 display_update_display (windows);
181 info_last_executed_command = NULL;
182 info_read_and_dispatch ();
183 /* On program exit, leave the cursor at the bottom of the window, and
184 restore the terminal I/O. */
185 terminal_goto_xy (0, screenheight - 1);
186 terminal_clear_to_eol ();
188 terminal_unprep_terminal ();
189 close_dribble_file ();
192 /* Here is a window-location dependent event loop. Called from the
193 functions info_session (), and from read_xxx_in_echo_area (). */
195 info_read_and_dispatch ()
201 while (!done && !quit_info_immediately)
205 /* If we haven't just gone up or down a line, there is no
206 goal column for this window. */
207 if ((info_last_executed_command != info_next_line) &&
208 (info_last_executed_command != info_prev_line))
209 active_window->goal_column = -1;
211 if (echo_area_is_active)
213 lk = echo_area_last_command_was_kill;
214 echo_area_prep_read ();
217 if (!info_any_buffered_input_p ())
218 display_update_display (windows);
220 display_cursor_at_point (active_window);
221 info_initialize_numeric_arg ();
223 initialize_keyseq ();
224 key = info_get_input_char ();
226 /* No errors yet. We just read a character, that's all. Only clear
227 the echo_area if it is not currently active. */
228 if (!echo_area_is_active)
229 window_clear_echo_area ();
231 info_error_was_printed = 0;
233 /* Do the selected command. */
234 info_dispatch_on_key (key, active_window->keymap);
236 if (echo_area_is_active)
238 /* Echo area commands that do killing increment the value of
239 ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no
240 change in the value of this variable, the last command
241 executed was not a kill command. */
242 if (lk == echo_area_last_command_was_kill)
243 echo_area_last_command_was_kill = 0;
245 if (ea_last_executed_command == ea_newline ||
246 info_aborted_echo_area)
248 ea_last_executed_command = (VFunction *)NULL;
252 if (info_last_executed_command == info_quit)
253 quit_info_immediately = 1;
255 else if (info_last_executed_command == info_quit)
260 /* Found in signals.c */
261 extern void initialize_info_signal_handler ();
263 /* Initialize the first info session by starting the terminal, window,
264 and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
266 initialize_info_session (node, clear_screen)
270 char *term_name = getenv ("TERM");
271 terminal_initialize_terminal (term_name);
273 if (terminal_is_dumb_p)
278 info_error (msg_term_too_dumb, term_name);
284 terminal_prep_terminal ();
285 terminal_clear_screen ();
288 initialize_info_keymaps ();
289 window_initialize_windows (screenwidth, screenheight);
290 initialize_info_signal_handler ();
291 display_initialize_display (screenwidth, screenheight);
292 info_set_node_of_window (0, active_window, node);
294 /* Tell the window system how to notify us when a window needs to be
295 asynchronously deleted (e.g., user resizes window very small). */
296 window_deletion_notifier = forget_window_and_nodes;
298 /* If input has not been redirected yet, make it come from unbuffered
300 if (!info_input_stream)
302 setbuf (stdin, NULL);
303 info_input_stream = stdin;
306 info_windows_initialized_p = 1;
309 /* Tell Info that input is coming from the file FILENAME. */
311 info_set_input_from_file (filename)
316 /* Input may include binary characters. */
317 stream = fopen (filename, FOPEN_RBIN);
322 if ((info_input_stream != (FILE *)NULL) &&
323 (info_input_stream != stdin))
324 fclose (info_input_stream);
326 info_input_stream = stream;
329 display_inhibited = 1;
332 /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
334 get_info_window_of_window (window)
338 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
340 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
341 if (info_win->window == window)
347 /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
348 values if the window and node are the same as the current one being
351 set_remembered_pagetop_and_point (window)
354 INFO_WINDOW *info_win;
356 info_win = get_info_window_of_window (window);
361 if (info_win->nodes_index &&
362 (info_win->nodes[info_win->current] == window->node))
364 info_win->pagetops[info_win->current] = window->pagetop;
365 info_win->points[info_win->current] = window->point;
370 remember_window_and_node (window, node)
374 /* See if we already have this window in our list. */
375 INFO_WINDOW *info_win = get_info_window_of_window (window);
377 /* If the window wasn't already on our list, then make a new entry. */
380 info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
381 info_win->window = window;
382 info_win->nodes = (NODE **)NULL;
383 info_win->pagetops = (int *)NULL;
384 info_win->points = (long *)NULL;
385 info_win->current = 0;
386 info_win->nodes_index = 0;
387 info_win->nodes_slots = 0;
389 add_pointer_to_array (info_win, info_windows_index, info_windows,
390 info_windows_slots, 10, INFO_WINDOW *);
393 /* If this node, the current pagetop, and the current point are the
394 same as the current saved node and pagetop, don't really add this to
395 the list of history nodes. This may happen only at the very
396 beginning of the program, I'm not sure. --karl */
398 && info_win->current >= 0
399 && info_win->nodes[info_win->current]->contents == node->contents
400 && info_win->pagetops[info_win->current] == window->pagetop
401 && info_win->points[info_win->current] == window->point)
404 /* Remember this node, the currently displayed pagetop, and the current
405 location of point in this window. Because we are updating pagetops
406 and points as well as nodes, it is more efficient to avoid the
407 add_pointer_to_array macro here. */
408 if (info_win->nodes_index + 2 >= info_win->nodes_slots)
410 info_win->nodes_slots += 20;
411 info_win->nodes = (NODE **) xrealloc (info_win->nodes,
412 info_win->nodes_slots * sizeof (NODE *));
413 info_win->pagetops = (int *) xrealloc (info_win->pagetops,
414 info_win->nodes_slots * sizeof (int));
415 info_win->points = (long *) xrealloc (info_win->points,
416 info_win->nodes_slots * sizeof (long));
419 info_win->nodes[info_win->nodes_index] = node;
420 info_win->pagetops[info_win->nodes_index] = window->pagetop;
421 info_win->points[info_win->nodes_index] = window->point;
422 info_win->current = info_win->nodes_index++;
423 info_win->nodes[info_win->nodes_index] = NULL;
424 info_win->pagetops[info_win->nodes_index] = 0;
425 info_win->points[info_win->nodes_index] = 0;
428 #define DEBUG_FORGET_WINDOW_AND_NODES
429 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
431 consistency_check_info_windows ()
435 for (i = 0; i < info_windows_index; i++)
439 for (win = windows; win; win = win->next)
440 if (win == info_windows[i]->window)
447 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
449 /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
451 forget_window_and_nodes (window)
455 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
457 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
458 if (info_win->window == window)
461 /* If we found the window to forget, then do so. */
464 while (i < info_windows_index)
466 info_windows[i] = info_windows[i + 1];
470 info_windows_index--;
471 info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
475 /* Free the node structures which held onto internal node contents
476 here. This doesn't free the contents; we have a garbage collector
478 for (i = 0; info_win->nodes[i]; i++)
479 if (internal_info_node_p (info_win->nodes[i]))
480 free (info_win->nodes[i]);
481 free (info_win->nodes);
483 maybe_free (info_win->pagetops);
484 maybe_free (info_win->points);
489 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
490 consistency_check_info_windows ();
491 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
494 /* Set WINDOW to show NODE. Remember the new window in our list of Info
495 windows. If we are doing automatic footnote display, also try to display
496 the footnotes for this window. If REMEMBER is nonzero, first call
497 set_remembered_pagetop_and_point. */
499 info_set_node_of_window (remember, window, node)
505 set_remembered_pagetop_and_point (window);
507 /* Put this node into the window. */
508 window_set_node_of_window (window, node);
510 /* Remember this node and window in our list of info windows. */
511 remember_window_and_node (window, node);
513 /* If doing auto-footnote display/undisplay, show the footnotes belonging
514 to this window's node. */
515 if (auto_footnotes_p)
516 info_get_or_remove_footnotes (window);
520 /* **************************************************************** */
522 /* Info Movement Commands */
524 /* **************************************************************** */
526 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
529 set_window_pagetop (window, desired_top)
533 int point_line, old_pagetop;
537 else if (desired_top > window->line_count)
538 desired_top = window->line_count - 1;
540 if (window->pagetop == desired_top)
543 old_pagetop = window->pagetop;
544 window->pagetop = desired_top;
546 /* Make sure that point appears in this window. */
547 point_line = window_line_of_point (window);
548 if ((point_line < window->pagetop) ||
549 ((point_line - window->pagetop) > window->height - 1))
551 window->line_starts[window->pagetop] - window->node->contents;
553 window->flags |= W_UpdateWindow;
555 /* Find out which direction to scroll, and scroll the window in that
556 direction. Do this only if there would be a savings in redisplay
557 time. This is true if the amount to scroll is less than the height
558 of the window, and if the number of lines scrolled would be greater
559 than 10 % of the window's height. */
560 if (old_pagetop < desired_top)
562 int start, end, amount;
564 amount = desired_top - old_pagetop;
566 if ((amount >= window->height) ||
567 (((window->height - amount) * 10) < window->height))
570 start = amount + window->first_row;
571 end = window->height + window->first_row;
573 display_scroll_display (start, end, -amount);
577 int start, end, amount;
579 amount = old_pagetop - desired_top;
581 if ((amount >= window->height) ||
582 (((window->height - amount) * 10) < window->height))
585 start = window->first_row;
586 end = (window->first_row + window->height) - amount;
587 display_scroll_display (start, end, amount);
591 /* Immediately make WINDOW->point visible on the screen, and move the
592 terminal cursor there. */
594 info_show_point (window)
599 old_pagetop = window->pagetop;
600 window_adjust_pagetop (window);
601 if (old_pagetop != window->pagetop)
605 new_pagetop = window->pagetop;
606 window->pagetop = old_pagetop;
607 set_window_pagetop (window, new_pagetop);
610 if (window->flags & W_UpdateWindow)
611 display_update_one_window (window);
613 display_cursor_at_point (window);
616 /* Move WINDOW->point from OLD line index to NEW line index. */
618 move_to_new_line (old, new, window)
624 info_error (msg_cant_find_point);
630 if (new >= window->line_count || new < 0)
633 goal = window_get_goal_column (window);
634 window->goal_column = goal;
636 window->point = window->line_starts[new] - window->node->contents;
637 window->point += window_chars_to_goal (window->line_starts[new], goal);
638 info_show_point (window);
642 /* Move WINDOW's point down to the next line if possible. */
643 DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
645 int old_line, new_line;
648 info_prev_line (window, -count, key);
651 old_line = window_line_of_point (window);
652 new_line = old_line + count;
653 move_to_new_line (old_line, new_line, window);
657 /* Move WINDOW's point up to the previous line if possible. */
658 DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
660 int old_line, new_line;
663 info_next_line (window, -count, key);
666 old_line = window_line_of_point (window);
667 new_line = old_line - count;
668 move_to_new_line (old_line, new_line, window);
672 /* Move WINDOW's point to the end of the true line. */
673 DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
675 register int point, len;
676 register char *buffer;
678 buffer = window->node->contents;
679 len = window->node->nodelen;
681 for (point = window->point;
682 (point < len) && (buffer[point] != '\n');
685 if (point != window->point)
687 window->point = point;
688 info_show_point (window);
692 /* Move WINDOW's point to the beginning of the true line. */
693 DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
696 register char *buffer;
698 buffer = window->node->contents;
699 point = window->point;
701 for (; (point) && (buffer[point - 1] != '\n'); point--);
703 /* If at a line start already, do nothing. */
704 if (point != window->point)
706 window->point = point;
707 info_show_point (window);
711 /* Move point forward in the node. */
712 DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
715 info_backward_char (window, -count, key);
718 window->point += count;
720 if (window->point >= window->node->nodelen)
721 window->point = window->node->nodelen - 1;
723 info_show_point (window);
727 /* Move point backward in the node. */
728 DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
731 info_forward_char (window, -count, key);
734 window->point -= count;
736 if (window->point < 0)
739 info_show_point (window);
743 #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
745 /* Move forward a word in this node. */
746 DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
754 info_backward_word (window, -count, key);
758 point = window->point;
759 buffer = window->node->contents;
760 end = window->node->nodelen;
764 if (point + 1 >= end)
767 /* If we are not in a word, move forward until we are in one.
768 Then, move forward until we hit a non-alphabetic character. */
773 while (++point < end)
781 if (point >= end) return;
783 while (++point < end)
791 window->point = point;
792 info_show_point (window);
795 DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
803 info_forward_word (window, -count, key);
807 buffer = window->node->contents;
808 point = window->point;
815 /* Like info_forward_word (), except that we look at the
816 characters just before point. */
818 c = buffer[point - 1];
824 c = buffer[point - 1];
832 c = buffer[point - 1];
840 window->point = point;
841 info_show_point (window);
844 /* Variable controlling the behaviour of default scrolling when you are
845 already at the bottom of a node. Possible values are defined in session.h.
848 IS_Continuous Try to get first menu item, or failing that, the
849 "Next:" pointer, or failing that, the "Up:" and
851 IS_NextOnly Try to get "Next:" menu item.
852 IS_PageOnly Simply give up at the bottom of a node. */
854 int info_scroll_behaviour = IS_Continuous;
856 /* Choices used by the completer when reading a value for the user-visible
857 variable "scroll-behaviour". */
858 char *info_scroll_choices[] = {
859 "Continuous", "Next Only", "Page Only", (char *)NULL
862 /* Default window sizes for scrolling commands. */
863 int default_window_size = -1; /* meaning 1 window-full */
864 int default_scroll_size = -1; /* meaning half screen size */
866 #define INFO_LABEL_FOUND() \
867 (info_parsed_nodename || (info_parsed_filename \
868 && !is_dir_name (info_parsed_filename)))
870 /* Move to 1st menu item, Next, Up/Next, or error in this window. */
872 forward_move_node_structure (window, behaviour)
879 info_error (msg_at_node_bottom);
883 info_next_label_of_node (window->node);
884 if (!info_parsed_nodename && !info_parsed_filename)
885 info_error (msg_no_pointer, _("Next"));
888 window_message_in_echo_area (_("Following Next node..."));
889 info_handle_pointer ("Next", window);
895 /* First things first. If this node contains a menu, move down
900 menu = info_menu_of_node (window->node);
904 info_free_references (menu);
905 window_message_in_echo_area (_("Selecting first menu item..."));
906 info_menu_digit (window, 1, '1');
911 /* Okay, this node does not contain a menu. If it contains a
912 "Next:" pointer, use that. */
913 info_next_label_of_node (window->node);
914 if (INFO_LABEL_FOUND ())
916 window_message_in_echo_area (_("Selecting Next node..."));
917 info_handle_pointer ("Next", window);
921 /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we
922 can move "Next:". If that isn't possible, complain that there
923 are no more nodes. */
925 int up_counter, old_current;
926 INFO_WINDOW *info_win;
928 /* Remember the current node and location. */
929 info_win = get_info_window_of_window (window);
930 old_current = info_win->current;
932 /* Back up through the "Up:" pointers until we have found a "Next:"
933 that isn't the same as the first menu item found in that node. */
935 while (!info_error_was_printed)
937 info_up_label_of_node (window->node);
938 if (INFO_LABEL_FOUND ())
940 info_handle_pointer ("Up", window);
941 if (info_error_was_printed)
946 info_next_label_of_node (window->node);
948 /* If no "Next" pointer, keep backing up. */
949 if (!INFO_LABEL_FOUND ())
952 /* If this node's first menu item is the same as this node's
953 Next pointer, keep backing up. */
954 if (!info_parsed_filename)
959 /* Remember the name of the Next node, since reading
960 the menu can overwrite the contents of the
961 info_parsed_xxx strings. */
962 next_nodename = xstrdup (info_parsed_nodename);
964 menu = info_menu_of_node (window->node);
967 (menu[0]->nodename, next_nodename) == 0))
969 info_free_references (menu);
970 free (next_nodename);
975 /* Restore the world to where it was before
976 reading the menu contents. */
977 info_free_references (menu);
978 free (next_nodename);
979 info_next_label_of_node (window->node);
983 /* This node has a "Next" pointer, and it is not the
984 same as the first menu item found in this node. */
985 window_message_in_echo_area
986 (_("Moving Up %d time(s), then Next."),
989 info_handle_pointer ("Next", window);
994 /* No more "Up" pointers. Print an error, and call it
998 for (i = 0; i < up_counter; i++)
1000 info_win->nodes_index--;
1001 free (info_win->nodes[info_win->nodes_index]);
1002 info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
1004 info_win->current = old_current;
1005 window->node = info_win->nodes[old_current];
1006 window->pagetop = info_win->pagetops[old_current];
1007 window->point = info_win->points[old_current];
1008 recalculate_line_starts (window);
1009 window->flags |= W_UpdateWindow;
1010 info_error (_("No more nodes within this document."));
1019 /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
1021 backward_move_node_structure (window, behaviour)
1028 info_error (msg_at_node_top);
1032 info_prev_label_of_node (window->node);
1033 if (!info_parsed_nodename && !info_parsed_filename)
1034 info_error (_("No `Prev' for this node."));
1037 window_message_in_echo_area (_("Moving Prev in this window."));
1038 info_handle_pointer ("Prev", window);
1043 info_prev_label_of_node (window->node);
1045 if (!info_parsed_nodename && (!info_parsed_filename
1046 || is_dir_name (info_parsed_filename)))
1048 info_up_label_of_node (window->node);
1049 if (!info_parsed_nodename && (!info_parsed_filename
1050 || is_dir_name (info_parsed_filename)))
1051 info_error (_("No `Prev' or `Up' for this node within this document."));
1054 window_message_in_echo_area (_("Moving Up in this window."));
1055 info_handle_pointer ("Up", window);
1061 int inhibit_menu_traversing = 0;
1063 /* Watch out! If this node's Prev is the same as the Up, then
1064 move Up. Otherwise, we could move Prev, and then to the last
1065 menu item in the Prev. This would cause the user to loop
1066 through a subsection of the info file. */
1067 if (!info_parsed_filename && info_parsed_nodename)
1071 pnode = xstrdup (info_parsed_nodename);
1072 info_up_label_of_node (window->node);
1074 if (!info_parsed_filename && info_parsed_nodename &&
1075 strcmp (info_parsed_nodename, pnode) == 0)
1077 /* The nodes are the same. Inhibit moving to the last
1080 inhibit_menu_traversing = 1;
1085 info_prev_label_of_node (window->node);
1089 /* Move to the previous node. If this node now contains a menu,
1090 and we have not inhibited movement to it, move to the node
1091 corresponding to the last menu item. */
1092 window_message_in_echo_area (_("Moving Prev in this window."));
1093 info_handle_pointer ("Prev", window);
1095 if (!inhibit_menu_traversing)
1097 while (!info_error_was_printed &&
1098 (menu = info_menu_of_node (window->node)))
1100 info_free_references (menu);
1101 window_message_in_echo_area
1102 (_("Moving to `Prev's last menu item."));
1103 info_menu_digit (window, 1, '0');
1111 /* Move continuously forward through the node structure of this info file. */
1112 DECLARE_INFO_COMMAND (info_global_next_node,
1113 _("Move forwards or down through node structure"))
1116 info_global_prev_node (window, -count, key);
1119 while (count && !info_error_was_printed)
1121 forward_move_node_structure (window, IS_Continuous);
1127 /* Move continuously backward through the node structure of this info file. */
1128 DECLARE_INFO_COMMAND (info_global_prev_node,
1129 _("Move backwards or up through node structure"))
1132 info_global_next_node (window, -count, key);
1135 while (count && !info_error_was_printed)
1137 backward_move_node_structure (window, IS_Continuous);
1143 static void _scroll_forward();
1144 static void _scroll_backward();
1147 _scroll_forward(window, count, key, behaviour)
1154 _scroll_backward (window, -count, key, behaviour);
1159 /* Without an explicit numeric argument, scroll the bottom two
1160 lines to the top of this window, Or, if at bottom of window,
1161 and the chosen behaviour is to scroll through nodes get the
1162 "Next" node for this window. */
1163 if (default_window_size > 0)
1164 desired_top = window->pagetop + default_window_size;
1165 else if (!info_explicit_arg && count == 1)
1167 desired_top = window->pagetop + (window->height - 2);
1169 /* If there are no more lines to scroll here, error, or get
1170 another node, depending on BEHAVIOUR. */
1171 if (desired_top > window->line_count)
1173 forward_move_node_structure (window, behaviour);
1178 desired_top = window->pagetop + count;
1180 if (desired_top >= window->line_count)
1181 desired_top = window->line_count - 2;
1183 if (window->pagetop > desired_top)
1186 set_window_pagetop (window, desired_top);
1191 _scroll_backward(window, count, key, behaviour)
1198 _scroll_forward (window, -count, key, behaviour);
1203 /* Without an explicit numeric argument, scroll the top two lines
1204 to the bottom of this window, or, depending on the selected
1205 behaviour, move to the previous, or Up'th node. */
1206 if (default_window_size > 0)
1207 desired_top = window->pagetop - default_window_size;
1208 else if (!info_explicit_arg && count == 1)
1210 desired_top = window->pagetop - (window->height - 2);
1212 if ((desired_top < 0) && (window->pagetop == 0))
1214 backward_move_node_structure (window, behaviour);
1219 desired_top = window->pagetop - count;
1221 if (desired_top < 0)
1224 set_window_pagetop (window, desired_top);
1228 /* Show the next screen of WINDOW's node. */
1229 DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
1231 _scroll_forward (window, count, key, info_scroll_behaviour);
1234 /* Like info_scroll_forward, but sets default_window_size as a side
1236 DECLARE_INFO_COMMAND (info_scroll_forward_set_window,
1237 _("Scroll forward in this window and set default window size"))
1239 if (info_explicit_arg)
1240 default_window_size = count;
1241 _scroll_forward (window, count, key, info_scroll_behaviour);
1244 /* Show the next screen of WINDOW's node but never advance to next node. */
1245 DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))
1247 _scroll_forward (window, count, key, IS_PageOnly);
1250 /* Like info_scroll_forward_page_only, but sets default_window_size as a side
1252 DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,
1253 _("Scroll forward in this window staying within node and set default window size"))
1255 if (info_explicit_arg)
1256 default_window_size = count;
1257 _scroll_forward (window, count, key, IS_PageOnly);
1260 /* Show the previous screen of WINDOW's node. */
1261 DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
1263 _scroll_backward (window, count, key, info_scroll_behaviour);
1266 /* Like info_scroll_backward, but sets default_window_size as a side
1268 DECLARE_INFO_COMMAND (info_scroll_backward_set_window,
1269 _("Scroll backward in this window and set default window size"))
1271 if (info_explicit_arg)
1272 default_window_size = count;
1273 _scroll_backward (window, count, key, info_scroll_behaviour);
1276 /* Show the previous screen of WINDOW's node but never move to previous
1278 DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))
1280 _scroll_backward (window, count, key, IS_PageOnly);
1283 /* Like info_scroll_backward_page_only, but sets default_window_size as a side
1285 DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,
1286 _("Scroll backward in this window staying within node and set default window size"))
1288 if (info_explicit_arg)
1289 default_window_size = count;
1290 _scroll_backward (window, count, key, IS_PageOnly);
1293 /* Move to the beginning of the node. */
1294 DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1296 window->pagetop = window->point = 0;
1297 window->flags |= W_UpdateWindow;
1300 /* Move to the end of the node. */
1301 DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1303 window->point = window->node->nodelen - 1;
1304 info_show_point (window);
1307 /* Scroll the window forward by N lines. */
1308 DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
1311 info_up_line (window, -count, key);
1314 int desired_top = window->pagetop + count;
1316 if (desired_top >= window->line_count)
1317 desired_top = window->line_count - 2;
1319 if (window->pagetop <= desired_top)
1320 set_window_pagetop (window, desired_top);
1324 /* Scroll the window backward by N lines. */
1325 DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
1328 info_down_line (window, -count, key);
1331 int desired_top = window->pagetop - count;
1333 if (desired_top < 0)
1336 set_window_pagetop (window, desired_top);
1340 /* Scroll the window forward by N lines and remember N as default for
1341 subsequent commands. */
1342 DECLARE_INFO_COMMAND (info_scroll_half_screen_down,
1343 _("Scroll down by half screen size"))
1346 info_scroll_half_screen_up (window, -count, key);
1349 int scroll_size = (the_screen->height + 1) / 2;
1352 if (info_explicit_arg)
1353 default_scroll_size = count;
1354 if (default_scroll_size > 0)
1355 scroll_size = default_scroll_size;
1357 desired_top = window->pagetop + scroll_size;
1358 if (desired_top >= window->line_count)
1359 desired_top = window->line_count - 2;
1361 if (window->pagetop <= desired_top)
1362 set_window_pagetop (window, desired_top);
1366 /* Scroll the window backward by N lines and remember N as default for
1367 subsequent commands. */
1368 DECLARE_INFO_COMMAND (info_scroll_half_screen_up,
1369 _("Scroll up by half screen size"))
1372 info_scroll_half_screen_down (window, -count, key);
1375 int scroll_size = (the_screen->height + 1) / 2;
1378 if (info_explicit_arg)
1379 default_scroll_size = count;
1380 if (default_scroll_size > 0)
1381 scroll_size = default_scroll_size;
1383 desired_top = window->pagetop - scroll_size;
1384 if (desired_top < 0)
1387 set_window_pagetop (window, desired_top);
1391 /* **************************************************************** */
1393 /* Commands for Manipulating Windows */
1395 /* **************************************************************** */
1397 /* Make the next window in the chain be the active window. */
1398 DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1402 info_prev_window (window, -count, key);
1406 /* If no other window, error now. */
1407 if (!windows->next && !echo_area_is_active)
1409 info_error (msg_one_window);
1416 window = window->next;
1419 if (window == the_echo_area || !echo_area_is_active)
1422 window = the_echo_area;
1426 if (active_window != window)
1428 if (auto_footnotes_p)
1429 info_get_or_remove_footnotes (window);
1431 window->flags |= W_UpdateWindow;
1432 active_window = window;
1436 /* Make the previous window in the chain be the active window. */
1437 DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1441 info_next_window (window, -count, key);
1445 /* Only one window? */
1447 if (!windows->next && !echo_area_is_active)
1449 info_error (msg_one_window);
1455 /* If we are in the echo area, or if the echo area isn't active and we
1456 are in the first window, find the last window in the chain. */
1457 if (window == the_echo_area ||
1458 (window == windows && !echo_area_is_active))
1460 register WINDOW *win, *last;
1462 for (win = windows; win; win = win->next)
1469 if (window == windows)
1470 window = the_echo_area;
1472 window = window->prev;
1476 if (active_window != window)
1478 if (auto_footnotes_p)
1479 info_get_or_remove_footnotes (window);
1481 window->flags |= W_UpdateWindow;
1482 active_window = window;
1486 /* Split WINDOW into two windows, both showing the same node. If we
1487 are automatically tiling windows, re-tile after the split. */
1488 DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
1490 WINDOW *split, *old_active;
1493 /* Remember the current pagetop of the window being split. If it doesn't
1494 change, we can scroll its contents around after the split. */
1495 pagetop = window->pagetop;
1497 /* Make the new window. */
1498 old_active = active_window;
1499 active_window = window;
1500 split = window_make_window (window->node);
1501 active_window = old_active;
1505 info_error (msg_win_too_small);
1509 #if defined (SPLIT_BEFORE_ACTIVE)
1510 /* Try to scroll the old window into its new postion. */
1511 if (pagetop == window->pagetop)
1513 int start, end, amount;
1515 start = split->first_row;
1516 end = start + window->height;
1517 amount = split->height + 1;
1518 display_scroll_display (start, end, amount);
1520 #else /* !SPLIT_BEFORE_ACTIVE */
1521 /* Make sure point still appears in the active window. */
1522 info_show_point (window);
1523 #endif /* !SPLIT_BEFORE_ACTIVE */
1525 /* If the window just split was one internal to Info, try to display
1526 something else in it. */
1527 if (internal_info_node_p (split->node))
1531 NODE *node = (NODE *)NULL;
1534 for (i = 0; (iw = info_windows[i]); i++)
1536 for (j = 0; j < iw->nodes_index; j++)
1537 if (!internal_info_node_p (iw->nodes[j]))
1539 if (iw->nodes[j]->parent)
1540 filename = iw->nodes[j]->parent;
1542 filename = iw->nodes[j]->filename;
1544 node = info_get_node (filename, iw->nodes[j]->nodename);
1547 window_set_node_of_window (split, node);
1548 i = info_windows_index - 1;
1554 split->pagetop = window->pagetop;
1557 window_tile_windows (DONT_TILE_INTERNALS);
1559 window_adjust_pagetop (split);
1561 remember_window_and_node (split, split->node);
1565 /* Delete WINDOW, forgetting the list of last visited nodes. If we are
1566 automatically displaying footnotes, show or remove the footnotes
1567 window. If we are automatically tiling windows, re-tile after the
1569 DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
1573 info_error (msg_cant_kill_last);
1575 else if (window->flags & W_WindowIsPerm)
1577 info_error (_("Cannot delete a permanent window"));
1581 info_delete_window_internal (window);
1583 if (auto_footnotes_p)
1584 info_get_or_remove_footnotes (active_window);
1587 window_tile_windows (DONT_TILE_INTERNALS);
1591 /* Do the physical deletion of WINDOW, and forget this window and
1592 associated nodes. */
1594 info_delete_window_internal (window)
1597 if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
1599 /* We not only delete the window from the display, we forget it from
1600 our list of remembered windows. */
1601 forget_window_and_nodes (window);
1602 window_delete_window (window);
1604 if (echo_area_is_active)
1605 echo_area_inform_of_deleted_window (window);
1609 /* Just keep WINDOW, deleting all others. */
1610 DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1612 int num_deleted; /* The number of windows we deleted. */
1613 int pagetop, start, end;
1615 /* Remember a few things about this window. We may be able to speed up
1616 redisplay later by scrolling its contents. */
1617 pagetop = window->pagetop;
1618 start = window->first_row;
1619 end = start + window->height;
1627 /* Find an eligible window and delete it. If no eligible windows
1628 are found, we are done. A window is eligible for deletion if
1629 is it not permanent, and it is not WINDOW. */
1630 for (win = windows; win; win = win->next)
1631 if (win != window && ((win->flags & W_WindowIsPerm) == 0))
1637 info_delete_window_internal (win);
1641 /* Scroll the contents of this window into the right place so that the
1642 user doesn't have to wait any longer than necessary for redisplay. */
1647 amount = (window->first_row - start);
1648 amount -= (window->pagetop - pagetop);
1649 display_scroll_display (start, end, amount);
1652 window->flags |= W_UpdateWindow;
1655 /* Scroll the "other" window of WINDOW. */
1656 DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1660 /* If only one window, give up. */
1663 info_error (msg_one_window);
1667 other = window->next;
1670 other = window->prev;
1672 info_scroll_forward (other, count, key);
1675 /* Scroll the "other" window of WINDOW. */
1676 DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
1677 _("Scroll the other window backward"))
1679 info_scroll_other_window (window, -count, key);
1682 /* Change the size of WINDOW by AMOUNT. */
1683 DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1685 window_change_window_height (window, count);
1688 /* When non-zero, tiling takes place automatically when info_split_window
1690 int auto_tiling_p = 0;
1692 /* Tile all of the visible windows. */
1693 DECLARE_INFO_COMMAND (info_tile_windows,
1694 _("Divide the available screen space among the visible windows"))
1696 window_tile_windows (TILE_INTERNALS);
1699 /* Toggle the state of this window's wrapping of lines. */
1700 DECLARE_INFO_COMMAND (info_toggle_wrap,
1701 _("Toggle the state of line wrapping in the current window"))
1703 window_toggle_wrap (window);
1706 /* **************************************************************** */
1708 /* Info Node Commands */
1710 /* **************************************************************** */
1712 /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
1713 filename is not set. */
1715 node_printed_rep (node)
1723 = filename_non_directory (node->parent ? node->parent : node->filename);
1724 rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
1725 sprintf (rep, "(%s)%s", filename, node->nodename);
1728 rep = node->nodename;
1734 /* Using WINDOW for various defaults, select the node referenced by ENTRY
1735 in it. If the node is selected, the window and node are remembered. */
1737 info_select_reference (window, entry)
1742 char *filename, *nodename, *file_system_error;
1744 file_system_error = (char *)NULL;
1746 filename = entry->filename;
1748 filename = window->node->parent;
1750 filename = window->node->filename;
1753 filename = xstrdup (filename);
1755 if (entry->nodename)
1756 nodename = xstrdup (entry->nodename);
1758 nodename = xstrdup ("Top");
1760 node = info_get_node (filename, nodename);
1762 /* Try something a little weird. If the node couldn't be found, and the
1763 reference was of the form "foo::", see if the entry->label can be found
1764 as a file, with a node of "Top". */
1767 if (info_recent_file_error)
1768 file_system_error = xstrdup (info_recent_file_error);
1770 if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
1772 node = info_get_node (entry->label, "Top");
1773 if (!node && info_recent_file_error)
1775 maybe_free (file_system_error);
1776 file_system_error = xstrdup (info_recent_file_error);
1783 if (file_system_error)
1784 info_error (file_system_error);
1786 info_error (msg_cant_find_node, nodename);
1789 maybe_free (file_system_error);
1790 maybe_free (filename);
1791 maybe_free (nodename);
1794 info_set_node_of_window (1, window, node);
1797 /* Parse the node specification in LINE using WINDOW to default the filename.
1798 Select the parsed node in WINDOW and remember it, or error if the node
1799 couldn't be found. */
1801 info_parse_and_select (line, window)
1807 info_parse_node (line, DONT_SKIP_NEWLINES);
1809 entry.nodename = info_parsed_nodename;
1810 entry.filename = info_parsed_filename;
1811 entry.label = "*info-parse-and-select*";
1813 info_select_reference (window, &entry);
1816 /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
1817 are previously filled, try to get the node represented by them into
1818 WINDOW. The node should have been pointed to by the LABEL pointer of
1821 info_handle_pointer (label, window)
1825 if (info_parsed_filename || info_parsed_nodename)
1827 char *filename, *nodename;
1830 filename = nodename = (char *)NULL;
1832 if (info_parsed_filename)
1833 filename = xstrdup (info_parsed_filename);
1836 if (window->node->parent)
1837 filename = xstrdup (window->node->parent);
1838 else if (window->node->filename)
1839 filename = xstrdup (window->node->filename);
1842 if (info_parsed_nodename)
1843 nodename = xstrdup (info_parsed_nodename);
1845 nodename = xstrdup ("Top");
1847 node = info_get_node (filename, nodename);
1851 INFO_WINDOW *info_win;
1853 info_win = get_info_window_of_window (window);
1856 info_win->pagetops[info_win->current] = window->pagetop;
1857 info_win->points[info_win->current] = window->point;
1859 info_set_node_of_window (1, window, node);
1863 if (info_recent_file_error)
1864 info_error (info_recent_file_error);
1866 info_error (msg_cant_file_node, filename, nodename);
1874 info_error (msg_no_pointer, label);
1878 /* Make WINDOW display the "Next:" node of the node currently being
1880 DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
1882 info_next_label_of_node (window->node);
1883 info_handle_pointer ("Next", window);
1886 /* Make WINDOW display the "Prev:" node of the node currently being
1888 DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
1890 info_prev_label_of_node (window->node);
1891 info_handle_pointer ("Prev", window);
1894 /* Make WINDOW display the "Up:" node of the node currently being
1896 DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
1898 info_up_label_of_node (window->node);
1899 info_handle_pointer ("Up", window);
1902 /* Make WINDOW display the last node of this info file. */
1903 DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
1906 FILE_BUFFER *fb = file_buffer_of_window (window);
1907 NODE *node = (NODE *)NULL;
1911 int last_node_tag_idx = -1;
1913 /* If no explicit argument, or argument of zero, default to the
1915 if (count == 0 || (count == 1 && !info_explicit_arg))
1917 for (i = 0; count && fb->tags[i]; i++)
1918 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1921 last_node_tag_idx = i;
1924 i = last_node_tag_idx + 1;
1926 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1930 info_error (_("This window has no additional nodes"));
1932 info_set_node_of_window (1, window, node);
1935 /* Make WINDOW display the first node of this info file. */
1936 DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
1938 FILE_BUFFER *fb = file_buffer_of_window (window);
1939 NODE *node = (NODE *)NULL;
1941 /* If no explicit argument, or argument of zero, default to the
1948 int last_node_tag_idx = -1;
1950 for (i = 0; count && fb->tags[i]; i++)
1951 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1954 last_node_tag_idx = i;
1957 i = last_node_tag_idx + 1;
1959 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1963 info_error (_("This window has no additional nodes"));
1965 info_set_node_of_window (1, window, node);
1968 /* Select the last menu item in WINDOW->node. */
1969 DECLARE_INFO_COMMAND (info_last_menu_item,
1970 _("Select the last item in this node's menu"))
1972 info_menu_digit (window, 1, '0');
1975 /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
1976 DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
1978 register int i, item;
1979 register REFERENCE *entry, **menu;
1981 menu = info_menu_of_node (window->node);
1985 info_error (msg_no_menu_node);
1989 /* We have the menu. See if there are this many items in it. */
1992 /* Special case. Item "0" is the last item in this menu. */
1994 for (i = 0; menu[i + 1]; i++);
1997 for (i = 0; (entry = menu[i]); i++)
2003 info_select_reference (window, menu[i]);
2005 info_error (_("There aren't %d items in this menu."), item);
2007 info_free_references (menu);
2013 /* Return a pointer to the xref in XREF_LIST that is nearest to POS, or
2014 NULL if XREF_LIST is empty. That is, if POS is within any of the
2015 given xrefs, return that one. Otherwise, return the one with the
2016 nearest beginning or end. If there are two that are equidistant,
2017 prefer the one forward. The return is in newly-allocated memory,
2018 since the caller frees it.
2020 This is called from info_menu_or_ref_item with XREF_LIST being all
2021 the xrefs in the node, and POS being point. The ui function that
2022 starts it all off is select-reference-this-line.
2024 This is not the same logic as in info.el. Info-get-token prefers
2025 searching backwards to searching forwards, and has a hardwired search
2026 limit of 200 chars (in Emacs 21.2). */
2029 nearest_xref (xref_list, pos)
2030 REFERENCE **xref_list;
2035 long best_delta = -1;
2037 for (this_xref = 0; xref_list[this_xref]; this_xref++)
2040 REFERENCE *xref = xref_list[this_xref];
2041 if (xref->start <= pos && pos <= xref->end)
2042 { /* POS is within this xref, we're done */
2043 nearest = this_xref;
2047 /* See how far POS is from this xref. Take into account the
2048 `*Note' that begins the xref, since as far as the user is
2049 concerned, that's where it starts. */
2050 delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))),
2051 labs (pos - xref->end));
2053 /* It's the <= instead of < that makes us choose the forward xref
2054 of POS if two are equidistant. Of course, because of all the
2055 punctuation surrounding xrefs, it's not necessarily obvious
2057 if (delta <= best_delta || best_delta < 0)
2059 nearest = this_xref;
2064 /* Maybe there was no list to search through. */
2068 /* Ok, we have a nearest xref, make a list of it. */
2070 REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2);
2071 ret[0] = info_copy_reference (xref_list[nearest]);
2078 /* Read a menu or followed reference from the user defaulting to the
2079 reference found on the current line, and select that node. The
2080 reading is done with completion. BUILDER is the function used
2081 to build the list of references. ASK_P is non-zero if the user
2082 should be prompted, or zero to select the default item. */
2084 info_menu_or_ref_item (window, count, key, builder, ask_p)
2088 REFERENCE **(*builder) ();
2093 REFERENCE *defentry = NULL;
2094 REFERENCE **menu = (*builder) (window->node);
2098 if (builder == info_menu_of_node)
2099 info_error (msg_no_menu_node);
2101 info_error (msg_no_xref_node);
2105 /* Default the selected reference to the one which is on the line that
2108 REFERENCE **refs = NULL;
2109 int point_line = window_line_of_point (window);
2111 if (point_line != -1)
2113 SEARCH_BINDING binding;
2115 binding.buffer = window->node->contents;
2116 binding.start = window->line_starts[point_line] - binding.buffer;
2117 if (window->line_starts[point_line + 1])
2118 binding.end = window->line_starts[point_line + 1] - binding.buffer;
2120 binding.end = window->node->nodelen;
2123 if (builder == info_menu_of_node)
2128 refs = info_menu_items (&binding);
2133 #if defined (HANDLE_MAN_PAGES)
2134 if (window->node->flags & N_IsManPage)
2135 refs = manpage_xrefs_in_binding (window->node, &binding);
2137 #endif /* HANDLE_MAN_PAGES */
2138 refs = nearest_xref (menu, window->point);
2141 if (refs && refs[0])
2143 if (strcmp (refs[0]->label, "Menu") != 0
2144 || builder == info_xrefs_of_node)
2148 /* For xrefs, find the closest reference to point,
2149 unless we only have one reference (as we will if
2150 we've called nearest_xref above). It would be better
2151 to have only one piece of code, but the conditions
2152 when we call this are tangled. */
2153 if (builder == info_xrefs_of_node && refs[1])
2157 for (; refs[which]; which++)
2159 if (window->point >= refs[which]->start
2160 && window->point <= refs[which]->end)
2165 else if (window->point < refs[which]->start)
2174 defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2175 defentry->label = xstrdup (refs[which]->label);
2176 defentry->filename = refs[which]->filename;
2177 defentry->nodename = refs[which]->nodename;
2179 if (defentry->filename)
2180 defentry->filename = xstrdup (defentry->filename);
2181 if (defentry->nodename)
2182 defentry->nodename = xstrdup (defentry->nodename);
2184 info_free_references (refs);
2189 /* If we are going to ask the user a question, do it now. */
2194 /* Build the prompt string. */
2196 prompt = (char *)xmalloc (99 + strlen (defentry->label));
2198 prompt = (char *)xmalloc (99);
2200 if (builder == info_menu_of_node)
2203 sprintf (prompt, _("Menu item (%s): "), defentry->label);
2205 sprintf (prompt, _("Menu item: "));
2210 sprintf (prompt, _("Follow xref (%s): "), defentry->label);
2212 sprintf (prompt, _("Follow xref: "));
2215 line = info_read_completing_in_echo_area (window, prompt, menu);
2218 window = active_window;
2220 /* User aborts, just quit. */
2223 maybe_free (defentry);
2224 info_free_references (menu);
2225 info_abort_key (window, 0, 0);
2229 /* If we had a default and the user accepted it, use that. */
2234 line = xstrdup (defentry->label);
2236 line = (char *)NULL;
2241 /* Not going to ask any questions. If we have a default entry, use
2242 that, otherwise return. */
2246 line = xstrdup (defentry->label);
2251 /* It is possible that the references have more than a single
2252 entry with the same label, and also LINE is down-cased, which
2253 complicates matters even more. Try to be as accurate as we
2254 can: if they've chosen the default, use defentry directly. */
2255 if (defentry && strcmp (line, defentry->label) == 0)
2258 /* Find the selected label in the references. If there are
2259 more than one label which matches, find the one that's
2260 closest to point. */
2263 int best = -1, min_dist = window->node->nodelen;
2266 for (i = 0; menu && (ref = menu[i]); i++)
2268 /* Need to use strcasecmp because LINE is downcased
2269 inside info_read_completing_in_echo_area. */
2270 if (strcasecmp (line, ref->label) == 0)
2272 /* ref->end is more accurate estimate of position
2273 for menus than ref->start. Go figure. */
2274 int dist = abs (window->point - ref->end);
2276 if (dist < min_dist)
2286 entry = (REFERENCE *)NULL;
2289 if (!entry && defentry)
2290 info_error (_("The reference disappeared! (%s)."), line);
2293 NODE *orig = window->node;
2294 info_select_reference (window, entry);
2295 if (builder == info_xrefs_of_node && window->node != orig
2296 && !(window->node->flags & N_FromAnchor))
2297 { /* Search for this reference in the node. */
2301 if (window->line_count > 0)
2302 start = window->line_starts[1] - window->node->contents;
2307 info_target_search_node (window->node, entry->label, start);
2311 window->point = offset;
2312 window_adjust_pagetop (window);
2320 free (defentry->label);
2321 maybe_free (defentry->filename);
2322 maybe_free (defentry->nodename);
2327 info_free_references (menu);
2329 if (!info_error_was_printed)
2330 window_clear_echo_area ();
2333 /* Read a line (with completion) which is the name of a menu item,
2334 and select that item. */
2335 DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
2337 info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2340 /* Read a line (with completion) which is the name of a reference to
2341 follow, and select the node. */
2342 DECLARE_INFO_COMMAND
2343 (info_xref_item, _("Read a footnote or cross reference and select its node"))
2345 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2348 /* Position the cursor at the start of this node's menu. */
2349 DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
2351 SEARCH_BINDING binding;
2354 binding.buffer = window->node->contents;
2356 binding.end = window->node->nodelen;
2357 binding.flags = S_FoldCase | S_SkipDest;
2359 position = search (INFO_MENU_LABEL, &binding);
2362 info_error (msg_no_menu_node);
2365 window->point = position;
2366 window_adjust_pagetop (window);
2367 window->flags |= W_UpdateWindow;
2371 /* Visit as many menu items as is possible, each in a separate window. */
2372 DECLARE_INFO_COMMAND (info_visit_menu,
2373 _("Visit as many menu items at once as possible"))
2376 REFERENCE *entry, **menu;
2378 menu = info_menu_of_node (window->node);
2381 info_error (msg_no_menu_node);
2383 for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2387 new = window_make_window (window->node);
2388 window_tile_windows (TILE_INTERNALS);
2391 info_error (msg_win_too_small);
2394 active_window = new;
2395 info_select_reference (new, entry);
2400 /* Read a line of input which is a node name, and go to that node. */
2401 DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
2405 #define GOTO_COMPLETES
2406 #if defined (GOTO_COMPLETES)
2407 /* Build a completion list of all of the known nodes. */
2409 register int fbi, i;
2410 FILE_BUFFER *current;
2411 REFERENCE **items = (REFERENCE **)NULL;
2412 int items_index = 0;
2413 int items_slots = 0;
2415 current = file_buffer_of_window (window);
2417 for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2421 int this_is_the_current_fb;
2423 fb = info_loaded_files[fbi];
2424 this_is_the_current_fb = (current == fb);
2426 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2427 entry->filename = entry->nodename = (char *)NULL;
2428 entry->label = (char *)xmalloc (4 + strlen (fb->filename));
2429 sprintf (entry->label, "(%s)*", fb->filename);
2431 add_pointer_to_array
2432 (entry, items_index, items, items_slots, 10, REFERENCE *);
2436 for (i = 0; fb->tags[i]; i++)
2438 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2439 entry->filename = entry->nodename = (char *)NULL;
2440 if (this_is_the_current_fb)
2441 entry->label = xstrdup (fb->tags[i]->nodename);
2444 entry->label = (char *) xmalloc
2445 (4 + strlen (fb->filename) +
2446 strlen (fb->tags[i]->nodename));
2447 sprintf (entry->label, "(%s)%s",
2448 fb->filename, fb->tags[i]->nodename);
2451 add_pointer_to_array
2452 (entry, items_index, items, items_slots, 100, REFERENCE *);
2456 line = info_read_maybe_completing (window, _("Goto node: "), items);
2457 info_free_references (items);
2459 #else /* !GOTO_COMPLETES */
2460 line = info_read_in_echo_area (window, _("Goto node: "));
2461 #endif /* !GOTO_COMPLETES */
2463 /* If the user aborted, quit now. */
2466 info_abort_key (window, 0, 0);
2470 canonicalize_whitespace (line);
2473 info_parse_and_select (line, window);
2476 if (!info_error_was_printed)
2477 window_clear_echo_area ();
2480 /* Follow the menu list in MENUS (list of strings terminated by a NULL
2481 entry) from INITIAL_NODE. If can't continue at any point (no menu or
2482 no menu entry for the next item), return the node so far -- that
2483 might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will
2484 be set to the error message and argument for message, otherwise they
2488 info_follow_menus (initial_node, menus, errstr, errarg1, errarg2)
2491 const char **errstr;
2492 char **errarg1, **errarg2;
2495 *errstr = *errarg1 = *errarg2 = NULL;
2497 for (; *menus; menus++)
2499 static char *first_arg = NULL;
2502 char *arg = *menus; /* Remember the name of the menu entry we want. */
2504 /* A leading space is certainly NOT part of a node name. Most
2505 probably, they typed a space after the separating comma. The
2506 strings in menus[] have their whitespace canonicalized, so
2507 there's at most one space to ignore. */
2513 /* Build and return a list of the menu items in this node. */
2514 menu = info_menu_of_node (initial_node);
2516 /* If no menu item in this node, stop here, but let the user
2517 continue to use Info. Perhaps they wanted this node and didn't
2521 if (arg == first_arg)
2523 node = make_manpage_node (first_arg);
2525 goto maybe_got_node;
2527 *errstr = _("No menu in node `%s'.");
2528 *errarg1 = node_printed_rep (initial_node);
2529 return initial_node;
2532 /* Find the specified menu item. */
2533 entry = info_get_labeled_reference (arg, menu);
2535 /* If the item wasn't found, search the list sloppily. Perhaps this
2536 user typed "buffer" when they really meant "Buffers". */
2540 int best_guess = -1;
2542 for (i = 0; (entry = menu[i]); i++)
2544 if (strcasecmp (entry->label, arg) == 0)
2547 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2551 if (!entry && best_guess != -1)
2552 entry = menu[best_guess];
2555 /* If we still failed to find the reference, start Info with the current
2556 node anyway. It is probably a misspelling. */
2559 if (arg == first_arg)
2561 /* Maybe they typed "info foo" instead of "info -f foo". */
2562 node = info_get_node (first_arg, 0);
2564 add_file_directory_to_path (first_arg);
2566 node = make_manpage_node (first_arg);
2568 goto maybe_got_node;
2571 info_free_references (menu);
2572 *errstr = _("No menu item `%s' in node `%s'.");
2574 *errarg2 = node_printed_rep (initial_node);
2575 return initial_node;
2578 /* We have found the reference that the user specified. If no
2579 filename in this reference, define it. */
2580 if (!entry->filename)
2581 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2582 : initial_node->filename);
2584 /* Try to find this node. */
2585 node = info_get_node (entry->filename, entry->nodename);
2586 if (!node && arg == first_arg)
2588 node = make_manpage_node (first_arg);
2590 goto maybe_got_node;
2593 /* Since we cannot find it, try using the label of the entry as a
2594 file, i.e., "(LABEL)Top". */
2595 if (!node && entry->nodename
2596 && strcmp (entry->label, entry->nodename) == 0)
2597 node = info_get_node (entry->label, "Top");
2602 *errstr = _("Unable to find node referenced by `%s' in `%s'.");
2603 *errarg1 = xstrdup (entry->label);
2604 *errarg2 = node_printed_rep (initial_node);
2605 info_free_references (menu);
2606 return initial_node;
2609 info_free_references (menu);
2611 /* Success. Go round the loop again. */
2612 free (initial_node);
2613 initial_node = node;
2616 return initial_node;
2619 /* Split STR into individual node names by writing null bytes in wherever
2620 there are commas and constructing a list of the resulting pointers.
2621 (We can do this since STR has had canonicalize_whitespace called on it.)
2622 Return array terminated with NULL. */
2625 split_list_of_nodenames (str)
2629 char **nodes = xmalloc (len * sizeof (char *));
2631 nodes[len - 2] = str;
2637 *str++ = 0; /* get past the null byte */
2639 nodes = xrealloc (nodes, len * sizeof (char *));
2640 nodes[len - 2] = str;
2644 nodes[len - 1] = NULL;
2650 /* Read a line of input which is a sequence of menus (starting from
2651 dir), and follow them. */
2652 DECLARE_INFO_COMMAND (info_menu_sequence,
2653 _("Read a list of menus starting from dir and follow them"))
2655 char *line = info_read_in_echo_area (window, _("Follow menus: "));
2657 /* If the user aborted, quit now. */
2660 info_abort_key (window, 0, 0);
2664 canonicalize_whitespace (line);
2668 char *errstr, *errarg1, *errarg2;
2669 NODE *dir_node = info_get_node (NULL, NULL);
2670 char **nodes = split_list_of_nodenames (line);
2673 /* If DIR_NODE is NULL, they might be reading a file directly,
2674 like in "info -d . -f ./foo". Try using "Top" instead. */
2677 char *file_name = window->node->parent;
2680 file_name = window->node->filename;
2681 dir_node = info_get_node (file_name, NULL);
2684 /* If we still cannot find the starting point, give up.
2685 We cannot allow a NULL pointer inside info_follow_menus. */
2687 info_error (msg_cant_find_node, "Top");
2690 = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
2694 info_set_node_of_window (1, window, node);
2696 info_error (errstr, errarg1, errarg2);
2700 if (!info_error_was_printed)
2701 window_clear_echo_area ();
2704 /* Search the menu MENU for a (possibly mis-spelled) entry ARG.
2705 Return the menu entry, or the best guess for what they meant by ARG,
2706 or NULL if there's nothing in this menu seems to fit the bill.
2707 If EXACT is non-zero, allow only exact matches. */
2709 entry_in_menu (arg, menu, exact)
2716 /* First, try to find the specified menu item verbatim. */
2717 entry = info_get_labeled_reference (arg, menu);
2719 /* If the item wasn't found, search the list sloppily. Perhaps we
2720 have "Option Summary", but ARG is "option". */
2721 if (!entry && !exact)
2724 int best_guess = -1;
2726 for (i = 0; (entry = menu[i]); i++)
2728 if (strcasecmp (entry->label, arg) == 0)
2731 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2735 if (!entry && best_guess != -1)
2736 entry = menu[best_guess];
2742 /* Find the node that is the best candidate to list the PROGRAM's
2743 invocation info and its command-line options, by looking for menu
2744 items and chains of menu items with characteristic names. */
2746 info_intuit_options_node (window, initial_node, program)
2751 /* The list of node names typical for GNU manuals where the program
2752 usage and specifically the command-line arguments are described.
2753 This is pure heuristics. I gathered these node names by looking
2754 at all the Info files I could put my hands on. If you are
2755 looking for evidence to complain to the GNU project about
2756 non-uniform style of documentation, here you have your case! */
2757 static const char *invocation_nodes[] = {
2760 "Preliminaries", /* m4 has Invoking under Preliminaries! */
2762 "Command Arguments",/* Emacs */
2766 "Option ", /* e.g. "Option Summary" */
2768 "All options", /* tar, paxutils */
2770 "%s cmdline", /* ar */
2771 "%s", /* last resort */
2776 const char **try_node;
2778 /* We keep looking deeper and deeper in the menu structure until
2779 there are no more menus or no menu items from the above list.
2780 Some manuals have the invocation node sitting 3 or 4 levels deep
2781 in the menu hierarchy... */
2782 for (node = initial_node; node; initial_node = node)
2786 /* Build and return a list of the menu items in this node. */
2787 menu = info_menu_of_node (initial_node);
2789 /* If no menu item in this node, stop here. Perhaps this node
2790 is the one they need. */
2794 /* Look for node names typical for usage nodes in this menu. */
2795 for (try_node = invocation_nodes; *try_node; try_node++)
2799 sprintf (nodename, *try_node, program);
2800 /* The last resort "%s" is dangerous, so we restrict it
2801 to exact matches here. */
2802 entry = entry_in_menu (nodename, menu,
2803 strcmp (*try_node, "%s") == 0);
2811 if (!entry->filename)
2812 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2813 : initial_node->filename);
2814 /* Try to find this node. */
2815 node = info_get_node (entry->filename, entry->nodename);
2816 info_free_references (menu);
2821 /* We've got our best shot at the invocation node. Now select it. */
2823 info_set_node_of_window (1, window, initial_node);
2824 if (!info_error_was_printed)
2825 window_clear_echo_area ();
2828 /* Given a name of an Info file, find the name of the package it
2829 describes by removing the leading directories and extensions. */
2831 program_name_from_file_name (file_name)
2835 char *program_name = xstrdup (filename_non_directory (file_name));
2837 for (i = strlen (program_name) - 1; i > 0; i--)
2838 if (program_name[i] == '.'
2839 && (FILENAME_CMPN (program_name + i, ".info", 5) == 0
2840 || FILENAME_CMPN (program_name + i, ".inf", 4) == 0
2842 || FILENAME_CMPN (program_name + i, ".i", 2) == 0
2844 || isdigit (program_name[i + 1]))) /* a man page foo.1 */
2846 program_name[i] = 0;
2849 return program_name;
2852 DECLARE_INFO_COMMAND (info_goto_invocation_node,
2853 _("Find the node describing program invocation"))
2855 const char *invocation_prompt = _("Find Invocation node of [%s]: ");
2856 char *program_name, *line;
2857 char *default_program_name, *prompt, *file_name;
2860 /* Intuit the name of the program they are likely to want.
2861 We use the file name of the current Info file as a hint. */
2862 file_name = window->node->parent ? window->node->parent
2863 : window->node->filename;
2864 default_program_name = program_name_from_file_name (file_name);
2866 prompt = (char *)xmalloc (strlen (default_program_name) +
2867 strlen (invocation_prompt));
2868 sprintf (prompt, invocation_prompt, default_program_name);
2869 line = info_read_in_echo_area (window, prompt);
2877 program_name = line;
2879 program_name = default_program_name;
2881 /* In interactive usage they'd probably expect us to begin looking
2882 from the Top node. */
2883 top_node = info_get_node (file_name, NULL);
2885 info_error (msg_cant_find_node, "Top");
2887 info_intuit_options_node (window, top_node, program_name);
2889 free (default_program_name);
2892 #if defined (HANDLE_MAN_PAGES)
2893 DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
2897 line = info_read_in_echo_area (window, _("Get Manpage: "));
2901 info_abort_key (window, 0, 0);
2905 canonicalize_whitespace (line);
2911 goto_command = (char *)xmalloc
2912 (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
2914 sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
2916 info_parse_and_select (goto_command, window);
2917 free (goto_command);
2921 if (!info_error_was_printed)
2922 window_clear_echo_area ();
2924 #endif /* HANDLE_MAN_PAGES */
2926 /* Move to the "Top" node in this file. */
2927 DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
2929 info_parse_and_select ("Top", window);
2932 /* Move to the node "(dir)Top". */
2933 DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
2935 info_parse_and_select ("(dir)Top", window);
2939 /* Read the name of a node to kill. The list of available nodes comes
2940 from the nodes appearing in the current window configuration. */
2942 read_nodename_to_kill (window)
2947 INFO_WINDOW *info_win;
2948 REFERENCE **menu = NULL;
2949 int menu_index = 0, menu_slots = 0;
2950 char *default_nodename = xstrdup (active_window->node->nodename);
2951 char *prompt = xmalloc (40 + strlen (default_nodename));
2953 sprintf (prompt, _("Kill node (%s): "), default_nodename);
2955 for (iw = 0; (info_win = info_windows[iw]); iw++)
2957 REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2958 entry->label = xstrdup (info_win->window->node->nodename);
2959 entry->filename = entry->nodename = (char *)NULL;
2961 add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
2965 nodename = info_read_completing_in_echo_area (window, prompt, menu);
2967 info_free_references (menu);
2968 if (nodename && !*nodename)
2971 nodename = default_nodename;
2974 free (default_nodename);
2980 /* Delete NODENAME from this window, showing the most
2981 recently selected node in this window. */
2983 kill_node (window, nodename)
2988 INFO_WINDOW *info_win;
2991 /* If there is no nodename to kill, quit now. */
2994 info_abort_key (window, 0, 0);
2998 /* If there is a nodename, find it in our window list. */
2999 for (iw = 0; (info_win = info_windows[iw]); iw++)
3000 if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
3001 && info_win->window == window)
3007 info_error (_("Cannot kill node `%s'"), nodename);
3009 window_clear_echo_area ();
3014 /* If there are no more nodes left anywhere to view, complain and exit. */
3015 if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
3017 info_error (_("Cannot kill the last node"));
3021 /* INFO_WIN contains the node that the user wants to stop viewing. Delete
3022 this node from the list of nodes previously shown in this window. */
3023 for (i = info_win->current; i < info_win->nodes_index; i++)
3024 info_win->nodes[i] = info_win->nodes[i + 1];
3026 /* There is one less node in this window's history list. */
3027 info_win->nodes_index--;
3029 /* Make this window show the most recent history node. */
3030 info_win->current = info_win->nodes_index - 1;
3032 /* If there aren't any nodes left in this window, steal one from the
3034 if (info_win->current < 0)
3036 INFO_WINDOW *stealer;
3040 if (info_windows[iw + 1])
3041 stealer = info_windows[iw + 1];
3043 stealer = info_windows[0];
3045 /* If the node being displayed in the next window is not the most
3046 recently loaded one, get the most recently loaded one. */
3047 if ((stealer->nodes_index - 1) != stealer->current)
3048 which = stealer->nodes_index - 1;
3050 /* Else, if there is another node behind the stealers current node,
3052 else if (stealer->current > 0)
3053 which = stealer->current - 1;
3055 /* Else, just use the node appearing in STEALER's window. */
3057 which = stealer->current;
3059 /* Copy this node. */
3061 NODE *copy = xmalloc (sizeof (NODE));
3063 temp = stealer->nodes[which];
3064 point = stealer->points[which];
3065 pagetop = stealer->pagetops[which];
3067 copy->filename = temp->filename;
3068 copy->parent = temp->parent;
3069 copy->nodename = temp->nodename;
3070 copy->contents = temp->contents;
3071 copy->nodelen = temp->nodelen;
3072 copy->flags = temp->flags;
3073 copy->display_pos = temp->display_pos;
3078 window_set_node_of_window (info_win->window, temp);
3079 window->point = point;
3080 window->pagetop = pagetop;
3081 remember_window_and_node (info_win->window, temp);
3085 temp = info_win->nodes[info_win->current];
3086 temp->display_pos = info_win->points[info_win->current];
3087 window_set_node_of_window (info_win->window, temp);
3090 if (!info_error_was_printed)
3091 window_clear_echo_area ();
3093 if (auto_footnotes_p)
3094 info_get_or_remove_footnotes (window);
3097 /* Kill current node, thus going back one in the node history. I (karl)
3098 do not think this is completely correct yet, because of the
3099 window-changing stuff in kill_node, but it's a lot better than the
3100 previous implementation, which did not account for nodes being
3101 visited twice at all. */
3102 DECLARE_INFO_COMMAND (info_history_node,
3103 _("Select the most recently selected node"))
3105 kill_node (window, active_window->node->nodename);
3108 /* Kill named node. */
3109 DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
3111 char *nodename = read_nodename_to_kill (window);
3112 kill_node (window, nodename);
3116 /* Read the name of a file and select the entire file. */
3117 DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
3121 line = info_read_in_echo_area (window, _("Find file: "));
3124 info_abort_key (active_window, 1, 0);
3132 node = info_get_node (line, "*");
3135 if (info_recent_file_error)
3136 info_error (info_recent_file_error);
3138 info_error (_("Cannot find `%s'."), line);
3141 info_set_node_of_window (1, window, node);
3146 if (!info_error_was_printed)
3147 window_clear_echo_area ();
3150 /* **************************************************************** */
3152 /* Dumping and Printing Nodes */
3154 /* **************************************************************** */
3156 #define VERBOSE_NODE_DUMPING
3157 static void write_node_to_stream ();
3158 static void dump_node_to_stream ();
3159 static void initialize_dumping ();
3161 /* Dump the nodes specified by FILENAME and NODENAMES to the file named
3162 in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3163 the nodes which appear in the menu of each node dumped. */
3165 dump_nodes_to_file (filename, nodenames, output_filename, dump_subnodes)
3168 char *output_filename;
3172 FILE *output_stream;
3174 /* Get the stream to print the nodes to. Special case of an output
3175 filename of "-" means to dump the nodes to stdout. */
3176 if (strcmp (output_filename, "-") == 0)
3177 output_stream = stdout;
3179 output_stream = fopen (output_filename, "w");
3183 info_error (_("Could not create output file `%s'."), output_filename);
3187 /* Print each node to stream. */
3188 initialize_dumping ();
3189 for (i = 0; nodenames[i]; i++)
3190 dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
3192 if (output_stream != stdout)
3193 fclose (output_stream);
3195 #if defined (VERBOSE_NODE_DUMPING)
3196 info_error (_("Done."));
3197 #endif /* VERBOSE_NODE_DUMPING */
3200 /* A place to remember already dumped nodes. */
3201 static char **dumped_already = (char **)NULL;
3202 static int dumped_already_index = 0;
3203 static int dumped_already_slots = 0;
3206 initialize_dumping ()
3208 dumped_already_index = 0;
3211 /* Get and print the node specified by FILENAME and NODENAME to STREAM.
3212 If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
3213 in the menu of each node dumped. */
3215 dump_node_to_stream (filename, nodename, stream, dump_subnodes)
3216 char *filename, *nodename;
3223 node = info_get_node (filename, nodename);
3227 if (info_recent_file_error)
3228 info_error (info_recent_file_error);
3231 if (filename && *nodename != '(')
3232 info_error (msg_cant_file_node, filename_non_directory (filename),
3235 info_error (msg_cant_find_node, nodename);
3240 /* If we have already dumped this node, don't dump it again. */
3241 for (i = 0; i < dumped_already_index; i++)
3242 if (strcmp (node->nodename, dumped_already[i]) == 0)
3247 add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
3248 dumped_already_slots, 50, char *);
3250 #if defined (VERBOSE_NODE_DUMPING)
3251 /* Maybe we should print some information about the node being output. */
3252 info_error (_("Writing node %s..."), node_printed_rep (node));
3253 #endif /* VERBOSE_NODE_DUMPING */
3255 write_node_to_stream (node, stream);
3257 /* If we are dumping subnodes, get the list of menu items in this node,
3258 and dump each one recursively. */
3261 REFERENCE **menu = (REFERENCE **)NULL;
3263 /* If this node is an Index, do not dump the menu references. */
3264 if (string_in_line ("Index", node->nodename) == -1)
3265 menu = info_menu_of_node (node);
3269 for (i = 0; menu[i]; i++)
3271 /* We don't dump Info files which are different than the
3273 if (!menu[i]->filename)
3275 (filename, menu[i]->nodename, stream, dump_subnodes);
3277 info_free_references (menu);
3284 /* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3285 the nodes which appear in the menu of each node dumped. */
3287 dump_node_to_file (node, filename, dump_subnodes)
3292 FILE *output_stream;
3293 char *nodes_filename;
3295 /* Get the stream to print this node to. Special case of an output
3296 filename of "-" means to dump the nodes to stdout. */
3297 if (strcmp (filename, "-") == 0)
3298 output_stream = stdout;
3300 output_stream = fopen (filename, "w");
3304 info_error (_("Could not create output file `%s'."), filename);
3309 nodes_filename = node->parent;
3311 nodes_filename = node->filename;
3313 initialize_dumping ();
3315 (nodes_filename, node->nodename, output_stream, dump_subnodes);
3317 if (output_stream != stdout)
3318 fclose (output_stream);
3320 #if defined (VERBOSE_NODE_DUMPING)
3321 info_error (_("Done."));
3322 #endif /* VERBOSE_NODE_DUMPING */
3325 #if !defined (DEFAULT_INFO_PRINT_COMMAND)
3326 # define DEFAULT_INFO_PRINT_COMMAND "lpr"
3327 #endif /* !DEFAULT_INFO_PRINT_COMMAND */
3329 DECLARE_INFO_COMMAND (info_print_node,
3330 _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
3332 print_node (window->node);
3335 /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3341 char *print_command = getenv ("INFO_PRINT_COMMAND");
3344 if (!print_command || !*print_command)
3345 print_command = DEFAULT_INFO_PRINT_COMMAND;
3347 /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
3348 (default) text mode, since the printer drivers there need to see
3349 DOS-style CRLF pairs at the end of each line.
3351 FIXME: if we are to support Mac-style text files, we might need
3352 to convert the text here. */
3354 /* INFO_PRINT_COMMAND which says ">file" means write to that file.
3355 Presumably, the name of the file is the local printer device. */
3356 if (*print_command == '>')
3357 printer_pipe = fopen (++print_command, "w");
3360 printer_pipe = popen (print_command, "w");
3366 info_error (_("Cannot open pipe to `%s'."), print_command);
3370 #if defined (VERBOSE_NODE_DUMPING)
3371 /* Maybe we should print some information about the node being output. */
3372 info_error (_("Printing node %s..."), node_printed_rep (node));
3373 #endif /* VERBOSE_NODE_DUMPING */
3375 write_node_to_stream (node, printer_pipe);
3377 pclose (printer_pipe);
3379 fclose (printer_pipe);
3381 #if defined (VERBOSE_NODE_DUMPING)
3382 info_error (_("Done."));
3383 #endif /* VERBOSE_NODE_DUMPING */
3387 write_node_to_stream (node, stream)
3391 fwrite (node->contents, 1, node->nodelen, stream);
3394 /* **************************************************************** */
3396 /* Info Searching Commands */
3398 /* **************************************************************** */
3400 /* Variable controlling the garbage collection of files briefly visited
3401 during searches. Such files are normally gc'ed, unless they were
3402 compressed to begin with. If this variable is non-zero, it says
3403 to gc even those file buffer contents which had to be uncompressed. */
3404 int gc_compressed_files = 0;
3406 static void info_gc_file_buffers ();
3407 static void info_search_1 ();
3409 static char *search_string = (char *)NULL;
3410 static int search_string_index = 0;
3411 static int search_string_size = 0;
3412 static int isearch_is_active = 0;
3414 static int last_search_direction = 0;
3415 static int last_search_case_sensitive = 0;
3417 /* Return the file buffer which belongs to WINDOW's node. */
3419 file_buffer_of_window (window)
3422 /* If this window has no node, then it has no file buffer. */
3424 return ((FILE_BUFFER *)NULL);
3426 if (window->node->parent)
3427 return (info_find_file (window->node->parent));
3429 if (window->node->filename)
3430 return (info_find_file (window->node->filename));
3432 return ((FILE_BUFFER *)NULL);
3435 /* Search for STRING in NODE starting at START. Return -1 if the string
3436 was not found, or the location of the string if it was. If WINDOW is
3437 passed as non-null, set the window's node to be NODE, its point to be
3438 the found string, and readjust the window's pagetop. Final argument
3439 DIR says which direction to search in. If it is positive, search
3440 forward, else backwards. */
3442 info_search_in_node (string, node, start, window, dir, case_sensitive)
3447 int dir, case_sensitive;
3449 SEARCH_BINDING binding;
3452 binding.buffer = node->contents;
3453 binding.start = start;
3454 binding.end = node->nodelen;
3456 if (!case_sensitive)
3457 binding.flags |= S_FoldCase;
3462 binding.flags |= S_SkipDest;
3465 if (binding.start < 0)
3468 /* For incremental searches, we always wish to skip past the string. */
3469 if (isearch_is_active)
3470 binding.flags |= S_SkipDest;
3472 offset = search (string, &binding);
3474 if (offset != -1 && window)
3476 set_remembered_pagetop_and_point (window);
3477 if (window->node != node)
3478 window_set_node_of_window (window, node);
3479 window->point = offset;
3480 window_adjust_pagetop (window);
3485 /* Search NODE, looking for the largest possible match of STRING. Start the
3486 search at START. Return the absolute position of the match, or -1, if
3487 no part of the string could be found. */
3489 info_target_search_node (node, string, start)
3498 target = xstrdup (string);
3499 i = strlen (target);
3501 /* Try repeatedly searching for this string while removing words from
3506 offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
3511 /* Delete the last word from TARGET. */
3512 for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
3518 /* Search for STRING starting in WINDOW at point. If the string is found
3519 in this node, set point to that position. Otherwise, get the file buffer
3520 associated with WINDOW's node, and search through each node in that file.
3521 If the search fails, return non-zero, else zero. Side-effect window
3522 leaving the node and point where the string was found current. */
3524 info_search_internal (string, window, dir, case_sensitive)
3527 int dir, case_sensitive;
3530 FILE_BUFFER *file_buffer;
3531 char *initial_nodename;
3532 long ret, start = 0;
3534 file_buffer = file_buffer_of_window (window);
3535 initial_nodename = window->node->nodename;
3537 /* This used to begin from window->point, unless this was a repeated
3538 search command. But invoking search with an argument loses with
3539 that logic, since info_last_executed_command is then set to
3540 info_add_digit_to_numeric_arg. I think there's no sense in
3541 ``finding'' a string that is already under the cursor, anyway. */
3542 ret = info_search_in_node
3543 (string, window->node, window->point + dir, window, dir,
3549 if (!echo_area_is_active && !isearch_is_active)
3550 window_clear_echo_area ();
3554 /* The string wasn't found in the current node. Search through the
3555 window's file buffer, iff the current node is not "*". */
3556 if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
3559 /* If this file has tags, search through every subfile, starting at
3560 this node's subfile and node. Otherwise, search through the
3561 file's node list. */
3562 if (file_buffer->tags)
3564 register int current_tag, number_of_tags;
3568 /* Find number of tags and current tag. */
3569 last_subfile = (char *)NULL;
3570 for (i = 0; file_buffer->tags[i]; i++)
3571 if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
3574 last_subfile = file_buffer->tags[i]->filename;
3579 /* If there is no last_subfile, our tag wasn't found. */
3583 /* Search through subsequent nodes, wrapping around to the top
3584 of the info file until we find the string or return to this
3585 window's node and point. */
3590 /* Allow C-g to quit the search, failing it if pressed. */
3591 return_if_control_g (-1);
3593 /* Find the next tag that isn't an anchor. */
3594 for (i = current_tag + dir; i != current_tag; i += dir)
3597 i = number_of_tags - 1;
3598 else if (i == number_of_tags)
3601 tag = file_buffer->tags[i];
3602 if (tag->nodelen != 0)
3606 /* If we got past out starting point, bail out. */
3607 if (i == current_tag)
3611 if (!echo_area_is_active && (last_subfile != tag->filename))
3613 window_message_in_echo_area
3614 (_("Searching subfile %s ..."),
3615 filename_non_directory (tag->filename));
3617 last_subfile = tag->filename;
3620 node = info_get_node (file_buffer->filename, tag->nodename);
3624 /* If not doing i-search... */
3625 if (!echo_area_is_active)
3627 if (info_recent_file_error)
3628 info_error (info_recent_file_error);
3630 info_error (msg_cant_file_node,
3631 filename_non_directory (file_buffer->filename),
3638 start = tag->nodelen;
3641 info_search_in_node (string, node, start, window, dir,
3644 /* Did we find the string in this node? */
3648 remember_window_and_node (window, node);
3649 if (!echo_area_is_active)
3650 window_clear_echo_area ();
3654 /* No. Free this node, and make sure that we haven't passed
3655 our starting point. */
3658 if (strcmp (initial_nodename, tag->nodename) == 0)
3665 DECLARE_INFO_COMMAND (info_search_case_sensitively,
3666 _("Read a string and search for it case-sensitively"))
3668 last_search_direction = count > 0 ? 1 : -1;
3669 last_search_case_sensitive = 1;
3670 info_search_1 (window, count, key, 1, 1);
3673 DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
3675 last_search_direction = count > 0 ? 1 : -1;
3676 last_search_case_sensitive = 0;
3677 info_search_1 (window, count, key, 0, 1);
3680 DECLARE_INFO_COMMAND (info_search_backward,
3681 _("Read a string and search backward for it"))
3683 last_search_direction = count > 0 ? -1 : 1;
3684 last_search_case_sensitive = 0;
3685 info_search_1 (window, -count, key, 0, 1);
3689 info_search_1 (window, count, key, case_sensitive, ask_for_string)
3696 char *line, *prompt;
3697 int result, old_pagetop;
3709 count = 1; /* for backward compatibility */
3712 /* Read a string from the user, defaulting the search to SEARCH_STRING. */
3715 search_string = (char *)xmalloc (search_string_size = 100);
3716 search_string[0] = '\0';
3721 prompt = (char *)xmalloc (50 + strlen (search_string));
3723 sprintf (prompt, _("%s%sfor string [%s]: "),
3724 direction < 0 ? _("Search backward") : _("Search"),
3725 case_sensitive ? _(" case-sensitively ") : _(" "),
3728 line = info_read_in_echo_area (window, prompt);
3739 if (strlen (line) + 1 > search_string_size)
3740 search_string = (char *) xrealloc
3741 (search_string, (search_string_size += 50 + strlen (line)));
3743 strcpy (search_string, line);
3744 search_string_index = strlen (line);
3749 /* If the search string includes upper-case letters, make the search
3751 if (case_sensitive == 0)
3752 for (line = search_string; *line; line++)
3753 if (isupper (*line))
3759 old_pagetop = active_window->pagetop;
3760 for (result = 0; result == 0 && count--; )
3761 result = info_search_internal (search_string,
3762 active_window, direction, case_sensitive);
3764 if (result != 0 && !info_error_was_printed)
3765 info_error (_("Search failed."));
3766 else if (old_pagetop != active_window->pagetop)
3770 new_pagetop = active_window->pagetop;
3771 active_window->pagetop = old_pagetop;
3772 set_window_pagetop (active_window, new_pagetop);
3773 if (auto_footnotes_p)
3774 info_get_or_remove_footnotes (active_window);
3777 /* Perhaps free the unreferenced file buffers that were searched, but
3779 info_gc_file_buffers ();
3782 DECLARE_INFO_COMMAND (info_search_next,
3783 _("Repeat last search in the same direction"))
3785 if (!last_search_direction)
3786 info_error (_("No previous search string"));
3788 info_search_1 (window, last_search_direction * count,
3789 key, last_search_case_sensitive, 0);
3792 DECLARE_INFO_COMMAND (info_search_previous,
3793 _("Repeat last search in the reverse direction"))
3795 if (!last_search_direction)
3796 info_error (_("No previous search string"));
3798 info_search_1 (window, -last_search_direction * count,
3799 key, last_search_case_sensitive, 0);
3802 /* **************************************************************** */
3804 /* Incremental Searching */
3806 /* **************************************************************** */
3808 static void incremental_search ();
3810 DECLARE_INFO_COMMAND (isearch_forward,
3811 _("Search interactively for a string as you type it"))
3813 incremental_search (window, count, key);
3816 DECLARE_INFO_COMMAND (isearch_backward,
3817 _("Search interactively for a string as you type it"))
3819 incremental_search (window, -count, key);
3822 /* Incrementally search for a string as it is typed. */
3823 /* The last accepted incremental search string. */
3824 static char *last_isearch_accepted = (char *)NULL;
3826 /* The current incremental search string. */
3827 static char *isearch_string = (char *)NULL;
3828 static int isearch_string_index = 0;
3829 static int isearch_string_size = 0;
3830 static unsigned char isearch_terminate_search_key = ESC;
3832 /* Structure defining the current state of an incremental search. */
3834 WINDOW_STATE_DECL; /* The node, pagetop and point. */
3835 int search_index; /* Offset of the last char in the search string. */
3836 int direction; /* The direction that this search is heading in. */
3837 int failing; /* Whether or not this search failed. */
3840 /* Array of search states. */
3841 static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
3842 static int isearch_states_index = 0;
3843 static int isearch_states_slots = 0;
3845 /* Push the state of this search. */
3847 push_isearch (window, search_index, direction, failing)
3849 int search_index, direction, failing;
3851 SEARCH_STATE *state;
3853 state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
3854 window_get_state (window, state);
3855 state->search_index = search_index;
3856 state->direction = direction;
3857 state->failing = failing;
3859 add_pointer_to_array (state, isearch_states_index, isearch_states,
3860 isearch_states_slots, 20, SEARCH_STATE *);
3863 /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3865 pop_isearch (window, search_index, direction, failing)
3867 int *search_index, *direction, *failing;
3869 SEARCH_STATE *state;
3871 if (isearch_states_index)
3873 isearch_states_index--;
3874 state = isearch_states[isearch_states_index];
3875 window_set_state (window, state);
3876 *search_index = state->search_index;
3877 *direction = state->direction;
3878 *failing = state->failing;
3881 isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
3885 /* Free the memory used by isearch_states. */
3887 free_isearch_states ()
3891 for (i = 0; i < isearch_states_index; i++)
3893 free (isearch_states[i]);
3894 isearch_states[i] = (SEARCH_STATE *)NULL;
3896 isearch_states_index = 0;
3899 /* Display the current search in the echo area. */
3901 show_isearch_prompt (dir, string, failing_p)
3903 unsigned char *string;
3908 char *prompt, *p_rep;
3909 int prompt_len, p_rep_index, p_rep_size;
3912 prefix = _("I-search backward: ");
3914 prefix = _("I-search: ");
3916 p_rep_index = p_rep_size = 0;
3917 p_rep = (char *)NULL;
3918 for (i = 0; string[i]; i++)
3924 case ' ': rep = " "; break;
3925 case LFD: rep = "\\n"; break;
3926 case TAB: rep = "\\t"; break;
3928 rep = pretty_keyname (string[i]);
3930 if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3931 p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3933 strcpy (p_rep + p_rep_index, rep);
3934 p_rep_index += strlen (rep);
3937 prompt_len = strlen (prefix) + p_rep_index + 1;
3939 prompt_len += strlen (_("Failing "));
3940 prompt = xmalloc (prompt_len);
3941 sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
3942 p_rep ? p_rep : "");
3944 window_message_in_echo_area ("%s", prompt);
3947 display_cursor_at_point (active_window);
3951 incremental_search (window, count, ignore)
3954 unsigned char ignore;
3957 int last_search_result, search_result, dir;
3958 SEARCH_STATE mystate, orig_state;
3960 int case_sensitive = 0;
3967 last_search_result = search_result = 0;
3969 window_get_state (window, &orig_state);
3971 isearch_string_index = 0;
3972 if (!isearch_string_size)
3973 isearch_string = (char *)xmalloc (isearch_string_size = 50);
3975 /* Show the search string in the echo area. */
3976 isearch_string[isearch_string_index] = '\0';
3977 show_isearch_prompt (dir, isearch_string, search_result);
3979 isearch_is_active = 1;
3981 while (isearch_is_active)
3983 VFunction *func = (VFunction *)NULL;
3986 /* If a recent display was interrupted, then do the redisplay now if
3987 it is convenient. */
3988 if (!info_any_buffered_input_p () && display_was_interrupted_p)
3990 display_update_one_window (window);
3991 display_cursor_at_point (active_window);
3994 /* Read a character and dispatch on it. */
3995 key = info_get_input_char ();
3996 window_get_state (window, &mystate);
3998 if (key == DEL || key == Control ('h'))
4000 /* User wants to delete one level of search? */
4001 if (!isearch_states_index)
4003 terminal_ring_bell ();
4009 (window, &isearch_string_index, &dir, &search_result);
4010 isearch_string[isearch_string_index] = '\0';
4011 show_isearch_prompt (dir, isearch_string, search_result);
4015 else if (key == Control ('q'))
4017 key = info_get_input_char ();
4021 /* We are about to search again, or quit. Save the current search. */
4022 push_isearch (window, isearch_string_index, dir, search_result);
4025 goto insert_and_search;
4027 if (!Meta_p (key) || key > 32)
4029 /* If this key is not a keymap, get its associated function,
4030 if any. If it is a keymap, then it's probably ESC from an
4031 arrow key, and we handle that case below. */
4032 char type = window->keymap[key].type;
4033 func = type == ISFUNC
4034 ? InfoFunction(window->keymap[key].function)
4035 : NULL; /* function member is a Keymap if ISKMAP */
4037 if (isprint (key) || (type == ISFUNC && func == NULL))
4041 if (isearch_string_index + 2 >= isearch_string_size)
4042 isearch_string = (char *)xrealloc
4043 (isearch_string, isearch_string_size += 100);
4045 isearch_string[isearch_string_index++] = key;
4046 isearch_string[isearch_string_index] = '\0';
4049 else if (func == isearch_forward || func == isearch_backward)
4051 /* If this key invokes an incremental search, then this
4052 means that we will either search again in the same
4053 direction, search again in the reverse direction, or
4054 insert the last search string that was accepted through
4055 incremental searching. */
4056 if ((func == isearch_forward && dir > 0) ||
4057 (func == isearch_backward && dir < 0))
4059 /* If the user has typed no characters, then insert the
4060 last successful search into the current search string. */
4061 if (isearch_string_index == 0)
4063 /* Of course, there must be something to insert. */
4064 if (last_isearch_accepted)
4066 if (strlen (last_isearch_accepted) + 1 >=
4067 isearch_string_size)
4068 isearch_string = (char *)
4069 xrealloc (isearch_string,
4070 isearch_string_size += 10 +
4071 strlen (last_isearch_accepted));
4072 strcpy (isearch_string, last_isearch_accepted);
4073 isearch_string_index = strlen (isearch_string);
4081 /* Search again in the same direction. This means start
4082 from a new place if the last search was successful. */
4083 if (search_result == 0)
4084 window->point += dir;
4089 /* Reverse the direction of the search. */
4093 else if (func == info_abort_key)
4095 /* If C-g pressed, and the search is failing, pop the search
4096 stack back to the last unfailed search. */
4097 if (isearch_states_index && (search_result != 0))
4099 terminal_ring_bell ();
4100 while (isearch_states_index && (search_result != 0))
4102 (window, &isearch_string_index, &dir, &search_result);
4103 isearch_string[isearch_string_index] = '\0';
4104 show_isearch_prompt (dir, isearch_string, search_result);
4116 /* The character is not printable, or it has a function which is
4117 non-null. Exit the search, remembering the search string. If
4118 the key is not the same as the isearch_terminate_search_key,
4119 then push it into pending input. */
4120 if (isearch_string_index && func != info_abort_key)
4122 maybe_free (last_isearch_accepted);
4123 last_isearch_accepted = xstrdup (isearch_string);
4126 /* If the key is the isearch_terminate_search_key, but some buffered
4127 input is pending, it is almost invariably because the ESC key is
4128 actually the beginning of an escape sequence, like in case they
4129 pressed an arrow key. So don't gobble the ESC key, push it back
4130 into pending input. */
4131 /* FIXME: this seems like a kludge! We need a more reliable
4132 mechanism to know when ESC is a separate key and when it is
4133 part of an escape sequence. */
4134 if (key != RET /* Emacs addicts want RET to get lost */
4135 && (key != isearch_terminate_search_key
4136 || info_any_buffered_input_p ()))
4137 info_set_pending_input (key);
4139 if (func == info_abort_key)
4141 if (isearch_states_index)
4142 window_set_state (window, &orig_state);
4145 if (!echo_area_is_active)
4146 window_clear_echo_area ();
4148 if (auto_footnotes_p)
4149 info_get_or_remove_footnotes (active_window);
4151 isearch_is_active = 0;
4155 /* Search for the contents of isearch_string. */
4157 show_isearch_prompt (dir, isearch_string, search_result);
4159 /* If the search string includes upper-case letters, make the
4160 search case-sensitive. */
4161 for (p = isearch_string; *p; p++)
4169 if (search_result == 0)
4171 /* Check to see if the current search string is right here. If
4172 we are looking at it, then don't bother calling the search
4175 ((case_sensitive ? strncmp : strncasecmp)
4176 (window->node->contents + window->point,
4177 isearch_string, isearch_string_index) == 0)) ||
4179 ((window->point - isearch_string_index) >= 0) &&
4180 ((case_sensitive ? strncmp : strncasecmp)
4181 (window->node->contents +
4182 (window->point - (isearch_string_index - 1)),
4183 isearch_string, isearch_string_index) == 0)))
4189 search_result = info_search_internal (isearch_string,
4190 window, dir, case_sensitive);
4193 /* If this search failed, and we didn't already have a failed search,
4194 then ring the terminal bell. */
4195 if (search_result != 0 && last_search_result == 0)
4196 terminal_ring_bell ();
4199 show_isearch_prompt (dir, isearch_string, search_result);
4201 if (search_result == 0)
4203 if ((mystate.node == window->node) &&
4204 (mystate.pagetop != window->pagetop))
4206 int newtop = window->pagetop;
4207 window->pagetop = mystate.pagetop;
4208 set_window_pagetop (window, newtop);
4210 display_update_one_window (window);
4211 display_cursor_at_point (window);
4214 last_search_result = search_result;
4217 /* Free the memory used to remember each search state. */
4218 free_isearch_states ();
4220 /* Perhaps GC some file buffers. */
4221 info_gc_file_buffers ();
4223 /* After searching, leave the window in the correct state. */
4224 if (!echo_area_is_active)
4225 window_clear_echo_area ();
4228 /* GC some file buffers. A file buffer can be gc-ed if there we have
4229 no nodes in INFO_WINDOWS that reference this file buffer's contents.
4230 Garbage collecting a file buffer means to free the file buffers
4233 info_gc_file_buffers ()
4235 register int fb_index, iw_index, i;
4236 register FILE_BUFFER *fb;
4237 register INFO_WINDOW *iw;
4239 if (!info_loaded_files)
4242 for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4244 int fb_referenced_p = 0;
4246 /* If already gc-ed, do nothing. */
4250 /* If this file had to be uncompressed, check to see if we should
4251 gc it. This means that the user-variable "gc-compressed-files"
4253 if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
4256 /* If this file's contents are not gc-able, move on. */
4257 if (fb->flags & N_CannotGC)
4260 /* Check each INFO_WINDOW to see if it has any nodes which reference
4262 for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
4264 for (i = 0; iw->nodes && iw->nodes[i]; i++)
4266 if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4267 (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
4269 fb_referenced_p = 1;
4275 /* If this file buffer wasn't referenced, free its contents. */
4276 if (!fb_referenced_p)
4278 free (fb->contents);
4279 fb->contents = (char *)NULL;
4284 /* **************************************************************** */
4286 /* Traversing and Selecting References */
4288 /* **************************************************************** */
4290 /* Move to the next or previous cross reference in this node. */
4292 info_move_to_xref (window, count, key, dir)
4298 long firstmenu, firstxref;
4299 long nextmenu, nextxref;
4300 long placement = -1;
4302 NODE *node = window->node;
4305 start = node->nodelen;
4307 /* This search is only allowed to fail if there is no menu or cross
4308 reference in the current node. Otherwise, the first menu or xref
4309 found is moved to. */
4311 firstmenu = info_search_in_node
4312 (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4314 /* FIRSTMENU may point directly to the line defining the menu. Skip that
4315 and go directly to the first item. */
4317 if (firstmenu != -1)
4319 char *text = node->contents + firstmenu;
4321 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4322 firstmenu = info_search_in_node
4323 (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0);
4327 info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4329 #if defined (HANDLE_MAN_PAGES)
4330 if ((firstxref == -1) && (node->flags & N_IsManPage))
4332 firstxref = locate_manpage_xref (node, start, dir);
4334 #endif /* HANDLE_MAN_PAGES */
4336 if (firstmenu == -1 && firstxref == -1)
4338 info_error (msg_no_xref_node);
4342 /* There is at least one cross reference or menu entry in this node.
4343 Try hard to find the next available one. */
4345 nextmenu = info_search_in_node
4346 (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4348 nextxref = info_search_in_node
4349 (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4351 #if defined (HANDLE_MAN_PAGES)
4352 if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
4353 nextxref = locate_manpage_xref (node, window->point + dir, dir);
4354 #endif /* HANDLE_MAN_PAGES */
4356 /* Ignore "Menu:" as a menu item. */
4359 char *text = node->contents + nextmenu;
4361 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4362 nextmenu = info_search_in_node
4363 (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
4366 /* If there is both a next menu entry, and a next xref entry, choose the
4367 one which occurs first. Otherwise, select the one which actually
4368 appears in this node following point. */
4369 if (nextmenu != -1 && nextxref != -1)
4371 if (((dir == 1) && (nextmenu < nextxref)) ||
4372 ((dir == -1) && (nextmenu > nextxref)))
4373 placement = nextmenu + 1;
4375 placement = nextxref;
4377 else if (nextmenu != -1)
4378 placement = nextmenu + 1;
4379 else if (nextxref != -1)
4380 placement = nextxref;
4382 /* If there was neither a menu or xref entry appearing in this node after
4383 point, choose the first menu or xref entry appearing in this node. */
4384 if (placement == -1)
4386 if (firstmenu != -1 && firstxref != -1)
4388 if (((dir == 1) && (firstmenu < firstxref)) ||
4389 ((dir == -1) && (firstmenu > firstxref)))
4390 placement = firstmenu + 1;
4392 placement = firstxref;
4394 else if (firstmenu != -1)
4395 placement = firstmenu + 1;
4397 placement = firstxref;
4399 window->point = placement;
4400 window_adjust_pagetop (window);
4401 window->flags |= W_UpdateWindow;
4404 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
4405 _("Move to the previous cross reference"))
4408 info_move_to_prev_xref (window, -count, key);
4410 info_move_to_xref (window, count, key, -1);
4413 DECLARE_INFO_COMMAND (info_move_to_next_xref,
4414 _("Move to the next cross reference"))
4417 info_move_to_next_xref (window, -count, key);
4419 info_move_to_xref (window, count, key, 1);
4422 /* Select the menu item or reference that appears on this line. */
4423 DECLARE_INFO_COMMAND (info_select_reference_this_line,
4424 _("Select reference or menu item appearing on this line"))
4429 line = window->line_starts[window_line_of_point (window)];
4430 orig = window->node;
4432 /* If this line contains a menu item, select that one. */
4433 if (strncmp ("* ", line, 2) == 0)
4434 info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
4436 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
4439 /* **************************************************************** */
4441 /* Miscellaneous Info Commands */
4443 /* **************************************************************** */
4445 /* What to do when C-g is pressed in a window. */
4446 DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
4448 /* If error printing doesn't oridinarily ring the bell, do it now,
4449 since C-g always rings the bell. Otherwise, let the error printer
4451 if (!info_error_rings_bell_p)
4452 terminal_ring_bell ();
4453 info_error (_("Quit"));
4455 info_initialize_numeric_arg ();
4456 info_clear_pending_input ();
4457 info_last_executed_command = (VFunction *)NULL;
4460 /* Move the cursor to the desired line of the window. */
4461 DECLARE_INFO_COMMAND (info_move_to_window_line,
4462 _("Move the cursor to a specific line of the window"))
4466 /* With no numeric argument of any kind, default to the center line. */
4467 if (!info_explicit_arg && count == 1)
4468 line = (window->height / 2) + window->pagetop;
4472 line = (window->height + count) + window->pagetop;
4474 line = window->pagetop + count;
4477 /* If the line doesn't appear in this window, make it do so. */
4478 if ((line - window->pagetop) >= window->height)
4479 line = window->pagetop + (window->height - 1);
4481 /* If the line is too small, make it fit. */
4482 if (line < window->pagetop)
4483 line = window->pagetop;
4485 /* If the selected line is past the bottom of the node, force it back. */
4486 if (line >= window->line_count)
4487 line = window->line_count - 1;
4489 window->point = (window->line_starts[line] - window->node->contents);
4492 /* Clear the screen and redraw its contents. Given a numeric argument,
4493 move the line the cursor is on to the COUNT'th line of the window. */
4494 DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
4496 if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4498 terminal_clear_screen ();
4499 display_clear_display (the_display);
4500 window_mark_chain (windows, W_UpdateWindow);
4501 display_update_display (windows);
4505 int desired_line, point_line;
4508 point_line = window_line_of_point (window) - window->pagetop;
4511 desired_line = window->height + count;
4513 desired_line = count;
4515 if (desired_line < 0)
4518 if (desired_line >= window->height)
4519 desired_line = window->height - 1;
4521 if (desired_line == point_line)
4524 new_pagetop = window->pagetop + (point_line - desired_line);
4526 set_window_pagetop (window, new_pagetop);
4529 /* This command does nothing. It is the fact that a key is bound to it
4530 that has meaning. See the code at the top of info_session (). */
4531 DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
4535 /* **************************************************************** */
4537 /* Reading Keys and Dispatching on Them */
4539 /* **************************************************************** */
4541 /* Declaration only. Special cased in info_dispatch_on_key ().
4542 Doc string is to avoid ugly results with describe_key etc. */
4543 DECLARE_INFO_COMMAND (info_do_lowercase_version,
4544 _("Run command bound to this key's lowercase variant"))
4548 dispatch_error (keyseq)
4553 rep = pretty_keyseq (keyseq);
4555 if (!echo_area_is_active)
4556 info_error (_("Unknown command (%s)."), rep);
4559 char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid")));
4560 sprintf (temp, _("\"%s\" is invalid"), rep);
4561 terminal_ring_bell ();
4562 inform_in_echo_area (temp);
4567 /* Keeping track of key sequences. */
4568 static char *info_keyseq = (char *)NULL;
4569 static int info_keyseq_index = 0;
4570 static int info_keyseq_size = 0;
4571 static int info_keyseq_displayed_p = 0;
4573 /* Initialize the length of the current key sequence. */
4575 initialize_keyseq ()
4577 info_keyseq_index = 0;
4578 info_keyseq_displayed_p = 0;
4581 /* Add CHARACTER to the current key sequence. */
4583 add_char_to_keyseq (character)
4586 if (info_keyseq_index + 2 >= info_keyseq_size)
4587 info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
4589 info_keyseq[info_keyseq_index++] = character;
4590 info_keyseq[info_keyseq_index] = '\0';
4593 /* Display the current value of info_keyseq. If argument EXPECTING is
4594 non-zero, input is expected to be read after the key sequence is
4595 displayed, so add an additional prompting character to the sequence. */
4597 display_info_keyseq (expecting_future_input)
4598 int expecting_future_input;
4602 rep = pretty_keyseq (info_keyseq);
4603 if (expecting_future_input)
4606 if (echo_area_is_active)
4607 inform_in_echo_area (rep);
4610 window_message_in_echo_area (rep);
4611 display_cursor_at_point (active_window);
4613 info_keyseq_displayed_p = 1;
4616 /* Called by interactive commands to read a keystroke. */
4618 info_get_another_input_char ()
4620 int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
4622 /* If there isn't any input currently available, then wait a
4623 moment looking for input. If we don't get it fast enough,
4624 prompt a little bit with the current key sequence. */
4625 if (!info_keyseq_displayed_p)
4628 if (!info_any_buffered_input_p () &&
4629 !info_input_pending_p ())
4631 #if defined (FD_SET)
4632 struct timeval timer;
4636 FD_SET (fileno (info_input_stream), &readfds);
4638 timer.tv_usec = 750;
4639 ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
4647 display_info_keyseq (1);
4649 return (info_get_input_char ());
4652 /* Do the command associated with KEY in MAP. If the associated command is
4653 really a keymap, then read another key, and dispatch into that map. */
4655 info_dispatch_on_key (key, map)
4659 #if !defined(INFOKEY)
4660 if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
4662 if (map[ESC].type == ISKMAP)
4664 map = (Keymap)map[ESC].function;
4665 add_char_to_keyseq (ESC);
4667 info_dispatch_on_key (key, map);
4671 dispatch_error (info_keyseq);
4675 #endif /* INFOKEY */
4677 switch (map[key].type)
4683 func = InfoFunction(map[key].function);
4684 if (func != (VFunction *)NULL)
4686 /* Special case info_do_lowercase_version (). */
4687 if (func == info_do_lowercase_version)
4689 #if defined(INFOKEY)
4690 unsigned char lowerkey;
4692 lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
4693 if (lowerkey == key)
4695 add_char_to_keyseq (key);
4696 dispatch_error (info_keyseq);
4699 info_dispatch_on_key (lowerkey, map);
4700 #else /* !INFOKEY */
4701 info_dispatch_on_key (tolower (key), map);
4702 #endif /* INFOKEY */
4706 add_char_to_keyseq (key);
4708 if (info_keyseq_displayed_p)
4709 display_info_keyseq (0);
4714 where = active_window;
4715 (*InfoFunction(map[key].function))
4716 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
4718 /* If we have input pending, then the last command was a prefix
4719 command. Don't change the value of the last function vars.
4720 Otherwise, remember the last command executed in the var
4721 appropriate to the window in which it was executed. */
4722 if (!info_input_pending_p ())
4724 if (where == the_echo_area)
4725 ea_last_executed_command = InfoFunction(map[key].function);
4727 info_last_executed_command = InfoFunction(map[key].function);
4733 add_char_to_keyseq (key);
4734 dispatch_error (info_keyseq);
4741 add_char_to_keyseq (key);
4742 if (map[key].function != (InfoCommand *)NULL)
4744 unsigned char newkey;
4746 newkey = info_get_another_input_char ();
4747 info_dispatch_on_key (newkey, (Keymap)map[key].function);
4751 dispatch_error (info_keyseq);
4758 /* **************************************************************** */
4760 /* Numeric Arguments */
4762 /* **************************************************************** */
4764 /* Handle C-u style numeric args, as well as M--, and M-digits. */
4766 /* Non-zero means that an explicit argument has been passed to this
4767 command, as in C-u C-v. */
4768 int info_explicit_arg = 0;
4770 /* The sign of the numeric argument. */
4771 int info_numeric_arg_sign = 1;
4773 /* The value of the argument itself. */
4774 int info_numeric_arg = 1;
4776 /* Add the current digit to the argument in progress. */
4777 DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
4778 _("Add this digit to the current numeric argument"))
4780 info_numeric_arg_digit_loop (window, 0, key);
4783 /* C-u, universal argument. Multiply the current argument by 4.
4784 Read a key. If the key has nothing to do with arguments, then
4785 dispatch on it. If the key is the abort character then abort. */
4786 DECLARE_INFO_COMMAND (info_universal_argument,
4787 _("Start (or multiply by 4) the current numeric argument"))
4789 info_numeric_arg *= 4;
4790 info_numeric_arg_digit_loop (window, 0, 0);
4793 /* Create a default argument. */
4795 info_initialize_numeric_arg ()
4797 info_numeric_arg = info_numeric_arg_sign = 1;
4798 info_explicit_arg = 0;
4801 DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
4802 _("Internally used by \\[universal-argument]"))
4804 unsigned char pure_key;
4805 Keymap keymap = window->keymap;
4813 if (display_was_interrupted_p && !info_any_buffered_input_p ())
4814 display_update_display (windows);
4816 if (active_window != the_echo_area)
4817 display_cursor_at_point (active_window);
4819 pure_key = key = info_get_another_input_char ();
4821 #if !defined(INFOKEY)
4823 add_char_to_keyseq (ESC);
4825 add_char_to_keyseq (UnMeta (key));
4826 #else /* defined(INFOKEY) */
4827 add_char_to_keyseq (key);
4828 #endif /* defined(INFOKEY) */
4831 #if !defined(INFOKEY)
4834 #endif /* !defined(INFOKEY) */
4836 if (keymap[key].type == ISFUNC &&
4837 InfoFunction(keymap[key].function) == info_universal_argument)
4839 info_numeric_arg *= 4;
4844 #if defined(INFOKEY)
4847 #endif /* !defined(INFOKEY) */
4852 if (info_explicit_arg)
4853 info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
4855 info_numeric_arg = (key - '0');
4856 info_explicit_arg = 1;
4860 if (key == '-' && !info_explicit_arg)
4862 info_numeric_arg_sign = -1;
4863 info_numeric_arg = 1;
4867 info_keyseq_index--;
4868 info_dispatch_on_key (pure_key, keymap);
4876 /* **************************************************************** */
4878 /* Input Character Buffering */
4880 /* **************************************************************** */
4882 /* Character waiting to be read next. */
4883 static int pending_input_character = 0;
4885 /* How to make there be no pending input. */
4887 info_clear_pending_input ()
4889 pending_input_character = 0;
4892 /* How to set the pending input character. */
4894 info_set_pending_input (key)
4897 pending_input_character = key;
4900 /* How to see if there is any pending input. */
4902 info_input_pending_p ()
4904 return (pending_input_character);
4907 /* Largest number of characters that we can read in advance. */
4908 #define MAX_INFO_INPUT_BUFFERING 512
4910 static int pop_index = 0, push_index = 0;
4911 static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
4913 /* Add KEY to the buffer of characters to be read. */
4915 info_push_typeahead (key)
4918 /* Flush all pending input in the case of C-g pressed. */
4919 if (key == Control ('g'))
4921 push_index = pop_index;
4922 info_set_pending_input (Control ('g'));
4926 info_input_buffer[push_index++] = key;
4927 if (push_index >= sizeof (info_input_buffer))
4932 /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4934 info_input_buffer_space_available ()
4936 if (pop_index > push_index)
4937 return (pop_index - push_index);
4939 return (sizeof (info_input_buffer) - (push_index - pop_index));
4942 /* Get a key from the buffer of characters to be read.
4943 Return the key in KEY.
4944 Result is non-zero if there was a key, or 0 if there wasn't. */
4946 info_get_key_from_typeahead (key)
4949 if (push_index == pop_index)
4952 *key = info_input_buffer[pop_index++];
4954 if (pop_index >= sizeof (info_input_buffer))
4961 info_any_buffered_input_p ()
4963 info_gather_typeahead ();
4964 return (push_index != pop_index);
4967 /* If characters are available to be read, then read them and stuff them into
4968 info_input_buffer. Otherwise, do nothing. */
4970 info_gather_typeahead ()
4973 int tty, space_avail;
4975 unsigned char input[MAX_INFO_INPUT_BUFFERING];
4977 tty = fileno (info_input_stream);
4980 space_avail = info_input_buffer_space_available ();
4982 /* If we can just find out how many characters there are to read, do so. */
4983 #if defined (FIONREAD)
4985 ioctl (tty, FIONREAD, &chars_avail);
4987 if (chars_avail > space_avail)
4988 chars_avail = space_avail;
4991 chars_avail = read (tty, &input[0], chars_avail);
4993 #else /* !FIONREAD */
4994 # if defined (O_NDELAY)
4998 flags = fcntl (tty, F_GETFL, 0);
5000 fcntl (tty, F_SETFL, (flags | O_NDELAY));
5001 chars_avail = read (tty, &input[0], space_avail);
5002 fcntl (tty, F_SETFL, flags);
5004 if (chars_avail == -1)
5007 # else /* !O_NDELAY */
5010 extern long pc_term_chars_avail (void);
5013 chars_avail = pc_term_chars_avail ();
5016 /* We could be more accurate by calling ltell, but we have no idea
5017 whether tty is buffered by stdio functions, and if so, how many
5018 characters are already waiting in the buffer. So we punt. */
5021 if (fstat (tty, &st) < 0)
5024 chars_avail = st.st_size;
5026 if (chars_avail > space_avail)
5027 chars_avail = space_avail;
5029 chars_avail = read (tty, &input[0], chars_avail);
5031 # endif/* __DJGPP__ */
5032 # endif /* O_NDELAY */
5033 #endif /* !FIONREAD */
5035 while (i < chars_avail)
5037 info_push_typeahead (input[i]);
5042 /* How to read a single character. */
5044 info_get_input_char ()
5046 unsigned char keystroke;
5048 info_gather_typeahead ();
5050 if (pending_input_character)
5052 keystroke = pending_input_character;
5053 pending_input_character = 0;
5055 else if (info_get_key_from_typeahead (&keystroke) == 0)
5059 int tty = fileno (info_input_stream);
5061 /* Using stream I/O causes FIONREAD etc to fail to work
5062 so unless someone can find a portable way of finding
5063 out how many characters are currently buffered, we
5064 should stay with away from stream I/O.
5065 --Egil Kvaleberg <egilk@sn.no>, January 1997. */
5067 /* Keep reading if we got EINTR, so that we don't just exit.
5068 --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
5073 n = read (tty, &c, 1);
5074 while (n == -1 && errno == EINTR);
5075 rawkey = n == 1 ? c : EOF;
5078 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5085 if (info_input_stream != stdin)
5087 fclose (info_input_stream);
5088 info_input_stream = stdin;
5089 tty = fileno (info_input_stream);
5090 display_inhibited = 0;
5091 display_update_display (windows);
5092 display_cursor_at_point (active_window);
5093 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5099 terminal_unprep_terminal ();
5100 close_dribble_file ();
5106 if (info_dribble_file)
5107 dribble (keystroke);