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