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