# Utils.pm: miscellaneous functions usable in simple converters # # 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 # # This module contains the methods that can be used in converters # even if they do not inherit Texinfo::Convert::Converter. In practice # it means that the converter argument will not be defined and # there will be no error reporting nor string translation in that case. # Some methods still require a converter, it means that they are used # conditionally in some converters that do not inherit # Texinfo::Convert::Converter but can have gotten a converter object # (case of Texinfo::Convert::Text). package Texinfo::Convert::Utils; use strict; use 5.006; # To check if there is no erroneous autovivification #no autovivification qw(fetch delete exists store strict); # debugging use Carp qw(cluck); use Texinfo::Commands; use Texinfo::Common; # only needed in debugging comments. Ok to keep it here anyway. use Texinfo::Convert::Texinfo; require Exporter; our @ISA = qw(Exporter); # There is no specific reason to export those functions and not # other functions of the module. It could be possible not to # export any function. our @EXPORT_OK = qw( expand_today expand_verbatiminclude add_heading_number ); our $VERSION = '7.2'; # API to open, set encoding and register files. Used in main program # and converters. # In general $SELF is stored as $converter->{'output_files'} # and should be accessed through $converter->output_files_information(); # TODO next four functions not documented anywhere, probably relevant to # document both in POD and in HTML Customization API. sub output_files_initialize { return {'unclosed_files' => {}, 'opened_files' => {}}; } sub output_files_disable_output_encoding($$) { my ($self, $no_output_encoding) = @_; $self->{'output_encoding_disabled'} = $no_output_encoding; } # All the opened files are registered, except for stdout, # and the closing of files should be registered too with # output_files_register_closed() below. This makes possible to # unlink all the opened files and close the files not already # closed. # # $FILE_PATH is the file path, it should be a binary string. # If $USE_BINMODE is set, call binmode() to set binary mode. # $OUTPUT_ENCODING argument overrides the output encoding. # Returns # - the opened filehandle, or undef if opening failed, # - the $! error message or undef if opening succeeded. # - 1 if the $FILE_PATH was already opened, which means overwriting. sub output_files_open_out($$$;$$) { my $self = shift; my $customization_information = shift; my $file_path = shift; my $use_binmode = shift; my $output_encoding = shift; #if (!defined($file_path)) { # cluck('output_files_open_out: file_path undef'); #} my $encoding; if ($self->{'output_encoding_disabled'}) { # leave $encoding undefined } elsif (defined($output_encoding)) { $encoding = $output_encoding; } elsif (defined( $customization_information->get_conf('OUTPUT_PERL_ENCODING'))) { $encoding = $customization_information->get_conf('OUTPUT_PERL_ENCODING'); } if ($file_path eq '-') { binmode(STDOUT) if $use_binmode; binmode(STDOUT, ":encoding($encoding)") if (defined($encoding)); if ($self) { $self->{'unclosed_files'}->{$file_path} = \*STDOUT; } return \*STDOUT, undef; } # Check that this file has not already been registered # as opened_file. If yes, it will be overwritten if open succeeds. # It is not possible to use the file name twice in converters # for regular output as files are only closed when all the output # units have been written. It could be possible in HTML with js # scripts licence file set by the user to the same name as an output # file. my $overwritten_file = 0; # NOTE paths are not normalized, therefore different paths names # that refers to the same file will not be found. if (exists($self->{'opened_files'}->{$file_path})) { $overwritten_file = 1; } my $filehandle = do { local *FH }; if (!open($filehandle, '>', $file_path)) { my $error_message = $!; return undef, $error_message, $overwritten_file; } # If $use_binmode is true, we run binmode to turn off outputting LF as CR LF # under MS-Windows, so that Info tag tables will have correct offsets. This # must be done before setting the encoding filters with binmode. binmode($filehandle) if $use_binmode; if ($encoding) { binmode($filehandle, ":encoding($encoding)"); } if ($self) { if ($self->{'unclosed_files'}->{$file_path}) { warn "BUG: already open: $file_path\n"; } else { $self->{'opened_files'}->{$file_path} = 1; } $self->{'unclosed_files'}->{$file_path} = $filehandle; } return $filehandle, undef, $overwritten_file; } # see the description of $SELF in comment above output_files_open_out. # # $FILE_PATH is the file path, it should be a binary string. sub output_files_register_closed($$) { my $self = shift; my $file_path = shift; if ($self->{'unclosed_files'}->{$file_path}) { delete $self->{'unclosed_files'}->{$file_path}; } else { cluck "BUG: $file_path not opened\n"; } } # The next two functions should not be called from user-defined # code, only from the main program. They are defined here for # consistency of the API and clarity of the code. # # see the description of $SELF in comment above output_files_open_out. sub output_files_opened_files($) { my $self = shift; return $self->{'opened_files'}; } # see the description of $SELF in comment above output_files_open_out. sub output_files_unclosed_files($) { my $self = shift; return $self->{'unclosed_files'}; } # end of output_files API