Bug Summary

File:src/gnu/usr.bin/texinfo/info/session.c
Warning:line 2673, column 11
Branch condition evaluates to a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name session.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/gnu/usr.bin/texinfo/obj/info -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -D LOCALEDIR="/usr/share/locale" -D INFODIR="/usr/share/info" -D INFODIR2="/usr/share/info" -I . -I /usr/src/gnu/usr.bin/texinfo/info -I .. -I . -I /usr/src/gnu/usr.bin/texinfo/lib -I ../intl -I .. -I /usr/src/gnu/usr.bin/texinfo/info -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/usr.bin/texinfo/obj/info -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/gnu/usr.bin/texinfo/info/session.c
1/* session.c -- user windowing interface to Info.
2 $Id: session.c,v 1.6 2006/07/17 16:12:36 espie 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_H1)
28# include <sys/time.h>
29# define HAVE_STRUCT_TIMEVAL
30#endif /* HAVE_SYS_TIME_H */
31
32#if defined (HANDLE_MAN_PAGES)
33# include "man.h"
34#endif
35
36static void info_clear_pending_input (void);
37static void info_set_pending_input (unsigned char key);
38static void info_handle_pointer (char *label, WINDOW *window);
39static void display_info_keyseq (int expecting_future_input);
40char *node_printed_rep (NODE *node);
41
42/* **************************************************************** */
43/* */
44/* Running an Info Session */
45/* */
46/* **************************************************************** */
47
48/* The place that we are reading input from. */
49static FILE *info_input_stream = NULL((void *)0);
50
51/* The last executed command. */
52VFunction *info_last_executed_command = NULL((void *)0);
53
54/* Becomes non-zero when 'q' is typed to an Info window. */
55int quit_info_immediately = 0;
56
57/* Array of structures describing for each window which nodes have been
58 visited in that window. */
59INFO_WINDOW **info_windows = NULL((void *)0);
60
61/* Where to add the next window, if we need to add one. */
62static int info_windows_index = 0;
63
64/* Number of slots allocated to `info_windows'. */
65static int info_windows_slots = 0;
66
67void remember_window_and_node (WINDOW *window, NODE *node);
68void forget_window_and_nodes (WINDOW *window);
69void display_startup_message_and_start (void);
70
71/* Begin an info session finding the nodes specified by FILENAME and NODENAMES.
72 For each loaded node, create a new window. Always split the largest of the
73 available windows. */
74void
75begin_multiple_window_info_session (char *filename, char **nodenames)
76{
77 register int i;
78 WINDOW *window = (WINDOW *)NULL((void *)0);
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((void *)0);
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((void *)0), NULL((void *)0));
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_INTERNALS1);
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((void *)0), NULL((void *)0));
129 info_session ();
130 xexit (0);
131 }
132 }
133 }
134 display_startup_message_and_start ();
135}
136
137/* Start an info session with INITIAL_NODE, and an error message in the echo
138 area made from FORMAT and ARG. */
139void
140begin_info_session_with_error (NODE *initial_node, char *format,
141 void *arg1, void *arg2)
142{
143 initialize_info_session (initial_node, 1);
144 info_error (format, arg1, arg2);
145 info_session ();
146}
147
148/* Start an info session with INITIAL_NODE. */
149void
150begin_info_session (NODE *initial_node)
151{
152 initialize_info_session (initial_node, 1);
153 display_startup_message_and_start ();
154}
155
156void
157display_startup_message_and_start (void)
158{
159 char *format;
160
161 format = replace_in_documentation
162 ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item.")((const 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"4.8", NULL((void *)0));
166 info_session ();
167}
168
169/* Run an info session with an already initialized window and node. */
170void
171info_session (void)
172{
173 display_update_display (windows);
174 info_last_executed_command = NULL((void *)0);
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(&__sF[1]));
181 terminal_unprep_terminal ();
182 close_dribble_file ();
183}
184
185/* Here is a window-location dependent event loop. Called from the
186 functions info_session (), and from read_xxx_in_echo_area (). */
187void
188info_read_and_dispatch (void)
189{
190 unsigned char key;
191 int done;
192 done = 0;
193
194 while (!done && !quit_info_immediately)
195 {
196 int lk = 0;
197
198 /* If we haven't just gone up or down a line, there is no
199 goal column for this window. */
200 if ((info_last_executed_command != (VFunction *) info_next_line) &&
201 (info_last_executed_command != (VFunction *) info_prev_line))
202 active_window->goal_column = -1;
203
204 if (echo_area_is_active)
205 {
206 lk = echo_area_last_command_was_kill;
207 echo_area_prep_read ();
208 }
209
210 if (!info_any_buffered_input_p ())
211 display_update_display (windows);
212
213 display_cursor_at_point (active_window);
214 info_initialize_numeric_arg ();
215
216 initialize_keyseq ();
217 key = info_get_input_char ();
218
219 /* No errors yet. We just read a character, that's all. Only clear
220 the echo_area if it is not currently active. */
221 if (!echo_area_is_active)
222 window_clear_echo_area ();
223
224 info_error_was_printed = 0;
225
226 /* Do the selected command. */
227 info_dispatch_on_key (key, active_window->keymap);
228
229 if (echo_area_is_active)
230 {
231 /* Echo area commands that do killing increment the value of
232 ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no
233 change in the value of this variable, the last command
234 executed was not a kill command. */
235 if (lk == echo_area_last_command_was_kill)
236 echo_area_last_command_was_kill = 0;
237
238 if (ea_last_executed_command == (VFunction *) ea_newline ||
239 info_aborted_echo_area)
240 {
241 ea_last_executed_command = (VFunction *)NULL((void *)0);
242 done = 1;
243 }
244
245 if (info_last_executed_command == (VFunction *) info_quit)
246 quit_info_immediately = 1;
247 }
248 else if (info_last_executed_command == (VFunction *) info_quit)
249 done = 1;
250 }
251}
252
253/* Found in signals.c */
254extern void initialize_info_signal_handler (void );
255
256/* Initialize the first info session by starting the terminal, window,
257 and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */
258void
259initialize_info_session (NODE *node, int clear_screen)
260{
261 char *term_name = getenv ("TERM");
262 terminal_initialize_terminal (term_name);
263
264 if (terminal_is_dumb_p)
265 {
266 if (!term_name)
267 term_name = "dumb";
268
269 info_error ((char *) msg_term_too_dumb, term_name, NULL((void *)0));
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(&__sF[0]), NULL((void *)0));
294 info_input_stream = stdin(&__sF[0]);
295 }
296
297 info_windows_initialized_p = 1;
298}
299
300/* Tell Info that input is coming from the file FILENAME. */
301void
302info_set_input_from_file (char *filename)
303{
304 FILE *stream;
305
306 /* Input may include binary characters. */
307 stream = fopen (filename, FOPEN_RBIN"r");
308
309 if (!stream)
310 return;
311
312 if ((info_input_stream != (FILE *)NULL((void *)0)) &&
313 (info_input_stream != stdin(&__sF[0])))
314 fclose (info_input_stream);
315
316 info_input_stream = stream;
317
318 if (stream != stdin(&__sF[0]))
319 display_inhibited = 1;
320}
321
322/* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */
323static INFO_WINDOW *
324get_info_window_of_window (WINDOW *window)
325{
326 register int i;
327 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL((void *)0);
328
329 for (i = 0; info_windows && (info_win = info_windows[i]); i++)
330 if (info_win->window == window)
331 break;
332
333 return (info_win);
334}
335
336/* Reset the remembered pagetop and point of WINDOW to WINDOW's current
337 values if the window and node are the same as the current one being
338 displayed. */
339void
340set_remembered_pagetop_and_point (WINDOW *window)
341{
342 INFO_WINDOW *info_win;
343
344 info_win = get_info_window_of_window (window);
345
346 if (!info_win)
347 return;
348
349 if (info_win->nodes_index &&
350 (info_win->nodes[info_win->current] == window->node))
351 {
352 info_win->pagetops[info_win->current] = window->pagetop;
353 info_win->points[info_win->current] = window->point;
354 }
355}
356
357void
358remember_window_and_node (WINDOW *window, NODE *node)
359{
360 /* See if we already have this window in our list. */
361 INFO_WINDOW *info_win = get_info_window_of_window (window);
362
363 /* If the window wasn't already on our list, then make a new entry. */
364 if (!info_win)
365 {
366 info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW));
367 info_win->window = window;
368 info_win->nodes = (NODE **)NULL((void *)0);
369 info_win->pagetops = (int *)NULL((void *)0);
370 info_win->points = (long *)NULL((void *)0);
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,do { if (info_windows_index + 2 >= info_windows_slots) info_windows
= (INFO_WINDOW * *)(xrealloc (info_windows, (info_windows_slots
+= 10) * sizeof (INFO_WINDOW *))); info_windows[info_windows_index
++] = (INFO_WINDOW *)info_win; info_windows[info_windows_index
] = (INFO_WINDOW *)((void *)0); } while (0)
376 info_windows_slots, 10, INFO_WINDOW *)do { if (info_windows_index + 2 >= info_windows_slots) info_windows
= (INFO_WINDOW * *)(xrealloc (info_windows, (info_windows_slots
+= 10) * sizeof (INFO_WINDOW *))); info_windows[info_windows_index
++] = (INFO_WINDOW *)info_win; info_windows[info_windows_index
] = (INFO_WINDOW *)((void *)0); } while (0)
;
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((void *)0);
410 info_win->pagetops[info_win->nodes_index] = 0;
411 info_win->points[info_win->nodes_index] = 0;
412}
413
414#define DEBUG_FORGET_WINDOW_AND_NODES
415#if defined (DEBUG_FORGET_WINDOW_AND_NODES)
416static void
417consistency_check_info_windows (void)
418{
419 register int i;
420
421 for (i = 0; i < info_windows_index; i++)
422 {
423 WINDOW *win;
424
425 for (win = windows; win; win = win->next)
426 if (win == info_windows[i]->window)
427 break;
428
429 if (!win)
430 abort ();
431 }
432}
433#endif /* DEBUG_FORGET_WINDOW_AND_NODES */
434
435/* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */
436void
437forget_window_and_nodes (WINDOW *window)
438{
439 register int i;
440 INFO_WINDOW *info_win = (INFO_WINDOW *)NULL((void *)0);
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((void *)0);
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)do { if (info_win->pagetops) free (info_win->pagetops);
} while (0)
;
469 maybe_free (info_win->points)do { if (info_win->points) free (info_win->points); } while
(0)
;
470 }
471
472 free (info_win);
473 }
474#if defined (DEBUG_FORGET_WINDOW_AND_NODES)
475 consistency_check_info_windows ();
476#endif /* DEBUG_FORGET_WINDOW_AND_NODES */
477}
478
479/* Set WINDOW to show NODE. Remember the new window in our list of Info
480 windows. If we are doing automatic footnote display, also try to display
481 the footnotes for this window. If REMEMBER is nonzero, first call
482 set_remembered_pagetop_and_point. */
483void
484info_set_node_of_window (int remember, WINDOW *window, NODE *node)
485{
486 if (remember)
487 set_remembered_pagetop_and_point (window);
488
489 /* Put this node into the window. */
490 window_set_node_of_window (window, node);
491
492 /* Remember this node and window in our list of info windows. */
493 remember_window_and_node (window, node);
494
495 /* If doing auto-footnote display/undisplay, show the footnotes belonging
496 to this window's node. */
497 if (auto_footnotes_p)
498 info_get_or_remove_footnotes (window);
499}
500
501
502/* **************************************************************** */
503/* */
504/* Info Movement Commands */
505/* */
506/* **************************************************************** */
507
508/* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen
509 to do so. */
510void
511set_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_UpdateWindow0x01;
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. */
573static void
574info_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_UpdateWindow0x01)
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. */
596static void
597move_to_new_line (int old, int new, WINDOW *window)
598{
599 if (old == -1)
600 {
601 info_error ((char *) msg_cant_find_point, NULL((void *)0), NULL((void *)0));
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. */
620DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line"))void info_next_line (WINDOW *window, int count, unsigned char
key)
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. */
635DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line"))void info_prev_line (WINDOW *window, int count, unsigned char
key)
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. */
650DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line"))void info_end_of_line (WINDOW *window, int count, unsigned char
key)
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. */
670DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line"))void info_beginning_of_line (WINDOW *window, int count, unsigned
char key)
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. */
689DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character"))void info_forward_char (WINDOW *window, int count, unsigned char
key)
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. */
705DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character"))void info_backward_char (WINDOW *window, int count, unsigned char
key)
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)) (islower (c) || isupper (c) || isdigit (c))
721
722/* Move forward a word in this node. */
723DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word"))void info_forward_word (WINDOW *window, int count, unsigned char
key)
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)(islower (c) || isupper (c) || isdigit (c)))
749 {
750 while (++point < end)
751 {
752 c = buffer[point];
753 if (alphabetic (c)(islower (c) || isupper (c) || isdigit (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)(islower (c) || isupper (c) || isdigit (c)))
764 break;
765 }
766 --count;
767 }
768 window->point = point;
769 info_show_point (window);
770}
771
772DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word"))void info_backward_word (WINDOW *window, int count, unsigned char
key)
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)(islower (c) || isupper (c) || isdigit (c)))
798 {
799 while (--point)
800 {
801 c = buffer[point - 1];
802 if (alphabetic (c)(islower (c) || isupper (c) || isdigit (c)))
803 break;
804 }
805 }
806
807 while (point)
808 {
809 c = buffer[point - 1];
810 if (!alphabetic (c)(islower (c) || isupper (c) || isdigit (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
831int info_scroll_behaviour = IS_Continuous0;
832
833/* Choices used by the completer when reading a value for the user-visible
834 variable "scroll-behaviour". */
835char *info_scroll_choices[] = {
836 "Continuous", "Next Only", "Page Only", (char *)NULL((void *)0)
837};
838
839/* Default window sizes for scrolling commands. */
840int default_window_size = -1; /* meaning 1 window-full */
841int default_scroll_size = -1; /* meaning half screen size */
842
843#define INFO_LABEL_FOUND()(info_parsed_nodename || (info_parsed_filename && !is_dir_name
(info_parsed_filename)))
\
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. */
848static void
849forward_move_node_structure (WINDOW *window, int behaviour)
850{
851 switch (behaviour)
852 {
853 case IS_PageOnly2:
854 info_error ((char *) msg_at_node_bottom, NULL((void *)0), NULL((void *)0));
855 break;
856
857 case IS_NextOnly1:
858 info_next_label_of_node (window->node)info_parse_label ("Next:", window->node);
859 if (!info_parsed_nodename && !info_parsed_filename)
860 info_error ((char *) msg_no_pointer, (char *) _("Next")((const char *) ("Next")), NULL((void *)0));
861 else
862 {
863 window_message_in_echo_area ((char *) _("Following Next node...")((const char *) ("Following Next node...")),
864 NULL((void *)0), NULL((void *)0));
865 info_handle_pointer ("Next", window);
866 }
867 break;
868
869 case IS_Continuous0:
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...")((const char *) ("Selecting first menu item...")),
882 NULL((void *)0), NULL((void *)0));
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)info_parse_label ("Next:", window->node);
891 if (INFO_LABEL_FOUND ()(info_parsed_nodename || (info_parsed_filename && !is_dir_name
(info_parsed_filename)))
)
892 {
893 window_message_in_echo_area ((char *) _("Selecting Next node...")((const char *) ("Selecting Next node...")),
894 NULL((void *)0), NULL((void *)0));
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)info_parse_label ("Up:", window->node);
916 if (INFO_LABEL_FOUND ()(info_parsed_nodename || (info_parsed_filename && !is_dir_name
(info_parsed_filename)))
)
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)info_parse_label ("Next:", window->node);
925
926 /* If no "Next" pointer, keep backing up. */
927 if (!INFO_LABEL_FOUND ()(info_parsed_nodename || (info_parsed_filename && !is_dir_name
(info_parsed_filename)))
)
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)info_parse_label ("Next:", 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.")((const char *) ("Moving Up %d time(s), then Next.")),
965 (void *) (long) up_counter, NULL((void *)0));
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((void *)0);
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_UpdateWindow0x01;
988 info_error ((char *) _("No more nodes within this document.")((const char *) ("No more nodes within this document.")),
989 NULL((void *)0), NULL((void *)0));
990 }
991 }
992 }
993 break;
994 }
995 }
996}
997
998/* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */
999static void
1000backward_move_node_structure (WINDOW *window, int behaviour)
1001{
1002 switch (behaviour)
1003 {
1004 case IS_PageOnly2:
1005 info_error ((char *) msg_at_node_top, NULL((void *)0), NULL((void *)0));
1006 break;
1007
1008 case IS_NextOnly1:
1009 info_prev_label_of_node (window->node)do { info_parse_label ("Prev:", window->node); if (!info_parsed_nodename
&& !info_parsed_filename) info_parse_label ("Previous:"
, window->node); } while (0)
;
1010 if (!info_parsed_nodename && !info_parsed_filename)
1011 info_error ((char *) _("No `Prev' for this node.")((const char *) ("No `Prev' for this node.")), NULL((void *)0), NULL((void *)0));
1012 else
1013 {
1014 window_message_in_echo_area ((char *) _("Moving Prev in this window.")((const char *) ("Moving Prev in this window.")),
1015 NULL((void *)0), NULL((void *)0));
1016 info_handle_pointer ("Prev", window);
1017 }
1018 break;
1019
1020 case IS_Continuous0:
1021 info_prev_label_of_node (window->node)do { info_parse_label ("Prev:", window->node); if (!info_parsed_nodename
&& !info_parsed_filename) info_parse_label ("Previous:"
, window->node); } while (0)
;
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)info_parse_label ("Up:", 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.")((const char *) ("No `Prev' or `Up' for this node within this document."
))
,
1031 NULL((void *)0), NULL((void *)0));
1032 else
1033 {
1034 window_message_in_echo_area ((char *) _("Moving Up in this window.")((const char *) ("Moving Up in this window.")),
1035 NULL((void *)0), NULL((void *)0));
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)info_parse_label ("Up:", 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)do { info_parse_label ("Prev:", window->node); if (!info_parsed_nodename
&& !info_parsed_filename) info_parse_label ("Previous:"
, window->node); } while (0)
;
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.")((const char *) ("Moving Prev in this window.")),
1074 NULL((void *)0), NULL((void *)0));
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.")((const char *) ("Moving to `Prev's last menu item.")), NULL((void *)0), NULL((void *)0));
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. */
1094DECLARE_INFO_COMMAND (info_global_next_node,void info_global_next_node (WINDOW *window, int count, unsigned
char key)
1095 _("Move forwards or down through node structure"))void info_global_next_node (WINDOW *window, int count, unsigned
char key)
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_Continuous0);
1104 count--;
1105 }
1106 }
1107}
1108
1109/* Move continuously backward through the node structure of this info file. */
1110DECLARE_INFO_COMMAND (info_global_prev_node,void info_global_prev_node (WINDOW *window, int count, unsigned
char key)
1111 _("Move backwards or up through node structure"))void info_global_prev_node (WINDOW *window, int count, unsigned
char key)
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_Continuous0);
1120 count--;
1121 }
1122 }
1123}
1124
1125static void _scroll_forward(WINDOW *window, int count,
1126 unsigned char key, int behaviour);
1127static void _scroll_backward(WINDOW *window, int count,
1128 unsigned char key, int behaviour);
1129
1130static void
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
1170static void
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. */
1205DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))void info_scroll_forward (WINDOW *window, int count, unsigned
char key)
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. */
1212DECLARE_INFO_COMMAND (info_scroll_forward_set_window,void info_scroll_forward_set_window (WINDOW *window, int count
, unsigned char key)
1213 _("Scroll forward in this window and set default window size"))void info_scroll_forward_set_window (WINDOW *window, int count
, unsigned char key)
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. */
1221DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node"))void info_scroll_forward_page_only (WINDOW *window, int count
, unsigned char key)
1222{
1223 _scroll_forward (window, count, key, IS_PageOnly2);
1224}
1225
1226/* Like info_scroll_forward_page_only, but sets default_window_size as a side
1227 effect. */
1228DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window,void info_scroll_forward_page_only_set_window (WINDOW *window
, int count, unsigned char key)
1229 _("Scroll forward in this window staying within node and set default window size"))void info_scroll_forward_page_only_set_window (WINDOW *window
, int count, unsigned char key)
1230{
1231 if (info_explicit_arg)
1232 default_window_size = count;
1233 _scroll_forward (window, count, key, IS_PageOnly2);
1234}
1235
1236/* Show the previous screen of WINDOW's node. */
1237DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window"))void info_scroll_backward (WINDOW *window, int count, unsigned
char key)
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. */
1244DECLARE_INFO_COMMAND (info_scroll_backward_set_window,void info_scroll_backward_set_window (WINDOW *window, int count
, unsigned char key)
1245 _("Scroll backward in this window and set default window size"))void info_scroll_backward_set_window (WINDOW *window, int count
, unsigned char key)
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. */
1254DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node"))void info_scroll_backward_page_only (WINDOW *window, int count
, unsigned char key)
1255{
1256 _scroll_backward (window, count, key, IS_PageOnly2);
1257}
1258
1259/* Like info_scroll_backward_page_only, but sets default_window_size as a side
1260 effect. */
1261DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window,void info_scroll_backward_page_only_set_window (WINDOW *window
, int count, unsigned char key)
1262 _("Scroll backward in this window staying within node and set default window size"))void info_scroll_backward_page_only_set_window (WINDOW *window
, int count, unsigned char key)
1263{
1264 if (info_explicit_arg)
1265 default_window_size = count;
1266 _scroll_backward (window, count, key, IS_PageOnly2);
1267}
1268
1269/* Move to the beginning of the node. */
1270DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node"))void info_beginning_of_node (WINDOW *window, int count, unsigned
char key)
1271{
1272 window->pagetop = window->point = 0;
1273 window->flags |= W_UpdateWindow0x01;
1274}
1275
1276/* Move to the end of the node. */
1277DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node"))void info_end_of_node (WINDOW *window, int count, unsigned char
key)
1278{
1279 window->point = window->node->nodelen - 1;
1280 info_show_point (window);
1281}
1282
1283/* Scroll the window forward by N lines. */
1284DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines"))void info_down_line (WINDOW *window, int count, unsigned char
key)
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. */
1301DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines"))void info_up_line (WINDOW *window, int count, unsigned char key
)
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. */
1318DECLARE_INFO_COMMAND (info_scroll_half_screen_down,void info_scroll_half_screen_down (WINDOW *window, int count,
unsigned char key)
1319 _("Scroll down by half screen size"))void info_scroll_half_screen_down (WINDOW *window, int count,
unsigned char key)
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. */
1344DECLARE_INFO_COMMAND (info_scroll_half_screen_up,void info_scroll_half_screen_up (WINDOW *window, int count, unsigned
char key)
1345 _("Scroll up by half screen size"))void info_scroll_half_screen_up (WINDOW *window, int count, unsigned
char key)
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. */
1374DECLARE_INFO_COMMAND (info_next_window, _("Select the next window"))void info_next_window (WINDOW *window, int count, unsigned char
key)
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((void *)0), NULL((void *)0));
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_UpdateWindow0x01;
1408 active_window = window;
1409 }
1410}
1411
1412/* Make the previous window in the chain be the active window. */
1413DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window"))void info_prev_window (WINDOW *window, int count, unsigned char
key)
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((void *)0), NULL((void *)0));
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((void *)0);
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_UpdateWindow0x01;
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. */
1464DECLARE_INFO_COMMAND (info_split_window, _("Split the current window"))void info_split_window (WINDOW *window, int count, unsigned char
key)
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((void *)0), NULL((void *)0));
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((void *)0);
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_INTERNALS0);
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. */
1545DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window"))void info_delete_window (WINDOW *window, int count, unsigned char
key)
1546{
1547 if (!windows->next)
1548 {
1549 info_error ((char *) msg_cant_kill_last, NULL((void *)0), NULL((void *)0));
1550 }
1551 else if (window->flags & W_WindowIsPerm0x02)
1552 {
1553 info_error ((char *) _("Cannot delete a permanent window")((const char *) ("Cannot delete a permanent window")), NULL((void *)0), NULL((void *)0));
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_INTERNALS0);
1564 }
1565}
1566
1567/* Do the physical deletion of WINDOW, and forget this window and
1568 associated nodes. */
1569void
1570info_delete_window_internal (WINDOW *window)
1571{
1572 if (windows->next && ((window->flags & W_WindowIsPerm0x02) == 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. */
1585DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows"))void info_keep_one_window (WINDOW *window, int count, unsigned
char key)
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_WindowIsPerm0x02) == 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_UpdateWindow0x01;
1628}
1629
1630/* Scroll the "other" window of WINDOW. */
1631DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window"))void info_scroll_other_window (WINDOW *window, int count, unsigned
char key)
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((void *)0), NULL((void *)0));
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. */
1651DECLARE_INFO_COMMAND (info_scroll_other_window_backward,void info_scroll_other_window_backward (WINDOW *window, int count
, unsigned char key)
1652 _("Scroll the other window backward"))void info_scroll_other_window_backward (WINDOW *window, int count
, unsigned char key)
1653{
1654 info_scroll_other_window (window, -count, key);
1655}
1656
1657/* Change the size of WINDOW by AMOUNT. */
1658DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window"))void info_grow_window (WINDOW *window, int count, unsigned char
key)
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. */
1665int auto_tiling_p = 0;
1666
1667/* Tile all of the visible windows. */
1668DECLARE_INFO_COMMAND (info_tile_windows,void info_tile_windows (WINDOW *window, int count, unsigned char
key)
1669 _("Divide the available screen space among the visible windows"))void info_tile_windows (WINDOW *window, int count, unsigned char
key)
1670{
1671 window_tile_windows (TILE_INTERNALS1);
1672}
1673
1674/* Toggle the state of this window's wrapping of lines. */
1675DECLARE_INFO_COMMAND (info_toggle_wrap,void info_toggle_wrap (WINDOW *window, int count, unsigned char
key)
1676 _("Toggle the state of line wrapping in the current window"))void info_toggle_wrap (WINDOW *window, int count, unsigned char
key)
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. */
1689char *
1690node_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. */
1710void
1711info_select_reference (WINDOW *window, REFERENCE *entry)
1712{
1713 NODE *node;
1714 char *filename, *nodename, *file_system_error;
1715
1716 file_system_error = (char *)NULL((void *)0);
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)do { if (file_system_error) free (file_system_error); } while
(0)
;
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((void *)0), NULL((void *)0));
1757 else
1758 info_error ((char *) msg_cant_find_node, nodename, NULL((void *)0));
1759 }
1760
1761 maybe_free (file_system_error)do { if (file_system_error) free (file_system_error); } while
(0)
;
1762 maybe_free (filename)do { if (filename) free (filename); } while (0);
1763 maybe_free (nodename)do { if (nodename) free (nodename); } while (0);
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. */
1772static void
1773info_parse_and_select (char *line, WINDOW *window)
1774{
1775 REFERENCE entry;
1776
1777 info_parse_node (line, DONT_SKIP_NEWLINES0);
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. */
1790static void
1791info_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((void *)0);
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((void *)0), NULL((void *)0));
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((void *)0));
1843 }
1844}
1845
1846/* Make WINDOW display the "Next:" node of the node currently being
1847 displayed. */
1848DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node"))void info_next_node (WINDOW *window, int count, unsigned char
key)
1849{
1850 info_next_label_of_node (window->node)info_parse_label ("Next:", window->node);
1851 info_handle_pointer ("Next", window);
1852}
1853
1854/* Make WINDOW display the "Prev:" node of the node currently being
1855 displayed. */
1856DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node"))void info_prev_node (WINDOW *window, int count, unsigned char
key)
1857{
1858 info_prev_label_of_node (window->node)do { info_parse_label ("Prev:", window->node); if (!info_parsed_nodename
&& !info_parsed_filename) info_parse_label ("Previous:"
, window->node); } while (0)
;
1859 info_handle_pointer ("Prev", window);
1860}
1861
1862/* Make WINDOW display the "Up:" node of the node currently being
1863 displayed. */
1864DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node"))void info_up_node (WINDOW *window, int count, unsigned char key
)
1865{
1866 info_up_label_of_node (window->node)info_parse_label ("Up:", window->node);
1867 info_handle_pointer ("Up", window);
1868}
1869
1870/* Make WINDOW display the last node of this info file. */
1871DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file"))void info_last_node (WINDOW *window, int count, unsigned char
key)
1872{
1873 register int i;
1874 FILE_BUFFER *fb = file_buffer_of_window (window);
1875 NODE *node = (NODE *)NULL((void *)0);
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")((const char *) ("This window has no additional nodes")), NULL((void *)0), NULL((void *)0));
1899 else
1900 info_set_node_of_window (1, window, node);
1901}
1902
1903/* Make WINDOW display the first node of this info file. */
1904DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file"))void info_first_node (WINDOW *window, int count, unsigned char
key)
1905{
1906 FILE_BUFFER *fb = file_buffer_of_window (window);
1907 NODE *node = (NODE *)NULL((void *)0);
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")((const char *) ("This window has no additional nodes")), NULL((void *)0), NULL((void *)0));
1932 else
1933 info_set_node_of_window (1, window, node);
1934}
1935
1936/* Select the last menu item in WINDOW->node. */
1937DECLARE_INFO_COMMAND (info_last_menu_item,void info_last_menu_item (WINDOW *window, int count, unsigned
char key)
1938 _("Select the last item in this node's menu"))void info_last_menu_item (WINDOW *window, int count, unsigned
char key)
1939{
1940 info_menu_digit (window, 1, '0');
1941}
1942
1943/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */
1944DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item"))void info_menu_digit (WINDOW *window, int count, unsigned char
key)
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((void *)0), NULL((void *)0));
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.")((const char *) ("There aren't %d items in this menu.")),
1978 (void *) (long) item, NULL((void *)0));
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
2001static REFERENCE **
2002nearest_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))),((labs (pos - (xref->start - strlen ("*Note")))) < (labs
(pos - xref->end)) ? (labs (pos - (xref->start - strlen
("*Note")))) : (labs (pos - xref->end)))
2022 labs (pos - xref->end))((labs (pos - (xref->start - strlen ("*Note")))) < (labs
(pos - xref->end)) ? (labs (pos - (xref->start - strlen
("*Note")))) : (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((void *)0);
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((void *)0);
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. */
2054static void
2055info_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((void *)0);
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((void *)0), NULL((void *)0));
2067 else
2068 info_error ((char *) msg_no_xref_node, NULL((void *)0), NULL((void *)0));
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((void *)0);
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_IsManPage0x40)
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 if (which < 0)
2145 which = 0;
2146 defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2147 defentry->label = xstrdup (refs[which]->label);
2148 defentry->filename = refs[which]->filename;
2149 defentry->nodename = refs[which]->nodename;
2150 defentry->line_number = refs[which]->line_number;
2151
2152 if (defentry->filename)
2153 defentry->filename = xstrdup (defentry->filename);
2154 if (defentry->nodename)
2155 defentry->nodename = xstrdup (defentry->nodename);
2156 }
2157 info_free_references (refs);
2158 }
2159 }
2160 }
2161
2162 /* If we are going to ask the user a question, do it now. */
2163 if (ask_p)
2164 {
2165 char *prompt;
2166
2167 /* Build the prompt string. */
2168 if (builder == info_menu_of_node)
2169 {
2170 if (defentry)
2171 {
2172 prompt = xmalloc (strlen (defentry->label)
2173 + strlen (_("Menu item (%s): ")((const char *) ("Menu item (%s): "))));
2174 sprintf (prompt, _("Menu item (%s): ")((const char *) ("Menu item (%s): ")), defentry->label);
2175 }
2176 else
2177 prompt = xstrdup (_("Menu item: ")((const char *) ("Menu item: ")));
2178 }
2179 else
2180 {
2181 if (defentry)
2182 {
2183 prompt = xmalloc (strlen (defentry->label)
2184 + strlen (_("Follow xref (%s): ")((const char *) ("Follow xref (%s): "))));
2185 sprintf (prompt, _("Follow xref (%s): ")((const char *) ("Follow xref (%s): ")), defentry->label);
2186 }
2187 else
2188 prompt = xstrdup (_("Follow xref: ")((const char *) ("Follow xref: ")));
2189 }
2190
2191 line = info_read_completing_in_echo_area (window, prompt, menu);
2192 free (prompt);
2193
2194 window = active_window;
2195
2196 /* User aborts, just quit. */
2197 if (!line)
2198 {
2199 maybe_free (defentry)do { if (defentry) free (defentry); } while (0);
2200 info_free_references (menu);
2201 info_abort_key (window, 0, 0);
2202 return;
2203 }
2204
2205 /* If we had a default and the user accepted it, use that. */
2206 if (!*line)
2207 {
2208 free (line);
2209 if (defentry)
2210 line = xstrdup (defentry->label);
2211 else
2212 line = (char *)NULL((void *)0);
2213 }
2214 }
2215 else
2216 {
2217 /* Not going to ask any questions. If we have a default entry, use
2218 that, otherwise return. */
2219 if (!defentry)
2220 return;
2221 else
2222 line = xstrdup (defentry->label);
2223 }
2224
2225 if (line)
2226 {
2227 /* It is possible that the references have more than a single
2228 entry with the same label, and also LINE is down-cased, which
2229 complicates matters even more. Try to be as accurate as we
2230 can: if they've chosen the default, use defentry directly. */
2231 if (defentry && strcmp (line, defentry->label) == 0)
2232 entry = defentry;
2233 else
2234 /* Find the selected label in the references. If there are
2235 more than one label which matches, find the one that's
2236 closest to point. */
2237 {
2238 register int i;
2239 int best = -1, min_dist = window->node->nodelen;
2240 REFERENCE *ref;
2241
2242 for (i = 0; menu && (ref = menu[i]); i++)
2243 {
2244 /* Need to use strcasecmp because LINE is downcased
2245 inside info_read_completing_in_echo_area. */
2246 if (strcasecmp (line, ref->label) == 0)
2247 {
2248 /* ref->end is more accurate estimate of position
2249 for menus than ref->start. Go figure. */
2250 int dist = abs (window->point - ref->end);
2251
2252 if (dist < min_dist)
2253 {
2254 min_dist = dist;
2255 best = i;
2256 }
2257 }
2258 }
2259 if (best != -1)
2260 entry = menu[best];
2261 else
2262 entry = (REFERENCE *)NULL((void *)0);
2263 }
2264
2265 if (!entry && defentry)
2266 info_error ((char *) _("The reference disappeared! (%s).")((const char *) ("The reference disappeared! (%s).")), line, NULL((void *)0));
2267 else
2268 {
2269 NODE *orig = window->node;
2270 info_select_reference (window, entry);
2271
2272 if (builder == info_xrefs_of_node && window->node != orig
2273 && !(window->node->flags & N_FromAnchor0x80))
2274 { /* Search for this reference in the node. */
2275 long offset;
2276 long start;
2277
2278 if (window->line_count > 0)
2279 start = window->line_starts[1] - window->node->contents;
2280 else
2281 start = 0;
2282
2283 offset =
2284 info_target_search_node (window->node, entry->label, start);
2285
2286 if (offset != -1)
2287 {
2288 window->point = offset;
2289 window_adjust_pagetop (window);
2290 }
2291 }
2292
2293 if (entry->line_number > 0)
2294 /* next_line starts at line 1? Anyway, the -1 makes it
2295 move to the right line. */
2296 info_next_line (window, entry->line_number - 1, key);
2297 }
2298
2299 free (line);
2300 if (defentry)
2301 {
2302 free (defentry->label);
2303 maybe_free (defentry->filename)do { if (defentry->filename) free (defentry->filename);
} while (0)
;
2304 maybe_free (defentry->nodename)do { if (defentry->nodename) free (defentry->nodename);
} while (0)
;
2305 free (defentry);
2306 }
2307 }
2308
2309 info_free_references (menu);
2310
2311 if (!info_error_was_printed)
2312 window_clear_echo_area ();
2313}
2314
2315/* Read a line (with completion) which is the name of a menu item,
2316 and select that item. */
2317DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))void info_menu_item (WINDOW *window, int count, unsigned char
key)
2318{
2319 info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
2320}
2321
2322/* Read a line (with completion) which is the name of a reference to
2323 follow, and select the node. */
2324DECLARE_INFO_COMMANDvoid info_xref_item (WINDOW *window, int count, unsigned char
key)
2325 (info_xref_item, _("Read a footnote or cross reference and select its node"))void info_xref_item (WINDOW *window, int count, unsigned char
key)
2326{
2327 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
2328}
2329
2330/* Position the cursor at the start of this node's menu. */
2331DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu"))void info_find_menu (WINDOW *window, int count, unsigned char
key)
2332{
2333 SEARCH_BINDING binding;
2334 long position;
2335
2336 binding.buffer = window->node->contents;
2337 binding.start = 0;
2338 binding.end = window->node->nodelen;
2339 binding.flags = S_FoldCase0x01 | S_SkipDest0x02;
2340
2341 position = search (INFO_MENU_LABEL"\n* Menu:", &binding);
2342
2343 if (position == -1)
2344 info_error ((char *) msg_no_menu_node, NULL((void *)0), NULL((void *)0));
2345 else
2346 {
2347 window->point = position;
2348 window_adjust_pagetop (window);
2349 window->flags |= W_UpdateWindow0x01;
2350 }
2351}
2352
2353/* Visit as many menu items as is possible, each in a separate window. */
2354DECLARE_INFO_COMMAND (info_visit_menu,void info_visit_menu (WINDOW *window, int count, unsigned char
key)
2355 _("Visit as many menu items at once as possible"))void info_visit_menu (WINDOW *window, int count, unsigned char
key)
2356{
2357 register int i;
2358 REFERENCE *entry, **menu;
2359
2360 menu = info_menu_of_node (window->node);
2361
2362 if (!menu)
2363 info_error ((char *) msg_no_menu_node, NULL((void *)0), NULL((void *)0));
2364
2365 for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++)
2366 {
2367 WINDOW *new;
2368
2369 new = window_make_window (window->node);
2370 window_tile_windows (TILE_INTERNALS1);
2371
2372 if (!new)
2373 info_error ((char *) msg_win_too_small, NULL((void *)0), NULL((void *)0));
2374 else
2375 {
2376 active_window = new;
2377 info_select_reference (new, entry);
2378 }
2379 }
2380}
2381
2382/* Read a line of input which is a node name, and go to that node. */
2383DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it"))void info_goto_node (WINDOW *window, int count, unsigned char
key)
2384{
2385 char *line;
2386
2387#define GOTO_COMPLETES
2388#if defined (GOTO_COMPLETES)
2389 /* Build a completion list of all of the known nodes. */
2390 {
2391 register int fbi, i;
2392 FILE_BUFFER *current;
2393 REFERENCE **items = (REFERENCE **)NULL((void *)0);
2394 int items_index = 0;
2395 int items_slots = 0;
2396
2397 current = file_buffer_of_window (window);
2398
2399 for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++)
2400 {
2401 FILE_BUFFER *fb;
2402 REFERENCE *entry;
2403 int this_is_the_current_fb;
2404
2405 fb = info_loaded_files[fbi];
2406 this_is_the_current_fb = (current == fb);
2407
2408 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2409 entry->filename = entry->nodename = (char *)NULL((void *)0);
2410 entry->label = (char *)xmalloc (4 + strlen (fb->filename));
2411 sprintf (entry->label, "(%s)*", fb->filename);
2412
2413 add_pointer_to_arraydo { if (items_index + 2 >= items_slots) items = (REFERENCE
* *)(xrealloc (items, (items_slots += 10) * sizeof (REFERENCE
*))); items[items_index++] = (REFERENCE *)entry; items[items_index
] = (REFERENCE *)((void *)0); } while (0)
2414 (entry, items_index, items, items_slots, 10, REFERENCE *)do { if (items_index + 2 >= items_slots) items = (REFERENCE
* *)(xrealloc (items, (items_slots += 10) * sizeof (REFERENCE
*))); items[items_index++] = (REFERENCE *)entry; items[items_index
] = (REFERENCE *)((void *)0); } while (0)
;
2415
2416 if (fb->tags)
2417 {
2418 for (i = 0; fb->tags[i]; i++)
2419 {
2420 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2421 entry->filename = entry->nodename = (char *)NULL((void *)0);
2422 if (this_is_the_current_fb)
2423 entry->label = xstrdup (fb->tags[i]->nodename);
2424 else
2425 {
2426 entry->label = (char *) xmalloc
2427 (4 + strlen (fb->filename) +
2428 strlen (fb->tags[i]->nodename));
2429 sprintf (entry->label, "(%s)%s",
2430 fb->filename, fb->tags[i]->nodename);
2431 }
2432
2433 add_pointer_to_arraydo { if (items_index + 2 >= items_slots) items = (REFERENCE
* *)(xrealloc (items, (items_slots += 100) * sizeof (REFERENCE
*))); items[items_index++] = (REFERENCE *)entry; items[items_index
] = (REFERENCE *)((void *)0); } while (0)
2434 (entry, items_index, items, items_slots, 100, REFERENCE *)do { if (items_index + 2 >= items_slots) items = (REFERENCE
* *)(xrealloc (items, (items_slots += 100) * sizeof (REFERENCE
*))); items[items_index++] = (REFERENCE *)entry; items[items_index
] = (REFERENCE *)((void *)0); } while (0)
;
2435 }
2436 }
2437 }
2438 line = info_read_maybe_completing (window, (char *) _("Goto node: ")((const char *) ("Goto node: ")),
2439 items);
2440 info_free_references (items);
2441 }
2442#else /* !GOTO_COMPLETES */
2443 line = info_read_in_echo_area (window, (char *) _("Goto node: ")((const char *) ("Goto node: ")));
2444#endif /* !GOTO_COMPLETES */
2445
2446 /* If the user aborted, quit now. */
2447 if (!line)
2448 {
2449 info_abort_key (window, 0, 0);
2450 return;
2451 }
2452
2453 canonicalize_whitespace (line);
2454
2455 if (*line)
2456 info_parse_and_select (line, window);
2457
2458 free (line);
2459 if (!info_error_was_printed)
2460 window_clear_echo_area ();
2461}
2462
2463/* Follow the menu list in MENUS (list of strings terminated by a NULL
2464 entry) from INITIAL_NODE. If can't continue at any point (no menu or
2465 no menu entry for the next item), return the node so far -- that
2466 might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will
2467 be set to the error message and argument for message, otherwise they
2468 will be NULL. */
2469
2470NODE *
2471info_follow_menus (NODE *initial_node, char **menus,
2472 const char **errstr, char **errarg1, char **errarg2)
2473{
2474 NODE *node = NULL((void *)0);
2475 *errstr = *errarg1 = *errarg2 = NULL((void *)0);
2476
2477 for (; *menus; menus++)
2478 {
2479 static char *first_arg = NULL((void *)0);
2480 REFERENCE **menu;
2481 REFERENCE *entry;
2482 char *arg = *menus; /* Remember the name of the menu entry we want. */
2483
2484 /* A leading space is certainly NOT part of a node name. Most
2485 probably, they typed a space after the separating comma. The
2486 strings in menus[] have their whitespace canonicalized, so
2487 there's at most one space to ignore. */
2488 if (*arg == ' ')
2489 arg++;
2490 if (!first_arg)
2491 first_arg = arg;
2492
2493 /* Build and return a list of the menu items in this node. */
2494 menu = info_menu_of_node (initial_node);
2495
2496 /* If no menu item in this node, stop here, but let the user
2497 continue to use Info. Perhaps they wanted this node and didn't
2498 realize it. */
2499 if (!menu)
2500 {
2501 if (arg == first_arg)
2502 {
2503 node = make_manpage_node (first_arg);
2504 if (node)
2505 goto maybe_got_node;
2506 }
2507 *errstr = _("No menu in node `%s'.")((const char *) ("No menu in node `%s'."));
2508 *errarg1 = node_printed_rep (initial_node);
2509 return initial_node;
2510 }
2511
2512 /* Find the specified menu item. */
2513 entry = info_get_labeled_reference (arg, menu);
2514
2515 /* If the item wasn't found, search the list sloppily. Perhaps this
2516 user typed "buffer" when they really meant "Buffers". */
2517 if (!entry)
2518 {
2519 int i;
2520 int best_guess = -1;
2521
2522 for (i = 0; (entry = menu[i]); i++)
2523 {
2524 if (strcasecmp (entry->label, arg) == 0)
2525 break;
2526 else
2527 if ((best_guess == -1)
2528 && (strncasecmp (entry->label, arg, strlen (arg)) == 0))
2529 best_guess = i;
2530 }
2531
2532 if (!entry && best_guess != -1)
2533 entry = menu[best_guess];
2534 }
2535
2536 /* If we still failed to find the reference, start Info with the current
2537 node anyway. It is probably a misspelling. */
2538 if (!entry)
2539 {
2540 if (arg == first_arg)
2541 {
2542 /* Maybe they typed "info foo" instead of "info -f foo". */
2543 node = info_get_node (first_arg, 0);
2544 if (node)
2545 add_file_directory_to_path (first_arg);
2546 else
2547 node = make_manpage_node (first_arg);
2548 if (node)
2549 goto maybe_got_node;
2550 }
2551
2552 info_free_references (menu);
2553 *errstr = _("No menu item `%s' in node `%s'.")((const char *) ("No menu item `%s' in node `%s'."));
2554 *errarg1 = arg;
2555 *errarg2 = node_printed_rep (initial_node);
2556 return initial_node;
2557 }
2558
2559 /* We have found the reference that the user specified. If no
2560 filename in this reference, define it. */
2561 if (!entry->filename)
2562 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2563 : initial_node->filename);
2564
2565 /* Try to find this node. */
2566 node = info_get_node (entry->filename, entry->nodename);
2567 if (!node && arg == first_arg)
2568 {
2569 node = make_manpage_node (first_arg);
2570 if (node)
2571 goto maybe_got_node;
2572 }
2573
2574 /* Since we cannot find it, try using the label of the entry as a
2575 file, i.e., "(LABEL)Top". */
2576 if (!node && entry->nodename
2577 && strcmp (entry->label, entry->nodename) == 0)
2578 node = info_get_node (entry->label, "Top");
2579
2580 maybe_got_node:
2581 if (!node)
2582 {
2583 *errstr = _("Unable to find node referenced by `%s' in `%s'.")((const char *) ("Unable to find node referenced by `%s' in `%s'."
))
;
2584 *errarg1 = xstrdup (entry->label);
2585 *errarg2 = node_printed_rep (initial_node);
2586 info_free_references (menu);
2587 return initial_node;
2588 }
2589
2590 info_free_references (menu);
2591
2592 /* Success. Go round the loop again. */
2593 free (initial_node);
2594 initial_node = node;
2595 }
2596
2597 return initial_node;
2598}
2599
2600/* Split STR into individual node names by writing null bytes in wherever
2601 there are commas and constructing a list of the resulting pointers.
2602 (We can do this since STR has had canonicalize_whitespace called on it.)
2603 Return array terminated with NULL. */
2604
2605static char **
2606split_list_of_nodenames (char *str)
2607{
2608 unsigned len = 2;
2609 char **nodes = xmalloc (len * sizeof (char *));
2610
2611 nodes[len - 2] = str;
2612
2613 while (*str++)
2614 {
2615 if (*str == ',')
2616 {
2617 *str++ = 0; /* get past the null byte */
2618 len++;
2619 nodes = xrealloc (nodes, len * sizeof (char *));
2620 nodes[len - 2] = str;
2621 }
2622 }
2623
2624 nodes[len - 1] = NULL((void *)0);
2625
2626 return nodes;
2627}
2628
2629
2630/* Read a line of input which is a sequence of menus (starting from
2631 dir), and follow them. */
2632DECLARE_INFO_COMMAND (info_menu_sequence,void info_menu_sequence (WINDOW *window, int count, unsigned char
key)
2633 _("Read a list of menus starting from dir and follow them"))void info_menu_sequence (WINDOW *window, int count, unsigned char
key)
2634{
2635 char *line = info_read_in_echo_area (window, (char *) _("Follow menus: ")((const char *) ("Follow menus: ")));
2636
2637 /* If the user aborted, quit now. */
2638 if (!line)
1
Assuming 'line' is non-null
2
Taking false branch
2639 {
2640 info_abort_key (window, 0, 0);
2641 return;
2642 }
2643
2644 canonicalize_whitespace (line);
2645
2646 if (*line)
3
Assuming the condition is true
4
Taking true branch
2647 {
2648 const char *errstr;
5
'errstr' declared without an initial value
2649 char *errarg1, *errarg2;
2650 NODE *dir_node = info_get_node (NULL((void *)0), NULL((void *)0));
2651 char **nodes = split_list_of_nodenames (line);
2652 NODE *node = NULL((void *)0);
2653
2654 /* If DIR_NODE is NULL, they might be reading a file directly,
2655 like in "info -d . -f ./foo". Try using "Top" instead. */
2656 if (!dir_node)
6
Assuming 'dir_node' is null
7
Taking true branch
2657 {
2658 char *file_name = window->node->parent;
2659
2660 if (!file_name)
8
Assuming 'file_name' is non-null
9
Taking false branch
2661 file_name = window->node->filename;
2662 dir_node = info_get_node (file_name, NULL((void *)0));
2663 }
2664
2665 /* If we still cannot find the starting point, give up.
2666 We cannot allow a NULL pointer inside info_follow_menus. */
2667 if (!dir_node)
10
Assuming 'dir_node' is null
11
Taking true branch
2668 info_error ((char *) msg_cant_find_node, "Top", NULL((void *)0));
2669 else
2670 node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2);
2671
2672 free (nodes);
2673 if (!errstr)
12
Branch condition evaluates to a garbage value
2674 info_set_node_of_window (1, window, node);
2675 else
2676 info_error ((char *) errstr, errarg1, errarg2);
2677 }
2678
2679 free (line);
2680 if (!info_error_was_printed)
2681 window_clear_echo_area ();
2682}
2683
2684/* Search the menu MENU for a (possibly mis-spelled) entry ARG.
2685 Return the menu entry, or the best guess for what they meant by ARG,
2686 or NULL if there's nothing in this menu seems to fit the bill.
2687 If EXACT is non-zero, allow only exact matches. */
2688static REFERENCE *
2689entry_in_menu (char *arg, REFERENCE **menu, int exact)
2690{
2691 REFERENCE *entry;
2692
2693 /* First, try to find the specified menu item verbatim. */
2694 entry = info_get_labeled_reference (arg, menu);
2695
2696 /* If the item wasn't found, search the list sloppily. Perhaps we
2697 have "Option Summary", but ARG is "option". */
2698 if (!entry && !exact)
2699 {
2700 int i;
2701 int best_guess = -1;
2702
2703 for (i = 0; (entry = menu[i]); i++)
2704 {
2705 if (strcasecmp (entry->label, arg) == 0)
2706 break;
2707 else
2708 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
2709 best_guess = i;
2710 }
2711
2712 if (!entry && best_guess != -1)
2713 entry = menu[best_guess];
2714 }
2715
2716 return entry;
2717}
2718
2719/* Find the node that is the best candidate to list the PROGRAM's
2720 invocation info and its command-line options, by looking for menu
2721 items and chains of menu items with characteristic names. */
2722void
2723info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program)
2724{
2725 /* The list of node names typical for GNU manuals where the program
2726 usage and specifically the command-line arguments are described.
2727 This is pure heuristics. I gathered these node names by looking
2728 at all the Info files I could put my hands on. If you are
2729 looking for evidence to complain to the GNU project about
2730 non-uniform style of documentation, here you have your case! */
2731 static const char *invocation_nodes[] = {
2732 "%s invocation",
2733 "Invoking %s",
2734 "Preliminaries", /* m4 has Invoking under Preliminaries! */
2735 "Invocation",
2736 "Command Arguments",/* Emacs */
2737 "Invoking `%s'",
2738 "%s options",
2739 "Options",
2740 "Option ", /* e.g. "Option Summary" */
2741 "Invoking",
2742 "All options", /* tar, paxutils */
2743 "Arguments",
2744 "%s cmdline", /* ar */
2745 "%s", /* last resort */
2746 (const char *)0
2747 };
2748 NODE *node = NULL((void *)0);
2749 REFERENCE **menu;
2750 const char **try_node;
2751
2752 /* We keep looking deeper and deeper in the menu structure until
2753 there are no more menus or no menu items from the above list.
2754 Some manuals have the invocation node sitting 3 or 4 levels deep
2755 in the menu hierarchy... */
2756 for (node = initial_node; node; initial_node = node)
2757 {
2758 REFERENCE *entry = NULL((void *)0);
2759
2760 /* Build and return a list of the menu items in this node. */
2761 menu = info_menu_of_node (initial_node);
2762
2763 /* If no menu item in this node, stop here. Perhaps this node
2764 is the one they need. */
2765 if (!menu)
2766 break;
2767
2768 /* Look for node names typical for usage nodes in this menu. */
2769 for (try_node = invocation_nodes; *try_node; try_node++)
2770 {
2771 char *nodename;
2772
2773 nodename = xmalloc (strlen (program) + strlen (*try_node));
2774 sprintf (nodename, *try_node, program);
2775 /* The last resort "%s" is dangerous, so we restrict it
2776 to exact matches here. */
2777 entry = entry_in_menu (nodename, menu,
2778 strcmp (*try_node, "%s") == 0);
2779 free (nodename);
2780 if (entry)
2781 break;
2782 }
2783
2784 if (!entry)
2785 break;
2786
2787 if (!entry->filename)
2788 entry->filename = xstrdup (initial_node->parent ? initial_node->parent
2789 : initial_node->filename);
2790 /* Try to find this node. */
2791 node = info_get_node (entry->filename, entry->nodename);
2792 info_free_references (menu);
2793 if (!node)
2794 break;
2795 }
2796
2797 /* We've got our best shot at the invocation node. Now select it. */
2798 if (initial_node)
2799 info_set_node_of_window (1, window, initial_node);
2800 if (!info_error_was_printed)
2801 window_clear_echo_area ();
2802}
2803
2804/* Given a name of an Info file, find the name of the package it
2805 describes by removing the leading directories and extensions. */
2806char *
2807program_name_from_file_name (char *file_name)
2808{
2809 int i;
2810 char *program_name = xstrdup (filename_non_directory (file_name));
2811
2812 for (i = strlen (program_name) - 1; i > 0; i--)
2813 if (program_name[i] == '.'
2814 && (FILENAME_CMPNstrncmp (program_name + i, ".info", 5) == 0
2815 || FILENAME_CMPNstrncmp (program_name + i, ".inf", 4) == 0
2816#ifdef __MSDOS__
2817 || FILENAME_CMPNstrncmp (program_name + i, ".i", 2) == 0
2818#endif
2819 || isdigit (program_name[i + 1]))) /* a man page foo.1 */
2820 {
2821 program_name[i] = 0;
2822 break;
2823 }
2824 return program_name;
2825}
2826
2827DECLARE_INFO_COMMAND (info_goto_invocation_node,void info_goto_invocation_node (WINDOW *window, int count, unsigned
char key)
2828 _("Find the node describing program invocation"))void info_goto_invocation_node (WINDOW *window, int count, unsigned
char key)
2829{
2830 const char *invocation_prompt = _("Find Invocation node of [%s]: ")((const char *) ("Find Invocation node of [%s]: "));
2831 char *program_name, *line;
2832 char *default_program_name, *prompt, *file_name;
2833 NODE *top_node;
2834
2835 /* Intuit the name of the program they are likely to want.
2836 We use the file name of the current Info file as a hint. */
2837 file_name = window->node->parent ? window->node->parent
2838 : window->node->filename;
2839 default_program_name = program_name_from_file_name (file_name);
2840
2841 prompt = (char *)xmalloc (strlen (default_program_name) +
2842 strlen (invocation_prompt));
2843 sprintf (prompt, invocation_prompt, default_program_name);
2844 line = info_read_in_echo_area (window, prompt);
2845 free (prompt);
2846 if (!line)
2847 {
2848 info_abort_key (window, 0, 0);
2849 return;
2850 }
2851 if (*line)
2852 program_name = line;
2853 else
2854 program_name = default_program_name;
2855
2856 /* In interactive usage they'd probably expect us to begin looking
2857 from the Top node. */
2858 top_node = info_get_node (file_name, NULL((void *)0));
2859 if (!top_node)
2860 info_error ((char *) msg_cant_find_node, "Top", NULL((void *)0));
2861
2862 info_intuit_options_node (window, top_node, program_name);
2863 free (line);
2864 free (default_program_name);
2865}
2866
2867#if defined (HANDLE_MAN_PAGES)
2868DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it"))void info_man (WINDOW *window, int count, unsigned char key)
2869{
2870 char *line;
2871
2872 line = info_read_in_echo_area (window, (char *) _("Get Manpage: ")((const char *) ("Get Manpage: ")));
2873
2874 if (!line)
2875 {
2876 info_abort_key (window, 0, 0);
2877 return;
2878 }
2879
2880 canonicalize_whitespace (line);
2881
2882 if (*line)
2883 {
2884 char *goto_command;
2885
2886 goto_command = (char *)xmalloc
2887 (4 + strlen (MANPAGE_FILE_BUFFER_NAME"*manpages*") + strlen (line));
2888
2889 sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME"*manpages*", line);
2890
2891 info_parse_and_select (goto_command, window);
2892 free (goto_command);
2893 }
2894
2895 free (line);
2896 if (!info_error_was_printed)
2897 window_clear_echo_area ();
2898}
2899#endif /* HANDLE_MAN_PAGES */
2900
2901/* Move to the "Top" node in this file. */
2902DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file"))void info_top_node (WINDOW *window, int count, unsigned char key
)
2903{
2904 info_parse_and_select ("Top", window);
2905}
2906
2907/* Move to the node "(dir)Top". */
2908DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'"))void info_dir_node (WINDOW *window, int count, unsigned char key
)
2909{
2910 info_parse_and_select ("(dir)Top", window);
2911}
2912
2913
2914/* Read the name of a node to kill. The list of available nodes comes
2915 from the nodes appearing in the current window configuration. */
2916static char *
2917read_nodename_to_kill (WINDOW *window)
2918{
2919 int iw;
2920 char *nodename;
2921 INFO_WINDOW *info_win;
2922 REFERENCE **menu = NULL((void *)0);
2923 int menu_index = 0, menu_slots = 0;
2924 char *default_nodename = xstrdup (active_window->node->nodename);
2925 char *prompt = xmalloc (strlen (_("Kill node (%s): ")((const char *) ("Kill node (%s): "))) + strlen (default_nodename));
2926
2927 sprintf (prompt, _("Kill node (%s): ")((const char *) ("Kill node (%s): ")), default_nodename);
2928
2929 for (iw = 0; (info_win = info_windows[iw]); iw++)
2930 {
2931 REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
2932 entry->label = xstrdup (info_win->window->node->nodename);
2933 entry->filename = entry->nodename = (char *)NULL((void *)0);
2934
2935 add_pointer_to_array (entry, menu_index, menu, menu_slots, 10,do { if (menu_index + 2 >= menu_slots) menu = (REFERENCE *
*)(xrealloc (menu, (menu_slots += 10) * sizeof (REFERENCE *)
)); menu[menu_index++] = (REFERENCE *)entry; menu[menu_index]
= (REFERENCE *)((void *)0); } while (0)
2936 REFERENCE *)do { if (menu_index + 2 >= menu_slots) menu = (REFERENCE *
*)(xrealloc (menu, (menu_slots += 10) * sizeof (REFERENCE *)
)); menu[menu_index++] = (REFERENCE *)entry; menu[menu_index]
= (REFERENCE *)((void *)0); } while (0)
;
2937 }
2938
2939 nodename = info_read_completing_in_echo_area (window, prompt, menu);
2940 free (prompt);
2941 info_free_references (menu);
2942 if (nodename && !*nodename)
2943 {
2944 free (nodename);
2945 nodename = default_nodename;
2946 }
2947 else
2948 free (default_nodename);
2949
2950 return nodename;
2951}
2952
2953
2954/* Delete NODENAME from this window, showing the most
2955 recently selected node in this window. */
2956static void
2957kill_node (WINDOW *window, char *nodename)
2958{
2959 int iw, i;
2960 INFO_WINDOW *info_win;
2961 NODE *temp;
2962
2963 /* If there is no nodename to kill, quit now. */
2964 if (!nodename)
2965 {
2966 info_abort_key (window, 0, 0);
2967 return;
2968 }
2969
2970 /* If there is a nodename, find it in our window list. */
2971 for (iw = 0; (info_win = info_windows[iw]); iw++)
2972 if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0
2973 && info_win->window == window)
2974 break;
2975
2976 if (!info_win)
2977 {
2978 if (*nodename)
2979 info_error ((char *) _("Cannot kill node `%s'")((const char *) ("Cannot kill node `%s'")), nodename, NULL((void *)0));
2980 else
2981 window_clear_echo_area ();
2982
2983 return;
2984 }
2985
2986 /* If there are no more nodes left anywhere to view, complain and exit. */
2987 if (info_windows_index == 1 && info_windows[0]->nodes_index == 1)
2988 {
2989 info_error ((char *) _("Cannot kill the last node")((const char *) ("Cannot kill the last node")), NULL((void *)0), NULL((void *)0));
2990 return;
2991 }
2992
2993 /* INFO_WIN contains the node that the user wants to stop viewing. Delete
2994 this node from the list of nodes previously shown in this window. */
2995 for (i = info_win->current; i < info_win->nodes_index; i++)
2996 info_win->nodes[i] = info_win->nodes[i + 1];
2997
2998 /* There is one less node in this window's history list. */
2999 info_win->nodes_index--;
3000
3001 /* Make this window show the most recent history node. */
3002 info_win->current = info_win->nodes_index - 1;
3003
3004 /* If there aren't any nodes left in this window, steal one from the
3005 next window. */
3006 if (info_win->current < 0)
3007 {
3008 INFO_WINDOW *stealer;
3009 int which, pagetop;
3010 long point;
3011
3012 if (info_windows[iw + 1])
3013 stealer = info_windows[iw + 1];
3014 else
3015 stealer = info_windows[0];
3016
3017 /* If the node being displayed in the next window is not the most
3018 recently loaded one, get the most recently loaded one. */
3019 if ((stealer->nodes_index - 1) != stealer->current)
3020 which = stealer->nodes_index - 1;
3021
3022 /* Else, if there is another node behind the stealers current node,
3023 use that one. */
3024 else if (stealer->current > 0)
3025 which = stealer->current - 1;
3026
3027 /* Else, just use the node appearing in STEALER's window. */
3028 else
3029 which = stealer->current;
3030
3031 /* Copy this node. */
3032 {
3033 NODE *copy = xmalloc (sizeof (NODE));
3034
3035 temp = stealer->nodes[which];
3036 point = stealer->points[which];
3037 pagetop = stealer->pagetops[which];
3038
3039 copy->filename = temp->filename;
3040 copy->parent = temp->parent;
3041 copy->nodename = temp->nodename;
3042 copy->contents = temp->contents;
3043 copy->nodelen = temp->nodelen;
3044 copy->flags = temp->flags;
3045 copy->display_pos = temp->display_pos;
3046
3047 temp = copy;
3048 }
3049
3050 window_set_node_of_window (info_win->window, temp);
3051 window->point = point;
3052 window->pagetop = pagetop;
3053 remember_window_and_node (info_win->window, temp);
3054 }
3055 else
3056 {
3057 temp = info_win->nodes[info_win->current];
3058 temp->display_pos = info_win->points[info_win->current];
3059 window_set_node_of_window (info_win->window, temp);
3060 }
3061
3062 if (!info_error_was_printed)
3063 window_clear_echo_area ();
3064
3065 if (auto_footnotes_p)
3066 info_get_or_remove_footnotes (window);
3067}
3068
3069/* Kill current node, thus going back one in the node history. I (karl)
3070 do not think this is completely correct yet, because of the
3071 window-changing stuff in kill_node, but it's a lot better than the
3072 previous implementation, which did not account for nodes being
3073 visited twice at all. */
3074DECLARE_INFO_COMMAND (info_history_node,void info_history_node (WINDOW *window, int count, unsigned char
key)
3075 _("Select the most recently selected node"))void info_history_node (WINDOW *window, int count, unsigned char
key)
3076{
3077 kill_node (window, active_window->node->nodename);
3078}
3079
3080/* Kill named node. */
3081DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node"))void info_kill_node (WINDOW *window, int count, unsigned char
key)
3082{
3083 char *nodename = read_nodename_to_kill (window);
3084 kill_node (window, nodename);
3085}
3086
3087
3088/* Read the name of a file and select the entire file. */
3089DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it"))void info_view_file (WINDOW *window, int count, unsigned char
key)
3090{
3091 char *line;
3092
3093 line = info_read_in_echo_area (window, (char *) _("Find file: ")((const char *) ("Find file: ")));
3094 if (!line)
3095 {
3096 info_abort_key (active_window, 1, 0);
3097 return;
3098 }
3099
3100 if (*line)
3101 {
3102 NODE *node;
3103
3104 node = info_get_node (line, "*");
3105 if (!node)
3106 {
3107 if (info_recent_file_error)
3108 info_error (info_recent_file_error, NULL((void *)0), NULL((void *)0));
3109 else
3110 info_error ((char *) _("Cannot find `%s'.")((const char *) ("Cannot find `%s'.")), line, NULL((void *)0));
3111 }
3112 else
3113 info_set_node_of_window (1, window, node);
3114
3115 free (line);
3116 }
3117
3118 if (!info_error_was_printed)
3119 window_clear_echo_area ();
3120}
3121
3122/* **************************************************************** */
3123/* */
3124/* Dumping and Printing Nodes */
3125/* */
3126/* **************************************************************** */
3127
3128#define VERBOSE_NODE_DUMPING
3129static void write_node_to_stream (NODE *node, FILE *stream);
3130static void dump_node_to_stream (char *filename, char *nodename,
3131 FILE *stream, int dump_subnodes);
3132static void initialize_dumping (void);
3133
3134/* Dump the nodes specified by FILENAME and NODENAMES to the file named
3135 in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3136 the nodes which appear in the menu of each node dumped. */
3137void
3138dump_nodes_to_file (char *filename, char **nodenames,
3139 char *output_filename, int dump_subnodes)
3140{
3141 register int i;
3142 FILE *output_stream;
3143
3144 /* Get the stream to print the nodes to. Special case of an output
3145 filename of "-" means to dump the nodes to stdout. */
3146 if (strcmp (output_filename, "-") == 0)
3147 output_stream = stdout(&__sF[1]);
3148 else
3149 output_stream = fopen (output_filename, "w");
3150
3151 if (!output_stream)
3152 {
3153 info_error ((char *) _("Could not create output file `%s'.")((const char *) ("Could not create output file `%s'.")),
3154 output_filename, NULL((void *)0));
3155 return;
3156 }
3157
3158 /* Print each node to stream. */
3159 initialize_dumping ();
3160 for (i = 0; nodenames[i]; i++)
3161 dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes);
3162
3163 if (output_stream != stdout(&__sF[1]))
3164 fclose (output_stream);
3165
3166#if defined (VERBOSE_NODE_DUMPING)
3167 info_error ((char *) _("Done.")((const char *) ("Done.")), NULL((void *)0), NULL((void *)0));
3168#endif /* VERBOSE_NODE_DUMPING */
3169}
3170
3171/* A place to remember already dumped nodes. */
3172static char **dumped_already = (char **)NULL((void *)0);
3173static int dumped_already_index = 0;
3174static int dumped_already_slots = 0;
3175
3176static void
3177initialize_dumping (void)
3178{
3179 dumped_already_index = 0;
3180}
3181
3182/* Get and print the node specified by FILENAME and NODENAME to STREAM.
3183 If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear
3184 in the menu of each node dumped. */
3185static void
3186dump_node_to_stream (char *filename, char *nodename,
3187 FILE *stream, int dump_subnodes)
3188{
3189 register int i;
3190 NODE *node;
3191
3192 node = info_get_node (filename, nodename);
3193
3194 if (!node)
3195 {
3196 if (info_recent_file_error)
3197 info_error (info_recent_file_error, NULL((void *)0), NULL((void *)0));
3198 else
3199 {
3200 if (filename && *nodename != '(')
3201 info_error ((char *) msg_cant_file_node,
3202 filename_non_directory (filename),
3203 nodename);
3204 else
3205 info_error ((char *) msg_cant_find_node, nodename, NULL((void *)0));
3206 }
3207 return;
3208 }
3209
3210 /* If we have already dumped this node, don't dump it again. */
3211 for (i = 0; i < dumped_already_index; i++)
3212 if (strcmp (node->nodename, dumped_already[i]) == 0)
3213 {
3214 free (node);
3215 return;
3216 }
3217 add_pointer_to_array (node->nodename, dumped_already_index, dumped_already,do { if (dumped_already_index + 2 >= dumped_already_slots)
dumped_already = (char * *)(xrealloc (dumped_already, (dumped_already_slots
+= 50) * sizeof (char *))); dumped_already[dumped_already_index
++] = (char *)node->nodename; dumped_already[dumped_already_index
] = (char *)((void *)0); } while (0)
3218 dumped_already_slots, 50, char *)do { if (dumped_already_index + 2 >= dumped_already_slots)
dumped_already = (char * *)(xrealloc (dumped_already, (dumped_already_slots
+= 50) * sizeof (char *))); dumped_already[dumped_already_index
++] = (char *)node->nodename; dumped_already[dumped_already_index
] = (char *)((void *)0); } while (0)
;
3219
3220#if defined (VERBOSE_NODE_DUMPING)
3221 /* Maybe we should print some information about the node being output. */
3222 info_error ((char *) _("Writing node %s...")((const char *) ("Writing node %s...")), node_printed_rep (node), NULL((void *)0));
3223#endif /* VERBOSE_NODE_DUMPING */
3224
3225 write_node_to_stream (node, stream);
3226
3227 /* If we are dumping subnodes, get the list of menu items in this node,
3228 and dump each one recursively. */
3229 if (dump_subnodes)
3230 {
3231 REFERENCE **menu = (REFERENCE **)NULL((void *)0);
3232
3233 /* If this node is an Index, do not dump the menu references. */
3234 if (string_in_line ("Index", node->nodename) == -1)
3235 menu = info_menu_of_node (node);
3236
3237 if (menu)
3238 {
3239 for (i = 0; menu[i]; i++)
3240 {
3241 /* We don't dump Info files which are different than the
3242 current one. */
3243 if (!menu[i]->filename)
3244 dump_node_to_stream
3245 (filename, menu[i]->nodename, stream, dump_subnodes);
3246 }
3247 info_free_references (menu);
3248 }
3249 }
3250
3251 free (node);
3252}
3253
3254/* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump
3255 the nodes which appear in the menu of each node dumped. */
3256void
3257dump_node_to_file (NODE *node, char *filename, int dump_subnodes)
3258{
3259 FILE *output_stream;
3260 char *nodes_filename;
3261
3262 /* Get the stream to print this node to. Special case of an output
3263 filename of "-" means to dump the nodes to stdout. */
3264 if (strcmp (filename, "-") == 0)
3265 output_stream = stdout(&__sF[1]);
3266 else
3267 output_stream = fopen (filename, "w");
3268
3269 if (!output_stream)
3270 {
3271 info_error ((char *) _("Could not create output file `%s'.")((const char *) ("Could not create output file `%s'.")), filename,
3272 NULL((void *)0));
3273 return;
3274 }
3275
3276 if (node->parent)
3277 nodes_filename = node->parent;
3278 else
3279 nodes_filename = node->filename;
3280
3281 initialize_dumping ();
3282 dump_node_to_stream
3283 (nodes_filename, node->nodename, output_stream, dump_subnodes);
3284
3285 if (output_stream != stdout(&__sF[1]))
3286 fclose (output_stream);
3287
3288#if defined (VERBOSE_NODE_DUMPING)
3289 info_error ((char *) _("Done.")((const char *) ("Done.")), NULL((void *)0), NULL((void *)0));
3290#endif /* VERBOSE_NODE_DUMPING */
3291}
3292
3293#if !defined (DEFAULT_INFO_PRINT_COMMAND"lpr")
3294# define DEFAULT_INFO_PRINT_COMMAND"lpr" "lpr"
3295#endif /* !DEFAULT_INFO_PRINT_COMMAND */
3296
3297DECLARE_INFO_COMMAND (info_print_node,void info_print_node (WINDOW *window, int count, unsigned char
key)
3298 _("Pipe the contents of this node through INFO_PRINT_COMMAND"))void info_print_node (WINDOW *window, int count, unsigned char
key)
3299{
3300 print_node (window->node);
3301}
3302
3303/* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */
3304void
3305print_node (NODE *node)
3306{
3307 FILE *printer_pipe;
3308 char *print_command = getenv ("INFO_PRINT_COMMAND");
3309 int piping = 0;
3310
3311 if (!print_command || !*print_command)
3312 print_command = DEFAULT_INFO_PRINT_COMMAND"lpr";
3313
3314 /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the
3315 (default) text mode, since the printer drivers there need to see
3316 DOS-style CRLF pairs at the end of each line.
3317
3318 FIXME: if we are to support Mac-style text files, we might need
3319 to convert the text here. */
3320
3321 /* INFO_PRINT_COMMAND which says ">file" means write to that file.
3322 Presumably, the name of the file is the local printer device. */
3323 if (*print_command == '>')
3324 printer_pipe = fopen (++print_command, "w");
3325 else
3326 {
3327 printer_pipe = popen (print_command, "w");
3328 piping = 1;
3329 }
3330
3331 if (!printer_pipe)
3332 {
3333 info_error ((char *) _("Cannot open pipe to `%s'.")((const char *) ("Cannot open pipe to `%s'.")), print_command, NULL((void *)0));
3334 return;
3335 }
3336
3337#if defined (VERBOSE_NODE_DUMPING)
3338 /* Maybe we should print some information about the node being output. */
3339 info_error ((char *) _("Printing node %s...")((const char *) ("Printing node %s...")), node_printed_rep (node), NULL((void *)0));
3340#endif /* VERBOSE_NODE_DUMPING */
3341
3342 write_node_to_stream (node, printer_pipe);
3343 if (piping)
3344 pclose (printer_pipe);
3345 else
3346 fclose (printer_pipe);
3347
3348#if defined (VERBOSE_NODE_DUMPING)
3349 info_error ((char *) _("Done.")((const char *) ("Done.")), NULL((void *)0), NULL((void *)0));
3350#endif /* VERBOSE_NODE_DUMPING */
3351}
3352
3353static void
3354write_node_to_stream (NODE *node, FILE *stream)
3355{
3356 fwrite (node->contents, 1, node->nodelen, stream);
3357}
3358
3359/* **************************************************************** */
3360/* */
3361/* Info Searching Commands */
3362/* */
3363/* **************************************************************** */
3364
3365/* Variable controlling the garbage collection of files briefly visited
3366 during searches. Such files are normally gc'ed, unless they were
3367 compressed to begin with. If this variable is non-zero, it says
3368 to gc even those file buffer contents which had to be uncompressed. */
3369int gc_compressed_files = 0;
3370
3371static void info_gc_file_buffers (void);
3372static void info_search_1 (WINDOW *window, int count,
3373 unsigned char key, int case_sensitive, int ask_for_string);
3374
3375static char *search_string = (char *)NULL((void *)0);
3376static int search_string_size = 0;
3377static int isearch_is_active = 0;
3378
3379static int last_search_direction = 0;
3380static int last_search_case_sensitive = 0;
3381
3382/* Return the file buffer which belongs to WINDOW's node. */
3383FILE_BUFFER *
3384file_buffer_of_window (WINDOW *window)
3385{
3386 /* If this window has no node, then it has no file buffer. */
3387 if (!window->node)
3388 return ((FILE_BUFFER *)NULL((void *)0));
3389
3390 if (window->node->parent)
3391 return (info_find_file (window->node->parent));
3392
3393 if (window->node->filename)
3394 return (info_find_file (window->node->filename));
3395
3396 return ((FILE_BUFFER *)NULL((void *)0));
3397}
3398
3399/* Search for STRING in NODE starting at START. Return -1 if the string
3400 was not found, or the location of the string if it was. If WINDOW is
3401 passed as non-null, set the window's node to be NODE, its point to be
3402 the found string, and readjust the window's pagetop. Final argument
3403 DIR says which direction to search in. If it is positive, search
3404 forward, else backwards. */
3405long
3406info_search_in_node (char *string, NODE *node, long int start,
3407 WINDOW *window, int dir, int case_sensitive)
3408{
3409 SEARCH_BINDING binding;
3410 long offset;
3411
3412 binding.buffer = node->contents;
3413 binding.start = start;
3414 binding.end = node->nodelen;
3415 binding.flags = 0;
3416 if (!case_sensitive)
3417 binding.flags |= S_FoldCase0x01;
3418
3419 if (dir < 0)
3420 {
3421 binding.end = 0;
3422 binding.flags |= S_SkipDest0x02;
3423 }
3424
3425 if (binding.start < 0)
3426 return (-1);
3427
3428 /* For incremental searches, we always wish to skip past the string. */
3429 if (isearch_is_active)
3430 binding.flags |= S_SkipDest0x02;
3431
3432 offset = search (string, &binding);
3433
3434 if (offset != -1 && window)
3435 {
3436 set_remembered_pagetop_and_point (window);
3437 if (window->node != node)
3438 window_set_node_of_window (window, node);
3439 window->point = offset;
3440 window_adjust_pagetop (window);
3441 }
3442 return (offset);
3443}
3444
3445/* Search NODE, looking for the largest possible match of STRING. Start the
3446 search at START. Return the absolute position of the match, or -1, if
3447 no part of the string could be found. */
3448long
3449info_target_search_node (NODE *node, char *string, long int start)
3450{
3451 register int i;
3452 long offset = 0;
3453 char *target;
3454
3455 target = xstrdup (string);
3456 i = strlen (target);
3457
3458 /* Try repeatedly searching for this string while removing words from
3459 the end of it. */
3460 while (i)
3461 {
3462 target[i] = '\0';
3463 offset = info_search_in_node (target, node, start, (WINDOW *)NULL((void *)0), 1, 0);
3464
3465 if (offset != -1)
3466 break;
3467
3468 /* Delete the last word from TARGET. */
3469 for (; i && (!whitespace (target[i])((target[i] == ' ') || (target[i] == '\t')) && (target[i] != ',')); i--);
3470 }
3471 free (target);
3472 return (offset);
3473}
3474
3475/* Search for STRING starting in WINDOW at point. If the string is found
3476 in this node, set point to that position. Otherwise, get the file buffer
3477 associated with WINDOW's node, and search through each node in that file.
3478 If the search fails, return non-zero, else zero. Side-effect window
3479 leaving the node and point where the string was found current. */
3480static int
3481info_search_internal (char *string, WINDOW *window,
3482 int dir, int case_sensitive)
3483{
3484 register int i;
3485 FILE_BUFFER *file_buffer;
3486 char *initial_nodename;
3487 long ret, start = 0;
3488
3489 file_buffer = file_buffer_of_window (window);
3490 initial_nodename = window->node->nodename;
3491
3492 /* This used to begin from window->point, unless this was a repeated
3493 search command. But invoking search with an argument loses with
3494 that logic, since info_last_executed_command is then set to
3495 info_add_digit_to_numeric_arg. I think there's no sense in
3496 ``finding'' a string that is already under the cursor, anyway. */
3497 ret = info_search_in_node
3498 (string, window->node, window->point + dir, window, dir,
3499 case_sensitive);
3500
3501 if (ret != -1)
3502 {
3503 /* We won! */
3504 if (!echo_area_is_active && !isearch_is_active)
3505 window_clear_echo_area ();
3506 return (0);
3507 }
3508
3509 /* The string wasn't found in the current node. Search through the
3510 window's file buffer, iff the current node is not "*". */
3511 if (!file_buffer || (strcmp (initial_nodename, "*") == 0))
3512 return (-1);
3513
3514 /* If this file has tags, search through every subfile, starting at
3515 this node's subfile and node. Otherwise, search through the
3516 file's node list. */
3517 if (file_buffer->tags)
3518 {
3519 register int current_tag = 0, number_of_tags;
3520 char *last_subfile;
3521 TAG *tag;
3522
3523 /* Find number of tags and current tag. */
3524 last_subfile = (char *)NULL((void *)0);
3525 for (i = 0; file_buffer->tags[i]; i++)
3526 if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0)
3527 {
3528 current_tag = i;
3529 last_subfile = file_buffer->tags[i]->filename;
3530 }
3531
3532 number_of_tags = i;
3533
3534 /* If there is no last_subfile, our tag wasn't found. */
3535 if (!last_subfile)
3536 return (-1);
3537
3538 /* Search through subsequent nodes, wrapping around to the top
3539 of the info file until we find the string or return to this
3540 window's node and point. */
3541 while (1)
3542 {
3543 NODE *node;
3544
3545 /* Allow C-g to quit the search, failing it if pressed. */
3546 return_if_control_g (-1)do { info_gather_typeahead (); if (info_input_pending_p () ==
((toupper ('g')) & (~0x40))) return (-1); } while (0)
;
3547
3548 /* Find the next tag that isn't an anchor. */
3549 for (i = current_tag + dir; i != current_tag; i += dir)
3550 {
3551 if (i < 0)
3552 i = number_of_tags - 1;
3553 else if (i == number_of_tags)
3554 i = 0;
3555
3556 tag = file_buffer->tags[i];
3557 if (tag->nodelen != 0)
3558 break;
3559 }
3560
3561 /* If we got past out starting point, bail out. */
3562 if (i == current_tag)
3563 return (-1);
3564 current_tag = i;
3565
3566 if (!echo_area_is_active && (last_subfile != tag->filename))
3567 {
3568 window_message_in_echo_area
3569 ((char *) _("Searching subfile %s ...")((const char *) ("Searching subfile %s ...")),
3570 filename_non_directory (tag->filename), NULL((void *)0));
3571
3572 last_subfile = tag->filename;
3573 }
3574
3575 node = info_get_node (file_buffer->filename, tag->nodename);
3576
3577 if (!node)
3578 {
3579 /* If not doing i-search... */
3580 if (!echo_area_is_active)
3581 {
3582 if (info_recent_file_error)
3583 info_error (info_recent_file_error, NULL((void *)0), NULL((void *)0));
3584 else
3585 info_error ((char *) msg_cant_file_node,
3586 filename_non_directory (file_buffer->filename),
3587 tag->nodename);
3588 }
3589 return (-1);
3590 }
3591
3592 if (dir < 0)
3593 start = tag->nodelen;
3594
3595 ret =
3596 info_search_in_node (string, node, start, window, dir,
3597 case_sensitive);
3598
3599 /* Did we find the string in this node? */
3600 if (ret != -1)
3601 {
3602 /* Yes! We win. */
3603 remember_window_and_node (window, node);
3604 if (!echo_area_is_active)
3605 window_clear_echo_area ();
3606 return (0);
3607 }
3608
3609 /* No. Free this node, and make sure that we haven't passed
3610 our starting point. */
3611 free (node);
3612
3613 if (strcmp (initial_nodename, tag->nodename) == 0)
3614 return (-1);
3615 }
3616 }
3617 return (-1);
3618}
3619
3620DECLARE_INFO_COMMAND (info_search_case_sensitively,void info_search_case_sensitively (WINDOW *window, int count,
unsigned char key)
3621 _("Read a string and search for it case-sensitively"))void info_search_case_sensitively (WINDOW *window, int count,
unsigned char key)
3622{
3623 last_search_direction = count > 0 ? 1 : -1;
3624 last_search_case_sensitive = 1;
3625 info_search_1 (window, count, key, 1, 1);
3626}
3627
3628DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it"))void info_search (WINDOW *window, int count, unsigned char key
)
3629{
3630 last_search_direction = count > 0 ? 1 : -1;
3631 last_search_case_sensitive = 0;
3632 info_search_1 (window, count, key, 0, 1);
3633}
3634
3635DECLARE_INFO_COMMAND (info_search_backward,void info_search_backward (WINDOW *window, int count, unsigned
char key)
3636 _("Read a string and search backward for it"))void info_search_backward (WINDOW *window, int count, unsigned
char key)
3637{
3638 last_search_direction = count > 0 ? -1 : 1;
3639 last_search_case_sensitive = 0;
3640 info_search_1 (window, -count, key, 0, 1);
3641}
3642
3643static void
3644info_search_1 (WINDOW *window, int count, unsigned char key,
3645 int case_sensitive, int ask_for_string)
3646{
3647 char *line, *prompt;
3648 int result, old_pagetop;
3649 int direction;
3650
3651 if (count < 0)
3652 {
3653 direction = -1;
3654 count = -count;
3655 }
3656 else
3657 {
3658 direction = 1;
3659 if (count == 0)
3660 count = 1; /* for backward compatibility */
3661 }
3662
3663 /* Read a string from the user, defaulting the search to SEARCH_STRING. */
3664 if (!search_string)
3665 {
3666 search_string = (char *)xmalloc (search_string_size = 100);
3667 search_string[0] = '\0';
3668 }
3669
3670 if (ask_for_string)
3671 {
3672 prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: ")((const char *) ("%s%sfor string [%s]: ")))
3673 + strlen (_("Search backward")((const char *) ("Search backward")))
3674 + strlen (_("Search")((const char *) ("Search")))
3675 + strlen (_(" case-sensitively ")((const char *) (" case-sensitively ")))
3676 + strlen (_(" ")((const char *) (" ")))
3677 + strlen (search_string));
3678
3679 sprintf (prompt, _("%s%sfor string [%s]: ")((const char *) ("%s%sfor string [%s]: ")),
3680 direction < 0 ? _("Search backward")((const char *) ("Search backward")) : _("Search")((const char *) ("Search")),
3681 case_sensitive ? _(" case-sensitively ")((const char *) (" case-sensitively ")) : _(" ")((const char *) (" ")),
3682 search_string);
3683
3684 line = info_read_in_echo_area (window, prompt);
3685 free (prompt);
3686
3687 if (!line)
3688 {
3689 info_abort_key (window, 0, 0);
3690 return;
3691 }
3692
3693 if (*line)
3694 {
3695 if (strlen (line) + 1 > (unsigned int) search_string_size)
3696 search_string = (char *) xrealloc
3697 (search_string, (search_string_size += 50 + strlen (line)));
3698
3699 strcpy (search_string, line);
3700 free (line);
3701 }
3702 }
3703
3704 /* If the search string includes upper-case letters, make the search
3705 case-sensitive. */
3706 if (case_sensitive == 0)
3707 for (line = search_string; *line; line++)
3708 if (isupper (*line))
3709 {
3710 case_sensitive = 1;
3711 break;
3712 }
3713
3714 old_pagetop = active_window->pagetop;
3715 for (result = 0; result == 0 && count--; )
3716 result = info_search_internal (search_string,
3717 active_window, direction, case_sensitive);
3718
3719 if (result != 0 && !info_error_was_printed)
3720 info_error ((char *) _("Search failed.")((const char *) ("Search failed.")), NULL((void *)0), NULL((void *)0));
3721 else if (old_pagetop != active_window->pagetop)
3722 {
3723 int new_pagetop;
3724
3725 new_pagetop = active_window->pagetop;
3726 active_window->pagetop = old_pagetop;
3727 set_window_pagetop (active_window, new_pagetop);
3728 if (auto_footnotes_p)
3729 info_get_or_remove_footnotes (active_window);
3730 }
3731
3732 /* Perhaps free the unreferenced file buffers that were searched, but
3733 not retained. */
3734 info_gc_file_buffers ();
3735}
3736
3737DECLARE_INFO_COMMAND (info_search_next,void info_search_next (WINDOW *window, int count, unsigned char
key)
3738 _("Repeat last search in the same direction"))void info_search_next (WINDOW *window, int count, unsigned char
key)
3739{
3740 if (!last_search_direction)
3741 info_error ((char *) _("No previous search string")((const char *) ("No previous search string")), NULL((void *)0), NULL((void *)0));
3742 else
3743 info_search_1 (window, last_search_direction * count,
3744 key, last_search_case_sensitive, 0);
3745}
3746
3747DECLARE_INFO_COMMAND (info_search_previous,void info_search_previous (WINDOW *window, int count, unsigned
char key)
3748 _("Repeat last search in the reverse direction"))void info_search_previous (WINDOW *window, int count, unsigned
char key)
3749{
3750 if (!last_search_direction)
3751 info_error ((char *) _("No previous search string")((const char *) ("No previous search string")), NULL((void *)0), NULL((void *)0));
3752 else
3753 info_search_1 (window, -last_search_direction * count,
3754 key, last_search_case_sensitive, 0);
3755}
3756
3757/* **************************************************************** */
3758/* */
3759/* Incremental Searching */
3760/* */
3761/* **************************************************************** */
3762
3763static void incremental_search (WINDOW *window, int count,
3764 unsigned char ignore);
3765
3766DECLARE_INFO_COMMAND (isearch_forward,void isearch_forward (WINDOW *window, int count, unsigned char
key)
3767 _("Search interactively for a string as you type it"))void isearch_forward (WINDOW *window, int count, unsigned char
key)
3768{
3769 incremental_search (window, count, key);
3770}
3771
3772DECLARE_INFO_COMMAND (isearch_backward,void isearch_backward (WINDOW *window, int count, unsigned char
key)
3773 _("Search interactively for a string as you type it"))void isearch_backward (WINDOW *window, int count, unsigned char
key)
3774{
3775 incremental_search (window, -count, key);
3776}
3777
3778/* Incrementally search for a string as it is typed. */
3779/* The last accepted incremental search string. */
3780static char *last_isearch_accepted = (char *)NULL((void *)0);
3781
3782/* The current incremental search string. */
3783static char *isearch_string = (char *)NULL((void *)0);
3784static int isearch_string_index = 0;
3785static int isearch_string_size = 0;
3786static unsigned char isearch_terminate_search_key = ESC'\033';
3787
3788/* Array of search states. */
3789static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL((void *)0);
3790static int isearch_states_index = 0;
3791static int isearch_states_slots = 0;
3792
3793/* Push the state of this search. */
3794static void
3795push_isearch (WINDOW *window, int search_index, int direction, int failing)
3796{
3797 SEARCH_STATE *state;
3798
3799 state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE));
3800 window_get_state (window, state);
3801 state->search_index = search_index;
3802 state->direction = direction;
3803 state->failing = failing;
3804
3805 add_pointer_to_array (state, isearch_states_index, isearch_states,do { if (isearch_states_index + 2 >= isearch_states_slots)
isearch_states = (SEARCH_STATE * *)(xrealloc (isearch_states
, (isearch_states_slots += 20) * sizeof (SEARCH_STATE *))); isearch_states
[isearch_states_index++] = (SEARCH_STATE *)state; isearch_states
[isearch_states_index] = (SEARCH_STATE *)((void *)0); } while
(0)
3806 isearch_states_slots, 20, SEARCH_STATE *)do { if (isearch_states_index + 2 >= isearch_states_slots)
isearch_states = (SEARCH_STATE * *)(xrealloc (isearch_states
, (isearch_states_slots += 20) * sizeof (SEARCH_STATE *))); isearch_states
[isearch_states_index++] = (SEARCH_STATE *)state; isearch_states
[isearch_states_index] = (SEARCH_STATE *)((void *)0); } while
(0)
;
3807}
3808
3809/* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */
3810static void
3811pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing)
3812{
3813 SEARCH_STATE *state;
3814
3815 if (isearch_states_index)
3816 {
3817 isearch_states_index--;
3818 state = isearch_states[isearch_states_index];
3819 window_set_state (window, state);
3820 *search_index = state->search_index;
3821 *direction = state->direction;
3822 *failing = state->failing;
3823
3824 free (state);
3825 isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL((void *)0);
3826 }
3827}
3828
3829/* Free the memory used by isearch_states. */
3830static void
3831free_isearch_states (void)
3832{
3833 register int i;
3834
3835 for (i = 0; i < isearch_states_index; i++)
3836 {
3837 free (isearch_states[i]);
3838 isearch_states[i] = (SEARCH_STATE *)NULL((void *)0);
3839 }
3840 isearch_states_index = 0;
3841}
3842
3843/* Display the current search in the echo area. */
3844static void
3845show_isearch_prompt (int dir, unsigned char *string, int failing_p)
3846{
3847 register int i;
3848 const char *prefix;
3849 char *prompt, *p_rep;
3850 unsigned int prompt_len, p_rep_index, p_rep_size;
3851
3852 if (dir < 0)
3853 prefix = _("I-search backward: ")((const char *) ("I-search backward: "));
3854 else
3855 prefix = _("I-search: ")((const char *) ("I-search: "));
3856
3857 p_rep_index = p_rep_size = 0;
3858 p_rep = (char *)NULL((void *)0);
3859 for (i = 0; string[i]; i++)
3860 {
3861 char *rep;
3862
3863 switch (string[i])
3864 {
3865 case ' ': rep = " "; break;
3866 case LFD'\n': rep = "\\n"; break;
3867 case TAB'\011': rep = "\\t"; break;
3868 default:
3869 rep = pretty_keyname (string[i]);
3870 }
3871 if ((p_rep_index + strlen (rep) + 1) >= p_rep_size)
3872 p_rep = (char *)xrealloc (p_rep, p_rep_size += 100);
3873
3874 strcpy (p_rep + p_rep_index, rep);
3875 p_rep_index += strlen (rep);
3876 }
3877
3878 prompt_len = strlen (prefix) + p_rep_index + 1;
3879 if (failing_p)
3880 prompt_len += strlen (_("Failing ")((const char *) ("Failing ")));
3881 prompt = xmalloc (prompt_len);
3882 sprintf (prompt, "%s%s%s", failing_p ? _("Failing ")((const char *) ("Failing ")) : "", prefix,
3883 p_rep ? p_rep : "");
3884
3885 window_message_in_echo_area ("%s", prompt, NULL((void *)0));
3886 maybe_free (p_rep)do { if (p_rep) free (p_rep); } while (0);
3887 free (prompt);
3888 display_cursor_at_point (active_window);
3889}
3890
3891static void
3892incremental_search (WINDOW *window, int count, unsigned char ignore)
3893{
3894 unsigned char key;
3895 int last_search_result, search_result, dir;
3896 SEARCH_STATE mystate, orig_state;
3897 char *p;
3898 int case_sensitive = 0;
3899
3900 if (count < 0)
3901 dir = -1;
3902 else
3903 dir = 1;
3904
3905 last_search_result = search_result = 0;
3906
3907 window_get_state (window, &orig_state);
3908
3909 isearch_string_index = 0;
3910 if (!isearch_string_size)
3911 isearch_string = (char *)xmalloc (isearch_string_size = 50);
3912
3913 /* Show the search string in the echo area. */
3914 isearch_string[isearch_string_index] = '\0';
3915 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
3916
3917 isearch_is_active = 1;
3918
3919 while (isearch_is_active)
3920 {
3921 VFunction *func = (VFunction *)NULL((void *)0);
3922 int quoted = 0;
3923
3924 /* If a recent display was interrupted, then do the redisplay now if
3925 it is convenient. */
3926 if (!info_any_buffered_input_p () && display_was_interrupted_p)
3927 {
3928 display_update_one_window (window);
3929 display_cursor_at_point (active_window);
3930 }
3931
3932 /* Read a character and dispatch on it. */
3933 key = info_get_input_char ();
3934 window_get_state (window, &mystate);
3935
3936 if (key == DEL'\177' || key == Control ('h')((toupper ('h')) & (~0x40)))
3937 {
3938 /* User wants to delete one level of search? */
3939 if (!isearch_states_index)
3940 {
3941 terminal_ring_bell ();
3942 continue;
3943 }
3944 else
3945 {
3946 pop_isearch
3947 (window, &isearch_string_index, &dir, &search_result);
3948 isearch_string[isearch_string_index] = '\0';
3949 show_isearch_prompt (dir, (unsigned char *) isearch_string,
3950 search_result);
3951 goto after_search;
3952 }
3953 }
3954 else if (key == Control ('q')((toupper ('q')) & (~0x40)))
3955 {
3956 key = info_get_input_char ();
3957 quoted = 1;
3958 }
3959
3960 /* We are about to search again, or quit. Save the current search. */
3961 push_isearch (window, isearch_string_index, dir, search_result);
3962
3963 if (quoted)
3964 goto insert_and_search;
3965
3966 if (!Meta_p (key)(((key) > ('\177' + 1))) || key > 32)
3967 {
3968 /* If this key is not a keymap, get its associated function,
3969 if any. If it is a keymap, then it's probably ESC from an
3970 arrow key, and we handle that case below. */
3971 char type = window->keymap[key].type;
3972 func = type == ISFUNC0
3973 ? InfoFunction(window->keymap[key].function)((window->keymap[key].function) ? (window->keymap[key].
function)->func : (VFunction *) ((void *)0))
3974 : NULL((void *)0); /* function member is a Keymap if ISKMAP */
3975
3976 if (isprint (key) || (type == ISFUNC0 && func == NULL((void *)0)))
3977 {
3978 insert_and_search:
3979
3980 if (isearch_string_index + 2 >= isearch_string_size)
3981 isearch_string = (char *)xrealloc
3982 (isearch_string, isearch_string_size += 100);
3983
3984 isearch_string[isearch_string_index++] = key;
3985 isearch_string[isearch_string_index] = '\0';
3986 goto search_now;
3987 }
3988 else if (func == (VFunction *) isearch_forward
3989 || func == (VFunction *) isearch_backward)
3990 {
3991 /* If this key invokes an incremental search, then this
3992 means that we will either search again in the same
3993 direction, search again in the reverse direction, or
3994 insert the last search string that was accepted through
3995 incremental searching. */
3996 if ((func == (VFunction *) isearch_forward && dir > 0) ||
3997 (func == (VFunction *) isearch_backward && dir < 0))
3998 {
3999 /* If the user has typed no characters, then insert the
4000 last successful search into the current search string. */
4001 if (isearch_string_index == 0)
4002 {
4003 /* Of course, there must be something to insert. */
4004 if (last_isearch_accepted)
4005 {
4006 if (strlen ((char *) last_isearch_accepted) + 1
4007 >= (unsigned int) isearch_string_size)
4008 isearch_string = (char *)
4009 xrealloc (isearch_string,
4010 isearch_string_size += 10 +
4011 strlen (last_isearch_accepted));
4012 strcpy (isearch_string, last_isearch_accepted);
4013 isearch_string_index = strlen (isearch_string);
4014 goto search_now;
4015 }
4016 else
4017 continue;
4018 }
4019 else
4020 {
4021 /* Search again in the same direction. This means start
4022 from a new place if the last search was successful. */
4023 if (search_result == 0)
4024 window->point += dir;
4025 }
4026 }
4027 else
4028 {
4029 /* Reverse the direction of the search. */
4030 dir = -dir;
4031 }
4032 }
4033 else if (func == (VFunction *) info_abort_key)
4034 {
4035 /* If C-g pressed, and the search is failing, pop the search
4036 stack back to the last unfailed search. */
4037 if (isearch_states_index && (search_result != 0))
4038 {
4039 terminal_ring_bell ();
4040 while (isearch_states_index && (search_result != 0))
4041 pop_isearch
4042 (window, &isearch_string_index, &dir, &search_result);
4043 isearch_string[isearch_string_index] = '\0';
4044 show_isearch_prompt (dir, (unsigned char *) isearch_string,
4045 search_result);
4046 continue;
4047 }
4048 else
4049 goto exit_search;
4050 }
4051 else
4052 goto exit_search;
4053 }
4054 else
4055 {
4056 exit_search:
4057 /* The character is not printable, or it has a function which is
4058 non-null. Exit the search, remembering the search string. If
4059 the key is not the same as the isearch_terminate_search_key,
4060 then push it into pending input. */
4061 if (isearch_string_index && func != (VFunction *) info_abort_key)
4062 {
4063 maybe_free (last_isearch_accepted)do { if (last_isearch_accepted) free (last_isearch_accepted);
} while (0)
;
4064 last_isearch_accepted = xstrdup (isearch_string);
4065 }
4066
4067 /* If the key is the isearch_terminate_search_key, but some buffered
4068 input is pending, it is almost invariably because the ESC key is
4069 actually the beginning of an escape sequence, like in case they
4070 pressed an arrow key. So don't gobble the ESC key, push it back
4071 into pending input. */
4072 /* FIXME: this seems like a kludge! We need a more reliable
4073 mechanism to know when ESC is a separate key and when it is
4074 part of an escape sequence. */
4075 if (key != RET'\r' /* Emacs addicts want RET to get lost */
4076 && (key != isearch_terminate_search_key
4077 || info_any_buffered_input_p ()))
4078 info_set_pending_input (key);
4079
4080 if (func == (VFunction *) info_abort_key)
4081 {
4082 if (isearch_states_index)
4083 window_set_state (window, &orig_state);
4084 }
4085
4086 if (!echo_area_is_active)
4087 window_clear_echo_area ();
4088
4089 if (auto_footnotes_p)
4090 info_get_or_remove_footnotes (active_window);
4091
4092 isearch_is_active = 0;
4093 continue;
4094 }
4095
4096 /* Search for the contents of isearch_string. */
4097 search_now:
4098 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4099
4100 /* If the search string includes upper-case letters, make the
4101 search case-sensitive. */
4102 for (p = isearch_string; *p; p++)
4103 if (isupper (*p))
4104 {
4105 case_sensitive = 1;
4106 break;
4107 }
4108
4109
4110 if (search_result == 0)
4111 {
4112 /* Check to see if the current search string is right here. If
4113 we are looking at it, then don't bother calling the search
4114 function. */
4115 if (((dir < 0) &&
4116 ((case_sensitive ? strncmp : strncasecmp)
4117 (window->node->contents + window->point,
4118 isearch_string, isearch_string_index) == 0)) ||
4119 ((dir > 0) &&
4120 ((window->point - isearch_string_index) >= 0) &&
4121 ((case_sensitive ? strncmp : strncasecmp)
4122 (window->node->contents +
4123 (window->point - (isearch_string_index - 1)),
4124 isearch_string, isearch_string_index) == 0)))
4125 {
4126 if (dir > 0)
4127 window->point++;
4128 }
4129 else
4130 search_result = info_search_internal (isearch_string,
4131 window, dir, case_sensitive);
4132 }
4133
4134 /* If this search failed, and we didn't already have a failed search,
4135 then ring the terminal bell. */
4136 if (search_result != 0 && last_search_result == 0)
4137 terminal_ring_bell ();
4138
4139 after_search:
4140 show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result);
4141
4142 if (search_result == 0)
4143 {
4144 if ((mystate.node == window->node) &&
4145 (mystate.pagetop != window->pagetop))
4146 {
4147 int newtop = window->pagetop;
4148 window->pagetop = mystate.pagetop;
4149 set_window_pagetop (window, newtop);
4150 }
4151 display_update_one_window (window);
4152 display_cursor_at_point (window);
4153 }
4154
4155 last_search_result = search_result;
4156 }
4157
4158 /* Free the memory used to remember each search state. */
4159 free_isearch_states ();
4160
4161 /* Perhaps GC some file buffers. */
4162 info_gc_file_buffers ();
4163
4164 /* After searching, leave the window in the correct state. */
4165 if (!echo_area_is_active)
4166 window_clear_echo_area ();
4167}
4168
4169/* GC some file buffers. A file buffer can be gc-ed if there we have
4170 no nodes in INFO_WINDOWS that reference this file buffer's contents.
4171 Garbage collecting a file buffer means to free the file buffers
4172 contents. */
4173static void
4174info_gc_file_buffers (void)
4175{
4176 register int fb_index, iw_index, i;
4177 register FILE_BUFFER *fb;
4178 register INFO_WINDOW *iw;
4179
4180 if (!info_loaded_files)
4181 return;
4182
4183 for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++)
4184 {
4185 int fb_referenced_p = 0;
4186
4187 /* If already gc-ed, do nothing. */
4188 if (!fb->contents)
4189 continue;
4190
4191 /* If this file had to be uncompressed, check to see if we should
4192 gc it. This means that the user-variable "gc-compressed-files"
4193 is non-zero. */
4194 if ((fb->flags & N_IsCompressed0x08) && !gc_compressed_files)
4195 continue;
4196
4197 /* If this file's contents are not gc-able, move on. */
4198 if (fb->flags & N_CannotGC0x20)
4199 continue;
4200
4201 /* Check each INFO_WINDOW to see if it has any nodes which reference
4202 this file. */
4203 for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++)
4204 {
4205 for (i = 0; iw->nodes && iw->nodes[i]; i++)
4206 {
4207 if ((FILENAME_CMPstrcmp (fb->fullpath, iw->nodes[i]->filename) == 0) ||
4208 (FILENAME_CMPstrcmp (fb->filename, iw->nodes[i]->filename) == 0))
4209 {
4210 fb_referenced_p = 1;
4211 break;
4212 }
4213 }
4214 }
4215
4216 /* If this file buffer wasn't referenced, free its contents. */
4217 if (!fb_referenced_p)
4218 {
4219 free (fb->contents);
4220 fb->contents = (char *)NULL((void *)0);
4221 }
4222 }
4223}
4224
4225/* **************************************************************** */
4226/* */
4227/* Traversing and Selecting References */
4228/* */
4229/* **************************************************************** */
4230
4231/* Move to the next or previous cross reference in this node. */
4232static void
4233info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir)
4234{
4235 long firstmenu, firstxref;
4236 long nextmenu, nextxref;
4237 long placement = -1;
4238 long start = 0;
4239 NODE *node = window->node;
4240
4241 if (dir < 0)
4242 start = node->nodelen;
4243
4244 /* This search is only allowed to fail if there is no menu or cross
4245 reference in the current node. Otherwise, the first menu or xref
4246 found is moved to. */
4247
4248 firstmenu = info_search_in_node
4249 (INFO_MENU_ENTRY_LABEL"\n* ", node, start, (WINDOW *)NULL((void *)0), dir, 0);
4250
4251 /* FIRSTMENU may point directly to the line defining the menu. Skip that
4252 and go directly to the first item. */
4253
4254 if (firstmenu != -1)
4255 {
4256 char *text = node->contents + firstmenu;
4257
4258 if (strncmp (text, INFO_MENU_LABEL"\n* Menu:", strlen (INFO_MENU_LABEL"\n* Menu:")) == 0)
4259 firstmenu = info_search_in_node
4260 (INFO_MENU_ENTRY_LABEL"\n* ", node, firstmenu + dir, (WINDOW *)NULL((void *)0), dir, 0);
4261 }
4262
4263 firstxref =
4264 info_search_in_node (INFO_XREF_LABEL"*Note", node, start, (WINDOW *)NULL((void *)0), dir, 0);
4265
4266#if defined (HANDLE_MAN_PAGES)
4267 if ((firstxref == -1) && (node->flags & N_IsManPage0x40))
4268 {
4269 firstxref = locate_manpage_xref (node, start, dir);
4270 }
4271#endif /* HANDLE_MAN_PAGES */
4272
4273 if (firstmenu == -1 && firstxref == -1)
4274 {
4275 info_error ((char *) msg_no_xref_node, NULL((void *)0), NULL((void *)0));
4276 return;
4277 }
4278
4279 /* There is at least one cross reference or menu entry in this node.
4280 Try hard to find the next available one. */
4281
4282 nextmenu = info_search_in_node
4283 (INFO_MENU_ENTRY_LABEL"\n* ", node, window->point + dir, (WINDOW *)NULL((void *)0), dir, 0);
4284
4285 nextxref = info_search_in_node
4286 (INFO_XREF_LABEL"*Note", node, window->point + dir, (WINDOW *)NULL((void *)0), dir, 0);
4287
4288#if defined (HANDLE_MAN_PAGES)
4289 if ((nextxref == -1) && (node->flags & N_IsManPage0x40) && (firstxref != -1))
4290 nextxref = locate_manpage_xref (node, window->point + dir, dir);
4291#endif /* HANDLE_MAN_PAGES */
4292
4293 /* Ignore "Menu:" as a menu item. */
4294 if (nextmenu != -1)
4295 {
4296 char *text = node->contents + nextmenu;
4297
4298 if (strncmp (text, INFO_MENU_LABEL"\n* Menu:", strlen (INFO_MENU_LABEL"\n* Menu:")) == 0)
4299 nextmenu = info_search_in_node
4300 (INFO_MENU_ENTRY_LABEL"\n* ", node, nextmenu + dir, (WINDOW *)NULL((void *)0), dir, 0);
4301 }
4302
4303 /* If there is both a next menu entry, and a next xref entry, choose the
4304 one which occurs first. Otherwise, select the one which actually
4305 appears in this node following point. */
4306 if (nextmenu != -1 && nextxref != -1)
4307 {
4308 if (((dir == 1) && (nextmenu < nextxref)) ||
4309 ((dir == -1) && (nextmenu > nextxref)))
4310 placement = nextmenu + 1;
4311 else
4312 placement = nextxref;
4313 }
4314 else if (nextmenu != -1)
4315 placement = nextmenu + 1;
4316 else if (nextxref != -1)
4317 placement = nextxref;
4318
4319 /* If there was neither a menu or xref entry appearing in this node after
4320 point, choose the first menu or xref entry appearing in this node. */
4321 if (placement == -1)
4322 {
4323 if (firstmenu != -1 && firstxref != -1)
4324 {
4325 if (((dir == 1) && (firstmenu < firstxref)) ||
4326 ((dir == -1) && (firstmenu > firstxref)))
4327 placement = firstmenu + 1;
4328 else
4329 placement = firstxref;
4330 }
4331 else if (firstmenu != -1)
4332 placement = firstmenu + 1;
4333 else
4334 placement = firstxref;
4335 }
4336 window->point = placement;
4337 window_adjust_pagetop (window);
4338 window->flags |= W_UpdateWindow0x01;
4339}
4340
4341DECLARE_INFO_COMMAND (info_move_to_prev_xref,void info_move_to_prev_xref (WINDOW *window, int count, unsigned
char key)
4342 _("Move to the previous cross reference"))void info_move_to_prev_xref (WINDOW *window, int count, unsigned
char key)
4343{
4344 if (count < 0)
4345 info_move_to_prev_xref (window, -count, key);
4346 else
4347 info_move_to_xref (window, count, key, -1);
4348}
4349
4350DECLARE_INFO_COMMAND (info_move_to_next_xref,void info_move_to_next_xref (WINDOW *window, int count, unsigned
char key)
4351 _("Move to the next cross reference"))void info_move_to_next_xref (WINDOW *window, int count, unsigned
char key)
4352{
4353 if (count < 0)
4354 info_move_to_next_xref (window, -count, key);
4355 else
4356 info_move_to_xref (window, count, key, 1);
4357}
4358
4359/* Select the menu item or reference that appears on this line. */
4360DECLARE_INFO_COMMAND (info_select_reference_this_line,void info_select_reference_this_line (WINDOW *window, int count
, unsigned char key)
4361 _("Select reference or menu item appearing on this line"))void info_select_reference_this_line (WINDOW *window, int count
, unsigned char key)
4362{
4363 char *line;
4364
4365 if (window->line_starts)
4366 line = window->line_starts[window_line_of_point (window)];
4367 else
4368 line = "";
4369
4370 /* If this line contains a menu item, select that one. */
4371 if (strncmp ("* ", line, 2) == 0)
4372 info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
4373 else
4374 info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
4375}
4376
4377/* **************************************************************** */
4378/* */
4379/* Miscellaneous Info Commands */
4380/* */
4381/* **************************************************************** */
4382
4383/* What to do when C-g is pressed in a window. */
4384DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation"))void info_abort_key (WINDOW *window, int count, unsigned char
key)
4385{
4386 /* If error printing doesn't oridinarily ring the bell, do it now,
4387 since C-g always rings the bell. Otherwise, let the error printer
4388 do it. */
4389 if (!info_error_rings_bell_p)
4390 terminal_ring_bell ();
4391 info_error ((char *) _("Quit")((const char *) ("Quit")), NULL((void *)0), NULL((void *)0));
4392
4393 info_initialize_numeric_arg ();
4394 info_clear_pending_input ();
4395 info_last_executed_command = (VFunction *)NULL((void *)0);
4396}
4397
4398/* Move the cursor to the desired line of the window. */
4399DECLARE_INFO_COMMAND (info_move_to_window_line,void info_move_to_window_line (WINDOW *window, int count, unsigned
char key)
4400 _("Move the cursor to a specific line of the window"))void info_move_to_window_line (WINDOW *window, int count, unsigned
char key)
4401{
4402 int line;
4403
4404 /* With no numeric argument of any kind, default to the center line. */
4405 if (!info_explicit_arg && count == 1)
4406 line = (window->height / 2) + window->pagetop;
4407 else
4408 {
4409 if (count < 0)
4410 line = (window->height + count) + window->pagetop;
4411 else
4412 line = window->pagetop + count;
4413 }
4414
4415 /* If the line doesn't appear in this window, make it do so. */
4416 if ((line - window->pagetop) >= window->height)
4417 line = window->pagetop + (window->height - 1);
4418
4419 /* If the line is too small, make it fit. */
4420 if (line < window->pagetop)
4421 line = window->pagetop;
4422
4423 /* If the selected line is past the bottom of the node, force it back. */
4424 if (line >= window->line_count)
4425 line = window->line_count - 1;
4426
4427 window->point = (window->line_starts[line] - window->node->contents);
4428}
4429
4430/* Clear the screen and redraw its contents. Given a numeric argument,
4431 move the line the cursor is on to the COUNT'th line of the window. */
4432DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display"))void info_redraw_display (WINDOW *window, int count, unsigned
char key)
4433{
4434 if ((!info_explicit_arg && count == 1) || echo_area_is_active)
4435 {
4436 terminal_clear_screen ();
4437 display_clear_display (the_display);
4438 window_mark_chain (windows, W_UpdateWindow0x01);
4439 display_update_display (windows);
4440 }
4441 else
4442 {
4443 int desired_line, point_line;
4444 int new_pagetop;
4445
4446 point_line = window_line_of_point (window) - window->pagetop;
4447
4448 if (count < 0)
4449 desired_line = window->height + count;
4450 else
4451 desired_line = count;
4452
4453 if (desired_line < 0)
4454 desired_line = 0;
4455
4456 if (desired_line >= window->height)
4457 desired_line = window->height - 1;
4458
4459 if (desired_line == point_line)
4460 return;
4461
4462 new_pagetop = window->pagetop + (point_line - desired_line);
4463
4464 set_window_pagetop (window, new_pagetop);
4465 }
4466}
4467/* This command does nothing. It is the fact that a key is bound to it
4468 that has meaning. See the code at the top of info_session (). */
4469DECLARE_INFO_COMMAND (info_quit, _("Quit using Info"))void info_quit (WINDOW *window, int count, unsigned char key)
4470{}
4471
4472
4473/* **************************************************************** */
4474/* */
4475/* Reading Keys and Dispatching on Them */
4476/* */
4477/* **************************************************************** */
4478
4479/* Declaration only. Special cased in info_dispatch_on_key ().
4480 Doc string is to avoid ugly results with describe_key etc. */
4481DECLARE_INFO_COMMAND (info_do_lowercase_version,void info_do_lowercase_version (WINDOW *window, int count, unsigned
char key)
4482 _("Run command bound to this key's lowercase variant"))void info_do_lowercase_version (WINDOW *window, int count, unsigned
char key)
4483{}
4484
4485static void
4486dispatch_error (char *keyseq)
4487{
4488 char *rep;
4489
4490 rep = pretty_keyseq (keyseq);
4491
4492 if (!echo_area_is_active)
4493 info_error ((char *) _("Unknown command (%s).")((const char *) ("Unknown command (%s).")), rep, NULL((void *)0));
4494 else
4495 {
4496 char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid")((const char *) ("\"%s\" is invalid"))));
4497 sprintf (temp, _("`%s' is invalid")((const char *) ("`%s' is invalid")), rep);
4498 terminal_ring_bell ();
4499 inform_in_echo_area (temp);
4500 free (temp);
4501 }
4502}
4503
4504/* Keeping track of key sequences. */
4505static char *info_keyseq = (char *)NULL((void *)0);
4506static int info_keyseq_index = 0;
4507static int info_keyseq_size = 0;
4508static int info_keyseq_displayed_p = 0;
4509
4510/* Initialize the length of the current key sequence. */
4511void
4512initialize_keyseq (void)
4513{
4514 info_keyseq_index = 0;
4515 info_keyseq_displayed_p = 0;
4516}
4517
4518/* Add CHARACTER to the current key sequence. */
4519void
4520add_char_to_keyseq (char character)
4521{
4522 if (info_keyseq_index + 2 >= info_keyseq_size)
4523 info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10);
4524
4525 info_keyseq[info_keyseq_index++] = character;
4526 info_keyseq[info_keyseq_index] = '\0';
4527}
4528
4529/* Display the current value of info_keyseq. If argument EXPECTING is
4530 non-zero, input is expected to be read after the key sequence is
4531 displayed, so add an additional prompting character to the sequence. */
4532static void
4533display_info_keyseq (int expecting_future_input)
4534{
4535 char *rep;
4536
4537 rep = pretty_keyseq (info_keyseq);
4538 if (expecting_future_input)
4539 strcat (rep, "-");
4540
4541 if (echo_area_is_active)
4542 inform_in_echo_area (rep);
4543 else
4544 {
4545 window_message_in_echo_area (rep, NULL((void *)0), NULL((void *)0));
4546 display_cursor_at_point (active_window);
4547 }
4548 info_keyseq_displayed_p = 1;
4549}
4550
4551/* Called by interactive commands to read a keystroke. */
4552unsigned char
4553info_get_another_input_char (void)
4554{
4555 int ready = !info_keyseq_displayed_p; /* ready if new and pending key */
4556
4557 /* If there isn't any input currently available, then wait a
4558 moment looking for input. If we don't get it fast enough,
4559 prompt a little bit with the current key sequence. */
4560 if (!info_keyseq_displayed_p)
4561 {
4562 ready = 1;
4563 if (!info_any_buffered_input_p () &&
4564 !info_input_pending_p ())
4565 {
4566#if defined (FD_SET)
4567 struct timeval timer;
4568 fd_set readfds;
4569
4570 FD_ZERO (&readfds)do { fd_set *_p = (&readfds); __size_t _n = (((1024) + ((
((unsigned)(sizeof(__fd_mask) * 8))) - 1)) / (((unsigned)(sizeof
(__fd_mask) * 8)))); while (_n > 0) _p->fds_bits[--_n] =
0; } while (0)
;
4571 FD_SET (fileno (info_input_stream), &readfds)__fd_set(((!__isthreaded ? ((info_input_stream)->_file) : (
fileno)(info_input_stream))), (&readfds))
;
4572 timer.tv_sec = 1;
4573 timer.tv_usec = 750;
4574 ready = select (fileno(info_input_stream)(!__isthreaded ? ((info_input_stream)->_file) : (fileno)(info_input_stream
))
+1, &readfds, (fd_set *)NULL((void *)0), (fd_set *)NULL((void *)0), &timer);
4575#else
4576 ready = 0;
4577#endif /* FD_SET */
4578 }
4579 }
4580
4581 if (!ready)
4582 display_info_keyseq (1);
4583
4584 return (info_get_input_char ());
4585}
4586
4587/* Do the command associated with KEY in MAP. If the associated command is
4588 really a keymap, then read another key, and dispatch into that map. */
4589void
4590info_dispatch_on_key (unsigned char key, Keymap map)
4591{
4592#if !defined(INFOKEY)
4593 if (Meta_p (key)(((key) > ('\177' + 1))) && (!ISO_Latin_p || map[key].function != ea_insert))
4594 {
4595 if (map[ESC'\033'].type == ISKMAP1)
4596 {
4597 map = (Keymap)map[ESC'\033'].function;
4598 add_char_to_keyseq (ESC'\033');
4599 key = UnMeta (key)((key) & (~0x80));
4600 info_dispatch_on_key (key, map);
4601 }
4602 else
4603 {
4604 dispatch_error (info_keyseq);
4605 }
4606 return;
4607 }
4608#endif /* INFOKEY */
4609
4610 switch (map[key].type)
4611 {
4612 case ISFUNC0:
4613 {
4614 VFunction *func;
4615
4616 func = InfoFunction(map[key].function)((map[key].function) ? (map[key].function)->func : (VFunction
*) ((void *)0))
;
4617 if (func != (VFunction *)NULL((void *)0))
4618 {
4619 /* Special case info_do_lowercase_version (). */
4620 if (func == (VFunction *) info_do_lowercase_version)
4621 {
4622#if defined(INFOKEY)
4623 unsigned char lowerkey;
4624
4625 lowerkey = Meta_p(key)(((key) > ('\177' + 1))) ? Meta (tolower (UnMeta (key)))((tolower (((key) & (~0x80)))) | (0x80)) : tolower (key);
4626 if (lowerkey == key)
4627 {
4628 add_char_to_keyseq (key);
4629 dispatch_error (info_keyseq);
4630 return;
4631 }
4632 info_dispatch_on_key (lowerkey, map);
4633#else /* !INFOKEY */
4634 info_dispatch_on_key (tolower (key), map);
4635#endif /* INFOKEY */
4636 return;
4637 }
4638
4639 add_char_to_keyseq (key);
4640
4641 if (info_keyseq_displayed_p)
4642 display_info_keyseq (0);
4643
4644 {
4645 WINDOW *where;
4646
4647 where = active_window;
4648 (*InfoFunction(map[key].function)((map[key].function) ? (map[key].function)->func : (VFunction
*) ((void *)0))
)
4649 (active_window, info_numeric_arg * info_numeric_arg_sign, key);
4650
4651 /* If we have input pending, then the last command was a prefix
4652 command. Don't change the value of the last function vars.
4653 Otherwise, remember the last command executed in the var
4654 appropriate to the window in which it was executed. */
4655 if (!info_input_pending_p ())
4656 {
4657 if (where == the_echo_area)
4658 ea_last_executed_command = InfoFunction(map[key].function)((map[key].function) ? (map[key].function)->func : (VFunction
*) ((void *)0))
;
4659 else
4660 info_last_executed_command = InfoFunction(map[key].function)((map[key].function) ? (map[key].function)->func : (VFunction
*) ((void *)0))
;
4661 }
4662 }
4663 }
4664 else
4665 {
4666 add_char_to_keyseq (key);
4667 dispatch_error (info_keyseq);
4668 return;
4669 }
4670 }
4671 break;
4672
4673 case ISKMAP1:
4674 add_char_to_keyseq (key);
4675 if (map[key].function != (InfoCommand *)NULL((void *)0))
4676 {
4677 unsigned char newkey;
4678
4679 newkey = info_get_another_input_char ();
4680 info_dispatch_on_key (newkey, (Keymap)map[key].function);
4681 }
4682 else
4683 {
4684 dispatch_error (info_keyseq);
4685 return;
4686 }
4687 break;
4688 }
4689}
4690
4691/* **************************************************************** */
4692/* */
4693/* Numeric Arguments */
4694/* */
4695/* **************************************************************** */
4696
4697/* Handle C-u style numeric args, as well as M--, and M-digits. */
4698
4699/* Non-zero means that an explicit argument has been passed to this
4700 command, as in C-u C-v. */
4701int info_explicit_arg = 0;
4702
4703/* The sign of the numeric argument. */
4704int info_numeric_arg_sign = 1;
4705
4706/* The value of the argument itself. */
4707int info_numeric_arg = 1;
4708
4709/* Add the current digit to the argument in progress. */
4710DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg,void info_add_digit_to_numeric_arg (WINDOW *window, int count
, unsigned char key)
4711 _("Add this digit to the current numeric argument"))void info_add_digit_to_numeric_arg (WINDOW *window, int count
, unsigned char key)
4712{
4713 info_numeric_arg_digit_loop (window, 0, key);
4714}
4715
4716/* C-u, universal argument. Multiply the current argument by 4.
4717 Read a key. If the key has nothing to do with arguments, then
4718 dispatch on it. If the key is the abort character then abort. */
4719DECLARE_INFO_COMMAND (info_universal_argument,void info_universal_argument (WINDOW *window, int count, unsigned
char key)
4720 _("Start (or multiply by 4) the current numeric argument"))void info_universal_argument (WINDOW *window, int count, unsigned
char key)
4721{
4722 info_numeric_arg *= 4;
4723 info_numeric_arg_digit_loop (window, 0, 0);
4724}
4725
4726/* Create a default argument. */
4727void
4728info_initialize_numeric_arg (void)
4729{
4730 info_numeric_arg = info_numeric_arg_sign = 1;
4731 info_explicit_arg = 0;
4732}
4733
4734DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop,void info_numeric_arg_digit_loop (WINDOW *window, int count, unsigned
char key)
4735 _("Internally used by \\[universal-argument]"))void info_numeric_arg_digit_loop (WINDOW *window, int count, unsigned
char key)
4736{
4737 unsigned char pure_key;
4738 Keymap keymap = window->keymap;
4739
4740 while (1)
4741 {
4742 if (key)
4743 pure_key = key;
4744 else
4745 {
4746 if (display_was_interrupted_p && !info_any_buffered_input_p ())
4747 display_update_display (windows);
4748
4749 if (active_window != the_echo_area)
4750 display_cursor_at_point (active_window);
4751
4752 pure_key = key = info_get_another_input_char ();
4753
4754#if !defined(INFOKEY)
4755 if (Meta_p (key)(((key) > ('\177' + 1))))
4756 add_char_to_keyseq (ESC'\033');
4757
4758 add_char_to_keyseq (UnMeta (key)((key) & (~0x80)));
4759#else /* defined(INFOKEY) */
4760 add_char_to_keyseq (key);
4761#endif /* defined(INFOKEY) */
4762 }
4763
4764#if !defined(INFOKEY)
4765 if (Meta_p (key)(((key) > ('\177' + 1))))
4766 key = UnMeta (key)((key) & (~0x80));
4767#endif /* !defined(INFOKEY) */
4768
4769 if (keymap[key].type == ISFUNC0
4770 && InfoFunction(keymap[key].function)((keymap[key].function) ? (keymap[key].function)->func : (
VFunction *) ((void *)0))
4771 == (VFunction *) info_universal_argument)
4772 {
4773 info_numeric_arg *= 4;
4774 key = 0;
4775 continue;
4776 }
4777
4778#if defined(INFOKEY)
4779 if (Meta_p (key)(((key) > ('\177' + 1))))
4780 key = UnMeta (key)((key) & (~0x80));
4781#endif /* !defined(INFOKEY) */
4782
4783
4784 if (isdigit (key))
4785 {
4786 if (info_explicit_arg)
4787 info_numeric_arg = (info_numeric_arg * 10) + (key - '0');
4788 else
4789 info_numeric_arg = (key - '0');
4790 info_explicit_arg = 1;
4791 }
4792 else
4793 {
4794 if (key == '-' && !info_explicit_arg)
4795 {
4796 info_numeric_arg_sign = -1;
4797 info_numeric_arg = 1;
4798 }
4799 else
4800 {
4801 info_keyseq_index--;
4802 info_dispatch_on_key (pure_key, keymap);
4803 return;
4804 }
4805 }
4806 key = 0;
4807 }
4808}
4809
4810/* **************************************************************** */
4811/* */
4812/* Input Character Buffering */
4813/* */
4814/* **************************************************************** */
4815
4816/* Character waiting to be read next. */
4817static int pending_input_character = 0;
4818
4819/* How to make there be no pending input. */
4820static void
4821info_clear_pending_input (void)
4822{
4823 pending_input_character = 0;
4824}
4825
4826/* How to set the pending input character. */
4827static void
4828info_set_pending_input (unsigned char key)
4829{
4830 pending_input_character = key;
4831}
4832
4833/* How to see if there is any pending input. */
4834unsigned char
4835info_input_pending_p (void)
4836{
4837 return (pending_input_character);
4838}
4839
4840/* Largest number of characters that we can read in advance. */
4841#define MAX_INFO_INPUT_BUFFERING512 512
4842
4843static int pop_index = 0, push_index = 0;
4844static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING512];
4845
4846/* Add KEY to the buffer of characters to be read. */
4847static void
4848info_push_typeahead (unsigned char key)
4849{
4850 /* Flush all pending input in the case of C-g pressed. */
4851 if (key == Control ('g')((toupper ('g')) & (~0x40)))
4852 {
4853 push_index = pop_index;
4854 info_set_pending_input (Control ('g')((toupper ('g')) & (~0x40)));
4855 }
4856 else
4857 {
4858 info_input_buffer[push_index++] = key;
4859 if ((unsigned int) push_index >= sizeof (info_input_buffer))
4860 push_index = 0;
4861 }
4862}
4863
4864/* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */
4865static int
4866info_input_buffer_space_available (void)
4867{
4868 if (pop_index > push_index)
4869 return (pop_index - push_index);
4870 else
4871 return (sizeof (info_input_buffer) - (push_index - pop_index));
4872}
4873
4874/* Get a key from the buffer of characters to be read.
4875 Return the key in KEY.
4876 Result is non-zero if there was a key, or 0 if there wasn't. */
4877static int
4878info_get_key_from_typeahead (unsigned char *key)
4879{
4880 if (push_index == pop_index)
4881 return (0);
4882
4883 *key = info_input_buffer[pop_index++];
4884
4885 if ((unsigned int) pop_index >= sizeof (info_input_buffer))
4886 pop_index = 0;
4887
4888 return (1);
4889}
4890
4891int
4892info_any_buffered_input_p (void)
4893{
4894 info_gather_typeahead ();
4895 return (push_index != pop_index);
4896}
4897
4898/* If characters are available to be read, then read them and stuff them into
4899 info_input_buffer. Otherwise, do nothing. */
4900void
4901info_gather_typeahead (void)
4902{
4903 register int i = 0;
4904 int tty, space_avail;
4905 long chars_avail;
4906 unsigned char input[MAX_INFO_INPUT_BUFFERING512];
4907
4908 tty = fileno (info_input_stream)(!__isthreaded ? ((info_input_stream)->_file) : (fileno)(info_input_stream
))
;
4909 chars_avail = 0;
4910
4911 space_avail = info_input_buffer_space_available ();
4912
4913 /* If we can just find out how many characters there are to read, do so. */
4914#if defined (FIONREAD((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('f')) << 8) | ((127)))
)
4915 {
4916 ioctl (tty, FIONREAD((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('f')) << 8) | ((127)))
, &chars_avail);
4917
4918 if (chars_avail > space_avail)
4919 chars_avail = space_avail;
4920
4921 if (chars_avail)
4922 chars_avail = read (tty, &input[0], chars_avail);
4923 }
4924#else /* !FIONREAD */
4925# if defined (O_NDELAY0x0004)
4926 {
4927 int flags;
4928
4929 flags = fcntl (tty, F_GETFL3, 0);
4930
4931 fcntl (tty, F_SETFL4, (flags | O_NDELAY0x0004));
4932 chars_avail = read (tty, &input[0], space_avail);
4933 fcntl (tty, F_SETFL4, flags);
4934
4935 if (chars_avail == -1)
4936 chars_avail = 0;
4937 }
4938# else /* !O_NDELAY */
4939# ifdef __DJGPP__
4940 {
4941 extern long pc_term_chars_avail (void);
4942
4943 if (isatty (tty))
4944 chars_avail = pc_term_chars_avail ();
4945 else
4946 {
4947 /* We could be more accurate by calling ltell, but we have no idea
4948 whether tty is buffered by stdio functions, and if so, how many
4949 characters are already waiting in the buffer. So we punt. */
4950 struct stat st;
4951
4952 if (fstat (tty, &st) < 0)
4953 chars_avail = 1;
4954 else
4955 chars_avail = st.st_size;
4956 }
4957 if (chars_avail > space_avail)
4958 chars_avail = space_avail;
4959 if (chars_avail)
4960 chars_avail = read (tty, &input[0], chars_avail);
4961 }
4962# endif/* __DJGPP__ */
4963# endif /* O_NDELAY */
4964#endif /* !FIONREAD */
4965
4966 while (i < chars_avail)
4967 {
4968 info_push_typeahead (input[i]);
4969 i++;
4970 }
4971}
4972
4973/* How to read a single character. */
4974unsigned char
4975info_get_input_char (void)
4976{
4977 unsigned char keystroke;
4978
4979 info_gather_typeahead ();
4980
4981 if (pending_input_character)
4982 {
4983 keystroke = pending_input_character;
4984 pending_input_character = 0;
4985 }
4986 else if (info_get_key_from_typeahead (&keystroke) == 0)
4987 {
4988 int rawkey;
4989 unsigned char c;
4990 int tty = fileno (info_input_stream)(!__isthreaded ? ((info_input_stream)->_file) : (fileno)(info_input_stream
))
;
4991
4992 /* Using stream I/O causes FIONREAD etc to fail to work
4993 so unless someone can find a portable way of finding
4994 out how many characters are currently buffered, we
4995 should stay with away from stream I/O.
4996 --Egil Kvaleberg <egilk@sn.no>, January 1997. */
4997#ifdef EINTR4
4998 /* Keep reading if we got EINTR, so that we don't just exit.
4999 --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>,
5000 22 Dec 1997. */
5001 {
5002 int n;
5003 do
5004 n = read (tty, &c, 1);
5005 while (n == -1 && errno(*__errno()) == EINTR4);
5006 rawkey = n == 1 ? c : EOF(-1);
5007 }
5008#else
5009 rawkey = (read (tty, &c, 1) == 1) ? c : EOF(-1);
5010#endif
5011
5012 keystroke = rawkey;
5013
5014 if (rawkey == EOF(-1))
5015 {
5016 if (info_input_stream != stdin(&__sF[0]))
5017 {
5018 fclose (info_input_stream);
5019 info_input_stream = stdin(&__sF[0]);
5020 tty = fileno (info_input_stream)(!__isthreaded ? ((info_input_stream)->_file) : (fileno)(info_input_stream
))
;
5021 display_inhibited = 0;
5022 display_update_display (windows);
5023 display_cursor_at_point (active_window);
5024 rawkey = (read (tty, &c, 1) == 1) ? c : EOF(-1);
5025 keystroke = rawkey;
5026 }
5027
5028 if (rawkey == EOF(-1))
5029 {
5030 terminal_unprep_terminal ();
5031 close_dribble_file ();
5032 xexit (0);
5033 }
5034 }
5035 }
5036
5037 if (info_dribble_file)
5038 dribble (keystroke);
5039
5040 return keystroke;
5041}