source: trunk/texinfo/info/session.c@ 2618

Last change on this file since 2618 was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

File size: 146.3 KB
Line 
1/* session.c -- user windowing interface to Info.
2 $Id: session.c,v 1.16 2004/12/14 00:15:36 karl Exp $
3
4 Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
5 Free Software Foundation, Inc.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 Originally written by Brian Fox (bfox@ai.mit.edu). */
22
23#include "info.h"
24#include "search.h"
25#include <sys/ioctl.h>
26
27#if defined (HAVE_SYS_TIME_H)
28# include <sys/time.h>
29# define HAVE_STRUCT_TIMEVAL
30#endif /* HAVE_SYS_TIME_H */
31
32#if defined (HANDLE_MAN_PAGES)
33# include "man.h"
34#endif
35
36static void info_clear_pending_input (void);
37static void info_set_pending_input (unsigned char key);
38static void info_handle_pointer (char *label, WINDOW *window);
39static void display_info_keyseq (int expecting_future_input);
40char *node_printed_rep (NODE *node);
41
42/* **************************************************************** */
43/* */
44/* Running an Info Session */
45/* */
46/* **************************************************************** */
47
48/* The place that we are reading input from. */
49static FILE *info_input_stream = NULL;
50
51/* The last executed command. */
52VFunction *info_last_executed_command = NULL;
53
54/* Becomes non-zero when 'q' is typed to an Info window. */
55int quit_info_immediately = 0;
56
57/* Array of structures describing for each window which nodes have been
58 visited in that window. */
59INFO_WINDOW **info_windows = NULL;
60
61/* Where to add the next window, if we need to add one. */
62static int info_windows_index = 0;
63
64/* Number of slots allocated to `info_windows'. */
65static int info_windows_slots = 0;
66
67void remember_window_and_node (WINDOW *window, NODE *node);
68void forget_window_and_nodes (WINDOW *window);
69void display_startup_message_and_start (void);
70
71/* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
72 For each loaded node, create a new window. Always split the largest of the
73 available windows. */
74void
75begin_multiple_window_info_session (char *filename, char **nodenames)
76{
77 register int i;
78 WINDOW *window = (WINDOW *)NULL;
79
80 for (i = 0; nodenames[i]; i++)
81 {
82 NODE *node;
83
84 node = info_get_node (filename, nodenames[i]);
85
86 if (!node)
87 break;
88
89 /* If this is the first node, initialize the info session. */
90 if (!window)
91 {
92 initialize_info_session (node, 1);
93 window = active_window;
94 }
95 else
96 {
97 /* Find the largest window in WINDOWS, and make that be the active
98 one. Then split it and add our window and node to the list
99 of remembered windows and nodes. Then tile the windows. */
100 WINDOW *win, *largest = NULL;
101 int max_height = 0;
102
103 for (win = windows; win; win = win->next)
104 if (win->height > max_height)
105 {
106 max_height = win->height;
107 largest = win;
108 }
109
110 if (!largest)
111 {
112 display_update_display (windows);
113 info_error ((char *) msg_cant_find_window, NULL, NULL);
114 info_session ();
115 xexit (0);
116 }
117
118 active_window = largest;
119 window = window_make_window (node);
120 if (window)
121 {
122 window_tile_windows (TILE_INTERNALS);
123 remember_window_and_node (window, node);
124 }
125 else
126 {
127 display_update_display (windows);
128 info_error ((char *) msg_win_too_small, NULL, NULL);
129 info_session ();
130 xexit (0);
131 }
132 }
133 }
134 display_startup_message_and_start ();
135}
136
137/* Start an info session with INITIAL_NODE, and an error message in the echo
138 area made from FORMAT and ARG. */
139void
140begin_info_session_with_error (NODE *initial_node, char *format,
141 void *arg1, void *arg2)
142{
143 initialize_info_session (initial_node, 1);
144 info_error (format, arg1, arg2);
145 info_session ();
146}
147
148/* Start an info session with INITIAL_NODE. */
149void
150begin_info_session (NODE *initial_node)
151{
152 initialize_info_session (initial_node, 1);
153 display_startup_message_and_start ();
154}
155
156void
157display_startup_message_and_start (void)
158{
159 char *format;
160
161 format = replace_in_documentation
162 ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."),
163 0);
164
165 window_message_in_echo_area (format, VERSION, NULL);
166 info_session ();
167}
168
169/* Run an info session with an already initialized window and node. */
170void
171info_session (void)
172{
173 display_update_display (windows);
174 info_last_executed_command = NULL;
175 info_read_and_dispatch ();
176 /* On program exit, leave the cursor at the bottom of the window, and
177 restore the terminal I/O. */
178 terminal_goto_xy (0, screenheight - 1);
179 terminal_clear_to_eol ();
180 fflush (stdout);
181 terminal_unprep_terminal ();
182 close_dribble_file ();
183}
184
185/* Here is a window-location dependent event loop. Called from the
186 functions info_session (), and from read_xxx_in_echo_area (). */
187void
188info_read_and_dispatch (void)
189{
190 unsigned char key;
191 int done;
192 done = 0;
193
194 while (!done && !quit_info_immediately)
195 {
196 int lk = 0;
197
198 /* If we haven't just gone up or down a line, there is no
199 goal column for this window. */
200 if ((info_last_executed_command != (VFunction *) info_next_line) &&
201 (info_last_executed_command != (VFunction *) info_prev_line))
202 active_window->goal_column = -1;
203
204 if (echo_area_is_active)
205 {
206 lk = echo_area_last_command_was_kill;
207 echo_area_prep_read ();
208 }
209
210 if (!info_any_buffered_input_p ())
211 display_update_display (windows);
212
213 display_cursor_at_point (active_window);
214 info_initialize_numeric_arg ();
215
216 initialize_keyseq ();
217 key = info_get_input_char ();
218
219 /* No errors yet. We just read a character, that's all. Only clear
220 the echo_area if it is not currently active. */
221 if (!echo_area_is_active)
222 window_clear_echo_area ();
223
224 info_error_was_printed = 0;
225
226 /* Do the selected command. */
227 info_dispatch_on_key (key, active_window->keymap);
228
229 if (echo_area_is_active)
230 {
231 /* Echo area commands that do killing increment the value of
232 ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no
233 change in the value of this variable, the last command
234 executed was not a kill command. */
235 if (lk == echo_area_last_command_was_kill)
236 echo_area_last_command_was_kill = 0;
237
238 if (ea_last_executed_command == (VFunction *) ea_newline ||
239 info_aborted_echo_area)
240 {
241 ea_last_executed_command = (VFunction *)NULL;
242 done = 1;
243 }
244
245 if (info_last_executed_command == (VFunction *) info_quit)
246 quit_info_immediately = 1;
247 }
248 else if (info_last_executed_command == (VFunction *) info_quit)
249 done = 1;
250 }
251}
252
253/* Found in signals.c */
254extern void initialize_info_signal_handler (void );
255
256/* Initialize the first info session by starting the terminal, window,
257 and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
258void
259initialize_info_session (NODE *node, int clear_screen)
260{
261 char *term_name = getenv ("TERM");
262 terminal_initialize_terminal (term_name);
263
264 if (terminal_is_dumb_p)
265 {
266 if (!term_name)
267 term_name = "dumb";
268
269 info_error ((char *) msg_term_too_dumb, term_name, NULL);
270 xexit (1);
271 }
272
273 if (clear_screen)
274 {
275 terminal_prep_terminal ();
276 terminal_clear_screen ();
277 }
278
279 initialize_info_keymaps ();
280 window_initialize_windows (screenwidth, screenheight);
281 initialize_info_signal_handler ();
282 display_initialize_display (screenwidth, screenheight);
283 info_set_node_of_window (0, active_window, node);
284
285 /* Tell the window system how to notify us when a window needs to be
286 asynchronously deleted (e.g., user resizes window very small). */
287 window_deletion_notifier = (VFunction *) forget_window_and_nodes;
288
289 /* If input has not been redirected yet, make it come from unbuffered
290 standard input. */
291 if (!info_input_stream)
292 {
293 setbuf (stdin, NULL);
294 info_input_stream = stdin;
295 }
296
297 info_windows_initialized_p = 1;
298}
299
300/* Tell Info that input is coming from the file FILENAME. */
301void
302info_set_input_from_file (char *filename)
303{
304 FILE *stream;
305
306 /* Input may include binary characters. */
307 stream = fopen (filename, FOPEN_RBIN);
308
309 if (!stream)
310 return;
311
312 if ((info_input_stream != (FILE *)NULL) &&
313 (info_input_stream != stdin))
314 fclose (info_input_stream);
315
316 info_input_stream = stream;
317
318 if (stream != stdin)
319 display_inhibited = 1;
320}
321
322/* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
323static INFO_WINDOW *
324get_info_window_of_window (WINDOW *window)
325{
326 register int i;
327 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
328
329 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
330 if (info_win->window == window)
331 break;
332
333 return (info_win);
334}
335
336/* Reset the remembered pagetop and point of WINDOW to WINDOW's current
337 values if the window and node are the same as the current one being
338 displayed. */
339void
340set_remembered_pagetop_and_point (WINDOW *window)
341{
342 INFO_WINDOW *info_win;
343
344 info_win = get_info_window_of_window (window);
345
346 if (!info_win)
347 return;
348
349 if (info_win->nodes_index &&
350 (info_win->nodes[info_win->current] == window->node))
351 {
352 info_win->pagetops[info_win->current] = window->pagetop;
353 info_win->points[info_win->current] = window->point;
354 }
355}
356
357void
358remember_window_and_node (WINDOW *window, NODE *node)
359{
360 /* See if we already have this window in our list. */
361 INFO_WINDOW *info_win = get_info_window_of_window (window);
362
363 /* If the window wasn't already on our list, then make a new entry. */
364 if (!info_win)
365 {
366 info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
367 info_win->window = window;
368 info_win->nodes = (NODE **)NULL;
369 info_win->pagetops = (int *)NULL;
370 info_win->points = (long *)NULL;
371 info_win->current = 0;
372 info_win->nodes_index = 0;
373 info_win->nodes_slots = 0;
374
375 add_pointer_to_array (info_win, info_windows_index, info_windows,
376 info_windows_slots, 10, INFO_WINDOW *);
377 }
378
379 /* If this node, the current pagetop, and the current point are the
380 same as the current saved node and pagetop, don't really add this to
381 the list of history nodes. This may happen only at the very
382 beginning of the program, I'm not sure. --karl */
383 if (info_win->nodes
384 && info_win->current >= 0
385 && info_win->nodes[info_win->current]->contents == node->contents
386 && info_win->pagetops[info_win->current] == window->pagetop
387 && info_win->points[info_win->current] == window->point)
388 return;
389
390 /* Remember this node, the currently displayed pagetop, and the current
391 location of point in this window. Because we are updating pagetops
392 and points as well as nodes, it is more efficient to avoid the
393 add_pointer_to_array macro here. */
394 if (info_win->nodes_index + 2 >= info_win->nodes_slots)
395 {
396 info_win->nodes_slots += 20;
397 info_win->nodes = (NODE **) xrealloc (info_win->nodes,
398 info_win->nodes_slots * sizeof (NODE *));
399 info_win->pagetops = (int *) xrealloc (info_win->pagetops,
400 info_win->nodes_slots * sizeof (int));
401 info_win->points = (long *) xrealloc (info_win->points,
402 info_win->nodes_slots * sizeof (long));
403 }
404
405 info_win->nodes[info_win->nodes_index] = node;
406 info_win->pagetops[info_win->nodes_index] = window->pagetop;
407 info_win->points[info_win->nodes_index] = window->point;
408 info_win->current = info_win->nodes_index++;
409 info_win->nodes[info_win->nodes_index] = NULL;
410 info_win->pagetops[info_win->nodes_index] = 0;
411 info_win->points[info_win->nodes_index] = 0;
412}
413
414#define DEBUG_FORGET_WINDOW_AND_NODES
415#if defined (DEBUG_FORGET_WINDOW_AND_NODES)
416static void
417consistency_check_info_windows (void)
418{
419 register int i;
420
421 for (i = 0; i < info_windows_index; i++)
422 {
423 WINDOW *win;
424
425 for (win = windows; win; win = win->next)
426 if (win == info_windows[i]->window)
427 break;
428
429 if (!win)
430 abort ();
431 }
432}
433#endif /* DEBUG_FORGET_WINDOW_AND_NODES */
434
435/* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
436void
437forget_window_and_nodes (WINDOW *window)
438{
439 register int i;
440 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL;
441
442 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
443 if (info_win->window == window)
444 break;
445
446 /* If we found the window to forget, then do so. */
447 if (info_win)
448 {
449 while (i < info_windows_index)
450 {
451 info_windows[i] = info_windows[i + 1];
452 i++;
453 }
454
455 info_windows_index--;
456 info_windows[info_windows_index] = (INFO_WINDOW *)NULL;
457
458 if (info_win->nodes)
459 {
460 /* Free the node structures which held onto internal node contents
461 here. This doesn't free the contents; we have a garbage collector
462 which does that. */
463 for (i = 0; info_win->nodes[i]; i++)
464 if (internal_info_node_p (info_win->nodes[i]))
465 free (info_win->nodes[i]);
466 free (info_win->nodes);
467
468 maybe_free (info_win->pagetops);
469 maybe_free (info_win->points);
470 }
471
472 free (info_win);
473 }
474#if defined (DEBUG_FORGET_WINDOW_AND_NODES)
475 consistency_check_info_windows ();
476#endif /* DEBUG_FORGET_WINDOW_AND_NODES */
477}
478
479/* Set WINDOW to show NODE. Remember the new window in our list of Info
480 windows. If we are doing automatic footnote display, also try to display
481 the footnotes for this window. If REMEMBER is nonzero, first call
482 set_remembered_pagetop_and_point. */
483void
484info_set_node_of_window (int remember, WINDOW *window, NODE *node)
485{
486 if (remember)
487 set_remembered_pagetop_and_point (window);
488
489 /* Put this node into the window. */
490 window_set_node_of_window (window, node);
491
492 /* Remember this node and window in our list of info windows. */
493 remember_window_and_node (window, node);
494
495 /* If doing auto-footnote display/undisplay, show the footnotes belonging
496 to this window's node. */
497 if (auto_footnotes_p)
498 info_get_or_remove_footnotes (window);
499}
500
501
502
503/* **************************************************************** */
504/* */
505/* Info Movement Commands */
506/* */
507/* **************************************************************** */
508
509/* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
510 to do so. */
511void
512set_window_pagetop (WINDOW *window, int desired_top)
513{
514 int point_line, old_pagetop;
515
516 if (desired_top < 0)
517 desired_top = 0;
518 else if (desired_top > window->line_count)
519 desired_top = window->line_count - 1;
520
521 if (window->pagetop == desired_top)
522 return;
523
524 old_pagetop = window->pagetop;
525 window->pagetop = desired_top;
526
527 /* Make sure that point appears in this window. */
528 point_line = window_line_of_point (window);
529 if ((point_line < window->pagetop) ||
530 ((point_line - window->pagetop) > window->height - 1))
531 window->point =
532 window->line_starts[window->pagetop] - window->node->contents;
533
534 window->flags |= W_UpdateWindow;
535
536 /* Find out which direction to scroll, and scroll the window in that
537 direction. Do this only if there would be a savings in redisplay
538 time. This is true if the amount to scroll is less than the height
539 of the window, and if the number of lines scrolled would be greater
540 than 10 % of the window's height. */
541 if (old_pagetop < desired_top)
542 {
543 int start, end, amount;
544
545 amount = desired_top - old_pagetop;
546
547 if ((amount >= window->height) ||
548 (((window->height - amount) * 10) < window->height))
549 return;
550
551 start = amount + window->first_row;
552 end = window->height + window->first_row;
553
554 display_scroll_display (start, end, -amount);
555 }
556 else
557 {
558 int start, end, amount;
559
560 amount = old_pagetop - desired_top;
561
562 if ((amount >= window->height) ||
563 (((window->height - amount) * 10) < window->height))
564 return;
565
566 start = window->first_row;
567 end = (window->first_row + window->height) - amount;
568 display_scroll_display (start, end, amount);
569 }
570}
571
572/* Immediately make WINDOW->point visible on the screen, and move the
573 terminal cursor there. */
574static void
575info_show_point (WINDOW *window)
576{
577 int old_pagetop;
578
579 old_pagetop = window->pagetop;
580 window_adjust_pagetop (window);
581 if (old_pagetop != window->pagetop)
582 {
583 int new_pagetop;
584
585 new_pagetop = window->pagetop;
586 window->pagetop = old_pagetop;
587 set_window_pagetop (window, new_pagetop);
588 }
589
590 if (window->flags & W_UpdateWindow)
591 display_update_one_window (window);
592
593 display_cursor_at_point (window);
594}
595
596/* Move WINDOW->point from OLD line index to NEW line index. */
597static void
598move_to_new_line (int old, int new, WINDOW *window)
599{
600 if (old == -1)
601 {
602 info_error ((char *) msg_cant_find_point, NULL, NULL);
603 }
604 else
605 {
606 int goal;
607
608 if (new >= window->line_count || new < 0)
609 return;
610
611 goal = window_get_goal_column (window);
612 window->goal_column = goal;
613
614 window->point = window->line_starts[new] - window->node->contents;
615 window->point += window_chars_to_goal (window->line_starts[new], goal);
616 info_show_point (window);
617 }
618}
619
620/* Move WINDOW's point down to the next line if possible. */
621DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))
622{
623 int old_line, new_line;
624
625 if (count < 0)
626 info_prev_line (window, -count, key);
627 else
628 {
629 old_line = window_line_of_point (window);
630 new_line = old_line + count;
631 move_to_new_line (old_line, new_line, window);
632 }
633}
634
635/* Move WINDOW's point up to the previous line if possible. */
636DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))
637{
638 int old_line, new_line;
639
640 if (count < 0)
641 info_next_line (window, -count, key);
642 else
643 {
644 old_line = window_line_of_point (window);
645 new_line = old_line - count;
646 move_to_new_line (old_line, new_line, window);
647 }
648}
649
650/* Move WINDOW's point to the end of the true line. */
651DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))
652{
653 register int point, len;
654 register char *buffer;
655
656 buffer = window->node->contents;
657 len = window->node->nodelen;
658
659 for (point = window->point;
660 (point < len) && (buffer[point] != '\n');
661 point++);
662
663 if (point != window->point)
664 {
665 window->point = point;
666 info_show_point (window);
667 }
668}
669
670/* Move WINDOW's point to the beginning of the true line. */
671DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))
672{
673 register int point;
674 register char *buffer;
675
676 buffer = window->node->contents;
677 point = window->point;
678
679 for (; (point) && (buffer[point - 1] != '\n'); point--);
680
681 /* If at a line start already, do nothing. */
682 if (point != window->point)
683 {
684 window->point = point;
685 info_show_point (window);
686 }
687}
688
689/* Move point forward in the node. */
690DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))
691{
692 if (count < 0)
693 info_backward_char (window, -count, key);
694 else
695 {
696 window->point += count;
697
698 if (window->point >= window->node->nodelen)
699 window->point = window->node->nodelen - 1;
700
701 info_show_point (window);
702 }
703}
704
705/* Move point backward in the node. */
706DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))
707{
708 if (count < 0)
709 info_forward_char (window, -count, key);
710 else
711 {
712 window->point -= count;
713
714 if (window->point < 0)
715 window->point = 0;
716
717 info_show_point (window);
718 }
719}
720
721#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
722
723/* Move forward a word in this node. */
724DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))
725{
726 long point;
727 char *buffer;
728 int end, c;
729
730 if (count < 0)
731 {
732 info_backward_word (window, -count, key);
733 return;
734 }
735
736 point = window->point;
737 buffer = window->node->contents;
738 end = window->node->nodelen;
739
740 while (count)
741 {
742 if (point + 1 >= end)
743 return;
744
745 /* If we are not in a word, move forward until we are in one.
746 Then, move forward until we hit a non-alphabetic character. */
747 c = buffer[point];
748
749 if (!alphabetic (c))
750 {
751 while (++point < end)
752 {
753 c = buffer[point];
754 if (alphabetic (c))
755 break;
756 }
757 }
758
759 if (point >= end) return;
760
761 while (++point < end)
762 {
763 c = buffer[point];
764 if (!alphabetic (c))
765 break;
766 }
767 --count;
768 }
769 window->point = point;
770 info_show_point (window);
771}
772
773DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))
774{
775 long point;
776 char *buffer;
777 int c;
778
779 if (count < 0)
780 {
781 info_forward_word (window, -count, key);
782 return;
783 }
784
785 buffer = window->node->contents;
786 point = window->point;
787
788 while (count)
789 {
790 if (point == 0)
791 break;
792
793 /* Like info_forward_word (), except that we look at the
794 characters just before point. */
795
796 c = buffer[point - 1];
797
798 if (!alphabetic (c))
799 {
800 while (--point)
801 {
802 c = buffer[point - 1];
803 if (alphabetic (c))
804 break;
805 }
806 }
807
808 while (point)
809 {
810 c = buffer[point - 1];
811 if (!alphabetic (c))
812 break;
813 else
814 --point;
815 }
816 --count;
817 }
818 window->point = point;
819 info_show_point (window);
820}
821
822/* Variable controlling the behaviour of default scrolling when you are
823 already at the bottom of a node. Possible values are defined in session.h.
824 The meanings are:
825
826 IS_Continuous Try to get first menu item, or failing that, the
827 "Next:" pointer, or failing that, the "Up:" and
828 "Next:" of the up.
829 IS_NextOnly Try to get "Next:" menu item.
830 IS_PageOnly Simply give up at the bottom of a node. */
831
832int info_scroll_behaviour = IS_Continuous;
833
834/* Choices used by the completer when reading a value for the user-visible
835 variable "scroll-behaviour". */
836char *info_scroll_choices[] = {
837 "Continuous", "Next Only", "Page Only", (char *)NULL
838};
839
840/* Default window sizes for scrolling commands. */
841int default_window_size = -1; /* meaning 1 window-full */
842int default_scroll_size = -1; /* meaning half screen size */
843
844#define INFO_LABEL_FOUND() \
845 (info_parsed_nodename || (info_parsed_filename \
846 && !is_dir_name (info_parsed_filename)))
847
848/* Move to 1st menu item, Next, Up/Next, or error in this window. */
849static void
850forward_move_node_structure (WINDOW *window, int behaviour)
851{
852 switch (behaviour)
853 {
854 case IS_PageOnly:
855 info_error ((char *) msg_at_node_bottom, NULL, NULL);
856 break;
857
858 case IS_NextOnly:
859 info_next_label_of_node (window->node);
860 if (!info_parsed_nodename && !info_parsed_filename)
861 info_error ((char *) msg_no_pointer, (char *) _("Next"), NULL);
862 else
863 {
864 window_message_in_echo_area ((char *) _("Following Next node..."),
865 NULL, NULL);
866 info_handle_pointer ("Next", window);
867 }
868 break;
869
870 case IS_Continuous:
871 {
872 /* First things first. If this node contains a menu, move down
873 into the menu. */
874 {
875 REFERENCE **menu;
876
877 menu = info_menu_of_node (window->node);
878
879 if (menu)
880 {
881 info_free_references (menu);
882 window_message_in_echo_area ((char *) _("Selecting first menu item..."),
883 NULL, NULL);
884 info_menu_digit (window, 1, '1');
885 return;
886 }
887 }
888
889 /* Okay, this node does not contain a menu. If it contains a
890 "Next:" pointer, use that. */
891 info_next_label_of_node (window->node);
892 if (INFO_LABEL_FOUND ())
893 {
894 window_message_in_echo_area ((char *) _("Selecting Next node..."),
895 NULL, NULL);
896 info_handle_pointer ("Next", window);
897 return;
898 }
899
900 /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we
901 can move "Next:". If that isn't possible, complain that there
902 are no more nodes. */
903 {
904 int up_counter, old_current;
905 INFO_WINDOW *info_win;
906
907 /* Remember the current node and location. */
908 info_win = get_info_window_of_window (window);
909 old_current = info_win->current;
910
911 /* Back up through the "Up:" pointers until we have found a "Next:"
912 that isn't the same as the first menu item found in that node. */
913 up_counter = 0;
914 while (!info_error_was_printed)
915 {
916 info_up_label_of_node (window->node);
917 if (INFO_LABEL_FOUND ())
918 {
919 info_handle_pointer ("Up", window);
920 if (info_error_was_printed)
921 continue;
922
923 up_counter++;
924
925 info_next_label_of_node (window->node);
926
927 /* If no "Next" pointer, keep backing up. */
928 if (!INFO_LABEL_FOUND ())
929 continue;
930
931 /* If this node's first menu item is the same as this node's
932 Next pointer, keep backing up. */
933 if (!info_parsed_filename)
934 {
935 REFERENCE **menu;
936 char *next_nodename;
937
938 /* Remember the name of the Next node, since reading
939 the menu can overwrite the contents of the
940 info_parsed_xxx strings. */
941 next_nodename = xstrdup (info_parsed_nodename);
942
943 menu = info_menu_of_node (window->node);
944 if (menu &&
945 (strcmp
946 (menu[0]->nodename, next_nodename) == 0))
947 {
948 info_free_references (menu);
949 free (next_nodename);
950 continue;
951 }
952 else
953 {
954 /* Restore the world to where it was before
955 reading the menu contents. */
956 info_free_references (menu);
957 free (next_nodename);
958 info_next_label_of_node (window->node);
959 }
960 }
961
962 /* This node has a "Next" pointer, and it is not the
963 same as the first menu item found in this node. */
964 window_message_in_echo_area
965 ((char *) _("Moving Up %d time(s), then Next."),
966 (void *) (long) up_counter, NULL);
967
968 info_handle_pointer ("Next", window);
969 return;
970 }
971 else
972 {
973 /* No more "Up" pointers. Print an error, and call it
974 quits. */
975 register int i;
976
977 for (i = 0; i < up_counter; i++)
978 {
979 info_win->nodes_index--;
980 free (info_win->nodes[info_win->nodes_index]);
981 info_win->nodes[info_win->nodes_index] = (NODE *)NULL;
982 }
983 info_win->current = old_current;
984 window->node = info_win->nodes[old_current];
985 window->pagetop = info_win->pagetops[old_current];
986 window->point = info_win->points[old_current];
987 recalculate_line_starts (window);
988 window->flags |= W_UpdateWindow;
989 info_error ((char *) _("No more nodes within this document."),
990 NULL, NULL);
991 }
992 }
993 }
994 break;
995 }
996 }
997}
998
999/* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
1000static void
1001backward_move_node_structure (WINDOW *window, int behaviour)
1002{
1003 switch (behaviour)
1004 {
1005 case IS_PageOnly:
1006 info_error ((char *) msg_at_node_top, NULL, NULL);
1007 break;
1008
1009 case IS_NextOnly:
1010 info_prev_label_of_node (window->node);
1011 if (!info_parsed_nodename && !info_parsed_filename)
1012 info_error ((char *) _("No `Prev' for this node."), NULL, NULL);
1013 else
1014 {
1015 window_message_in_echo_area ((char *) _("Moving Prev in this window."),
1016 NULL, NULL);
1017 info_handle_pointer ("Prev", window);
1018 }
1019 break;
1020
1021 case IS_Continuous:
1022 info_prev_label_of_node (window->node);
1023
1024 if (!info_parsed_nodename && (!info_parsed_filename
1025 || is_dir_name (info_parsed_filename)))
1026 {
1027 info_up_label_of_node (window->node);
1028 if (!info_parsed_nodename && (!info_parsed_filename
1029 || is_dir_name (info_parsed_filename)))
1030 info_error ((char *)
1031 _("No `Prev' or `Up' for this node within this document."),
1032 NULL, NULL);
1033 else
1034 {
1035 window_message_in_echo_area ((char *) _("Moving Up in this window."),
1036 NULL, NULL);
1037 info_handle_pointer ("Up", window);
1038 }
1039 }
1040 else
1041 {
1042 REFERENCE **menu;
1043 int inhibit_menu_traversing = 0;
1044
1045 /* Watch out! If this node's Prev is the same as the Up, then
1046 move Up. Otherwise, we could move Prev, and then to the last
1047 menu item in the Prev. This would cause the user to loop
1048 through a subsection of the info file. */
1049 if (!info_parsed_filename && info_parsed_nodename)
1050 {
1051 char *pnode;
1052
1053 pnode = xstrdup (info_parsed_nodename);
1054 info_up_label_of_node (window->node);
1055
1056 if (!info_parsed_filename && info_parsed_nodename &&
1057 strcmp (info_parsed_nodename, pnode) == 0)
1058 {
1059 /* The nodes are the same. Inhibit moving to the last
1060 menu item. */
1061 free (pnode);
1062 inhibit_menu_traversing = 1;
1063 }
1064 else
1065 {
1066 free (pnode);
1067 info_prev_label_of_node (window->node);
1068 }
1069 }
1070
1071 /* Move to the previous node. If this node now contains a menu,
1072 and we have not inhibited movement to it, move to the node
1073 corresponding to the last menu item. */
1074 window_message_in_echo_area ((char *) _("Moving Prev in this window."),
1075 NULL, NULL);
1076 info_handle_pointer ("Prev", window);
1077
1078 if (!inhibit_menu_traversing)
1079 {
1080 while (!info_error_was_printed &&
1081 (menu = info_menu_of_node (window->node)))
1082 {
1083 info_free_references (menu);
1084 window_message_in_echo_area
1085 ((char *) _("Moving to `Prev's last menu item."), NULL, NULL);
1086 info_menu_digit (window, 1, '0');
1087 }
1088 }
1089 }
1090 break;
1091 }
1092}
1093
1094/* Move continuously forward through the node structure of this info file. */
1095DECLARE_INFO_COMMAND (info_global_next_node,
1096 _("Move forwards or down through node structure"))
1097{
1098 if (count < 0)
1099 info_global_prev_node (window, -count, key);
1100 else
1101 {
1102 while (count && !info_error_was_printed)
1103 {
1104 forward_move_node_structure (window, IS_Continuous);
1105 count--;
1106 }
1107 }
1108}
1109
1110/* Move continuously backward through the node structure of this info file. */
1111DECLARE_INFO_COMMAND (info_global_prev_node,
1112 _("Move backwards or up through node structure"))
1113{
1114 if (count < 0)
1115 info_global_next_node (window, -count, key);
1116 else
1117 {
1118 while (count && !info_error_was_printed)
1119 {
1120 backward_move_node_structure (window, IS_Continuous);
1121 count--;
1122 }
1123 }
1124}
1125
1126static void _scroll_forward(WINDOW *window, int count,
1127 unsigned char key, int behaviour);
1128static void _scroll_backward(WINDOW *window, int count,
1129 unsigned char key, int behaviour);
1130
1131static void
1132_scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour)
1133{
1134 if (count < 0)
1135 _scroll_backward (window, -count, key, behaviour);
1136 else
1137 {
1138 int desired_top;
1139
1140 /* Without an explicit numeric argument, scroll the bottom two
1141 lines to the top of this window, Or, if at bottom of window,
1142 and the chosen behaviour is to scroll through nodes get the
1143 "Next" node for this window. */
1144 if (default_window_size > 0)
1145 desired_top = window->pagetop + default_window_size;
1146 else if (!info_explicit_arg && count == 1)
1147 {
1148 desired_top = window->pagetop + (window->height - 2);
1149
1150 /* If there are no more lines to scroll here, error, or get
1151 another node, depending on BEHAVIOUR. */
1152 if (desired_top > window->line_count)
1153 {
1154 forward_move_node_structure (window, behaviour);
1155 return;
1156 }
1157 }
1158 else
1159 desired_top = window->pagetop + count;
1160
1161 if (desired_top >= window->line_count)
1162 desired_top = window->line_count - 2;
1163
1164 if (window->pagetop > desired_top)
1165 return;
1166 else
1167 set_window_pagetop (window, desired_top);
1168 }
1169}
1170
1171static void
1172_scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour)
1173{
1174 if (count < 0)
1175 _scroll_forward (window, -count, key, behaviour);
1176 else
1177 {
1178 int desired_top;
1179
1180 /* Without an explicit numeric argument, scroll the top two lines
1181 to the bottom of this window, or, depending on the selected
1182 behaviour, move to the previous, or Up'th node. */
1183 if (default_window_size > 0)
1184 desired_top = window->pagetop - default_window_size;
1185 else if (!info_explicit_arg && count == 1)
1186 {
1187 desired_top = window->pagetop - (window->height - 2);
1188
1189 if ((desired_top < 0) && (window->pagetop == 0))
1190 {
1191 backward_move_node_structure (window, behaviour);
1192 return;
1193 }
1194 }
1195 else
1196 desired_top = window->pagetop - count;
1197
1198 if (desired_top < 0)
1199 desired_top = 0;
1200
1201 set_window_pagetop (window, desired_top);
1202 }
1203}
1204
1205/* Show the next screen of WINDOW's node. */
1206DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))
1207{
1208 _scroll_forward (window, count, key, info_scroll_behaviour);
1209}
1210
1211/* Like info_scroll_forward, but sets default_window_size as a side
1212 effect. */
1213DECLARE_INFO_COMMAND (info_scroll_forward_set_window,
1214 _("Scroll forward in this window and set default window size"))
1215{
1216 if (info_explicit_arg)
1217 default_window_size = count;
1218 _scroll_forward (window, count, key, info_scroll_behaviour);
1219}
1220
1221/* Show the next screen of WINDOW's node but never advance to next node. */
1222DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))
1223{
1224 _scroll_forward (window, count, key, IS_PageOnly);
1225}
1226
1227/* Like info_scroll_forward_page_only, but sets default_window_size as a side
1228 effect. */
1229DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,
1230 _("Scroll forward in this window staying within node and set default window size"))
1231{
1232 if (info_explicit_arg)
1233 default_window_size = count;
1234 _scroll_forward (window, count, key, IS_PageOnly);
1235}
1236
1237/* Show the previous screen of WINDOW's node. */
1238DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))
1239{
1240 _scroll_backward (window, count, key, info_scroll_behaviour);
1241}
1242
1243/* Like info_scroll_backward, but sets default_window_size as a side
1244 effect. */
1245DECLARE_INFO_COMMAND (info_scroll_backward_set_window,
1246 _("Scroll backward in this window and set default window size"))
1247{
1248 if (info_explicit_arg)
1249 default_window_size = count;
1250 _scroll_backward (window, count, key, info_scroll_behaviour);
1251}
1252
1253/* Show the previous screen of WINDOW's node but never move to previous
1254 node. */
1255DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))
1256{
1257 _scroll_backward (window, count, key, IS_PageOnly);
1258}
1259
1260/* Like info_scroll_backward_page_only, but sets default_window_size as a side
1261 effect. */
1262DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,
1263 _("Scroll backward in this window staying within node and set default window size"))
1264{
1265 if (info_explicit_arg)
1266 default_window_size = count;
1267 _scroll_backward (window, count, key, IS_PageOnly);
1268}
1269
1270/* Move to the beginning of the node. */
1271DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))
1272{
1273 window->pagetop = window->point = 0;
1274 window->flags |= W_UpdateWindow;
1275}
1276
1277/* Move to the end of the node. */
1278DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))
1279{
1280 window->point = window->node->nodelen - 1;
1281 info_show_point (window);
1282}
1283
1284/* Scroll the window forward by N lines. */
1285DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))
1286{
1287 if (count < 0)
1288 info_up_line (window, -count, key);
1289 else
1290 {
1291 int desired_top = window->pagetop + count;
1292
1293 if (desired_top >= window->line_count)
1294 desired_top = window->line_count - 2;
1295
1296 if (window->pagetop <= desired_top)
1297 set_window_pagetop (window, desired_top);
1298 }
1299}
1300
1301/* Scroll the window backward by N lines. */
1302DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))
1303{
1304 if (count < 0)
1305 info_down_line (window, -count, key);
1306 else
1307 {
1308 int desired_top = window->pagetop - count;
1309
1310 if (desired_top < 0)
1311 desired_top = 0;
1312
1313 set_window_pagetop (window, desired_top);
1314 }
1315}
1316
1317/* Scroll the window forward by N lines and remember N as default for
1318 subsequent commands. */
1319DECLARE_INFO_COMMAND (info_scroll_half_screen_down,
1320 _("Scroll down by half screen size"))
1321{
1322 if (count < 0)
1323 info_scroll_half_screen_up (window, -count, key);
1324 else
1325 {
1326 int scroll_size = (the_screen->height + 1) / 2;
1327 int desired_top;
1328
1329 if (info_explicit_arg)
1330 default_scroll_size = count;
1331 if (default_scroll_size > 0)
1332 scroll_size = default_scroll_size;
1333
1334 desired_top = window->pagetop + scroll_size;
1335 if (desired_top >= window->line_count)
1336 desired_top = window->line_count - 2;
1337
1338 if (window->pagetop <= desired_top)
1339 set_window_pagetop (window, desired_top);
1340 }
1341}
1342
1343/* Scroll the window backward by N lines and remember N as default for
1344 subsequent commands. */
1345DECLARE_INFO_COMMAND (info_scroll_half_screen_up,
1346 _("Scroll up by half screen size"))
1347{
1348 if (count < 0)
1349 info_scroll_half_screen_down (window, -count, key);
1350 else
1351 {
1352 int scroll_size = (the_screen->height + 1) / 2;
1353 int desired_top;
1354
1355 if (info_explicit_arg)
1356 default_scroll_size = count;
1357 if (default_scroll_size > 0)
1358 scroll_size = default_scroll_size;
1359
1360 desired_top = window->pagetop - scroll_size;
1361 if (desired_top < 0)
1362 desired_top = 0;
1363
1364 set_window_pagetop (window, desired_top);
1365 }
1366}
1367
1368
1369/* **************************************************************** */
1370/* */
1371/* Commands for Manipulating Windows */
1372/* */
1373/* **************************************************************** */
1374
1375/* Make the next window in the chain be the active window. */
1376DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))
1377{
1378 if (count < 0)
1379 {
1380 info_prev_window (window, -count, key);
1381 return;
1382 }
1383
1384 /* If no other window, error now. */
1385 if (!windows->next && !echo_area_is_active)
1386 {
1387 info_error ((char *) msg_one_window, NULL, NULL);
1388 return;
1389 }
1390
1391 while (count--)
1392 {
1393 if (window->next)
1394 window = window->next;
1395 else
1396 {
1397 if (window == the_echo_area || !echo_area_is_active)
1398 window = windows;
1399 else
1400 window = the_echo_area;
1401 }
1402 }
1403
1404 if (active_window != window)
1405 {
1406 if (auto_footnotes_p)
1407 info_get_or_remove_footnotes (window);
1408
1409 window->flags |= W_UpdateWindow;
1410 active_window = window;
1411 }
1412}
1413
1414/* Make the previous window in the chain be the active window. */
1415DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))
1416{
1417 if (count < 0)
1418 {
1419 info_next_window (window, -count, key);
1420 return;
1421 }
1422
1423 /* Only one window? */
1424
1425 if (!windows->next && !echo_area_is_active)
1426 {
1427 info_error ((char *) msg_one_window, NULL, NULL);
1428 return;
1429 }
1430
1431 while (count--)
1432 {
1433 /* If we are in the echo area, or if the echo area isn't active and we
1434 are in the first window, find the last window in the chain. */
1435 if (window == the_echo_area ||
1436 (window == windows && !echo_area_is_active))
1437 {
1438 register WINDOW *win, *last = NULL;
1439
1440 for (win = windows; win; win = win->next)
1441 last = win;
1442
1443 window = last;
1444 }
1445 else
1446 {
1447 if (window == windows)
1448 window = the_echo_area;
1449 else
1450 window = window->prev;
1451 }
1452 }
1453
1454 if (active_window != window)
1455 {
1456 if (auto_footnotes_p)
1457 info_get_or_remove_footnotes (window);
1458
1459 window->flags |= W_UpdateWindow;
1460 active_window = window;
1461 }
1462}
1463
1464/* Split WINDOW into two windows, both showing the same node. If we
1465 are automatically tiling windows, re-tile after the split. */
1466DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))
1467{
1468 WINDOW *split, *old_active;
1469 int pagetop;
1470
1471 /* Remember the current pagetop of the window being split. If it doesn't
1472 change, we can scroll its contents around after the split. */
1473 pagetop = window->pagetop;
1474
1475 /* Make the new window. */
1476 old_active = active_window;
1477 active_window = window;
1478 split = window_make_window (window->node);
1479 active_window = old_active;
1480
1481 if (!split)
1482 {
1483 info_error ((char *) msg_win_too_small, NULL, NULL);
1484 }
1485 else
1486 {
1487#if defined (SPLIT_BEFORE_ACTIVE)
1488 /* Try to scroll the old window into its new postion. */
1489 if (pagetop == window->pagetop)
1490 {
1491 int start, end, amount;
1492
1493 start = split->first_row;
1494 end = start + window->height;
1495 amount = split->height + 1;
1496 display_scroll_display (start, end, amount);
1497 }
1498#else /* !SPLIT_BEFORE_ACTIVE */
1499 /* Make sure point still appears in the active window. */
1500 info_show_point (window);
1501#endif /* !SPLIT_BEFORE_ACTIVE */
1502
1503 /* If the window just split was one internal to Info, try to display
1504 something else in it. */
1505 if (internal_info_node_p (split->node))
1506 {
1507 register int i, j;
1508 INFO_WINDOW *iw;
1509 NODE *node = (NODE *)NULL;
1510 char *filename;
1511
1512 for (i = 0; (iw = info_windows[i]); i++)
1513 {
1514 for (j = 0; j < iw->nodes_index; j++)
1515 if (!internal_info_node_p (iw->nodes[j]))
1516 {
1517 if (iw->nodes[j]->parent)
1518 filename = iw->nodes[j]->parent;
1519 else
1520 filename = iw->nodes[j]->filename;
1521
1522 node = info_get_node (filename, iw->nodes[j]->nodename);
1523 if (node)
1524 {
1525 window_set_node_of_window (split, node);
1526 i = info_windows_index - 1;
1527 break;
1528 }
1529 }
1530 }
1531 }
1532 split->pagetop = window->pagetop;
1533
1534 if (auto_tiling_p)
1535 window_tile_windows (DONT_TILE_INTERNALS);
1536 else
1537 window_adjust_pagetop (split);
1538
1539 remember_window_and_node (split, split->node);
1540 }
1541}
1542
1543/* Delete WINDOW, forgetting the list of last visited nodes. If we are
1544 automatically displaying footnotes, show or remove the footnotes
1545 window. If we are automatically tiling windows, re-tile after the
1546 deletion. */
1547DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))
1548{
1549 if (!windows->next)
1550 {
1551 info_error ((char *) msg_cant_kill_last, NULL, NULL);
1552 }
1553 else if (window->flags & W_WindowIsPerm)
1554 {
1555 info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL);
1556 }
1557 else
1558 {
1559 info_delete_window_internal (window);
1560
1561 if (auto_footnotes_p)
1562 info_get_or_remove_footnotes (active_window);
1563
1564 if (auto_tiling_p)
1565 window_tile_windows (DONT_TILE_INTERNALS);
1566 }
1567}
1568
1569/* Do the physical deletion of WINDOW, and forget this window and
1570 associated nodes. */
1571void
1572info_delete_window_internal (WINDOW *window)
1573{
1574 if (windows->next && ((window->flags & W_WindowIsPerm) == 0))
1575 {
1576 /* We not only delete the window from the display, we forget it from
1577 our list of remembered windows. */
1578 forget_window_and_nodes (window);
1579 window_delete_window (window);
1580
1581 if (echo_area_is_active)
1582 echo_area_inform_of_deleted_window (window);
1583 }
1584}
1585
1586/* Just keep WINDOW, deleting all others. */
1587DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))
1588{
1589 int num_deleted; /* The number of windows we deleted. */
1590 int pagetop, start, end;
1591
1592 /* Remember a few things about this window. We may be able to speed up
1593 redisplay later by scrolling its contents. */
1594 pagetop = window->pagetop;
1595 start = window->first_row;
1596 end = start + window->height;
1597
1598 num_deleted = 0;
1599
1600 while (1)
1601 {
1602 WINDOW *win;
1603
1604 /* Find an eligible window and delete it. If no eligible windows
1605 are found, we are done. A window is eligible for deletion if
1606 is it not permanent, and it is not WINDOW. */
1607 for (win = windows; win; win = win->next)
1608 if (win != window && ((win->flags & W_WindowIsPerm) == 0))
1609 break;
1610
1611 if (!win)
1612 break;
1613
1614 info_delete_window_internal (win);
1615 num_deleted++;
1616 }
1617
1618 /* Scroll the contents of this window into the right place so that the
1619 user doesn't have to wait any longer than necessary for redisplay. */
1620 if (num_deleted)
1621 {
1622 int amount;
1623
1624 amount = (window->first_row - start);
1625 amount -= (window->pagetop - pagetop);
1626 display_scroll_display (start, end, amount);
1627 }
1628
1629 window->flags |= W_UpdateWindow;
1630}
1631
1632/* Scroll the "other" window of WINDOW. */
1633DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))
1634{
1635 WINDOW *other;
1636
1637 /* If only one window, give up. */
1638 if (!windows->next)
1639 {
1640 info_error ((char *) msg_one_window, NULL, NULL);
1641 return;
1642 }
1643
1644 other = window->next;
1645
1646 if (!other)
1647 other = window->prev;
1648
1649 info_scroll_forward (other, count, key);
1650}
1651
1652/* Scroll the "other" window of WINDOW. */
1653DECLARE_INFO_COMMAND (info_scroll_other_window_backward,
1654 _("Scroll the other window backward"))
1655{
1656 info_scroll_other_window (window, -count, key);
1657}
1658
1659/* Change the size of WINDOW by AMOUNT. */
1660DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))
1661{
1662 window_change_window_height (window, count);
1663}
1664
1665/* When non-zero, tiling takes place automatically when info_split_window
1666 is called. */
1667int auto_tiling_p = 0;
1668
1669/* Tile all of the visible windows. */
1670DECLARE_INFO_COMMAND (info_tile_windows,
1671 _("Divide the available screen space among the visible windows"))
1672{
1673 window_tile_windows (TILE_INTERNALS);
1674}
1675
1676/* Toggle the state of this window's wrapping of lines. */
1677DECLARE_INFO_COMMAND (info_toggle_wrap,
1678 _("Toggle the state of line wrapping in the current window"))
1679{
1680 window_toggle_wrap (window);
1681}
1682
1683
1684/* **************************************************************** */
1685/* */
1686/* Info Node Commands */
1687/* */
1688/* **************************************************************** */
1689
1690/* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's
1691 filename is not set. */
1692char *
1693node_printed_rep (NODE *node)
1694{
1695 char *rep;
1696
1697 if (node->filename)
1698 {
1699 char *filename
1700 = filename_non_directory (node->parent ? node->parent : node->filename);
1701 rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
1702 sprintf (rep, "(%s)%s", filename, node->nodename);
1703 }
1704 else
1705 rep = node->nodename;
1706
1707 return rep;
1708}
1709
1710
1711/* Using WINDOW for various defaults, select the node referenced by ENTRY
1712 in it. If the node is selected, the window and node are remembered. */
1713void
1714info_select_reference (WINDOW *window, REFERENCE *entry)
1715{
1716 NODE *node;
1717 char *filename, *nodename, *file_system_error;
1718
1719 file_system_error = (char *)NULL;
1720
1721 filename = entry->filename;
1722 if (!filename)
1723 filename = window->node->parent;
1724 if (!filename)
1725 filename = window->node->filename;
1726
1727 if (filename)
1728 filename = xstrdup (filename);
1729
1730 if (entry->nodename)
1731 nodename = xstrdup (entry->nodename);
1732 else
1733 nodename = xstrdup ("Top");
1734
1735 node = info_get_node (filename, nodename);
1736
1737 /* Try something a little weird. If the node couldn't be found, and the
1738 reference was of the form "foo::", see if the entry->label can be found
1739 as a file, with a node of "Top". */
1740 if (!node)
1741 {
1742 if (info_recent_file_error)
1743 file_system_error = xstrdup (info_recent_file_error);
1744
1745 if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0))
1746 {
1747 node = info_get_node (entry->label, "Top");
1748 if (!node && info_recent_file_error)
1749 {
1750 maybe_free (file_system_error);
1751 file_system_error = xstrdup (info_recent_file_error);
1752 }
1753 }
1754 }
1755
1756 if (!node)
1757 {
1758 if (file_system_error)
1759 info_error (file_system_error, NULL, NULL);
1760 else
1761 info_error ((char *) msg_cant_find_node, nodename, NULL);
1762 }
1763
1764 maybe_free (file_system_error);
1765 maybe_free (filename);
1766 maybe_free (nodename);
1767
1768 if (node)
1769 info_set_node_of_window (1, window, node);
1770}
1771
1772/* Parse the node specification in LINE using WINDOW to default the filename.
1773 Select the parsed node in WINDOW and remember it, or error if the node
1774 couldn't be found. */
1775static void
1776info_parse_and_select (char *line, WINDOW *window)
1777{
1778 REFERENCE entry;
1779
1780 info_parse_node (line, DONT_SKIP_NEWLINES);
1781
1782 entry.nodename = info_parsed_nodename;
1783 entry.filename = info_parsed_filename;
1784 entry.label = "*info-parse-and-select*";
1785
1786 info_select_reference (window, &entry);
1787}
1788
1789/* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME
1790 are previously filled, try to get the node represented by them into
1791 WINDOW. The node should have been pointed to by the LABEL pointer of
1792 WINDOW->node. */
1793static void
1794info_handle_pointer (char *label, WINDOW *window)
1795{
1796 if (info_parsed_filename || info_parsed_nodename)
1797 {
1798 char *filename, *nodename;
1799 NODE *node;
1800
1801 filename = nodename = (char *)NULL;
1802
1803 if (info_parsed_filename)
1804 filename = xstrdup (info_parsed_filename);
1805 else
1806 {
1807 if (window->node->parent)
1808 filename = xstrdup (window->node->parent);
1809 else if (window->node->filename)
1810 filename = xstrdup (window->node->filename);
1811 }
1812
1813 if (info_parsed_nodename)
1814 nodename = xstrdup (info_parsed_nodename);
1815 else
1816 nodename = xstrdup ("Top");
1817
1818 node = info_get_node (filename, nodename);
1819
1820 if (node)
1821 {
1822 INFO_WINDOW *info_win;
1823
1824 info_win = get_info_window_of_window (window);
1825 if (info_win)
1826 {
1827 info_win->pagetops[info_win->current] = window->pagetop;
1828 info_win->points[info_win->current] = window->point;
1829 }
1830 info_set_node_of_window (1, window, node);
1831 }
1832 else
1833 {
1834 if (info_recent_file_error)
1835 info_error (info_recent_file_error, NULL, NULL);
1836 else
1837 info_error ((char *) msg_cant_file_node, filename, nodename);
1838 }
1839
1840 free (filename);
1841 free (nodename);
1842 }
1843 else
1844 {
1845 info_error ((char *) msg_no_pointer, label, NULL);
1846 }
1847}
1848
1849/* Make WINDOW display the "Next:" node of the node currently being
1850 displayed. */
1851DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))
1852{
1853 info_next_label_of_node (window->node);
1854 info_handle_pointer ("Next", window);
1855}
1856
1857/* Make WINDOW display the "Prev:" node of the node currently being
1858 displayed. */
1859DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))
1860{
1861 info_prev_label_of_node (window->node);
1862 info_handle_pointer ("Prev", window);
1863}
1864
1865/* Make WINDOW display the "Up:" node of the node currently being
1866 displayed. */
1867DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))
1868{
1869 info_up_label_of_node (window->node);
1870 info_handle_pointer ("Up", window);
1871}
1872
1873/* Make WINDOW display the last node of this info file. */
1874DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))
1875{
1876 register int i;
1877 FILE_BUFFER *fb = file_buffer_of_window (window);
1878 NODE *node = (NODE *)NULL;
1879
1880 if (fb && fb->tags)
1881 {
1882 int last_node_tag_idx = -1;
1883
1884 /* If no explicit argument, or argument of zero, default to the
1885 last node. */
1886 if (count == 0 || (count == 1 && !info_explicit_arg))
1887 count = -1;
1888 for (i = 0; count && fb->tags[i]; i++)
1889 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1890 {
1891 count--;
1892 last_node_tag_idx = i;
1893 }
1894 if (count > 0)
1895 i = last_node_tag_idx + 1;
1896 if (i > 0)
1897 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1898 }
1899
1900 if (!node)
1901 info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
1902 else
1903 info_set_node_of_window (1, window, node);
1904}
1905
1906/* Make WINDOW display the first node of this info file. */
1907DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))
1908{
1909 FILE_BUFFER *fb = file_buffer_of_window (window);
1910 NODE *node = (NODE *)NULL;
1911
1912 /* If no explicit argument, or argument of zero, default to the
1913 first node. */
1914 if (count == 0)
1915 count = 1;
1916 if (fb && fb->tags)
1917 {
1918 register int i;
1919 int last_node_tag_idx = -1;
1920
1921 for (i = 0; count && fb->tags[i]; i++)
1922 if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */
1923 {
1924 count--;
1925 last_node_tag_idx = i;
1926 }
1927 if (count > 0)
1928 i = last_node_tag_idx + 1;
1929 if (i > 0)
1930 node = info_get_node (fb->filename, fb->tags[i - 1]->nodename);
1931 }
1932
1933 if (!node)
1934 info_error ((char *) _("This window has no additional nodes"), NULL, NULL);
1935 else
1936 info_set_node_of_window (1, window, node);
1937}
1938
1939/* Select the last menu item in WINDOW->node. */
1940DECLARE_INFO_COMMAND (info_last_menu_item,
1941 _("Select the last item in this node's menu"))
1942{
1943 info_menu_digit (window, 1, '0');
1944}
1945
1946/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
1947DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))
1948{
1949 register int i, item;
1950 register REFERENCE **menu;
1951
1952 menu = info_menu_of_node (window->node);
1953
1954 if (!menu)
1955 {
1956 info_error ((char *) msg_no_menu_node, NULL, NULL);
1957 return;
1958 }
1959
1960 /* We have the menu. See if there are this many items in it. */
1961 item = key - '0';
1962
1963 /* Special case. Item "0" is the last item in this menu. */
1964 if (item == 0)
1965 for (i = 0; menu[i + 1]; i++);
1966 else
1967 {
1968 for (i = 0; menu[i]; i++)
1969 if (i == item - 1)
1970 break;
1971 }
1972
1973 if (menu[i])
1974 {
1975 info_select_reference (window, menu[i]);
1976 if (menu[i]->line_number > 0)
1977 info_next_line (window, menu[i]->line_number - 1, key);
1978 }
1979 else
1980 info_error ((char *) _("There aren't %d items in this menu."),
1981 (void *) (long) item, NULL);
1982
1983 info_free_references (menu);
1984 return;
1985}
1986
1987
1988
1989
1990/* Return a pointer to the xref in XREF_LIST that is nearest to POS, or
1991 NULL if XREF_LIST is empty. That is, if POS is within any of the
1992 given xrefs, return that one. Otherwise, return the one with the
1993 nearest beginning or end. If there are two that are equidistant,
1994 prefer the one forward. The return is in newly-allocated memory,
1995 since the caller frees it.
1996
1997 This is called from info_menu_or_ref_item with XREF_LIST being all
1998 the xrefs in the node, and POS being point. The ui function that
1999 starts it all off is select-reference-this-line.
2000
2001 This is not the same logic as in info.el. Info-get-token prefers
2002 searching backwards to searching forwards, and has a hardwired search
2003 limit of 200 chars (in Emacs 21.2). */
2004
2005static REFERENCE **
2006nearest_xref (REFERENCE **xref_list, long int pos)
2007{
2008 int this_xref;
2009 int nearest = -1;
2010 long best_delta = -1;
2011
2012 for (this_xref = 0; xref_list[this_xref]; this_xref++)
2013 {
2014 long delta;
2015 REFERENCE *xref = xref_list[this_xref];
2016 if (xref->start <= pos && pos <= xref->end)
2017 { /* POS is within this xref, we're done */
2018 nearest = this_xref;
2019 break;
2020 }
2021
2022 /* See how far POS is from this xref. Take into account the
2023 `*Note' that begins the xref, since as far as the user is
2024 concerned, that's where it starts. */
2025 delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))),
2026 labs (pos - xref->end));
2027
2028 /* It's the <= instead of < that makes us choose the forward xref
2029 of POS if two are equidistant. Of course, because of all the
2030 punctuation surrounding xrefs, it's not necessarily obvious
2031 where one ends. */
2032 if (delta <= best_delta || best_delta < 0)
2033 {
2034 nearest = this_xref;
2035 best_delta = delta;
2036 }
2037 }
2038
2039 /* Maybe there was no list to search through. */
2040 if (nearest < 0)
2041 return NULL;
2042
2043 /* Ok, we have a nearest xref, make a list of it. */
2044 {
2045 REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2);
2046 ret[0] = info_copy_reference (xref_list[nearest]);
2047 ret[1] = NULL;
2048 return ret;
2049 }
2050}
2051
2052
2053/* Read a menu or followed reference from the user defaulting to the
2054 reference found on the current line, and select that node. The
2055 reading is done with completion. BUILDER is the function used
2056 to build the list of references. ASK_P is non-zero if the user
2057 should be prompted, or zero to select the default item. */
2058static void
2059info_menu_or_ref_item (WINDOW *window, int count,
2060 unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p)
2061{
2062 char *line;
2063 REFERENCE *entry;
2064 REFERENCE *defentry = NULL;
2065 REFERENCE **menu = (*builder) (window->node);
2066
2067 if (!menu)
2068 {
2069 if (builder == info_menu_of_node)
2070 info_error ((char *) msg_no_menu_node, NULL, NULL);
2071 else
2072 info_error ((char *) msg_no_xref_node, NULL, NULL);
2073 return;
2074 }
2075
2076 /* Default the selected reference to the one which is on the line that
2077 point is in. */
2078 {
2079 REFERENCE **refs = NULL;
2080 int point_line = window_line_of_point (window);
2081
2082 if (point_line != -1)
2083 {
2084 SEARCH_BINDING binding;
2085
2086 binding.buffer = window->node->contents;
2087 binding.start = window->line_starts[point_line] - binding.buffer;
2088 if (window->line_starts[point_line + 1])
2089 binding.end = window->line_starts[point_line + 1] - binding.buffer;
2090 else
2091 binding.end = window->node->nodelen;
2092 binding.flags = 0;
2093
2094 if (builder == info_menu_of_node)
2095 {
2096 if (point_line)
2097 {
2098 binding.start--;
2099 refs = info_menu_items (&binding);
2100 }
2101 }
2102 else
2103 {
2104#if defined (HANDLE_MAN_PAGES)
2105 if (window->node->flags & N_IsManPage)
2106 refs = manpage_xrefs_in_binding (window->node, &binding);
2107 else
2108#endif /* HANDLE_MAN_PAGES */
2109 refs = nearest_xref (menu, window->point);
2110 }
2111
2112 if (refs && refs[0])
2113 {
2114 if (strcmp (refs[0]->label, "Menu") != 0
2115 || builder == info_xrefs_of_node)
2116 {
2117 int which = 0;
2118
2119 /* For xrefs, find the closest reference to point,
2120 unless we only have one reference (as we will if
2121 we've called nearest_xref above). It would be better
2122 to have only one piece of code, but the conditions
2123 when we call this are tangled. */
2124 if (builder == info_xrefs_of_node && refs[1])
2125 {
2126 int closest = -1;
2127
2128 for (; refs[which]; which++)
2129 {
2130 if (window->point >= refs[which]->start
2131 && window->point <= refs[which]->end)
2132 {
2133 closest = which;
2134 break;
2135 }
2136 else if (window->point < refs[which]->start)
2137 break;
2138 }
2139 if (which > 0)
2140 {
2141 if (closest == -1)
2142 which--;
2143 else
2144 which = closest;
2145 }
2146 }
2147
2148 defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2149 defentry->label = xstrdup (refs[which]->label);
2150 defentry->filename = refs[which]->filename;
2151 defentry->nodename = refs[which]->nodename;
2152 defentry->line_number = refs[which]->line_number;
2153
2154 if (defentry->filename)
2155 defentry->filename = xstrdup (defentry->filename);
2156 if (defentry->nodename)
2157 defentry->nodename = xstrdup (defentry->nodename);
2158 }
2159 info_free_references (refs);
2160 }
2161 }
2162 }
2163
2164 /* If we are going to ask the user a question, do it now. */
2165 if (ask_p)
2166 {
2167 char *prompt;
2168
2169 /* Build the prompt string. */
2170 if (builder == info_menu_of_node)
2171 {
2172 if (defentry)
2173 {
2174 prompt = xmalloc (strlen (defentry->label)
2175 + strlen (_("Menu item (%s): ")));
2176 sprintf (prompt, _("Menu item (%s): "), defentry->label);
2177 }
2178 else
2179 prompt = xstrdup (_("Menu item: "));
2180 }
2181 else
2182 {
2183 if (defentry)
2184 {
2185 prompt = xmalloc (strlen (defentry->label)
2186 + strlen (_("Follow xref (%s): ")));
2187 sprintf (prompt, _("Follow xref (%s): "), defentry->label);
2188 }
2189 else
2190 prompt = xstrdup (_("Follow xref: "));
2191 }
2192
2193 line = info_read_completing_in_echo_area (window, prompt, menu);
2194 free (prompt);
2195
2196 window = active_window;
2197
2198 /* User aborts, just quit. */
2199 if (!line)
2200 {
2201 maybe_free (defentry);
2202 info_free_references (menu);
2203 info_abort_key (window, 0, 0);
2204 return;
2205 }
2206
2207 /* If we had a default and the user accepted it, use that. */
2208 if (!*line)
2209 {
2210 free (line);
2211 if (defentry)
2212 line = xstrdup (defentry->label);
2213 else
2214 line = (char *)NULL;
2215 }
2216 }
2217 else
2218 {
2219 /* Not going to ask any questions. If we have a default entry, use
2220 that, otherwise return. */
2221 if (!defentry)
2222 return;
2223 else
2224 line = xstrdup (defentry->label);
2225 }
2226
2227 if (line)
2228 {
2229 /* It is possible that the references have more than a single
2230 entry with the same label, and also LINE is down-cased, which
2231 complicates matters even more. Try to be as accurate as we
2232 can: if they've chosen the default, use defentry directly. */
2233 if (defentry && strcmp (line, defentry->label) == 0)
2234 entry = defentry;
2235 else
2236 /* Find the selected label in the references. If there are
2237 more than one label which matches, find the one that's
2238 closest to point. */
2239 {
2240 register int i;
2241 int best = -1, min_dist = window->node->nodelen;
2242 REFERENCE *ref;
2243
2244 for (i = 0; menu && (ref = menu[i]); i++)
2245 {
2246 /* Need to use strcasecmp because LINE is downcased
2247 inside info_read_completing_in_echo_area. */
2248 if (strcasecmp (line, ref->label) == 0)
2249 {
2250 /* ref->end is more accurate estimate of position
2251 for menus than ref->start. Go figure. */
2252 int dist = abs (window->point - ref->end);
2253
2254 if (dist < min_dist)
2255 {
2256 min_dist = dist;
2257 best = i;
2258 }
2259 }
2260 }
2261 if (best != -1)
2262 entry = menu[best];
2263 else
2264 entry = (REFERENCE *)NULL;
2265 }
2266
2267 if (!entry && defentry)
2268 info_error ((char *) _("The reference disappeared! (%s)."), line, NULL);
2269 else
2270 {
2271 NODE *orig = window->node;
2272 info_select_reference (window, entry);
2273
2274 if (builder == info_xrefs_of_node && window->node != orig
2275 && !(window->node->flags & N_FromAnchor))
2276 { /* Search for this reference in the node. */
2277 long offset;
2278 long start;
2279
2280 if (window->line_count > 0)
2281 start = window->line_starts[1] - window->node->contents;
2282 else
2283 start = 0;
2284
2285 offset =
2286 info_target_search_node (window->node, entry->label, start);
2287
2288 if (offset != -1)
2289 {
2290 window->point = offset;
2291 window_adjust_pagetop (window);
2292 }
2293 }
2294
2295 if (entry->line_number > 0)
2296 /* next_line starts at line 1? Anyway, the -1 makes it
2297 move to the right line. */
2298 info_next_line (window, entry->line_number - 1, key);
2299 }
2300
2301 free (line);
2302 if (defentry)
2303 {
2304 free (defentry->label);
2305 maybe_free (defentry->filename);
2306 maybe_free (defentry->nodename);
2307 free (defentry);
2308 }
2309 }
2310
2311 info_free_references (menu);
2312
2313 if (!info_error_was_printed)
2314 window_clear_echo_area ();
2315}
2316
2317/* Read a line (with completion) which is the name of a menu item,
2318 and select that item. */
2319DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
2320{
2321 info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2322}
2323
2324/* Read a line (with completion) which is the name of a reference to
2325 follow, and select the node. */
2326DECLARE_INFO_COMMAND
2327 (info_xref_item, _("Read a footnote or cross reference and select its node"))
2328{
2329 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2330}
2331
2332/* Position the cursor at the start of this node's menu. */
2333DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))
2334{
2335 SEARCH_BINDING binding;
2336 long position;
2337
2338 binding.buffer = window->node->contents;
2339 binding.start = 0;
2340 binding.end = window->node->nodelen;
2341 binding.flags = S_FoldCase | S_SkipDest;
2342
2343 position = search (INFO_MENU_LABEL, &binding);
2344
2345 if (position == -1)
2346 info_error ((char *) msg_no_menu_node, NULL, NULL);
2347 else
2348 {
2349 window->point = position;
2350 window_adjust_pagetop (window);
2351 window->flags |= W_UpdateWindow;
2352 }
2353}
2354
2355/* Visit as many menu items as is possible, each in a separate window. */
2356DECLARE_INFO_COMMAND (info_visit_menu,
2357 _("Visit as many menu items at once as possible"))
2358{
2359 register int i;
2360 REFERENCE *entry, **menu;
2361
2362 menu = info_menu_of_node (window->node);
2363
2364 if (!menu)
2365 info_error ((char *) msg_no_menu_node, NULL, NULL);
2366
2367 for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2368 {
2369 WINDOW *new;
2370
2371 new = window_make_window (window->node);
2372 window_tile_windows (TILE_INTERNALS);
2373
2374 if (!new)
2375 info_error ((char *) msg_win_too_small, NULL, NULL);
2376 else
2377 {
2378 active_window = new;
2379 info_select_reference (new, entry);
2380 }
2381 }
2382}
2383
2384/* Read a line of input which is a node name, and go to that node. */
2385DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))
2386{
2387 char *line;
2388
2389#define GOTO_COMPLETES
2390#if defined (GOTO_COMPLETES)
2391 /* Build a completion list of all of the known nodes. */
2392 {
2393 register int fbi, i;
2394 FILE_BUFFER *current;
2395 REFERENCE **items = (REFERENCE **)NULL;
2396 int items_index = 0;
2397 int items_slots = 0;
2398
2399 current = file_buffer_of_window (window);
2400
2401 for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2402 {
2403 FILE_BUFFER *fb;
2404 REFERENCE *entry;
2405 int this_is_the_current_fb;
2406
2407 fb = info_loaded_files[fbi];
2408 this_is_the_current_fb = (current == fb);
2409
2410 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2411 entry->filename = entry->nodename = (char *)NULL;
2412 entry->label = (char *)xmalloc (4 + strlen (fb->filename));
2413 sprintf (entry->label, "(%s)*", fb->filename);
2414
2415 add_pointer_to_array
2416 (entry, items_index, items, items_slots, 10, REFERENCE *);
2417
2418 if (fb->tags)
2419 {
2420 for (i = 0; fb->tags[i]; i++)
2421 {
2422 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2423 entry->filename = entry->nodename = (char *)NULL;
2424 if (this_is_the_current_fb)
2425 entry->label = xstrdup (fb->tags[i]->nodename);
2426 else
2427 {
2428 entry->label = (char *) xmalloc
2429 (4 + strlen (fb->filename) +
2430 strlen (fb->tags[i]->nodename));
2431 sprintf (entry->label, "(%s)%s",
2432 fb->filename, fb->tags[i]->nodename);
2433 }
2434
2435 add_pointer_to_array
2436 (entry, items_index, items, items_slots, 100, REFERENCE *);
2437 }
2438 }
2439 }
2440 line = info_read_maybe_completing (window, (char *) _("Goto node: "),
2441 items);
2442 info_free_references (items);
2443 }
2444#else /* !GOTO_COMPLETES */
2445 line = info_read_in_echo_area (window, (char *) _("Goto node: "));
2446#endif /* !GOTO_COMPLETES */
2447
2448 /* If the user aborted, quit now. */
2449 if (!line)
2450 {
2451 info_abort_key (window, 0, 0);
2452 return;
2453 }
2454
2455 canonicalize_whitespace (line);
2456
2457 if (*line)
2458 info_parse_and_select (line, window);
2459
2460 free (line);
2461 if (!info_error_was_printed)
2462 window_clear_echo_area ();
2463}
2464
2465
2466/* Follow the menu list in MENUS (list of strings terminated by a NULL
2467 entry) from INITIAL_NODE. If can't continue at any point (no menu or
2468 no menu entry for the next item), return the node so far -- that
2469 might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will
2470 be set to the error message and argument for message, otherwise they
2471 will be NULL. */
2472
2473NODE *
2474info_follow_menus (NODE *initial_node, char **menus,
2475 const char **errstr, char **errarg1, char **errarg2)
2476{
2477 NODE *node = NULL;
2478 *errstr = *errarg1 = *errarg2 = NULL;
2479
2480 for (; *menus; menus++)
2481 {
2482 static char *first_arg = NULL;
2483 REFERENCE **menu;
2484 REFERENCE *entry;
2485 char *arg = *menus; /* Remember the name of the menu entry we want. */
2486
2487 /* A leading space is certainly NOT part of a node name. Most
2488 probably, they typed a space after the separating comma. The
2489 strings in menus[] have their whitespace canonicalized, so
2490 there's at most one space to ignore. */
2491 if (*arg == ' ')
2492 arg++;
2493 if (!first_arg)
2494 first_arg = arg;
2495
2496 /* Build and return a list of the menu items in this node. */
2497 menu = info_menu_of_node (initial_node);
2498
2499 /* If no menu item in this node, stop here, but let the user
2500 continue to use Info. Perhaps they wanted this node and didn't
2501 realize it. */
2502 if (!menu)
2503 {
2504 if (arg == first_arg)
2505 {
2506 node = make_manpage_node (first_arg);
2507 if (node)
2508 goto maybe_got_node;
2509 }
2510 *errstr = _("No menu in node `%s'.");
2511 *errarg1 = node_printed_rep (initial_node);
2512 return initial_node;
2513 }
2514
2515 /* Find the specified menu item. */
2516 entry = info_get_labeled_reference (arg, menu);
2517
2518 /* If the item wasn't found, search the list sloppily. Perhaps this
2519 user typed "buffer" when they really meant "Buffers". */
2520 if (!entry)
2521 {
2522 int i;
2523 int best_guess = -1;
2524
2525 for (i = 0; (entry = menu[i]); i++)
2526 {
2527 if (strcasecmp (entry->label, arg) == 0)
2528 break;
2529 else
2530 if ((best_guess == -1)
2531 && (strncasecmp (entry->label, arg, strlen (arg)) == 0))
2532 best_guess = i;
2533 }
2534
2535 if (!entry && best_guess != -1)
2536 entry = menu[best_guess];
2537 }
2538
2539 /* If we still failed to find the reference, start Info with the current
2540 node anyway. It is probably a misspelling. */
2541 if (!entry)
2542 {
2543 if (arg == first_arg)
2544 {
2545 /* Maybe they typed "info foo" instead of "info -f foo". */
2546 node = info_get_node (first_arg, 0);
2547 if (node)
2548 add_file_directory_to_path (first_arg);
2549 else
2550 node = make_manpage_node (first_arg);
2551 if (node)
2552 goto maybe_got_node;
2553 }
2554
2555 info_free_references (menu);
2556 *errstr = _("No menu item `%s' in node `%s'.");
2557 *errarg1 = arg;
2558 *errarg2 = node_printed_rep (initial_node);
2559 return initial_node;
2560 }
2561
2562 /* We have found the reference that the user specified. If no
2563 filename in this reference, define it. */
2564 if (!entry->filename)
2565 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2566 : initial_node->filename);
2567
2568 /* Try to find this node. */
2569 node = info_get_node (entry->filename, entry->nodename);
2570 if (!node && arg == first_arg)
2571 {
2572 node = make_manpage_node (first_arg);
2573 if (node)
2574 goto maybe_got_node;
2575 }
2576
2577 /* Since we cannot find it, try using the label of the entry as a
2578 file, i.e., "(LABEL)Top". */
2579 if (!node && entry->nodename
2580 && strcmp (entry->label, entry->nodename) == 0)
2581 node = info_get_node (entry->label, "Top");
2582
2583 maybe_got_node:
2584 if (!node)
2585 {
2586 *errstr = _("Unable to find node referenced by `%s' in `%s'.");
2587 *errarg1 = xstrdup (entry->label);
2588 *errarg2 = node_printed_rep (initial_node);
2589 info_free_references (menu);
2590 return initial_node;
2591 }
2592
2593 info_free_references (menu);
2594
2595 /* Success. Go round the loop again. */
2596 free (initial_node);
2597 initial_node = node;
2598 }
2599
2600 return initial_node;
2601}
2602
2603/* Split STR into individual node names by writing null bytes in wherever
2604 there are commas and constructing a list of the resulting pointers.
2605 (We can do this since STR has had canonicalize_whitespace called on it.)
2606 Return array terminated with NULL. */
2607
2608static char **
2609split_list_of_nodenames (char *str)
2610{
2611 unsigned len = 2;
2612 char **nodes = xmalloc (len * sizeof (char *));
2613
2614 nodes[len - 2] = str;
2615
2616 while (*str++)
2617 {
2618 if (*str == ',')
2619 {
2620 *str++ = 0; /* get past the null byte */
2621 len++;
2622 nodes = xrealloc (nodes, len * sizeof (char *));
2623 nodes[len - 2] = str;
2624 }
2625 }
2626
2627 nodes[len - 1] = NULL;
2628
2629 return nodes;
2630}
2631
2632
2633/* Read a line of input which is a sequence of menus (starting from
2634 dir), and follow them. */
2635DECLARE_INFO_COMMAND (info_menu_sequence,
2636 _("Read a list of menus starting from dir and follow them"))
2637{
2638 char *line = info_read_in_echo_area (window, (char *) _("Follow menus: "));
2639
2640 /* If the user aborted, quit now. */
2641 if (!line)
2642 {
2643 info_abort_key (window, 0, 0);
2644 return;
2645 }
2646
2647 canonicalize_whitespace (line);
2648
2649 if (*line)
2650 {
2651 const char *errstr;
2652 char *errarg1, *errarg2;
2653 NODE *dir_node = info_get_node (NULL, NULL);
2654 char **nodes = split_list_of_nodenames (line);
2655 NODE *node = NULL;
2656
2657 /* If DIR_NODE is NULL, they might be reading a file directly,
2658 like in "info -d . -f ./foo". Try using "Top" instead. */
2659 if (!dir_node)
2660 {
2661 char *file_name = window->node->parent;
2662
2663 if (!file_name)
2664 file_name = window->node->filename;
2665 dir_node = info_get_node (file_name, NULL);
2666 }
2667
2668 /* If we still cannot find the starting point, give up.
2669 We cannot allow a NULL pointer inside info_follow_menus. */
2670 if (!dir_node)
2671 info_error ((char *) msg_cant_find_node, "Top", NULL);
2672 else
2673 node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
2674
2675 free (nodes);
2676 if (!errstr)
2677 info_set_node_of_window (1, window, node);
2678 else
2679 info_error ((char *) errstr, errarg1, errarg2);
2680 }
2681
2682 free (line);
2683 if (!info_error_was_printed)
2684 window_clear_echo_area ();
2685}
2686
2687/* Search the menu MENU for a (possibly mis-spelled) entry ARG.
2688 Return the menu entry, or the best guess for what they meant by ARG,
2689 or NULL if there's nothing in this menu seems to fit the bill.
2690 If EXACT is non-zero, allow only exact matches. */
2691static REFERENCE *
2692entry_in_menu (char *arg, REFERENCE **menu, int exact)
2693{
2694 REFERENCE *entry;
2695
2696 /* First, try to find the specified menu item verbatim. */
2697 entry = info_get_labeled_reference (arg, menu);
2698
2699 /* If the item wasn't found, search the list sloppily. Perhaps we
2700 have "Option Summary", but ARG is "option". */
2701 if (!entry && !exact)
2702 {
2703 int i;
2704 int best_guess = -1;
2705
2706 for (i = 0; (entry = menu[i]); i++)
2707 {
2708 if (strcasecmp (entry->label, arg) == 0)
2709 break;
2710 else
2711 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2712 best_guess = i;
2713 }
2714
2715 if (!entry && best_guess != -1)
2716 entry = menu[best_guess];
2717 }
2718
2719 return entry;
2720}
2721
2722/* Find the node that is the best candidate to list the PROGRAM's
2723 invocation info and its command-line options, by looking for menu
2724 items and chains of menu items with characteristic names. */
2725void
2726info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program)
2727{
2728 /* The list of node names typical for GNU manuals where the program
2729 usage and specifically the command-line arguments are described.
2730 This is pure heuristics. I gathered these node names by looking
2731 at all the Info files I could put my hands on. If you are
2732 looking for evidence to complain to the GNU project about
2733 non-uniform style of documentation, here you have your case! */
2734 static const char *invocation_nodes[] = {
2735 "%s invocation",
2736 "Invoking %s",
2737 "Preliminaries", /* m4 has Invoking under Preliminaries! */
2738 "Invocation",
2739 "Command Arguments",/* Emacs */
2740 "Invoking `%s'",
2741 "%s options",
2742 "Options",
2743 "Option ", /* e.g. "Option Summary" */
2744 "Invoking",
2745 "All options", /* tar, paxutils */
2746 "Arguments",
2747 "%s cmdline", /* ar */
2748 "%s", /* last resort */
2749 (const char *)0
2750 };
2751 NODE *node = NULL;
2752 REFERENCE **menu;
2753 const char **try_node;
2754
2755 /* We keep looking deeper and deeper in the menu structure until
2756 there are no more menus or no menu items from the above list.
2757 Some manuals have the invocation node sitting 3 or 4 levels deep
2758 in the menu hierarchy... */
2759 for (node = initial_node; node; initial_node = node)
2760 {
2761 REFERENCE *entry = NULL;
2762
2763 /* Build and return a list of the menu items in this node. */
2764 menu = info_menu_of_node (initial_node);
2765
2766 /* If no menu item in this node, stop here. Perhaps this node
2767 is the one they need. */
2768 if (!menu)
2769 break;
2770
2771 /* Look for node names typical for usage nodes in this menu. */
2772 for (try_node = invocation_nodes; *try_node; try_node++)
2773 {
2774 char *nodename;
2775
2776 nodename = xmalloc (strlen (program) + strlen (*try_node));
2777 sprintf (nodename, *try_node, program);
2778 /* The last resort "%s" is dangerous, so we restrict it
2779 to exact matches here. */
2780 entry = entry_in_menu (nodename, menu,
2781 strcmp (*try_node, "%s") == 0);
2782 free (nodename);
2783 if (entry)
2784 break;
2785 }
2786
2787 if (!entry)
2788 break;
2789
2790 if (!entry->filename)
2791 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2792 : initial_node->filename);
2793 /* Try to find this node. */
2794 node = info_get_node (entry->filename, entry->nodename);
2795 info_free_references (menu);
2796 if (!node)
2797 break;
2798 }
2799
2800 /* We've got our best shot at the invocation node. Now select it. */
2801 if (initial_node)
2802 info_set_node_of_window (1, window, initial_node);
2803 if (!info_error_was_printed)
2804 window_clear_echo_area ();
2805}
2806
2807/* Given a name of an Info file, find the name of the package it
2808 describes by removing the leading directories and extensions. */
2809char *
2810program_name_from_file_name (char *file_name)
2811{
2812 int i;
2813 char *program_name = xstrdup (filename_non_directory (file_name));
2814
2815 for (i = strlen (program_name) - 1; i > 0; i--)
2816 if (program_name[i] == '.'
2817 && (FILENAME_CMPN (program_name + i, ".info", 5) == 0
2818 || FILENAME_CMPN (program_name + i, ".inf", 4) == 0
2819#ifdef __MSDOS__
2820 || FILENAME_CMPN (program_name + i, ".i", 2) == 0
2821#endif
2822 || isdigit (program_name[i + 1]))) /* a man page foo.1 */
2823 {
2824 program_name[i] = 0;
2825 break;
2826 }
2827 return program_name;
2828}
2829
2830DECLARE_INFO_COMMAND (info_goto_invocation_node,
2831 _("Find the node describing program invocation"))
2832{
2833 const char *invocation_prompt = _("Find Invocation node of [%s]: ");
2834 char *program_name, *line;
2835 char *default_program_name, *prompt, *file_name;
2836 NODE *top_node;
2837
2838 /* Intuit the name of the program they are likely to want.
2839 We use the file name of the current Info file as a hint. */
2840 file_name = window->node->parent ? window->node->parent
2841 : window->node->filename;
2842 default_program_name = program_name_from_file_name (file_name);
2843
2844 prompt = (char *)xmalloc (strlen (default_program_name) +
2845 strlen (invocation_prompt));
2846 sprintf (prompt, invocation_prompt, default_program_name);
2847 line = info_read_in_echo_area (window, prompt);
2848 free (prompt);
2849 if (!line)
2850 {
2851 info_abort_key (window, 0, 0);
2852 return;
2853 }
2854 if (*line)
2855 program_name = line;
2856 else
2857 program_name = default_program_name;
2858
2859 /* In interactive usage they'd probably expect us to begin looking
2860 from the Top node. */
2861 top_node = info_get_node (file_name, NULL);
2862 if (!top_node)
2863 info_error ((char *) msg_cant_find_node, "Top", NULL);
2864
2865 info_intuit_options_node (window, top_node, program_name);
2866 free (line);
2867 free (default_program_name);
2868}
2869
2870
2871#if defined (HANDLE_MAN_PAGES)
2872DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))
2873{
2874 char *line;
2875
2876 line = info_read_in_echo_area (window, (char *) _("Get Manpage: "));
2877
2878 if (!line)
2879 {
2880 info_abort_key (window, 0, 0);
2881 return;
2882 }
2883
2884 canonicalize_whitespace (line);
2885
2886 if (*line)
2887 {
2888 char *goto_command;
2889
2890 goto_command = (char *)xmalloc
2891 (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line));
2892
2893 sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line);
2894
2895 info_parse_and_select (goto_command, window);
2896 free (goto_command);
2897 }
2898
2899 free (line);
2900 if (!info_error_was_printed)
2901 window_clear_echo_area ();
2902}
2903#endif /* HANDLE_MAN_PAGES */
2904
2905/* Move to the "Top" node in this file. */
2906DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))
2907{
2908 info_parse_and_select ("Top", window);
2909}
2910
2911/* Move to the node "(dir)Top". */
2912DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))
2913{
2914 info_parse_and_select ("(dir)Top", window);
2915}
2916
2917
2918
2919/* Read the name of a node to kill. The list of available nodes comes
2920 from the nodes appearing in the current window configuration. */
2921static char *
2922read_nodename_to_kill (WINDOW *window)
2923{
2924 int iw;
2925 char *nodename;
2926 INFO_WINDOW *info_win;
2927 REFERENCE **menu = NULL;
2928 int menu_index = 0, menu_slots = 0;
2929 char *default_nodename = xstrdup (active_window->node->nodename);
2930 char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename));
2931
2932 sprintf (prompt, _("Kill node (%s): "), default_nodename);
2933
2934 for (iw = 0; (info_win = info_windows[iw]); iw++)
2935 {
2936 REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2937 entry->label = xstrdup (info_win->window->node->nodename);
2938 entry->filename = entry->nodename = (char *)NULL;
2939
2940 add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,
2941 REFERENCE *);
2942 }
2943
2944 nodename = info_read_completing_in_echo_area (window, prompt, menu);
2945 free (prompt);
2946 info_free_references (menu);
2947 if (nodename && !*nodename)
2948 {
2949 free (nodename);
2950 nodename = default_nodename;
2951 }
2952 else
2953 free (default_nodename);
2954
2955 return nodename;
2956}
2957
2958
2959/* Delete NODENAME from this window, showing the most
2960 recently selected node in this window. */
2961static void
2962kill_node (WINDOW *window, char *nodename)
2963{
2964 int iw, i;
2965 INFO_WINDOW *info_win;
2966 NODE *temp;
2967
2968 /* If there is no nodename to kill, quit now. */
2969 if (!nodename)
2970 {
2971 info_abort_key (window, 0, 0);
2972 return;
2973 }
2974
2975 /* If there is a nodename, find it in our window list. */
2976 for (iw = 0; (info_win = info_windows[iw]); iw++)
2977 if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
2978 && info_win->window == window)
2979 break;
2980
2981 if (!info_win)
2982 {
2983 if (*nodename)
2984 info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL);
2985 else
2986 window_clear_echo_area ();
2987
2988 return;
2989 }
2990
2991 /* If there are no more nodes left anywhere to view, complain and exit. */
2992 if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
2993 {
2994 info_error ((char *) _("Cannot kill the last node"), NULL, NULL);
2995 return;
2996 }
2997
2998 /* INFO_WIN contains the node that the user wants to stop viewing. Delete
2999 this node from the list of nodes previously shown in this window. */
3000 for (i = info_win->current; i < info_win->nodes_index; i++)
3001 info_win->nodes[i] = info_win->nodes[i + 1];
3002
3003 /* There is one less node in this window's history list. */
3004 info_win->nodes_index--;
3005
3006 /* Make this window show the most recent history node. */
3007 info_win->current = info_win->nodes_index - 1;
3008
3009 /* If there aren't any nodes left in this window, steal one from the
3010 next window. */
3011 if (info_win->current < 0)
3012 {
3013 INFO_WINDOW *stealer;
3014 int which, pagetop;
3015 long point;
3016
3017 if (info_windows[iw + 1])
3018 stealer = info_windows[iw + 1];
3019 else
3020 stealer = info_windows[0];
3021
3022 /* If the node being displayed in the next window is not the most
3023 recently loaded one, get the most recently loaded one. */
3024 if ((stealer->nodes_index - 1) != stealer->current)
3025 which = stealer->nodes_index - 1;
3026
3027 /* Else, if there is another node behind the stealers current node,
3028 use that one. */
3029 else if (stealer->current > 0)
3030 which = stealer->current - 1;
3031
3032 /* Else, just use the node appearing in STEALER's window. */
3033 else
3034 which = stealer->current;
3035
3036 /* Copy this node. */
3037 {
3038 NODE *copy = xmalloc (sizeof (NODE));
3039
3040 temp = stealer->nodes[which];
3041 point = stealer->points[which];
3042 pagetop = stealer->pagetops[which];
3043
3044 copy->filename = temp->filename;
3045 copy->parent = temp->parent;
3046 copy->nodename = temp->nodename;
3047 copy->contents = temp->contents;
3048 copy->nodelen = temp->nodelen;
3049 copy->flags = temp->flags;
3050 copy->display_pos = temp->display_pos;
3051
3052 temp = copy;
3053 }
3054
3055 window_set_node_of_window (info_win->window, temp);
3056 window->point = point;
3057 window->pagetop = pagetop;
3058 remember_window_and_node (info_win->window, temp);
3059 }
3060 else
3061 {
3062 temp = info_win->nodes[info_win->current];
3063 temp->display_pos = info_win->points[info_win->current];
3064 window_set_node_of_window (info_win->window, temp);
3065 }
3066
3067 if (!info_error_was_printed)
3068 window_clear_echo_area ();
3069
3070 if (auto_footnotes_p)
3071 info_get_or_remove_footnotes (window);
3072}
3073
3074/* Kill current node, thus going back one in the node history. I (karl)
3075 do not think this is completely correct yet, because of the
3076 window-changing stuff in kill_node, but it's a lot better than the
3077 previous implementation, which did not account for nodes being
3078 visited twice at all. */
3079DECLARE_INFO_COMMAND (info_history_node,
3080 _("Select the most recently selected node"))
3081{
3082 kill_node (window, active_window->node->nodename);
3083}
3084
3085/* Kill named node. */
3086DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))
3087{
3088 char *nodename = read_nodename_to_kill (window);
3089 kill_node (window, nodename);
3090}
3091
3092
3093
3094/* Read the name of a file and select the entire file. */
3095DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))
3096{
3097 char *line;
3098
3099 line = info_read_in_echo_area (window, (char *) _("Find file: "));
3100 if (!line)
3101 {
3102 info_abort_key (active_window, 1, 0);
3103 return;
3104 }
3105
3106 if (*line)
3107 {
3108 NODE *node;
3109
3110 node = info_get_node (line, "*");
3111 if (!node)
3112 {
3113 if (info_recent_file_error)
3114 info_error (info_recent_file_error, NULL, NULL);
3115 else
3116 info_error ((char *) _("Cannot find `%s'."), line, NULL);
3117 }
3118 else
3119 info_set_node_of_window (1, window, node);
3120
3121 free (line);
3122 }
3123
3124 if (!info_error_was_printed)
3125 window_clear_echo_area ();
3126}
3127
3128
3129/* **************************************************************** */
3130/* */
3131/* Dumping and Printing Nodes */
3132/* */
3133/* **************************************************************** */
3134
3135#define VERBOSE_NODE_DUMPING
3136static void write_node_to_stream (NODE *node, FILE *stream);
3137static void dump_node_to_stream (char *filename, char *nodename,
3138 FILE *stream, int dump_subnodes);
3139static void initialize_dumping (void);
3140
3141/* Dump the nodes specified by FILENAME and NODENAMES to the file named
3142 in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3143 the nodes which appear in the menu of each node dumped. */
3144void
3145dump_nodes_to_file (char *filename, char **nodenames,
3146 char *output_filename, int dump_subnodes)
3147{
3148 register int i;
3149 FILE *output_stream;
3150
3151 /* Get the stream to print the nodes to. Special case of an output
3152 filename of "-" means to dump the nodes to stdout. */
3153 if (strcmp (output_filename, "-") == 0)
3154 output_stream = stdout;
3155 else
3156 output_stream = fopen (output_filename, "w");
3157
3158 if (!output_stream)
3159 {
3160 info_error ((char *) _("Could not create output file `%s'."),
3161 output_filename, NULL);
3162 return;
3163 }
3164
3165 /* Print each node to stream. */
3166 initialize_dumping ();
3167 for (i = 0; nodenames[i]; i++)
3168 dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
3169
3170 if (output_stream != stdout)
3171 fclose (output_stream);
3172
3173#if defined (VERBOSE_NODE_DUMPING)
3174 info_error ((char *) _("Done."), NULL, NULL);
3175#endif /* VERBOSE_NODE_DUMPING */
3176}
3177
3178/* A place to remember already dumped nodes. */
3179static char **dumped_already = (char **)NULL;
3180static int dumped_already_index = 0;
3181static int dumped_already_slots = 0;
3182
3183static void
3184initialize_dumping (void)
3185{
3186 dumped_already_index = 0;
3187}
3188
3189/* Get and print the node specified by FILENAME and NODENAME to STREAM.
3190 If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
3191 in the menu of each node dumped. */
3192static void
3193dump_node_to_stream (char *filename, char *nodename,
3194 FILE *stream, int dump_subnodes)
3195{
3196 register int i;
3197 NODE *node;
3198
3199 node = info_get_node (filename, nodename);
3200
3201 if (!node)
3202 {
3203 if (info_recent_file_error)
3204 info_error (info_recent_file_error, NULL, NULL);
3205 else
3206 {
3207 if (filename && *nodename != '(')
3208 info_error ((char *) msg_cant_file_node,
3209 filename_non_directory (filename),
3210 nodename);
3211 else
3212 info_error ((char *) msg_cant_find_node, nodename, NULL);
3213 }
3214 return;
3215 }
3216
3217 /* If we have already dumped this node, don't dump it again. */
3218 for (i = 0; i < dumped_already_index; i++)
3219 if (strcmp (node->nodename, dumped_already[i]) == 0)
3220 {
3221 free (node);
3222 return;
3223 }
3224 add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,
3225 dumped_already_slots, 50, char *);
3226
3227#if defined (VERBOSE_NODE_DUMPING)
3228 /* Maybe we should print some information about the node being output. */
3229 info_error ((char *) _("Writing node %s..."), node_printed_rep (node), NULL);
3230#endif /* VERBOSE_NODE_DUMPING */
3231
3232 write_node_to_stream (node, stream);
3233
3234 /* If we are dumping subnodes, get the list of menu items in this node,
3235 and dump each one recursively. */
3236 if (dump_subnodes)
3237 {
3238 REFERENCE **menu = (REFERENCE **)NULL;
3239
3240 /* If this node is an Index, do not dump the menu references. */
3241 if (string_in_line ("Index", node->nodename) == -1)
3242 menu = info_menu_of_node (node);
3243
3244 if (menu)
3245 {
3246 for (i = 0; menu[i]; i++)
3247 {
3248 /* We don't dump Info files which are different than the
3249 current one. */
3250 if (!menu[i]->filename)
3251 dump_node_to_stream
3252 (filename, menu[i]->nodename, stream, dump_subnodes);
3253 }
3254 info_free_references (menu);
3255 }
3256 }
3257
3258 free (node);
3259}
3260
3261/* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3262 the nodes which appear in the menu of each node dumped. */
3263void
3264dump_node_to_file (NODE *node, char *filename, int dump_subnodes)
3265{
3266 FILE *output_stream;
3267 char *nodes_filename;
3268
3269 /* Get the stream to print this node to. Special case of an output
3270 filename of "-" means to dump the nodes to stdout. */
3271 if (strcmp (filename, "-") == 0)
3272 output_stream = stdout;
3273 else
3274 output_stream = fopen (filename, "w");
3275
3276 if (!output_stream)
3277 {
3278 info_error ((char *) _("Could not create output file `%s'."), filename,
3279 NULL);
3280 return;
3281 }
3282
3283 if (node->parent)
3284 nodes_filename = node->parent;
3285 else
3286 nodes_filename = node->filename;
3287
3288 initialize_dumping ();
3289 dump_node_to_stream
3290 (nodes_filename, node->nodename, output_stream, dump_subnodes);
3291
3292 if (output_stream != stdout)
3293 fclose (output_stream);
3294
3295#if defined (VERBOSE_NODE_DUMPING)
3296 info_error ((char *) _("Done."), NULL, NULL);
3297#endif /* VERBOSE_NODE_DUMPING */
3298}
3299
3300#if !defined (DEFAULT_INFO_PRINT_COMMAND)
3301# define DEFAULT_INFO_PRINT_COMMAND "lpr"
3302#endif /* !DEFAULT_INFO_PRINT_COMMAND */
3303
3304DECLARE_INFO_COMMAND (info_print_node,
3305 _("Pipe the contents of this node through INFO_PRINT_COMMAND"))
3306{
3307 print_node (window->node);
3308}
3309
3310/* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3311void
3312print_node (NODE *node)
3313{
3314 FILE *printer_pipe;
3315 char *print_command = getenv ("INFO_PRINT_COMMAND");
3316 int piping = 0;
3317
3318 if (!print_command || !*print_command)
3319 print_command = DEFAULT_INFO_PRINT_COMMAND;
3320
3321 /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
3322 (default) text mode, since the printer drivers there need to see
3323 DOS-style CRLF pairs at the end of each line.
3324
3325 FIXME: if we are to support Mac-style text files, we might need
3326 to convert the text here. */
3327
3328 /* INFO_PRINT_COMMAND which says ">file" means write to that file.
3329 Presumably, the name of the file is the local printer device. */
3330 if (*print_command == '>')
3331 printer_pipe = fopen (++print_command, "w");
3332 else
3333 {
3334 printer_pipe = popen (print_command, "w");
3335 piping = 1;
3336 }
3337
3338 if (!printer_pipe)
3339 {
3340 info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL);
3341 return;
3342 }
3343
3344#if defined (VERBOSE_NODE_DUMPING)
3345 /* Maybe we should print some information about the node being output. */
3346 info_error ((char *) _("Printing node %s..."), node_printed_rep (node), NULL);
3347#endif /* VERBOSE_NODE_DUMPING */
3348
3349 write_node_to_stream (node, printer_pipe);
3350 if (piping)
3351 pclose (printer_pipe);
3352 else
3353 fclose (printer_pipe);
3354
3355#if defined (VERBOSE_NODE_DUMPING)
3356 info_error ((char *) _("Done."), NULL, NULL);
3357#endif /* VERBOSE_NODE_DUMPING */
3358}
3359
3360static void
3361write_node_to_stream (NODE *node, FILE *stream)
3362{
3363 fwrite (node->contents, 1, node->nodelen, stream);
3364}
3365
3366
3367/* **************************************************************** */
3368/* */
3369/* Info Searching Commands */
3370/* */
3371/* **************************************************************** */
3372
3373/* Variable controlling the garbage collection of files briefly visited
3374 during searches. Such files are normally gc'ed, unless they were
3375 compressed to begin with. If this variable is non-zero, it says
3376 to gc even those file buffer contents which had to be uncompressed. */
3377int gc_compressed_files = 0;
3378
3379static void info_gc_file_buffers (void);
3380static void info_search_1 (WINDOW *window, int count,
3381 unsigned char key, int case_sensitive, int ask_for_string);
3382
3383static char *search_string = (char *)NULL;
3384static int search_string_size = 0;
3385static int isearch_is_active = 0;
3386
3387static int last_search_direction = 0;
3388static int last_search_case_sensitive = 0;
3389
3390/* Return the file buffer which belongs to WINDOW's node. */
3391FILE_BUFFER *
3392file_buffer_of_window (WINDOW *window)
3393{
3394 /* If this window has no node, then it has no file buffer. */
3395 if (!window->node)
3396 return ((FILE_BUFFER *)NULL);
3397
3398 if (window->node->parent)
3399 return (info_find_file (window->node->parent));
3400
3401 if (window->node->filename)
3402 return (info_find_file (window->node->filename));
3403
3404 return ((FILE_BUFFER *)NULL);
3405}
3406
3407/* Search for STRING in NODE starting at START. Return -1 if the string
3408 was not found, or the location of the string if it was. If WINDOW is
3409 passed as non-null, set the window's node to be NODE, its point to be
3410 the found string, and readjust the window's pagetop. Final argument
3411 DIR says which direction to search in. If it is positive, search
3412 forward, else backwards. */
3413long
3414info_search_in_node (char *string, NODE *node, long int start,
3415 WINDOW *window, int dir, int case_sensitive)
3416{
3417 SEARCH_BINDING binding;
3418 long offset;
3419
3420 binding.buffer = node->contents;
3421 binding.start = start;
3422 binding.end = node->nodelen;
3423 binding.flags = 0;
3424 if (!case_sensitive)
3425 binding.flags |= S_FoldCase;
3426
3427 if (dir < 0)
3428 {
3429 binding.end = 0;
3430 binding.flags |= S_SkipDest;
3431 }
3432
3433 if (binding.start < 0)
3434 return (-1);
3435
3436 /* For incremental searches, we always wish to skip past the string. */
3437 if (isearch_is_active)
3438 binding.flags |= S_SkipDest;
3439
3440 offset = search (string, &binding);
3441
3442 if (offset != -1 && window)
3443 {
3444 set_remembered_pagetop_and_point (window);
3445 if (window->node != node)
3446 window_set_node_of_window (window, node);
3447 window->point = offset;
3448 window_adjust_pagetop (window);
3449 }
3450 return (offset);
3451}
3452
3453/* Search NODE, looking for the largest possible match of STRING. Start the
3454 search at START. Return the absolute position of the match, or -1, if
3455 no part of the string could be found. */
3456long
3457info_target_search_node (NODE *node, char *string, long int start)
3458{
3459 register int i;
3460 long offset = 0;
3461 char *target;
3462
3463 target = xstrdup (string);
3464 i = strlen (target);
3465
3466 /* Try repeatedly searching for this string while removing words from
3467 the end of it. */
3468 while (i)
3469 {
3470 target[i] = '\0';
3471 offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0);
3472
3473 if (offset != -1)
3474 break;
3475
3476 /* Delete the last word from TARGET. */
3477 for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--);
3478 }
3479 free (target);
3480 return (offset);
3481}
3482
3483/* Search for STRING starting in WINDOW at point. If the string is found
3484 in this node, set point to that position. Otherwise, get the file buffer
3485 associated with WINDOW's node, and search through each node in that file.
3486 If the search fails, return non-zero, else zero. Side-effect window
3487 leaving the node and point where the string was found current. */
3488static int
3489info_search_internal (char *string, WINDOW *window,
3490 int dir, int case_sensitive)
3491{
3492 register int i;
3493 FILE_BUFFER *file_buffer;
3494 char *initial_nodename;
3495 long ret, start = 0;
3496
3497 file_buffer = file_buffer_of_window (window);
3498 initial_nodename = window->node->nodename;
3499
3500 /* This used to begin from window->point, unless this was a repeated
3501 search command. But invoking search with an argument loses with
3502 that logic, since info_last_executed_command is then set to
3503 info_add_digit_to_numeric_arg. I think there's no sense in
3504 ``finding'' a string that is already under the cursor, anyway. */
3505 ret = info_search_in_node
3506 (string, window->node, window->point + dir, window, dir,
3507 case_sensitive);
3508
3509 if (ret != -1)
3510 {
3511 /* We won! */
3512 if (!echo_area_is_active && !isearch_is_active)
3513 window_clear_echo_area ();
3514 return (0);
3515 }
3516
3517 /* The string wasn't found in the current node. Search through the
3518 window's file buffer, iff the current node is not "*". */
3519 if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
3520 return (-1);
3521
3522 /* If this file has tags, search through every subfile, starting at
3523 this node's subfile and node. Otherwise, search through the
3524 file's node list. */
3525 if (file_buffer->tags)
3526 {
3527 register int current_tag = 0, number_of_tags;
3528 char *last_subfile;
3529 TAG *tag;
3530
3531 /* Find number of tags and current tag. */
3532 last_subfile = (char *)NULL;
3533 for (i = 0; file_buffer->tags[i]; i++)
3534 if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
3535 {
3536 current_tag = i;
3537 last_subfile = file_buffer->tags[i]->filename;
3538 }
3539
3540 number_of_tags = i;
3541
3542 /* If there is no last_subfile, our tag wasn't found. */
3543 if (!last_subfile)
3544 return (-1);
3545
3546 /* Search through subsequent nodes, wrapping around to the top
3547 of the info file until we find the string or return to this
3548 window's node and point. */
3549 while (1)
3550 {
3551 NODE *node;
3552
3553 /* Allow C-g to quit the search, failing it if pressed. */
3554 return_if_control_g (-1);
3555
3556 /* Find the next tag that isn't an anchor. */
3557 for (i = current_tag + dir; i != current_tag; i += dir)
3558 {
3559 if (i < 0)
3560 i = number_of_tags - 1;
3561 else if (i == number_of_tags)
3562 i = 0;
3563
3564 tag = file_buffer->tags[i];
3565 if (tag->nodelen != 0)
3566 break;
3567 }
3568
3569 /* If we got past out starting point, bail out. */
3570 if (i == current_tag)
3571 return (-1);
3572 current_tag = i;
3573
3574 if (!echo_area_is_active && (last_subfile != tag->filename))
3575 {
3576 window_message_in_echo_area
3577 ((char *) _("Searching subfile %s ..."),
3578 filename_non_directory (tag->filename), NULL);
3579
3580 last_subfile = tag->filename;
3581 }
3582
3583 node = info_get_node (file_buffer->filename, tag->nodename);
3584
3585 if (!node)
3586 {
3587 /* If not doing i-search... */
3588 if (!echo_area_is_active)
3589 {
3590 if (info_recent_file_error)
3591 info_error (info_recent_file_error, NULL, NULL);
3592 else
3593 info_error ((char *) msg_cant_file_node,
3594 filename_non_directory (file_buffer->filename),
3595 tag->nodename);
3596 }
3597 return (-1);
3598 }
3599
3600 if (dir < 0)
3601 start = tag->nodelen;
3602
3603 ret =
3604 info_search_in_node (string, node, start, window, dir,
3605 case_sensitive);
3606
3607 /* Did we find the string in this node? */
3608 if (ret != -1)
3609 {
3610 /* Yes! We win. */
3611 remember_window_and_node (window, node);
3612 if (!echo_area_is_active)
3613 window_clear_echo_area ();
3614 return (0);
3615 }
3616
3617 /* No. Free this node, and make sure that we haven't passed
3618 our starting point. */
3619 free (node);
3620
3621 if (strcmp (initial_nodename, tag->nodename) == 0)
3622 return (-1);
3623 }
3624 }
3625 return (-1);
3626}
3627
3628DECLARE_INFO_COMMAND (info_search_case_sensitively,
3629 _("Read a string and search for it case-sensitively"))
3630{
3631 last_search_direction = count > 0 ? 1 : -1;
3632 last_search_case_sensitive = 1;
3633 info_search_1 (window, count, key, 1, 1);
3634}
3635
3636DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))
3637{
3638 last_search_direction = count > 0 ? 1 : -1;
3639 last_search_case_sensitive = 0;
3640 info_search_1 (window, count, key, 0, 1);
3641}
3642
3643DECLARE_INFO_COMMAND (info_search_backward,
3644 _("Read a string and search backward for it"))
3645{
3646 last_search_direction = count > 0 ? -1 : 1;
3647 last_search_case_sensitive = 0;
3648 info_search_1 (window, -count, key, 0, 1);
3649}
3650
3651static void
3652info_search_1 (WINDOW *window, int count, unsigned char key,
3653 int case_sensitive, int ask_for_string)
3654{
3655 char *line, *prompt;
3656 int result, old_pagetop;
3657 int direction;
3658
3659 if (count < 0)
3660 {
3661 direction = -1;
3662 count = -count;
3663 }
3664 else
3665 {
3666 direction = 1;
3667 if (count == 0)
3668 count = 1; /* for backward compatibility */
3669 }
3670
3671 /* Read a string from the user, defaulting the search to SEARCH_STRING. */
3672 if (!search_string)
3673 {
3674 search_string = (char *)xmalloc (search_string_size = 100);
3675 search_string[0] = '\0';
3676 }
3677
3678 if (ask_for_string)
3679 {
3680 prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: "))
3681 + strlen (_("Search backward"))
3682 + strlen (_("Search"))
3683 + strlen (_(" case-sensitively "))
3684 + strlen (_(" "))
3685 + strlen (search_string));
3686
3687 sprintf (prompt, _("%s%sfor string [%s]: "),
3688 direction < 0 ? _("Search backward") : _("Search"),
3689 case_sensitive ? _(" case-sensitively ") : _(" "),
3690 search_string);
3691
3692 line = info_read_in_echo_area (window, prompt);
3693 free (prompt);
3694
3695 if (!line)
3696 {
3697 info_abort_key (window, 0, 0);
3698 return;
3699 }
3700
3701 if (*line)
3702 {
3703 if (strlen (line) + 1 > (unsigned int) search_string_size)
3704 search_string = (char *) xrealloc
3705 (search_string, (search_string_size += 50 + strlen (line)));
3706
3707 strcpy (search_string, line);
3708 free (line);
3709 }
3710 }
3711
3712 /* If the search string includes upper-case letters, make the search
3713 case-sensitive. */
3714 if (case_sensitive == 0)
3715 for (line = search_string; *line; line++)
3716 if (isupper (*line))
3717 {
3718 case_sensitive = 1;
3719 break;
3720 }
3721
3722 old_pagetop = active_window->pagetop;
3723 for (result = 0; result == 0 && count--; )
3724 result = info_search_internal (search_string,
3725 active_window, direction, case_sensitive);
3726
3727 if (result != 0 && !info_error_was_printed)
3728 info_error ((char *) _("Search failed."), NULL, NULL);
3729 else if (old_pagetop != active_window->pagetop)
3730 {
3731 int new_pagetop;
3732
3733 new_pagetop = active_window->pagetop;
3734 active_window->pagetop = old_pagetop;
3735 set_window_pagetop (active_window, new_pagetop);
3736 if (auto_footnotes_p)
3737 info_get_or_remove_footnotes (active_window);
3738 }
3739
3740 /* Perhaps free the unreferenced file buffers that were searched, but
3741 not retained. */
3742 info_gc_file_buffers ();
3743}
3744
3745DECLARE_INFO_COMMAND (info_search_next,
3746 _("Repeat last search in the same direction"))
3747{
3748 if (!last_search_direction)
3749 info_error ((char *) _("No previous search string"), NULL, NULL);
3750 else
3751 info_search_1 (window, last_search_direction * count,
3752 key, last_search_case_sensitive, 0);
3753}
3754
3755DECLARE_INFO_COMMAND (info_search_previous,
3756 _("Repeat last search in the reverse direction"))
3757{
3758 if (!last_search_direction)
3759 info_error ((char *) _("No previous search string"), NULL, NULL);
3760 else
3761 info_search_1 (window, -last_search_direction * count,
3762 key, last_search_case_sensitive, 0);
3763}
3764
3765/* **************************************************************** */
3766/* */
3767/* Incremental Searching */
3768/* */
3769/* **************************************************************** */
3770
3771static void incremental_search (WINDOW *window, int count,
3772 unsigned char ignore);
3773
3774DECLARE_INFO_COMMAND (isearch_forward,
3775 _("Search interactively for a string as you type it"))
3776{
3777 incremental_search (window, count, key);
3778}
3779
3780DECLARE_INFO_COMMAND (isearch_backward,
3781 _("Search interactively for a string as you type it"))
3782{
3783 incremental_search (window, -count, key);
3784}
3785
3786/* Incrementally search for a string as it is typed. */
3787/* The last accepted incremental search string. */
3788static char *last_isearch_accepted = (char *)NULL;
3789
3790/* The current incremental search string. */
3791static char *isearch_string = (char *)NULL;
3792static int isearch_string_index = 0;
3793static int isearch_string_size = 0;
3794static unsigned char isearch_terminate_search_key = ESC;
3795
3796/* Array of search states. */
3797static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL;
3798static int isearch_states_index = 0;
3799static int isearch_states_slots = 0;
3800
3801/* Push the state of this search. */
3802static void
3803push_isearch (WINDOW *window, int search_index, int direction, int failing)
3804{
3805 SEARCH_STATE *state;
3806
3807 state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
3808 window_get_state (window, state);
3809 state->search_index = search_index;
3810 state->direction = direction;
3811 state->failing = failing;
3812
3813 add_pointer_to_array (state, isearch_states_index, isearch_states,
3814 isearch_states_slots, 20, SEARCH_STATE *);
3815}
3816
3817/* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3818static void
3819pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing)
3820{
3821 SEARCH_STATE *state;
3822
3823 if (isearch_states_index)
3824 {
3825 isearch_states_index--;
3826 state = isearch_states[isearch_states_index];
3827 window_set_state (window, state);
3828 *search_index = state->search_index;
3829 *direction = state->direction;
3830 *failing = state->failing;
3831
3832 free (state);
3833 isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL;
3834 }
3835}
3836
3837/* Free the memory used by isearch_states. */
3838static void
3839free_isearch_states (void)
3840{
3841 register int i;
3842
3843 for (i = 0; i < isearch_states_index; i++)
3844 {
3845 free (isearch_states[i]);
3846 isearch_states[i] = (SEARCH_STATE *)NULL;
3847 }
3848 isearch_states_index = 0;
3849}
3850
3851/* Display the current search in the echo area. */
3852static void
3853show_isearch_prompt (int dir, unsigned char *string, int failing_p)
3854{
3855 register int i;
3856 const char *prefix;
3857 char *prompt, *p_rep;
3858 unsigned int prompt_len, p_rep_index, p_rep_size;
3859
3860 if (dir < 0)
3861 prefix = _("I-search backward: ");
3862 else
3863 prefix = _("I-search: ");
3864
3865 p_rep_index = p_rep_size = 0;
3866 p_rep = (char *)NULL;
3867 for (i = 0; string[i]; i++)
3868 {
3869 char *rep;
3870
3871 switch (string[i])
3872 {
3873 case ' ': rep = " "; break;
3874 case LFD: rep = "\\n"; break;
3875 case TAB: rep = "\\t"; break;
3876 default:
3877 rep = pretty_keyname (string[i]);
3878 }
3879 if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3880 p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3881
3882 strcpy (p_rep + p_rep_index, rep);
3883 p_rep_index += strlen (rep);
3884 }
3885
3886 prompt_len = strlen (prefix) + p_rep_index + 1;
3887 if (failing_p)
3888 prompt_len += strlen (_("Failing "));
3889 prompt = xmalloc (prompt_len);
3890 sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix,
3891 p_rep ? p_rep : "");
3892
3893 window_message_in_echo_area ("%s", prompt, NULL);
3894 maybe_free (p_rep);
3895 free (prompt);
3896 display_cursor_at_point (active_window);
3897}
3898
3899static void
3900incremental_search (WINDOW *window, int count, unsigned char ignore)
3901{
3902 unsigned char key;
3903 int last_search_result, search_result, dir;
3904 SEARCH_STATE mystate, orig_state;
3905 char *p;
3906 int case_sensitive = 0;
3907
3908 if (count < 0)
3909 dir = -1;
3910 else
3911 dir = 1;
3912
3913 last_search_result = search_result = 0;
3914
3915 window_get_state (window, &orig_state);
3916
3917 isearch_string_index = 0;
3918 if (!isearch_string_size)
3919 isearch_string = (char *)xmalloc (isearch_string_size = 50);
3920
3921 /* Show the search string in the echo area. */
3922 isearch_string[isearch_string_index] = '\0';
3923 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
3924
3925 isearch_is_active = 1;
3926
3927 while (isearch_is_active)
3928 {
3929 VFunction *func = (VFunction *)NULL;
3930 int quoted = 0;
3931
3932 /* If a recent display was interrupted, then do the redisplay now if
3933 it is convenient. */
3934 if (!info_any_buffered_input_p () && display_was_interrupted_p)
3935 {
3936 display_update_one_window (window);
3937 display_cursor_at_point (active_window);
3938 }
3939
3940 /* Read a character and dispatch on it. */
3941 key = info_get_input_char ();
3942 window_get_state (window, &mystate);
3943
3944 if (key == DEL || key == Control ('h'))
3945 {
3946 /* User wants to delete one level of search? */
3947 if (!isearch_states_index)
3948 {
3949 terminal_ring_bell ();
3950 continue;
3951 }
3952 else
3953 {
3954 pop_isearch
3955 (window, &isearch_string_index, &dir, &search_result);
3956 isearch_string[isearch_string_index] = '\0';
3957 show_isearch_prompt (dir, (unsigned char *) isearch_string,
3958 search_result);
3959 goto after_search;
3960 }
3961 }
3962 else if (key == Control ('q'))
3963 {
3964 key = info_get_input_char ();
3965 quoted = 1;
3966 }
3967
3968 /* We are about to search again, or quit. Save the current search. */
3969 push_isearch (window, isearch_string_index, dir, search_result);
3970
3971 if (quoted)
3972 goto insert_and_search;
3973
3974 if (!Meta_p (key) || key > 32)
3975 {
3976 /* If this key is not a keymap, get its associated function,
3977 if any. If it is a keymap, then it's probably ESC from an
3978 arrow key, and we handle that case below. */
3979 char type = window->keymap[key].type;
3980 func = type == ISFUNC
3981 ? InfoFunction(window->keymap[key].function)
3982 : NULL; /* function member is a Keymap if ISKMAP */
3983
3984 if (isprint (key) || (type == ISFUNC && func == NULL))
3985 {
3986 insert_and_search:
3987
3988 if (isearch_string_index + 2 >= isearch_string_size)
3989 isearch_string = (char *)xrealloc
3990 (isearch_string, isearch_string_size += 100);
3991
3992 isearch_string[isearch_string_index++] = key;
3993 isearch_string[isearch_string_index] = '\0';
3994 goto search_now;
3995 }
3996 else if (func == (VFunction *) isearch_forward
3997 || func == (VFunction *) isearch_backward)
3998 {
3999 /* If this key invokes an incremental search, then this
4000 means that we will either search again in the same
4001 direction, search again in the reverse direction, or
4002 insert the last search string that was accepted through
4003 incremental searching. */
4004 if ((func == (VFunction *) isearch_forward && dir > 0) ||
4005 (func == (VFunction *) isearch_backward && dir < 0))
4006 {
4007 /* If the user has typed no characters, then insert the
4008 last successful search into the current search string. */
4009 if (isearch_string_index == 0)
4010 {
4011 /* Of course, there must be something to insert. */
4012 if (last_isearch_accepted)
4013 {
4014 if (strlen ((char *) last_isearch_accepted) + 1
4015 >= (unsigned int) isearch_string_size)
4016 isearch_string = (char *)
4017 xrealloc (isearch_string,
4018 isearch_string_size += 10 +
4019 strlen (last_isearch_accepted));
4020 strcpy (isearch_string, last_isearch_accepted);
4021 isearch_string_index = strlen (isearch_string);
4022 goto search_now;
4023 }
4024 else
4025 continue;
4026 }
4027 else
4028 {
4029 /* Search again in the same direction. This means start
4030 from a new place if the last search was successful. */
4031 if (search_result == 0)
4032 window->point += dir;
4033 }
4034 }
4035 else
4036 {
4037 /* Reverse the direction of the search. */
4038 dir = -dir;
4039 }
4040 }
4041 else if (func == (VFunction *) info_abort_key)
4042 {
4043 /* If C-g pressed, and the search is failing, pop the search
4044 stack back to the last unfailed search. */
4045 if (isearch_states_index && (search_result != 0))
4046 {
4047 terminal_ring_bell ();
4048 while (isearch_states_index && (search_result != 0))
4049 pop_isearch
4050 (window, &isearch_string_index, &dir, &search_result);
4051 isearch_string[isearch_string_index] = '\0';
4052 show_isearch_prompt (dir, (unsigned char *) isearch_string,
4053 search_result);
4054 continue;
4055 }
4056 else
4057 goto exit_search;
4058 }
4059 else
4060 goto exit_search;
4061 }
4062 else
4063 {
4064 exit_search:
4065 /* The character is not printable, or it has a function which is
4066 non-null. Exit the search, remembering the search string. If
4067 the key is not the same as the isearch_terminate_search_key,
4068 then push it into pending input. */
4069 if (isearch_string_index && func != (VFunction *) info_abort_key)
4070 {
4071 maybe_free (last_isearch_accepted);
4072 last_isearch_accepted = xstrdup (isearch_string);
4073 }
4074
4075 /* If the key is the isearch_terminate_search_key, but some buffered
4076 input is pending, it is almost invariably because the ESC key is
4077 actually the beginning of an escape sequence, like in case they
4078 pressed an arrow key. So don't gobble the ESC key, push it back
4079 into pending input. */
4080 /* FIXME: this seems like a kludge! We need a more reliable
4081 mechanism to know when ESC is a separate key and when it is
4082 part of an escape sequence. */
4083 if (key != RET /* Emacs addicts want RET to get lost */
4084 && (key != isearch_terminate_search_key
4085 || info_any_buffered_input_p ()))
4086 info_set_pending_input (key);
4087
4088 if (func == (VFunction *) info_abort_key)
4089 {
4090 if (isearch_states_index)
4091 window_set_state (window, &orig_state);
4092 }
4093
4094 if (!echo_area_is_active)
4095 window_clear_echo_area ();
4096
4097 if (auto_footnotes_p)
4098 info_get_or_remove_footnotes (active_window);
4099
4100 isearch_is_active = 0;
4101 continue;
4102 }
4103
4104 /* Search for the contents of isearch_string. */
4105 search_now:
4106 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4107
4108 /* If the search string includes upper-case letters, make the
4109 search case-sensitive. */
4110 for (p = isearch_string; *p; p++)
4111 if (isupper (*p))
4112 {
4113 case_sensitive = 1;
4114 break;
4115 }
4116
4117
4118 if (search_result == 0)
4119 {
4120 /* Check to see if the current search string is right here. If
4121 we are looking at it, then don't bother calling the search
4122 function. */
4123 if (((dir < 0) &&
4124 ((case_sensitive ? strncmp : strncasecmp)
4125 (window->node->contents + window->point,
4126 isearch_string, isearch_string_index) == 0)) ||
4127 ((dir > 0) &&
4128 ((window->point - isearch_string_index) >= 0) &&
4129 ((case_sensitive ? strncmp : strncasecmp)
4130 (window->node->contents +
4131 (window->point - (isearch_string_index - 1)),
4132 isearch_string, isearch_string_index) == 0)))
4133 {
4134 if (dir > 0)
4135 window->point++;
4136 }
4137 else
4138 search_result = info_search_internal (isearch_string,
4139 window, dir, case_sensitive);
4140 }
4141
4142 /* If this search failed, and we didn't already have a failed search,
4143 then ring the terminal bell. */
4144 if (search_result != 0 && last_search_result == 0)
4145 terminal_ring_bell ();
4146
4147 after_search:
4148 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4149
4150 if (search_result == 0)
4151 {
4152 if ((mystate.node == window->node) &&
4153 (mystate.pagetop != window->pagetop))
4154 {
4155 int newtop = window->pagetop;
4156 window->pagetop = mystate.pagetop;
4157 set_window_pagetop (window, newtop);
4158 }
4159 display_update_one_window (window);
4160 display_cursor_at_point (window);
4161 }
4162
4163 last_search_result = search_result;
4164 }
4165
4166 /* Free the memory used to remember each search state. */
4167 free_isearch_states ();
4168
4169 /* Perhaps GC some file buffers. */
4170 info_gc_file_buffers ();
4171
4172 /* After searching, leave the window in the correct state. */
4173 if (!echo_area_is_active)
4174 window_clear_echo_area ();
4175}
4176
4177/* GC some file buffers. A file buffer can be gc-ed if there we have
4178 no nodes in INFO_WINDOWS that reference this file buffer's contents.
4179 Garbage collecting a file buffer means to free the file buffers
4180 contents. */
4181static void
4182info_gc_file_buffers (void)
4183{
4184 register int fb_index, iw_index, i;
4185 register FILE_BUFFER *fb;
4186 register INFO_WINDOW *iw;
4187
4188 if (!info_loaded_files)
4189 return;
4190
4191 for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4192 {
4193 int fb_referenced_p = 0;
4194
4195 /* If already gc-ed, do nothing. */
4196 if (!fb->contents)
4197 continue;
4198
4199 /* If this file had to be uncompressed, check to see if we should
4200 gc it. This means that the user-variable "gc-compressed-files"
4201 is non-zero. */
4202 if ((fb->flags & N_IsCompressed) && !gc_compressed_files)
4203 continue;
4204
4205 /* If this file's contents are not gc-able, move on. */
4206 if (fb->flags & N_CannotGC)
4207 continue;
4208
4209 /* Check each INFO_WINDOW to see if it has any nodes which reference
4210 this file. */
4211 for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
4212 {
4213 for (i = 0; iw->nodes && iw->nodes[i]; i++)
4214 {
4215 if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4216 (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0))
4217 {
4218 fb_referenced_p = 1;
4219 break;
4220 }
4221 }
4222 }
4223
4224 /* If this file buffer wasn't referenced, free its contents. */
4225 if (!fb_referenced_p)
4226 {
4227 free (fb->contents);
4228 fb->contents = (char *)NULL;
4229 }
4230 }
4231}
4232
4233
4234/* **************************************************************** */
4235/* */
4236/* Traversing and Selecting References */
4237/* */
4238/* **************************************************************** */
4239
4240/* Move to the next or previous cross reference in this node. */
4241static void
4242info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir)
4243{
4244 long firstmenu, firstxref;
4245 long nextmenu, nextxref;
4246 long placement = -1;
4247 long start = 0;
4248 NODE *node = window->node;
4249
4250 if (dir < 0)
4251 start = node->nodelen;
4252
4253 /* This search is only allowed to fail if there is no menu or cross
4254 reference in the current node. Otherwise, the first menu or xref
4255 found is moved to. */
4256
4257 firstmenu = info_search_in_node
4258 (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4259
4260 /* FIRSTMENU may point directly to the line defining the menu. Skip that
4261 and go directly to the first item. */
4262
4263 if (firstmenu != -1)
4264 {
4265 char *text = node->contents + firstmenu;
4266
4267 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4268 firstmenu = info_search_in_node
4269 (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0);
4270 }
4271
4272 firstxref =
4273 info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
4274
4275#if defined (HANDLE_MAN_PAGES)
4276 if ((firstxref == -1) && (node->flags & N_IsManPage))
4277 {
4278 firstxref = locate_manpage_xref (node, start, dir);
4279 }
4280#endif /* HANDLE_MAN_PAGES */
4281
4282 if (firstmenu == -1 && firstxref == -1)
4283 {
4284 info_error ((char *) msg_no_xref_node, NULL, NULL);
4285 return;
4286 }
4287
4288 /* There is at least one cross reference or menu entry in this node.
4289 Try hard to find the next available one. */
4290
4291 nextmenu = info_search_in_node
4292 (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4293
4294 nextxref = info_search_in_node
4295 (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
4296
4297#if defined (HANDLE_MAN_PAGES)
4298 if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
4299 nextxref = locate_manpage_xref (node, window->point + dir, dir);
4300#endif /* HANDLE_MAN_PAGES */
4301
4302 /* Ignore "Menu:" as a menu item. */
4303 if (nextmenu != -1)
4304 {
4305 char *text = node->contents + nextmenu;
4306
4307 if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
4308 nextmenu = info_search_in_node
4309 (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
4310 }
4311
4312 /* If there is both a next menu entry, and a next xref entry, choose the
4313 one which occurs first. Otherwise, select the one which actually
4314 appears in this node following point. */
4315 if (nextmenu != -1 && nextxref != -1)
4316 {
4317 if (((dir == 1) && (nextmenu < nextxref)) ||
4318 ((dir == -1) && (nextmenu > nextxref)))
4319 placement = nextmenu + 1;
4320 else
4321 placement = nextxref;
4322 }
4323 else if (nextmenu != -1)
4324 placement = nextmenu + 1;
4325 else if (nextxref != -1)
4326 placement = nextxref;
4327
4328 /* If there was neither a menu or xref entry appearing in this node after
4329 point, choose the first menu or xref entry appearing in this node. */
4330 if (placement == -1)
4331 {
4332 if (firstmenu != -1 && firstxref != -1)
4333 {
4334 if (((dir == 1) && (firstmenu < firstxref)) ||
4335 ((dir == -1) && (firstmenu > firstxref)))
4336 placement = firstmenu + 1;
4337 else
4338 placement = firstxref;
4339 }
4340 else if (firstmenu != -1)
4341 placement = firstmenu + 1;
4342 else
4343 placement = firstxref;
4344 }
4345 window->point = placement;
4346 window_adjust_pagetop (window);
4347 window->flags |= W_UpdateWindow;
4348}
4349
4350DECLARE_INFO_COMMAND (info_move_to_prev_xref,
4351 _("Move to the previous cross reference"))
4352{
4353 if (count < 0)
4354 info_move_to_prev_xref (window, -count, key);
4355 else
4356 info_move_to_xref (window, count, key, -1);
4357}
4358
4359DECLARE_INFO_COMMAND (info_move_to_next_xref,
4360 _("Move to the next cross reference"))
4361{
4362 if (count < 0)
4363 info_move_to_next_xref (window, -count, key);
4364 else
4365 info_move_to_xref (window, count, key, 1);
4366}
4367
4368/* Select the menu item or reference that appears on this line. */
4369DECLARE_INFO_COMMAND (info_select_reference_this_line,
4370 _("Select reference or menu item appearing on this line"))
4371{
4372 char *line;
4373
4374 if (window->line_starts)
4375 line = window->line_starts[window_line_of_point (window)];
4376 else
4377 line = "";
4378
4379 /* If this line contains a menu item, select that one. */
4380 if (strncmp ("* ", line, 2) == 0)
4381 info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
4382 else
4383 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
4384}
4385
4386
4387/* **************************************************************** */
4388/* */
4389/* Miscellaneous Info Commands */
4390/* */
4391/* **************************************************************** */
4392
4393/* What to do when C-g is pressed in a window. */
4394DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))
4395{
4396 /* If error printing doesn't oridinarily ring the bell, do it now,
4397 since C-g always rings the bell. Otherwise, let the error printer
4398 do it. */
4399 if (!info_error_rings_bell_p)
4400 terminal_ring_bell ();
4401 info_error ((char *) _("Quit"), NULL, NULL);
4402
4403 info_initialize_numeric_arg ();
4404 info_clear_pending_input ();
4405 info_last_executed_command = (VFunction *)NULL;
4406}
4407
4408/* Move the cursor to the desired line of the window. */
4409DECLARE_INFO_COMMAND (info_move_to_window_line,
4410 _("Move the cursor to a specific line of the window"))
4411{
4412 int line;
4413
4414 /* With no numeric argument of any kind, default to the center line. */
4415 if (!info_explicit_arg && count == 1)
4416 line = (window->height / 2) + window->pagetop;
4417 else
4418 {
4419 if (count < 0)
4420 line = (window->height + count) + window->pagetop;
4421 else
4422 line = window->pagetop + count;
4423 }
4424
4425 /* If the line doesn't appear in this window, make it do so. */
4426 if ((line - window->pagetop) >= window->height)
4427 line = window->pagetop + (window->height - 1);
4428
4429 /* If the line is too small, make it fit. */
4430 if (line < window->pagetop)
4431 line = window->pagetop;
4432
4433 /* If the selected line is past the bottom of the node, force it back. */
4434 if (line >= window->line_count)
4435 line = window->line_count - 1;
4436
4437 window->point = (window->line_starts[line] - window->node->contents);
4438}
4439
4440/* Clear the screen and redraw its contents. Given a numeric argument,
4441 move the line the cursor is on to the COUNT'th line of the window. */
4442DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))
4443{
4444 if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4445 {
4446 terminal_clear_screen ();
4447 display_clear_display (the_display);
4448 window_mark_chain (windows, W_UpdateWindow);
4449 display_update_display (windows);
4450 }
4451 else
4452 {
4453 int desired_line, point_line;
4454 int new_pagetop;
4455
4456 point_line = window_line_of_point (window) - window->pagetop;
4457
4458 if (count < 0)
4459 desired_line = window->height + count;
4460 else
4461 desired_line = count;
4462
4463 if (desired_line < 0)
4464 desired_line = 0;
4465
4466 if (desired_line >= window->height)
4467 desired_line = window->height - 1;
4468
4469 if (desired_line == point_line)
4470 return;
4471
4472 new_pagetop = window->pagetop + (point_line - desired_line);
4473
4474 set_window_pagetop (window, new_pagetop);
4475 }
4476}
4477/* This command does nothing. It is the fact that a key is bound to it
4478 that has meaning. See the code at the top of info_session (). */
4479DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))
4480{}
4481
4482
4483
4484/* **************************************************************** */
4485/* */
4486/* Reading Keys and Dispatching on Them */
4487/* */
4488/* **************************************************************** */
4489
4490/* Declaration only. Special cased in info_dispatch_on_key ().
4491 Doc string is to avoid ugly results with describe_key etc. */
4492DECLARE_INFO_COMMAND (info_do_lowercase_version,
4493 _("Run command bound to this key's lowercase variant"))
4494{}
4495
4496static void
4497dispatch_error (char *keyseq)
4498{
4499 char *rep;
4500
4501 rep = pretty_keyseq (keyseq);
4502
4503 if (!echo_area_is_active)
4504 info_error ((char *) _("Unknown command (%s)."), rep, NULL);
4505 else
4506 {
4507 char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid")));
4508 sprintf (temp, _("`%s' is invalid"), rep);
4509 terminal_ring_bell ();
4510 inform_in_echo_area (temp);
4511 free (temp);
4512 }
4513}
4514
4515/* Keeping track of key sequences. */
4516static char *info_keyseq = (char *)NULL;
4517static int info_keyseq_index = 0;
4518static int info_keyseq_size = 0;
4519static int info_keyseq_displayed_p = 0;
4520
4521/* Initialize the length of the current key sequence. */
4522void
4523initialize_keyseq (void)
4524{
4525 info_keyseq_index = 0;
4526 info_keyseq_displayed_p = 0;
4527}
4528
4529/* Add CHARACTER to the current key sequence. */
4530void
4531add_char_to_keyseq (char character)
4532{
4533 if (info_keyseq_index + 2 >= info_keyseq_size)
4534 info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
4535
4536 info_keyseq[info_keyseq_index++] = character;
4537 info_keyseq[info_keyseq_index] = '\0';
4538}
4539
4540/* Display the current value of info_keyseq. If argument EXPECTING is
4541 non-zero, input is expected to be read after the key sequence is
4542 displayed, so add an additional prompting character to the sequence. */
4543static void
4544display_info_keyseq (int expecting_future_input)
4545{
4546 char *rep;
4547
4548 rep = pretty_keyseq (info_keyseq);
4549 if (expecting_future_input)
4550 strcat (rep, "-");
4551
4552 if (echo_area_is_active)
4553 inform_in_echo_area (rep);
4554 else
4555 {
4556 window_message_in_echo_area (rep, NULL, NULL);
4557 display_cursor_at_point (active_window);
4558 }
4559 info_keyseq_displayed_p = 1;
4560}
4561
4562/* Called by interactive commands to read a keystroke. */
4563unsigned char
4564info_get_another_input_char (void)
4565{
4566 int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
4567
4568 /* If there isn't any input currently available, then wait a
4569 moment looking for input. If we don't get it fast enough,
4570 prompt a little bit with the current key sequence. */
4571 if (!info_keyseq_displayed_p)
4572 {
4573 ready = 1;
4574 if (!info_any_buffered_input_p () &&
4575 !info_input_pending_p ())
4576 {
4577#if defined (FD_SET)
4578 struct timeval timer;
4579 fd_set readfds;
4580
4581 FD_ZERO (&readfds);
4582 FD_SET (fileno (info_input_stream), &readfds);
4583 timer.tv_sec = 1;
4584 timer.tv_usec = 750;
4585 ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
4586#else
4587 ready = 0;
4588#endif /* FD_SET */
4589 }
4590 }
4591
4592 if (!ready)
4593 display_info_keyseq (1);
4594
4595 return (info_get_input_char ());
4596}
4597
4598/* Do the command associated with KEY in MAP. If the associated command is
4599 really a keymap, then read another key, and dispatch into that map. */
4600void
4601info_dispatch_on_key (unsigned char key, Keymap map)
4602{
4603#if !defined(INFOKEY)
4604 if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert))
4605 {
4606 if (map[ESC].type == ISKMAP)
4607 {
4608 map = (Keymap)map[ESC].function;
4609 add_char_to_keyseq (ESC);
4610 key = UnMeta (key);
4611 info_dispatch_on_key (key, map);
4612 }
4613 else
4614 {
4615 dispatch_error (info_keyseq);
4616 }
4617 return;
4618 }
4619#endif /* INFOKEY */
4620
4621 switch (map[key].type)
4622 {
4623 case ISFUNC:
4624 {
4625 VFunction *func;
4626
4627 func = InfoFunction(map[key].function);
4628 if (func != (VFunction *)NULL)
4629 {
4630 /* Special case info_do_lowercase_version (). */
4631 if (func == (VFunction *) info_do_lowercase_version)
4632 {
4633#if defined(INFOKEY)
4634 unsigned char lowerkey;
4635
4636 lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key);
4637 if (lowerkey == key)
4638 {
4639 add_char_to_keyseq (key);
4640 dispatch_error (info_keyseq);
4641 return;
4642 }
4643 info_dispatch_on_key (lowerkey, map);
4644#else /* !INFOKEY */
4645 info_dispatch_on_key (tolower (key), map);
4646#endif /* INFOKEY */
4647 return;
4648 }
4649
4650 add_char_to_keyseq (key);
4651
4652 if (info_keyseq_displayed_p)
4653 display_info_keyseq (0);
4654
4655 {
4656 WINDOW *where;
4657
4658 where = active_window;
4659 (*InfoFunction(map[key].function))
4660 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
4661
4662 /* If we have input pending, then the last command was a prefix
4663 command. Don't change the value of the last function vars.
4664 Otherwise, remember the last command executed in the var
4665 appropriate to the window in which it was executed. */
4666 if (!info_input_pending_p ())
4667 {
4668 if (where == the_echo_area)
4669 ea_last_executed_command = InfoFunction(map[key].function);
4670 else
4671 info_last_executed_command = InfoFunction(map[key].function);
4672 }
4673 }
4674 }
4675 else
4676 {
4677 add_char_to_keyseq (key);
4678 dispatch_error (info_keyseq);
4679 return;
4680 }
4681 }
4682 break;
4683
4684 case ISKMAP:
4685 add_char_to_keyseq (key);
4686 if (map[key].function != (InfoCommand *)NULL)
4687 {
4688 unsigned char newkey;
4689
4690 newkey = info_get_another_input_char ();
4691 info_dispatch_on_key (newkey, (Keymap)map[key].function);
4692 }
4693 else
4694 {
4695 dispatch_error (info_keyseq);
4696 return;
4697 }
4698 break;
4699 }
4700}
4701
4702
4703/* **************************************************************** */
4704/* */
4705/* Numeric Arguments */
4706/* */
4707/* **************************************************************** */
4708
4709/* Handle C-u style numeric args, as well as M--, and M-digits. */
4710
4711/* Non-zero means that an explicit argument has been passed to this
4712 command, as in C-u C-v. */
4713int info_explicit_arg = 0;
4714
4715/* The sign of the numeric argument. */
4716int info_numeric_arg_sign = 1;
4717
4718/* The value of the argument itself. */
4719int info_numeric_arg = 1;
4720
4721/* Add the current digit to the argument in progress. */
4722DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,
4723 _("Add this digit to the current numeric argument"))
4724{
4725 info_numeric_arg_digit_loop (window, 0, key);
4726}
4727
4728/* C-u, universal argument. Multiply the current argument by 4.
4729 Read a key. If the key has nothing to do with arguments, then
4730 dispatch on it. If the key is the abort character then abort. */
4731DECLARE_INFO_COMMAND (info_universal_argument,
4732 _("Start (or multiply by 4) the current numeric argument"))
4733{
4734 info_numeric_arg *= 4;
4735 info_numeric_arg_digit_loop (window, 0, 0);
4736}
4737
4738/* Create a default argument. */
4739void
4740info_initialize_numeric_arg (void)
4741{
4742 info_numeric_arg = info_numeric_arg_sign = 1;
4743 info_explicit_arg = 0;
4744}
4745
4746DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,
4747 _("Internally used by \\[universal-argument]"))
4748{
4749 unsigned char pure_key;
4750 Keymap keymap = window->keymap;
4751
4752 while (1)
4753 {
4754 if (key)
4755 pure_key = key;
4756 else
4757 {
4758 if (display_was_interrupted_p && !info_any_buffered_input_p ())
4759 display_update_display (windows);
4760
4761 if (active_window != the_echo_area)
4762 display_cursor_at_point (active_window);
4763
4764 pure_key = key = info_get_another_input_char ();
4765
4766#if !defined(INFOKEY)
4767 if (Meta_p (key))
4768 add_char_to_keyseq (ESC);
4769
4770 add_char_to_keyseq (UnMeta (key));
4771#else /* defined(INFOKEY) */
4772 add_char_to_keyseq (key);
4773#endif /* defined(INFOKEY) */
4774 }
4775
4776#if !defined(INFOKEY)
4777 if (Meta_p (key))
4778 key = UnMeta (key);
4779#endif /* !defined(INFOKEY) */
4780
4781 if (keymap[key].type == ISFUNC
4782 && InfoFunction(keymap[key].function)
4783 == (VFunction *) info_universal_argument)
4784 {
4785 info_numeric_arg *= 4;
4786 key = 0;
4787 continue;
4788 }
4789
4790#if defined(INFOKEY)
4791 if (Meta_p (key))
4792 key = UnMeta (key);
4793#endif /* !defined(INFOKEY) */
4794
4795
4796 if (isdigit (key))
4797 {
4798 if (info_explicit_arg)
4799 info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
4800 else
4801 info_numeric_arg = (key - '0');
4802 info_explicit_arg = 1;
4803 }
4804 else
4805 {
4806 if (key == '-' && !info_explicit_arg)
4807 {
4808 info_numeric_arg_sign = -1;
4809 info_numeric_arg = 1;
4810 }
4811 else
4812 {
4813 info_keyseq_index--;
4814 info_dispatch_on_key (pure_key, keymap);
4815 return;
4816 }
4817 }
4818 key = 0;
4819 }
4820}
4821
4822
4823/* **************************************************************** */
4824/* */
4825/* Input Character Buffering */
4826/* */
4827/* **************************************************************** */
4828
4829/* Character waiting to be read next. */
4830static int pending_input_character = 0;
4831
4832/* How to make there be no pending input. */
4833static void
4834info_clear_pending_input (void)
4835{
4836 pending_input_character = 0;
4837}
4838
4839/* How to set the pending input character. */
4840static void
4841info_set_pending_input (unsigned char key)
4842{
4843 pending_input_character = key;
4844}
4845
4846/* How to see if there is any pending input. */
4847unsigned char
4848info_input_pending_p (void)
4849{
4850 return (pending_input_character);
4851}
4852
4853/* Largest number of characters that we can read in advance. */
4854#define MAX_INFO_INPUT_BUFFERING 512
4855
4856static int pop_index = 0, push_index = 0;
4857static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING];
4858
4859/* Add KEY to the buffer of characters to be read. */
4860static void
4861info_push_typeahead (unsigned char key)
4862{
4863 /* Flush all pending input in the case of C-g pressed. */
4864 if (key == Control ('g'))
4865 {
4866 push_index = pop_index;
4867 info_set_pending_input (Control ('g'));
4868 }
4869 else
4870 {
4871 info_input_buffer[push_index++] = key;
4872 if ((unsigned int) push_index >= sizeof (info_input_buffer))
4873 push_index = 0;
4874 }
4875}
4876
4877/* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4878static int
4879info_input_buffer_space_available (void)
4880{
4881 if (pop_index > push_index)
4882 return (pop_index - push_index);
4883 else
4884 return (sizeof (info_input_buffer) - (push_index - pop_index));
4885}
4886
4887/* Get a key from the buffer of characters to be read.
4888 Return the key in KEY.
4889 Result is non-zero if there was a key, or 0 if there wasn't. */
4890static int
4891info_get_key_from_typeahead (unsigned char *key)
4892{
4893 if (push_index == pop_index)
4894 return (0);
4895
4896 *key = info_input_buffer[pop_index++];
4897
4898 if ((unsigned int) pop_index >= sizeof (info_input_buffer))
4899 pop_index = 0;
4900
4901 return (1);
4902}
4903
4904int
4905info_any_buffered_input_p (void)
4906{
4907 info_gather_typeahead ();
4908 return (push_index != pop_index);
4909}
4910
4911/* If characters are available to be read, then read them and stuff them into
4912 info_input_buffer. Otherwise, do nothing. */
4913void
4914info_gather_typeahead (void)
4915{
4916 register int i = 0;
4917 int tty, space_avail;
4918 long chars_avail;
4919 unsigned char input[MAX_INFO_INPUT_BUFFERING];
4920
4921 tty = fileno (info_input_stream);
4922 chars_avail = 0;
4923
4924 space_avail = info_input_buffer_space_available ();
4925
4926 /* If we can just find out how many characters there are to read, do so. */
4927#if defined (FIONREAD)
4928 {
4929 ioctl (tty, FIONREAD, &chars_avail);
4930
4931 if (chars_avail > space_avail)
4932 chars_avail = space_avail;
4933
4934 if (chars_avail)
4935 chars_avail = read (tty, &input[0], chars_avail);
4936 }
4937#else /* !FIONREAD */
4938# if defined (O_NDELAY)
4939 {
4940 int flags;
4941
4942 flags = fcntl (tty, F_GETFL, 0);
4943
4944 fcntl (tty, F_SETFL, (flags | O_NDELAY));
4945 chars_avail = read (tty, &input[0], space_avail);
4946 fcntl (tty, F_SETFL, flags);
4947
4948 if (chars_avail == -1)
4949 chars_avail = 0;
4950 }
4951# else /* !O_NDELAY */
4952# ifdef __DJGPP__
4953 {
4954 extern long pc_term_chars_avail (void);
4955
4956 if (isatty (tty))
4957 chars_avail = pc_term_chars_avail ();
4958 else
4959 {
4960 /* We could be more accurate by calling ltell, but we have no idea
4961 whether tty is buffered by stdio functions, and if so, how many
4962 characters are already waiting in the buffer. So we punt. */
4963 struct stat st;
4964
4965 if (fstat (tty, &st) < 0)
4966 chars_avail = 1;
4967 else
4968 chars_avail = st.st_size;
4969 }
4970 if (chars_avail > space_avail)
4971 chars_avail = space_avail;
4972 if (chars_avail)
4973 chars_avail = read (tty, &input[0], chars_avail);
4974 }
4975# endif/* __DJGPP__ */
4976# endif /* O_NDELAY */
4977#endif /* !FIONREAD */
4978
4979 while (i < chars_avail)
4980 {
4981 info_push_typeahead (input[i]);
4982 i++;
4983 }
4984}
4985
4986/* How to read a single character. */
4987unsigned char
4988info_get_input_char (void)
4989{
4990 unsigned char keystroke;
4991
4992 info_gather_typeahead ();
4993
4994 if (pending_input_character)
4995 {
4996 keystroke = pending_input_character;
4997 pending_input_character = 0;
4998 }
4999 else if (info_get_key_from_typeahead (&keystroke) == 0)
5000 {
5001 int rawkey;
5002 unsigned char c;
5003 int tty = fileno (info_input_stream);
5004
5005 /* Using stream I/O causes FIONREAD etc to fail to work
5006 so unless someone can find a portable way of finding
5007 out how many characters are currently buffered, we
5008 should stay with away from stream I/O.
5009 --Egil Kvaleberg <egilk@sn.no>, January 1997. */
5010#ifdef EINTR
5011 /* Keep reading if we got EINTR, so that we don't just exit.
5012 --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
5013 22 Dec 1997. */
5014 {
5015 int n;
5016 do
5017 n = read (tty, &c, 1);
5018 while (n == -1 && errno == EINTR);
5019 rawkey = n == 1 ? c : EOF;
5020 }
5021#else
5022 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5023#endif
5024
5025 keystroke = rawkey;
5026
5027 if (rawkey == EOF)
5028 {
5029 if (info_input_stream != stdin)
5030 {
5031 fclose (info_input_stream);
5032 info_input_stream = stdin;
5033 tty = fileno (info_input_stream);
5034 display_inhibited = 0;
5035 display_update_display (windows);
5036 display_cursor_at_point (active_window);
5037 rawkey = (read (tty, &c, 1) == 1) ? c : EOF;
5038 keystroke = rawkey;
5039 }
5040
5041 if (rawkey == EOF)
5042 {
5043 terminal_unprep_terminal ();
5044 close_dribble_file ();
5045 xexit (0);
5046 }
5047 }
5048 }
5049
5050 if (info_dribble_file)
5051 dribble (keystroke);
5052
5053 return keystroke;
5054}
Note: See TracBrowser for help on using the repository browser.