/* info.c -- Display nodes of Info files in multiple windows. Copyright 1993-2023 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Originally written by Brian Fox. */ #include "info.h" #include "filesys.h" #include "scan.h" #include "util.h" #include "session.h" #include "indices.h" #include "dribble.h" #include "getopt.h" #include "man.h" #include "variables.h" char *program_name = "info"; /* Non-zero means search all indices for APROPOS_SEARCH_STRING. */ static int apropos_p = 0; /* Variable containing the string to search for when apropos_p is non-zero. */ static char *apropos_search_string = NULL; /* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike apropos, this puts the user at the node, running info. */ static int index_search_p = 0; /* Non-zero means look for the node which describes the invocation and command-line options of the program, and start the info session at that node. */ static int goto_invocation_p = 0; static char *invocation_program_name = 0; /* Variable containing the string to search for when index_search_p is non-zero. */ static char *index_search_string = NULL; /* Non-zero means print version info only. */ static int print_version_p = 0; /* Non-zero means print a short description of the options. */ static int print_help_p = 0; /* Name of file to start session with. Default file for --node arguments. */ static char *initial_file = 0; /* Array of the names of nodes that the user specified with "--node" on the command line. */ static char **user_nodenames = NULL; static size_t user_nodenames_index = 0; static size_t user_nodenames_slots = 0; /* References to the nodes to start the session with. */ static REFERENCE **ref_list = NULL; static size_t ref_slots = 0; static size_t ref_index = 0; /* String specifying the first file to load. This string can only be set by the user specifying "--file" on the command line. */ static char *user_filename = NULL; /* String specifying the name of the file to dump nodes to. This value is filled if the user speficies "--output" on the command line. */ static char *user_output_filename = NULL; /* Non-zero indicates that when "--output" is specified, all of the menu items of the specified nodes (and their subnodes as well) should be dumped in the order encountered. This basically can print a book. */ int dump_subnodes = 0; /* Non-zero means make default keybindings be loosely modeled on vi(1). */ int vi_keys_p = 0; /* Non-zero means don't remove ANSI escape sequences. */ int raw_escapes_p = 1; /* Print/visit all matching documents. */ static int all_matches_p = 0; /* Non-zero means print the absolute location of the file to be loaded. */ static int print_where_p = 0; /* Non-zero means don't try to be smart when searching for nodes. */ int strict_node_location_p = 0; #if defined(__MSDOS__) || defined(__MINGW32__) /* Non-zero indicates that screen output should be made 'speech-friendly'. Since on MSDOS the usual behavior is to write directly to the video memory, speech synthesizer software cannot grab the output. Therefore, we provide a user option which tells us to avoid direct screen output and use stdout instead (which loses the color output). */ int speech_friendly = 0; #endif /* Structure describing the options that Info accepts. We pass this structure to getopt_long (). If you add or otherwise change this structure, you must also change the string which follows it. */ #define DRIBBLE_OPTION 2 #define RESTORE_OPTION 3 #define IDXSRCH_OPTION 4 #define INITFLE_OPTION 5 #define VIRTIDX_OPTION 6 static struct option long_options[] = { { "all", 0, 0, 'a' }, { "apropos", 1, 0, 'k' }, { "debug", 1, 0, 'x' }, { "directory", 1, 0, 'd' }, { "dribble", 1, 0, DRIBBLE_OPTION }, { "file", 1, 0, 'f' }, { "help", 0, &print_help_p, 1 }, { "index-search", 1, 0, IDXSRCH_OPTION }, { "init-file", 1, 0, INITFLE_OPTION }, { "location", 0, &print_where_p, 1 }, { "node", 1, 0, 'n' }, { "output", 1, 0, 'o' }, { "raw-escapes", 0, &raw_escapes_p, 1 }, { "no-raw-escapes", 0, &raw_escapes_p, 0 }, { "show-malformed-multibytes", 0, &show_malformed_multibyte_p, 1 }, { "no-show-malformed-multibytes", 0, &show_malformed_multibyte_p, 0 }, { "restore", 1, 0, RESTORE_OPTION }, { "show-options", 0, 0, 'O' }, { "strict-node-location", 0, &strict_node_location_p, 1 }, { "subnodes", 0, &dump_subnodes, 1 }, { "usage", 0, 0, 'O' }, { "variable", 1, 0, 'v' }, { "version", 0, &print_version_p, 1 }, { "vi-keys", 0, &vi_keys_p, 1 }, { "where", 0, &print_where_p, 1 }, #if defined(__MSDOS__) || defined(__MINGW32__) { "speech-friendly", 0, &speech_friendly, 1 }, #endif {NULL, 0, NULL, 0} }; /* String describing the shorthand versions of the long options found above. */ #if defined(__MSDOS__) || defined(__MINGW32__) static char *short_options = "ak:d:n:f:ho:ORsv:wbx:"; #else static char *short_options = "ak:d:n:f:ho:ORv:wsx:"; #endif /* When non-zero, the Info window system has been initialized. */ int info_windows_initialized_p = 0; /* Some "forward" declarations. */ static void info_short_help (void); static void init_messages (void); /* Find the first file to load (and possibly the first node as well). If the --file option is given, use that as the file, otherwise try to interpret the first non-option argument, either by looking it up as a dir entry, looking for a file by that name, or finding a man page by that name. Set INITIAL_FILE to the name of the initial Info file. */ static void get_initial_file (int *argc, char ***argv, char **error) { REFERENCE *entry; /* If --file was not used and there is a slash in the first non-option argument (e.g. "info subdir/file.info"), do not search the dir files for a matching entry. */ if (!user_filename && (*argv)[0] && HAS_SLASH ((*argv)[0]) && (*argv)[0][0] != '(') /* don't treat "(manual)node" as a filename */ { user_filename = xstrdup ((*argv)[0]); (*argv)++; /* Advance past first remaining argument. */ (*argc)--; } /* User used "--file". */ if (user_filename) { if (!IS_ABSOLUTE(user_filename) && HAS_SLASH(user_filename) && !(user_filename[0] == '.' && IS_SLASH(user_filename[1]))) { /* Prefix "./" to the filename to prevent a lookup in INFOPATH. */ char *s; xasprintf (&s, "%s%s", "./", user_filename); free (user_filename); user_filename = s; } if (IS_ABSOLUTE(user_filename) || HAS_SLASH(user_filename)) initial_file = info_add_extension (0, user_filename, 0); else initial_file = info_find_fullpath (user_filename, 0); if (!initial_file) { if (!filesys_error_number) filesys_error_number = ENOENT; *error = filesys_error_string (user_filename, filesys_error_number); } return; } if (!(*argv)[0]) { /* No more non-option arguments. */ initial_file = xstrdup("dir"); return; } /* If first argument begins with '(', add it as if it were given with '--node'. This is to support invoking like "info '(emacs)Buffers'". If it is a well-formed node spec then the rest of the arguments are menu entries to follow, or an index entry. */ if ((*argv)[0][0] == '(') { info_parse_node ((*argv)[0]); if (info_parsed_filename) { initial_file = info_find_fullpath (info_parsed_filename, 0); if (initial_file) { add_pointer_to_array (info_new_reference (initial_file, info_parsed_nodename), ref_index, ref_list, ref_slots, 2); /* Remove this argument from the argument list. */ memmove (*argv, *argv + 1, (*argc)-- * sizeof (char *)); return; } } } /* If there are any more arguments, the initial file is the dir entry given by the first one. */ { /* If they say info info (or info -O info, etc.), show them info-stnd.texi. (Get info.texi with info -f info.) */ if ((*argv)[0] && mbscasecmp ((*argv)[0], "info") == 0) (*argv)[0] = "info-stnd"; entry = lookup_dir_entry ((*argv)[0], 0); if (entry) { initial_file = info_find_fullpath (entry->filename, 0); if (initial_file) { REFERENCE *copy; (*argv)++; /* Advance past first remaining argument. */ (*argc)--; copy = info_copy_reference (entry); /* Store full path, so that we find the already loaded file in info_find_file, and show the full path if --where is used. */ free (copy->filename); copy->filename = xstrdup (initial_file); add_pointer_to_array (copy, ref_index, ref_list, ref_slots, 2); return; } } } /* File name lookup. */ { /* Try finding a file with this name, in case it exists, but wasn't listed in dir. */ initial_file = info_find_fullpath ((*argv)[0], 0); if (initial_file) { add_pointer_to_array (info_new_reference ((*argv)[0], "Top"), ref_index, ref_list, ref_slots, 2); (*argv)++; /* Advance past first remaining argument. */ (*argc)--; return; } else xasprintf (error, _("No menu item '%s' in node '%s'"), (*argv)[0], "(dir)Top"); } /* Fall back to loading man page. */ { int man_exists; debug (3, ("falling back to manpage node")); man_exists = check_manpage_node ((*argv)[0]); if (man_exists) { add_pointer_to_array (info_new_reference (MANPAGE_FILE_BUFFER_NAME, (*argv)[0]), ref_index, ref_list, ref_slots, 2); initial_file = MANPAGE_FILE_BUFFER_NAME; return; } } /* Inexact dir lookup. */ { entry = lookup_dir_entry ((*argv)[0], 1); if (entry) { initial_file = info_find_fullpath (entry->filename, 0); if (initial_file) { REFERENCE *copy; (*argv)++; /* Advance past first remaining argument. */ (*argc)--; /* Clear error message. */ free (*error); *error = 0; copy = info_copy_reference (entry); /* Store full path, so that we find the already loaded file in info_find_file, and show the full path if --where is used. */ free (copy->filename); copy->filename = initial_file; add_pointer_to_array (copy, ref_index, ref_list, ref_slots, 2); return; } } } return; } /* Expand list of nodes to be loaded. */ static void add_initial_nodes (int argc, char **argv, char **error) { /* Add nodes specified with --node. */ if (user_nodenames) { int i; /* If any --node arguments were given, the node in ref_list[0] is only used to set initial_file. */ if (user_nodenames_index > 0 && ref_index > 0) { info_reference_free (ref_list[0]); ref_list[0] = 0; ref_index = 0; } for (i = 0; user_nodenames[i]; i++) { char *node_filename = 0; char *node_nodename = 0; /* Parse node spec to support invoking like info --node "(emacs)Buffers". */ info_parse_node (user_nodenames[i]); if (info_parsed_filename) { node_filename = info_parsed_filename; node_nodename = info_parsed_nodename; } else { FILE_BUFFER *file_buffer; TAG *tag; int j; if (!initial_file) continue; /* Shouldn't happen. */ /* Check for a node by this name, and if there isn't one look for an inexact match. */ node_filename = initial_file; node_nodename = 0; file_buffer = info_find_file (node_filename); if (!file_buffer) continue; /* First look for an exact match. */ for (j = 0; (tag = file_buffer->tags[j]); j++) if (strcmp (user_nodenames[i], tag->nodename) == 0) { node_nodename = tag->nodename; break; } if (!node_nodename) { int best_guess = -1; int len = strlen (user_nodenames[i]); for (j = 0; (tag = file_buffer->tags[j]); j++) { if (mbscasecmp (user_nodenames[i], tag->nodename) == 0) { /* Exact, case-insensitive match. */ node_nodename = tag->nodename; best_guess = -1; break; } else if (best_guess == -1 && (mbsncasecmp (user_nodenames[i], tag->nodename, len) == 0)) /* Case-insensitive initial substring. */ best_guess = j; } if (best_guess != -1) { node_nodename = file_buffer->tags[best_guess]->nodename; } } if (!node_nodename) { free (*error); xasprintf (error, _("Cannot find node '%s'"), user_nodenames[i]); continue; } } if (node_filename && node_nodename) add_pointer_to_array (info_new_reference (node_filename, node_nodename), ref_index, ref_list, ref_slots, 2); } } if (goto_invocation_p) { NODE *top_node = 0; REFERENCE *invoc_ref = 0; char *program; if (ref_index == 0) { info_error (_("No program name given")); exit (1); } if (invocation_program_name) program = xstrdup (invocation_program_name); else if (ref_list[0] && ref_list[0]->filename) /* If there's no command-line arguments to supply the program name, use the Info file name (sans extension and leading directories) instead. */ program = program_name_from_file_name (ref_list[0]->filename); else program = xstrdup (""); if (ref_index > 0) top_node = info_get_node (ref_list[0]->filename, ref_list[0]->nodename); if (top_node) invoc_ref = info_intuit_options_node (top_node, program); if (invoc_ref) { info_reference_free (ref_list[0]); ref_index = 0; add_pointer_to_array (invoc_ref, ref_index, ref_list, ref_slots, 2); } free (program); } /* Default is the "Top" node if there were no other nodes. */ if (ref_index == 0 && initial_file) { add_pointer_to_array (info_new_reference (initial_file, "Top"), ref_index, ref_list, ref_slots, 2); } /* If there are arguments remaining, they are the names of menu items in sequential info files starting from the first one loaded. */ if (*argv && ref_index > 0) { NODE *initial_node; /* Node to start following menus from. */ NODE *node_via_menus; initial_node = info_get_node_with_defaults (ref_list[0]->filename, ref_list[0]->nodename, 0); if (!initial_node) return; node_via_menus = info_follow_menus (initial_node, argv, error, 1); if (node_via_menus) { argv += argc; argc = 0; info_reference_free (ref_list[0]); ref_list[0] = info_new_reference (node_via_menus->fullpath, node_via_menus->nodename); free_history_node (node_via_menus); } /* If no nodes found, and there is exactly one argument remaining, check for it as an index entry. */ else if (argc == 1 && argv[0]) { FILE_BUFFER *fb; REFERENCE *match; debug (3, ("looking in indices")); fb = info_find_file (ref_list[0]->filename); if (fb) { match = look_in_indices (fb, argv[0], 0); if (match) { argv += argc; argc = 0; free (*error); *error = 0; info_reference_free (ref_list[0]); ref_list[0] = info_copy_reference (match); } } } /* If there are arguments remaining, follow menus inexactly. */ if (argc != 0) { initial_node = info_get_node_with_defaults (ref_list[0]->filename, ref_list[0]->nodename, 0); free (*error); *error = 0; node_via_menus = info_follow_menus (initial_node, argv, error, 0); if (node_via_menus) { if (argc >= 2 || !*error) { argv += argc; argc = 0; info_reference_free (ref_list[0]); ref_list[0] = info_new_reference (node_via_menus->fullpath, node_via_menus->nodename); } free_history_node (node_via_menus); } } /* If still no nodes found, and there is exactly one argument remaining, look in indices sloppily. */ if (argc == 1) { FILE_BUFFER *fb; REFERENCE *nearest; debug (3, ("looking in indices sloppily")); fb = info_find_file (ref_list[0]->filename); if (fb) { nearest = look_in_indices (fb, argv[0], 1); if (nearest) { argv += argc; argc = 0; free (*error); *error = 0; info_reference_free (ref_list[0]); ref_list[0] = info_copy_reference (nearest); } } } } return; } static void info_find_matching_files (char *filename) { int i; char *searchdir; NODE *man_node; /* Check for dir entries first. */ i = 0; for (searchdir = infopath_first (&i); searchdir; searchdir = infopath_next (&i)) { REFERENCE *new_ref = dir_entry_of_infodir (filename, searchdir); if (new_ref) add_pointer_to_array (new_ref, ref_index, ref_list, ref_slots, 2); } /* Look for files with matching names. */ i = 0; while (1) { char *p; int j; p = info_file_find_next_in_path (filename, &i, 0); if (!p) break; /* Add to list only if the file is not in the list already (which would happen if there was a dir entry with the label and filename both being this file). */ for (j = 0; j < ref_index; j++) { if (!strcmp (p, ref_list[j]->filename)) break; } if (j == ref_index) { add_pointer_to_array (info_new_reference (p, 0), ref_index, ref_list, ref_slots, 2); } free (p); } /* Check for man page. */ man_node = get_manpage_node (filename); if (man_node) { free (man_node); add_pointer_to_array (info_new_reference (MANPAGE_FILE_BUFFER_NAME, filename), ref_index, ref_list, ref_slots, 2); } } static void set_debug_level (const char *arg) { char *p; long n = strtol (arg, &p, 10); if (*p) { fprintf (stderr, _("invalid number: %s\n"), arg); exit (EXIT_FAILURE); } if (n < 0 || n > UINT_MAX) debug_level = UINT_MAX; else debug_level = n; } static void add_file_directory_to_path (char *filename) { char *directory_name = xstrdup (filename); char *temp = filename_non_directory (directory_name); if (temp != directory_name) { if (HAVE_DRIVE (directory_name) && temp == directory_name + 2) { /* The directory of "d:foo" is stored as "d:.", to avoid mixing it with "d:/" when a slash is appended. */ *temp = '.'; temp += 2; } temp[-1] = 0; infopath_add (directory_name); } free (directory_name); } /* **************************************************************** */ /* */ /* Main Entry Point to the Info Program */ /* */ /* **************************************************************** */ int main (int argc, char *argv[]) { int getopt_long_index; /* Index returned by getopt_long (). */ char *init_file = 0; /* Name of init file specified. */ char *error = 0; /* Error message to display in mini-buffer. */ #ifdef HAVE_SETLOCALE /* Set locale via LC_ALL. */ setlocale (LC_ALL, ""); #endif /* HAVE_SETLOCALE */ #ifdef ENABLE_NLS /* Set the text message domain. */ bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); #endif init_messages (); while (1) { int option_character; option_character = getopt_long (argc, argv, short_options, long_options, &getopt_long_index); /* getopt_long returns EOF when there are no more long options. */ if (option_character == EOF) break; /* If this is a long option, then get the short version of it. */ if (option_character == 0 && long_options[getopt_long_index].flag == 0) option_character = long_options[getopt_long_index].val; /* Case on the option that we have received. */ switch (option_character) { case 0: break; case 'a': all_matches_p = 1; break; /* User wants to add a directory. */ case 'd': infopath_add (optarg); break; /* User is specifying a particular node. */ case 'n': add_pointer_to_array (optarg, user_nodenames_index, user_nodenames, user_nodenames_slots, 10); break; /* User is specifying a particular Info file. */ case 'f': if (user_filename) free (user_filename); user_filename = xstrdup (optarg); break; /* Treat -h like --help. */ case 'h': print_help_p = 1; break; /* User is specifying the name of a file to output to. */ case 'o': if (user_output_filename) free (user_output_filename); user_output_filename = xstrdup (optarg); break; /* User has specified that she wants to find the "Options" or "Invocation" node for the program. */ case 'O': goto_invocation_p = 1; break; /* User has specified that she wants the escape sequences in man pages to be passed thru unaltered. */ case 'R': raw_escapes_p = 1; break; /* User is specifying that she wishes to dump the subnodes of the node that she is dumping. */ case 's': dump_subnodes = 1; break; /* For compatibility with man, -w is --where. */ case 'w': print_where_p = 1; break; #if defined(__MSDOS__) || defined(__MINGW32__) /* User wants speech-friendly output. */ case 'b': speech_friendly = 1; break; #endif /* __MSDOS__ || __MINGW32__ */ /* User has specified a string to search all indices for. */ case 'k': apropos_p = 1; free (apropos_search_string); apropos_search_string = xstrdup (optarg); break; /* User has specified a dribble file to receive keystrokes. */ case DRIBBLE_OPTION: close_dribble_file (); open_dribble_file (optarg); break; /* User has specified an alternate input stream. */ case RESTORE_OPTION: info_set_input_from_file (optarg); break; /* User has specified a string to search all indices for. */ case IDXSRCH_OPTION: index_search_p = 1; free (index_search_string); index_search_string = xstrdup (optarg); break; /* User has specified a file to use as the init file. */ case INITFLE_OPTION: init_file = optarg; break; case 'v': { VARIABLE_ALIST *var; char *p; p = strchr (optarg, '='); if (!p) { info_error (_("malformed variable assignment: %s"), optarg); exit (EXIT_FAILURE); } *p++ = 0; if (!(var = variable_by_name (optarg))) { info_error (_("%s: no such variable"), optarg); exit (EXIT_FAILURE); } if (!set_variable_to_value (var, p, SET_ON_COMMAND_LINE)) { info_error (_("value %s is not valid for variable %s"), p, optarg); exit (EXIT_FAILURE); } } break; case 'x': set_debug_level (optarg); break; default: fprintf (stderr, _("Try --help for more information.\n")); exit (EXIT_FAILURE); } } /* If the output device is not a terminal, and no output filename has been specified, make user_output_filename be "-", so that the info is written to stdout, and turn on the dumping of subnodes. */ if ((!isatty (fileno (stdout))) && (user_output_filename == NULL)) { user_output_filename = xstrdup ("-"); dump_subnodes = 1; } /* If the user specified --version, then show the version and exit. */ if (print_version_p) { printf ("info (GNU %s) %s\n", PACKAGE, VERSION); puts (""); printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ License GPLv3+: GNU GPL version 3 or later \n\ This is free software: you are free to change and redistribute it.\n\ There is NO WARRANTY, to the extent permitted by law.\n"), "2023"); exit (EXIT_SUCCESS); } /* If the `--help' option was present, show the help and exit. */ if (print_help_p) { info_short_help (); exit (EXIT_SUCCESS); } argc -= optind; argv += optind; /* Load custom key mappings and variable settings */ initialize_terminal_and_keymaps (init_file); /* Add extra search directories to any already specified with --directory. */ infopath_init (); /* If the user wants to search every known index for a given string, do that now, and report the results. */ if (apropos_p) { REFERENCE **apropos_list; apropos_list = apropos_in_all_indices (apropos_search_string, 0); if (!apropos_list) info_error (_(APROPOS_NONE), apropos_search_string); else { register int i; REFERENCE *entry; for (i = 0; (entry = apropos_list[i]); i++) fprintf (stdout, "\"(%s)%s\" -- %s\n", entry->filename, entry->nodename, entry->label); } exit (0); } /* Initialize empty list of nodes to load. */ add_pointer_to_array (0, ref_index, ref_list, ref_slots, 2); ref_index--; /* --all */ if (all_matches_p && !index_search_p) { if (!user_filename && argv[0]) { user_filename = xstrdup (argv[0]); argv++; argc--; } else if (!user_filename) { exit (1); } info_find_matching_files (user_filename); /* If only one match, don't start in a menu of matches. */ if (ref_index == 1) all_matches_p = 0; /* --where */ if (print_where_p) { int i; if (!ref_list) exit (1); for (i = 0; ref_list[i]; i++) printf ("%s\n", ref_list[i]->filename); exit (0); } } else { if (goto_invocation_p) { /* If they said "info --show-options foo bar baz", the last of the arguments is the program whose options they want to see. */ char **p = argv; if (*p) { while (p[1]) p++; invocation_program_name = *p; } } get_initial_file (&argc, &argv, &error); /* If the user specified a particular filename, add the path of that file to the contents of INFOPATH, for '--variable follow-strategy=path'. */ if (user_filename) add_file_directory_to_path (user_filename); /* If the user specified `--index-search=STRING --all', create and display the menu of results. */ if (index_search_p && all_matches_p && initial_file) { FILE_BUFFER *initial_fb; initial_fb = info_find_file (initial_file); if (initial_fb) { NODE *node = create_virtual_index (initial_fb, index_search_string); if (node) { if (user_output_filename) { FILE *output_stream = 0; if (strcmp (user_output_filename, "-") == 0) output_stream = stdout; else output_stream = fopen (user_output_filename, "w"); if (output_stream) { write_node_to_stream (node, output_stream); } exit (0); } else { initialize_info_session (); info_set_node_of_window (active_window, node); info_read_and_dispatch (); close_info_session (); exit (0); } } } } /* If the user specified `--index-search=STRING', start the info session in the node corresponding to what they want. */ else if (index_search_p && initial_file && !user_output_filename) { FILE_BUFFER *initial_fb; initial_fb = info_find_file (initial_file); if (initial_fb) { REFERENCE *result; int i, match_offset; result = next_index_match (initial_fb, index_search_string, 0, 1, &i, &match_offset); if (result) { initialize_info_session (); report_index_match (i, match_offset); info_select_reference (active_window, result); info_read_and_dispatch (); close_info_session (); exit (0); } } fprintf (stderr, _("no index entries found for '%s'\n"), index_search_string); close_dribble_file (); exit (1); } /* Add nodes to start with (unless we fell back to the man page). */ if (!ref_list[0] || strcmp (ref_list[0]->filename, MANPAGE_FILE_BUFFER_NAME)) { add_initial_nodes (argc, argv, &error); } /* --where */ if (print_where_p) { if (initial_file) printf ("%s\n", initial_file); exit (0); } } /* --output */ if (user_output_filename) { preprocess_nodes_p = 0; dump_nodes_to_file (ref_list, user_output_filename, dump_subnodes); if (error) { info_error ("%s", error); exit (1); } exit (0); } if (ref_index == 0) { if (error) { info_error ("%s", error); exit (1); } exit (0); } info_session (ref_list, all_matches_p ? user_filename : 0, error); close_info_session (); exit (0); } /* Produce a scaled down description of the available options to Info. */ static void info_short_help (void) { /* Note: split usage information up into separate strings when usage revised to make it easier for translators. */ printf (_("\ Usage: %s [OPTION]... [MENU-ITEM...]\n\ \n\ Read documentation in Info format.\n"), program_name); puts (""); puts (_("\ Frequently-used options:\n\ -a, --all use all matching manuals\n\ -k, --apropos=STRING look up STRING in all indices of all manuals\n\ -d, --directory=DIR add DIR to INFOPATH\n\ -f, --file=MANUAL specify Info manual to visit")); puts (_("\ -h, --help display this help and exit\n\ --index-search=STRING go to node pointed by index entry STRING\n\ -n, --node=NODENAME specify nodes in first visited Info file\n\ -o, --output=FILE output selected nodes to FILE")); #if defined(__MSDOS__) || defined(__MINGW32__) puts (_("\ -b, --speech-friendly be friendly to speech synthesizers")); #endif puts (_("\ --subnodes recursively output menu items\n\ -v, --variable VAR=VALUE assign VALUE to Info variable VAR\n\ --version display version information and exit\n\ -w, --where, --location print physical location of Info file")); puts (_("\n\ The first non-option argument, if present, is the menu entry to start from;\n\ it is searched for in all 'dir' files along INFOPATH.\n\ If it is not present, info merges all 'dir' files and shows the result.\n\ Any remaining arguments are treated as the names of menu\n\ items relative to the initial node visited.")); puts (_("\n\ For a summary of key bindings, type H within Info.")); puts (""); puts (_("\ Examples:")); puts (_("\ info show top-level dir menu")); puts (_("\ info info-stnd show the manual for this Info program")); puts (_("\ info emacs start at emacs node from top-level dir")); puts (_("\ info emacs buffers select buffers menu entry in emacs manual")); puts (_("\ info emacs -n Files start at Files node within emacs manual")); puts (_("\ info '(emacs)Files' alternative way to start at Files node")); puts (_("\ info --subnodes -o out.txt emacs\n\ dump entire emacs manual to out.txt")); puts (_("\ info -f ./foo.info show file ./foo.info, not searching dir")); puts (""); puts (_("\ Email bug reports to bug-texinfo@gnu.org,\n\ general questions and discussion to help-texinfo@gnu.org.\n\ Texinfo home page: http://www.gnu.org/software/texinfo/")); exit (EXIT_SUCCESS); } /* Initialize strings for gettext. Because gettext doesn't handle N_ or _ within macro definitions, we put shared messages into variables and use them that way. This also has the advantage that there's only one copy of the strings. */ const char *msg_cant_find_node; const char *msg_cant_file_node; const char *msg_cant_find_window; const char *msg_cant_find_point; const char *msg_cant_kill_last; const char *msg_no_menu_node; const char *msg_no_foot_node; const char *msg_no_xref_node; const char *msg_no_pointer; const char *msg_unknown_command; const char *msg_term_too_dumb; const char *msg_at_node_bottom; const char *msg_at_node_top; const char *msg_one_window; const char *msg_win_too_small; const char *msg_cant_make_help; static void init_messages (void) { msg_cant_find_node = _("Cannot find node '%s'"); msg_cant_file_node = _("Cannot find node '(%s)%s'"); msg_cant_find_window = _("Cannot find a window!"); msg_cant_find_point = _("Point doesn't appear within this window's node!"); msg_cant_kill_last = _("Cannot delete the last window"); msg_no_menu_node = _("No menu in this node"); msg_no_foot_node = _("No footnotes in this node"); msg_no_xref_node = _("No cross references in this node"); msg_no_pointer = _("No '%s' pointer for this node"); msg_unknown_command = _("Unknown Info command '%c'; try '?' for help"); msg_term_too_dumb = _("Terminal type '%s' is not smart enough to run Info"); msg_at_node_bottom = _("You are already at the last page of this node"); msg_at_node_top = _("You are already at the first page of this node"); msg_one_window = _("Only one window"); msg_win_too_small = _("Resulting window would be too small"); msg_cant_make_help = _("Not enough room for a help window, please delete a window"); }