info.c revision 100513
1/* info.c -- Display nodes of Info files in multiple windows. 2 $Id: info.c,v 1.60 2002/03/11 19:54:29 karl Exp $ 3 4 Copyright (C) 1993, 96, 97, 98, 99, 2000, 01, 02 5 Free Software Foundation, Inc. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 21 Written by Brian Fox (bfox@ai.mit.edu). */ 22 23#include "info.h" 24#include "indices.h" 25#include "dribble.h" 26#include "getopt.h" 27#if defined (HANDLE_MAN_PAGES) 28# include "man.h" 29#endif /* HANDLE_MAN_PAGES */ 30 31static char *program_name = "info"; 32 33/* Non-zero means search all indices for APROPOS_SEARCH_STRING. */ 34static int apropos_p = 0; 35 36/* Variable containing the string to search for when apropos_p is non-zero. */ 37static char *apropos_search_string = (char *)NULL; 38 39/* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike 40 apropos, this puts the user at the node, running info. */ 41static int index_search_p = 0; 42 43/* Non-zero means look for the node which describes the invocation 44 and command-line options of the program, and start the info 45 session at that node. */ 46static int goto_invocation_p = 0; 47 48/* Variable containing the string to search for when index_search_p is 49 non-zero. */ 50static char *index_search_string = (char *)NULL; 51 52/* Non-zero means print version info only. */ 53static int print_version_p = 0; 54 55/* Non-zero means print a short description of the options. */ 56static int print_help_p = 0; 57 58/* Array of the names of nodes that the user specified with "--node" on the 59 command line. */ 60static char **user_nodenames = (char **)NULL; 61static int user_nodenames_index = 0; 62static int user_nodenames_slots = 0; 63 64/* String specifying the first file to load. This string can only be set 65 by the user specifying "--file" on the command line. */ 66static char *user_filename = (char *)NULL; 67 68/* String specifying the name of the file to dump nodes to. This value is 69 filled if the user speficies "--output" on the command line. */ 70static char *user_output_filename = (char *)NULL; 71 72/* Non-zero indicates that when "--output" is specified, all of the menu 73 items of the specified nodes (and their subnodes as well) should be 74 dumped in the order encountered. This basically can print a book. */ 75int dump_subnodes = 0; 76 77/* Non-zero means make default keybindings be loosely modeled on vi(1). */ 78int vi_keys_p = 0; 79 80/* Non-zero means don't remove ANSI escape sequences from man pages. */ 81int raw_escapes_p = 0; 82 83#ifdef __MSDOS__ 84/* Non-zero indicates that screen output should be made 'speech-friendly'. 85 Since on MSDOS the usual behavior is to write directly to the video 86 memory, speech synthesizer software cannot grab the output. Therefore, 87 we provide a user option which tells us to avoid direct screen output 88 and use stdout instead (which loses the color output). */ 89int speech_friendly = 0; 90#endif 91 92/* Structure describing the options that Info accepts. We pass this structure 93 to getopt_long (). If you add or otherwise change this structure, you must 94 also change the string which follows it. */ 95#define APROPOS_OPTION 1 96#define DRIBBLE_OPTION 2 97#define RESTORE_OPTION 3 98#define IDXSRCH_OPTION 4 99static struct option long_options[] = { 100 { "apropos", 1, 0, APROPOS_OPTION }, 101 { "directory", 1, 0, 'd' }, 102 { "dribble", 1, 0, DRIBBLE_OPTION }, 103 { "file", 1, 0, 'f' }, 104 { "help", 0, &print_help_p, 1 }, 105 { "index-search", 1, 0, IDXSRCH_OPTION }, 106 { "node", 1, 0, 'n' }, 107 { "output", 1, 0, 'o' }, 108 { "raw-escapes", 0, &raw_escapes_p, 1 }, 109 { "restore", 1, 0, RESTORE_OPTION }, 110 { "show-options", 0, 0, 'O' }, 111 { "subnodes", 0, &dump_subnodes, 1 }, 112 { "usage", 0, 0, 'O' }, 113 { "version", 0, &print_version_p, 1 }, 114 { "vi-keys", 0, &vi_keys_p, 1 }, 115#ifdef __MSDOS__ 116 { "speech-friendly", 0, &speech_friendly, 1 }, 117#endif 118 {NULL, 0, NULL, 0} 119}; 120 121/* String describing the shorthand versions of the long options found above. */ 122#ifdef __MSDOS__ 123static char *short_options = "d:n:f:o:ORsb"; 124#else 125static char *short_options = "d:n:f:o:ORs"; 126#endif 127 128/* When non-zero, the Info window system has been initialized. */ 129int info_windows_initialized_p = 0; 130 131/* Some "forward" declarations. */ 132static void info_short_help (), remember_info_program_name (); 133static void init_messages (); 134extern void add_file_directory_to_path (); 135 136 137/* **************************************************************** */ 138/* */ 139/* Main Entry Point to the Info Program */ 140/* */ 141/* **************************************************************** */ 142 143int 144main (argc, argv) 145 int argc; 146 char **argv; 147{ 148 int getopt_long_index; /* Index returned by getopt_long (). */ 149 NODE *initial_node; /* First node loaded by Info. */ 150 151#ifdef HAVE_SETLOCALE 152 /* Set locale via LC_ALL. */ 153 setlocale (LC_ALL, ""); 154#endif 155 156 /* Set the text message domain. */ 157 bindtextdomain (PACKAGE, LOCALEDIR); 158 textdomain (PACKAGE); 159 160 init_messages (); 161 162 while (1) 163 { 164 int option_character; 165 166 option_character = getopt_long 167 (argc, argv, short_options, long_options, &getopt_long_index); 168 169 /* getopt_long () returns EOF when there are no more long options. */ 170 if (option_character == EOF) 171 break; 172 173 /* If this is a long option, then get the short version of it. */ 174 if (option_character == 0 && long_options[getopt_long_index].flag == 0) 175 option_character = long_options[getopt_long_index].val; 176 177 /* Case on the option that we have received. */ 178 switch (option_character) 179 { 180 case 0: 181 break; 182 183 /* User wants to add a directory. */ 184 case 'd': 185 info_add_path (optarg, INFOPATH_PREPEND); 186 break; 187 188 /* User is specifying a particular node. */ 189 case 'n': 190 add_pointer_to_array (optarg, user_nodenames_index, user_nodenames, 191 user_nodenames_slots, 10, char *); 192 break; 193 194 /* User is specifying a particular Info file. */ 195 case 'f': 196 if (user_filename) 197 free (user_filename); 198 199 user_filename = xstrdup (optarg); 200 break; 201 202 /* User is specifying the name of a file to output to. */ 203 case 'o': 204 if (user_output_filename) 205 free (user_output_filename); 206 user_output_filename = xstrdup (optarg); 207 break; 208 209 /* User has specified that she wants to find the "Options" 210 or "Invocation" node for the program. */ 211 case 'O': 212 goto_invocation_p = 1; 213 break; 214 215 /* User has specified that she wants the escape sequences 216 in man pages to be passed thru unaltered. */ 217 case 'R': 218 raw_escapes_p = 1; 219 break; 220 221 /* User is specifying that she wishes to dump the subnodes of 222 the node that she is dumping. */ 223 case 's': 224 dump_subnodes = 1; 225 break; 226 227#ifdef __MSDOS__ 228 /* User wants speech-friendly output. */ 229 case 'b': 230 speech_friendly = 1; 231 break; 232#endif /* __MSDOS__ */ 233 234 /* User has specified a string to search all indices for. */ 235 case APROPOS_OPTION: 236 apropos_p = 1; 237 maybe_free (apropos_search_string); 238 apropos_search_string = xstrdup (optarg); 239 break; 240 241 /* User has specified a dribble file to receive keystrokes. */ 242 case DRIBBLE_OPTION: 243 close_dribble_file (); 244 open_dribble_file (optarg); 245 break; 246 247 /* User has specified an alternate input stream. */ 248 case RESTORE_OPTION: 249 info_set_input_from_file (optarg); 250 break; 251 252 /* User has specified a string to search all indices for. */ 253 case IDXSRCH_OPTION: 254 index_search_p = 1; 255 maybe_free (index_search_string); 256 index_search_string = xstrdup (optarg); 257 break; 258 259 default: 260 fprintf (stderr, _("Try --help for more information.\n")); 261 xexit (1); 262 } 263 } 264 265 /* If the output device is not a terminal, and no output filename has been 266 specified, make user_output_filename be "-", so that the info is written 267 to stdout, and turn on the dumping of subnodes. */ 268 if ((!isatty (fileno (stdout))) && (user_output_filename == (char *)NULL)) 269 { 270 user_output_filename = xstrdup ("-"); 271 dump_subnodes = 1; 272 } 273 274 /* If the user specified --version, then show the version and exit. */ 275 if (print_version_p) 276 { 277 printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION); 278 puts (""); 279 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ 280There is NO warranty. You may redistribute this software\n\ 281under the terms of the GNU General Public License.\n\ 282For more information about these matters, see the files named COPYING.\n"), 283 "2002"); 284 xexit (0); 285 } 286 287 /* If the `--help' option was present, show the help and exit. */ 288 if (print_help_p) 289 { 290 info_short_help (); 291 xexit (0); 292 } 293 294 /* If the user hasn't specified a path for Info files, default it. 295 Lowest priority is our messy hardwired list in filesys.h. 296 Then comes the user's INFODIR from the Makefile. 297 Highest priority is the environment variable, if set. */ 298 if (!infopath) 299 { 300 char *path_from_env = getenv ("INFOPATH"); 301 302 if (path_from_env) 303 { 304 unsigned len = strlen (path_from_env); 305 /* Trailing : on INFOPATH means insert the default path. */ 306 if (len && path_from_env[len - 1] == PATH_SEP[0]) 307 { 308 path_from_env[len - 1] = 0; 309 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND); 310 } 311#ifdef INFODIR /* from the Makefile */ 312 info_add_path (INFODIR, INFOPATH_PREPEND); 313#endif 314 info_add_path (path_from_env, INFOPATH_PREPEND); 315 } 316 else 317 { 318 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND); 319#ifdef INFODIR /* from the Makefile */ 320 info_add_path (INFODIR, INFOPATH_PREPEND); 321#endif 322 } 323 } 324 325 /* If the user specified a particular filename, add the path of that 326 file to the contents of INFOPATH. */ 327 if (user_filename) 328 add_file_directory_to_path (user_filename); 329 330 /* If the user wants to search every known index for a given string, 331 do that now, and report the results. */ 332 if (apropos_p) 333 { 334 info_apropos (apropos_search_string); 335 xexit (0); 336 } 337 338 /* Get the initial Info node. It is either "(dir)Top", or what the user 339 specifed with values in user_filename and user_nodenames. */ 340 initial_node = info_get_node (user_filename, 341 user_nodenames ? user_nodenames[0] : 0); 342 343 /* If we couldn't get the initial node, this user is in trouble. */ 344 if (!initial_node) 345 { 346 if (info_recent_file_error) 347 info_error (info_recent_file_error); 348 else 349 info_error (msg_cant_find_node, 350 user_nodenames ? user_nodenames[0] : "Top"); 351 xexit (1); 352 } 353 354 /* Special cases for when the user specifies multiple nodes. If we 355 are dumping to an output file, dump all of the nodes specified. 356 Otherwise, attempt to create enough windows to handle the nodes 357 that this user wants displayed. */ 358 if (user_nodenames_index > 1) 359 { 360 free (initial_node); 361 362 if (user_output_filename) 363 dump_nodes_to_file 364 (user_filename, user_nodenames, user_output_filename, dump_subnodes); 365 else 366 begin_multiple_window_info_session (user_filename, user_nodenames); 367 368 xexit (0); 369 } 370 371 /* If there are arguments remaining, they are the names of menu items 372 in sequential info files starting from the first one loaded. That 373 file name is either "dir", or the contents of user_filename if one 374 was specified. */ 375 { 376 char *errstr, *errarg1, *errarg2; 377 NODE *new_initial_node = info_follow_menus (initial_node, argv + optind, 378 &errstr, &errarg1, &errarg2); 379 380 if (new_initial_node && new_initial_node != initial_node) 381 initial_node = new_initial_node; 382 383 /* If the user specified that this node should be output, then do that 384 now. Otherwise, start the Info session with this node. Or act 385 accordingly if the initial node was not found. */ 386 if (user_output_filename && !goto_invocation_p) 387 { 388 if (!errstr) 389 dump_node_to_file (initial_node, user_output_filename, 390 dump_subnodes); 391 else 392 info_error (errstr, errarg1, errarg2); 393 } 394 else 395 { 396 397 if (errstr) 398 begin_info_session_with_error (initial_node, errstr, 399 errarg1, errarg2); 400 /* If the user specified `--index-search=STRING' or 401 --show-options, start the info session in the node 402 corresponding to what they want. */ 403 else if (index_search_p || goto_invocation_p) 404 { 405 int status = 0; 406 407 initialize_info_session (initial_node, 0); 408 409 if (goto_invocation_p 410 || index_entry_exists (windows, index_search_string)) 411 { 412 terminal_prep_terminal (); 413 terminal_clear_screen (); 414 info_last_executed_command = (VFunction *)NULL; 415 416 if (index_search_p) 417 do_info_index_search (windows, 0, index_search_string); 418 else 419 { 420 /* If they said "info --show-options foo bar baz", 421 the last of the arguments is the program whose 422 options they want to see. */ 423 char **p = argv + optind; 424 char *program; 425 426 if (*p) 427 { 428 while (p[1]) 429 p++; 430 program = xstrdup (*p); 431 } 432 else if (user_filename) 433 /* If there's no command-line arguments to 434 supply the program name, use the Info file 435 name (sans extension and leading directories) 436 instead. */ 437 program = program_name_from_file_name (user_filename); 438 else 439 program = xstrdup (""); 440 441 info_intuit_options_node (windows, initial_node, program); 442 free (program); 443 } 444 445 if (user_output_filename) 446 { 447 dump_node_to_file (windows->node, user_output_filename, 448 dump_subnodes); 449 } 450 else 451 info_read_and_dispatch (); 452 453 /* On program exit, leave the cursor at the bottom of the 454 window, and restore the terminal IO. */ 455 terminal_goto_xy (0, screenheight - 1); 456 terminal_clear_to_eol (); 457 fflush (stdout); 458 terminal_unprep_terminal (); 459 } 460 else 461 { 462 fprintf (stderr, _("no index entries found for `%s'\n"), 463 index_search_string); 464 status = 2; 465 } 466 467 close_dribble_file (); 468 xexit (status); 469 } 470 else 471 begin_info_session (initial_node); 472 } 473 474 xexit (0); 475 } 476} 477 478void 479add_file_directory_to_path (filename) 480 char *filename; 481{ 482 char *directory_name = xstrdup (filename); 483 char *temp = filename_non_directory (directory_name); 484 485 if (temp != directory_name) 486 { 487 if (HAVE_DRIVE (directory_name) && temp == directory_name + 2) 488 { 489 /* The directory of "d:foo" is stored as "d:.", to avoid 490 mixing it with "d:/" when a slash is appended. */ 491 *temp = '.'; 492 temp += 2; 493 } 494 temp[-1] = 0; 495 info_add_path (directory_name, INFOPATH_PREPEND); 496 } 497 498 free (directory_name); 499} 500 501 502/* Error handling. */ 503 504/* Non-zero if an error has been signalled. */ 505int info_error_was_printed = 0; 506 507/* Non-zero means ring terminal bell on errors. */ 508int info_error_rings_bell_p = 1; 509 510/* Print FORMAT with ARG1 and ARG2. If the window system was initialized, 511 then the message is printed in the echo area. Otherwise, a message is 512 output to stderr. */ 513void 514info_error (format, arg1, arg2) 515 char *format; 516 void *arg1, *arg2; 517{ 518 info_error_was_printed = 1; 519 520 if (!info_windows_initialized_p || display_inhibited) 521 { 522 fprintf (stderr, "%s: ", program_name); 523 fprintf (stderr, format, arg1, arg2); 524 fprintf (stderr, "\n"); 525 fflush (stderr); 526 } 527 else 528 { 529 if (!echo_area_is_active) 530 { 531 if (info_error_rings_bell_p) 532 terminal_ring_bell (); 533 window_message_in_echo_area (format, arg1, arg2); 534 } 535 else 536 { 537 NODE *temp; 538 539 temp = build_message_node (format, arg1, arg2); 540 if (info_error_rings_bell_p) 541 terminal_ring_bell (); 542 inform_in_echo_area (temp->contents); 543 free (temp->contents); 544 free (temp); 545 } 546 } 547} 548 549 550/* Produce a scaled down description of the available options to Info. */ 551static void 552info_short_help () 553{ 554#ifdef __MSDOS__ 555 static const char speech_friendly_string[] = N_("\ 556 -b, --speech-friendly be friendly to speech synthesizers.\n"); 557#else 558 static const char speech_friendly_string[] = ""; 559#endif 560 561 562 printf (_("\ 563Usage: %s [OPTION]... [MENU-ITEM...]\n\ 564\n\ 565Read documentation in Info format.\n\ 566\n\ 567Options:\n\ 568 --apropos=STRING look up STRING in all indices of all manuals.\n\ 569 -d, --directory=DIR add DIR to INFOPATH.\n\ 570 --dribble=FILENAME remember user keystrokes in FILENAME.\n\ 571 -f, --file=FILENAME specify Info file to visit.\n\ 572 -h, --help display this help and exit.\n\ 573 --index-search=STRING go to node pointed by index entry STRING.\n\ 574 -n, --node=NODENAME specify nodes in first visited Info file.\n\ 575 -o, --output=FILENAME output selected nodes to FILENAME.\n\ 576 -R, --raw-escapes don't remove ANSI escapes from man pages.\n\ 577 --restore=FILENAME read initial keystrokes from FILENAME.\n\ 578 -O, --show-options, --usage go to command-line options node.\n%s\ 579 --subnodes recursively output menu items.\n\ 580 --vi-keys use vi-like and less-like key bindings.\n\ 581 --version display version information and exit.\n\ 582\n\ 583The first non-option argument, if present, is the menu entry to start from;\n\ 584it is searched for in all `dir' files along INFOPATH.\n\ 585If it is not present, info merges all `dir' files and shows the result.\n\ 586Any remaining arguments are treated as the names of menu\n\ 587items relative to the initial node visited.\n\ 588\n\ 589Examples:\n\ 590 info show top-level dir menu\n\ 591 info emacs start at emacs node from top-level dir\n\ 592 info emacs buffers start at buffers node within emacs manual\n\ 593 info --show-options emacs start at node with emacs' command line options\n\ 594 info -f ./foo.info show file ./foo.info, not searching dir\n\ 595"), 596 program_name, speech_friendly_string); 597 598 puts (_("\n\ 599Email bug reports to bug-texinfo@gnu.org,\n\ 600general questions and discussion to help-texinfo@gnu.org.\n\ 601Texinfo home page: http://www.gnu.org/software/texinfo/")); 602 603 xexit (0); 604} 605 606 607/* Initialize strings for gettext. Because gettext doesn't handle N_ or 608 _ within macro definitions, we put shared messages into variables and 609 use them that way. This also has the advantage that there's only one 610 copy of the strings. */ 611 612char *msg_cant_find_node; 613char *msg_cant_file_node; 614char *msg_cant_find_window; 615char *msg_cant_find_point; 616char *msg_cant_kill_last; 617char *msg_no_menu_node; 618char *msg_no_foot_node; 619char *msg_no_xref_node; 620char *msg_no_pointer; 621char *msg_unknown_command; 622char *msg_term_too_dumb; 623char *msg_at_node_bottom; 624char *msg_at_node_top; 625char *msg_one_window; 626char *msg_win_too_small; 627char *msg_cant_make_help; 628 629static void 630init_messages () 631{ 632 msg_cant_find_node = _("Cannot find node `%s'."); 633 msg_cant_file_node = _("Cannot find node `(%s)%s'."); 634 msg_cant_find_window = _("Cannot find a window!"); 635 msg_cant_find_point = _("Point doesn't appear within this window's node!"); 636 msg_cant_kill_last = _("Cannot delete the last window."); 637 msg_no_menu_node = _("No menu in this node."); 638 msg_no_foot_node = _("No footnotes in this node."); 639 msg_no_xref_node = _("No cross references in this node."); 640 msg_no_pointer = _("No `%s' pointer for this node."); 641 msg_unknown_command = _("Unknown Info command `%c'; try `?' for help."); 642 msg_term_too_dumb = _("Terminal type `%s' is not smart enough to run Info."); 643 msg_at_node_bottom = _("You are already at the last page of this node."); 644 msg_at_node_top = _("You are already at the first page of this node."); 645 msg_one_window = _("Only one window."); 646 msg_win_too_small = _("Resulting window would be too small."); 647 msg_cant_make_help = _("Not enough room for a help window, please delete a window."); 648} 649