* decl.c (pop_cp_function_context): Don't call free on a NULL
[official-gcc.git] / texinfo / info / session.c
blob04a548bfb51533c5d04e92dd83bb46ec935627fe
1 /* session.c -- The user windowing interface to Info.
2 $Id: session.c,v 1.1.1.3 1998/03/24 18:20:15 law Exp $
4 Copyright (C) 1993, 96, 97 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 Written by Brian Fox (bfox@ai.mit.edu). */
22 #include "info.h"
23 #include <sys/ioctl.h>
25 #if defined (HAVE_SYS_TIME_H)
26 # include <sys/time.h>
27 # define HAVE_STRUCT_TIMEVAL
28 #endif /* HAVE_SYS_TIME_H */
30 #if defined (HANDLE_MAN_PAGES)
31 # include "man.h"
32 #endif
34 static void info_clear_pending_input (), info_set_pending_input ();
35 static void info_handle_pointer ();
37 /* **************************************************************** */
38 /* */
39 /* Running an Info Session */
40 /* */
41 /* **************************************************************** */
43 /* The place that we are reading input from. */
44 static FILE *info_input_stream = NULL;
46 /* The last executed command. */
47 VFunction *info_last_executed_command = NULL;
49 /* Becomes non-zero when 'q' is typed to an Info window. */
50 int quit_info_immediately = 0;
52 /* Array of structures describing for each window which nodes have been
53 visited in that window. */
54 INFO_WINDOW **info_windows = NULL;
56 /* Where to add the next window, if we need to add one. */
57 static int info_windows_index = 0;
59 /* Number of slots allocated to `info_windows'. */
60 static int info_windows_slots = 0;
62 void remember_window_and_node (), forget_window_and_nodes ();
63 void initialize_info_session (), info_session ();
64 void display_startup_message_and_start ();
66 /* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
67 For each loaded node, create a new window. Always split the largest of the
68 available windows. */
69 void
70 begin_multiple_window_info_session (filename, nodenames)
71 char *filename;
72 char **nodenames;
74 register int i;
75 WINDOW *window = (WINDOW *)NULL;
77 for (i = 0; nodenames[i]; i++)
79 NODE *node;
81 node = info_get_node (filename, nodenames[i]);
83 if (!node)
84 break;
86 /* If this is the first node, initialize the info session. */
87 if (!window)
89 initialize_info_session (node, 1);
90 window = active_window;
92 else
94 /* Find the largest window in WINDOWS, and make that be the active
95 one. Then split it and add our window and node to the list
96 of remembered windows and nodes. Then tile the windows. */
97 register WINDOW *win, *largest = (WINDOW *)NULL;
98 int max_height = 0;
100 for (win = windows; win; win = win->next)
101 if (win->height > max_height)
103 max_height = win->height;
104 largest = win;
107 if (!largest)
109 display_update_display (windows);
110 info_error (CANT_FIND_WIND);
111 info_session ();
112 exit (0);
115 active_window = largest;
116 window = window_make_window (node);
117 if (window)
119 window_tile_windows (TILE_INTERNALS);
120 remember_window_and_node (window, node);
122 else
124 display_update_display (windows);
125 info_error (WIN_TOO_SMALL);
126 info_session ();
127 exit (0);
131 display_startup_message_and_start ();
134 /* Start an info session with INITIAL_NODE, and an error message in the echo
135 area made from FORMAT and ARG. */
136 void
137 begin_info_session_with_error (initial_node, format, arg)
138 NODE *initial_node;
139 char *format;
140 void *arg;
142 initialize_info_session (initial_node, 1);
143 info_error (format, arg, (void *)NULL);
144 info_session ();
147 /* Start an info session with INITIAL_NODE. */
148 void
149 begin_info_session (initial_node)
150 NODE *initial_node;
152 initialize_info_session (initial_node, 1);
153 display_startup_message_and_start ();
156 void
157 display_startup_message_and_start ()
159 char *format;
161 format = replace_in_documentation
162 (_("Welcome to Info version %s. \"\\[get-help-window]\" for help, \"\\[menu-item]\" for menu item."));
164 window_message_in_echo_area (format, version_string ());
165 info_session ();
168 /* Run an info session with an already initialized window and node. */
169 void
170 info_session ()
172 display_update_display (windows);
173 info_last_executed_command = NULL;
174 info_read_and_dispatch ();
175 /* On program exit, leave the cursor at the bottom of the window, and
176 restore the terminal I/O. */
177 terminal_goto_xy (0, screenheight - 1);
178 terminal_clear_to_eol ();
179 fflush (stdout);
180 terminal_unprep_terminal ();
181 close_dribble_file ();
184 /* Here is a window-location dependent event loop. Called from the
185 functions info_session (), and from read_xxx_in_echo_area (). */
186 void
187 info_read_and_dispatch ()
189 unsigned char key;
190 int done;
191 done = 0;
193 while (!done && !quit_info_immediately)
195 int lk;
197 /* If we haven't just gone up or down a line, there is no
198 goal column for this window. */
199 if ((info_last_executed_command != info_next_line) &&
200 (info_last_executed_command != info_prev_line))
201 active_window->goal_column = -1;
203 if (echo_area_is_active)
205 lk = echo_area_last_command_was_kill;
206 echo_area_prep_read ();
209 if (!info_any_buffered_input_p ())
210 display_update_display (windows);
212 display_cursor_at_point (active_window);
213 info_initialize_numeric_arg ();
215 initialize_keyseq ();
216 key = info_get_input_char ();
218 /* No errors yet. We just read a character, that's all. Only clear
219 the echo_area if it is not currently active. */
220 if (!echo_area_is_active)
221 window_clear_echo_area ();
223 info_error_was_printed = 0;
225 /* Do the selected command. */
226 info_dispatch_on_key (key, active_window->keymap);
228 if (echo_area_is_active)
230 /* Echo area commands that do killing increment the value of
231 ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no
232 change in the value of this variable, the last command
233 executed was not a kill command. */
234 if (lk == echo_area_last_command_was_kill)
235 echo_area_last_command_was_kill = 0;
237 if (ea_last_executed_command == ea_newline ||
238 info_aborted_echo_area)
240 ea_last_executed_command = (VFunction *)NULL;
241 done = 1;
244 if (info_last_executed_command == info_quit)
245 quit_info_immediately = 1;
247 else if (info_last_executed_command == info_quit)
248 done = 1;
252 /* Found in signals.c */
253 extern void initialize_info_signal_handler ();
255 /* Initialize the first info session by starting the terminal, window,
256 and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
257 void
258 initialize_info_session (node, clear_screen)
259 NODE *node;
260 int clear_screen;
262 char *term_name = getenv ("TERM");
263 terminal_initialize_terminal (term_name);
265 if (terminal_is_dumb_p)
267 if (!term_name)
268 term_name = "dumb";
270 info_error (TERM_TOO_DUMB, term_name);
271 exit (1);
274 if (clear_screen)
276 terminal_prep_terminal ();
277 terminal_clear_screen ();
280 initialize_info_keymaps ();
281 window_initialize_windows (screenwidth, screenheight);
282 initialize_info_signal_handler ();
283 display_initialize_display (screenwidth, screenheight);
284 info_set_node_of_window (active_window, node);
286 /* Tell the window system how to notify us when a window needs to be
287 asynchronously deleted (e.g., user resizes window very small). */
288 window_deletion_notifier = forget_window_and_nodes;
290 /* If input has not been redirected yet, make it come from unbuffered
291 standard input. */
292 if (!info_input_stream)
294 setbuf(stdin, NULL);
295 info_input_stream = stdin;
298 info_windows_initialized_p = 1;
301 /* Tell Info that input is coming from the file FILENAME. */
302 void
303 info_set_input_from_file (filename)
304 char *filename;
306 FILE *stream;
308 stream = fopen (filename, "r");
310 if (!stream)
311 return;
313 if ((info_input_stream != (FILE *)NULL) &&
314 (info_input_stream != stdin))
315 fclose (info_input_stream);
317 info_input_stream = stream;
319 if (stream != stdin)
320 display_inhibited = 1;
323 /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
324 static INFO_WINDOW *
325 get_info_window_of_window (window)
326 WINDOW *window;
328 register int i;
329 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
331 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
332 if (info_win->window == window)
333 break;
335 return (info_win);
338 /* Reset the remembered pagetop and point of WINDOW to WINDOW's current
339 values if the window and node are the same as the current one being
340 displayed. */
341 void
342 set_remembered_pagetop_and_point (window)
343 WINDOW *window;
345 INFO_WINDOW *info_win;
347 info_win = get_info_window_of_window (window);
349 if (!info_win)
350 return;
352 if (info_win->nodes_index &&
353 (info_win->nodes[info_win->current] == window->node))
355 info_win->pagetops[info_win->current] = window->pagetop;
356 info_win->points[info_win->current] = window->point;
360 void
361 remember_window_and_node (window, node)
362 WINDOW *window;
363 NODE *node;
365 /* See if we already have this window in our list. */
366 INFO_WINDOW *info_win = get_info_window_of_window (window);
368 /* If the window wasn't already on our list, then make a new entry. */
369 if (!info_win)
371 info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
372 info_win->window = window;
373 info_win->nodes = (NODE **)NULL;
374 info_win->pagetops = (int *)NULL;
375 info_win->points = (long *)NULL;
376 info_win->current = 0;
377 info_win->nodes_index = 0;
378 info_win->nodes_slots = 0;
380 add_pointer_to_array (info_win, info_windows_index, info_windows,
381 info_windows_slots, 10, INFO_WINDOW *);
384 /* If this node, the current pagetop, and the current point are the
385 same as the current saved node and pagetop, don't really add this to
386 the list of history nodes. This may happen only at the very
387 beginning of the program, I'm not sure. --karl */
388 if (info_win->nodes
389 && info_win->current >= 0
390 && info_win->nodes[info_win->current]->contents == node->contents
391 && info_win->pagetops[info_win->current] == window->pagetop
392 && info_win->points[info_win->current] == window->point)
393 return;
395 /* Remember this node, the currently displayed pagetop, and the current
396 location of point in this window. Because we are updating pagetops
397 and points as well as nodes, it is more efficient to avoid the
398 add_pointer_to_array macro here. */
399 if (info_win->nodes_index + 2 >= info_win->nodes_slots)
401 info_win->nodes_slots += 20;
402 info_win->nodes = (NODE **) xrealloc (info_win->nodes,
403 info_win->nodes_slots * sizeof (NODE *));
404 info_win->pagetops = (int *) xrealloc (info_win->pagetops,
405 info_win->nodes_slots * sizeof (int));
406 info_win->points = (long *) xrealloc (info_win->points,
407 info_win->nodes_slots * sizeof (long));
410 info_win->nodes[info_win->nodes_index] = node;
411 info_win->pagetops[info_win->nodes_index] = window->pagetop;
412 info_win->points[info_win->nodes_index] = window->point;
413 info_win->current = info_win->nodes_index++;
414 info_win->nodes[info_win->nodes_index] = NULL;
415 info_win->pagetops[info_win->nodes_index] = 0;
416 info_win->points[info_win->nodes_index] = 0;
419 #define DEBUG_FORGET_WINDOW_AND_NODES
420 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
421 static void
422 consistency_check_info_windows ()
424 register int i;
426 for (i = 0; i < info_windows_index; i++)
428 WINDOW *win;
430 for (win = windows; win; win = win->next)
431 if (win == info_windows[i]->window)
432 break;
434 if (!win)
435 abort ();
438 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
440 /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
441 void
442 forget_window_and_nodes (window)
443 WINDOW *window;
445 register int i;
446 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
448 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
449 if (info_win->window == window)
450 break;
452 /* If we found the window to forget, then do so. */
453 if (info_win)
455 while (i < info_windows_index)
457 info_windows[i] = info_windows[i + 1];
458 i++;
461 info_windows_index--;
462 info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
464 if (info_win->nodes)
466 /* Free the node structures which held onto internal node contents
467 here. This doesn't free the contents; we have a garbage collector
468 which does that. */
469 for (i = 0; info_win->nodes[i]; i++)
470 if (internal_info_node_p (info_win->nodes[i]))
471 free (info_win->nodes[i]);
472 free (info_win->nodes);
474 maybe_free (info_win->pagetops);
475 maybe_free (info_win->points);
478 free (info_win);
480 #if defined (DEBUG_FORGET_WINDOW_AND_NODES)
481 consistency_check_info_windows ();
482 #endif /* DEBUG_FORGET_WINDOW_AND_NODES */
485 /* Set WINDOW to show NODE. Remember the new window in our list of Info
486 windows. If we are doing automatic footnote display, also try to display
487 the footnotes for this window. */
488 void
489 info_set_node_of_window (window, node)
490 WINDOW *window;
491 NODE *node;
493 /* Put this node into the window. */
494 window_set_node_of_window (window, node);
496 /* Remember this node and window in our list of info windows. */
497 remember_window_and_node (window, node);
499 /* If doing auto-footnote display/undisplay, show the footnotes belonging
500 to this window's node. */
501 if (auto_footnotes_p)
502 info_get_or_remove_footnotes (window);
506 /* **************************************************************** */
507 /* */
508 /* Info Movement Commands */
509 /* */
510 /* **************************************************************** */
512 /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
513 to do so. */
514 void
515 set_window_pagetop (window, desired_top)
516 WINDOW *window;
517 int desired_top;
519 int point_line, old_pagetop;
521 if (desired_top < 0)
522 desired_top = 0;
523 else if (desired_top > window->line_count)
524 desired_top = window->line_count - 1;
526 if (window->pagetop == desired_top)
527 return;
529 old_pagetop = window->pagetop;
530 window->pagetop = desired_top;
532 /* Make sure that point appears in this window. */
533 point_line = window_line_of_point (window);
534 if ((point_line < window->pagetop) ||
535 ((point_line - window->pagetop) > window->height - 1))
536 window->point =
537 window->line_starts[window->pagetop] - window->node->contents;
539 window->flags |= W_UpdateWindow;
541 /* Find out which direction to scroll, and scroll the window in that
542 direction. Do this only if there would be a savings in redisplay
543 time. This is true if the amount to scroll is less than the height
544 of the window, and if the number of lines scrolled would be greater
545 than 10 % of the window's height. */
546 if (old_pagetop < desired_top)
548 int start, end, amount;
550 amount = desired_top - old_pagetop;
552 if ((amount >= window->height) ||
553 (((window->height - amount) * 10) < window->height))
554 return;
556 start = amount + window->first_row;
557 end = window->height + window->first_row;
559 display_scroll_display (start, end, -amount);
561 else
563 int start, end, amount;
565 amount = old_pagetop - desired_top;
567 if ((amount >= window->height) ||
568 (((window->height - amount) * 10) < window->height))
569 return;
571 start = window->first_row;
572 end = (window->first_row + window->height) - amount;
573 display_scroll_display (start, end, amount);
577 /* Immediately make WINDOW->point visible on the screen, and move the
578 terminal cursor there. */
579 static void
580 info_show_point (window)
581 WINDOW *window;
583 int old_pagetop;
585 old_pagetop = window->pagetop;
586 window_adjust_pagetop (window);
587 if (old_pagetop != window->pagetop)
589 int new_pagetop;
591 new_pagetop = window->pagetop;
592 window->pagetop = old_pagetop;
593 set_window_pagetop (window, new_pagetop);
596 if (window->flags & W_UpdateWindow)
597 display_update_one_window (window);
599 display_cursor_at_point (window);
602 /* Move WINDOW->point from OLD line index to NEW line index. */
603 static void
604 move_to_new_line (old, new, window)
605 int old, new;
606 WINDOW *window;
608 if (old == -1)
610 info_error (CANT_FIND_POINT);
612 else
614 int goal;
616 if (new >= window->line_count || new < 0)
617 return;
619 goal = window_get_goal_column (window);
620 window->goal_column = goal;
622 window->point = window->line_starts[new] - window->node->contents;
623 window->point += window_chars_to_goal (window->line_starts[new], goal);
624 info_show_point (window);
628 /* Move WINDOW's point down to the next line if possible. */
629 DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
631 int old_line, new_line;
633 if (count < 0)
634 info_prev_line (window, -count, key);
635 else
637 old_line = window_line_of_point (window);
638 new_line = old_line + count;
639 move_to_new_line (old_line, new_line, window);
643 /* Move WINDOW's point up to the previous line if possible. */
644 DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
646 int old_line, new_line;
648 if (count < 0)
649 info_next_line (window, -count, key);
650 else
652 old_line = window_line_of_point (window);
653 new_line = old_line - count;
654 move_to_new_line (old_line, new_line, window);
658 /* Move WINDOW's point to the end of the true line. */
659 DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
661 register int point, len;
662 register char *buffer;
664 buffer = window->node->contents;
665 len = window->node->nodelen;
667 for (point = window->point;
668 (point < len) && (buffer[point] != '\n');
669 point++);
671 if (point != window->point)
673 window->point = point;
674 info_show_point (window);
678 /* Move WINDOW's point to the beginning of the true line. */
679 DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
681 register int point;
682 register char *buffer;
684 buffer = window->node->contents;
685 point = window->point;
687 for (; (point) && (buffer[point - 1] != '\n'); point--);
689 /* If at a line start alreay, do nothing. */
690 if (point != window->point)
692 window->point = point;
693 info_show_point (window);
697 /* Move point forward in the node. */
698 DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
700 if (count < 0)
701 info_backward_char (window, -count, key);
702 else
704 window->point += count;
706 if (window->point >= window->node->nodelen)
707 window->point = window->node->nodelen - 1;
709 info_show_point (window);
713 /* Move point backward in the node. */
714 DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
716 if (count < 0)
717 info_forward_char (window, -count, key);
718 else
720 window->point -= count;
722 if (window->point < 0)
723 window->point = 0;
725 info_show_point (window);
729 #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
731 /* Move forward a word in this node. */
732 DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
734 long point;
735 char *buffer;
736 int end, c;
738 if (count < 0)
740 info_backward_word (window, -count, key);
741 return;
744 point = window->point;
745 buffer = window->node->contents;
746 end = window->node->nodelen;
748 while (count)
750 if (point + 1 >= end)
751 return;
753 /* If we are not in a word, move forward until we are in one.
754 Then, move forward until we hit a non-alphabetic character. */
755 c = buffer[point];
757 if (!alphabetic (c))
759 while (++point < end)
761 c = buffer[point];
762 if (alphabetic (c))
763 break;
767 if (point >= end) return;
769 while (++point < end)
771 c = buffer[point];
772 if (!alphabetic (c))
773 break;
775 --count;
777 window->point = point;
778 info_show_point (window);
781 DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
783 long point;
784 char *buffer;
785 int c;
787 if (count < 0)
789 info_forward_word (window, -count, key);
790 return;
793 buffer = window->node->contents;
794 point = window->point;
796 while (count)
798 if (point == 0)
799 break;
801 /* Like info_forward_word (), except that we look at the
802 characters just before point. */
804 c = buffer[point - 1];
806 if (!alphabetic (c))
808 while (--point)
810 c = buffer[point - 1];
811 if (alphabetic (c))
812 break;
816 while (point)
818 c = buffer[point - 1];
819 if (!alphabetic (c))
820 break;
821 else
822 --point;
824 --count;
826 window->point = point;
827 info_show_point (window);
830 /* Here is a list of time counter names which correspond to ordinal numbers.
831 It is used to print "once" instead of "1". */
832 static char *counter_names[] = {
833 "not at all", "once", "twice", "three", "four", "five", "six",
834 (char *)NULL
837 /* Buffer used to return values from times_description (). */
838 static char td_buffer[50];
840 /* Function returns a static string fully describing the number of times
841 present in COUNT. */
842 static char *
843 times_description (count)
844 int count;
846 register int i;
848 td_buffer[0] = '\0';
850 for (i = 0; counter_names[i]; i++)
851 if (count == i)
852 break;
854 if (counter_names[i])
855 sprintf (td_buffer, "%s%s", counter_names[i], count > 2 ? _(" times") : "");
856 else
857 sprintf (td_buffer, _("%d times"), count);
859 return (td_buffer);
862 /* Variable controlling the behaviour of default scrolling when you are
863 already at the bottom of a node. Possible values are defined in session.h.
864 The meanings are:
866 IS_Continuous Try to get first menu item, or failing that, the
867 "Next:" pointer, or failing that, the "Up:" and
868 "Next:" of the up.
869 IS_NextOnly Try to get "Next:" menu item.
870 IS_PageOnly Simply give up at the bottom of a node. */
872 int info_scroll_behaviour = IS_Continuous;
874 /* Choices used by the completer when reading a value for the user-visible
875 variable "scroll-behaviour". */
876 char *info_scroll_choices[] = {
877 "Continuous", "Next Only", "Page Only", (char *)NULL
880 /* Move to 1st menu item, Next, Up/Next, or error in this window. */
881 static void
882 forward_move_node_structure (window, behaviour)
883 WINDOW *window;
884 int behaviour;
886 switch (behaviour)
888 case IS_PageOnly:
889 info_error (AT_NODE_BOTTOM);
890 break;
892 case IS_NextOnly:
893 info_next_label_of_node (window->node);
894 if (!info_parsed_nodename && !info_parsed_filename)
895 info_error (_("No \"Next\" pointer for this node."));
896 else
898 window_message_in_echo_area (_("Following \"Next\" node..."));
899 info_handle_pointer (_("Next"), window);
901 break;
903 case IS_Continuous:
905 /* First things first. If this node contains a menu, move down
906 into the menu. */
908 REFERENCE **menu;
910 menu = info_menu_of_node (window->node);
912 if (menu)
914 info_free_references (menu);
915 window_message_in_echo_area (_("Selecting first menu item..."));
916 info_menu_digit (window, 1, '1');
917 return;
921 /* Okay, this node does not contain a menu. If it contains a
922 "Next:" pointer, use that. */
923 info_next_label_of_node (window->node);
924 if (info_label_was_found)
926 window_message_in_echo_area (_("Selecting \"Next\" node..."));
927 info_handle_pointer (_("Next"), window);
928 return;
931 /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we
932 can move "Next:". If that isn't possible, complain that there
933 are no more nodes. */
935 int up_counter, old_current;
936 INFO_WINDOW *info_win;
938 /* Remember the current node and location. */
939 info_win = get_info_window_of_window (window);
940 old_current = info_win->current;
942 /* Back up through the "Up:" pointers until we have found a "Next:"
943 that isn't the same as the first menu item found in that node. */
944 up_counter = 0;
945 while (!info_error_was_printed)
947 info_up_label_of_node (window->node);
948 if (info_label_was_found)
950 info_handle_pointer (_("Up"), window);
951 if (info_error_was_printed)
952 continue;
954 up_counter++;
956 info_next_label_of_node (window->node);
958 /* If no "Next" pointer, keep backing up. */
959 if (!info_label_was_found)
960 continue;
962 /* If this node's first menu item is the same as this node's
963 Next pointer, keep backing up. */
964 if (!info_parsed_filename)
966 REFERENCE **menu;
967 char *next_nodename;
969 /* Remember the name of the Next node, since reading
970 the menu can overwrite the contents of the
971 info_parsed_xxx strings. */
972 next_nodename = xstrdup (info_parsed_nodename);
974 menu = info_menu_of_node (window->node);
975 if (menu &&
976 (strcmp
977 (menu[0]->nodename, next_nodename) == 0))
979 info_free_references (menu);
980 free (next_nodename);
981 continue;
983 else
985 /* Restore the world to where it was before
986 reading the menu contents. */
987 info_free_references (menu);
988 free (next_nodename);
989 info_next_label_of_node (window->node);
993 /* This node has a "Next" pointer, and it is not the
994 same as the first menu item found in this node. */
995 window_message_in_echo_area
996 ("Moving \"Up\" %s, then \"Next\".",
997 times_description (up_counter));
999 info_handle_pointer (_("Next"), window);
1000 return;
1002 else
1004 /* No more "Up" pointers. Print an error, and call it
1005 quits. */
1006 register int i;
1008 for (i = 0; i < up_counter; i++)
1010 info_win->nodes_index--;
1011 free (info_win->nodes[info_win->nodes_index]);
1012 info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
1014 info_win->current = old_current;
1015 window->node = info_win->nodes[old_current];
1016 window->pagetop = info_win->pagetops[old_current];
1017 window->point = info_win->points[old_current];
1018 recalculate_line_starts (window);
1019 window->flags |= W_UpdateWindow;
1020 info_error (_("No more nodes."));
1024 break;
1029 /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
1030 static void
1031 backward_move_node_structure (window, behaviour)
1032 WINDOW *window;
1033 int behaviour;
1035 switch (behaviour)
1037 case IS_PageOnly:
1038 info_error (AT_NODE_TOP);
1039 break;
1041 case IS_NextOnly:
1042 info_prev_label_of_node (window->node);
1043 if (!info_parsed_nodename && !info_parsed_filename)
1044 info_error (_("No \"Prev\" for this node."));
1045 else
1047 window_message_in_echo_area (_("Moving \"Prev\" in this window."));
1048 info_handle_pointer (_("Prev"), window);
1050 break;
1052 case IS_Continuous:
1053 info_prev_label_of_node (window->node);
1055 if (!info_parsed_nodename && !info_parsed_filename)
1057 info_up_label_of_node (window->node);
1058 if (!info_parsed_nodename && !info_parsed_filename)
1059 info_error (_("No \"Prev\" or \"Up\" for this node."));
1060 else
1062 window_message_in_echo_area (_("Moving \"Up\" in this window."));
1063 info_handle_pointer (_("Up"), window);
1066 else
1068 REFERENCE **menu;
1069 int inhibit_menu_traversing = 0;
1071 /* Watch out! If this node's Prev is the same as the Up, then
1072 move Up. Otherwise, we could move Prev, and then to the last
1073 menu item in the Prev. This would cause the user to loop
1074 through a subsection of the info file. */
1075 if (!info_parsed_filename && info_parsed_nodename)
1077 char *pnode;
1079 pnode = xstrdup (info_parsed_nodename);
1080 info_up_label_of_node (window->node);
1082 if (!info_parsed_filename && info_parsed_nodename &&
1083 strcmp (info_parsed_nodename, pnode) == 0)
1085 /* The nodes are the same. Inhibit moving to the last
1086 menu item. */
1087 free (pnode);
1088 inhibit_menu_traversing = 1;
1090 else
1092 free (pnode);
1093 info_prev_label_of_node (window->node);
1097 /* Move to the previous node. If this node now contains a menu,
1098 and we have not inhibited movement to it, move to the node
1099 corresponding to the last menu item. */
1100 window_message_in_echo_area (_("Moving \"Prev\" in this window."));
1101 info_handle_pointer (_("Prev"), window);
1103 if (!inhibit_menu_traversing)
1105 while (!info_error_was_printed &&
1106 (menu = info_menu_of_node (window->node)))
1108 info_free_references (menu);
1109 window_message_in_echo_area
1110 (_("Moving to \"Prev\"'s last menu item."));
1111 info_menu_digit (window, 1, '0');
1115 break;
1119 /* Move continuously forward through the node structure of this info file. */
1120 DECLARE_INFO_COMMAND (info_global_next_node,
1121 _("Move forwards or down through node structure"))
1123 if (count < 0)
1124 info_global_prev_node (window, -count, key);
1125 else
1127 while (count && !info_error_was_printed)
1129 forward_move_node_structure (window, IS_Continuous);
1130 count--;
1135 /* Move continuously backward through the node structure of this info file. */
1136 DECLARE_INFO_COMMAND (info_global_prev_node,
1137 _("Move backwards or up through node structure"))
1139 if (count < 0)
1140 info_global_next_node (window, -count, key);
1141 else
1143 while (count && !info_error_was_printed)
1145 backward_move_node_structure (window, IS_Continuous);
1146 count--;
1151 /* Show the next screen of WINDOW's node. */
1152 DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
1154 if (count < 0)
1155 info_scroll_backward (window, -count, key);
1156 else
1158 int desired_top;
1160 /* Without an explicit numeric argument, scroll the bottom two
1161 lines to the top of this window, Or, if at bottom of window,
1162 and the user wishes to scroll through nodes get the "Next" node
1163 for this window. */
1164 if (!info_explicit_arg && count == 1)
1166 desired_top = window->pagetop + (window->height - 2);
1168 /* If there are no more lines to scroll here, error, or get
1169 another node, depending on INFO_SCROLL_BEHAVIOUR. */
1170 if (desired_top > window->line_count)
1172 int behaviour = info_scroll_behaviour;
1174 /* Here is a hack. If the key being used is not SPC, do the
1175 PageOnly behaviour. */
1176 if (key != SPC && key != DEL)
1177 behaviour = IS_PageOnly;
1179 forward_move_node_structure (window, behaviour);
1180 return;
1183 else
1184 desired_top = window->pagetop + count;
1186 if (desired_top >= window->line_count)
1187 desired_top = window->line_count - 2;
1189 if (window->pagetop > desired_top)
1190 return;
1191 else
1192 set_window_pagetop (window, desired_top);
1196 /* Show the previous screen of WINDOW's node. */
1197 DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
1199 if (count < 0)
1200 info_scroll_forward (window, -count, key);
1201 else
1203 int desired_top;
1205 /* Without an explicit numeric argument, scroll the top two lines
1206 to the bottom of this window, or move to the previous, or Up'th
1207 node. */
1208 if (!info_explicit_arg && count == 1)
1210 desired_top = window->pagetop - (window->height - 2);
1212 if ((desired_top < 0) && (window->pagetop == 0))
1214 int behaviour = info_scroll_behaviour;
1216 /* Same kind of hack as in info_scroll_forward. If the key
1217 used to invoke this command is not DEL, do only the PageOnly
1218 behaviour. */
1219 if (key != DEL && key != SPC)
1220 behaviour = IS_PageOnly;
1222 backward_move_node_structure (window, behaviour);
1223 return;
1226 else
1227 desired_top = window->pagetop - count;
1229 if (desired_top < 0)
1230 desired_top = 0;
1232 set_window_pagetop (window, desired_top);
1236 /* Move to the beginning of the node. */
1237 DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1239 window->pagetop = window->point = 0;
1240 window->flags |= W_UpdateWindow;
1243 /* Move to the end of the node. */
1244 DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1246 window->point = window->node->nodelen - 1;
1247 info_show_point (window);
1250 /* **************************************************************** */
1251 /* */
1252 /* Commands for Manipulating Windows */
1253 /* */
1254 /* **************************************************************** */
1256 /* Make the next window in the chain be the active window. */
1257 DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1259 if (count < 0)
1261 info_prev_window (window, -count, key);
1262 return;
1265 /* If no other window, error now. */
1266 if (!windows->next && !echo_area_is_active)
1268 info_error (ONE_WINDOW);
1269 return;
1272 while (count--)
1274 if (window->next)
1275 window = window->next;
1276 else
1278 if (window == the_echo_area || !echo_area_is_active)
1279 window = windows;
1280 else
1281 window = the_echo_area;
1285 if (active_window != window)
1287 if (auto_footnotes_p)
1288 info_get_or_remove_footnotes (window);
1290 window->flags |= W_UpdateWindow;
1291 active_window = window;
1295 /* Make the previous window in the chain be the active window. */
1296 DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1298 if (count < 0)
1300 info_next_window (window, -count, key);
1301 return;
1304 /* Only one window? */
1306 if (!windows->next && !echo_area_is_active)
1308 info_error (ONE_WINDOW);
1309 return;
1312 while (count--)
1314 /* If we are in the echo area, or if the echo area isn't active and we
1315 are in the first window, find the last window in the chain. */
1316 if (window == the_echo_area ||
1317 (window == windows && !echo_area_is_active))
1319 register WINDOW *win, *last;
1321 for (win = windows; win; win = win->next)
1322 last = win;
1324 window = last;
1326 else
1328 if (window == windows)
1329 window = the_echo_area;
1330 else
1331 window = window->prev;
1335 if (active_window != window)
1337 if (auto_footnotes_p)
1338 info_get_or_remove_footnotes (window);
1340 window->flags |= W_UpdateWindow;
1341 active_window = window;
1345 /* Split WINDOW into two windows, both showing the same node. If we
1346 are automatically tiling windows, re-tile after the split. */
1347 DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
1349 WINDOW *split, *old_active;
1350 int pagetop;
1352 /* Remember the current pagetop of the window being split. If it doesn't
1353 change, we can scroll its contents around after the split. */
1354 pagetop = window->pagetop;
1356 /* Make the new window. */
1357 old_active = active_window;
1358 active_window = window;
1359 split = window_make_window (window->node);
1360 active_window = old_active;
1362 if (!split)
1364 info_error (WIN_TOO_SMALL);
1366 else
1368 #if defined (SPLIT_BEFORE_ACTIVE)
1369 /* Try to scroll the old window into its new postion. */
1370 if (pagetop == window->pagetop)
1372 int start, end, amount;
1374 start = split->first_row;
1375 end = start + window->height;
1376 amount = split->height + 1;
1377 display_scroll_display (start, end, amount);
1379 #else /* !SPLIT_BEFORE_ACTIVE */
1380 /* Make sure point still appears in the active window. */
1381 info_show_point (window);
1382 #endif /* !SPLIT_BEFORE_ACTIVE */
1384 /* If the window just split was one internal to Info, try to display
1385 something else in it. */
1386 if (internal_info_node_p (split->node))
1388 register int i, j;
1389 INFO_WINDOW *iw;
1390 NODE *node = (NODE *)NULL;
1391 char *filename;
1393 for (i = 0; (iw = info_windows[i]); i++)
1395 for (j = 0; j < iw->nodes_index; j++)
1396 if (!internal_info_node_p (iw->nodes[j]))
1398 if (iw->nodes[j]->parent)
1399 filename = iw->nodes[j]->parent;
1400 else
1401 filename = iw->nodes[j]->filename;
1403 node = info_get_node (filename, iw->nodes[j]->nodename);
1404 if (node)
1406 window_set_node_of_window (split, node);
1407 i = info_windows_index - 1;
1408 break;
1413 split->pagetop = window->pagetop;
1415 if (auto_tiling_p)
1416 window_tile_windows (DONT_TILE_INTERNALS);
1417 else
1418 window_adjust_pagetop (split);
1420 remember_window_and_node (split, split->node);
1424 /* Delete WINDOW, forgetting the list of last visited nodes. If we are
1425 automatically displaying footnotes, show or remove the footnotes
1426 window. If we are automatically tiling windows, re-tile after the
1427 deletion. */
1428 DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
1430 if (!windows->next)
1432 info_error (CANT_KILL_LAST);
1434 else if (window->flags & W_WindowIsPerm)
1436 info_error (_("Cannot delete a permanent window"));
1438 else
1440 info_delete_window_internal (window);
1442 if (auto_footnotes_p)
1443 info_get_or_remove_footnotes (active_window);
1445 if (auto_tiling_p)
1446 window_tile_windows (DONT_TILE_INTERNALS);
1450 /* Do the physical deletion of WINDOW, and forget this window and
1451 associated nodes. */
1452 void
1453 info_delete_window_internal (window)
1454 WINDOW *window;
1456 if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
1458 /* We not only delete the window from the display, we forget it from
1459 our list of remembered windows. */
1460 forget_window_and_nodes (window);
1461 window_delete_window (window);
1463 if (echo_area_is_active)
1464 echo_area_inform_of_deleted_window (window);
1468 /* Just keep WINDOW, deleting all others. */
1469 DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1471 int num_deleted; /* The number of windows we deleted. */
1472 int pagetop, start, end;
1474 /* Remember a few things about this window. We may be able to speed up
1475 redisplay later by scrolling its contents. */
1476 pagetop = window->pagetop;
1477 start = window->first_row;
1478 end = start + window->height;
1480 num_deleted = 0;
1482 while (1)
1484 WINDOW *win;
1486 /* Find an eligible window and delete it. If no eligible windows
1487 are found, we are done. A window is eligible for deletion if
1488 is it not permanent, and it is not WINDOW. */
1489 for (win = windows; win; win = win->next)
1490 if (win != window && ((win->flags & W_WindowIsPerm) == 0))
1491 break;
1493 if (!win)
1494 break;
1496 info_delete_window_internal (win);
1497 num_deleted++;
1500 /* Scroll the contents of this window into the right place so that the
1501 user doesn't have to wait any longer than necessary for redisplay. */
1502 if (num_deleted)
1504 int amount;
1506 amount = (window->first_row - start);
1507 amount -= (window->pagetop - pagetop);
1508 display_scroll_display (start, end, amount);
1511 window->flags |= W_UpdateWindow;
1514 /* Scroll the "other" window of WINDOW. */
1515 DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1517 WINDOW *other;
1519 /* If only one window, give up. */
1520 if (!windows->next)
1522 info_error (ONE_WINDOW);
1523 return;
1526 other = window->next;
1528 if (!other)
1529 other = window->prev;
1531 info_scroll_forward (other, count, key);
1534 /* Change the size of WINDOW by AMOUNT. */
1535 DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1537 window_change_window_height (window, count);
1540 /* When non-zero, tiling takes place automatically when info_split_window
1541 is called. */
1542 int auto_tiling_p = 0;
1544 /* Tile all of the visible windows. */
1545 DECLARE_INFO_COMMAND (info_tile_windows,
1546 _("Divide the available screen space among the visible windows"))
1548 window_tile_windows (TILE_INTERNALS);
1551 /* Toggle the state of this window's wrapping of lines. */
1552 DECLARE_INFO_COMMAND (info_toggle_wrap,
1553 _("Toggle the state of line wrapping in the current window"))
1555 window_toggle_wrap (window);
1558 /* **************************************************************** */
1559 /* */
1560 /* Info Node Commands */
1561 /* */
1562 /* **************************************************************** */
1564 /* Using WINDOW for various defaults, select the node referenced by ENTRY
1565 in it. If the node is selected, the window and node are remembered. */
1566 void
1567 info_select_reference (window, entry)
1568 WINDOW *window;
1569 REFERENCE *entry;
1571 NODE *node;
1572 char *filename, *nodename, *file_system_error;
1574 file_system_error = (char *)NULL;
1576 filename = entry->filename;
1577 if (!filename)
1578 filename = window->node->parent;
1579 if (!filename)
1580 filename = window->node->filename;
1582 if (filename)
1583 filename = xstrdup (filename);
1585 if (entry->nodename)
1586 nodename = xstrdup (entry->nodename);
1587 else
1588 nodename = xstrdup ("Top");
1590 node = info_get_node (filename, nodename);
1592 /* Try something a little weird. If the node couldn't be found, and the
1593 reference was of the form "foo::", see if the entry->label can be found
1594 as a file, with a node of "Top". */
1595 if (!node)
1597 if (info_recent_file_error)
1598 file_system_error = xstrdup (info_recent_file_error);
1600 if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
1602 node = info_get_node (entry->label, "Top");
1603 if (!node && info_recent_file_error)
1605 maybe_free (file_system_error);
1606 file_system_error = xstrdup (info_recent_file_error);
1611 if (!node)
1613 if (file_system_error)
1614 info_error (file_system_error);
1615 else
1616 info_error (CANT_FIND_NODE, nodename);
1619 maybe_free (file_system_error);
1620 maybe_free (filename);
1621 maybe_free (nodename);
1623 if (node)
1625 set_remembered_pagetop_and_point (window);
1626 info_set_node_of_window (window, node);
1630 /* Parse the node specification in LINE using WINDOW to default the filename.
1631 Select the parsed node in WINDOW and remember it, or error if the node
1632 couldn't be found. */
1633 static void
1634 info_parse_and_select (line, window)
1635 char *line;
1636 WINDOW *window;
1638 REFERENCE entry;
1640 info_parse_node (line, DONT_SKIP_NEWLINES);
1642 entry.nodename = info_parsed_nodename;
1643 entry.filename = info_parsed_filename;
1644 entry.label = "*info-parse-and-select*";
1646 info_select_reference (window, &entry);
1649 /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
1650 are previously filled, try to get the node represented by them into
1651 WINDOW. The node should have been pointed to by the LABEL pointer of
1652 WINDOW->node. */
1653 static void
1654 info_handle_pointer (label, window)
1655 char *label;
1656 WINDOW *window;
1658 if (info_parsed_filename || info_parsed_nodename)
1660 char *filename, *nodename;
1661 NODE *node;
1663 filename = nodename = (char *)NULL;
1665 if (info_parsed_filename)
1666 filename = xstrdup (info_parsed_filename);
1667 else
1669 if (window->node->parent)
1670 filename = xstrdup (window->node->parent);
1671 else if (window->node->filename)
1672 filename = xstrdup (window->node->filename);
1675 if (info_parsed_nodename)
1676 nodename = xstrdup (info_parsed_nodename);
1677 else
1678 nodename = xstrdup ("Top");
1680 node = info_get_node (filename, nodename);
1682 if (node)
1684 INFO_WINDOW *info_win;
1686 info_win = get_info_window_of_window (window);
1687 if (info_win)
1689 info_win->pagetops[info_win->current] = window->pagetop;
1690 info_win->points[info_win->current] = window->point;
1692 set_remembered_pagetop_and_point (window);
1693 info_set_node_of_window (window, node);
1695 else
1697 if (info_recent_file_error)
1698 info_error (info_recent_file_error);
1699 else
1700 info_error (CANT_FILE_NODE, filename, nodename);
1703 free (filename);
1704 free (nodename);
1706 else
1708 info_error (NO_POINTER, label);
1712 /* Make WINDOW display the "Next:" node of the node currently being
1713 displayed. */
1714 DECLARE_INFO_COMMAND (info_next_node, _("Select the `Next' node"))
1716 info_next_label_of_node (window->node);
1717 info_handle_pointer (_("Next"), window);
1720 /* Make WINDOW display the "Prev:" node of the node currently being
1721 displayed. */
1722 DECLARE_INFO_COMMAND (info_prev_node, _("Select the `Prev' node"))
1724 info_prev_label_of_node (window->node);
1725 info_handle_pointer (_("Prev"), window);
1728 /* Make WINDOW display the "Up:" node of the node currently being
1729 displayed. */
1730 DECLARE_INFO_COMMAND (info_up_node, _("Select the `Up' node"))
1732 info_up_label_of_node (window->node);
1733 info_handle_pointer (_("Up"), window);
1736 /* Make WINDOW display the last node of this info file. */
1737 DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
1739 register int i;
1740 FILE_BUFFER *fb = file_buffer_of_window (window);
1741 NODE *node = (NODE *)NULL;
1743 if (fb && fb->tags)
1745 for (i = 0; fb->tags[i]; i++);
1746 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1749 if (!node)
1750 info_error (_("This window has no additional nodes"));
1751 else
1753 set_remembered_pagetop_and_point (window);
1754 info_set_node_of_window (window, node);
1758 /* Make WINDOW display the first node of this info file. */
1759 DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
1761 FILE_BUFFER *fb = file_buffer_of_window (window);
1762 NODE *node = (NODE *)NULL;
1764 if (fb && fb->tags)
1765 node = info_get_node (fb->filename, fb->tags[0]->nodename);
1767 if (!node)
1768 info_error (_("This window has no additional nodes"));
1769 else
1771 set_remembered_pagetop_and_point (window);
1772 info_set_node_of_window (window, node);
1776 /* Select the last menu item in WINDOW->node. */
1777 DECLARE_INFO_COMMAND (info_last_menu_item,
1778 _("Select the last item in this node's menu"))
1780 info_menu_digit (window, 1, '0');
1783 /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
1784 DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
1786 register int i, item;
1787 register REFERENCE *entry, **menu;
1789 menu = info_menu_of_node (window->node);
1791 if (!menu)
1793 info_error (NO_MENU_NODE);
1794 return;
1797 /* We have the menu. See if there are this many items in it. */
1798 item = key - '0';
1800 /* Special case. Item "0" is the last item in this menu. */
1801 if (item == 0)
1802 for (i = 0; menu[i + 1]; i++);
1803 else
1805 for (i = 0; (entry = menu[i]); i++)
1806 if (i == item - 1)
1807 break;
1810 if (menu[i])
1811 info_select_reference (window, menu[i]);
1812 else
1813 info_error (_("There aren't %d items in this menu."), item);
1815 info_free_references (menu);
1816 return;
1819 /* Read a menu or followed reference from the user defaulting to the
1820 reference found on the current line, and select that node. The
1821 reading is done with completion. BUILDER is the function used
1822 to build the list of references. ASK_P is non-zero if the user
1823 should be prompted, or zero to select the default item. */
1824 static void
1825 info_menu_or_ref_item (window, count, key, builder, ask_p)
1826 WINDOW *window;
1827 int count;
1828 unsigned char key;
1829 REFERENCE **(*builder) ();
1830 int ask_p;
1832 REFERENCE **menu, *entry, *defentry = (REFERENCE *)NULL;
1833 char *line;
1835 menu = (*builder) (window->node);
1837 if (!menu)
1839 if (builder == info_menu_of_node)
1840 info_error (NO_MENU_NODE);
1841 else
1842 info_error (NO_XREF_NODE);
1843 return;
1846 /* Default the selected reference to the one which is on the line that
1847 point is in. */
1849 REFERENCE **refs = (REFERENCE **)NULL;
1850 int point_line;
1852 point_line = window_line_of_point (window);
1854 if (point_line != -1)
1856 SEARCH_BINDING binding;
1858 binding.buffer = window->node->contents;
1859 binding.start = window->line_starts[point_line] - binding.buffer;
1860 if (window->line_starts[point_line + 1])
1861 binding.end = window->line_starts[point_line + 1] - binding.buffer;
1862 else
1863 binding.end = window->node->nodelen;
1864 binding.flags = 0;
1866 if (builder == info_menu_of_node)
1868 if (point_line)
1870 binding.start--;
1871 refs = info_menu_items (&binding);
1874 else
1876 #if defined (HANDLE_MAN_PAGES)
1877 if (window->node->flags & N_IsManPage)
1878 refs = manpage_xrefs_in_binding (window->node, &binding);
1879 else
1880 #endif /* HANDLE_MAN_PAGES */
1881 refs = info_xrefs (&binding);
1884 if (refs)
1886 if ((strcmp (refs[0]->label, "Menu") != 0) ||
1887 (builder == info_xrefs_of_node))
1889 int which = 0;
1891 /* Find the closest reference to point. */
1892 if (builder == info_xrefs_of_node)
1894 int closest = -1;
1896 for (; refs[which]; which++)
1898 if ((window->point >= refs[which]->start) &&
1899 (window->point <= refs[which]->end))
1901 closest = which;
1902 break;
1904 else if (window->point < refs[which]->start)
1906 break;
1909 if (closest == -1)
1910 which--;
1911 else
1912 which = closest;
1915 defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
1916 defentry->label = xstrdup (refs[which]->label);
1917 defentry->filename = refs[which]->filename;
1918 defentry->nodename = refs[which]->nodename;
1920 if (defentry->filename)
1921 defentry->filename = xstrdup (defentry->filename);
1922 if (defentry->nodename)
1923 defentry->nodename = xstrdup (defentry->nodename);
1925 info_free_references (refs);
1930 /* If we are going to ask the user a question, do it now. */
1931 if (ask_p)
1933 char *prompt;
1935 /* Build the prompt string. */
1936 if (defentry)
1937 prompt = (char *)xmalloc (20 + strlen (defentry->label));
1938 else
1939 prompt = (char *)xmalloc (20);
1941 if (builder == info_menu_of_node)
1943 if (defentry)
1944 sprintf (prompt, _("Menu item (%s): "), defentry->label);
1945 else
1946 sprintf (prompt, _("Menu item: "));
1948 else
1950 if (defentry)
1951 sprintf (prompt, _("Follow xref (%s): "), defentry->label);
1952 else
1953 sprintf (prompt, _("Follow xref: "));
1956 line = info_read_completing_in_echo_area (window, prompt, menu);
1957 free (prompt);
1959 window = active_window;
1961 /* User aborts, just quit. */
1962 if (!line)
1964 maybe_free (defentry);
1965 info_free_references (menu);
1966 info_abort_key (window, 0, 0);
1967 return;
1970 /* If we had a default and the user accepted it, use that. */
1971 if (!*line)
1973 free (line);
1974 if (defentry)
1975 line = xstrdup (defentry->label);
1976 else
1977 line = (char *)NULL;
1980 else
1982 /* Not going to ask any questions. If we have a default entry, use
1983 that, otherwise return. */
1984 if (!defentry)
1985 return;
1986 else
1987 line = xstrdup (defentry->label);
1990 if (line)
1992 /* Find the selected label in the references. */
1993 entry = info_get_labeled_reference (line, menu);
1995 if (!entry && defentry)
1996 info_error (_("The reference disappeared! (%s)."), line);
1997 else
1999 NODE *orig;
2001 orig = window->node;
2002 info_select_reference (window, entry);
2003 if ((builder == info_xrefs_of_node) && (window->node != orig))
2005 long offset;
2006 long start;
2008 if (window->line_count > 0)
2009 start = window->line_starts[1] - window->node->contents;
2010 else
2011 start = 0;
2013 offset =
2014 info_target_search_node (window->node, entry->label, start);
2016 if (offset != -1)
2018 window->point = offset;
2019 window_adjust_pagetop (window);
2024 free (line);
2025 if (defentry)
2027 free (defentry->label);
2028 maybe_free (defentry->filename);
2029 maybe_free (defentry->nodename);
2030 free (defentry);
2034 info_free_references (menu);
2036 if (!info_error_was_printed)
2037 window_clear_echo_area ();
2040 /* Read a line (with completion) which is the name of a menu item,
2041 and select that item. */
2042 DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
2044 info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2047 /* Read a line (with completion) which is the name of a reference to
2048 follow, and select the node. */
2049 DECLARE_INFO_COMMAND
2050 (info_xref_item, _("Read a footnote or cross reference and select its node"))
2052 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2055 /* Position the cursor at the start of this node's menu. */
2056 DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
2058 SEARCH_BINDING binding;
2059 long position;
2061 binding.buffer = window->node->contents;
2062 binding.start = 0;
2063 binding.end = window->node->nodelen;
2064 binding.flags = S_FoldCase | S_SkipDest;
2066 position = search (INFO_MENU_LABEL, &binding);
2068 if (position == -1)
2069 info_error (NO_MENU_NODE);
2070 else
2072 window->point = position;
2073 window_adjust_pagetop (window);
2074 window->flags |= W_UpdateWindow;
2078 /* Visit as many menu items as is possible, each in a separate window. */
2079 DECLARE_INFO_COMMAND (info_visit_menu,
2080 _("Visit as many menu items at once as possible"))
2082 register int i;
2083 REFERENCE *entry, **menu;
2085 menu = info_menu_of_node (window->node);
2087 if (!menu)
2088 info_error (NO_MENU_NODE);
2090 for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2092 WINDOW *new;
2094 new = window_make_window (window->node);
2095 window_tile_windows (TILE_INTERNALS);
2097 if (!new)
2098 info_error (WIN_TOO_SMALL);
2099 else
2101 active_window = new;
2102 info_select_reference (new, entry);
2107 /* Read a line of input which is a node name, and go to that node. */
2108 DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
2110 char *line;
2112 #define GOTO_COMPLETES
2113 #if defined (GOTO_COMPLETES)
2114 /* Build a completion list of all of the known nodes. */
2116 register int fbi, i;
2117 FILE_BUFFER *current;
2118 REFERENCE **items = (REFERENCE **)NULL;
2119 int items_index = 0;
2120 int items_slots = 0;
2122 current = file_buffer_of_window (window);
2124 for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2126 FILE_BUFFER *fb;
2127 REFERENCE *entry;
2128 int this_is_the_current_fb;
2130 fb = info_loaded_files[fbi];
2131 this_is_the_current_fb = (current == fb);
2133 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2134 entry->filename = entry->nodename = (char *)NULL;
2135 entry->label = (char *)xmalloc (4 + strlen (fb->filename));
2136 sprintf (entry->label, "(%s)*", fb->filename);
2138 add_pointer_to_array
2139 (entry, items_index, items, items_slots, 10, REFERENCE *);
2141 if (fb->tags)
2143 for (i = 0; fb->tags[i]; i++)
2145 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2146 entry->filename = entry->nodename = (char *)NULL;
2147 entry->label = (char *) xmalloc
2148 (4 + strlen (fb->filename) + strlen (fb->tags[i]->nodename));
2149 sprintf (entry->label, "(%s)%s",
2150 fb->filename, fb->tags[i]->nodename);
2152 add_pointer_to_array
2153 (entry, items_index, items, items_slots, 100, REFERENCE *);
2156 if (this_is_the_current_fb)
2158 for (i = 0; fb->tags[i]; i++)
2160 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2161 entry->filename = entry->nodename = (char *)NULL;
2162 entry->label = xstrdup (fb->tags[i]->nodename);
2163 add_pointer_to_array (entry, items_index, items,
2164 items_slots, 100, REFERENCE *);
2169 line = info_read_maybe_completing (window, _("Goto Node: "), items);
2170 info_free_references (items);
2172 #else /* !GOTO_COMPLETES */
2173 line = info_read_in_echo_area (window, _("Goto Node: "));
2174 #endif /* !GOTO_COMPLETES */
2176 /* If the user aborted, quit now. */
2177 if (!line)
2179 info_abort_key (window, 0, 0);
2180 return;
2183 canonicalize_whitespace (line);
2185 if (*line)
2186 info_parse_and_select (line, window);
2188 free (line);
2189 if (!info_error_was_printed)
2190 window_clear_echo_area ();
2193 #if defined (HANDLE_MAN_PAGES)
2194 DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
2196 char *line;
2198 line = info_read_in_echo_area (window, _("Get Manpage: "));
2200 if (!line)
2202 info_abort_key (window, 0, 0);
2203 return;
2206 canonicalize_whitespace (line);
2208 if (*line)
2210 char *goto_command;
2212 goto_command = (char *)xmalloc
2213 (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
2215 sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
2217 info_parse_and_select (goto_command, window);
2218 free (goto_command);
2221 free (line);
2222 if (!info_error_was_printed)
2223 window_clear_echo_area ();
2225 #endif /* HANDLE_MAN_PAGES */
2227 /* Move to the "Top" node in this file. */
2228 DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
2230 info_parse_and_select (_("Top"), window);
2233 /* Move to the node "(dir)Top". */
2234 DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
2236 info_parse_and_select ("(dir)Top", window);
2240 /* Read the name of a node to kill. The list of available nodes comes
2241 from the nodes appearing in the current window configuration. */
2242 static char *
2243 read_nodename_to_kill (window)
2244 WINDOW *window;
2246 int iw;
2247 char *nodename;
2248 INFO_WINDOW *info_win;
2249 REFERENCE **menu = NULL;
2250 int menu_index = 0, menu_slots = 0;
2251 char *default_nodename = xstrdup (active_window->node->nodename);
2252 char *prompt = xmalloc (40 + strlen (default_nodename));
2254 sprintf (prompt, _("Kill node (%s): "), default_nodename);
2256 for (iw = 0; (info_win = info_windows[iw]); iw++)
2258 REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2259 entry->label = xstrdup (info_win->window->node->nodename);
2260 entry->filename = entry->nodename = (char *)NULL;
2262 add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
2263 REFERENCE *);
2266 nodename = info_read_completing_in_echo_area (window, prompt, menu);
2267 free (prompt);
2268 info_free_references (menu);
2269 if (nodename && !*nodename)
2271 free (nodename);
2272 nodename = default_nodename;
2274 else
2275 free (default_nodename);
2277 return nodename;
2281 /* Delete NODENAME from this window, showing the most
2282 recently selected node in this window. */
2283 static void
2284 kill_node (window, nodename)
2285 WINDOW *window;
2286 char *nodename;
2288 int iw, i;
2289 INFO_WINDOW *info_win;
2290 NODE *temp;
2292 /* If there is no nodename to kill, quit now. */
2293 if (!nodename)
2295 info_abort_key (window, 0, 0);
2296 return;
2299 /* If there is a nodename, find it in our window list. */
2300 for (iw = 0; (info_win = info_windows[iw]); iw++)
2301 if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0)
2302 break;
2304 if (!info_win)
2306 if (*nodename)
2307 info_error (_("Cannot kill node `%s'"), nodename);
2308 else
2309 window_clear_echo_area ();
2311 return;
2314 /* If there are no more nodes left anywhere to view, complain and exit. */
2315 if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
2317 info_error (_("Cannot kill the last node"));
2318 return;
2321 /* INFO_WIN contains the node that the user wants to stop viewing. Delete
2322 this node from the list of nodes previously shown in this window. */
2323 for (i = info_win->current; i < info_win->nodes_index; i++)
2324 info_win->nodes[i] = info_win->nodes[i++];
2326 /* There is one less node in this window's history list. */
2327 info_win->nodes_index--;
2329 /* Make this window show the most recent history node. */
2330 info_win->current = info_win->nodes_index - 1;
2332 /* If there aren't any nodes left in this window, steal one from the
2333 next window. */
2334 if (info_win->current < 0)
2336 INFO_WINDOW *stealer;
2337 int which, pagetop;
2338 long point;
2340 if (info_windows[iw + 1])
2341 stealer = info_windows[iw + 1];
2342 else
2343 stealer = info_windows[0];
2345 /* If the node being displayed in the next window is not the most
2346 recently loaded one, get the most recently loaded one. */
2347 if ((stealer->nodes_index - 1) != stealer->current)
2348 which = stealer->nodes_index - 1;
2350 /* Else, if there is another node behind the stealers current node,
2351 use that one. */
2352 else if (stealer->current > 0)
2353 which = stealer->current - 1;
2355 /* Else, just use the node appearing in STEALER's window. */
2356 else
2357 which = stealer->current;
2359 /* Copy this node. */
2361 NODE *copy = xmalloc (sizeof (NODE));
2363 temp = stealer->nodes[which];
2364 point = stealer->points[which];
2365 pagetop = stealer->pagetops[which];
2367 copy->filename = temp->filename;
2368 copy->parent = temp->parent;
2369 copy->nodename = temp->nodename;
2370 copy->contents = temp->contents;
2371 copy->nodelen = temp->nodelen;
2372 copy->flags = temp->flags;
2374 temp = copy;
2377 window_set_node_of_window (info_win->window, temp);
2378 window->point = point;
2379 window->pagetop = pagetop;
2380 remember_window_and_node (info_win->window, temp);
2382 else
2384 temp = info_win->nodes[info_win->current];
2385 window_set_node_of_window (info_win->window, temp);
2388 if (!info_error_was_printed)
2389 window_clear_echo_area ();
2391 if (auto_footnotes_p)
2392 info_get_or_remove_footnotes (window);
2395 /* Kill current node, thus going back one in the node history. I (karl)
2396 do not think this is completely correct yet, because of the
2397 window-changing stuff in kill_node, but it's a lot better than the
2398 previous implementation, which did not account for nodes being
2399 visited twice at all. */
2400 DECLARE_INFO_COMMAND (info_history_node,
2401 _("Select the most recently selected node"))
2403 kill_node (window, active_window->node->nodename);
2406 /* Kill named node. */
2407 DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
2409 char *nodename = read_nodename_to_kill (window);
2410 kill_node (window, nodename);
2414 /* Read the name of a file and select the entire file. */
2415 DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
2417 char *line;
2419 line = info_read_in_echo_area (window, _("Find file: "));
2420 if (!line)
2422 info_abort_key (active_window, 1, 0);
2423 return;
2426 if (*line)
2428 NODE *node;
2430 node = info_get_node (line, "*");
2431 if (!node)
2433 if (info_recent_file_error)
2434 info_error (info_recent_file_error);
2435 else
2436 info_error (_("Cannot find \"%s\"."), line);
2438 else
2440 set_remembered_pagetop_and_point (active_window);
2441 info_set_node_of_window (window, node);
2443 free (line);
2446 if (!info_error_was_printed)
2447 window_clear_echo_area ();
2450 /* **************************************************************** */
2451 /* */
2452 /* Dumping and Printing Nodes */
2453 /* */
2454 /* **************************************************************** */
2456 #define VERBOSE_NODE_DUMPING
2457 static void write_node_to_stream ();
2458 static void dump_node_to_stream ();
2459 static void initialize_dumping ();
2461 /* Dump the nodes specified by FILENAME and NODENAMES to the file named
2462 in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
2463 the nodes which appear in the menu of each node dumped. */
2464 void
2465 dump_nodes_to_file (filename, nodenames, output_filename, dump_subnodes)
2466 char *filename;
2467 char **nodenames;
2468 char *output_filename;
2469 int dump_subnodes;
2471 register int i;
2472 FILE *output_stream;
2474 /* Get the stream to print the nodes to. Special case of an output
2475 filename of "-" means to dump the nodes to stdout. */
2476 if (strcmp (output_filename, "-") == 0)
2477 output_stream = stdout;
2478 else
2479 output_stream = fopen (output_filename, "w");
2481 if (!output_stream)
2483 info_error (_("Could not create output file \"%s\"."), output_filename);
2484 return;
2487 /* Print each node to stream. */
2488 initialize_dumping ();
2489 for (i = 0; nodenames[i]; i++)
2490 dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
2492 if (output_stream != stdout)
2493 fclose (output_stream);
2495 #if defined (VERBOSE_NODE_DUMPING)
2496 info_error (_("Done."));
2497 #endif /* VERBOSE_NODE_DUMPING */
2500 /* A place to remember already dumped nodes. */
2501 static char **dumped_already = (char **)NULL;
2502 static int dumped_already_index = 0;
2503 static int dumped_already_slots = 0;
2505 static void
2506 initialize_dumping ()
2508 dumped_already_index = 0;
2511 /* Get and print the node specified by FILENAME and NODENAME to STREAM.
2512 If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
2513 in the menu of each node dumped. */
2514 static void
2515 dump_node_to_stream (filename, nodename, stream, dump_subnodes)
2516 char *filename, *nodename;
2517 FILE *stream;
2518 int dump_subnodes;
2520 register int i;
2521 NODE *node;
2523 node = info_get_node (filename, nodename);
2525 if (!node)
2527 if (info_recent_file_error)
2528 info_error (info_recent_file_error);
2529 else
2531 if (filename && *nodename != '(')
2532 info_error
2533 (CANT_FILE_NODE, filename_non_directory (filename), nodename);
2534 else
2535 info_error (CANT_FIND_NODE, nodename);
2537 return;
2540 /* If we have already dumped this node, don't dump it again. */
2541 for (i = 0; i < dumped_already_index; i++)
2542 if (strcmp (node->nodename, dumped_already[i]) == 0)
2544 free (node);
2545 return;
2547 add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
2548 dumped_already_slots, 50, char *);
2550 #if defined (VERBOSE_NODE_DUMPING)
2551 /* Maybe we should print some information about the node being output. */
2552 if (node->filename)
2553 info_error (_("Writing node \"(%s)%s\"..."),
2554 filename_non_directory (node->filename), node->nodename);
2555 else
2556 info_error (_("Writing node \"%s\"..."), node->nodename);
2557 #endif /* VERBOSE_NODE_DUMPING */
2559 write_node_to_stream (node, stream);
2561 /* If we are dumping subnodes, get the list of menu items in this node,
2562 and dump each one recursively. */
2563 if (dump_subnodes)
2565 REFERENCE **menu = (REFERENCE **)NULL;
2567 /* If this node is an Index, do not dump the menu references. */
2568 if (string_in_line ("Index", node->nodename) == -1)
2569 menu = info_menu_of_node (node);
2571 if (menu)
2573 for (i = 0; menu[i]; i++)
2575 /* We don't dump Info files which are different than the
2576 current one. */
2577 if (!menu[i]->filename)
2578 dump_node_to_stream
2579 (filename, menu[i]->nodename, stream, dump_subnodes);
2581 info_free_references (menu);
2585 free (node);
2588 /* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
2589 the nodes which appear in the menu of each node dumped. */
2590 void
2591 dump_node_to_file (node, filename, dump_subnodes)
2592 NODE *node;
2593 char *filename;
2594 int dump_subnodes;
2596 FILE *output_stream;
2597 char *nodes_filename;
2599 /* Get the stream to print this node to. Special case of an output
2600 filename of "-" means to dump the nodes to stdout. */
2601 if (strcmp (filename, "-") == 0)
2602 output_stream = stdout;
2603 else
2604 output_stream = fopen (filename, "w");
2606 if (!output_stream)
2608 info_error (_("Could not create output file \"%s\"."), filename);
2609 return;
2612 if (node->parent)
2613 nodes_filename = node->parent;
2614 else
2615 nodes_filename = node->filename;
2617 initialize_dumping ();
2618 dump_node_to_stream
2619 (nodes_filename, node->nodename, output_stream, dump_subnodes);
2621 if (output_stream != stdout)
2622 fclose (output_stream);
2624 #if defined (VERBOSE_NODE_DUMPING)
2625 info_error (_("Done."));
2626 #endif /* VERBOSE_NODE_DUMPING */
2629 #if !defined (DEFAULT_INFO_PRINT_COMMAND)
2630 # define DEFAULT_INFO_PRINT_COMMAND "lpr"
2631 #endif /* !DEFAULT_INFO_PRINT_COMMAND */
2633 DECLARE_INFO_COMMAND (info_print_node,
2634 _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
2636 print_node (window->node);
2639 /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
2640 void
2641 print_node (node)
2642 NODE *node;
2644 FILE *printer_pipe;
2645 char *print_command = getenv ("INFO_PRINT_COMMAND");
2647 if (!print_command || !*print_command)
2648 print_command = DEFAULT_INFO_PRINT_COMMAND;
2650 printer_pipe = popen (print_command, "w");
2652 if (!printer_pipe)
2654 info_error (_("Cannot open pipe to \"%s\"."), print_command);
2655 return;
2658 #if defined (VERBOSE_NODE_DUMPING)
2659 /* Maybe we should print some information about the node being output. */
2660 if (node->filename)
2661 info_error (_("Printing node \"(%s)%s\"..."),
2662 filename_non_directory (node->filename), node->nodename);
2663 else
2664 info_error (_("Printing node \"%s\"..."), node->nodename);
2665 #endif /* VERBOSE_NODE_DUMPING */
2667 write_node_to_stream (node, printer_pipe);
2668 pclose (printer_pipe);
2670 #if defined (VERBOSE_NODE_DUMPING)
2671 info_error (_("Done."));
2672 #endif /* VERBOSE_NODE_DUMPING */
2675 static void
2676 write_node_to_stream (node, stream)
2677 NODE *node;
2678 FILE *stream;
2680 fwrite (node->contents, 1, node->nodelen, stream);
2683 /* **************************************************************** */
2684 /* */
2685 /* Info Searching Commands */
2686 /* */
2687 /* **************************************************************** */
2689 /* Variable controlling the garbage collection of files briefly visited
2690 during searches. Such files are normally gc'ed, unless they were
2691 compressed to begin with. If this variable is non-zero, it says
2692 to gc even those file buffer contents which had to be uncompressed. */
2693 int gc_compressed_files = 0;
2695 static void info_gc_file_buffers ();
2697 static char *search_string = (char *)NULL;
2698 static int search_string_index = 0;
2699 static int search_string_size = 0;
2700 static int isearch_is_active = 0;
2702 /* Return the file buffer which belongs to WINDOW's node. */
2703 FILE_BUFFER *
2704 file_buffer_of_window (window)
2705 WINDOW *window;
2707 /* If this window has no node, then it has no file buffer. */
2708 if (!window->node)
2709 return ((FILE_BUFFER *)NULL);
2711 if (window->node->parent)
2712 return (info_find_file (window->node->parent));
2714 if (window->node->filename)
2715 return (info_find_file (window->node->filename));
2717 return ((FILE_BUFFER *)NULL);
2720 /* Search for STRING in NODE starting at START. Return -1 if the string
2721 was not found, or the location of the string if it was. If WINDOW is
2722 passed as non-null, set the window's node to be NODE, its point to be
2723 the found string, and readjust the window's pagetop. Final argument
2724 DIR says which direction to search in. If it is positive, search
2725 forward, else backwards. */
2726 long
2727 info_search_in_node (string, node, start, window, dir)
2728 char *string;
2729 NODE *node;
2730 long start;
2731 WINDOW *window;
2732 int dir;
2734 SEARCH_BINDING binding;
2735 long offset;
2737 binding.buffer = node->contents;
2738 binding.start = start;
2739 binding.end = node->nodelen;
2740 binding.flags = S_FoldCase;
2742 if (dir < 0)
2744 binding.end = 0;
2745 binding.flags |= S_SkipDest;
2748 if (binding.start < 0)
2749 return (-1);
2751 /* For incremental searches, we always wish to skip past the string. */
2752 if (isearch_is_active)
2753 binding.flags |= S_SkipDest;
2755 offset = search (string, &binding);
2757 if (offset != -1 && window)
2759 set_remembered_pagetop_and_point (window);
2760 if (window->node != node)
2761 window_set_node_of_window (window, node);
2762 window->point = offset;
2763 window_adjust_pagetop (window);
2765 return (offset);
2768 /* Search NODE, looking for the largest possible match of STRING. Start the
2769 search at START. Return the absolute position of the match, or -1, if
2770 no part of the string could be found. */
2771 long
2772 info_target_search_node (node, string, start)
2773 NODE *node;
2774 char *string;
2775 long start;
2777 register int i;
2778 long offset;
2779 char *target;
2781 target = xstrdup (string);
2782 i = strlen (target);
2784 /* Try repeatedly searching for this string while removing words from
2785 the end of it. */
2786 while (i)
2788 target[i] = '\0';
2789 offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1);
2791 if (offset != -1)
2792 break;
2794 /* Delete the last word from TARGET. */
2795 for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
2797 free (target);
2798 return (offset);
2801 /* Search for STRING starting in WINDOW at point. If the string is found
2802 in this node, set point to that position. Otherwise, get the file buffer
2803 associated with WINDOW's node, and search through each node in that file.
2804 If the search fails, return non-zero, else zero. Side-effect window
2805 leaving the node and point where the string was found current. */
2806 static char *last_searched_for_string = (char *)NULL;
2807 static int
2808 info_search_internal (string, window, dir)
2809 char *string;
2810 WINDOW *window;
2811 int dir;
2813 register int i;
2814 FILE_BUFFER *file_buffer;
2815 char *initial_nodename;
2816 long ret, start = 0;
2818 file_buffer = file_buffer_of_window (window);
2819 initial_nodename = window->node->nodename;
2821 if ((info_last_executed_command == info_search) &&
2822 (last_searched_for_string) &&
2823 (strcmp (last_searched_for_string, string) == 0))
2825 ret = info_search_in_node
2826 (string, window->node, window->point + dir, window, dir);
2828 else
2830 ret = info_search_in_node
2831 (string, window->node, window->point, window, dir);
2834 maybe_free (last_searched_for_string);
2835 last_searched_for_string = xstrdup (string);
2837 if (ret != -1)
2839 /* We won! */
2840 if (!echo_area_is_active && !isearch_is_active)
2841 window_clear_echo_area ();
2842 return (0);
2845 /* The string wasn't found in the current node. Search through the
2846 window's file buffer, iff the current node is not "*". */
2847 if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
2848 return (-1);
2850 /* If this file has tags, search through every subfile, starting at
2851 this node's subfile and node. Otherwise, search through the
2852 file's node list. */
2853 if (file_buffer->tags)
2855 register int current_tag, number_of_tags;
2856 char *last_subfile;
2857 TAG *tag;
2859 /* Find number of tags and current tag. */
2860 last_subfile = (char *)NULL;
2861 for (i = 0; file_buffer->tags[i]; i++)
2862 if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
2864 current_tag = i;
2865 last_subfile = file_buffer->tags[i]->filename;
2868 number_of_tags = i;
2870 /* If there is no last_subfile, our tag wasn't found. */
2871 if (!last_subfile)
2872 return (-1);
2874 /* Search through subsequent nodes, wrapping around to the top
2875 of the info file until we find the string or return to this
2876 window's node and point. */
2877 while (1)
2879 NODE *node;
2881 /* Allow C-g to quit the search, failing it if pressed. */
2882 return_if_control_g (-1);
2884 current_tag += dir;
2886 if (current_tag < 0)
2887 current_tag = number_of_tags - 1;
2888 else if (current_tag == number_of_tags)
2889 current_tag = 0;
2891 tag = file_buffer->tags[current_tag];
2893 if (!echo_area_is_active && (last_subfile != tag->filename))
2895 window_message_in_echo_area
2896 (_("Searching subfile \"%s\"..."),
2897 filename_non_directory (tag->filename));
2899 last_subfile = tag->filename;
2902 node = info_get_node (file_buffer->filename, tag->nodename);
2904 if (!node)
2906 /* If not doing i-search... */
2907 if (!echo_area_is_active)
2909 if (info_recent_file_error)
2910 info_error (info_recent_file_error);
2911 else
2912 info_error (CANT_FILE_NODE,
2913 filename_non_directory (file_buffer->filename),
2914 tag->nodename);
2916 return (-1);
2919 if (dir < 0)
2920 start = tag->nodelen;
2922 ret =
2923 info_search_in_node (string, node, start, window, dir);
2925 /* Did we find the string in this node? */
2926 if (ret != -1)
2928 /* Yes! We win. */
2929 remember_window_and_node (window, node);
2930 if (!echo_area_is_active)
2931 window_clear_echo_area ();
2932 return (0);
2935 /* No. Free this node, and make sure that we haven't passed
2936 our starting point. */
2937 free (node);
2939 if (strcmp (initial_nodename, tag->nodename) == 0)
2940 return (-1);
2943 return (-1);
2946 DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
2948 char *line, *prompt;
2949 int result, old_pagetop;
2950 int direction;
2952 if (count < 0)
2953 direction = -1;
2954 else
2955 direction = 1;
2957 /* Read a string from the user, defaulting the search to SEARCH_STRING. */
2958 if (!search_string)
2960 search_string = (char *)xmalloc (search_string_size = 100);
2961 search_string[0] = '\0';
2964 prompt = (char *)xmalloc (50 + strlen (search_string));
2966 sprintf (prompt, _("%s for string [%s]: "),
2967 direction < 0 ? _("Search backward") : _("Search"),
2968 search_string);
2970 line = info_read_in_echo_area (window, prompt);
2971 free (prompt);
2973 if (!line)
2975 info_abort_key ();
2976 return;
2979 if (*line)
2981 if (strlen (line) + 1 > search_string_size)
2982 search_string = (char *)
2983 xrealloc (search_string, (search_string_size += 50 + strlen (line)));
2985 strcpy (search_string, line);
2986 search_string_index = strlen (line);
2987 free (line);
2990 old_pagetop = active_window->pagetop;
2991 result = info_search_internal (search_string, active_window, direction);
2993 if (result != 0 && !info_error_was_printed)
2994 info_error (_("Search failed."));
2995 else if (old_pagetop != active_window->pagetop)
2997 int new_pagetop;
2999 new_pagetop = active_window->pagetop;
3000 active_window->pagetop = old_pagetop;
3001 set_window_pagetop (active_window, new_pagetop);
3002 if (auto_footnotes_p)
3003 info_get_or_remove_footnotes (active_window);
3006 /* Perhaps free the unreferenced file buffers that were searched, but
3007 not retained. */
3008 info_gc_file_buffers ();
3011 /* **************************************************************** */
3012 /* */
3013 /* Incremental Searching */
3014 /* */
3015 /* **************************************************************** */
3017 static void incremental_search ();
3019 DECLARE_INFO_COMMAND (isearch_forward,
3020 _("Search interactively for a string as you type it"))
3022 incremental_search (window, count, key);
3025 DECLARE_INFO_COMMAND (isearch_backward,
3026 _("Search interactively for a string as you type it"))
3028 incremental_search (window, -count, key);
3031 /* Incrementally search for a string as it is typed. */
3032 /* The last accepted incremental search string. */
3033 static char *last_isearch_accepted = (char *)NULL;
3035 /* The current incremental search string. */
3036 static char *isearch_string = (char *)NULL;
3037 static int isearch_string_index = 0;
3038 static int isearch_string_size = 0;
3039 static unsigned char isearch_terminate_search_key = ESC;
3041 /* Structure defining the current state of an incremental search. */
3042 typedef struct {
3043 WINDOW_STATE_DECL; /* The node, pagetop and point. */
3044 int search_index; /* Offset of the last char in the search string. */
3045 int direction; /* The direction that this search is heading in. */
3046 int failing; /* Whether or not this search failed. */
3047 } SEARCH_STATE;
3049 /* Array of search states. */
3050 static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
3051 static int isearch_states_index = 0;
3052 static int isearch_states_slots = 0;
3054 /* Push the state of this search. */
3055 static void
3056 push_isearch (window, search_index, direction, failing)
3057 WINDOW *window;
3058 int search_index, direction, failing;
3060 SEARCH_STATE *state;
3062 state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
3063 window_get_state (window, state);
3064 state->search_index = search_index;
3065 state->direction = direction;
3066 state->failing = failing;
3068 add_pointer_to_array (state, isearch_states_index, isearch_states,
3069 isearch_states_slots, 20, SEARCH_STATE *);
3072 /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3073 static void
3074 pop_isearch (window, search_index, direction, failing)
3075 WINDOW *window;
3076 int *search_index, *direction, *failing;
3078 SEARCH_STATE *state;
3080 if (isearch_states_index)
3082 isearch_states_index--;
3083 state = isearch_states[isearch_states_index];
3084 window_set_state (window, state);
3085 *search_index = state->search_index;
3086 *direction = state->direction;
3087 *failing = state->failing;
3089 free (state);
3090 isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
3094 /* Free the memory used by isearch_states. */
3095 static void
3096 free_isearch_states ()
3098 register int i;
3100 for (i = 0; i < isearch_states_index; i++)
3102 free (isearch_states[i]);
3103 isearch_states[i] = (SEARCH_STATE *)NULL;
3105 isearch_states_index = 0;
3108 /* Display the current search in the echo area. */
3109 static void
3110 show_isearch_prompt (dir, string, failing_p)
3111 int dir;
3112 unsigned char *string;
3113 int failing_p;
3115 register int i;
3116 char *prefix, *prompt, *p_rep;
3117 int prompt_len, p_rep_index, p_rep_size;
3119 if (dir < 0)
3120 prefix = _("I-search backward: ");
3121 else
3122 prefix = _("I-search: ");
3124 p_rep_index = p_rep_size = 0;
3125 p_rep = (char *)NULL;
3126 for (i = 0; string[i]; i++)
3128 char *rep;
3130 switch (string[i])
3132 case ' ': rep = " "; break;
3133 case LFD: rep = "\\n"; break;
3134 case TAB: rep = "\\t"; break;
3135 default:
3136 rep = pretty_keyname (string[i]);
3138 if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3139 p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3141 strcpy (p_rep + p_rep_index, rep);
3142 p_rep_index += strlen (rep);
3145 prompt_len = strlen (prefix) + p_rep_index + 20;
3146 prompt = (char *)xmalloc (prompt_len);
3147 sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
3148 p_rep ? p_rep : "");
3150 window_message_in_echo_area ("%s", prompt);
3151 maybe_free (p_rep);
3152 free (prompt);
3153 display_cursor_at_point (active_window);
3156 static void
3157 incremental_search (window, count, ignore)
3158 WINDOW *window;
3159 int count;
3160 unsigned char ignore;
3162 unsigned char key;
3163 int last_search_result, search_result, dir;
3164 SEARCH_STATE mystate, orig_state;
3166 if (count < 0)
3167 dir = -1;
3168 else
3169 dir = 1;
3171 last_search_result = search_result = 0;
3173 window_get_state (window, &orig_state);
3175 isearch_string_index = 0;
3176 if (!isearch_string_size)
3177 isearch_string = (char *)xmalloc (isearch_string_size = 50);
3179 /* Show the search string in the echo area. */
3180 isearch_string[isearch_string_index] = '\0';
3181 show_isearch_prompt (dir, isearch_string, search_result);
3183 isearch_is_active = 1;
3185 while (isearch_is_active)
3187 VFunction *func = (VFunction *)NULL;
3188 int quoted = 0;
3190 /* If a recent display was interrupted, then do the redisplay now if
3191 it is convenient. */
3192 if (!info_any_buffered_input_p () && display_was_interrupted_p)
3194 display_update_one_window (window);
3195 display_cursor_at_point (active_window);
3198 /* Read a character and dispatch on it. */
3199 key = info_get_input_char ();
3200 window_get_state (window, &mystate);
3202 if (key == DEL)
3204 /* User wants to delete one level of search? */
3205 if (!isearch_states_index)
3207 terminal_ring_bell ();
3208 continue;
3210 else
3212 pop_isearch
3213 (window, &isearch_string_index, &dir, &search_result);
3214 isearch_string[isearch_string_index] = '\0';
3215 show_isearch_prompt (dir, isearch_string, search_result);
3216 goto after_search;
3219 else if (key == Control ('q'))
3221 key = info_get_input_char ();
3222 quoted = 1;
3225 /* We are about to search again, or quit. Save the current search. */
3226 push_isearch (window, isearch_string_index, dir, search_result);
3228 if (quoted)
3229 goto insert_and_search;
3231 if (!Meta_p (key) || (ISO_Latin_p && key < 160))
3233 func = window->keymap[key].function;
3235 /* If this key invokes an incremental search, then this means that
3236 we will either search again in the same direction, search
3237 again in the reverse direction, or insert the last search
3238 string that was accepted through incremental searching. */
3239 if (func == isearch_forward || func == isearch_backward)
3241 if ((func == isearch_forward && dir > 0) ||
3242 (func == isearch_backward && dir < 0))
3244 /* If the user has typed no characters, then insert the
3245 last successful search into the current search string. */
3246 if (isearch_string_index == 0)
3248 /* Of course, there must be something to insert. */
3249 if (last_isearch_accepted)
3251 if (strlen (last_isearch_accepted) + 1 >=
3252 isearch_string_size)
3253 isearch_string = (char *)
3254 xrealloc (isearch_string,
3255 isearch_string_size += 10 +
3256 strlen (last_isearch_accepted));
3257 strcpy (isearch_string, last_isearch_accepted);
3258 isearch_string_index = strlen (isearch_string);
3259 goto search_now;
3261 else
3262 continue;
3264 else
3266 /* Search again in the same direction. This means start
3267 from a new place if the last search was successful. */
3268 if (search_result == 0)
3269 window->point += dir;
3272 else
3274 /* Reverse the direction of the search. */
3275 dir = -dir;
3278 else if (isprint (key) || func == (VFunction *)NULL)
3280 insert_and_search:
3282 if (isearch_string_index + 2 >= isearch_string_size)
3283 isearch_string = (char *)xrealloc
3284 (isearch_string, isearch_string_size += 100);
3286 isearch_string[isearch_string_index++] = key;
3287 isearch_string[isearch_string_index] = '\0';
3288 goto search_now;
3290 else if (func == info_abort_key)
3292 /* If C-g pressed, and the search is failing, pop the search
3293 stack back to the last unfailed search. */
3294 if (isearch_states_index && (search_result != 0))
3296 terminal_ring_bell ();
3297 while (isearch_states_index && (search_result != 0))
3298 pop_isearch
3299 (window, &isearch_string_index, &dir, &search_result);
3300 isearch_string[isearch_string_index] = '\0';
3301 show_isearch_prompt (dir, isearch_string, search_result);
3302 continue;
3304 else
3305 goto exit_search;
3307 else
3308 goto exit_search;
3310 else
3312 exit_search:
3313 /* The character is not printable, or it has a function which is
3314 non-null. Exit the search, remembering the search string. If
3315 the key is not the same as the isearch_terminate_search_key,
3316 then push it into pending input. */
3317 if (isearch_string_index && func != info_abort_key)
3319 maybe_free (last_isearch_accepted);
3320 last_isearch_accepted = xstrdup (isearch_string);
3323 if (key != isearch_terminate_search_key)
3324 info_set_pending_input (key);
3326 if (func == info_abort_key)
3328 if (isearch_states_index)
3329 window_set_state (window, &orig_state);
3332 if (!echo_area_is_active)
3333 window_clear_echo_area ();
3335 if (auto_footnotes_p)
3336 info_get_or_remove_footnotes (active_window);
3338 isearch_is_active = 0;
3339 continue;
3342 /* Search for the contents of isearch_string. */
3343 search_now:
3344 show_isearch_prompt (dir, isearch_string, search_result);
3346 if (search_result == 0)
3348 /* Check to see if the current search string is right here. If
3349 we are looking at it, then don't bother calling the search
3350 function. */
3351 if (((dir < 0) &&
3352 (strncasecmp (window->node->contents + window->point,
3353 isearch_string, isearch_string_index) == 0)) ||
3354 ((dir > 0) &&
3355 ((window->point - isearch_string_index) >= 0) &&
3356 (strncasecmp (window->node->contents +
3357 (window->point - (isearch_string_index - 1)),
3358 isearch_string, isearch_string_index) == 0)))
3360 if (dir > 0)
3361 window->point++;
3363 else
3364 search_result = info_search_internal (isearch_string, window, dir);
3367 /* If this search failed, and we didn't already have a failed search,
3368 then ring the terminal bell. */
3369 if (search_result != 0 && last_search_result == 0)
3370 terminal_ring_bell ();
3372 after_search:
3373 show_isearch_prompt (dir, isearch_string, search_result);
3375 if (search_result == 0)
3377 if ((mystate.node == window->node) &&
3378 (mystate.pagetop != window->pagetop))
3380 int newtop = window->pagetop;
3381 window->pagetop = mystate.pagetop;
3382 set_window_pagetop (window, newtop);
3384 display_update_one_window (window);
3385 display_cursor_at_point (window);
3388 last_search_result = search_result;
3391 /* Free the memory used to remember each search state. */
3392 free_isearch_states ();
3394 /* Perhaps GC some file buffers. */
3395 info_gc_file_buffers ();
3397 /* After searching, leave the window in the correct state. */
3398 if (!echo_area_is_active)
3399 window_clear_echo_area ();
3402 /* GC some file buffers. A file buffer can be gc-ed if there we have
3403 no nodes in INFO_WINDOWS that reference this file buffer's contents.
3404 Garbage collecting a file buffer means to free the file buffers
3405 contents. */
3406 static void
3407 info_gc_file_buffers ()
3409 register int fb_index, iw_index, i;
3410 register FILE_BUFFER *fb;
3411 register INFO_WINDOW *iw;
3413 if (!info_loaded_files)
3414 return;
3416 for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
3418 int fb_referenced_p = 0;
3420 /* If already gc-ed, do nothing. */
3421 if (!fb->contents)
3422 continue;
3424 /* If this file had to be uncompressed, check to see if we should
3425 gc it. This means that the user-variable "gc-compressed-files"
3426 is non-zero. */
3427 if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
3428 continue;
3430 /* If this file's contents are not gc-able, move on. */
3431 if (fb->flags & N_CannotGC)
3432 continue;
3434 /* Check each INFO_WINDOW to see if it has any nodes which reference
3435 this file. */
3436 for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
3438 for (i = 0; iw->nodes && iw->nodes[i]; i++)
3440 if ((strcmp (fb->fullpath, iw->nodes[i]->filename) == 0) ||
3441 (strcmp (fb->filename, iw->nodes[i]->filename) == 0))
3443 fb_referenced_p = 1;
3444 break;
3449 /* If this file buffer wasn't referenced, free its contents. */
3450 if (!fb_referenced_p)
3452 free (fb->contents);
3453 fb->contents = (char *)NULL;
3458 /* **************************************************************** */
3459 /* */
3460 /* Traversing and Selecting References */
3461 /* */
3462 /* **************************************************************** */
3464 /* Move to the next or previous cross reference in this node. */
3465 static void
3466 info_move_to_xref (window, count, key, dir)
3467 WINDOW *window;
3468 int count;
3469 unsigned char key;
3470 int dir;
3472 long firstmenu, firstxref;
3473 long nextmenu, nextxref;
3474 long placement = -1;
3475 long start = 0;
3476 NODE *node = window->node;
3478 if (dir < 0)
3479 start = node->nodelen;
3481 /* This search is only allowed to fail if there is no menu or cross
3482 reference in the current node. Otherwise, the first menu or xref
3483 found is moved to. */
3485 firstmenu = info_search_in_node
3486 (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir);
3488 /* FIRSTMENU may point directly to the line defining the menu. Skip that
3489 and go directly to the first item. */
3491 if (firstmenu != -1)
3493 char *text = node->contents + firstmenu;
3495 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
3496 firstmenu = info_search_in_node
3497 (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir);
3500 firstxref =
3501 info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir);
3503 #if defined (HANDLE_MAN_PAGES)
3504 if ((firstxref == -1) && (node->flags & N_IsManPage))
3506 firstxref = locate_manpage_xref (node, start, dir);
3508 #endif /* HANDLE_MAN_PAGES */
3510 if (firstmenu == -1 && firstxref == -1)
3512 info_error (_("No cross references in this node."));
3513 return;
3516 /* There is at least one cross reference or menu entry in this node.
3517 Try hard to find the next available one. */
3519 nextmenu = info_search_in_node
3520 (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir);
3522 nextxref = info_search_in_node
3523 (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir);
3525 #if defined (HANDLE_MAN_PAGES)
3526 if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
3527 nextxref = locate_manpage_xref (node, window->point + dir, dir);
3528 #endif /* HANDLE_MAN_PAGES */
3530 /* Ignore "Menu:" as a menu item. */
3531 if (nextmenu != -1)
3533 char *text = node->contents + nextmenu;
3535 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
3536 nextmenu = info_search_in_node
3537 (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir);
3540 /* If there is both a next menu entry, and a next xref entry, choose the
3541 one which occurs first. Otherwise, select the one which actually
3542 appears in this node following point. */
3543 if (nextmenu != -1 && nextxref != -1)
3545 if (((dir == 1) && (nextmenu < nextxref)) ||
3546 ((dir == -1) && (nextmenu > nextxref)))
3547 placement = nextmenu + 1;
3548 else
3549 placement = nextxref;
3551 else if (nextmenu != -1)
3552 placement = nextmenu + 1;
3553 else if (nextxref != -1)
3554 placement = nextxref;
3556 /* If there was neither a menu or xref entry appearing in this node after
3557 point, choose the first menu or xref entry appearing in this node. */
3558 if (placement == -1)
3560 if (firstmenu != -1 && firstxref != -1)
3562 if (((dir == 1) && (firstmenu < firstxref)) ||
3563 ((dir == -1) && (firstmenu > firstxref)))
3564 placement = firstmenu + 1;
3565 else
3566 placement = firstxref;
3568 else if (firstmenu != -1)
3569 placement = firstmenu + 1;
3570 else
3571 placement = firstxref;
3573 window->point = placement;
3574 window_adjust_pagetop (window);
3575 window->flags |= W_UpdateWindow;
3578 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
3579 _("Move to the previous cross reference"))
3581 if (count < 0)
3582 info_move_to_prev_xref (window, -count, key);
3583 else
3584 info_move_to_xref (window, count, key, -1);
3587 DECLARE_INFO_COMMAND (info_move_to_next_xref,
3588 _("Move to the next cross reference"))
3590 if (count < 0)
3591 info_move_to_next_xref (window, -count, key);
3592 else
3593 info_move_to_xref (window, count, key, 1);
3596 /* Select the menu item or reference that appears on this line. */
3597 DECLARE_INFO_COMMAND (info_select_reference_this_line,
3598 _("Select reference or menu item appearing on this line"))
3600 char *line;
3601 NODE *orig;
3603 line = window->line_starts[window_line_of_point (window)];
3604 orig = window->node;
3606 /* If this line contains a menu item, select that one. */
3607 if (strncmp ("* ", line, 2) == 0)
3608 info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
3609 else
3610 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
3613 /* **************************************************************** */
3614 /* */
3615 /* Miscellaneous Info Commands */
3616 /* */
3617 /* **************************************************************** */
3619 /* What to do when C-g is pressed in a window. */
3620 DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
3622 /* If error printing doesn't oridinarily ring the bell, do it now,
3623 since C-g always rings the bell. Otherwise, let the error printer
3624 do it. */
3625 if (!info_error_rings_bell_p)
3626 terminal_ring_bell ();
3627 info_error (_("Quit"));
3629 info_initialize_numeric_arg ();
3630 info_clear_pending_input ();
3631 info_last_executed_command = (VFunction *)NULL;
3634 /* Move the cursor to the desired line of the window. */
3635 DECLARE_INFO_COMMAND (info_move_to_window_line,
3636 _("Move to the cursor to a specific line of the window"))
3638 int line;
3640 /* With no numeric argument of any kind, default to the center line. */
3641 if (!info_explicit_arg && count == 1)
3642 line = (window->height / 2) + window->pagetop;
3643 else
3645 if (count < 0)
3646 line = (window->height + count) + window->pagetop;
3647 else
3648 line = window->pagetop + count;
3651 /* If the line doesn't appear in this window, make it do so. */
3652 if ((line - window->pagetop) >= window->height)
3653 line = window->pagetop + (window->height - 1);
3655 /* If the line is too small, make it fit. */
3656 if (line < window->pagetop)
3657 line = window->pagetop;
3659 /* If the selected line is past the bottom of the node, force it back. */
3660 if (line >= window->line_count)
3661 line = window->line_count - 1;
3663 window->point = (window->line_starts[line] - window->node->contents);
3666 /* Clear the screen and redraw its contents. Given a numeric argument,
3667 move the line the cursor is on to the COUNT'th line of the window. */
3668 DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
3670 if ((!info_explicit_arg && count == 1) || echo_area_is_active)
3672 terminal_clear_screen ();
3673 display_clear_display (the_display);
3674 window_mark_chain (windows, W_UpdateWindow);
3675 display_update_display (windows);
3677 else
3679 int desired_line, point_line;
3680 int new_pagetop;
3682 point_line = window_line_of_point (window) - window->pagetop;
3684 if (count < 0)
3685 desired_line = window->height + count;
3686 else
3687 desired_line = count;
3689 if (desired_line < 0)
3690 desired_line = 0;
3692 if (desired_line >= window->height)
3693 desired_line = window->height - 1;
3695 if (desired_line == point_line)
3696 return;
3698 new_pagetop = window->pagetop + (point_line - desired_line);
3700 set_window_pagetop (window, new_pagetop);
3703 /* This command does nothing. It is the fact that a key is bound to it
3704 that has meaning. See the code at the top of info_session (). */
3705 DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
3709 /* **************************************************************** */
3710 /* */
3711 /* Reading Keys and Dispatching on Them */
3712 /* */
3713 /* **************************************************************** */
3715 /* Declaration only. Special cased in info_dispatch_on_key (). */
3716 DECLARE_INFO_COMMAND (info_do_lowercase_version, "")
3719 static void
3720 dispatch_error (keyseq)
3721 char *keyseq;
3723 char *rep;
3725 rep = pretty_keyseq (keyseq);
3727 if (!echo_area_is_active)
3728 info_error (_("Unknown command (%s)."), rep);
3729 else
3731 char *temp;
3733 temp = (char *)xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid")));
3735 sprintf (temp, _("\"%s\" is invalid"), rep);
3736 terminal_ring_bell ();
3737 inform_in_echo_area (temp);
3738 free (temp);
3742 /* Keeping track of key sequences. */
3743 static char *info_keyseq = (char *)NULL;
3744 static char keyseq_rep[100];
3745 static int info_keyseq_index = 0;
3746 static int info_keyseq_size = 0;
3747 static int info_keyseq_displayed_p = 0;
3749 /* Initialize the length of the current key sequence. */
3750 void
3751 initialize_keyseq ()
3753 info_keyseq_index = 0;
3754 info_keyseq_displayed_p = 0;
3757 /* Add CHARACTER to the current key sequence. */
3758 void
3759 add_char_to_keyseq (character)
3760 char character;
3762 if (info_keyseq_index + 2 >= info_keyseq_size)
3763 info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
3765 info_keyseq[info_keyseq_index++] = character;
3766 info_keyseq[info_keyseq_index] = '\0';
3769 /* Return the pretty printable string which represents KEYSEQ. */
3770 char *
3771 pretty_keyseq (keyseq)
3772 char *keyseq;
3774 register int i;
3776 keyseq_rep[0] = '\0';
3778 for (i = 0; keyseq[i]; i++)
3780 sprintf (keyseq_rep + strlen (keyseq_rep), "%s%s",
3781 strlen (keyseq_rep) ? " " : "",
3782 pretty_keyname (keyseq[i]));
3785 return (keyseq_rep);
3788 /* Display the current value of info_keyseq. If argument EXPECTING is
3789 non-zero, input is expected to be read after the key sequence is
3790 displayed, so add an additional prompting character to the sequence. */
3791 void
3792 display_info_keyseq (expecting_future_input)
3793 int expecting_future_input;
3795 char *rep;
3797 rep = pretty_keyseq (info_keyseq);
3798 if (expecting_future_input)
3799 strcat (rep, "-");
3801 if (echo_area_is_active)
3802 inform_in_echo_area (rep);
3803 else
3805 window_message_in_echo_area (rep);
3806 display_cursor_at_point (active_window);
3808 info_keyseq_displayed_p = 1;
3811 /* Called by interactive commands to read a keystroke. */
3812 unsigned char
3813 info_get_another_input_char ()
3815 int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
3817 /* If there isn't any input currently available, then wait a
3818 moment looking for input. If we don't get it fast enough,
3819 prompt a little bit with the current key sequence. */
3820 if (!info_keyseq_displayed_p)
3822 ready = 1;
3823 if (!info_any_buffered_input_p () &&
3824 !info_input_pending_p ())
3826 #if defined (FD_SET)
3827 struct timeval timer;
3828 fd_set readfds;
3830 FD_ZERO (&readfds);
3831 FD_SET (fileno (info_input_stream), &readfds);
3832 timer.tv_sec = 1;
3833 timer.tv_usec = 750;
3834 ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
3835 #else
3836 ready = 0;
3837 #endif /* FD_SET */
3841 if (!ready)
3842 display_info_keyseq (1);
3844 return (info_get_input_char ());
3847 /* Do the command associated with KEY in MAP. If the associated command is
3848 really a keymap, then read another key, and dispatch into that map. */
3849 void
3850 info_dispatch_on_key (key, map)
3851 unsigned char key;
3852 Keymap map;
3854 if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
3856 if (map[ESC].type == ISKMAP)
3858 map = (Keymap)map[ESC].function;
3859 add_char_to_keyseq (ESC);
3860 key = UnMeta (key);
3861 info_dispatch_on_key (key, map);
3863 else
3865 dispatch_error (info_keyseq);
3867 return;
3870 switch (map[key].type)
3872 case ISFUNC:
3874 VFunction *func;
3876 func = map[key].function;
3877 if (func != (VFunction *)NULL)
3879 /* Special case info_do_lowercase_version (). */
3880 if (func == info_do_lowercase_version)
3882 info_dispatch_on_key (tolower (key), map);
3883 return;
3886 add_char_to_keyseq (key);
3888 if (info_keyseq_displayed_p)
3889 display_info_keyseq (0);
3892 WINDOW *where;
3894 where = active_window;
3895 (*map[key].function)
3896 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
3898 /* If we have input pending, then the last command was a prefix
3899 command. Don't change the value of the last function vars.
3900 Otherwise, remember the last command executed in the var
3901 appropriate to the window in which it was executed. */
3902 if (!info_input_pending_p ())
3904 if (where == the_echo_area)
3905 ea_last_executed_command = map[key].function;
3906 else
3907 info_last_executed_command = map[key].function;
3911 else
3913 add_char_to_keyseq (key);
3914 dispatch_error (info_keyseq);
3915 return;
3918 break;
3920 case ISKMAP:
3921 add_char_to_keyseq (key);
3922 if (map[key].function != (VFunction *)NULL)
3924 unsigned char newkey;
3926 newkey = info_get_another_input_char ();
3927 info_dispatch_on_key (newkey, (Keymap)map[key].function);
3929 else
3931 dispatch_error (info_keyseq);
3932 return;
3934 break;
3938 /* **************************************************************** */
3939 /* */
3940 /* Numeric Arguments */
3941 /* */
3942 /* **************************************************************** */
3944 /* Handle C-u style numeric args, as well as M--, and M-digits. */
3946 /* Non-zero means that an explicit argument has been passed to this
3947 command, as in C-u C-v. */
3948 int info_explicit_arg = 0;
3950 /* The sign of the numeric argument. */
3951 int info_numeric_arg_sign = 1;
3953 /* The value of the argument itself. */
3954 int info_numeric_arg = 1;
3956 /* Add the current digit to the argument in progress. */
3957 DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
3958 _("Add this digit to the current numeric argument"))
3960 info_numeric_arg_digit_loop (window, 0, key);
3963 /* C-u, universal argument. Multiply the current argument by 4.
3964 Read a key. If the key has nothing to do with arguments, then
3965 dispatch on it. If the key is the abort character then abort. */
3966 DECLARE_INFO_COMMAND (info_universal_argument,
3967 _("Start (or multiply by 4) the current numeric argument"))
3969 info_numeric_arg *= 4;
3970 info_numeric_arg_digit_loop (window, 0, 0);
3973 /* Create a default argument. */
3974 void
3975 info_initialize_numeric_arg ()
3977 info_numeric_arg = info_numeric_arg_sign = 1;
3978 info_explicit_arg = 0;
3981 DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
3982 _("Internally used by \\[universal-argument]"))
3984 unsigned char pure_key;
3985 Keymap keymap = window->keymap;
3987 while (1)
3989 if (key)
3990 pure_key = key;
3991 else
3993 if (display_was_interrupted_p && !info_any_buffered_input_p ())
3994 display_update_display (windows);
3996 if (active_window != the_echo_area)
3997 display_cursor_at_point (active_window);
3999 pure_key = key = info_get_another_input_char ();
4001 if (Meta_p (key))
4002 add_char_to_keyseq (ESC);
4004 add_char_to_keyseq (UnMeta (key));
4007 if (Meta_p (key))
4008 key = UnMeta (key);
4010 if (keymap[key].type == ISFUNC &&
4011 keymap[key].function == info_universal_argument)
4013 info_numeric_arg *= 4;
4014 key = 0;
4015 continue;
4018 if (isdigit (key))
4020 if (info_explicit_arg)
4021 info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
4022 else
4023 info_numeric_arg = (key - '0');
4024 info_explicit_arg = 1;
4026 else
4028 if (key == '-' && !info_explicit_arg)
4030 info_numeric_arg_sign = -1;
4031 info_numeric_arg = 1;
4033 else
4035 info_keyseq_index--;
4036 info_dispatch_on_key (pure_key, keymap);
4037 return;
4040 key = 0;
4044 /* **************************************************************** */
4045 /* */
4046 /* Input Character Buffering */
4047 /* */
4048 /* **************************************************************** */
4050 /* Character waiting to be read next. */
4051 static int pending_input_character = 0;
4053 /* How to make there be no pending input. */
4054 static void
4055 info_clear_pending_input ()
4057 pending_input_character = 0;
4060 /* How to set the pending input character. */
4061 static void
4062 info_set_pending_input (key)
4063 unsigned char key;
4065 pending_input_character = key;
4068 /* How to see if there is any pending input. */
4069 unsigned char
4070 info_input_pending_p ()
4072 return (pending_input_character);
4075 /* Largest number of characters that we can read in advance. */
4076 #define MAX_INFO_INPUT_BUFFERING 512
4078 static int pop_index = 0, push_index = 0;
4079 static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
4081 /* Add KEY to the buffer of characters to be read. */
4082 static void
4083 info_push_typeahead (key)
4084 unsigned char key;
4086 /* Flush all pending input in the case of C-g pressed. */
4087 if (key == Control ('g'))
4089 push_index = pop_index;
4090 info_set_pending_input (Control ('g'));
4092 else
4094 info_input_buffer[push_index++] = key;
4095 if (push_index >= sizeof (info_input_buffer))
4096 push_index = 0;
4100 /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4101 static int
4102 info_input_buffer_space_available ()
4104 if (pop_index > push_index)
4105 return (pop_index - push_index);
4106 else
4107 return (sizeof (info_input_buffer) - (push_index - pop_index));
4110 /* Get a key from the buffer of characters to be read.
4111 Return the key in KEY.
4112 Result is non-zero if there was a key, or 0 if there wasn't. */
4113 static int
4114 info_get_key_from_typeahead (key)
4115 unsigned char *key;
4117 if (push_index == pop_index)
4118 return (0);
4120 *key = info_input_buffer[pop_index++];
4122 if (pop_index >= sizeof (info_input_buffer))
4123 pop_index = 0;
4125 return (1);
4129 info_any_buffered_input_p ()
4131 info_gather_typeahead ();
4132 return (push_index != pop_index);
4135 /* If characters are available to be read, then read them and stuff them into
4136 info_input_buffer. Otherwise, do nothing. */
4137 void
4138 info_gather_typeahead ()
4140 register int i = 0;
4141 int tty, space_avail;
4142 long chars_avail;
4143 unsigned char input[MAX_INFO_INPUT_BUFFERING];
4145 tty = fileno (info_input_stream);
4146 chars_avail = 0;
4148 space_avail = info_input_buffer_space_available ();
4150 /* If we can just find out how many characters there are to read, do so. */
4151 #if defined (FIONREAD)
4153 ioctl (tty, FIONREAD, &chars_avail);
4155 if (chars_avail > space_avail)
4156 chars_avail = space_avail;
4158 if (chars_avail)
4159 chars_avail = read (tty, &input[0], chars_avail);
4161 #else /* !FIONREAD */
4162 # if defined (O_NDELAY)
4164 int flags;
4166 flags = fcntl (tty, F_GETFL, 0);
4168 fcntl (tty, F_SETFL, (flags | O_NDELAY));
4169 chars_avail = read (tty, &input[0], space_avail);
4170 fcntl (tty, F_SETFL, flags);
4172 if (chars_avail == -1)
4173 chars_avail = 0;
4175 # endif /* O_NDELAY */
4176 #endif /* !FIONREAD */
4178 while (i < chars_avail)
4180 info_push_typeahead (input[i]);
4181 i++;
4185 /* How to read a single character. */
4186 unsigned char
4187 info_get_input_char ()
4189 unsigned char keystroke;
4191 info_gather_typeahead ();
4193 if (pending_input_character)
4195 keystroke = pending_input_character;
4196 pending_input_character = 0;
4198 else if (info_get_key_from_typeahead (&keystroke) == 0)
4200 int rawkey;
4201 unsigned char c;
4202 int tty = fileno (info_input_stream);
4204 /* Using stream I/O causes FIONREAD etc to fail to work
4205 so unless someone can find a portable way of finding
4206 out how many characters are currently buffered, we
4207 should stay with away from stream I/O.
4208 --Egil Kvaleberg <egilk@sn.no>, January 1997. */
4209 #ifdef EINTR
4210 /* Keep reading if we got EINTR, so that we don't just exit.
4211 --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
4212 22 Dec 1997. */
4214 int n;
4216 n = read (tty, &c, 1);
4217 while (n == -1 && errno == EINTR);
4218 rawkey = n == 1 ? c : EOF;
4220 #else
4221 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
4222 #endif
4224 keystroke = rawkey;
4226 if (rawkey == EOF)
4228 if (info_input_stream != stdin)
4230 fclose (info_input_stream);
4231 info_input_stream = stdin;
4232 display_inhibited = 0;
4233 display_update_display (windows);
4234 display_cursor_at_point (active_window);
4235 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
4236 keystroke = rawkey;
4239 if (rawkey == EOF)
4241 terminal_unprep_terminal ();
4242 close_dribble_file ();
4243 exit (0);
4248 if (info_dribble_file)
4249 dribble (keystroke);
4251 return keystroke;