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

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

applied OS/2 patches (manually).

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