#! /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