#! /usr/bin/perl # texi2any: Texinfo converter. # # Copyright 2010-2024 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 . # # Original author: Patrice Dumas # Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init. use 5.006; use strict; use warnings; # Through rules in Makefile.am, directory paths set through configure are # substituted directly in strings in the code, for example # my $datadir = '/usr/share'; # We always use these strings as byte string, therefore we explicitly # set no utf8 to be sure that strings in code will never be considered as # character strings by Perl. no utf8; # to decode command line arguments use Encode qw(decode encode find_encoding); # for file names portability use File::Spec; # to determine the path separator and null file use Config; # for dirname and fileparse use File::Basename; #use Cwd; use Getopt::Long qw(GetOptions); # for carp #use Carp; # for dclone use Storable; use Data::Dumper; Getopt::Long::Configure("gnu_getopt"); my ($real_command_name, $command_directory, $command_suffix); # This big BEGIN block deals with finding modules and # some dependencies that we ship # * in source or # * installed or # * installed relative to the script BEGIN { ($real_command_name, $command_directory, $command_suffix) = fileparse($0, '.pl'); my $updir = File::Spec->updir(); # These are substituted by the Makefile to create "texi2any". my $datadir = '/usr/share'; my $converter = 'texi2any'; my $libdir = '/usr/lib'; my $xsdir; if ($datadir eq '@' .'datadir@' or defined($ENV{'TEXINFO_DEV_SOURCE'}) and $ENV{'TEXINFO_DEV_SOURCE'} ne '0') { # Use uninstalled modules # To find Texinfo::ModulePath if (defined($ENV{'top_builddir'})) { unshift @INC, join('/', ($ENV{'top_builddir'}, 'tp')); } else { unshift @INC, $command_directory; } require Texinfo::ModulePath; Texinfo::ModulePath::init(undef, undef, undef, 'updirs' => 1); } else { # Look for modules in their installed locations. my $modules_dir = join('/', ($datadir, $converter)); # look for package data in the installed location. # actually the same as $converterdatadir in main program below, but use # another name to avoid confusion. my $modules_converterdatadir = $modules_dir; $xsdir = join('/', ($libdir, $converter)); # try to make package relocatable, will only work if # standard relative paths are used if (! -f join('/', ($modules_dir, 'Texinfo', 'Parser.pm')) and -f join('/', ($command_directory, $updir, 'share', $converter, 'Texinfo', 'Parser.pm'))) { $modules_dir = join('/', ($command_directory, $updir, 'share', $converter)); $modules_converterdatadir = join('/', ($command_directory, $updir, 'share', $converter)); $xsdir = join('/', ($command_directory, $updir, 'lib', $converter)); } unshift @INC, $modules_dir; require Texinfo::ModulePath; Texinfo::ModulePath::init($modules_dir, $xsdir, $modules_converterdatadir, 'installed' => 1); } } # end BEGIN # This allows disabling use of XS modules when Texinfo is built. BEGIN { my $enable_xs = 'yes'; if ($enable_xs eq 'no') { package Texinfo::XSLoader; our $disable_XS; $disable_XS = 1; } } use Texinfo::XSLoader; use Locale::Messages; use Texinfo::Options; use Texinfo::Common; use Texinfo::Config; use Texinfo::Report; # determine the path separators my $path_separator = $Config{'path_sep'}; $path_separator = ':' if (!defined($path_separator)); my $quoted_path_separator = quotemeta($path_separator); # Paths and file names my $curdir = File::Spec->curdir(); my $updir = File::Spec->updir(); # set by configure, prefix for the sysconfdir and so on # This could be used in the eval my $prefix = '/usr'; my $datadir; my $datarootdir; my $sysconfdir; #my $pkgdatadir; my $converter; # the result is not good when using rootdir, maybe using a concatenation # of rootdir would be better. #my $fallback_prefix = join('/', (File::Spec->rootdir(), 'usr', 'local')); my $fallback_prefix = File::Spec->catdir(File::Spec->rootdir(), 'usr', 'local'); # We need to eval as $prefix has to be expanded. However when we haven't # run configure @sysconfdir will be expanded as an array, thus we verify # whether configure was run or not if ('/usr/etc' ne '@' . 'sysconfdir@') { $sysconfdir = eval '"/usr/etc"'; } else { $sysconfdir = "$fallback_prefix/etc"; } if ('/usr/share' ne '@' . 'datarootdir@') { $datarootdir = eval '"/usr/share"'; } else { $datarootdir = "$fallback_prefix/share"; } if ('/usr/share' ne '@' . 'datadir@' and 'texinfo' ne '@' . 'PACKAGE@') { $datadir = eval '"/usr/share"'; my $package = 'texinfo'; $converter = 'texi2any'; } else { $datadir = "$fallback_prefix/share"; $converter = 'texi2any'; } my $extensions_dir; if ($Texinfo::ModulePath::texinfo_uninstalled) { $extensions_dir = join('/', ($Texinfo::ModulePath::top_srcdir, 'tp', 'ext')); } else { $extensions_dir = join('/', ($Texinfo::ModulePath::converterdatadir, 'ext')); } my $internal_extension_dirs = [$extensions_dir]; # initial setup of messages internalisation framework # work-around in case libintl-perl do not do it itself # see http://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable if ((defined($ENV{"LC_ALL"}) and $ENV{"LC_ALL"} =~ /^(C|POSIX)$/) or (defined($ENV{"LANG"}) and $ENV{"LANG"} =~ /^(C|POSIX)$/)) { delete $ENV{"LANGUAGE"} if defined($ENV{"LANGUAGE"}); } #my $messages_textdomain = 'texinfo'; my $messages_textdomain = 'texinfo'; $messages_textdomain = 'texinfo' if ($messages_textdomain eq '@'.'PACKAGE@'); my $strings_textdomain = 'texinfo' . '_document'; $strings_textdomain = 'texinfo_document' if ($strings_textdomain eq '@'.'PACKAGE@' . '_document'); # we want a reliable way to switch locale, so we don't use the system # gettext. Locale::Messages->select_package('gettext_pp'); # Note: this uses installed messages even when the program is uninstalled Locale::Messages::bindtextdomain($messages_textdomain, File::Spec->catdir($datadir, 'locale')); # Set initial configuration # Version setting is complicated, because we cope with # * script with configure values substituted or not # * script shipped as part of texinfo or as a standalone perl module # (although standalone module infrastructure was removed in 2019) # When the script could be shipped with perl modules independently from # the remaining of Texinfo, $hardcoded_version was set to undef here # by a sed one liner. The consequence is that configure.ac is not used # to retrieve the version number, version came from Texinfo::Common in that # case. # Otherwise this is only used as a safety value, and should never be used # in practice as a regexp extracts the version from configure.ac. my $hardcoded_version = "0.00-hardcoded"; # Version set in configure.ac my $configured_version = '7.2'; if ($configured_version eq '@' . 'PACKAGE_VERSION@') { # if not configured, and $hardcoded_version is set search for the version # in configure.ac if (defined($hardcoded_version)) { if (open(CONFIGURE, "< ".File::Spec->catfile($Texinfo::ModulePath::top_srcdir, 'configure.ac'))) { while () { if (/^AC_INIT\(\[[^\]]+\]\s*,\s*\[([^\]]+)\]\s*,/) { $configured_version = "$1+dev"; # +dev to distinguish from installed last; } } close (CONFIGURE); } # This should never be used, but is a safety value $configured_version = $hardcoded_version if (!defined($configured_version)); } else { # was used in the standalone perl module, as $hardcoded_version is undef # and it should never be configured in that setup. require Texinfo::Common; $configured_version = $Texinfo::Common::VERSION; } } # Compare the version of this file with the version of the modules # it is using. If they are different, don't go any further. This # can happen if multiple versions of texi2any are installed under a # different names, e.g. with the --program-suffix option to 'configure'. # The version in Common.pm is checked because that file has been present # since Texinfo 5.0 (the first release with texi2any in Perl). if ($configured_version ne $Texinfo::Common::VERSION and $configured_version ne $Texinfo::Common::VERSION."+dev") { warn "This is texi2any $configured_version but modules ". "for texi2any $Texinfo::Common::VERSION found!\n"; die "Your installation of Texinfo is broken; aborting.\n"; } my $configured_package = 'texinfo'; $configured_package = 'texinfo' if ($configured_package eq '@' . 'PACKAGE@'); my $configured_name = 'GNU Texinfo'; $configured_name = 'GNU Texinfo' if ($configured_name eq '@' .'PACKAGE_NAME@'); my $configured_name_version = "$configured_name $configured_version"; my $configured_url = 'https://www.gnu.org/software/texinfo/'; $configured_url = 'https://www.gnu.org/software/texinfo/' if ($configured_url eq '@' .'PACKAGE_URL@'); my $texinfo_dtd_version = '7.2'; # $hardcoded_version is undef for a standalone perl module (standalone # infrastructure was removed in 2019). The code is kept in case it becomes # useful again, for a standalone perl module or in some specific cases. if ($texinfo_dtd_version eq '@' . 'TEXINFO_DTD_VERSION@') { $texinfo_dtd_version = undef; if (defined($hardcoded_version)) { if (open(CONFIGURE, "< ".File::Spec->catfile($Texinfo::ModulePath::top_srcdir, 'configure.ac'))) { while () { if (/^TEXINFO_DTD_VERSION=([0-9]\S*)/) { $texinfo_dtd_version = "$1"; last; } } close (CONFIGURE); } } } # the encoding used to decode command line arguments, and also for # file names encoding, perl is expecting sequences of bytes, not unicode # code points. my $locale_encoding; eval 'require I18N::Langinfo'; if (!$@) { $locale_encoding = I18N::Langinfo::langinfo(I18N::Langinfo::CODESET()); $locale_encoding = undef if ($locale_encoding eq ''); } if (!defined($locale_encoding) and $^O eq 'MSWin32') { eval 'require Win32::API'; if (!$@) { Win32::API::More->Import("kernel32", "int GetACP()"); my $CP = GetACP(); if (defined($CP)) { $locale_encoding = 'cp'.$CP; } } } # Used in case it is not hardcoded in configure and for standalone perl module $texinfo_dtd_version = $configured_version if (!defined($texinfo_dtd_version)); my $configured_information = { 'PACKAGE_VERSION' => $configured_version, 'PACKAGE' => $configured_package, 'PACKAGE_NAME' => $configured_name, 'PACKAGE_AND_VERSION' => $configured_name_version, 'PACKAGE_URL' => $configured_url, }; # options set in the main program. my $main_program_set_options = { 'PROGRAM' => $real_command_name, 'TEXINFO_DTD_VERSION' => $texinfo_dtd_version, 'COMMAND_LINE_ENCODING' => $locale_encoding, 'MESSAGE_ENCODING' => $locale_encoding, 'LOCALE_ENCODING' => $locale_encoding, # better than making it the default value independently of the implementation 'TEXINFO_OUTPUT_FORMAT' => 'info', }; # set configure information as constants foreach my $configured_variable (keys(%$configured_information)) { Texinfo::Common::set_build_constant($configured_variable, $configured_information->{$configured_variable}); # set also with _CONFIG prepended, as in C code. Texinfo::Common::set_build_constant($configured_variable.'_CONFIG', $configured_information->{$configured_variable}); } foreach my $configured_variable (keys(%$configured_information)) { $main_program_set_options->{$configured_variable} = $configured_information->{$configured_variable}; } # In Windows, a character in file name is encoded according to the current # codepage, and converted to/from UTF-16 in the filesystem. If a file name is # not encoded in the current codepage, the file name will appear with erroneous # characters when listing file names. Also the encoding and decoding to # UTF-16 may fail, especially when the codepage is 8bit while the file name # is encoded in a multibyte encoding. # We assume that in Windows the file names are reencoded in the current # codepage encoding to avoid those issues. if ($^O eq 'MSWin32') { $main_program_set_options->{'DOC_ENCODING_FOR_INPUT_FILE_NAME'} = 0; } # defaults for options relevant in the main program. Also used as # defaults for all the converters. my $main_program_default_options = { %$main_program_set_options, %Texinfo::Common::default_main_program_customization_options, }; # determine configuration directories. # used as part of binary strings my $conf_file_name = 'texi2any-config.pm'; # When we replace a directory, we emit a warning for some time, # using %deprecated_directories to match to the directory that # should be used. # In 2024 we switched to using the XDG Base Directory Specification, # https://specifications.freedesktop.org/basedir-spec/latest/index.html # $HOME/texinfo should be $XDG_CONFIG_HOME default: $HOME/.config/texinfo my %deprecated_directories; # We use first the installation directory, and then the environment variable # directories. sub add_config_paths($$$$;$$) { my $env_string = shift; my $subdir = shift; my $default_base_dirs = shift; my $installation_dir = shift; my $overriding_dirs = shift; my $deprecated_dirs = shift; # read the env directories to avoid setting the overriding_dirs # as deprecated if they are explicitely specified in the environnement # variable. my @xdg_result_dirs; my %used_xdg_base_dirs; if (defined($ENV{$env_string}) and $ENV{$env_string} ne '') { foreach my $dir (split(':', $ENV{$env_string})) { if ($dir ne '') { push @xdg_result_dirs, $dir; $used_xdg_base_dirs{$dir} = 1; } } } my @result_dirs; my %used_base_dirs; if (defined($installation_dir)) { my $install_result_dir = "$installation_dir/$subdir"; push @result_dirs, $install_result_dir; $used_base_dirs{$installation_dir} = 1; if ($overriding_dirs and $overriding_dirs->{$installation_dir}) { my $deprecated_dir = $overriding_dirs->{$installation_dir}; my $deprecated_result_dir = "$deprecated_dir/$subdir"; if (not $used_xdg_base_dirs{$deprecated_dir}) { $deprecated_dirs->{$deprecated_result_dir} = $install_result_dir; push @result_dirs, $deprecated_result_dir; $used_base_dirs{$deprecated_dir} = 1; } } } foreach my $dir (@xdg_result_dirs) { if (!$used_base_dirs{$dir}) { push @result_dirs, "$dir/$subdir"; $used_base_dirs{$dir} = 1; } } # to also use XDG Base Directory Specification defaults #foreach my $dir (@$default_base_dirs) { # if (!$used_base_dirs{$dir}) { # push @result_dirs, "$dir/$subdir"; # } #} return \@result_dirs; } sub set_subdir_directories($$) { my $subdir = shift; my $deprecated_dirs = shift; my @result = (".$subdir"); my $config_home; my $deprecated_config_home; if (defined($ENV{'XDG_CONFIG_HOME'}) and $ENV{'XDG_CONFIG_HOME'} ne '') { $config_home = $ENV{'XDG_CONFIG_HOME'}."/$subdir"; } else { if (defined($ENV{'HOME'})) { $config_home = join('/', ($ENV{'HOME'}, '.config', $subdir)); $deprecated_config_home = $ENV{'HOME'}.'/.'.$subdir; $deprecated_dirs->{$deprecated_config_home} = $config_home; } } push @result, $config_home if (defined($config_home)); push @result, $deprecated_config_home if (defined($deprecated_config_home)); my $sysconf_install_dir = "$sysconfdir/xdg"; # associate new location to deprecated location my $overriding_dirs = {$sysconf_install_dir => $sysconfdir}; # in 2024, mark $sysconfdir overriden by $sysconfdir/xdg. my $config_dirs = add_config_paths('XDG_CONFIG_DIRS', $subdir, ['/etc/xdg'], "$sysconfdir/xdg", $overriding_dirs, $deprecated_dirs); push @result, @$config_dirs; # the following code could have been used to use XDG_DATA_DIRS for # datadir directories and files too #my $data_dirs = add_config_paths('XDG_DATA_DIRS', $subdir, # ['/usr/local/share/', '/usr/share/'], $datadir); #push @result, @$data_dirs; # Do not use XDG base specification for directories and files in # datadir, there is no need for customization of those directories # since the sysconfdir directories are already customized, just use # the installation directory. push @result, "$datadir/$subdir"; return \@result; } # directories for Texinfo configuration files, as far as possible # implementation independent. Used as part of binary strings. # curdir and the input file path directory are prepended later on. my $language_config_dirs = set_subdir_directories('texinfo', \%deprecated_directories); my @texinfo_language_config_dirs = @$language_config_dirs; #push @texinfo_language_config_dirs, "$sysconfdir/texinfo" # if (defined($sysconfdir)); #push @texinfo_language_config_dirs, "$datadir/texinfo" # if (defined($datadir)); # these variables are used as part of binary strings. my @converter_config_dirs; my @converter_init_dirs; my $converter_config_dirs_array_ref = set_subdir_directories($converter, \%deprecated_directories); @converter_config_dirs = ($curdir, @$converter_config_dirs_array_ref); #@converter_config_dirs # = ($curdir, "$curdir/.$converter"); #push @converter_config_dirs, $ENV{'HOME'}."/.$converter") # if (defined($ENV{'HOME'})); #push @converter_config_dirs, "$sysconfdir/$converter" # if (defined($sysconfdir)); #push @converter_config_dirs, "$datadir/$converter" # if (defined($datadir)); @converter_init_dirs = @converter_config_dirs; foreach my $texinfo_config_dir (@texinfo_language_config_dirs) { my $init_dir = "$texinfo_config_dir/init"; push @converter_init_dirs, $init_dir; if ($deprecated_directories{$texinfo_config_dir}) { $deprecated_directories{$init_dir} = "$deprecated_directories{$texinfo_config_dir}/init"; } } # add texi2any extensions dir too, such as the init files there # can also be loaded as regular init files. push @converter_init_dirs, $extensions_dir; #print STDERR join("\n", @converter_init_dirs)."\n\n"; #print STDERR join("\n", sort(keys(%deprecated_directories)))."\n"; sub _decode_i18n_string($$) { my $string = shift; my $encoding = shift; return decode($encoding, $string); } sub _encode_message($) { my $text = shift; my $encoding = get_conf('MESSAGE_ENCODING'); if (defined($encoding)) { return encode($encoding, $text); } else { return $text; } } sub document_warn($) { return if (get_conf('NO_WARN')); my $text = shift; chomp ($text); warn(_encode_message( sprintf(__p("program name: warning: warning_message", "%s: warning: %s")."\n", $real_command_name, $text))); } sub _decode_input($) { my $text = shift; my $encoding = get_conf('COMMAND_LINE_ENCODING'); if (defined($encoding)) { return decode($encoding, $text); } else { return $text; } } sub _warn_deprecated_dirs($$) { my $deprecated_dirs = shift; my $deprecated_dirs_used = shift; if (defined($deprecated_dirs_used)) { foreach my $dir (@$deprecated_dirs_used) { my $dir_name = _decode_input($dir); my $replacement_dir = _decode_input($deprecated_dirs->{$dir}); document_warn(sprintf(__( "%s directory is deprecated. Use %s instead"), $dir_name, $replacement_dir)); } } } # arguments are binary strings. sub locate_and_load_init_file($$;$) { my $filename = shift; my $directories = shift; my $deprecated_dirs = shift; my ($files, $deprecated_dirs_used) = Texinfo::Common::locate_file_in_dirs($filename, $directories, 0, $deprecated_dirs); if (defined($files)) { my $file = $files->[0]; # evaluate the code in the Texinfo::Config namespace Texinfo::Config::GNUT_load_init_file($file); } else { document_warn(sprintf(__("could not read init file %s"), _decode_input($filename))); } if ($deprecated_dirs and $deprecated_dirs_used) { _warn_deprecated_dirs($deprecated_dirs, $deprecated_dirs_used); } } # arguments are binary strings. # Init files that are used in texi2any, considered # as internal extensions code. sub locate_and_load_extension_file($$) { my $filename = shift; my $directories = shift; # no possible deprecated dirs with the path passed to this sub my ($files, $deprecated_dirs_used) = Texinfo::Common::locate_file_in_dirs($filename, $directories, 0); if (defined($files)) { # evaluate the code in the Texinfo::Config namespace my $file = $files->[0]; Texinfo::Config::GNUT_load_init_file($file); } else { die _encode_message(sprintf(__("could not read extension file %s"), _decode_input($filename))); } } sub set_from_cmdline($$) { return &Texinfo::Config::GNUT_set_from_cmdline(@_); } sub set_main_program_default($$) { return &Texinfo::Config::GNUT_set_customization_default(@_); } sub get_conf($) { return &Texinfo::Config::texinfo_get_conf(@_); } sub add_to_option_list($$) { return &Texinfo::Config::texinfo_add_to_option_list(@_); } sub remove_from_option_list($$) { return &Texinfo::Config::texinfo_remove_from_option_list(@_); } sub set_translations_encoding($) { my $translations_encoding = shift; if (defined($translations_encoding) and $translations_encoding ne 'us-ascii') { my $Encode_encoding_object = find_encoding($translations_encoding); my $perl_translations_encoding = $Encode_encoding_object->name(); Locale::Messages::bind_textdomain_codeset($messages_textdomain, $translations_encoding); if (defined($perl_translations_encoding)) { Locale::Messages::bind_textdomain_filter($messages_textdomain, \&_decode_i18n_string, $perl_translations_encoding); } } } # Setup customization and read customization files processed each time # the program is run # this associates the command line options to the arrays set during # command line parsing. my @css_files = (); my @css_refs = (); my @include_dirs = (); my @expanded_formats = (); # note that CSS_FILES and INCLUDE_DIRECTORIES are not decoded when # read from the command line and should be binary strings. # TEXINFO_LANGUAGE_DIRECTORIES is not actually read from the command # line, but it is still best to have it here, and it should also # contain binary strings. my $cmdline_options = { 'CSS_FILES' => \@css_files, 'CSS_REFS' => \@css_refs, 'INCLUDE_DIRECTORIES' => \@include_dirs, 'TEXINFO_LANGUAGE_DIRECTORIES' => \@texinfo_language_config_dirs, 'EXPANDED_FORMATS' => \@expanded_formats }; my @conf_dirs = (); my @prepend_dirs = (); # The $cmdline_options passed to Texinfo::Config::GNUT_initialize_customization # are considered to be arrays in which items can be added or deleted both # from the command line and from init files. $cmdline_options text values # are set by GNUT_set_from_cmdline (aliased as set_from_cmdline) from the # main program. $cmdline_options are also accessed in main program. # $init_files_options are managed by Texinfo::Config, set by # texinfo_set_from_init_file in init files. # # There is in addition $parser_options for parser related information # that is not gathered otherwise. # The configuration values are later on copied over to the parser if # they are parser options. my $parser_options = {'values' => {'txicommandconditionals' => 1}}; my $init_files_options = Texinfo::Config::GNUT_initialize_customization( $real_command_name, $main_program_default_options, $cmdline_options); # Need to do that early for early messages my $translations_encoding = get_conf('COMMAND_LINE_ENCODING'); set_translations_encoding($translations_encoding); # read initialization files. Better to do that after # Texinfo::Config::GNUT_initialize_customization() in case loaded # files replace default options. my ($config_init_files, $deprecated_dirs_for_config_init) = Texinfo::Common::locate_file_in_dirs($conf_file_name, [ reverse(@converter_config_dirs) ], 1, \%deprecated_directories); if (defined($config_init_files)) { foreach my $file (@$config_init_files) { Texinfo::Config::GNUT_load_init_file($file); } } if ($deprecated_dirs_for_config_init) { _warn_deprecated_dirs(\%deprecated_directories, $deprecated_dirs_for_config_init); } # reset translations encodings if COMMAND_LINE_ENCODING was reset my $set_translations_encoding = get_conf('COMMAND_LINE_ENCODING'); if (defined($set_translations_encoding) and (not defined($translations_encoding) or $set_translations_encoding ne $translations_encoding)) { $translations_encoding = $set_translations_encoding; set_translations_encoding($translations_encoding); } # Parse command line my %ignored_formats; sub set_expansion($$) { my $region = shift; my $set = shift; $set = 1 if (!defined($set)); if ($set) { add_to_option_list('EXPANDED_FORMATS', [$region]); delete $ignored_formats{$region}; } else { remove_from_option_list('EXPANDED_FORMATS', [$region]); $ignored_formats{$region} = 1; } } my %possible_split = ( 'chapter' => 1, 'section' => 1, 'node' => 1, ); my $format_from_command_line = 0; my %converter_format_expanded_region_name = ( 'texinfoxml' => 'xml', ); my %format_command_line_names = ( 'xml' => 'texinfoxml', ); my %formats_table = ( 'info' => { 'nodes_tree' => 1, 'floats' => 1, 'setup_index_entries_sort_strings' => 1, 'module' => 'Texinfo::Convert::Info' }, 'plaintext' => { 'nodes_tree' => 1, 'floats' => 1, 'split' => 1, 'setup_index_entries_sort_strings' => 1, 'module' => 'Texinfo::Convert::Plaintext' }, 'html' => { 'nodes_tree' => 1, 'floats' => 1, 'split' => 1, 'internal_links' => 1, 'move_index_entries_after_items' => 1, 'relate_index_entries_to_table_items' => 1, 'no_warn_non_empty_parts' => 1, 'setup_index_entries_sort_strings' => 1, 'module' => 'Texinfo::Convert::HTML' }, 'latex' => { 'floats' => 1, 'internal_links' => 1, 'move_index_entries_after_items' => 1, 'no_warn_non_empty_parts' => 1, 'module' => 'Texinfo::Convert::LaTeX' }, 'texinfoxml' => { 'nodes_tree' => 1, 'module' => 'Texinfo::Convert::TexinfoXML', 'floats' => 1, }, 'texinfosxml' => { 'nodes_tree' => 1, 'module' => 'Texinfo::Convert::TexinfoSXML', 'floats' => 1, }, 'ixinsxml' => { # note that the Texinfo tree is converted to # 'texinfosxml', but the conversion as a whole # is 'ixinsxml', as Texinfo tree conversion is done # from within Texinfo::Convert::IXINSXML 'nodes_tree' => 1, 'setup_index_entries_sort_strings' => 1, 'module' => 'Texinfo::Convert::IXINSXML', 'floats' => 1, }, 'docbook' => { 'move_index_entries_after_items' => 1, 'no_warn_non_empty_parts' => 1, 'module' => 'Texinfo::Convert::DocBook' }, 'epub3' => { 'converted_format' => 'html', 'init_file' => 'epub3.pm', }, 'pdf' => { 'texi2dvi_format' => 1, }, 'ps' => { 'texi2dvi_format' => 1, }, 'dvi' => { 'texi2dvi_format' => 1, }, 'dvipdf' => { 'texi2dvi_format' => 1, }, 'debugtree' => { 'split' => 1, 'module' => 'Texinfo::DebugTree' }, 'textcontent' => { 'module' => 'Texinfo::Convert::TextContent' }, 'rawtext' => { 'module' => 'Texinfo::Convert::Text' }, 'plaintexinfo' => { 'module' => 'Texinfo::Convert::PlainTexinfo' }, 'parse' => { }, 'structure' => { 'nodes_tree' => 1, 'floats' => 1, 'split' => 1, }, ); my $call_texi2dvi = 0; my @texi2dvi_args = (); sub set_cmdline_format($) { my $set_format = shift; set_from_cmdline('TEXINFO_OUTPUT_FORMAT', $set_format); } sub set_format($) { my $set_format = shift; my $new_output_format; if ($format_command_line_names{$set_format}) { $new_output_format = $format_command_line_names{$set_format}; } else { $new_output_format = $set_format; } if (!$formats_table{$new_output_format}) { document_warn(sprintf(__( "ignoring unrecognized TEXINFO_OUTPUT_FORMAT value `%s'\n"), $set_format)); } else { Texinfo::Config::texinfo_set_from_init_file('TEXINFO_OUTPUT_FORMAT', $new_output_format); } } sub _format_expanded_formats($) { my $new_output_format = shift; my $default_expanded_formats = {}; my $converter_format; my $expanded_region; if ($formats_table{$new_output_format}->{'texi2dvi_format'}) { $call_texi2dvi = 1; push @texi2dvi_args, '--'.$new_output_format; $converter_format = 'tex'; } elsif ($formats_table{$new_output_format}->{'converted_format'}) { $converter_format = $formats_table{$new_output_format}->{'converted_format'}; } else { $converter_format = $new_output_format; } if ($converter_format_expanded_region_name{$converter_format}) { $expanded_region = $converter_format_expanded_region_name{$converter_format}; } else { $expanded_region = $converter_format; } if ($Texinfo::Common::texinfo_output_formats{$expanded_region}) { if ($expanded_region eq 'plaintext') { $default_expanded_formats = {$expanded_region => 1, 'info' => 1}; } else { $default_expanded_formats = {$expanded_region => 1}; } } return $default_expanded_formats; } sub _get_converter_default($) { my $option = shift; if (defined($Texinfo::Options::converter_cmdline_options{$option})) { return $Texinfo::Options::converter_cmdline_options{$option}; } elsif (defined($Texinfo::Options::multiple_at_command_options{$option})) { return $Texinfo::Options::multiple_at_command_options{$option}; } return undef; } # translation related todo to be done when the string change anyway to # avoid requiring translation sub makeinfo_help() { # TODO: avoid \n in translated strings. Report from Benno Schulenberg my $makeinfo_help = sprintf(__("Usage: %s [OPTION]... TEXINFO-FILE...\n"), $real_command_name . $command_suffix) ."\n". __("Translate Texinfo source documentation to various other formats, by default Info files suitable for reading online with Emacs or standalone GNU Info. This program is commonly installed as both `makeinfo' and `texi2any'; the behavior is identical, and does not depend on the installed name.\n") ."\n"; $makeinfo_help .= __("General options:")."\n" .__(" --document-language=STR locale to use in translating Texinfo keywords for the output document (default C).")."\n" .sprintf(__(" --error-limit=NUM quit after NUM errors (default %d)."), get_conf('ERROR_LIMIT'))."\n" .__(" --force preserve output even if errors.")."\n" .__(" --help display this help and exit.")."\n" .__(" --no-validate suppress node cross-reference validation.")."\n" .__(" --no-warn suppress warnings (but not errors).")."\n" .__(" --conf-dir=DIR search also for initialization files in DIR.")."\n" .__(" --init-file=FILE load FILE to modify the default behavior.")."\n" .__(" -c, --set-customization-variable VAR=VAL set customization variable VAR to value VAL.")."\n" .__(" --trace-includes print names of included files.")."\n" .__(" -v, --verbose explain what is being done.")."\n" .__(" --version display version information and exit.")."\n" ."\n"; $makeinfo_help .= __("Output format selection (default is to produce Info):")."\n" .__(" --docbook output Docbook XML.")."\n" .__(" --html output HTML.")."\n" .__(" --epub3 output EPUB 3.")."\n" .__(" --latex output LaTeX.")."\n" .__(" --plaintext output plain text rather than Info.")."\n" .__(" --xml output Texinfo XML.")."\n" .__(" --dvi, --dvipdf, --ps, --pdf call texi2dvi to generate given output, after checking validity of TEXINFO-FILE.")."\n" ."\n"; $makeinfo_help .= __("General output options:")."\n" .__( " -E, --macro-expand=FILE output macro-expanded source to FILE, ignoring any \@setfilename.")."\n" .__( " --no-headers suppress node separators, Node: lines, and menus from Info output (thus producing plain text) or from HTML (thus producing shorter output). Also, if producing Info, write to standard output by default.")."\n" .__( " --no-split suppress any splitting of the output; generate only one output file.")."\n" .__( " --[no-]number-sections output chapter and sectioning numbers; default is on.")."\n" .__( " -o, --output=DEST output to DEST. With split output, create DEST as a directory and put the output files there. With non-split output, if DEST is already a directory or ends with a /, put the output file there. Otherwise, DEST names the output file.")."\n" .__( " --disable-encoding do not output accented and special characters in Info and plain text output based on document encoding.")."\n" .__( " --enable-encoding override --disable-encoding (default).")."\n" ."\n"; $makeinfo_help .= sprintf(__("Options for Info and plain text:")."\n" .__( " --fill-column=NUM break Info lines at NUM columns (default %d).")."\n" .__( " --footnote-style=STYLE output footnotes in Info according to STYLE: `separate' to put them in their own node; `end' to put them at the end of the node, in which they are defined (this is the default).")."\n" .__( " --paragraph-indent=VAL indent Info paragraphs by VAL spaces (default %d). If VAL is `none', do not indent; if VAL is `asis', preserve existing indentation.")."\n" .__( " --split-size=NUM split Info files at size NUM (default %d).")."\n" ."\n", _get_converter_default('FILLCOLUMN'), _get_converter_default('paragraphindent'), _get_converter_default('SPLIT_SIZE')) ."\n"; # TODO: avoid \n in translated strings, split each option in a translatable # string. Report from Benno Schulenberg $makeinfo_help .= __("Options for HTML: --css-include=FILE include FILE in HTML