1 /* info.c -- Display nodes of Info files in multiple windows.
2 $Id: info.c,v 1.33 2008/08/14 17:36:13 karl Exp $
4 Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
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 3 of the License, or
10 (at your option) any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Originally written by Brian Fox (bfox@ai.mit.edu). */
26 #if defined (HANDLE_MAN_PAGES)
28 #endif /* HANDLE_MAN_PAGES */
30 char *program_name = "info";
32 /* Non-zero means search all indices for APROPOS_SEARCH_STRING. */
33 static int apropos_p = 0;
35 /* Variable containing the string to search for when apropos_p is non-zero. */
36 static char *apropos_search_string = NULL;
38 /* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike
39 apropos, this puts the user at the node, running info. */
40 static int index_search_p = 0;
42 /* Non-zero means look for the node which describes the invocation
43 and command-line options of the program, and start the info
44 session at that node. */
45 static int goto_invocation_p = 0;
47 /* Variable containing the string to search for when index_search_p is
49 static char *index_search_string = NULL;
51 /* Non-zero means print version info only. */
52 static int print_version_p = 0;
54 /* Non-zero means print a short description of the options. */
55 static int print_help_p = 0;
57 /* Array of the names of nodes that the user specified with "--node" on the
59 static char **user_nodenames = NULL;
60 static int user_nodenames_index = 0;
61 static int user_nodenames_slots = 0;
63 /* String specifying the first file to load. This string can only be set
64 by the user specifying "--file" on the command line. */
65 static char *user_filename = NULL;
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. */
69 static char *user_output_filename = NULL;
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. */
74 int dump_subnodes = 0;
76 /* Non-zero means make default keybindings be loosely modeled on vi(1). */
79 /* Non-zero means don't remove ANSI escape sequences. */
80 int raw_escapes_p = 1;
82 /* Non-zero means print the absolute location of the file to be loaded. */
83 static int print_where_p = 0;
86 /* Non-zero indicates that screen output should be made 'speech-friendly'.
87 Since on MSDOS the usual behavior is to write directly to the video
88 memory, speech synthesizer software cannot grab the output. Therefore,
89 we provide a user option which tells us to avoid direct screen output
90 and use stdout instead (which loses the color output). */
91 int speech_friendly = 0;
94 /* Structure describing the options that Info accepts. We pass this structure
95 to getopt_long (). If you add or otherwise change this structure, you must
96 also change the string which follows it. */
97 #define DRIBBLE_OPTION 2
98 #define RESTORE_OPTION 3
99 #define IDXSRCH_OPTION 4
100 static struct option long_options[] = {
101 { "apropos", 1, 0, 'k' },
102 { "directory", 1, 0, 'd' },
103 { "dribble", 1, 0, DRIBBLE_OPTION },
104 { "file", 1, 0, 'f' },
105 { "help", 0, &print_help_p, 1 },
106 { "index-search", 1, 0, IDXSRCH_OPTION },
107 { "location", 0, &print_where_p, 1 },
108 { "node", 1, 0, 'n' },
109 { "output", 1, 0, 'o' },
110 { "raw-escapes", 0, &raw_escapes_p, 1 },
111 { "no-raw-escapes", 0, &raw_escapes_p, 0 },
112 { "show-malformed-multibytes", 0, &show_malformed_multibyte_p, 1 },
113 { "no-show-malformed-multibytes", 0, &show_malformed_multibyte_p, 0 },
114 { "restore", 1, 0, RESTORE_OPTION },
115 { "show-options", 0, 0, 'O' },
116 { "subnodes", 0, &dump_subnodes, 1 },
117 { "usage", 0, 0, 'O' },
118 { "version", 0, &print_version_p, 1 },
119 { "vi-keys", 0, &vi_keys_p, 1 },
120 { "where", 0, &print_where_p, 1 },
122 { "speech-friendly", 0, &speech_friendly, 1 },
127 /* String describing the shorthand versions of the long options found above. */
129 static char *short_options = "k:d:n:f:ho:ORswb";
131 static char *short_options = "k:d:n:f:ho:ORws";
134 /* When non-zero, the Info window system has been initialized. */
135 int info_windows_initialized_p = 0;
137 /* Some "forward" declarations. */
138 static void info_short_help (void);
139 static void init_messages (void);
142 /* **************************************************************** */
144 /* Main Entry Point to the Info Program */
146 /* **************************************************************** */
149 main (int argc, char *argv[])
151 int getopt_long_index; /* Index returned by getopt_long (). */
152 NODE *initial_node; /* First node loaded by Info. */
154 #ifdef HAVE_SETLOCALE
155 /* Set locale via LC_ALL. */
156 setlocale (LC_ALL, "");
157 #endif /* HAVE_SETLOCALE */
160 /* Set the text message domain. */
161 bindtextdomain (PACKAGE, LOCALEDIR);
162 textdomain (PACKAGE);
169 int option_character;
171 option_character = getopt_long
172 (argc, argv, short_options, long_options, &getopt_long_index);
174 /* getopt_long returns EOF when there are no more long options. */
175 if (option_character == EOF)
178 /* If this is a long option, then get the short version of it. */
179 if (option_character == 0 && long_options[getopt_long_index].flag == 0)
180 option_character = long_options[getopt_long_index].val;
182 /* Case on the option that we have received. */
183 switch (option_character)
188 /* User wants to add a directory. */
190 info_add_path (optarg, INFOPATH_PREPEND);
193 /* User is specifying a particular node. */
195 add_pointer_to_array (optarg, user_nodenames_index, user_nodenames,
196 user_nodenames_slots, 10, char *);
199 /* User is specifying a particular Info file. */
202 free (user_filename);
204 user_filename = xstrdup (optarg);
207 /* Treat -h like --help. */
212 /* User is specifying the name of a file to output to. */
214 if (user_output_filename)
215 free (user_output_filename);
216 user_output_filename = xstrdup (optarg);
219 /* User has specified that she wants to find the "Options"
220 or "Invocation" node for the program. */
222 goto_invocation_p = 1;
225 /* User has specified that she wants the escape sequences
226 in man pages to be passed thru unaltered. */
231 /* User is specifying that she wishes to dump the subnodes of
232 the node that she is dumping. */
237 /* For compatibility with man, -w is --where. */
243 /* User wants speech-friendly output. */
247 #endif /* __MSDOS__ */
249 /* User has specified a string to search all indices for. */
252 maybe_free (apropos_search_string);
253 apropos_search_string = xstrdup (optarg);
256 /* User has specified a dribble file to receive keystrokes. */
258 close_dribble_file ();
259 open_dribble_file (optarg);
262 /* User has specified an alternate input stream. */
264 info_set_input_from_file (optarg);
267 /* User has specified a string to search all indices for. */
270 maybe_free (index_search_string);
271 index_search_string = xstrdup (optarg);
275 fprintf (stderr, _("Try --help for more information.\n"));
280 /* If the output device is not a terminal, and no output filename has been
281 specified, make user_output_filename be "-", so that the info is written
282 to stdout, and turn on the dumping of subnodes. */
283 if ((!isatty (fileno (stdout))) && (user_output_filename == NULL))
285 user_output_filename = xstrdup ("-");
289 /* If the user specified --version, then show the version and exit. */
292 printf ("info (GNU %s) %s\n", PACKAGE, VERSION);
294 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
295 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
296 This is free software: you are free to change and redistribute it.\n\
297 There is NO WARRANTY, to the extent permitted by law.\n"),
302 /* If the `--help' option was present, show the help and exit. */
309 /* If the user hasn't specified a path for Info files, default it.
310 Lowest priority is our messy hardwired list in filesys.h.
311 Then comes the user's INFODIR from the Makefile.
312 Highest priority is the environment variable, if set. */
315 char *path_from_env = getenv ("INFOPATH");
319 unsigned len = strlen (path_from_env);
320 /* Trailing : on INFOPATH means insert the default path. */
321 if (len && path_from_env[len - 1] == PATH_SEP[0])
323 path_from_env[len - 1] = 0;
324 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND);
326 #ifdef INFODIR /* from the Makefile */
327 info_add_path (INFODIR, INFOPATH_PREPEND);
329 info_add_path (path_from_env, INFOPATH_PREPEND);
333 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND);
334 #ifdef INFODIR /* from the Makefile */
335 info_add_path (INFODIR, INFOPATH_PREPEND);
337 #ifdef INFODIR2 /* from the Makefile, too */
339 if (!STREQ (INFODIR, INFODIR2))
341 info_add_path (INFODIR2, INFOPATH_PREPEND);
346 /* If the user specified a particular filename, add the path of that
347 file to the contents of INFOPATH. */
349 add_file_directory_to_path (user_filename);
351 /* If the user wants to search every known index for a given string,
352 do that now, and report the results. */
355 info_apropos (apropos_search_string);
359 /* Get the initial Info node. It is either "(dir)Top", or what the user
360 specifed with values in user_filename and user_nodenames. */
361 initial_node = info_get_node (user_filename,
362 user_nodenames ? user_nodenames[0] : 0);
364 /* If we couldn't get the initial node, this user is in trouble. */
367 if (info_recent_file_error)
368 info_error (info_recent_file_error, NULL, NULL);
370 info_error (msg_cant_find_node,
371 user_nodenames ? user_nodenames[0] : "Top", NULL);
375 /* Special cases for when the user specifies multiple nodes. If we
376 are dumping to an output file, dump all of the nodes specified.
377 Otherwise, attempt to create enough windows to handle the nodes
378 that this user wants displayed. */
379 if (user_nodenames_index > 1)
384 printf ("%s\n", user_filename ? user_filename : "unknown?!");
385 else if (user_output_filename)
387 (user_filename, user_nodenames, user_output_filename, dump_subnodes);
389 begin_multiple_window_info_session (user_filename, user_nodenames);
394 /* If there are arguments remaining, they are the names of menu items
395 in sequential info files starting from the first one loaded. That
396 file name is either "dir", or the contents of user_filename if one
400 char *errarg1, *errarg2;
401 NODE *new_initial_node;
403 /* If they say info -O info, we want to show them the invocation node
404 for standalone info; there's nothing useful in info.texi. */
405 if (goto_invocation_p && argv[optind]
406 && mbscasecmp (argv[optind], "info") == 0)
407 argv[optind] = "info-stnd";
409 new_initial_node = info_follow_menus (initial_node, argv + optind,
410 &errstr, &errarg1, &errarg2);
412 if (new_initial_node && new_initial_node != initial_node)
413 initial_node = new_initial_node;
417 if (initial_node->parent)
418 printf ("%s\n", initial_node->parent);
419 else if (initial_node->filename
420 && !is_dir_name (filename_non_directory (initial_node->filename)))
421 printf ("%s\n", initial_node->filename);
427 /* If the user specified that this node should be output, then do that
428 now. Otherwise, start the Info session with this node. Or act
429 accordingly if the initial node was not found. */
430 if (user_output_filename && !goto_invocation_p)
433 dump_node_to_file (initial_node, user_output_filename,
436 info_error (errstr, errarg1, errarg2);
442 begin_info_session_with_error (initial_node, errstr,
444 /* If the user specified `--index-search=STRING' or
445 --show-options, start the info session in the node
446 corresponding to what they want. */
447 else if (index_search_p || goto_invocation_p)
451 initialize_info_session (initial_node, 0);
453 if (goto_invocation_p
454 || index_entry_exists (windows, index_search_string))
456 terminal_prep_terminal ();
457 terminal_clear_screen ();
458 info_last_executed_command = NULL;
461 do_info_index_search (windows, 0, index_search_string);
464 /* If they said "info --show-options foo bar baz",
465 the last of the arguments is the program whose
466 options they want to see. */
467 char **p = argv + optind;
474 program = xstrdup (*p);
476 else if (user_filename)
477 /* If there's no command-line arguments to
478 supply the program name, use the Info file
479 name (sans extension and leading directories)
481 program = program_name_from_file_name (user_filename);
483 program = xstrdup ("");
485 info_intuit_options_node (windows, initial_node, program);
489 if (user_output_filename)
491 dump_node_to_file (windows->node, user_output_filename,
495 info_read_and_dispatch ();
497 /* On program exit, leave the cursor at the bottom of the
498 window, and restore the terminal IO. */
499 terminal_goto_xy (0, screenheight - 1);
500 terminal_clear_to_eol ();
502 terminal_unprep_terminal ();
506 fprintf (stderr, _("no index entries found for `%s'\n"),
507 index_search_string);
511 close_dribble_file ();
515 begin_info_session (initial_node);
521 return 0; /* Avoid bogus warnings. */
525 add_file_directory_to_path (char *filename)
527 char *directory_name = xstrdup (filename);
528 char *temp = filename_non_directory (directory_name);
530 if (temp != directory_name)
532 if (HAVE_DRIVE (directory_name) && temp == directory_name + 2)
534 /* The directory of "d:foo" is stored as "d:.", to avoid
535 mixing it with "d:/" when a slash is appended. */
540 info_add_path (directory_name, INFOPATH_PREPEND);
543 free (directory_name);
547 /* Error handling. */
549 /* Non-zero if an error has been signalled. */
550 int info_error_was_printed = 0;
552 /* Non-zero means ring terminal bell on errors. */
553 int info_error_rings_bell_p = 1;
555 /* Print FORMAT with ARG1 and ARG2. If the window system was initialized,
556 then the message is printed in the echo area. Otherwise, a message is
559 info_error (const char *format, void *arg1, void *arg2)
561 info_error_was_printed = 1;
563 if (!info_windows_initialized_p || display_inhibited)
565 fprintf (stderr, "%s: ", program_name);
567 fprintf (stderr, format, arg1, arg2);
569 /* If we're passed a string, just print it. Otherwise a % in a
570 filename gets treated as a format specifier. */
571 fputs (format, stderr);
572 fprintf (stderr, "\n");
577 if (!echo_area_is_active)
579 if (info_error_rings_bell_p)
580 terminal_ring_bell ();
581 window_message_in_echo_area (format, arg1, arg2);
585 NODE *temp = build_message_node (format, arg1, arg2);
586 if (info_error_rings_bell_p)
587 terminal_ring_bell ();
588 inform_in_echo_area (temp->contents);
589 free (temp->contents);
596 /* Produce a scaled down description of the available options to Info. */
598 info_short_help (void)
601 Usage: %s [OPTION]... [MENU-ITEM...]\n\
603 Read documentation in Info format.\n"), program_name);
608 -k, --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."));
614 -h, --help display this help and exit.\n\
615 --index-search=STRING go to node pointed by index entry STRING.\n\
616 -n, --node=NODENAME specify nodes in first visited Info file.\n\
617 -o, --output=FILENAME output selected nodes to FILENAME."));
620 -R, --raw-escapes output \"raw\" ANSI escapes (default).\n\
621 --no-raw-escapes output escapes as literal text.\n\
622 --restore=FILENAME read initial keystrokes from FILENAME.\n\
623 -O, --show-options, --usage go to command-line options node."));
627 -b, --speech-friendly be friendly to speech synthesizers."));
631 --subnodes recursively output menu items.\n\
632 --vi-keys use vi-like and less-like key bindings.\n\
633 --version display version information and exit.\n\
634 -w, --where, --location print physical location of Info file."));
637 The first non-option argument, if present, is the menu entry to start from;\n\
638 it is searched for in all `dir' files along INFOPATH.\n\
639 If it is not present, info merges all `dir' files and shows the result.\n\
640 Any remaining arguments are treated as the names of menu\n\
641 items relative to the initial node visited."));
644 For a summary of key bindings, type h within Info."));
648 info show top-level dir menu\n\
649 info info show the general manual for Info readers\n\
650 info info-stnd show the manual specific to this Info program\n\
651 info emacs start at emacs node from top-level dir\n\
652 info emacs buffers start at buffers node within emacs manual\n\
653 info --show-options emacs start at node with emacs' command line options\n\
654 info --subnodes -o out.txt emacs dump entire manual to out.txt\n\
655 info -f ./foo.info show file ./foo.info, not searching dir"));
658 Email bug reports to bug-texinfo@gnu.org,\n\
659 general questions and discussion to help-texinfo@gnu.org.\n\
660 Texinfo home page: http://www.gnu.org/software/texinfo/"));
666 /* Initialize strings for gettext. Because gettext doesn't handle N_ or
667 _ within macro definitions, we put shared messages into variables and
668 use them that way. This also has the advantage that there's only one
669 copy of the strings. */
671 const char *msg_cant_find_node;
672 const char *msg_cant_file_node;
673 const char *msg_cant_find_window;
674 const char *msg_cant_find_point;
675 const char *msg_cant_kill_last;
676 const char *msg_no_menu_node;
677 const char *msg_no_foot_node;
678 const char *msg_no_xref_node;
679 const char *msg_no_pointer;
680 const char *msg_unknown_command;
681 const char *msg_term_too_dumb;
682 const char *msg_at_node_bottom;
683 const char *msg_at_node_top;
684 const char *msg_one_window;
685 const char *msg_win_too_small;
686 const char *msg_cant_make_help;
691 msg_cant_find_node = _("Cannot find node `%s'.");
692 msg_cant_file_node = _("Cannot find node `(%s)%s'.");
693 msg_cant_find_window = _("Cannot find a window!");
694 msg_cant_find_point = _("Point doesn't appear within this window's node!");
695 msg_cant_kill_last = _("Cannot delete the last window.");
696 msg_no_menu_node = _("No menu in this node.");
697 msg_no_foot_node = _("No footnotes in this node.");
698 msg_no_xref_node = _("No cross references in this node.");
699 msg_no_pointer = _("No `%s' pointer for this node.");
700 msg_unknown_command = _("Unknown Info command `%c'; try `?' for help.");
701 msg_term_too_dumb = _("Terminal type `%s' is not smart enough to run Info.");
702 msg_at_node_bottom = _("You are already at the last page of this node.");
703 msg_at_node_top = _("You are already at the first page of this node.");
704 msg_one_window = _("Only one window.");
705 msg_win_too_small = _("Resulting window would be too small.");
706 msg_cant_make_help = _("Not enough room for a help window, please delete a window.");