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