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