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