File: | src/gnu/usr.bin/texinfo/info/session.c |
Warning: | line 2673, column 11 Branch condition evaluates to a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
36 | static void info_clear_pending_input (void); | |||
37 | static void info_set_pending_input (unsigned char key); | |||
38 | static void info_handle_pointer (char *label, WINDOW *window); | |||
39 | static void display_info_keyseq (int expecting_future_input); | |||
40 | char *node_printed_rep (NODE *node); | |||
41 | ||||
42 | /* **************************************************************** */ | |||
43 | /* */ | |||
44 | /* Running an Info Session */ | |||
45 | /* */ | |||
46 | /* **************************************************************** */ | |||
47 | ||||
48 | /* The place that we are reading input from. */ | |||
49 | static FILE *info_input_stream = NULL((void *)0); | |||
50 | ||||
51 | /* The last executed command. */ | |||
52 | VFunction *info_last_executed_command = NULL((void *)0); | |||
53 | ||||
54 | /* Becomes non-zero when 'q' is typed to an Info window. */ | |||
55 | int quit_info_immediately = 0; | |||
56 | ||||
57 | /* Array of structures describing for each window which nodes have been | |||
58 | visited in that window. */ | |||
59 | INFO_WINDOW **info_windows = NULL((void *)0); | |||
60 | ||||
61 | /* Where to add the next window, if we need to add one. */ | |||
62 | static int info_windows_index = 0; | |||
63 | ||||
64 | /* Number of slots allocated to `info_windows'. */ | |||
65 | static int info_windows_slots = 0; | |||
66 | ||||
67 | void remember_window_and_node (WINDOW *window, NODE *node); | |||
68 | void forget_window_and_nodes (WINDOW *window); | |||
69 | void display_startup_message_and_start (void); | |||
70 | ||||
71 | /* Begin an info session finding the nodes specified by FILENAME and NODENAMES. | |||
72 | For each loaded node, create a new window. Always split the largest of the | |||
73 | available windows. */ | |||
74 | void | |||
75 | begin_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. */ | |||
139 | void | |||
140 | begin_info_session_with_error (NODE *initial_node, char *format, | |||
141 | void *arg1, void *arg2) | |||
142 | { | |||
143 | initialize_info_session (initial_node, 1); | |||
144 | info_error (format, arg1, arg2); | |||
145 | info_session (); | |||
146 | } | |||
147 | ||||
148 | /* Start an info session with INITIAL_NODE. */ | |||
149 | void | |||
150 | begin_info_session (NODE *initial_node) | |||
151 | { | |||
152 | initialize_info_session (initial_node, 1); | |||
153 | display_startup_message_and_start (); | |||
154 | } | |||
155 | ||||
156 | void | |||
157 | display_startup_message_and_start (void) | |||
158 | { | |||
159 | char *format; | |||
160 | ||||
161 | format = replace_in_documentation | |||
162 | ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item.")((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. */ | |||
170 | void | |||
171 | info_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 (). */ | |||
187 | void | |||
188 | info_read_and_dispatch (void) | |||
189 | { | |||
190 | unsigned char key; | |||
191 | int done; | |||
192 | done = 0; | |||
193 | ||||
194 | while (!done && !quit_info_immediately) | |||
195 | { | |||
196 | int lk = 0; | |||
197 | ||||
198 | /* If we haven't just gone up or down a line, there is no | |||
199 | goal column for this window. */ | |||
200 | if ((info_last_executed_command != (VFunction *) info_next_line) && | |||
201 | (info_last_executed_command != (VFunction *) info_prev_line)) | |||
202 | active_window->goal_column = -1; | |||
203 | ||||
204 | if (echo_area_is_active) | |||
205 | { | |||
206 | lk = echo_area_last_command_was_kill; | |||
207 | echo_area_prep_read (); | |||
208 | } | |||
209 | ||||
210 | if (!info_any_buffered_input_p ()) | |||
211 | display_update_display (windows); | |||
212 | ||||
213 | display_cursor_at_point (active_window); | |||
214 | info_initialize_numeric_arg (); | |||
215 | ||||
216 | initialize_keyseq (); | |||
217 | key = info_get_input_char (); | |||
218 | ||||
219 | /* No errors yet. We just read a character, that's all. Only clear | |||
220 | the echo_area if it is not currently active. */ | |||
221 | if (!echo_area_is_active) | |||
222 | window_clear_echo_area (); | |||
223 | ||||
224 | info_error_was_printed = 0; | |||
225 | ||||
226 | /* Do the selected command. */ | |||
227 | info_dispatch_on_key (key, active_window->keymap); | |||
228 | ||||
229 | if (echo_area_is_active) | |||
230 | { | |||
231 | /* Echo area commands that do killing increment the value of | |||
232 | ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no | |||
233 | change in the value of this variable, the last command | |||
234 | executed was not a kill command. */ | |||
235 | if (lk == echo_area_last_command_was_kill) | |||
236 | echo_area_last_command_was_kill = 0; | |||
237 | ||||
238 | if (ea_last_executed_command == (VFunction *) ea_newline || | |||
239 | info_aborted_echo_area) | |||
240 | { | |||
241 | ea_last_executed_command = (VFunction *)NULL((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 */ | |||
254 | extern void initialize_info_signal_handler (void ); | |||
255 | ||||
256 | /* Initialize the first info session by starting the terminal, window, | |||
257 | and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ | |||
258 | void | |||
259 | initialize_info_session (NODE *node, int clear_screen) | |||
260 | { | |||
261 | char *term_name = getenv ("TERM"); | |||
262 | terminal_initialize_terminal (term_name); | |||
263 | ||||
264 | if (terminal_is_dumb_p) | |||
265 | { | |||
266 | if (!term_name) | |||
267 | term_name = "dumb"; | |||
268 | ||||
269 | info_error ((char *) msg_term_too_dumb, term_name, NULL((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. */ | |||
301 | void | |||
302 | info_set_input_from_file (char *filename) | |||
303 | { | |||
304 | FILE *stream; | |||
305 | ||||
306 | /* Input may include binary characters. */ | |||
307 | stream = fopen (filename, FOPEN_RBIN"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. */ | |||
323 | static INFO_WINDOW * | |||
324 | get_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. */ | |||
339 | void | |||
340 | set_remembered_pagetop_and_point (WINDOW *window) | |||
341 | { | |||
342 | INFO_WINDOW *info_win; | |||
343 | ||||
344 | info_win = get_info_window_of_window (window); | |||
345 | ||||
346 | if (!info_win) | |||
347 | return; | |||
348 | ||||
349 | if (info_win->nodes_index && | |||
350 | (info_win->nodes[info_win->current] == window->node)) | |||
351 | { | |||
352 | info_win->pagetops[info_win->current] = window->pagetop; | |||
353 | info_win->points[info_win->current] = window->point; | |||
354 | } | |||
355 | } | |||
356 | ||||
357 | void | |||
358 | remember_window_and_node (WINDOW *window, NODE *node) | |||
359 | { | |||
360 | /* See if we already have this window in our list. */ | |||
361 | INFO_WINDOW *info_win = get_info_window_of_window (window); | |||
362 | ||||
363 | /* If the window wasn't already on our list, then make a new entry. */ | |||
364 | if (!info_win) | |||
365 | { | |||
366 | info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW)); | |||
367 | info_win->window = window; | |||
368 | info_win->nodes = (NODE **)NULL((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) | |||
416 | static void | |||
417 | consistency_check_info_windows (void) | |||
418 | { | |||
419 | register int i; | |||
420 | ||||
421 | for (i = 0; i < info_windows_index; i++) | |||
422 | { | |||
423 | WINDOW *win; | |||
424 | ||||
425 | for (win = windows; win; win = win->next) | |||
426 | if (win == info_windows[i]->window) | |||
427 | break; | |||
428 | ||||
429 | if (!win) | |||
430 | abort (); | |||
431 | } | |||
432 | } | |||
433 | #endif /* DEBUG_FORGET_WINDOW_AND_NODES */ | |||
434 | ||||
435 | /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */ | |||
436 | void | |||
437 | forget_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. */ | |||
483 | void | |||
484 | info_set_node_of_window (int remember, WINDOW *window, NODE *node) | |||
485 | { | |||
486 | if (remember) | |||
487 | set_remembered_pagetop_and_point (window); | |||
488 | ||||
489 | /* Put this node into the window. */ | |||
490 | window_set_node_of_window (window, node); | |||
491 | ||||
492 | /* Remember this node and window in our list of info windows. */ | |||
493 | remember_window_and_node (window, node); | |||
494 | ||||
495 | /* If doing auto-footnote display/undisplay, show the footnotes belonging | |||
496 | to this window's node. */ | |||
497 | if (auto_footnotes_p) | |||
498 | info_get_or_remove_footnotes (window); | |||
499 | } | |||
500 | ||||
501 | ||||
502 | /* **************************************************************** */ | |||
503 | /* */ | |||
504 | /* Info Movement Commands */ | |||
505 | /* */ | |||
506 | /* **************************************************************** */ | |||
507 | ||||
508 | /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen | |||
509 | to do so. */ | |||
510 | void | |||
511 | set_window_pagetop (WINDOW *window, int desired_top) | |||
512 | { | |||
513 | int point_line, old_pagetop; | |||
514 | ||||
515 | if (desired_top < 0) | |||
516 | desired_top = 0; | |||
517 | else if (desired_top > window->line_count) | |||
518 | desired_top = window->line_count - 1; | |||
519 | ||||
520 | if (window->pagetop == desired_top) | |||
521 | return; | |||
522 | ||||
523 | old_pagetop = window->pagetop; | |||
524 | window->pagetop = desired_top; | |||
525 | ||||
526 | /* Make sure that point appears in this window. */ | |||
527 | point_line = window_line_of_point (window); | |||
528 | if ((point_line < window->pagetop) || | |||
529 | ((point_line - window->pagetop) > window->height - 1)) | |||
530 | window->point = | |||
531 | window->line_starts[window->pagetop] - window->node->contents; | |||
532 | ||||
533 | window->flags |= W_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. */ | |||
573 | static void | |||
574 | info_show_point (WINDOW *window) | |||
575 | { | |||
576 | int old_pagetop; | |||
577 | ||||
578 | old_pagetop = window->pagetop; | |||
579 | window_adjust_pagetop (window); | |||
580 | if (old_pagetop != window->pagetop) | |||
581 | { | |||
582 | int new_pagetop; | |||
583 | ||||
584 | new_pagetop = window->pagetop; | |||
585 | window->pagetop = old_pagetop; | |||
586 | set_window_pagetop (window, new_pagetop); | |||
587 | } | |||
588 | ||||
589 | if (window->flags & W_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. */ | |||
596 | static void | |||
597 | move_to_new_line (int old, int new, WINDOW *window) | |||
598 | { | |||
599 | if (old == -1) | |||
600 | { | |||
601 | info_error ((char *) msg_cant_find_point, NULL((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. */ | |||
620 | DECLARE_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. */ | |||
635 | DECLARE_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. */ | |||
650 | DECLARE_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. */ | |||
670 | DECLARE_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. */ | |||
689 | DECLARE_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. */ | |||
705 | DECLARE_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. */ | |||
723 | DECLARE_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 | ||||
772 | DECLARE_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 | ||||
831 | int info_scroll_behaviour = IS_Continuous0; | |||
832 | ||||
833 | /* Choices used by the completer when reading a value for the user-visible | |||
834 | variable "scroll-behaviour". */ | |||
835 | char *info_scroll_choices[] = { | |||
836 | "Continuous", "Next Only", "Page Only", (char *)NULL((void *)0) | |||
837 | }; | |||
838 | ||||
839 | /* Default window sizes for scrolling commands. */ | |||
840 | int default_window_size = -1; /* meaning 1 window-full */ | |||
841 | int default_scroll_size = -1; /* meaning half screen size */ | |||
842 | ||||
843 | #define INFO_LABEL_FOUND()(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. */ | |||
848 | static void | |||
849 | forward_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. */ | |||
999 | static void | |||
1000 | backward_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. */ | |||
1094 | DECLARE_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. */ | |||
1110 | DECLARE_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 | ||||
1125 | static void _scroll_forward(WINDOW *window, int count, | |||
1126 | unsigned char key, int behaviour); | |||
1127 | static void _scroll_backward(WINDOW *window, int count, | |||
1128 | unsigned char key, int behaviour); | |||
1129 | ||||
1130 | static void | |||
1131 | _scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour) | |||
1132 | { | |||
1133 | if (count < 0) | |||
1134 | _scroll_backward (window, -count, key, behaviour); | |||
1135 | else | |||
1136 | { | |||
1137 | int desired_top; | |||
1138 | ||||
1139 | /* Without an explicit numeric argument, scroll the bottom two | |||
1140 | lines to the top of this window, Or, if at bottom of window, | |||
1141 | and the chosen behaviour is to scroll through nodes get the | |||
1142 | "Next" node for this window. */ | |||
1143 | if (default_window_size > 0) | |||
1144 | desired_top = window->pagetop + default_window_size; | |||
1145 | else if (!info_explicit_arg && count == 1) | |||
1146 | { | |||
1147 | desired_top = window->pagetop + (window->height - 2); | |||
1148 | ||||
1149 | /* If there are no more lines to scroll here, error, or get | |||
1150 | another node, depending on BEHAVIOUR. */ | |||
1151 | if (desired_top > window->line_count) | |||
1152 | { | |||
1153 | forward_move_node_structure (window, behaviour); | |||
1154 | return; | |||
1155 | } | |||
1156 | } | |||
1157 | else | |||
1158 | desired_top = window->pagetop + count; | |||
1159 | ||||
1160 | if (desired_top >= window->line_count) | |||
1161 | desired_top = window->line_count - 2; | |||
1162 | ||||
1163 | if (window->pagetop > desired_top) | |||
1164 | return; | |||
1165 | else | |||
1166 | set_window_pagetop (window, desired_top); | |||
1167 | } | |||
1168 | } | |||
1169 | ||||
1170 | static void | |||
1171 | _scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour) | |||
1172 | { | |||
1173 | if (count < 0) | |||
1174 | _scroll_forward (window, -count, key, behaviour); | |||
1175 | else | |||
1176 | { | |||
1177 | int desired_top; | |||
1178 | ||||
1179 | /* Without an explicit numeric argument, scroll the top two lines | |||
1180 | to the bottom of this window, or, depending on the selected | |||
1181 | behaviour, move to the previous, or Up'th node. */ | |||
1182 | if (default_window_size > 0) | |||
1183 | desired_top = window->pagetop - default_window_size; | |||
1184 | else if (!info_explicit_arg && count == 1) | |||
1185 | { | |||
1186 | desired_top = window->pagetop - (window->height - 2); | |||
1187 | ||||
1188 | if ((desired_top < 0) && (window->pagetop == 0)) | |||
1189 | { | |||
1190 | backward_move_node_structure (window, behaviour); | |||
1191 | return; | |||
1192 | } | |||
1193 | } | |||
1194 | else | |||
1195 | desired_top = window->pagetop - count; | |||
1196 | ||||
1197 | if (desired_top < 0) | |||
1198 | desired_top = 0; | |||
1199 | ||||
1200 | set_window_pagetop (window, desired_top); | |||
1201 | } | |||
1202 | } | |||
1203 | ||||
1204 | /* Show the next screen of WINDOW's node. */ | |||
1205 | DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window"))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. */ | |||
1212 | DECLARE_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. */ | |||
1221 | DECLARE_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. */ | |||
1228 | DECLARE_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. */ | |||
1237 | DECLARE_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. */ | |||
1244 | DECLARE_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. */ | |||
1254 | DECLARE_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. */ | |||
1261 | DECLARE_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. */ | |||
1270 | DECLARE_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. */ | |||
1277 | DECLARE_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. */ | |||
1284 | DECLARE_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. */ | |||
1301 | DECLARE_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. */ | |||
1318 | DECLARE_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. */ | |||
1344 | DECLARE_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. */ | |||
1374 | DECLARE_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. */ | |||
1413 | DECLARE_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. */ | |||
1464 | DECLARE_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. */ | |||
1545 | DECLARE_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. */ | |||
1569 | void | |||
1570 | info_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. */ | |||
1585 | DECLARE_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. */ | |||
1631 | DECLARE_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. */ | |||
1651 | DECLARE_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. */ | |||
1658 | DECLARE_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. */ | |||
1665 | int auto_tiling_p = 0; | |||
1666 | ||||
1667 | /* Tile all of the visible windows. */ | |||
1668 | DECLARE_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. */ | |||
1675 | DECLARE_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. */ | |||
1689 | char * | |||
1690 | node_printed_rep (NODE *node) | |||
1691 | { | |||
1692 | char *rep; | |||
1693 | ||||
1694 | if (node->filename) | |||
1695 | { | |||
1696 | char *filename | |||
1697 | = filename_non_directory (node->parent ? node->parent : node->filename); | |||
1698 | rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1); | |||
1699 | sprintf (rep, "(%s)%s", filename, node->nodename); | |||
1700 | } | |||
1701 | else | |||
1702 | rep = node->nodename; | |||
1703 | ||||
1704 | return rep; | |||
1705 | } | |||
1706 | ||||
1707 | ||||
1708 | /* Using WINDOW for various defaults, select the node referenced by ENTRY | |||
1709 | in it. If the node is selected, the window and node are remembered. */ | |||
1710 | void | |||
1711 | info_select_reference (WINDOW *window, REFERENCE *entry) | |||
1712 | { | |||
1713 | NODE *node; | |||
1714 | char *filename, *nodename, *file_system_error; | |||
1715 | ||||
1716 | file_system_error = (char *)NULL((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. */ | |||
1772 | static void | |||
1773 | info_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. */ | |||
1790 | static void | |||
1791 | info_handle_pointer (char *label, WINDOW *window) | |||
1792 | { | |||
1793 | if (info_parsed_filename || info_parsed_nodename) | |||
1794 | { | |||
1795 | char *filename, *nodename; | |||
1796 | NODE *node; | |||
1797 | ||||
1798 | filename = nodename = (char *)NULL((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. */ | |||
1848 | DECLARE_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. */ | |||
1856 | DECLARE_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. */ | |||
1864 | DECLARE_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. */ | |||
1871 | DECLARE_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. */ | |||
1904 | DECLARE_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. */ | |||
1937 | DECLARE_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. */ | |||
1944 | DECLARE_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 | ||||
2001 | static REFERENCE ** | |||
2002 | nearest_xref (REFERENCE **xref_list, long int pos) | |||
2003 | { | |||
2004 | int this_xref; | |||
2005 | int nearest = -1; | |||
2006 | long best_delta = -1; | |||
2007 | ||||
2008 | for (this_xref = 0; xref_list[this_xref]; this_xref++) | |||
2009 | { | |||
2010 | long delta; | |||
2011 | REFERENCE *xref = xref_list[this_xref]; | |||
2012 | if (xref->start <= pos && pos <= xref->end) | |||
2013 | { /* POS is within this xref, we're done */ | |||
2014 | nearest = this_xref; | |||
2015 | break; | |||
2016 | } | |||
2017 | ||||
2018 | /* See how far POS is from this xref. Take into account the | |||
2019 | `*Note' that begins the xref, since as far as the user is | |||
2020 | concerned, that's where it starts. */ | |||
2021 | delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))),((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. */ | |||
2054 | static void | |||
2055 | info_menu_or_ref_item (WINDOW *window, int count, | |||
2056 | unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p) | |||
2057 | { | |||
2058 | char *line; | |||
2059 | REFERENCE *entry; | |||
2060 | REFERENCE *defentry = NULL((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. */ | |||
2317 | DECLARE_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. */ | |||
2324 | DECLARE_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. */ | |||
2331 | DECLARE_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. */ | |||
2354 | DECLARE_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. */ | |||
2383 | DECLARE_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 | ||||
2470 | NODE * | |||
2471 | info_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 | ||||
2605 | static char ** | |||
2606 | split_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. */ | |||
2632 | DECLARE_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) | |||
| ||||
2639 | { | |||
2640 | info_abort_key (window, 0, 0); | |||
2641 | return; | |||
2642 | } | |||
2643 | ||||
2644 | canonicalize_whitespace (line); | |||
2645 | ||||
2646 | if (*line) | |||
2647 | { | |||
2648 | const char *errstr; | |||
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) | |||
2657 | { | |||
2658 | char *file_name = window->node->parent; | |||
2659 | ||||
2660 | if (!file_name) | |||
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) | |||
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) | |||
| ||||
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. */ | |||
2688 | static REFERENCE * | |||
2689 | entry_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. */ | |||
2722 | void | |||
2723 | info_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. */ | |||
2806 | char * | |||
2807 | program_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 | ||||
2827 | DECLARE_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) | |||
2868 | DECLARE_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. */ | |||
2902 | DECLARE_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". */ | |||
2908 | DECLARE_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. */ | |||
2916 | static char * | |||
2917 | read_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. */ | |||
2956 | static void | |||
2957 | kill_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. */ | |||
3074 | DECLARE_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. */ | |||
3081 | DECLARE_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. */ | |||
3089 | DECLARE_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 | |||
3129 | static void write_node_to_stream (NODE *node, FILE *stream); | |||
3130 | static void dump_node_to_stream (char *filename, char *nodename, | |||
3131 | FILE *stream, int dump_subnodes); | |||
3132 | static 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. */ | |||
3137 | void | |||
3138 | dump_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. */ | |||
3172 | static char **dumped_already = (char **)NULL((void *)0); | |||
3173 | static int dumped_already_index = 0; | |||
3174 | static int dumped_already_slots = 0; | |||
3175 | ||||
3176 | static void | |||
3177 | initialize_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. */ | |||
3185 | static void | |||
3186 | dump_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. */ | |||
3256 | void | |||
3257 | dump_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 | ||||
3297 | DECLARE_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. */ | |||
3304 | void | |||
3305 | print_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 | ||||
3353 | static void | |||
3354 | write_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. */ | |||
3369 | int gc_compressed_files = 0; | |||
3370 | ||||
3371 | static void info_gc_file_buffers (void); | |||
3372 | static void info_search_1 (WINDOW *window, int count, | |||
3373 | unsigned char key, int case_sensitive, int ask_for_string); | |||
3374 | ||||
3375 | static char *search_string = (char *)NULL((void *)0); | |||
3376 | static int search_string_size = 0; | |||
3377 | static int isearch_is_active = 0; | |||
3378 | ||||
3379 | static int last_search_direction = 0; | |||
3380 | static int last_search_case_sensitive = 0; | |||
3381 | ||||
3382 | /* Return the file buffer which belongs to WINDOW's node. */ | |||
3383 | FILE_BUFFER * | |||
3384 | file_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. */ | |||
3405 | long | |||
3406 | info_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. */ | |||
3448 | long | |||
3449 | info_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. */ | |||
3480 | static int | |||
3481 | info_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 | ||||
3620 | DECLARE_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 | ||||
3628 | DECLARE_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 | ||||
3635 | DECLARE_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 | ||||
3643 | static void | |||
3644 | info_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 | ||||
3737 | DECLARE_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 | ||||
3747 | DECLARE_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 | ||||
3763 | static void incremental_search (WINDOW *window, int count, | |||
3764 | unsigned char ignore); | |||
3765 | ||||
3766 | DECLARE_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 | ||||
3772 | DECLARE_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. */ | |||
3780 | static char *last_isearch_accepted = (char *)NULL((void *)0); | |||
3781 | ||||
3782 | /* The current incremental search string. */ | |||
3783 | static char *isearch_string = (char *)NULL((void *)0); | |||
3784 | static int isearch_string_index = 0; | |||
3785 | static int isearch_string_size = 0; | |||
3786 | static unsigned char isearch_terminate_search_key = ESC'\033'; | |||
3787 | ||||
3788 | /* Array of search states. */ | |||
3789 | static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL((void *)0); | |||
3790 | static int isearch_states_index = 0; | |||
3791 | static int isearch_states_slots = 0; | |||
3792 | ||||
3793 | /* Push the state of this search. */ | |||
3794 | static void | |||
3795 | push_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. */ | |||
3810 | static void | |||
3811 | pop_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. */ | |||
3830 | static void | |||
3831 | free_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. */ | |||
3844 | static void | |||
3845 | show_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 | ||||
3891 | static void | |||
3892 | incremental_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. */ | |||
4173 | static void | |||
4174 | info_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. */ | |||
4232 | static void | |||
4233 | info_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 | ||||
4341 | DECLARE_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 | ||||
4350 | DECLARE_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. */ | |||
4360 | DECLARE_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. */ | |||
4384 | DECLARE_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. */ | |||
4399 | DECLARE_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. */ | |||
4432 | DECLARE_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 (). */ | |||
4469 | DECLARE_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. */ | |||
4481 | DECLARE_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 | ||||
4485 | static void | |||
4486 | dispatch_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. */ | |||
4505 | static char *info_keyseq = (char *)NULL((void *)0); | |||
4506 | static int info_keyseq_index = 0; | |||
4507 | static int info_keyseq_size = 0; | |||
4508 | static int info_keyseq_displayed_p = 0; | |||
4509 | ||||
4510 | /* Initialize the length of the current key sequence. */ | |||
4511 | void | |||
4512 | initialize_keyseq (void) | |||
4513 | { | |||
4514 | info_keyseq_index = 0; | |||
4515 | info_keyseq_displayed_p = 0; | |||
4516 | } | |||
4517 | ||||
4518 | /* Add CHARACTER to the current key sequence. */ | |||
4519 | void | |||
4520 | add_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. */ | |||
4532 | static void | |||
4533 | display_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. */ | |||
4552 | unsigned char | |||
4553 | info_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. */ | |||
4589 | void | |||
4590 | info_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. */ | |||
4701 | int info_explicit_arg = 0; | |||
4702 | ||||
4703 | /* The sign of the numeric argument. */ | |||
4704 | int info_numeric_arg_sign = 1; | |||
4705 | ||||
4706 | /* The value of the argument itself. */ | |||
4707 | int info_numeric_arg = 1; | |||
4708 | ||||
4709 | /* Add the current digit to the argument in progress. */ | |||
4710 | DECLARE_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. */ | |||
4719 | DECLARE_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. */ | |||
4727 | void | |||
4728 | info_initialize_numeric_arg (void) | |||
4729 | { | |||
4730 | info_numeric_arg = info_numeric_arg_sign = 1; | |||
4731 | info_explicit_arg = 0; | |||
4732 | } | |||
4733 | ||||
4734 | DECLARE_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. */ | |||
4817 | static int pending_input_character = 0; | |||
4818 | ||||
4819 | /* How to make there be no pending input. */ | |||
4820 | static void | |||
4821 | info_clear_pending_input (void) | |||
4822 | { | |||
4823 | pending_input_character = 0; | |||
4824 | } | |||
4825 | ||||
4826 | /* How to set the pending input character. */ | |||
4827 | static void | |||
4828 | info_set_pending_input (unsigned char key) | |||
4829 | { | |||
4830 | pending_input_character = key; | |||
4831 | } | |||
4832 | ||||
4833 | /* How to see if there is any pending input. */ | |||
4834 | unsigned char | |||
4835 | info_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 | ||||
4843 | static int pop_index = 0, push_index = 0; | |||
4844 | static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING512]; | |||
4845 | ||||
4846 | /* Add KEY to the buffer of characters to be read. */ | |||
4847 | static void | |||
4848 | info_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. */ | |||
4865 | static int | |||
4866 | info_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. */ | |||
4877 | static int | |||
4878 | info_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 | ||||
4891 | int | |||
4892 | info_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. */ | |||
4900 | void | |||
4901 | info_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. */ | |||
4974 | unsigned char | |||
4975 | info_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 | } |