# Config.pm: namespace used for user configuration (init files) evaluation
#
# Copyright 2010-2026 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
#
# functions that should not be called by user init files codes, but
# are called by the main program or tests are prefixed by GNUT_ while
# functions that can be called by user init files codes are prefixed
# by texinfo_.
# Functions prefixed by _GNUT_ are private.
#
# This package is documented in the texi2any_api Texinfo manual. This
# package is integrated with the Texinfo main program and the Texinfo
# HTML converter, such that it does not make sense to document the
# public interface separately.
#
# The main calling context can be the main program or the test suite code,
# it is distinguished from initialization user-defined files calling context.
package Texinfo::Config;
use strict;
# To check if there is no erroneous autovivification
#no autovivification qw(fetch delete exists store strict);
use Carp qw(carp confess);
# for Encode::encode
use Encode;
# for __( and __p( and some functions
use Texinfo::Common;
use Texinfo::ConfigXS;
my %XS_overrides = (
"Texinfo::Config::texinfo_get_conf"
=> "Texinfo::ConfigXS::texinfo_get_conf",
"Texinfo::Config::texinfo_add_to_option_list"
=> "Texinfo::ConfigXS::texinfo_add_to_option_list",
"Texinfo::Config::texinfo_remove_from_option_list"
=> "Texinfo::ConfigXS::texinfo_remove_from_option_list",
"Texinfo::Config::texinfo_set_from_init_file"
=> "Texinfo::ConfigXS::texinfo_set_from_init_file",
"Texinfo::Config::texinfo_set_format_from_init_file"
=> "Texinfo::ConfigXS::texinfo_set_format_from_init_file",
);
our $module_loaded = 0;
sub import {
if (!$module_loaded) {
# override only if Perl is embedded
if ($Texinfo::XSLoader::embedded_xs) {
foreach my $sub (keys(%XS_overrides)) {
Texinfo::XSLoader::override($sub, $XS_overrides{$sub});
}
}
$module_loaded = 1;
}
# The usual import method
goto &Exporter::import;
}
# for error messages, passed from main calling context through initialization
# function.
my $real_command_name = '';
#####################################################################
# customization API, used from main calling context (main program or
# t/test_utils.pl) and from init files.
# The main calling context may merge $cmdline_options, $options_defaults
# and $init_files_options. That's why it is important, in *_set_from_*
# functions below to also delete keys in other hashes for overriden
# customization variables, even though it wouldn't change the output of
# texinfo_get_conf.
my $cmdline_options;
my $options_defaults;
my $init_files_options = {};
# List options that can be set at initialization from the main calling context
# but are not handled in the same way than string options.
# Indeed, the lists are filled in the main calling context, in high precedence
# setting, i.e. these lists are filled with command-line
# options in the main program. If they were set in the main calling context
# those lists should be high precedence options, and the list options set from
# init file would never have any effect.
# Therefore, for such list options, items are added and removed by
# calls to texinfo_add_to_option_list
# and texinfo_remove_from_option_list, be it from command line
# or init files, there is no precedence, but the order of calls
# matter.
my %options_as_lists;
# Note that list options not set in the main calling context, but only set
# from init files are set like string options, with equal precedence
# (the value set last is used).
# called from texi2any.pl main program and t/test_utils.pl.
# References on $OPTIONS_DEFAULT and $CMDLINE_OPTIONS are retained in
# the main calling context, therefore the hash reference should be used
# directly, not copies.
sub GNUT_initialize_customization($$$) {
$real_command_name = shift;
$options_defaults = shift;
$cmdline_options = shift;
# highest precedence options passed for initialization from the main context
# should only be list options
foreach my $cmdline_option (keys(%$cmdline_options)) {
if (ref($cmdline_options->{$cmdline_option}) eq ''
or ref($cmdline_options->{$cmdline_option}) ne 'ARRAY') {
warn "BUG: $cmdline_option not an ARRAY: "
." $cmdline_options->{$cmdline_option}\n";
}
$options_as_lists{$cmdline_option} = 1;
}
#print STDERR "options_defaults: ".join('|',sort(keys(%$options_defaults)))."\n";
#print STDERR "cmdline_options: ".join('|',sort(keys(%$cmdline_options)))."\n";
return $init_files_options;
}
# duplicated from texi2any.pl
sub _GNUT_encode_message($) {
my $text = shift;
my $encoding = texinfo_get_conf('MESSAGE_ENCODING');
if (defined($encoding)) {
return Encode::encode($encoding, $text);
} else {
return $text;
}
}
# duplicated from texi2any.pl
sub _GNUT_decode_input($) {
my $text = shift;
my $encoding = texinfo_get_conf('COMMAND_LINE_ENCODING');
if (defined($encoding)) {
return decode($encoding, $text);
} else {
return $text;
}
}
# duplicated from texi2any.pl
sub _GNUT_document_warn($) {
return if (texinfo_get_conf('NO_WARN'));
my $text = shift;
warn(_GNUT_encode_message(
sprintf(__p("program name: warning: warning_message",
"%s: warning: %s"), $real_command_name, $text)."\n"));
}
sub _GNUT_document_fatal($) {
my $text = shift;
warn(_GNUT_encode_message(
sprintf(__p("program name: error_message",
"%s: %s"), $real_command_name, $text)."\n"));
exit 1 unless (texinfo_get_conf('FORCE'));
}
# used to register messages by the user with texinfo_register_init_loading_*
my @init_file_loading_messages;
# called from texi2any.pl main program and t/test_utils.pl.
# eval $FILE in the Texinfo::Config namespace. $FILE should be a binary string.
sub GNUT_load_init_file($) {
my $file = shift;
push @init_file_loading_messages, [];
my $result = do($file);
my $message = $@;
my $read_error = $!;
if (!defined($result)) {
if (defined($message) and $message ne '') {
_GNUT_document_fatal(sprintf
(__("error parsing %s: %s"),
_GNUT_decode_input($file), $message));
} elsif (defined($read_error)) {
_GNUT_document_fatal(sprintf
(__("error reading %s: %s"),
_GNUT_decode_input($file), $read_error));
}
}
# Note: $message or $read_error may be incorrectly "double encoded" if they
# are encoded byte strings. However, it appears that they are unencoded
# character strings if the init file uses the "use utf8" pragma to mark the
# file as UTF-8 encoded, which may become the default in the future according
# to the Perl documentation.
my $file_loading_messages = pop @init_file_loading_messages;
my $error_nr = 0;
for my $error (@{$file_loading_messages}) {
my $type = $error->{'type'};
my $message = $error->{'text'};
chomp($message);
if ($type eq 'error') {
$error_nr += 1;
warn(_GNUT_encode_message(
sprintf(__p("init file: error_message",
"%s: %s"),
_GNUT_decode_input($file), $message)."\n"));
} else {
if (not texinfo_get_conf('NO_WARN')) {
warn(_GNUT_encode_message(
sprintf(__p("init file: warning: warning_message",
"%s: warning: %s"),
_GNUT_decode_input($file), $message)."\n"));
}
}
}
if ($error_nr > 0 and !texinfo_get_conf('FORCE')) {
exit 1;
}
}
# called from init files in case of errors at loading.
sub texinfo_register_init_loading_error($) {
my $message = shift;
push @{$init_file_loading_messages[-1]}, {'type' => 'error',
'text' => $message};
}
# called from init files in case of warnings at loading.
sub texinfo_register_init_loading_warning($) {
my $message = shift;
push @{$init_file_loading_messages[-1]}, {'type' => 'warning',
'text' => $message};
}
# L2H removed in 2021
# return undef var when there is nothing to set.
# NOTE this has not been done consistently, many options were renamed
# without doing something here.
# NOTE in C there is no mapping of values.
sub _GNUT_map_obsolete_options($$) {
my ($input_var, $input_value) = @_;
my $var = $input_var;
my $value = $input_value;
if ($input_var eq 'L2H') {
_GNUT_document_warn(sprintf(__("obsolete option: %s"), $input_var));
if (! $input_value) {
# nothing to do in that case
$var = undef;
$value = undef;
} else {
$var = 'HTML_MATH';
$value = 'l2h';
}
}
return $var, $value;
}
# Called from init files to set configuration options.
sub texinfo_set_from_init_file($$) {
my ($var, $value) = @_;
($var, $value) = _GNUT_map_obsolete_options($var, $value);
if (!defined($var)) {
return 1;
}
if (!Texinfo::Common::valid_customization_option($var)) {
# carp may be better, but infortunately, it points to the routine
# that loads the file, and not to the init file.
_GNUT_document_warn(sprintf(__("%s: unknown variable %s"),
'texinfo_set_from_init_file', $var));
return 0;
}
return 0 if (exists($cmdline_options->{$var}));
delete $options_defaults->{$var};
$init_files_options->{$var} = $value;
return 1;
}
# set option from the command line, called from main program.
# Highest precedence.
sub GNUT_set_from_cmdline($$) {
my ($var, $value) = @_;
($var, $value) = _GNUT_map_obsolete_options($var, $value);
if (!defined($var)) {
return 1;
}
delete $init_files_options->{$var};
delete $options_defaults->{$var};
if (!Texinfo::Common::valid_customization_option($var)) {
_GNUT_document_warn(sprintf(__("unknown variable from command line: %s"),
$var));
return 0;
}
$cmdline_options->{$var} = $value;
return 1;
}
# add default based, for instance, on the format.
sub GNUT_set_customization_default($$) {
my ($var, $value) = @_;
($var, $value) = _GNUT_map_obsolete_options($var, $value);
if (!defined($var)) {
return 1;
}
return 0 if (exists($cmdline_options->{$var})
or exists($init_files_options->{$var}));
$options_defaults->{$var} = $value;
return 1;
}
# called both from main program and init files, for %options_as_lists
# options with lists set in main program.
sub texinfo_add_to_option_list($$;$) {
my ($var, $values_array_ref, $prepend) = @_;
if (not $options_as_lists{$var}) {
return 0;
}
if ($prepend) {
# accept duplicates in that case, as prepending should in general
# be used to override by being first
unshift @{$cmdline_options->{$var}}, @$values_array_ref;
} else {
foreach my $value (@$values_array_ref) {
push @{$cmdline_options->{$var}}, $value
unless (grep {$_ eq $value} @{$cmdline_options->{$var}});
}
}
return 1;
}
# called both from main program and init files.
sub texinfo_remove_from_option_list($$) {
my ($var, $values_array_ref) = @_;
if (not $options_as_lists{$var}) {
return 0;
}
foreach my $value (@$values_array_ref) {
@{$cmdline_options->{$var}}
= grep {$_ ne $value} @{$cmdline_options->{$var}};
}
return 1;
}
# This function can be used to get main program variables
# customization values.
# For conversion customization variables, converter methods
# should be used instead, the implementation usually used being
# from Texinfo::Convert::Converter.
# NOTE It is possible to set up an interface similar to those used in
# converters for the main program and tests configuration with the
# Texinfo::MainConfig package below, but it should not be accessed/used
# in user defined code (and the Texinfo::MainConfig interface
# is therefore undocumented).
sub texinfo_get_conf($) {
my $var = shift;
confess("BUG: texinfo_get_conf: undef \$cmdline_options."
." Call GNUT_initialize_customization")
if (!$cmdline_options);
if (exists($cmdline_options->{$var})) {
return $cmdline_options->{$var};
} elsif (exists($init_files_options->{$var})) {
return $init_files_options->{$var};
} elsif (exists($options_defaults->{$var})) {
return $options_defaults->{$var};
} else {
return undef;
}
}
########################################################################
# Output format API. Handled differently from customization option
# because a function from main program need to be called on formats, so
# there is a function called from the main program to get the format set
# in init files.
my $init_file_format;
sub texinfo_set_format_from_init_file($) {
$init_file_format = shift;
}
sub GNUT_get_format_from_init_file() {
return $init_file_format;
}
#####################################################################
# If another format than HTML can be customized, it may be a good
# idea to add another level with the format name, maybe change some
# texinfo_ function names and add the format in GNUT_* functions called
# from main program.
#####################################################################
# stages handlers API. Used in HTML only.
my @possible_stages = ('setup', 'structure', 'init', 'finish');
my $default_priority = 'default';
my $GNUT_stage_handlers;
sub _GNUT_initialize_stage_handlers() {
$GNUT_stage_handlers = {};
foreach my $stage (@possible_stages) {
$GNUT_stage_handlers->{$stage} = {};
}
}
_GNUT_initialize_stage_handlers();
sub texinfo_register_handler($$;$) {
my ($stage, $handler, $priority) = @_;
if (!$GNUT_stage_handlers->{$stage}) {
carp("Unknown stage $stage\n");
return 0;
}
$priority = $default_priority if (!defined($priority));
push @{$GNUT_stage_handlers->{$stage}->{$priority}}, $handler;
return 1;
}
# called from the Converter. Sort according to priority and return sorted
# handlers by stage. (Return actually handler and priority pairs in case the
# priority name information is interesting).
sub GNUT_get_stage_handlers() {
my %sorted_stage_handlers;
foreach my $stage (keys(%$GNUT_stage_handlers)) {
$sorted_stage_handlers{$stage} = [];
my @sorted_priorities = sort keys(%{$GNUT_stage_handlers->{$stage}});
foreach my $priority (@sorted_priorities) {
foreach my $handler (@{$GNUT_stage_handlers->{$stage}->{$priority}}) {
push @{$sorted_stage_handlers{$stage}}, [$handler, $priority];
}
}
}
return \%sorted_stage_handlers;
}
#####################################################################
# API used to override formatting. Used in HTML only.
my $GNUT_file_id_setting_references = {};
my $GNUT_formatting_references = {};
my $GNUT_formatting_special_unit_body = {};
my $GNUT_commands_conversion = {};
my $GNUT_commands_open = {};
my $GNUT_output_units_conversion = {};
my $GNUT_types_conversion = {};
my $GNUT_types_open = {};
my $GNUT_upper_case_commands = {};
my $GNUT_no_arg_commands_formatting_strings = {};
my $GNUT_style_commands_formatting_info = {};
my $GNUT_accent_command_formatting_info = {};
my $GNUT_types_code_info = {};
my $GNUT_types_pre_class = {};
my $GNUT_global_directions = {};
my $GNUT_text_directions = {};
my $GNUT_direction_string_info = {};
my $GNUT_special_unit_info = {};
# called from init files
sub texinfo_register_file_id_setting_function($$) {
my ($thing, $handler) = @_;
$GNUT_file_id_setting_references->{$thing} = $handler;
}
# called from the Converter
sub GNUT_get_file_id_setting_references() {
return $GNUT_file_id_setting_references;
}
# called from init files
sub texinfo_register_formatting_function($$) {
my ($thing, $handler) = @_;
$GNUT_formatting_references->{$thing} = $handler;
}
# called from the Converter
sub GNUT_get_formatting_references() {
return $GNUT_formatting_references;
}
# called from init files
sub texinfo_register_command_formatting($$) {
my ($command, $reference) = @_;
$GNUT_commands_conversion->{$command} = $reference;
}
# called from the Converter
sub GNUT_get_commands_conversion() {
return $GNUT_commands_conversion;
}
# called from init files
sub texinfo_register_command_opening($$) {
my ($command, $reference) = @_;
$GNUT_commands_open->{$command} = $reference;
}
# called from the Converter
sub GNUT_get_commands_open() {
return $GNUT_commands_open;
}
# called from init files
sub texinfo_register_output_unit_formatting($$) {
my ($command, $reference) = @_;
$GNUT_output_units_conversion->{$command} = $reference;
}
# called from the Converter
sub GNUT_get_output_units_conversion() {
return $GNUT_output_units_conversion;
}
# called from init files
sub texinfo_register_type_formatting($$) {
my ($command, $reference) = @_;
$GNUT_types_conversion->{$command} = $reference;
}
# called from the Converter
sub GNUT_get_types_conversion() {
return $GNUT_types_conversion;
}
# called from init files
sub texinfo_register_type_opening($$) {
my ($type, $reference) = @_;
$GNUT_types_open->{$type} = $reference;
}
# called from the Converter
sub GNUT_get_types_open() {
return $GNUT_types_open;
}
# called from init files
sub texinfo_register_formatting_special_unit_body($$) {
my ($special_unit_variety, $handler) = @_;
$GNUT_formatting_special_unit_body->{$special_unit_variety} = $handler;
}
# called from the Converter
sub GNUT_get_formatting_special_unit_body_references() {
return $GNUT_formatting_special_unit_body;
}
my $default_formatting_context = 'normal';
my @all_possible_formatting_context = ($default_formatting_context,
'preformatted', 'string', 'css_string');
sub _GNUT_initialize_no_arg_commands_formatting_strings() {
$GNUT_no_arg_commands_formatting_strings = {};
foreach my $possible_formatting_context (@all_possible_formatting_context,
'translated_to_convert') {
$GNUT_no_arg_commands_formatting_strings->{$possible_formatting_context} = {};
}
}
my @all_style_commands_formatting_context = ($default_formatting_context,
'preformatted');
_GNUT_initialize_no_arg_commands_formatting_strings();
sub _GNUT_initialize_style_commands_formatting_info() {
$GNUT_style_commands_formatting_info = {};
foreach my $possible_formatting_context
(@all_style_commands_formatting_context) {
$GNUT_style_commands_formatting_info->{$possible_formatting_context} = {};
}
}
_GNUT_initialize_style_commands_formatting_info();
my @all_special_unit_info_types = ('class', 'direction', 'heading', 'order',
'file_string', 'target');
sub _GNUT_initialize_special_unit_info() {
$GNUT_special_unit_info = {};
foreach my $possible_type (@all_special_unit_info_types) {
$GNUT_special_unit_info->{$possible_type} = {};
}
}
_GNUT_initialize_special_unit_info();
# $translated_converted_string is supposed to be already formatted.
sub texinfo_register_no_arg_command_formatting($$;$$$) {
my ($command, $context, $text,
# html element
$element, $translated_converted_string) = @_;
if (!defined($context)) {
$context = $default_formatting_context;
} elsif (not defined($GNUT_no_arg_commands_formatting_strings->{$context})) {
_GNUT_document_warn(sprintf(__("%s: unknown formatting context %s"),
'texinfo_register_no_arg_command_formatting', $context));
return 0;
}
my $specification = {};
if (defined($text)) {
$specification->{'text'} = $text;
}
if (defined($element)) {
$specification->{'element'} = $element;
}
if (defined($translated_converted_string)) {
$specification->{'translated_converted'} = $translated_converted_string;
# NOTE unset 'text'? A priori not needed, it will be overwritten
}
$GNUT_no_arg_commands_formatting_strings->{$context}->{$command}
= $specification;
return 1;
}
# Independent of context, as the Texinfo code will be converted in
# the appropriate context.
sub texinfo_register_no_arg_command_texinfo($$) {
my ($command, $translated_to_convert_string) = @_;
if (defined($translated_to_convert_string)) {
$GNUT_no_arg_commands_formatting_strings->{'translated_to_convert'}
->{$command} = $translated_to_convert_string;
} else {
delete $GNUT_no_arg_commands_formatting_strings->{'translated_to_convert'}
->{$command};
}
return 1;
}
sub GNUT_get_no_arg_command_formatting($;$) {
my ($command, $context) = @_;
if (!defined($context)) {
$context = $default_formatting_context;
} elsif (not defined($GNUT_no_arg_commands_formatting_strings->{$context})) {
_GNUT_document_warn(sprintf(__("%s: unknown formatting context %s"),
'GNUT_get_no_arg_command_formatting', $context));
return undef;
}
if (exists($GNUT_no_arg_commands_formatting_strings->{$context})
and exists($GNUT_no_arg_commands_formatting_strings->{$context}->{$command})) {
return $GNUT_no_arg_commands_formatting_strings->{$context}->{$command};
}
return undef;
}
# called from init files
sub texinfo_register_style_command_formatting($$;$) {
my ($command, $html_element, $context) = @_;
if (!defined($context)) {
$context = $default_formatting_context;
} elsif (not defined($GNUT_style_commands_formatting_info->{$context})) {
_GNUT_document_warn(sprintf(__("%s: unknown formatting context %s"),
'texinfo_register_style_command_formatting', $context));
return 0;
}
if (!defined($html_element)) {
if (exists($GNUT_style_commands_formatting_info->{$context}->{$command})) {
delete $GNUT_style_commands_formatting_info->{$context}
->{$command}->{'element'};
}
} else {
$GNUT_style_commands_formatting_info->{$context}->{$command} = {}
if (!exists($GNUT_style_commands_formatting_info->{$context}->{$command}));
$GNUT_style_commands_formatting_info->{$context}->{$command}->{'element'}
= $html_element;
}
return 1;
}
# called from init files
sub texinfo_style_command_set_quoting($$;$) {
my ($command, $in_quotes, $context) = @_;
if (!defined($context)) {
$context = $default_formatting_context;
} elsif (not defined($GNUT_style_commands_formatting_info->{$context})) {
_GNUT_document_warn(sprintf(__("%s: unknown formatting context %s"),
'texinfo_style_command_set_quoting', $context));
return 0;
}
if (!defined($in_quotes)) {
if (exists($GNUT_style_commands_formatting_info->{$context}->{$command})) {
delete $GNUT_style_commands_formatting_info->{$context}
->{$command}->{'quote'};
}
} elsif ($in_quotes) {
$GNUT_style_commands_formatting_info->{$context}->{$command} = {}
if (!exists($GNUT_style_commands_formatting_info->{$context}->{$command}));
$GNUT_style_commands_formatting_info->{$context}->{$command}->{'quote'}
= $in_quotes;
}
return 1;
}
sub GNUT_get_style_command_formatting($;$) {
my ($command, $context) = @_;
if (!defined($context)) {
$context = $default_formatting_context;
} elsif (not defined($GNUT_style_commands_formatting_info->{$context})) {
_GNUT_document_warn(sprintf(__("%s: unknown formatting context %s"),
'GNUT_get_style_command_formatting', $context));
return undef;
}
if (exists($GNUT_style_commands_formatting_info->{$context})
and exists($GNUT_style_commands_formatting_info->{$context}->{$command})) {
return $GNUT_style_commands_formatting_info->{$context}->{$command};
}
return undef;
}
# called from init files
sub texinfo_register_upper_case_command($$) {
my ($command, $value) = @_;
if ($value) {
$GNUT_upper_case_commands->{$command} = 1;
} else {
$GNUT_upper_case_commands->{$command} = 0;
}
}
# called from the Converter
sub GNUT_get_upper_case_commands_info() {
return $GNUT_upper_case_commands;
}
# called from init files
sub texinfo_register_accent_command_formatting($$$) {
my ($command, $accent_command_entity,
$accent_command_text_with_entities) = @_;
$GNUT_accent_command_formatting_info->{$command}
= [$accent_command_entity, $accent_command_text_with_entities];
return 1;
}
# called from the Converter
sub GNUT_get_accent_command_formatting($) {
my $command = shift;
if (exists($GNUT_accent_command_formatting_info->{$command})) {
return @{$GNUT_accent_command_formatting_info->{$command}};
}
return (undef, undef);
}
sub texinfo_register_type_code($$) {
my ($type, $code_type) = @_;
$GNUT_types_code_info->{$type} = $code_type;
}
sub texinfo_register_type_pre_class($$) {
my ($type, $pre_class_type) = @_;
$GNUT_types_pre_class->{$type} = $pre_class_type;
}
sub GNUT_get_types_code_info() {
return { %$GNUT_types_code_info };
}
sub GNUT_get_types_pre_class() {
return { %$GNUT_types_pre_class };
}
# if $NODE_TEXI_NAME is undef, the direction is a direction text not
# associated to an output unit
sub texinfo_register_global_direction($;$) {
my ($direction, $node_texi_name) = @_;
$GNUT_global_directions->{$direction} = $node_texi_name;
}
sub texinfo_register_text_direction($) {
my $direction = shift;
$GNUT_text_directions->{$direction} = 1;
}
sub GNUT_get_global_directions() {
return { %$GNUT_global_directions };
}
sub GNUT_get_text_directions() {
return { %$GNUT_text_directions };
}
# no check on type and direction, but only the ones known in the HTML
# converted will be used
sub texinfo_register_direction_string_info($$;$$$) {
my ($direction, $type, $converted_string,
$string_to_convert, $context) = @_;
$context = 'normal' if (!defined($context));
$GNUT_direction_string_info->{$type} = {}
if (not exists($GNUT_direction_string_info->{$type}));
$GNUT_direction_string_info->{$type}->{$direction} = {}
if (not exists($GNUT_direction_string_info->{$type}->{$direction}));
$GNUT_direction_string_info->{$type}->{$direction}->{'to_convert'}
= $string_to_convert;
if (defined($converted_string)) {
$GNUT_direction_string_info->{$type}->{$direction}->{'converted'} = {}
if (!defined($GNUT_direction_string_info->{$type}->{$direction}->{'converted'}));
$GNUT_direction_string_info->{$type}->{$direction}->{'converted'}->{$context}
= $converted_string;
}
}
sub GNUT_get_direction_string_info() {
return { %$GNUT_direction_string_info };
}
sub texinfo_register_special_unit_info($$$) {
my ($type, $variety, $thing) = @_;
if (not defined($GNUT_special_unit_info->{$type})) {
_GNUT_document_warn(
sprintf(__("%s: unknown special element information type %s"),
'texinfo_register_special_unit_info', $type));
return 0;
}
$GNUT_special_unit_info->{$type}->{$variety} = {}
if (not exists($GNUT_special_unit_info->{$type}->{$variety}));
$GNUT_special_unit_info->{$type}->{$variety} = $thing;
return 1;
}
sub GNUT_get_special_unit_info() {
return { %$GNUT_special_unit_info };
}
# Not needed from the main program, as the init files should affect all
# the manuals, but needed for tests, to have isolated tests.
sub GNUT_reinitialize_init_files() {
@init_file_loading_messages = ();
foreach my $reference ($init_files_options,
$GNUT_file_id_setting_references,
$GNUT_formatting_references, $GNUT_formatting_special_unit_body,
$GNUT_upper_case_commands,
$GNUT_commands_conversion, $GNUT_commands_open, $GNUT_types_conversion,
$GNUT_types_open, $GNUT_accent_command_formatting_info,
$GNUT_types_code_info, $GNUT_types_pre_class, $GNUT_direction_string_info,
$GNUT_global_directions, $GNUT_text_directions) {
$reference = {};
}
_GNUT_initialize_stage_handlers();
_GNUT_initialize_no_arg_commands_formatting_strings();
_GNUT_initialize_style_commands_formatting_info();
_GNUT_initialize_special_unit_info();
}
#####################################################################
# the objective of this small package is to be in another
# scope than init files and setup blessed objects that can call
# get_conf() and set_conf() methods.
#
# There is also the need to have access to configuration options
# in order to have get_conf() return the same as
# Texinfo::Config::texinfo_get_conf().
package Texinfo::MainConfig;
sub new() {
# setup additional config to be used with other Texinfo::Config information.
my $config = {};
bless $config;
return $config;
}
sub get_conf($$) {
my ($self, $var) = @_;
# as get_conf, but with self having precedence on
# main calling context defaults
if (exists($cmdline_options->{$var})) {
return $cmdline_options->{$var};
} elsif (exists($init_files_options->{$var})) {
return $init_files_options->{$var};
} elsif (exists($self->{$var})) {
return $self->{$var};
} elsif (exists($options_defaults->{$var})) {
return $options_defaults->{$var};
}
return undef;
}
sub set_conf($$$) {
my ($self, $var, $val) = @_;
$self->{$var} = $val;
return 1;
}
# get options in a simple hash. It is not possible to set
# customization variables afterwards as the information on
# precedence has been lost, so this should be called when all the
# options have been definitively set.
sub get_customization_options_hash($) {
my $self = shift;
my %options = %{$options_defaults};
foreach my $config ($self, $init_files_options, $cmdline_options) {
foreach my $option (keys(%$config)) {
$options{$option} = $config->{$option};
}
}
#print STDERR "MAIN: ".join('|', sort(keys(%options)))."\n";
return \%options;
}
1;