texinfo-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

branch master updated: * tp/Texinfo/Convert/HTML.pm (_html_convert_outpu


From: Patrice Dumas
Subject: branch master updated: * tp/Texinfo/Convert/HTML.pm (_html_convert_output): if a special element body is empty, do not return immediately but check if there is already a body registered with the file, as in that case, the body would need to be output.
Date: Wed, 01 Nov 2023 16:17:10 -0400

This is an automated email from the git hooks/post-receive script.

pertusus pushed a commit to branch master
in repository texinfo.

The following commit(s) were added to refs/heads/master by this push:
     new f796367e02 * tp/Texinfo/Convert/HTML.pm (_html_convert_output): if a 
special element body is empty, do not return immediately but check if there is 
already a body registered with the file, as in that case, the body would need 
to be output.
f796367e02 is described below

commit f796367e025a859b6b1dff8ae401f4f72b03a66e
Author: Patrice Dumas <pertusus@free.fr>
AuthorDate: Wed Nov 1 21:15:29 2023 +0100

    * tp/Texinfo/Convert/HTML.pm (_html_convert_output): if a special
    element body is empty, do not return immediately but check if there is
    already a body registered with the file, as in that case, the body
    would need to be output.
    
    * tp/Texinfo/Common.pm (output_files_open_out): warn if an unclosed
    file has already been registered.
    
    * tp/Texinfo/XS/main/utils.c (add_include_directory): simplify, set
    string argument const.
    
    * tp/Texinfo/Convert/HTML.pm (_html_convert_output),
    tp/Texinfo/XS/convert/convert_html.c (html_convert_output): rename $footer
    as $file_end and $header as $file_beginning.
    
    * tp/Texinfo/XS/convert/ConvertXS.xs
    (html_prepare_units_directions_files),
    tp/Texinfo/XS/convert/convert_html.c
    (html_prepare_units_directions_files),
    tp/Texinfo/XS/convert/converter.c (initialize_output_units_files),
    tp/Texinfo/XS/convert/convert_html.c (html_set_pages_files),
    tp/Texinfo/XS/convert/converter.c (find_output_unit_file)
    (add_output_units_file, set_output_unit_file)
    (register_normalize_case_filename, set_file_path),
    tp/Texinfo/XS/main/converter_types.h (CONVERTER): put
    output_unit_files directly in CONVERTER. Start indices of found files
    in output_unit_files list at 0. have set_output_unit_file and
    functions called by set_output_unit_file return indices in
    output_unit_files list. Setup converter output_unit_file_indices and
    special_unit_file_indices with each output unit output_unit_files file
    index.
    
    * tp/Texinfo/XS/convert/convert_html.c (unique_target),
    tp/Texinfo/XS/main/utils.c (find_string): add
    find_string function.
    
    * tp/Texinfo/XS/main/build_perl_info.c (newSVpv_byte): add
    newSVpv_byte.
    
    * tp/Texinfo/Convert/HTML.pm (_html_convert_output),
    tp/Texinfo/XS/convert/convert_html.c
    (convert_output_output_unit_internal, html_convert_output),
    tp/Texinfo/XS/main/build_perl_info.c (build_html_formatting_state),
    tp/Texinfo/XS/main/convert_utils.c (encoded_output_file_name),
    (register_unclosed_file, output_files_open_out)
    (output_files_register_closed), tp/Texinfo/XS/main/converter_types.h
    (ARRAY_INDEX_LIST, FILE_NAME_PATH_COUNTER, FILE_STREAM)
    (FILE_STREAM_LIST, OUTPUT_FILES_INFORMATION):
    finish C html_convert_output output implementation. Pass file counters
    back to perl. Add Texinfo::Common output_files_information API to C to
    open and register files.
    
    * tp/Texinfo/XS/convert/ConvertXS.xs (html_convert_output),
    tp/Texinfo/XS/convert/convert_html.c (html_converter_initialize),
    tp/Texinfo/XS/main/build_perl_info.c
    (build_output_files_unclosed_files, build_output_files_opened_files)
    (build_output_files_information): build perl converter
    output_files_information.
    
    * tp/texi2any.pl (%main_unclosed_files): rename %unclosed_files as
    %main_unclosed_files.
---
 ChangeLog                            |  64 +++++++++
 tp/Texinfo/Common.pm                 |  14 +-
 tp/Texinfo/Convert/HTML.pm           |  33 +++--
 tp/Texinfo/XS/convert/ConvertXS.xs   |  10 +-
 tp/Texinfo/XS/convert/convert_html.c | 248 +++++++++++++++++++++++++++--------
 tp/Texinfo/XS/convert/converter.c    |  77 ++++++-----
 tp/Texinfo/XS/convert/converter.h    |   3 +-
 tp/Texinfo/XS/main/build_perl_info.c | 167 ++++++++++++++++++++++-
 tp/Texinfo/XS/main/build_perl_info.h |   2 +
 tp/Texinfo/XS/main/convert_utils.c   | 156 ++++++++++++++++++++++
 tp/Texinfo/XS/main/convert_utils.h   |  13 ++
 tp/Texinfo/XS/main/converter_types.h |  60 ++++++++-
 tp/Texinfo/XS/main/utils.c           |  20 ++-
 tp/Texinfo/XS/main/utils.h           |   3 +-
 tp/texi2any.pl                       |   8 +-
 15 files changed, 750 insertions(+), 128 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index cd0d8caccb..88ff669eb1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,67 @@
+2023-11-01  Patrice Dumas  <pertusus@free.fr>
+
+       * tp/Texinfo/Convert/HTML.pm (_html_convert_output): if a special
+       element body is empty, do not return immediately but check if there is
+       already a body registered with the file, as in that case, the body
+       would need to be output.
+
+       * tp/Texinfo/Common.pm (output_files_open_out): warn if an unclosed
+       file has already been registered.
+
+       * tp/Texinfo/XS/main/utils.c (add_include_directory): simplify, set
+       string argument const.
+
+       * tp/Texinfo/Convert/HTML.pm (_html_convert_output),
+       tp/Texinfo/XS/convert/convert_html.c (html_convert_output): rename 
$footer
+       as $file_end and $header as $file_beginning.
+
+       * tp/Texinfo/XS/convert/ConvertXS.xs
+       (html_prepare_units_directions_files),
+       tp/Texinfo/XS/convert/convert_html.c
+       (html_prepare_units_directions_files),
+       tp/Texinfo/XS/convert/converter.c (initialize_output_units_files),
+       tp/Texinfo/XS/convert/convert_html.c (html_set_pages_files),
+       tp/Texinfo/XS/convert/converter.c (find_output_unit_file)
+       (add_output_units_file, set_output_unit_file)
+       (register_normalize_case_filename, set_file_path),
+       tp/Texinfo/XS/main/converter_types.h (CONVERTER): put
+       output_unit_files directly in CONVERTER. Start indices of found files
+       in output_unit_files list at 0. have set_output_unit_file and
+       functions called by set_output_unit_file return indices in
+       output_unit_files list. Setup converter output_unit_file_indices and
+       special_unit_file_indices with each output unit output_unit_files file
+       index.
+
+       * tp/Texinfo/XS/convert/convert_html.c (unique_target),
+       tp/Texinfo/XS/main/utils.c (find_string): add
+       find_string function.
+
+       * tp/Texinfo/XS/main/build_perl_info.c (newSVpv_byte): add
+       newSVpv_byte.
+
+       * tp/Texinfo/Convert/HTML.pm (_html_convert_output),
+       tp/Texinfo/XS/convert/convert_html.c
+       (convert_output_output_unit_internal, html_convert_output),
+       tp/Texinfo/XS/main/build_perl_info.c (build_html_formatting_state),
+       tp/Texinfo/XS/main/convert_utils.c (encoded_output_file_name),
+       (register_unclosed_file, output_files_open_out)
+       (output_files_register_closed), tp/Texinfo/XS/main/converter_types.h
+       (ARRAY_INDEX_LIST, FILE_NAME_PATH_COUNTER, FILE_STREAM)
+       (FILE_STREAM_LIST, OUTPUT_FILES_INFORMATION):
+       finish C html_convert_output output implementation. Pass file counters
+       back to perl. Add Texinfo::Common output_files_information API to C to
+       open and register files.
+
+       * tp/Texinfo/XS/convert/ConvertXS.xs (html_convert_output),
+       tp/Texinfo/XS/convert/convert_html.c (html_converter_initialize),
+       tp/Texinfo/XS/main/build_perl_info.c
+       (build_output_files_unclosed_files, build_output_files_opened_files)
+       (build_output_files_information): build perl converter
+       output_files_information.
+
+       * tp/texi2any.pl (%main_unclosed_files): rename %unclosed_files as
+       %main_unclosed_files.
+
 2023-10-31  Patrice Dumas  <pertusus@free.fr>
 
        * tp/Texinfo/XS/main/call_perl_function.h (FILE_NAME_PATH)
diff --git a/tp/Texinfo/Common.pm b/tp/Texinfo/Common.pm
index bc2c9e9d2d..69f3569831 100644
--- a/tp/Texinfo/Common.pm
+++ b/tp/Texinfo/Common.pm
@@ -609,7 +609,17 @@ sub output_files_open_out($$$;$$)
     binmode($filehandle, ":encoding($encoding)");
   }
   if ($self) {
-    push @{$self->{'opened_files'}}, $file_path;
+    if ($self->{'unclosed_files'}->{$file_path}) {
+      warn "BUG: already open: $file_path\n";
+    } else {
+      # FIXME check that this file has not already been registered
+      # as opened_file?  If not, it will be unlink'ed twice if the
+      # main program aborts.  It is not possible to use the file name
+      # twice except with user customization file name set to a file
+      # name also used for a specific purpose such as MACRO_EXPAND
+      # or the like, as output units files are never opened twice.
+      push @{$self->{'opened_files'}}, $file_path;
+    }
     $self->{'unclosed_files'}->{$file_path} = $filehandle;
   }
   return $filehandle, undef;
