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