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