@@ -625,7 +635,7 @@ sub output_files_register_closed($$)
   if ($self->{'unclosed_files'}->{$file_path}) {
     delete $self->{'unclosed_files'}->{$file_path};
   } else {
-    cluck "$file_path not opened\n";
+    cluck "BUG: $file_path not opened\n";
   }
 }
 
diff --git a/tp/Texinfo/Convert/HTML.pm b/tp/Texinfo/Convert/HTML.pm
index 6fcffa1491..8859416628 100644
--- a/tp/Texinfo/Convert/HTML.pm
+++ b/tp/Texinfo/Convert/HTML.pm
@@ -11429,7 +11429,7 @@ sub _html_convert_output($$$$$$$$)
   my ($self, $root, $output_units, $special_units, $output_file,
       $destination_directory, $output_filename, $document_name) = @_;
 
-  if (0 and $self->{'converter_descriptor'} and $XS_convert) {
+  if ($self->{'converter_descriptor'} and $XS_convert) {
     my $encoded_converter = $self->encode_converter_for_output();
     my $encoded_document_name = Encode::encode('UTF-8', $document_name);
     my $encoded_output_file = Encode::encode('UTF-8', $output_file);
@@ -11437,7 +11437,8 @@ sub _html_convert_output($$$$$$$$)
          = Encode::encode('UTF-8', $destination_directory);
     my $encoded_output_filename = Encode::encode('UTF-8', $output_filename);
 
-    my $XS_result = _XS_html_convert_output ($encoded_converter,
+    my $XS_result
+           = _XS_html_convert_output ($encoded_converter,
                      $root, $output_units, $special_units, 
$encoded_output_file,
                      $encoded_destination_directory, $encoded_output_filename,
                      $encoded_document_name);
@@ -11527,13 +11528,14 @@ sub _html_convert_output($$$$$$$$)
     }
 
     # do end file first, in case it needs some CSS
-    my $footer = &{$self->formatting_function('format_end_file')}($self,
+    my $file_end = &{$self->formatting_function('format_end_file')}($self,
                                                   $output_filename, undef);
-    my $header = &{$self->formatting_function('format_begin_file')}($self,
+    my $file_beginning
+        = &{$self->formatting_function('format_begin_file')}($self,
                                                   $output_filename, undef);
-    $text_output .= $self->write_or_return($header, $fh);
+    $text_output .= $self->write_or_return($file_beginning, $fh);
     $text_output .= $self->write_or_return($body, $fh);
-    $text_output .= $self->write_or_return($footer, $fh);
+    $text_output .= $self->write_or_return($file_end, $fh);
 
     # NOTE do not close STDOUT now to avoid a perl warning.
     if ($fh and $no_page_out_filepath ne '-') {
@@ -11572,8 +11574,7 @@ sub _html_convert_output($$$$$$$$)
         $body = $self->convert_output_unit($output_unit,
                                            "output s-unit $unit_nr");
         if ($body eq '') {
-          $self->{'file_counters'}->{$output_unit_filename}--;
-          next;
+          $body = undef;
         }
       } else {
         print STDERR "\nUNIT $unit_nr\n" if ($self->get_conf('DEBUG'));
@@ -11581,15 +11582,21 @@ sub _html_convert_output($$$$$$$$)
                                            "output unit $unit_nr");
       }
 
+      $self->{'file_counters'}->{$output_unit_filename}--;
+
       # register the output but do not print anything. Printing
       # only when file_counters reach 0, to be sure that all the
       # elements have been converted before headers are done.
-      if (!exists($files{$output_unit_filename})) {
-        $files{$output_unit_filename} = {'first_unit' => $output_unit,
-                                         'body' => ''};
+      if (defined($body)) {
+        if (!exists($files{$output_unit_filename})) {
+          $files{$output_unit_filename} = {'first_unit' => $output_unit,
+                                           'body' => ''};
+        }
+        $files{$output_unit_filename}->{'body'} .= $body;
+      } else {
+        next if (!exists($files{$output_unit_filename})
+                 or $files{$output_unit_filename}->{'body'} eq '');
       }
-      $files{$output_unit_filename}->{'body'} .= $body;
-      $self->{'file_counters'}->{$output_unit_filename}--;
 
       if ($self->{'file_counters'}->{$output_unit_filename} == 0) {
         my $out_filepath = $self->{'out_filepaths'}->{$output_unit_filename};
diff --git a/tp/Texinfo/XS/convert/ConvertXS.xs 
b/tp/Texinfo/XS/convert/ConvertXS.xs
index c992c6ee87..ca5dcd3788 100644
--- a/tp/Texinfo/XS/convert/ConvertXS.xs
+++ b/tp/Texinfo/XS/convert/ConvertXS.xs
@@ -233,12 +233,12 @@ html_prepare_units_directions_files (SV *converter_in, SV 
*output_units_in, SV *
            = build_html_global_units_directions (self->global_units_directions,
                                           self->special_units_direction_name);
          elements_in_file_count_sv
-           = build_html_elements_in_file_count (self->output_unit_files);
+           = build_html_elements_in_file_count (&self->output_unit_files);
 
          /* file names API */
-         filenames_sv = build_filenames (self->output_unit_files);
-         file_counters_sv = build_file_counters (self->output_unit_files);
-         out_filepaths_sv = build_out_filepaths (self->output_unit_files);
+         filenames_sv = build_filenames (&self->output_unit_files);
+         file_counters_sv = build_file_counters (&self->output_unit_files);
+         out_filepaths_sv = build_out_filepaths (&self->output_unit_files);
 
          EXTEND(SP, 6);
          PUSHs(sv_2mortal(files_source_info_sv));
@@ -442,6 +442,8 @@ html_convert_output (SV *converter_in, SV *tree_in, SV 
*output_units_in, SV *spe
                  result_sv = newSVpv_utf8 (result, 0);
                  free (result);
                }
+
+             build_output_files_information (self);
            }
 
          if (result_sv)
diff --git a/tp/Texinfo/XS/convert/convert_html.c 
b/tp/Texinfo/XS/convert/convert_html.c
index 9d73189b6e..2d00429795 100644
--- a/tp/Texinfo/XS/convert/convert_html.c
+++ b/tp/Texinfo/XS/convert/convert_html.c
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
 
 #include "global_commands_types.h"
 #include "tree_types.h"
@@ -879,17 +880,7 @@ unique_target (CONVERTER *self, char *target_base)
   char *target = strdup (target_base);
   while (1)
     {
-      int j;
-      int non_unique = 0;
-      for (j = 0; j < self->seen_ids->number; j++)
-        {
-          if (!strcmp (target, self->seen_ids->list[j]))
-            {
-              non_unique = 1;
-              break;
-            }
-        }
-      if (non_unique)
+      if (find_string (self->seen_ids, target))
         {
           free (target);
           xasprintf (&target, "%s-%d", target_base, nr);
@@ -1802,8 +1793,13 @@ html_set_pages_files (CONVERTER *self, OUTPUT_UNIT_LIST 
*output_units,
         }
     }
 
+  self->output_unit_file_indices = (size_t *)
+    malloc (output_units->number * sizeof (size_t));
+
+  memset (self->output_unit_file_indices, 0, output_units->number);
   for (i = 0; i < output_units->number; i++)
     {
+      size_t output_unit_file_idx = 0;
       FILE_NAME_PATH_COUNTER *output_unit_file;
       OUTPUT_UNIT *output_unit = output_units->list[i];
       char *output_unit_file_name = unit_file_name_paths[i];
@@ -1847,7 +1843,10 @@ html_set_pages_files (CONVERTER *self, OUTPUT_UNIT_LIST 
*output_units,
             }
           free (file_name_path);
         }
-      output_unit_file = set_output_unit_file (self, output_unit, filename, 1);
+      output_unit_file_idx
+        = set_output_unit_file (self, output_unit, filename, 1);
+      self->output_unit_file_indices[i] = output_unit_file_idx;
+      output_unit_file = &self->output_unit_files.list[output_unit_file_idx];
       if (self->conf->DEBUG > 0)
         fprintf (stderr, "Page %s: %s(%d)\n",
                  output_unit_texi (output_unit),
@@ -1857,9 +1856,14 @@ html_set_pages_files (CONVERTER *self, OUTPUT_UNIT_LIST 
*output_units,
 
   if (special_units && special_units->number)
     {
+      self->special_unit_file_indices = (size_t *)
+        malloc (special_units->number * sizeof (size_t));
+      memset (self->special_unit_file_indices, 0,
+                       special_units->number);
       int i;
       for (i = 0; i < special_units->number; i++)
         {
+          size_t special_unit_file_idx = 0;
           FILE_NAME_PATH_COUNTER *special_unit_file;
           OUTPUT_UNIT *special_unit = special_units->list[i];
           ELEMENT *unit_command = special_unit->unit_command;
@@ -1892,8 +1896,11 @@ html_set_pages_files (CONVERTER *self, OUTPUT_UNIT_LIST 
*output_units,
                 add_to_files_source_info (files_source_info, filename,
                                           "special_unit", 0, unit_command, 0);
             }
-          special_unit_file
+          special_unit_file_idx
             = set_output_unit_file (self, special_unit, filename, 1);
+          self->special_unit_file_indices[i] = special_unit_file_idx;
+          special_unit_file
+             = &self->output_unit_files.list[special_unit_file_idx];
           if (self->conf->DEBUG > 0)
             fprintf (stderr, "Special page: %s(%d)\n", filename,
                              special_unit_file->counter);
@@ -2000,12 +2007,12 @@ html_prepare_units_directions_files (CONVERTER *self,
 
  /* elements_in_file_count is only set in HTML, not in
     Texinfo::Convert::Converter */
-  if (self->output_unit_files)
+  if (self->output_unit_files.number)
     {
-      for (i = 0; i < self->output_unit_files->number; i++)
+      for (i = 0; i < self->output_unit_files.number; i++)
         {
           FILE_NAME_PATH_COUNTER *file_counter
-            = &self->output_unit_files->list[i];
+            = &self->output_unit_files.list[i];
 
           /* counter is dynamic, decreased when the element is encountered
              elements_in_file_count is not modified afterwards */
@@ -2265,6 +2272,11 @@ html_converter_initialize (CONVERTER *self)
       memset (self->no_arg_formatted_cmd_translated.list, 0,
               self->no_arg_formatted_cmd.number * sizeof (enum command_id));
     }
+
+  self->file_changed_counter.list = (size_t *)
+      malloc (self->output_unit_files.number * sizeof (size_t));
+  memset (self->file_changed_counter.list, 0,
+          self->output_unit_files.number * sizeof (size_t));
 }
 
 void
@@ -3588,51 +3600,150 @@ html_convert_convert (CONVERTER *self, ELEMENT *root,
   return result.text;
 }
 
-void
-convert_output_output_unit_internal (CONVERTER *self, TEXT *result,
+int
+convert_output_output_unit_internal (CONVERTER *self,
+                                     ENCODING_CONVERSION *conversion,
+                                     TEXT *text,
                                      OUTPUT_UNIT *output_unit, int unit_nr)
 {
-  TEXT body;
+  FILE_NAME_PATH_COUNTER *unit_file = 0;
+  size_t file_index;
+  int empty_body = 0; /* set if body is empty and it is a special unit */
   char *output_unit_filename = output_unit->unit_filename;
 
   self->current_filename = output_unit_filename;
   self->modified_state |= HMSF_current_filename;
 
-  text_init (&body);
-  text_append (&body, "");
+  text_reset (text);
+  text_append (text, "");
 
   if (output_unit->unit_type == OU_special_unit)
     {
       char *debug_str;
       char *special_unit_variety = output_unit->special_unit_variety;
+
+      file_index = self->special_unit_file_indices[output_unit->index];
+      unit_file = &self->output_unit_files.list[file_index];
+
       xasprintf (&debug_str, "UNIT SPECIAL %s", special_unit_variety);
-      convert_convert_output_unit_internal (self, &body,
+      convert_convert_output_unit_internal (self, text,
                     output_unit, unit_nr, debug_str, "output s-unit");
       free (debug_str);
 
-      if (!strcmp (body.text, ""))
-        {
-         /*
-                   $self->{'file_counters'}->{$output_unit_filename}--;
-          */
-          return;
-        }
+      if (!strcmp (text->text, ""))
+        empty_body = 1;
     }
   else
     {
-      convert_convert_output_unit_internal (self, &body, output_unit,
+      file_index = self->output_unit_file_indices[output_unit->index];
+      unit_file = &self->output_unit_files.list[file_index];
+
+      convert_convert_output_unit_internal (self, text, output_unit,
                                             unit_nr, "UNIT", "output unit");
     }
 
+  unit_file->counter--;
+  if (!unit_file->counter_changed)
+    {
+      self->file_changed_counter.list[self->file_changed_counter.number]
+        = file_index;
+      self->file_changed_counter.number++;
+      unit_file->counter_changed = 1;
+    }
+
       /* register the output but do not print anything. Printing
          only when file_counters reach 0, to be sure that all the
          elements have been converted before headers are done. */
 
-  /* */
+  if (!empty_body)
+    {
+      if (!unit_file->first_unit)
+        {
+          unit_file->first_unit = output_unit;
+          text_init (&unit_file->body);
+        }
+      text_append (&unit_file->body, text->text);
+    }
+  else
+    {
+      if (!unit_file->first_unit
+          || unit_file->body.end == 0)
+        {
+          return 1;
+        }
+    }
+
+  if (unit_file->counter == 0)
+    {
+      OUTPUT_UNIT *file_output_unit = unit_file->first_unit;
+      char *file_end;
+      char *file_beginning;
+      char *out_filepath = unit_file->filepath;
+      char *path_encoding;
+      char *open_error_message;
 
+      char *encoded_out_filepath = encoded_output_file_name (self->conf,
+                               self->document->global_info, out_filepath,
+                                                       &path_encoding, 0);
+      FILE *file_fh = output_files_open_out (&self->output_files_information,
+                               encoded_out_filepath, &open_error_message, 0);
+      if (!file_fh)
+        {
+          message_list_document_error (self->error_messages, self->conf,
+                             "could not open %s for writing: %s",
+                             out_filepath, open_error_message);
+          return 0;
+        }
 
-  self->current_filename = 0;
-  self->modified_state |= HMSF_current_filename;
+      /* do end file first in case it requires some CSS */
+      file_end = call_formatting_function_format_end_file (self,
+                                              out_filepath, output_unit);
+      file_beginning = call_formatting_function_format_begin_file (self,
+                                           out_filepath, file_output_unit);
+      text_reset (text);
+      if (file_beginning)
+        {
+          text_append (text, file_beginning);
+          free (file_beginning);
+        }
+      if (unit_file->body.end)
+        {
+          text_append (text, unit_file->body.text);
+        }
+      if (file_end)
+        {
+          text_append (text, file_end);
+          free (file_end);
+        }
+      if (text->end)
+        {
+          char *result = encode_with_iconv (conversion->iconv, text->text, 0);
+          size_t res_len = strlen (result)+1;
+          size_t write_len = fwrite (result, sizeof (char), res_len,
+                                     file_fh);
+          free (result);
+          if (write_len != res_len)
+            { /* register error message instead? */
+              fprintf (stderr, "write to %s failed (%zu/%zu)\n",
+                       encoded_out_filepath, write_len, res_len);
+              return 0;
+            }
+        }
+      /* NOTE do not close STDOUT here to be in line with perl code */
+      if (strcmp (out_filepath, "-"))
+        {
+          output_files_register_closed (&self->output_files_information,
+                                        encoded_out_filepath);
+          if (!fclose (file_fh))
+            {
+              message_list_document_error (self->error_messages, self->conf,
+                             "error on closing %s: %s",
+                             out_filepath, strerror (errno));
+              return 0;
+            }
+        }
+    }
+  return 1;
 }
 
 char *
@@ -3642,7 +3753,9 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
                      char *output_file, char *destination_directory,
                      char *output_filename, char *document_name)
 {
+  int status = 1;
   TEXT result;
+  TEXT text; /* reused for all the output units */
   char *title_titlepage;
 
   OUTPUT_UNIT_LIST *output_units
@@ -3651,6 +3764,7 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
     = retrieve_output_units (special_units_descriptor);
 
   text_init (&result);
+  text_init (&text);
 
   text_append (&result, "");
 
@@ -3700,12 +3814,10 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
   if (!output_units || !output_units->number
       || !output_units->list[0]->unit_filename)
     {
-      TEXT body;
-      char *footer;
-      char *header;
+      char *file_end;
+      char *file_beginning;
 
-      text_init (&body);
-      text_append (&body, "");
+      text_append (&text, "");
 
       /* in perl there is code for a case that should not be possible,
          with current_filename ne '' here.  This code is no present here */
@@ -3716,7 +3828,7 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
           for (i = 0; i < output_units->number; i++)
             {
               OUTPUT_UNIT *output_unit = output_units->list[i];
-              convert_convert_output_unit_internal (self, &body, output_unit,
+              convert_convert_output_unit_internal (self, &text, output_unit,
                              unit_nr, "UNIT NO-PAGE", "no-page output unit");
               unit_nr++;
             }
@@ -3728,7 +3840,7 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
               for (i = 0; i < special_units->number; i++)
                 {
                   OUTPUT_UNIT *special_unit = special_units->list[i];
-                  convert_convert_output_unit_internal (self, &body,
+                  convert_convert_output_unit_internal (self, &text,
                                  special_unit, unit_nr, "UNIT NO-PAGE",
                                  "no-page output unit");
                   unit_nr++;
@@ -3741,34 +3853,33 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
           if (self->conf->DEBUG > 0)
             fprintf (stderr, "\nNO UNIT NO PAGE\n");
 
-          text_append (&body, self->title_titlepage);
-          convert_to_html_internal (self, root, &body,
+          text_append (&text, self->title_titlepage);
+          convert_to_html_internal (self, root, &text,
                                      "no-page output no unit");
           footnotes_segment
             = call_formatting_function_format_footnotes_segment (self);
           if (footnotes_segment)
             {
-              text_append (&body, footnotes_segment);
+              text_append (&text, footnotes_segment);
               free (footnotes_segment);
             }
         }
 
       /* do end file first, in case it needs some CSS */
-      footer = call_formatting_function_format_end_file (self,
+      file_end = call_formatting_function_format_end_file (self,
                                                      output_filename, 0);
-      header = call_formatting_function_format_begin_file (self,
+      file_beginning = call_formatting_function_format_begin_file (self,
                                                      output_filename, 0);
-      if (footer)
+      if (file_beginning)
         {
-          text_append (&result, footer);
-          free (footer);
+          text_append (&result, file_beginning);
+          free (file_beginning);
         }
-      text_append (&result, body.text);
-      free (body.text);
-      if (header)
+      text_append (&result, text.text);
+      if (file_end)
         {
-          text_append (&result, header);
-          free (header);
+          text_append (&result, file_end);
+          free (file_end);
         }
       self->current_filename = 0;
       self->modified_state |= HMSF_current_filename;
@@ -3777,6 +3888,11 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
     {
       int unit_nr = 0;
       int i;
+      ENCODING_CONVERSION *conversion = 0;
+
+      if (self->conf->OUTPUT_ENCODING_NAME)
+        conversion = get_encoding_conversion (self->conf->OUTPUT_ENCODING_NAME,
+                                              &output_conversions);
 
       if (self->conf->DEBUG > 0)
         fprintf (stderr, "DO Units with filenames\n");
@@ -3784,8 +3900,10 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
       for (i = 0; i < output_units->number; i++)
         {
           OUTPUT_UNIT *output_unit = output_units->list[i];
-          convert_output_output_unit_internal (self, &result, output_unit,
-                                               unit_nr);
+          status = convert_output_output_unit_internal (self, conversion,
+                                               &text, output_unit, unit_nr);
+          if (!status)
+            goto out;
           unit_nr++;
         }
       if (special_units && special_units->number)
@@ -3793,11 +3911,25 @@ html_convert_output (CONVERTER *self, ELEMENT *root,
           for (i = 0; i < special_units->number; i++)
             {
               OUTPUT_UNIT *special_unit = special_units->list[i];
-              convert_output_output_unit_internal (self, &result, special_unit,
-                                                   unit_nr);
+              status = convert_output_output_unit_internal (self, conversion,
+                                                &text, special_unit, unit_nr);
+              if (!status)
+                goto out;
               unit_nr++;
             }
         }
+      self->current_filename = 0;
+      self->modified_state |= HMSF_current_filename;
+    }
+
+ out:
+  free (text.text);
+
+  if (status)
+    return result.text;
+  else
+    {
+      free (result.text);
+      return 0;
     }
-  return result.text;
 }
diff --git a/tp/Texinfo/XS/convert/converter.c 
b/tp/Texinfo/XS/convert/converter.c
index 49934d339f..ae45a554a9 100644
--- a/tp/Texinfo/XS/convert/converter.c
+++ b/tp/Texinfo/XS/convert/converter.c
@@ -360,33 +360,37 @@ top_node_filename (CONVERTER *self, char *document_name)
 void
 initialize_output_units_files (CONVERTER *self)
 {
-  self->output_unit_files = (FILE_NAME_PATH_COUNTER_LIST *)
-    malloc (sizeof (FILE_NAME_PATH_COUNTER_LIST));
-  memset (self->output_unit_files, 0,
-    sizeof (FILE_NAME_PATH_COUNTER_LIST));
+  /* nothing to do, should have been initialized during converter
+     initialization */
 }
 
-static FILE_NAME_PATH_COUNTER *
-find_output_unit_file (CONVERTER *self, char *filename)
+static size_t
+find_output_unit_file (CONVERTER *self, char *filename, int *status)
 {
   FILE_NAME_PATH_COUNTER_LIST *output_unit_files
-    = self->output_unit_files;
+    = &self->output_unit_files;
   int i;
+  *status = 0;
+
   for (i = 0; i < output_unit_files->number; i++)
     {
       if (!strcmp (output_unit_files->list[i].normalized_filename, filename))
-        return &output_unit_files->list[i];
+        {
+          *status = 1;
+          return i;
+        }
     }
   return 0;
 }
 
-static FILE_NAME_PATH_COUNTER *
+static size_t
 add_output_units_file (CONVERTER *self, char *filename,
                        char *normalized_filename)
 {
+  size_t file_index;
   FILE_NAME_PATH_COUNTER *new_output_unit_file;
   FILE_NAME_PATH_COUNTER_LIST *output_unit_files
-    = self->output_unit_files;
+    = &self->output_unit_files;
 
   if (output_unit_files->number == output_unit_files->space)
     {
@@ -396,7 +400,8 @@ add_output_units_file (CONVERTER *self, char *filename,
         fatal ("realloc failed");
     }
 
-  new_output_unit_file = &output_unit_files->list[output_unit_files->number];
+  file_index = output_unit_files->number;
+  new_output_unit_file = &output_unit_files->list[file_index];
   memset (new_output_unit_file, 0, sizeof (FILE_NAME_PATH_COUNTER));
   new_output_unit_file->filename = strdup (filename);
   if (normalized_filename)
@@ -406,72 +411,84 @@ add_output_units_file (CONVERTER *self, char *filename,
 
   output_unit_files->number++;
 
-  return new_output_unit_file;
+  return file_index;
 }
 
 /*
   If CASE_INSENSITIVE_FILENAMES is set, reuse the first
   filename with the same name insensitive to the case.
  */
-static FILE_NAME_PATH_COUNTER *
+static size_t
 register_normalize_case_filename (CONVERTER *self, char *filename)
 {
-  FILE_NAME_PATH_COUNTER *output_unit_file;
+  size_t output_unit_file_idx;
   if (self->conf->CASE_INSENSITIVE_FILENAMES > 0)
     {
       char *lc_filename = to_upper_or_lower_multibyte (filename, -1);
-      output_unit_file = find_output_unit_file (self, lc_filename);
-      if (output_unit_file)
+      int status;
+      output_unit_file_idx = find_output_unit_file (self, lc_filename, 
&status);
+      if (status)
         {
           if (self->conf->DEBUG > 0)
             {
-              fprintf (stderr, "Reusing case-insensitive %s for %s\n",
-                       output_unit_file->filename, filename);
+              FILE_NAME_PATH_COUNTER *output_unit_file
+                = &self->output_unit_files.list[output_unit_file_idx];
+              fprintf (stderr, "Reusing case-insensitive %s(%zu) for %s\n",
+                       output_unit_file->filename, output_unit_file_idx,
+                       filename);
             }
           free (lc_filename);
         }
       else
         {
-          output_unit_file = add_output_units_file (self, filename,
-                                                    lc_filename);
+          output_unit_file_idx = add_output_units_file (self, filename,
+                                                        lc_filename);
           free (lc_filename);
         }
     }
   else
     {
-      output_unit_file = find_output_unit_file (self, filename);
-      if (output_unit_file)
+      int status;
+      output_unit_file_idx = find_output_unit_file (self, filename, &status);
+      if (status)
         {
           if (self->conf->DEBUG > 0)
             {
-              fprintf (stderr, "Reusing %s for %s\n",
-                       output_unit_file->filename, filename);
+              FILE_NAME_PATH_COUNTER *output_unit_file
+                = &self->output_unit_files.list[output_unit_file_idx];
+              fprintf (stderr, "Reusing %s(%zu) for %s\n",
+                       output_unit_file->filename, output_unit_file_idx,
+                       filename);
             }
         }
       else
-        output_unit_file = add_output_units_file (self, filename, 0);
+        output_unit_file_idx = add_output_units_file (self, filename, 0);
     }
-  return output_unit_file;
+  return output_unit_file_idx;
 }
 
-FILE_NAME_PATH_COUNTER *
+size_t
 set_output_unit_file (CONVERTER *self, OUTPUT_UNIT *output_unit,
                       char *filename, int set_counter)
 {
+  size_t output_unit_file_idx
+     = register_normalize_case_filename (self, filename);
   FILE_NAME_PATH_COUNTER *output_unit_file
-    = register_normalize_case_filename (self, filename);
+    = &self->output_unit_files.list[output_unit_file_idx];
   if (set_counter)
     output_unit_file->counter++;
   output_unit->unit_filename = output_unit_file->filename;
-  return output_unit_file;
+  return output_unit_file_idx;
 }
 
 void
 set_file_path (CONVERTER *self, char *filename, char *filepath,
                char *destination_directory)
 {
+  size_t output_unit_file_idx
+      = register_normalize_case_filename (self, filename);
   FILE_NAME_PATH_COUNTER *output_unit_file
-    = register_normalize_case_filename (self, filename);
+    = &self->output_unit_files.list[output_unit_file_idx];
   char *filepath_str;
   int free_filepath = 0;
 
diff --git a/tp/Texinfo/XS/convert/converter.h 
b/tp/Texinfo/XS/convert/converter.h
index e7e8c087f8..9fab969d77 100644
--- a/tp/Texinfo/XS/convert/converter.h
+++ b/tp/Texinfo/XS/convert/converter.h
@@ -31,8 +31,7 @@ char *top_node_filename (CONVERTER *self, char 
*document_name);
 
 
 void initialize_output_units_files (CONVERTER *self);
-FILE_NAME_PATH_COUNTER *set_output_unit_file (CONVERTER *self,
-                                          OUTPUT_UNIT *output_unit,
+size_t set_output_unit_file (CONVERTER *self, OUTPUT_UNIT *output_unit,
                                     char *filename, int set_counter);
 void set_file_path (CONVERTER *self, char *filename, char *filepath,
                     char *destination_directory);
diff --git a/tp/Texinfo/XS/main/build_perl_info.c 
b/tp/Texinfo/XS/main/build_perl_info.c
index 232b8b4bc3..96c8f1cd1b 100644
--- a/tp/Texinfo/XS/main/build_perl_info.c
+++ b/tp/Texinfo/XS/main/build_perl_info.c
@@ -211,6 +211,18 @@ newSVpv_utf8 (const char *str, STRLEN len)
   return sv;
 }
 
+/* Used to create a string considered as bytes by perl */
+SV *
+newSVpv_byte (const char *str, STRLEN len)
+{
+  SV *sv;
+  dTHX;
+
+  sv = newSVpv (str, len);
+  SvUTF8_off (sv);
+  return sv;
+}
+
 static void
 store_additional_info (ELEMENT *e, ASSOCIATED_INFO* a, char *key)
 {
@@ -2191,6 +2203,8 @@ build_html_formatting_state (CONVERTER *converter, 
unsigned long flags)
   AV *document_context_av;
   SV **multiple_pass_sv;
   AV *multiple_pass_av;
+  SV **file_counters_sv;
+  HV *file_counters_hv;
   /*
   SV **files_information_sv;
   HV *files_information_hv;
@@ -2204,6 +2218,7 @@ build_html_formatting_state (CONVERTER *converter, 
unsigned long flags)
 
   hv = converter->hv;
 
+#define FETCH(key) key##_sv = hv_fetch (hv, #key, strlen (#key), 0);
 #define STORE(key, value) hv_store (hv, key, strlen (key), value, 0)
 
   if (flags & HMSF_converter_state)
@@ -2251,8 +2266,7 @@ build_html_formatting_state (CONVERTER *converter, 
unsigned long flags)
 
   if (flags & HMSF_document_context)
     {
-      document_context_sv = hv_fetch (hv, "document_context",
-                                      strlen ("document_context"), 0);
+      FETCH(document_context);
 
       if (!document_context_sv)
         {
@@ -2341,8 +2355,7 @@ build_html_formatting_state (CONVERTER *converter, 
unsigned long flags)
 
   if (flags & HMSF_multiple_pass)
     {
-      multiple_pass_sv = hv_fetch (hv, "multiple_pass",
-                                   strlen ("multiple_pass"), 0);
+      FETCH(multiple_pass);
 
       if (!multiple_pass_sv)
         {
@@ -2362,6 +2375,31 @@ build_html_formatting_state (CONVERTER *converter, 
unsigned long flags)
         }
     }
 
+  if (converter->file_changed_counter.number)
+    {
+      FETCH(file_counters);
+      file_counters_hv = (HV *) SvRV (*file_counters_sv);
+
+      int j;
+      for (j = 0; j < converter->file_changed_counter.number; j++)
+        {
+          size_t file_idx = converter->file_changed_counter.list[j];
+          FILE_NAME_PATH_COUNTER *output_unit_file
+            = &converter->output_unit_files.list[file_idx];
+          char *filename = output_unit_file->filename;
+
+          SV *filename_sv = newSVpv_utf8 (filename, 0);
+
+          hv_store_ent (file_counters_hv, filename_sv,
+                        newSViv (output_unit_file->counter), 0);
+
+          output_unit_file->counter_changed = 0;
+        }
+      memset (converter->file_changed_counter.list, 0,
+              converter->file_changed_counter.number * sizeof (size_t));
+      converter->file_changed_counter.number = 0;
+    }
+
   /*
   files_information_sv = hv_fetch (hv, "files_information",
                                   strlen ("files_information"), 0);
@@ -2386,6 +2424,127 @@ build_html_formatting_state (CONVERTER *converter, 
unsigned long flags)
   return newRV_noinc ((SV *) hv);
 }
 
+/* Texinfo::Common output_files_information API */
+void
+build_output_files_unclosed_files (HV *hv,
+                    OUTPUT_FILES_INFORMATION *output_files_information)
+{
+  SV **unclosed_files_sv;
+  HV *unclosed_files_hv;
+
+  FILE_STREAM_LIST *unclosed_files;
+  int i;
+
+  dTHX;
+
+  unclosed_files_sv = hv_fetch (hv, "unclosed_files",
+                                strlen ("unclosed_files"), 0);
+
+  if (!unclosed_files_sv)
+    {
+      unclosed_files_hv = newHV ();
+      hv_store (hv, "unclosed_files", strlen ("unclosed_files"),
+                newRV_noinc ((SV *) unclosed_files_hv), 0);
+    }
+  else
+    {
+      unclosed_files_hv = (HV *)SvRV (*unclosed_files_sv);
+    }
+
+  unclosed_files = &output_files_information->unclosed_files;
+  if (unclosed_files->number > 0)
+    {
+      for (i = 0; i < unclosed_files->number; i++)
+        {
+          FILE_STREAM *file_stream = &unclosed_files->list[i];
+          char *file_path = file_stream->file_path;
+          /* FIXME no way to pass back the FILE *stream see comments in
+             converter_types.h.  So for now we close here.
+          SV *file_path_sv = newSVpv_byte (file_path, 0);
+          hv_store_ent (unclosed_files_hv, file_path_sv, newSV (0), 0);
+           */
+          if (strcmp (file_path, "-"))
+            {
+              fclose (file_stream->stream);
+            }
+        }
+    }
+}
+
+/* input hv should be an output_files hv, in general setup by
+ $converter->{'output_files'} = Texinfo::Common::output_files_initialize(); */
+void
+build_output_files_opened_files (HV *hv,
+                    OUTPUT_FILES_INFORMATION *output_files_information)
+{
+  SV **opened_files_sv;
+  AV *opened_files_av;
+
+  STRING_LIST *opened_files;
+  int i;
+
+  dTHX;
+
+  opened_files_sv = hv_fetch (hv, "opened_files", strlen ("opened_files"), 0);
+
+  if (!opened_files_sv)
+    {
+      opened_files_av = newAV ();
+      hv_store (hv, "opened_files", strlen ("opened_files"),
+                newRV_noinc ((SV *) opened_files_av), 0);
+    }
+  else
+    {
+      opened_files_av = (AV *)SvRV (*opened_files_sv);
+    }
+
+  opened_files = &output_files_information->opened_files;
+  if (opened_files->number > 0)
+    {
+      for (i = 0; i < opened_files->number; i++)
+        {
+          char *file_path = opened_files->list[i];
+          SV *file_path_sv = newSVpv_byte (file_path, 0);
+          av_push (opened_files_av, file_path_sv);
+        }
+    }
+}
+
+/* for the perl converter associated to CONVERTER */
+void
+build_output_files_information (CONVERTER *converter)
+{
+  HV *hv;
+  SV **output_files_sv;
+  HV *output_files_hv;
+
+  dTHX;
+
+  if (!converter->hv)
+    return;
+
+  hv = converter->hv;
+
+  output_files_sv = hv_fetch (hv, "output_files",
+                                strlen ("output_files"), 0);
+
+  if (!output_files_sv)
+    {
+      output_files_hv = newHV ();
+      hv_store (hv, "output_files", strlen ("output_files"),
+                newRV_noinc ((SV *) output_files_hv), 0);
+    }
+  else
+    {
+      output_files_hv = (HV *)SvRV (*output_files_sv);
+    }
+
+  build_output_files_opened_files (output_files_hv,
+                                   &converter->output_files_information);
+  build_output_files_unclosed_files (output_files_hv,
+                                   &converter->output_files_information);
+}
+
 SV *
 build_html_command_formatted_args (HTML_ARGS_FORMATTED *args_formatted)
 {
diff --git a/tp/Texinfo/XS/main/build_perl_info.h 
b/tp/Texinfo/XS/main/build_perl_info.h
index b790c175ed..3638e76962 100644
--- a/tp/Texinfo/XS/main/build_perl_info.h
+++ b/tp/Texinfo/XS/main/build_perl_info.h
@@ -54,5 +54,7 @@ SV *build_out_filepaths (FILE_NAME_PATH_COUNTER_LIST 
*output_unit_files);
 
 SV *build_html_formatting_state (CONVERTER *converter, unsigned long flags);
 
+void build_output_files_information (CONVERTER *converter);
+
 SV *build_html_command_formatted_args (HTML_ARGS_FORMATTED *args_formatted);
 #endif
diff --git a/tp/Texinfo/XS/main/convert_utils.c 
b/tp/Texinfo/XS/main/convert_utils.c
index 6df76949e5..43101d9077 100644
--- a/tp/Texinfo/XS/main/convert_utils.c
+++ b/tp/Texinfo/XS/main/convert_utils.c
@@ -220,6 +220,35 @@ encoded_input_file_name (OPTIONS *options,
   return result;
 }
 
+char *
+encoded_output_file_name (OPTIONS *options, GLOBAL_INFO *global_information,
+                          char *file_name, char **file_name_encoding,
+                          SOURCE_INFO *source_info)
+{
+  char *result;
+  char *encoding = 0;
+  int status;
+
+  if (options && options->OUTPUT_FILE_NAME_ENCODING)
+    encoding = options->OUTPUT_FILE_NAME_ENCODING;
+  else if (options && options->DOC_ENCODING_FOR_OUTPUT_FILE_NAME != 0
+           || (!options))
+    {
+      if (global_information && global_information->input_encoding_name)
+        encoding = global_information->input_encoding_name;
+    }
+  else if (options)
+    encoding = options->LOCALE_ENCODING;
+
+  result = encode_string (file_name, encoding, &status, source_info);
+
+  if (status)
+    *file_name_encoding = strdup(encoding);
+   else
+    *file_name_encoding = 0;
+  return result;
+}
+
 ELEMENT *
 expand_verbatiminclude (ERROR_MESSAGE_LIST *error_messages,
                         OPTIONS *options, GLOBAL_INFO *global_information,
@@ -526,3 +555,130 @@ translated_command_tree (CONVERTER *self, enum command_id 
cmd)
     }
   return 0;
 }
+
+/*
+  API to open, set encoding and register files.
+*/
+
+/* in Texinfo::Common (because it is also used by main program) */
+
+/* in contrast with perl, we do not handle conversion to output encoding
+   in output_files_open_out, but in the caller program */
+
+static void
+register_unclosed_file (OUTPUT_FILES_INFORMATION *self, const char *file_path,
+                        FILE *stream)
+{
+  FILE_STREAM *file_stream;
+  int slot_found = 0;
+  size_t file_stream_index;
+  if (self->unclosed_files.number)
+    {
+      size_t i;
+      for (i = 0; i < self->unclosed_files.number; i++)
+        {
+          file_stream = &self->unclosed_files.list[i];
+          if (file_stream->file_path)
+            {
+              fprintf (stderr, "RUF:%zu: %s\n", i, file_stream->file_path);
+              if (!strcmp (file_path, file_stream->file_path))
+                {
+                  fprintf (stderr, "BUG: RUF: already open %zu: %s\n",
+                           i, file_path);
+                  file_stream->stream = stream;
+                  return;
+                }
+            }
+          else if (!slot_found)
+            {
+              file_stream_index = i;
+              slot_found = 1;
+            }
+        }
+    }
+
+  if (!slot_found)
+    {
+      if (self->unclosed_files.number == self->unclosed_files.space)
+        {
+          self->unclosed_files.list = realloc (self->unclosed_files.list,
+             (self->unclosed_files.space += 5) * sizeof (FILE_STREAM));
+        }
+      file_stream_index = self->unclosed_files.number;
+      self->unclosed_files.number++;
+    }
+
+  file_stream = &self->unclosed_files.list[file_stream_index];
+
+  file_stream->file_path = strdup (file_path);
+  file_stream->stream = stream;
+
+  return;
+}
+
+
+/*
+ FILE_PATH is the file path, it should be a binary string.
+ If BINARY is set, set binary mode.
+ Returns
+  - as return value, the opened filehandle, or 0 if opening failed,
+  - in *error_message, the errno message or 0 if opening succeeded.
+*/
+FILE *
+output_files_open_out (OUTPUT_FILES_INFORMATION *self, const char *file_path,
+                       char **error_message, int binary)
+{
+  FILE *stream_handle;
+  *error_message = 0;
+  if (!strcmp (file_path, "-"))
+    {
+      register_unclosed_file (self, file_path, stdout);
+      return stdout;
+    }
+  if (binary)
+    stream_handle = fopen (file_path, "wb");
+  else
+    stream_handle = fopen (file_path, "w");
+  if (!stream_handle)
+    {
+      *error_message = strdup (strerror (errno));
+      return 0;
+    }
+  else
+    {
+      register_unclosed_file (self, file_path, stream_handle);
+      add_string (file_path, &self->opened_files);
+    }
+  return stream_handle;
+}
+
+void
+output_files_register_closed (OUTPUT_FILES_INFORMATION *self,
+                              const char *file_path)
+{
+  size_t unclosed_files_nr = self->unclosed_files.number;
+  if (unclosed_files_nr)
+    {
+      size_t j;
+      for (j = unclosed_files_nr -1; j >= 0; j--)
+        {
+          FILE_STREAM *file_stream = &self->unclosed_files.list[j];
+          if (file_stream->file_path)
+            {
+              if (!strcmp (file_path, file_stream->file_path))
+                {
+                  free (file_stream->file_path);
+                  file_stream->file_path = 0;
+                  if (j == unclosed_files_nr -1)
+                    {
+                      self->unclosed_files.number--;
+                    }
+                  return;
+                }
+            }
+          else
+            fprintf (stderr, "REMARK: no unclosed file at %zu\n", j);
+        }
+    }
+  fprintf (stderr, "BUG: %s not opened\n", file_path);
+}
diff --git a/tp/Texinfo/XS/main/convert_utils.h 
b/tp/Texinfo/XS/main/convert_utils.h
index 64bcd1d443..658764f17b 100644
--- a/tp/Texinfo/XS/main/convert_utils.h
+++ b/tp/Texinfo/XS/main/convert_utils.h
@@ -2,6 +2,8 @@
 #ifndef CONVERT_UTILS_H
 #define CONVERT_UTILS_H
 
+#include <stdio.h>
+
 #include "options_types.h"
 #include "tree_types.h"
 #include "converter_types.h"
@@ -38,4 +40,15 @@ void destroy_parsed_def (PARSED_DEF *parsed_def);
 ELEMENT *definition_category_tree (OPTIONS *options, ELEMENT *current);
 
 ELEMENT *translated_command_tree (CONVERTER *self, enum command_id cmd);
+
+char *encoded_output_file_name (OPTIONS *options,
+                                GLOBAL_INFO *global_information,
+                                char *file_name, char **file_name_encoding,
+                                SOURCE_INFO *source_info);
+
+FILE *output_files_open_out (OUTPUT_FILES_INFORMATION *self,
+                             const char *file_path,
+                             char **error_message, int binary);
+void output_files_register_closed (OUTPUT_FILES_INFORMATION *self,
+                                   const char *file_path);
 #endif
diff --git a/tp/Texinfo/XS/main/converter_types.h 
b/tp/Texinfo/XS/main/converter_types.h
index 4d7e4ba42c..a1d538d2b7 100644
--- a/tp/Texinfo/XS/main/converter_types.h
+++ b/tp/Texinfo/XS/main/converter_types.h
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <stddef.h>
+#include <stdio.h>
 
 #include "tree_types.h"
 #include "element_types.h"
@@ -270,6 +271,11 @@ typedef struct COMMAND_ID_LIST {
     enum command_id *list;
 } COMMAND_ID_LIST;
 
+typedef struct ARRAY_INDEX_LIST {
+    size_t number;
+    size_t *list;
+} ARRAY_INDEX_LIST;
+
 typedef struct TRANSLATED_COMMAND {
     enum command_id cmd;
     char *translation;
@@ -282,6 +288,10 @@ typedef struct FILE_NAME_PATH_COUNTER {
     int counter;
     int elements_in_file_count; /* only used in HTML, corresponds to
                                    'elements_in_file_count' */
+    TEXT body;           /* file body output, used for HTML */
+    OUTPUT_UNIT *first_unit;
+    int counter_changed;  /* indicator to determine if the file has already
+                             been setup for change in counter passed to perl */
 } FILE_NAME_PATH_COUNTER;
 
 typedef struct FILE_NAME_PATH_COUNTER_LIST {
@@ -290,6 +300,34 @@ typedef struct FILE_NAME_PATH_COUNTER_LIST {
     FILE_NAME_PATH_COUNTER *list;
 } FILE_NAME_PATH_COUNTER_LIST;
 
+typedef struct FILE_STREAM {
+    char *file_path;
+    /* TODO see https://perldoc.perl.org/perlapio to pass to perl
+       PerlIO_importFILE. This creates a perl io object.
+       PerlIO *   PerlIO_importFILE  (FILE *stdio, const char *mode)
+       A SV is IO *, created by newIO
+        IO *  newIO()
+       the output perolIO of an IO is obtained by:
+        PerlIO *IoOFP(IO *io);
+       FIXME no way to set the PerlIO associated to an IO corresponding
+       to IoOFP?
+   From perlguts
+  All of these accessors macros are lvalues, there are no distinct _set() 
macros to modify the members of the IO object.
+       */
+    FILE *stream;
+} FILE_STREAM;
+
+typedef struct FILE_STREAM_LIST {
+    size_t number;
+    size_t space;
+    FILE_STREAM *list;
+} FILE_STREAM_LIST;
+
+typedef struct OUTPUT_FILES_INFORMATION {
+    STRING_LIST opened_files;
+    FILE_STREAM_LIST unclosed_files;
+} OUTPUT_FILES_INFORMATION;
+
 typedef struct SPECIAL_UNIT_DIRECTION {
     OUTPUT_UNIT *output_unit;
     char *direction;
@@ -352,7 +390,10 @@ typedef struct CONVERTER {
     EXPANDED_FORMAT *expanded_formats;
 
   /* output unit files API */
-    FILE_NAME_PATH_COUNTER_LIST *output_unit_files;
+    FILE_NAME_PATH_COUNTER_LIST output_unit_files;
+
+  /* API to open, set encoding and register files */
+    OUTPUT_FILES_INFORMATION output_files_information;
 
   /* perl converter. This should be HV *hv,
      but we don't want to include the Perl headers everywhere; */
@@ -387,14 +428,19 @@ typedef struct CONVERTER {
     FORMATTING_REFERENCE output_units_conversion[OU_special_unit+1];
 
     /* state only in C converter */
-    unsigned long modified_state; /* to determine if perl data should be 
rebuilt */
+    unsigned long modified_state; /* specifies which perl state to rebuild */
     ELEMENT *tree_to_build; /* C tree that needs to be built to perl before
                                calling perl functions on it */
-    COMMAND_ID_LIST no_arg_formatted_cmd_translated; /* list of commands that 
were
-                                translated that need to be passed back to perl 
*/
-    ELEMENT_LIST reset_target_commands; /* element targets that should have 
their texts
-                                           reset after language change */
-
+    COMMAND_ID_LIST no_arg_formatted_cmd_translated; /* list of commands that
+                         were translated and need to be passed back to perl */
+    ELEMENT_LIST reset_target_commands; /* element targets that should have
+                                           their texts reset after language
+                                           change */
+    ARRAY_INDEX_LIST file_changed_counter;  /* index of files in
+                                 output_unit_files with changed counter */
+    size_t *output_unit_file_indices;   /* array of indices in 
output_unit_files
+              each position corresponding to an output unit. */
+    size_t *special_unit_file_indices;  /* same for special output units */
 
     /* state common with perl converter */
     int document_global_context;
diff --git a/tp/Texinfo/XS/main/utils.c b/tp/Texinfo/XS/main/utils.c
index 5b462bf921..1ee53f6402 100644
--- a/tp/Texinfo/XS/main/utils.c
+++ b/tp/Texinfo/XS/main/utils.c
@@ -755,15 +755,14 @@ add_include_directory (char *input_filename, STRING_LIST 
*include_dirs_list)
 }
 
 void
-add_string (char *string, STRING_LIST *strings_list)
+add_string (const char *string, STRING_LIST *strings_list)
 {
   if (strings_list->number == strings_list->space)
     {
       strings_list->list = realloc (strings_list->list,
                    sizeof (char *) * (strings_list->space += 5));
     }
-  string = strdup (string);
-  strings_list->list[strings_list->number++] = string;
+  strings_list->list[strings_list->number++] = strdup (string);
 }
 
 void
@@ -783,6 +782,21 @@ merge_strings (STRING_LIST *strings_list, STRING_LIST 
*merged_strings)
   strings_list->number += merged_strings->number;
 }
 
+/* return the index +1, to return 0 if not found */
+size_t
+find_string (STRING_LIST *strings_list, const char *target)
+{
+  size_t j;
+  for (j = 0; j < strings_list->number; j++)
+    {
+      if (!strcmp (target, strings_list->list[j]))
+        {
+          return j+1;
+        }
+    }
+  return 0;
+}
+
 /* Return value to be freed by caller. */
 /* try to locate a file called FILENAME, looking for it in the list of include
    directories. */
diff --git a/tp/Texinfo/XS/main/utils.h b/tp/Texinfo/XS/main/utils.h
index 3fd9949407..ca9c1ced69 100644
--- a/tp/Texinfo/XS/main/utils.h
+++ b/tp/Texinfo/XS/main/utils.h
@@ -276,8 +276,9 @@ int is_content_empty (ELEMENT *tree, int 
do_not_ignore_index_entries);
 void clear_strings_list (STRING_LIST *include_dirs_list);
 void free_strings_list (STRING_LIST *strings);
 void destroy_strings_list (STRING_LIST *strings);
-void add_string (char *string, STRING_LIST *strings_list);
+void add_string (const char *string, STRING_LIST *strings_list);
 void merge_strings (STRING_LIST *strings_list, STRING_LIST *merged_strings);
+size_t find_string (STRING_LIST *strings_list, const char *string);
 
 void wipe_index (INDEX *idx);
 void wipe_index_names (INDEX **index_names);
diff --git a/tp/texi2any.pl b/tp/texi2any.pl
index f1f7eecff7..de51d7b96a 100755
--- a/tp/texi2any.pl
+++ b/tp/texi2any.pl
@@ -1400,7 +1400,7 @@ my $with_XS = ((not defined($ENV{TEXINFO_XS})
 
 my $file_number = -1;
 my @opened_files = ();
-my %unclosed_files;
+my %main_unclosed_files;
 my $error_count = 0;
 # main processing
 while(@input_files) {
@@ -1697,7 +1697,7 @@ while(@input_files) {
   if ($converter_unclosed_files) {
     foreach my $unclosed_file (keys(%$converter_unclosed_files)) {
       if ($unclosed_file eq '-') {
-        $unclosed_files{$unclosed_file}
+        $main_unclosed_files{$unclosed_file}
           = $converter_unclosed_files->{$unclosed_file};
       } else {
         if (!close($converter_unclosed_files->{$unclosed_file})) {
@@ -1827,8 +1827,8 @@ while(@input_files) {
   Texinfo::Document::remove_document($document);
 }
 
-foreach my $unclosed_file (keys(%unclosed_files)) {
-  if (!close($unclosed_files{$unclosed_file})) {
+foreach my $unclosed_file (keys(%main_unclosed_files)) {
+  if (!close($main_unclosed_files{$unclosed_file})) {
     warn(sprintf(__("%s: error on closing %s: %s\n"),
                      $real_command_name, $unclosed_file, $!));
     $error_count++;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]