1/* info.c -- Display nodes of Info files in multiple windows. 2 $Id: info.c,v 1.1 2004/10/28 18:14:09 zooey Exp $ 3 4 Copyright (C) 1993, 96, 97, 98 Free Software Foundation, Inc. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 20 Written by Brian Fox (bfox@ai.mit.edu). */ 21 22#include "info.h" 23#include "indices.h" 24#include "dribble.h" 25#include "getopt.h" 26#if defined (HANDLE_MAN_PAGES) 27# include "man.h" 28#endif /* HANDLE_MAN_PAGES */ 29 30/* The version numbers of this version of Info. */ 31int info_major_version = 2; 32int info_minor_version = 18; 33 34/* basename (argv[0]) */ 35static char *program_name = NULL; 36 37/* Non-zero means search all indices for APROPOS_SEARCH_STRING. */ 38static int apropos_p = 0; 39 40/* Variable containing the string to search for when apropos_p is non-zero. */ 41static char *apropos_search_string = (char *)NULL; 42 43/* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike 44 apropos, this puts the user at the node, running info. */ 45static int index_search_p = 0; 46 47/* Variable containing the string to search for when index_search_p is 48 non-zero. */ 49static char *index_search_string = (char *)NULL; 50 51/* Non-zero means print version info only. */ 52static int print_version_p = 0; 53 54/* Non-zero means print a short description of the options. */ 55static int print_help_p = 0; 56 57/* Array of the names of nodes that the user specified with "--node" on the 58 command line. */ 59static char **user_nodenames = (char **)NULL; 60static int user_nodenames_index = 0; 61static int user_nodenames_slots = 0; 62 63/* String specifying the first file to load. This string can only be set 64 by the user specifying "--file" on the command line. */ 65static char *user_filename = (char *)NULL; 66 67/* String specifying the name of the file to dump nodes to. This value is 68 filled if the user speficies "--output" on the command line. */ 69static char *user_output_filename = (char *)NULL; 70 71/* Non-zero indicates that when "--output" is specified, all of the menu 72 items of the specified nodes (and their subnodes as well) should be 73 dumped in the order encountered. This basically can print a book. */ 74int dump_subnodes = 0; 75 76/* Structure describing the options that Info accepts. We pass this structure 77 to getopt_long (). If you add or otherwise change this structure, you must 78 also change the string which follows it. */ 79#define APROPOS_OPTION 1 80#define DRIBBLE_OPTION 2 81#define RESTORE_OPTION 3 82#define IDXSRCH_OPTION 4 83static struct option long_options[] = { 84 { "apropos", 1, 0, APROPOS_OPTION }, 85 { "directory", 1, 0, 'd' }, 86 { "node", 1, 0, 'n' }, 87 { "file", 1, 0, 'f' }, 88 { "subnodes", 0, &dump_subnodes, 1 }, 89 { "output", 1, 0, 'o' }, 90 { "help", 0, &print_help_p, 1 }, 91 { "version", 0, &print_version_p, 1 }, 92 { "dribble", 1, 0, DRIBBLE_OPTION }, 93 { "restore", 1, 0, RESTORE_OPTION }, 94 { "index-search", 1, 0, IDXSRCH_OPTION }, 95 {NULL, 0, NULL, 0} 96}; 97 98/* String describing the shorthand versions of the long options found above. */ 99static char *short_options = "d:n:f:o:s"; 100 101/* When non-zero, the Info window system has been initialized. */ 102int info_windows_initialized_p = 0; 103 104/* Some "forward" declarations. */ 105static void info_short_help (), remember_info_program_name (); 106 107 108/* **************************************************************** */ 109/* */ 110/* Main Entry Point to the Info Program */ 111/* */ 112/* **************************************************************** */ 113 114int 115main (argc, argv) 116 int argc; 117 char **argv; 118{ 119 int getopt_long_index; /* Index returned by getopt_long (). */ 120 NODE *initial_node; /* First node loaded by Info. */ 121 122 remember_info_program_name (argv[0]); 123 124#ifdef HAVE_SETLOCALE 125 /* Set locale via LC_ALL. */ 126 setlocale (LC_ALL, ""); 127#endif 128 129 /* Set the text message domain. */ 130 bindtextdomain (PACKAGE, LOCALEDIR); 131 textdomain (PACKAGE); 132 133 while (1) 134 { 135 int option_character; 136 137 option_character = getopt_long 138 (argc, argv, short_options, long_options, &getopt_long_index); 139 140 /* getopt_long () returns EOF when there are no more long options. */ 141 if (option_character == EOF) 142 break; 143 144 /* If this is a long option, then get the short version of it. */ 145 if (option_character == 0 && long_options[getopt_long_index].flag == 0) 146 option_character = long_options[getopt_long_index].val; 147 148 /* Case on the option that we have received. */ 149 switch (option_character) 150 { 151 case 0: 152 break; 153 154 /* User wants to add a directory. */ 155 case 'd': 156 info_add_path (optarg, INFOPATH_PREPEND); 157 break; 158 159 /* User is specifying a particular node. */ 160 case 'n': 161 add_pointer_to_array (optarg, user_nodenames_index, user_nodenames, 162 user_nodenames_slots, 10, char *); 163 break; 164 165 /* User is specifying a particular Info file. */ 166 case 'f': 167 if (user_filename) 168 free (user_filename); 169 170 user_filename = xstrdup (optarg); 171 break; 172 173 /* User is specifying the name of a file to output to. */ 174 case 'o': 175 if (user_output_filename) 176 free (user_output_filename); 177 user_output_filename = xstrdup (optarg); 178 break; 179 180 /* User is specifying that she wishes to dump the subnodes of 181 the node that she is dumping. */ 182 case 's': 183 dump_subnodes = 1; 184 break; 185 186 /* User has specified a string to search all indices for. */ 187 case APROPOS_OPTION: 188 apropos_p = 1; 189 maybe_free (apropos_search_string); 190 apropos_search_string = xstrdup (optarg); 191 break; 192 193 /* User has specified a dribble file to receive keystrokes. */ 194 case DRIBBLE_OPTION: 195 close_dribble_file (); 196 open_dribble_file (optarg); 197 break; 198 199 /* User has specified an alternate input stream. */ 200 case RESTORE_OPTION: 201 info_set_input_from_file (optarg); 202 break; 203 204 /* User has specified a string to search all indices for. */ 205 case IDXSRCH_OPTION: 206 index_search_p = 1; 207 maybe_free (index_search_string); 208 index_search_string = xstrdup (optarg); 209 break; 210 211 default: 212 fprintf (stderr, _("Try --help for more information.")); 213 exit (1); 214 } 215 } 216 217 /* If the output device is not a terminal, and no output filename has been 218 specified, make user_output_filename be "-", so that the info is written 219 to stdout, and turn on the dumping of subnodes. */ 220 if ((!isatty (fileno (stdout))) && (user_output_filename == (char *)NULL)) 221 { 222 user_output_filename = xstrdup ("-"); 223 dump_subnodes = 1; 224 } 225 226 /* If the user specified --version, then show the version and exit. */ 227 if (print_version_p) 228 { 229 printf ("%s (GNU %s %s) %s\n", program_name, PACKAGE, VERSION, 230 version_string ()); 231 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ 232There is NO warranty. You may redistribute this software\n\ 233under the terms of the GNU General Public License.\n\ 234For more information about these matters, see the files named COPYING.\n"), 235 "1998"); 236 exit (0); 237 } 238 239 /* If the `--help' option was present, show the help and exit. */ 240 if (print_help_p) 241 { 242 info_short_help (); 243 exit (0); 244 } 245 246 /* If the user hasn't specified a path for Info files, default it. 247 Lowest priority is our messy hardwired list in filesys.h. 248 Then comes the user's INFODIR from the Makefile. 249 Highest priority is the environment variable, if set. */ 250 if (!infopath) 251 { 252 char *path_from_env = getenv ("INFOPATH"); 253 254 if (path_from_env) 255 { 256 unsigned len = strlen (path_from_env); 257 /* Trailing : on INFOPATH means insert the default path. */ 258 if (len && path_from_env[len - 1] == ':') 259 { 260 path_from_env[len - 1] = 0; 261 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND); 262 } 263#ifdef INFODIR /* from the Makefile */ 264 info_add_path (INFODIR, INFOPATH_PREPEND); 265#endif 266 info_add_path (path_from_env, INFOPATH_PREPEND); 267 } 268 else 269 { 270 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND); 271#ifdef INFODIR /* from the Makefile */ 272 info_add_path (INFODIR, INFOPATH_PREPEND); 273#endif 274 } 275 } 276 277 /* If the user specified a particular filename, add the path of that 278 file to the contents of INFOPATH. */ 279 if (user_filename) 280 { 281 char *directory_name = xstrdup (user_filename); 282 char *temp = filename_non_directory (directory_name); 283 284 if (temp != directory_name) 285 { 286 *temp = 0; 287 info_add_path (directory_name, INFOPATH_PREPEND); 288 } 289 290 free (directory_name); 291 } 292 293 /* If the user wants to search every known index for a given string, 294 do that now, and report the results. */ 295 if (apropos_p) 296 { 297 info_apropos (apropos_search_string); 298 exit (0); 299 } 300 301 /* Get the initial Info node. It is either "(dir)Top", or what the user 302 specifed with values in user_filename and user_nodenames. */ 303 initial_node = info_get_node (user_filename, 304 user_nodenames ? user_nodenames[0] : NULL); 305 306 /* If we couldn't get the initial node, this user is in trouble. */ 307 if (!initial_node) 308 { 309 if (info_recent_file_error) 310 info_error (info_recent_file_error); 311 else 312 info_error 313 (CANT_FIND_NODE, user_nodenames ? user_nodenames[0] : "Top"); 314 exit (1); 315 } 316 317 /* Special cases for when the user specifies multiple nodes. If we 318 are dumping to an output file, dump all of the nodes specified. 319 Otherwise, attempt to create enough windows to handle the nodes 320 that this user wants displayed. */ 321 if (user_nodenames_index > 1) 322 { 323 free (initial_node); 324 325 if (user_output_filename) 326 dump_nodes_to_file 327 (user_filename, user_nodenames, user_output_filename, dump_subnodes); 328 else 329 begin_multiple_window_info_session (user_filename, user_nodenames); 330 331 exit (0); 332 } 333 334 /* If the user specified `--index-search=STRING', start the info 335 session in the node corresponding to the first match. */ 336 if (index_search_p) 337 { 338 int status = 0; 339 340 initialize_info_session (initial_node, 0); 341 342 if (index_entry_exists (windows, index_search_string)) 343 { 344 terminal_clear_screen (); 345 terminal_prep_terminal (); 346 display_update_display (windows); 347 info_last_executed_command = (VFunction *)NULL; 348 349 do_info_index_search (windows, 0, index_search_string); 350 351 info_read_and_dispatch (); 352 353 terminal_unprep_terminal (); 354 355 /* On program exit, leave the cursor at the bottom of the 356 window, and restore the terminal IO. */ 357 terminal_goto_xy (0, screenheight - 1); 358 terminal_clear_to_eol (); 359 fflush (stdout); 360 } 361 else 362 { 363 fputs (_("no entries found\n"), stderr); 364 status = 2; 365 } 366 367 close_dribble_file (); 368 exit (status); 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 while (optind != argc) 376 { 377 REFERENCE **menu; 378 REFERENCE *entry; 379 NODE *node; 380 char *arg; 381 static char *first_arg = (char *)NULL; 382 383 /* Remember the name of the menu entry we want. */ 384 arg = argv[optind++]; 385 386 if (!first_arg) 387 first_arg = arg; 388 389 /* Build and return a list of the menu items in this node. */ 390 menu = info_menu_of_node (initial_node); 391 392 /* If there wasn't a menu item in this node, stop here, but let 393 the user continue to use Info. Perhaps they wanted this node 394 and didn't realize it. */ 395 if (!menu) 396 { 397#if defined (HANDLE_MAN_PAGES) 398 if (first_arg == arg) 399 { 400 node = make_manpage_node (first_arg); 401 if (node) 402 goto maybe_got_node; 403 } 404#endif /* HANDLE_MAN_PAGES */ 405 begin_info_session_with_error 406 (initial_node, _("There is no menu in this node.")); 407 exit (0); 408 } 409 410 /* Find the specified menu item. */ 411 entry = info_get_labeled_reference (arg, menu); 412 413 /* If the item wasn't found, search the list sloppily. Perhaps this 414 user typed "buffer" when they really meant "Buffers". */ 415 if (!entry) 416 { 417 register int i; 418 int best_guess = -1; 419 420 for (i = 0; (entry = menu[i]); i++) 421 { 422 if (strcasecmp (entry->label, arg) == 0) 423 break; 424 else 425 if (strncasecmp (entry->label, arg, strlen (arg)) == 0) 426 best_guess = i; 427 } 428 429 if (!entry && best_guess != -1) 430 entry = menu[best_guess]; 431 } 432 433 /* If we failed to find the reference, start Info with the current 434 node anyway. It is probably a misspelling. */ 435 if (!entry) 436 { 437 char *error_message = _("There is no menu item \"%s\" in this node."); 438 439#if defined (HANDLE_MAN_PAGES) 440 if (first_arg == arg) 441 { 442 node = make_manpage_node (first_arg); 443 if (node) 444 goto maybe_got_node; 445 } 446#endif /* HANDLE_MAN_PAGES */ 447 448 info_free_references (menu); 449 450 /* If we were supposed to dump this node, complain. */ 451 if (user_output_filename) 452 info_error (error_message, arg); 453 else 454 begin_info_session_with_error (initial_node, error_message, arg); 455 456 exit (0); 457 } 458 459 /* We have found the reference that the user specified. Clean it 460 up a little bit. */ 461 if (!entry->filename) 462 { 463 if (initial_node->parent) 464 entry->filename = xstrdup (initial_node->parent); 465 else 466 entry->filename = xstrdup (initial_node->filename); 467 } 468 469 /* Find this node. If we can find it, then turn the initial_node 470 into this one. If we cannot find it, try using the label of the 471 entry as a file (i.e., "(LABEL)Top"). Otherwise the Info file is 472 malformed in some way, and we will just use the current value of 473 initial node. */ 474 node = info_get_node (entry->filename, entry->nodename); 475 476#if defined (HANDLE_MAN_PAGES) 477 if ((first_arg == arg) && !node) 478 { 479 node = make_manpage_node (first_arg); 480 if (node) 481 goto maybe_got_node; 482 } 483#endif /* HANDLE_MAN_PAGES */ 484 485 if (!node && entry->nodename && 486 (strcmp (entry->label, entry->nodename) == 0)) 487 node = info_get_node (entry->label, "Top"); 488 489 maybe_got_node: 490 if (node) 491 { 492 free (initial_node); 493 initial_node = node; 494 info_free_references (menu); 495 } 496 else 497 { 498 char *temp = xstrdup (entry->label); 499 char *error_message; 500 501 error_message = _("Unable to find the node referenced by \"%s\"."); 502 503 info_free_references (menu); 504 505 /* If we were trying to dump the node, then give up. Otherwise, 506 start the session with an error message. */ 507 if (user_output_filename) 508 info_error (error_message, temp); 509 else 510 begin_info_session_with_error (initial_node, error_message, temp); 511 512 exit (0); 513 } 514 } 515 516 /* If the user specified that this node should be output, then do that 517 now. Otherwise, start the Info session with this node. */ 518 if (user_output_filename) 519 dump_node_to_file (initial_node, user_output_filename, dump_subnodes); 520 else 521 begin_info_session (initial_node); 522 523 exit (0); 524} 525 526/* Return a string describing the current version of Info. */ 527char * 528version_string () 529{ 530 static char *vstring = (char *)NULL; 531 532 if (!vstring) 533 { 534 vstring = (char *)xmalloc (50); 535 sprintf (vstring, "%d.%d", info_major_version, info_minor_version); 536 } 537 return (vstring); 538} 539 540 541/* Error handling. */ 542 543static void 544remember_info_program_name (fullpath) 545 char *fullpath; 546{ 547 char *filename; 548 549 filename = filename_non_directory (fullpath); 550 program_name = xstrdup (filename); 551} 552 553/* Non-zero if an error has been signalled. */ 554int info_error_was_printed = 0; 555 556/* Non-zero means ring terminal bell on errors. */ 557int info_error_rings_bell_p = 1; 558 559/* Print FORMAT with ARG1 and ARG2. If the window system was initialized, 560 then the message is printed in the echo area. Otherwise, a message is 561 output to stderr. */ 562void 563info_error (format, arg1, arg2) 564 char *format; 565 void *arg1, *arg2; 566{ 567 info_error_was_printed = 1; 568 569 if (!info_windows_initialized_p || display_inhibited) 570 { 571 fprintf (stderr, "%s: ", program_name); 572 fprintf (stderr, format, arg1, arg2); 573 fprintf (stderr, "\n"); 574 fflush (stderr); 575 } 576 else 577 { 578 if (!echo_area_is_active) 579 { 580 if (info_error_rings_bell_p) 581 terminal_ring_bell (); 582 window_message_in_echo_area (format, arg1, arg2); 583 } 584 else 585 { 586 NODE *temp; 587 588 temp = build_message_node (format, arg1, arg2); 589 if (info_error_rings_bell_p) 590 terminal_ring_bell (); 591 inform_in_echo_area (temp->contents); 592 free (temp->contents); 593 free (temp); 594 } 595 } 596} 597 598/* Produce a scaled down description of the available options to Info. */ 599static void 600info_short_help () 601{ 602 printf (_("\ 603Usage: %s [OPTION]... [INFO-FILE [MENU-ITEM...]]\n\ 604\n\ 605Read documentation in Info format.\n\ 606For more complete documentation on how to use Info, run `info info options'.\n\ 607\n\ 608Options:\n\ 609--directory DIR add DIR to INFOPATH.\n\ 610--dribble FILENAME remember user keystrokes in FILENAME.\n\ 611--file FILENAME specify Info file to visit.\n\ 612--node NODENAME specify nodes in first visited Info file.\n\ 613--output FILENAME output selected nodes to FILENAME.\n\ 614--restore FILENAME read initial keystrokes from FILENAME.\n\ 615--subnodes recursively output menu items.\n\ 616--help display this help and exit.\n\ 617--version display version information and exit.\n\ 618\n\ 619The first argument, if present, is the name of the Info file to read.\n\ 620Any remaining arguments are treated as the names of menu\n\ 621items in the initial node visited. For example, `info emacs buffers'\n\ 622moves to the node `buffers' in the info file `emacs'.\n\ 623\n\ 624Email bug reports to bug-texinfo@gnu.org."), program_name); 625 626 exit (0); 627} 628