texinfo-commits
[Top][All Lists]
Advanced

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

[no subject]


From: Patrice Dumas
Date: Sun, 17 Dec 2023 15:43:33 -0500 (EST)

branch: master
commit 08fba52d947c2b65d27f36e7d672bec05f979d6a
Author: Patrice Dumas <pertusus@free.fr>
AuthorDate: Sun Dec 17 21:38:09 2023 +0100

    * tp/Texinfo/XS/convert/call_html_perl_function.c
    (call_file_id_setting_label_target_name): allow null label_element.
    
    * tp/Texinfo/XS/convert/call_html_perl_function.c
    (call_formatting_function_format_css_lines),
    tp/Texinfo/XS/convert/convert_html.c (format_css_lines): add.
    
    * tp/Texinfo/XS/convert/convert_html.c
    (convert_string_tree_new_formatting_context)
    (html_prepare_converted_output_info): add to replace similar
    code used in diverse places.
    
    * tp/Texinfo/XS/convert/convert_html.c (html_default_entity_nbsp)
    (substitute_html_non_breaking_space, close_html_lone_element):
    implement substitute_html_non_breaking_space and
    close_html_lone_element in C.
    
    * tp/Texinfo/XS/main/converter_types.h (enum html_text_type)
    (direction_unit_direction), tp/Texinfo/XS/main/get_perl_info.c
    (html_get_direction_index): rename enum html_command_text_type as enum
    html_text_type, rename HCTT_* as HTT_* add possibilities of
    from_element_direction.  Rename enum button_unit_direction as enum
    direction_unit_direction.  Rename D_button_* as D_direction_*.  Define
    FIRSTINFILE_MIN_IDX, FIRSTINFILE_MAX_IDX, FIRSTINFILE_OFFSET,
    FIRSTINFILE_NR, NON_SPECIAL_DIRECTIONS_NR.
    
    * tp/Texinfo/XS/convert/convert_html.c (from_element_direction):
    implement in C.
    
    * tp/Texinfo/XS/convert/convert_html.c (BEGIN_FILE_INFORMATION)
    (destroy_begin_file_information, root_html_element_attributes_string)
    (file_header_information, html_reset_converter, html_convert_output),
    tp/Texinfo/XS/main/converter_types.h (CONVERTER): implement
    file_header_information in C, set date_in_header in converter.
    
    * tp/Texinfo/XS/convert/convert_html.c (direction_string_type_names)
    (direction_string_context_names, clear_direction_string_type)
    (direction_type_translation_context, direction_string)
    (html_free_converter, html_translate_names),
    tp/Texinfo/XS/convert/get_html_perl_info.c
    (html_converter_initialize_sv), tp/Texinfo/XS/main/converter_types.h
    (enum direction_string_context, enum direction_string_type)
    (HTML_DIRECTION_STRING_TRANSLATED, CONVERTER): get direction strings and
    translated direction s trings information from perl. implement
    direction_string in C.  Rename enum direction_string as enum
    direction_string_type.
    
    * tp/Texinfo/XS/convert/convert_html.c (get_links): implement in C.
    
    * tp/Texinfo/XS/convert/convert_html.c (html_command_text): fix case
    of string type, by removing element in tree to build, using
    add_to_contents_as_array instead of add_to_element_contents and
    destroying elements.
    
    * tp/Texinfo/XS/convert/convert_html.c (html_command_text): return an
    empty string instead of 0.
    
    * tp/Texinfo/XS/convert/convert_html.c (normalized_label_id_file):
    label_element can be null.
    
    * tp/Texinfo/XS/convert/convert_html.c
    (html_default_format_begin_file, format_begin_file): implement in C.
    
    * tp/Texinfo/Convert/HTML.pm (_check_htmlxref_already_warned)
    (_external_node_href), tp/Texinfo/XS/convert/ConvertXS.xs
    (html_check_htmlxref_already_warned),
    tp/Texinfo/XS/convert/convert_html.c
    (html_check_htmlxref_already_warned, external_node_href)
    (source_info_id, html_reset_converter, html_free_converter),
    tp/Texinfo/XS/main/converter_types.h (CONVERT): use a
    function for check_htmlxref_already_warned in perl, rename function to
    html_check_htmlxref_already_warned in C, add source_info_id, use
    source_info information to avoid duplicate messages for missing
    htmlxref.cnf entry in C.  Make check_htmlxref_already_warned a
    STRING_LIST, remove HTMLXREF_MANUAL_ELEMENT_WARNED_LIST and
    HTMLXREF_MANUAL_ELEMENT_WARNED.
    
    * tp/Texinfo/XS/convert/convert_html.c (external_node_href): bugfix
    setting up paths of external hrefs.
    
    * tp/Texinfo/Convert/HTML.pm (_source_info_id): remove code without
    effect.
---
 ChangeLog                                       |   87 +-
 tp/Texinfo/Convert/HTML.pm                      |   34 +-
 tp/Texinfo/XS/convert/ConvertXS.xs              |   23 +
 tp/Texinfo/XS/convert/call_html_perl_function.c |   70 +-
 tp/Texinfo/XS/convert/call_html_perl_function.h |    3 +
 tp/Texinfo/XS/convert/convert_html.c            | 1011 ++++++++++++++++++++---
 tp/Texinfo/XS/convert/convert_html.h            |    7 +
 tp/Texinfo/XS/convert/get_html_perl_info.c      |  165 ++++
 tp/Texinfo/XS/main/converter_types.h            |   66 +-
 tp/Texinfo/XS/main/get_perl_info.c              |    4 +-
 10 files changed, 1314 insertions(+), 156 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c226b2047f..ace9f45dba 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,88 @@
+2023-12-17  Patrice Dumas  <pertusus@free.fr>
+
+       * tp/Texinfo/XS/convert/call_html_perl_function.c
+       (call_file_id_setting_label_target_name): allow null label_element.
+
+       * tp/Texinfo/XS/convert/call_html_perl_function.c
+       (call_formatting_function_format_css_lines),
+       tp/Texinfo/XS/convert/convert_html.c (format_css_lines): add.
+
+       * tp/Texinfo/XS/convert/convert_html.c
+       (convert_string_tree_new_formatting_context)
+       (html_prepare_converted_output_info): add to replace similar
+       code used in diverse places.
+
+       * tp/Texinfo/XS/convert/convert_html.c (html_default_entity_nbsp)
+       (substitute_html_non_breaking_space, close_html_lone_element):
+       implement substitute_html_non_breaking_space and
+       close_html_lone_element in C.
+
+       * tp/Texinfo/XS/main/converter_types.h (enum html_text_type)
+       (direction_unit_direction), tp/Texinfo/XS/main/get_perl_info.c
+       (html_get_direction_index): rename enum html_command_text_type as enum
+       html_text_type, rename HCTT_* as HTT_* add possibilities of
+       from_element_direction.  Rename enum button_unit_direction as enum
+       direction_unit_direction.  Rename D_button_* as D_direction_*.  Define
+       FIRSTINFILE_MIN_IDX, FIRSTINFILE_MAX_IDX, FIRSTINFILE_OFFSET,
+       FIRSTINFILE_NR, NON_SPECIAL_DIRECTIONS_NR.
+
+       * tp/Texinfo/XS/convert/convert_html.c (from_element_direction):
+       implement in C.
+
+       * tp/Texinfo/XS/convert/convert_html.c (BEGIN_FILE_INFORMATION)
+       (destroy_begin_file_information, root_html_element_attributes_string)
+       (file_header_information, html_reset_converter, html_convert_output),
+       tp/Texinfo/XS/main/converter_types.h (CONVERTER): implement
+       file_header_information in C, set date_in_header in converter.
+
+       * tp/Texinfo/XS/convert/convert_html.c (direction_string_type_names)
+       (direction_string_context_names, clear_direction_string_type)
+       (direction_type_translation_context, direction_string)
+       (html_free_converter, html_translate_names),
+       tp/Texinfo/XS/convert/get_html_perl_info.c
+       (html_converter_initialize_sv), tp/Texinfo/XS/main/converter_types.h
+       (enum direction_string_context, enum direction_string_type)
+       (HTML_DIRECTION_STRING_TRANSLATED, CONVERTER): get direction strings and
+       translated direction s trings information from perl. implement
+       direction_string in C.  Rename enum direction_string as enum
+       direction_string_type.
+
+       * tp/Texinfo/XS/convert/convert_html.c (get_links): implement in C.
+
+       * tp/Texinfo/XS/convert/convert_html.c (html_command_text): fix case
+       of string type, by removing element in tree to build, using
+       add_to_contents_as_array instead of add_to_element_contents and
+       destroying elements.
+
+       * tp/Texinfo/XS/convert/convert_html.c (html_command_text): return an
+       empty string instead of 0.
+
+       * tp/Texinfo/XS/convert/convert_html.c (normalized_label_id_file):
+       label_element can be null.
+
+       * tp/Texinfo/XS/convert/convert_html.c
+       (html_default_format_begin_file, format_begin_file): implement in C.
+
+       * tp/Texinfo/Convert/HTML.pm (_check_htmlxref_already_warned)
+       (_external_node_href), tp/Texinfo/XS/convert/ConvertXS.xs
+       (html_check_htmlxref_already_warned),
+       tp/Texinfo/XS/convert/convert_html.c
+       (html_check_htmlxref_already_warned, external_node_href)
+       (source_info_id, html_reset_converter, html_free_converter),
+       tp/Texinfo/XS/main/converter_types.h (CONVERT): use a
+       function for check_htmlxref_already_warned in perl, rename function to
+       html_check_htmlxref_already_warned in C, add source_info_id, use
+       source_info information to avoid duplicate messages for missing
+       htmlxref.cnf entry in C.  Make check_htmlxref_already_warned a
+       STRING_LIST, remove HTMLXREF_MANUAL_ELEMENT_WARNED_LIST and
+       HTMLXREF_MANUAL_ELEMENT_WARNED.
+
+       * tp/Texinfo/XS/convert/convert_html.c (external_node_href): bugfix
+       setting up paths of external hrefs.
+
+       * tp/Texinfo/Convert/HTML.pm (_source_info_id): remove code without
+       effect.
+
 2023-12-17  Patrice Dumas  <pertusus@free.fr>
 
        * tp/Texinfo/Convert/HTML.pm (from_element_direction): remove
@@ -10,7 +95,7 @@
        $command, $command as $target_root and $node as $target_node.
 
        * tp/Texinfo/Convert/HTML.pm (_source_info_id, _external_node_href):
-       use source_info information to avoid duplicate messages above missing
+       use source_info information to avoid duplicate messages for missing
        htmlxref.cnf entry, not tree element.
 
        * tp/Texinfo/Convert/HTML.pm (_file_header_information): change order
diff --git a/tp/Texinfo/Convert/HTML.pm b/tp/Texinfo/Convert/HTML.pm
index 4f2429a8f2..744a919911 100644
--- a/tp/Texinfo/Convert/HTML.pm
+++ b/tp/Texinfo/Convert/HTML.pm
@@ -215,6 +215,9 @@ my %XS_conversion_overrides = (
    => 
"Texinfo::Convert::ConvertXS::html_associate_pending_formatted_inline_content",
   "Texinfo::Convert::HTML::get_associated_formatted_inline_content",
    => 
"Texinfo::Convert::ConvertXS::html_get_associated_formatted_inline_content",
+  "Texinfo::Convert::HTML::_check_htmlxref_already_warned"
+   => "Texinfo::Convert::ConvertXS::html_check_htmlxref_already_warned",
+
   "Texinfo::Convert::HTML::_XS_get_index_entries_sorted_by_letter"
    => "Texinfo::Convert::ConvertXS::get_index_entries_sorted_by_letter",
   "Texinfo::Convert::HTML::_XS_html_merge_index_entries"
@@ -10175,8 +10178,6 @@ sub _source_info_id($)
   $result .= '-';
   if (defined($source_info->{'macro'})) {
     $result .= $source_info->{'macro'};
-  } else {
-    $result .= '';
   }
   $result .= '-';
   if (defined($source_info->{'line_nr'})) {
@@ -10187,6 +10188,26 @@ sub _source_info_id($)
   return $result;
 }
 
+sub _check_htmlxref_already_warned($$$)
+{
+  my $self = shift;
+  my $manual_name = shift;
+  my $source_info = shift;
+
+  my $node_manual_key;
+  if ($source_info) {
+    $node_manual_key = _source_info_id($source_info).'-'.$manual_name;
+  } else {
+    $node_manual_key = 'UNDEF-'.$manual_name;
+  }
+  if ($self->{'check_htmlxref_already_warned'}->{$node_manual_key}) {
+    return 1;
+  } else {
+    $self->{'check_htmlxref_already_warned'}->{$node_manual_key} = 1;
+    return 0;
+  }
+}
+
 sub _external_node_href($$$;$)
 {
   my $self = shift;
@@ -10261,20 +10282,17 @@ sub _external_node_href($$$;$)
     } else { # nothing specified for that manual, use default
       if ($self->get_conf('CHECK_HTMLXREF')) {
         if (defined($source_command) and $source_command->{'source_info'}) {
-          my $node_manual_key
-            = 
_source_info_id($source_command->{'source_info'}).'-'.$manual_name;
-          if (!$self->{'check_htmlxref_already_warned'}->{$node_manual_key}) {
+          if (!_check_htmlxref_already_warned($self, $manual_name,
+                                         $source_command->{'source_info'})) {
             $self->converter_line_warn(sprintf(__(
                     "no htmlxref.cnf entry found for `%s'"), $manual_name),
                              $source_command->{'source_info'});
-            $self->{'check_htmlxref_already_warned'}->{$node_manual_key} = 1;
           }
         } else {
-          if 
(!$self->{'check_htmlxref_already_warned'}->{'UNDEF-'.$manual_name}) {
+          if (!_check_htmlxref_already_warned($self, $manual_name, undef)) {
             $self->converter_document_warn(sprintf(__(
               "no htmlxref.cnf entry found for `%s'"), $manual_name),
               );
-            $self->{'check_htmlxref_already_warned'}->{'UNDEF-'.$manual_name} 
= 1;
             cluck;
           }
         }
diff --git a/tp/Texinfo/XS/convert/ConvertXS.xs 
b/tp/Texinfo/XS/convert/ConvertXS.xs
index d7b9aa8a88..767d32e64c 100644
--- a/tp/Texinfo/XS/convert/ConvertXS.xs
+++ b/tp/Texinfo/XS/convert/ConvertXS.xs
@@ -1098,6 +1098,29 @@ html_get_associated_formatted_inline_content (SV 
*converter_in, SV *element_sv)
     OUTPUT:
          RETVAL
 
+int
+html_check_htmlxref_already_warned (SV *converter_in, manual_name, SV 
*source_info_sv)
+         char *manual_name = (char *)SvPVutf8_nolen($arg);
+      PREINIT:
+         CONVERTER *self;
+         SOURCE_INFO *source_info = 0;
+     CODE:
+         self = get_sv_converter (converter_in,
+                                  "html_check_htmlxref_already_warned");
+         if (SvOK (source_info_sv))
+           source_info = get_source_info (source_info_sv);
+
+         RETVAL = html_check_htmlxref_already_warned (self, manual_name,
+                                                      source_info);
+         if (source_info)
+           {
+             free (source_info->macro);
+             free (source_info->file_name);
+             free (source_info);
+           }
+    OUTPUT:
+         RETVAL
+
 void
 html_merge_index_entries (SV *converter_in)
       PREINIT:
diff --git a/tp/Texinfo/XS/convert/call_html_perl_function.c 
b/tp/Texinfo/XS/convert/call_html_perl_function.c
index 4c14ef7d0a..12855a73f5 100644
--- a/tp/Texinfo/XS/convert/call_html_perl_function.c
+++ b/tp/Texinfo/XS/convert/call_html_perl_function.c
@@ -201,9 +201,6 @@ call_file_id_setting_label_target_name (CONVERTER *self,
 
   *called = 0;
 
-  if (!label_element->hv)
-    return 0;
-
   if (!self->hv)
     return 0;
 
@@ -224,6 +221,12 @@ call_file_id_setting_label_target_name (CONVERTER *self,
           STRLEN len;
           char *result;
           SV *target_ret_sv;
+          SV *label_element_sv;
+
+          if (label_element)
+            label_element_sv = newRV_inc (label_element->hv);
+          else
+            label_element_sv = newSV (0);
 
           *called = 1;
 
@@ -237,7 +240,7 @@ call_file_id_setting_label_target_name (CONVERTER *self,
 
           PUSHs(sv_2mortal (newRV_inc (self->hv)));
           PUSHs(sv_2mortal (newSVpv (normalized, 0)));
-          PUSHs(sv_2mortal (newRV_inc (label_element->hv)));
+          PUSHs(sv_2mortal (label_element_sv));
           PUSHs(sv_2mortal (newSVpv (target, 0)));
           PUTBACK;
 
@@ -1097,6 +1100,65 @@ call_formatting_function_format_footnotes_sequence 
(CONVERTER *self,
   return result;
 }
 
+char *
+call_formatting_function_format_css_lines (CONVERTER *self,
+                         const FORMATTING_REFERENCE *formatting_reference,
+                                              const char *filename)
+{
+  int count;
+  char *result;
+  char *result_ret;
+  STRLEN len;
+  SV *result_sv;
+  SV *formatting_reference_sv;
+
+  dTHX;
+
+  if (!self->hv)
+    return 0;
+
+  formatting_reference_sv = formatting_reference->sv_reference;
+
+  if (self->modified_state)
+    {
+      build_html_formatting_state (self, self->modified_state);
+      self->modified_state = 0;
+    }
+
+  dSP;
+
+  ENTER;
+  SAVETMPS;
+
+  PUSHMARK(SP);
+  EXTEND(SP, 2);
+
+  PUSHs(sv_2mortal (newRV_inc (self->hv)));
+  PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0)));
+  PUTBACK;
+
+  count = call_sv (formatting_reference_sv,
+                   G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("format_css_lines should return 1 item\n");
+
+  result_sv = POPs;
+  result_ret = SvPVutf8 (result_sv, len);
+  result = strdup (result_ret);
+
+  PUTBACK;
+
+  FREETMPS;
+  LEAVE;
+
+  get_shared_conversion_state (self);
+
+  return result;
+}
+
 char *
 call_formatting_function_format_end_file (CONVERTER *self,
                          const FORMATTING_REFERENCE *formatting_reference,
diff --git a/tp/Texinfo/XS/convert/call_html_perl_function.h 
b/tp/Texinfo/XS/convert/call_html_perl_function.h
index 365d974cdd..007335d79c 100644
--- a/tp/Texinfo/XS/convert/call_html_perl_function.h
+++ b/tp/Texinfo/XS/convert/call_html_perl_function.h
@@ -70,6 +70,9 @@ char *call_formatting_function_format_footnotes_segment 
(CONVERTER *self,
                          const FORMATTING_REFERENCE *formatting_reference);
 char *call_formatting_function_format_footnotes_sequence (CONVERTER *self,
                          const FORMATTING_REFERENCE *formatting_reference);
+char *call_formatting_function_format_css_lines (CONVERTER *self,
+                         const FORMATTING_REFERENCE *formatting_reference,
+                                              const char *filename);
 char *call_formatting_function_format_end_file (CONVERTER *self,
                          const FORMATTING_REFERENCE *formatting_reference,
                     const char *filename, const OUTPUT_UNIT *output_unit);
diff --git a/tp/Texinfo/XS/convert/convert_html.c 
b/tp/Texinfo/XS/convert/convert_html.c
index 9f2bc8d848..2bcbb92925 100644
--- a/tp/Texinfo/XS/convert/convert_html.c
+++ b/tp/Texinfo/XS/convert/convert_html.c
@@ -111,7 +111,7 @@ char *html_global_unit_direction_names[] = {
 
 char *html_conversion_context_type_names[] = {
   #define cctx_type(name) #name,
-    HCC_CONTEXT_TYPES_LIST
+   HCC_CONTEXT_TYPES_LIST
   #undef cctx_type
 };
 
@@ -122,18 +122,31 @@ char *html_formatting_reference_names[] = {
 };
 
 const char *html_argument_formatting_type_names[] = {
-   #define html_aft_type(name) #name,
-    HTML_ARGUMENTS_FORMATTED_FORMAT_TYPE
-   #undef html_aft_type
+  #define html_aft_type(name) #name,
+   HTML_ARGUMENTS_FORMATTED_FORMAT_TYPE
+  #undef html_aft_type
 };
 
 const char *special_unit_info_type_names[SUI_type_heading + 1] =
 {
   #define sui_type(name) #name,
-    SUI_TYPES_LIST
+   SUI_TYPES_LIST
   #undef sui_type
 };
 
+const char *direction_string_type_names[] =
+{
+  #define tds_type(name) #name,
+   TDS_TRANSLATED_TYPES_LIST
+   TDS_NON_TRANSLATED_TYPES_LIST
+  #undef tds_type
+};
+
+const char *direction_string_context_names[] =
+{
+  "normal", "string"
+};
+
 const char *htmlxref_split_type_names[htmlxref_split_type_chapter + 1] =
 {
   "mono", "node", "section", "chapter"
@@ -811,6 +824,42 @@ html_count_elements_in_filename (CONVERTER *self,
   return count_elements_in_file_number (self, type, page_number);
 }
 
+/*
+static const char *xml_named_entity_nbsp = "&nbsp;";
+ */
+static const char *html_default_entity_nbsp = "&nbsp;";
+
+char *
+substitute_html_non_breaking_space (CONVERTER *self, const char *text)
+{
+  TEXT result;
+  text_init (&result);
+
+  const char *p = text;
+
+  while (*p)
+    {
+      const char *q = strstr (p, html_default_entity_nbsp);
+      if (q)
+        {
+          if (q - p)
+            {
+              text_append_n (&result, p, q - p);
+            }
+          text_append_n (&result,
+                self->special_character[SC_non_breaking_space].string,
+                self->special_character[SC_non_breaking_space].len);
+          p = q + 6; /* 6: length of html_default_entity_nbsp */
+        }
+      else
+        {
+          text_append (&result, p);
+          break;
+        }
+    }
+  return result.text;
+}
+
 ELEMENT *
 special_unit_info_tree (CONVERTER *self, const enum special_unit_info_tree 
type,
                         const char *special_unit_variety)
@@ -1718,7 +1767,7 @@ normalized_label_id_file (CONVERTER *self, char 
*normalized,
     = (TARGET_FILENAME *) malloc (sizeof (TARGET_FILENAME));
 
   int normalized_need_to_be_freed = 0;
-  if (!normalized)
+  if (!normalized && label_element)
     {
       normalized = convert_contents_to_identifier (label_element);
       normalized_need_to_be_freed = 1;
@@ -2500,6 +2549,14 @@ destroy_tree_added_elements (CONVERTER *self, 
TREE_ADDED_ELEMENTS *tree_elements
   free (tree_elements);
 }
 
+ELEMENT *
+new_element_added (TREE_ADDED_ELEMENTS *added_elements, enum element_type type)
+{
+  ELEMENT *new = new_element (type);
+  add_to_element_list (&added_elements->added, new);
+  return new;
+}
+
 static void
 push_html_formatting_context (HTML_FORMATTING_CONTEXT_STACK *stack,
                               char *context_name)
@@ -2661,6 +2718,126 @@ convert_tree_new_formatting_context (CONVERTER *self, 
const ELEMENT *tree,
   return result;
 }
 
+void
+clear_direction_string_type (CONVERTER *self, char ***type_directions_strings)
+{
+  int i;
+  int nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
+                      + self->special_unit_varieties.number;
+  int nr_dir_str_contexts = TDS_context_string + 1;
+
+  for (i = 0; i < nr_string_directions; i++)
+    {
+      char **direction_strings = type_directions_strings[i];
+      int j;
+      for (j = 0; j < nr_dir_str_contexts; j++)
+        {
+          free (direction_strings[j]);
+          direction_strings[j] = 0;
+        }
+    }
+}
+
+static const char *direction_type_translation_context[] =
+{
+  "button label", "description", "string"
+};
+
+char *
+direction_string (CONVERTER *self, int direction,
+                  enum direction_string_type string_type,
+                  enum direction_string_context context)
+{
+  if (direction >= FIRSTINFILE_MIN_IDX && direction <= FIRSTINFILE_MAX_IDX)
+    direction += FIRSTINFILE_OFFSET;
+  else if (direction > NON_SPECIAL_DIRECTIONS_NR - 1)
+    direction -= FIRSTINFILE_NR;
+
+  if (!self->directions_strings[string_type][direction][context])
+    {
+      HTML_DIRECTION_STRING_TRANSLATED *dir_translated
+        = &self->translated_direction_strings[string_type][direction];
+      if (dir_translated->to_convert)
+        {
+          char *result_string;
+          TEXT translation_context;
+          char *context_str;
+          ELEMENT *translated_tree;
+          ELEMENT *converted_tree;
+          const char *direction_name;
+          TREE_ADDED_ELEMENTS *string_tree = 0;
+          text_init (&translation_context);
+          if (direction < FIRSTINFILE_MIN_IDX)
+            direction_name = html_button_direction_names[direction];
+          else
+            direction_name =
+               self->special_unit_info[SUI_type_direction]
+                                  [direction - FIRSTINFILE_MIN_IDX];
+          text_append (&translation_context, direction_name);
+
+          if (direction == RUD_type_This)
+            text_append_n (&translation_context, " (current section)", 18);
+          text_append_n (&translation_context, " direction ", 11);
+          text_append (&translation_context,
+                       direction_type_translation_context[string_type]);
+          translated_tree = html_pgdt_tree (translation_context.text,
+                              dir_translated->to_convert, self->document,
+                              self, 0, 0);
+          free (translation_context.text);
+          if (context == TDS_context_string)
+            {
+              string_tree
+               = new_tree_added_elements (tree_added_status_elements_added);
+
+              converted_tree = new_element_added (string_tree, ET__string);
+
+              add_to_element_contents (converted_tree, translated_tree);
+            }
+          else
+            converted_tree = translated_tree;
+
+          xasprintf (&context_str, "DIRECTION %s %s/%s",
+                    direction_string_type_names[string_type],
+                    direction_string_context_names[context], direction_name);
+
+          add_to_element_list (&self->tree_to_build, converted_tree);
+
+          result_string
+            = convert_tree_new_formatting_context (self, converted_tree,
+                                  context_str, 0, context_str, 0);
+
+          remove_element_from_list (&self->tree_to_build, converted_tree);
+          free (context_str);
+
+          if (context == TDS_context_string)
+            destroy_tree_added_elements (self, string_tree);
+          destroy_element_and_children (translated_tree);
+          self->directions_strings[string_type][direction][context]
+                = result_string;
+        }
+      else
+        {
+          const char *context_converted_string = 0;
+          if (dir_translated->converted[context])
+            context_converted_string = dir_translated->converted[context];
+          else if (context == TDS_context_string)
+            context_converted_string
+              = dir_translated->converted[TDS_context_normal];
+          if (context_converted_string)
+            {
+              char *translated_string
+                = html_gdt_string (context_converted_string, self, 0, 0, 0);
+              char *result_string
+                = substitute_html_non_breaking_space (self, translated_string);
+              self->directions_strings[string_type][direction][context]
+                = result_string;
+              free (translated_string);
+            }
+        }
+    }
+  return self->directions_strings[string_type][direction][context];
+}
+
 static void
 register_added_target (HTML_ADDED_TARGET_LIST *added_targets,
                        HTML_TARGET *target)
@@ -2731,35 +2908,49 @@ find_htmlxref_manual
   return result;
 }
 
+/* to be freed by caller */
+static char *
+source_info_id (const SOURCE_INFO *source_info)
+{
+  TEXT result;
+
+  text_init (&result);
+  if (source_info->file_name)
+    text_append (&result, source_info->file_name);
+  text_append_n (&result, "-", 1);
+  if (source_info->macro)
+    text_append (&result, source_info->macro);
+  text_append_n (&result, "-", 1);
+  text_printf (&result, "%d", source_info->line_nr);
+  return result.text;
+}
+
 size_t
-check_htmlxref_already_warned (CONVERTER *self, const char *manual_name,
-                               const ELEMENT *source_command)
+html_check_htmlxref_already_warned (CONVERTER *self, const char *manual_name,
+                                    const SOURCE_INFO *source_info)
 {
-  size_t i;
-  HTMLXREF_MANUAL_ELEMENT_WARNED *htmlxref_warned;
-  HTMLXREF_MANUAL_ELEMENT_WARNED_LIST *htmlxref_warned_list
+  STRING_LIST *htmlxref_warned_list
     = &self->check_htmlxref_already_warned;
+  char *node_manual_key;
+  char *info_id;
+  size_t entry_nr;
 
-  for (i = 0; i < htmlxref_warned_list->number; i++)
-    {
-      htmlxref_warned = &htmlxref_warned_list->list[i];
-      if (!strcmp (htmlxref_warned->manual, manual_name)
-          && source_command == htmlxref_warned->element)
-        return i+1;
-    }
+  if (source_info)
+    info_id = source_info_id (source_info);
+  else
+    info_id = strdup ("UNDEF");
 
-  if (htmlxref_warned_list->number >= htmlxref_warned_list->space)
-    {
-      htmlxref_warned_list->list
-        = realloc (htmlxref_warned_list->list,
- (htmlxref_warned_list->space += 5) * sizeof (HTMLXREF_MANUAL_ELEMENT_WARNED));
-    }
-  htmlxref_warned = &htmlxref_warned_list->list[htmlxref_warned_list->number];
+  xasprintf (&node_manual_key, "%s-%s", info_id, manual_name);
+  free (info_id);
+
+  entry_nr = find_string (htmlxref_warned_list, node_manual_key);
+
+  if (entry_nr)
+    return entry_nr;
 
-  htmlxref_warned->element = source_command;
-  htmlxref_warned->manual = strdup (manual_name);
+  add_string (node_manual_key, htmlxref_warned_list);
+  free (node_manual_key);
 
-  htmlxref_warned_list->number++;
   return 0;
 }
 
@@ -2819,7 +3010,7 @@ external_node_href (CONVERTER *self, const ELEMENT 
*external_node,
 
       free (text_conv_options);
 
-      if (self->conf->IGNORE_REF_TO_TOP_NODE_UP && !strlen (target))
+      if (self->conf->IGNORE_REF_TO_TOP_NODE_UP > 0 && !strlen (target))
         {
           char *top_node_up = self->conf->TOP_NODE_UP;
           if (top_node_up)
@@ -2839,6 +3030,8 @@ external_node_href (CONVERTER *self, const ELEMENT 
*external_node,
       p = strrchr (manual_name, '/');
       if (!p)
         p = manual_name;
+      else
+        p++;
       manual_len = strlen (manual_name);
       if (manual_len >= 5
           && !memcmp (manual_name +manual_len - 5, ".info", 5))
@@ -2888,8 +3081,8 @@ external_node_href (CONVERTER *self, const ELEMENT 
*external_node,
               if ((source_command != 0) &&
                   (source_command->source_info.line_nr != 0))
                 { /* check if already set and set if not */
-                  if (!check_htmlxref_already_warned (self, manual_name,
-                                                      source_command))
+                  if (!html_check_htmlxref_already_warned (self, manual_name,
+                                              &source_command->source_info))
                     {
                       message_list_command_warn (&self->error_messages,
                                                  self->conf,
@@ -2900,7 +3093,8 @@ external_node_href (CONVERTER *self, const ELEMENT 
*external_node,
                 }
               else
                 {
-                  if (!check_htmlxref_already_warned (self, manual_name, 0))
+                  if (!html_check_htmlxref_already_warned (self,
+                                                           manual_name, 0))
                     {
                       message_list_document_warn (&self->error_messages,
                         self->conf,
@@ -2923,6 +3117,7 @@ external_node_href (CONVERTER *self, const ELEMENT 
*external_node,
               TEXT dir_path;
               char *url_encoded_path;
               text_init (&dir_path);
+
               if (self->conf->EXTERNAL_DIR)
                 {
                   text_printf (&dir_path, "%s/%s", self->conf->EXTERNAL_DIR,
@@ -3136,6 +3331,7 @@ command_root_element_command (CONVERTER *self, const 
ELEMENT *command)
 }
 
 /* Return string for linking to $COMMAND with <a href> */
+/* return value to be freed */
 char *html_command_href (CONVERTER *self, const ELEMENT *command,
                          const char *source_filename,
                          const ELEMENT *source_command, /* for messages only */
@@ -3446,14 +3642,6 @@ register_modified_shared_conversion_state_integer 
(CONVERTER *self,
     add_string (key, &self->shared_conversion_state_integer);
 }
 
-ELEMENT *
-new_element_added (TREE_ADDED_ELEMENTS *added_elements, enum element_type type)
-{
-  ELEMENT *new = new_element (type);
-  add_to_element_list (&added_elements->added, new);
-  return new;
-}
-
 TREE_ADDED_ELEMENTS *
 html_command_tree (CONVERTER *self, const ELEMENT *command, int no_number)
 {
@@ -3587,15 +3775,15 @@ html_command_tree (CONVERTER *self, const ELEMENT 
*command, int no_number)
   return 0;
 }
 
-/* keep in sync with enum html_command_text_type */
+/* keep in sync with enum html_text_type */
 static char *html_command_text_type_name[] = {
   "text", "text_nonumber", "string", "string_nonumber"
 };
 
-/* to be freed by caller */
+/* return value to be freed by caller */
 char *
 html_command_text (CONVERTER *self, const ELEMENT *command,
-                   const enum html_command_text_type type)
+                   const enum html_text_type type)
 {
   char *result;
   HTML_TARGET *target_info;
@@ -3604,10 +3792,9 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
                                                   "manual_content");
   if (manual_content)
     {
-      /* FIXME command_tree not destroyed anywhere */
       TREE_ADDED_ELEMENTS *command_tree = html_command_tree (self, command, 0);
       TREE_ADDED_ELEMENTS *string_tree = 0;
-      if (type == HCTT_string)
+      if (type == HTT_string)
         {
           ELEMENT *tree_root_string;
 
@@ -3616,12 +3803,8 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
 
           tree_root_string = new_element_added (string_tree, ET__string);
 
-          add_to_element_contents (tree_root_string, command_tree->tree);
+          add_to_contents_as_array (tree_root_string, command_tree->tree);
           tree_root = tree_root_string;
-          /* TODO tree_root_string/tree_root probably remains in tree_to_build
-                  as it is not removed nor set to string_tree->tree */
-          fprintf (stderr, "TODO: manual_content check tree_to_build '%s'\n",
-                            convert_to_texinfo (tree_root));
           add_to_element_list (&self->tree_to_build, tree_root);
         }
       else
@@ -3631,8 +3814,12 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
                                      element_command_name(command),
                                      "command_text-manual_content", 0, 0);
 
-      if (type == HCTT_string)
-        destroy_tree_added_elements (self, string_tree);
+      if (type == HTT_string)
+        {
+          remove_element_from_list (&self->tree_to_build, tree_root);
+          destroy_tree_added_elements (self, string_tree);
+        }
+      destroy_tree_added_elements (self, command_tree);
       return result;
     }
 
@@ -3651,7 +3838,7 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
             = html_command_tree (self, command, 0);
 
           if (!command_tree->tree)
-            return 0;
+            return strdup ("");
 
           if (command->cmd)
             {
@@ -3674,13 +3861,13 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
             }
           html_new_document_context (self, context_name, explanation, 0);
 
-          if ((type == HCTT_text_nonumber || type == HCTT_string_nonumber)
+          if ((type == HTT_text_nonumber || type == HTT_string_nonumber)
               && target_info->tree_nonumber.tree)
             selected_tree = target_info->tree_nonumber.tree;
           else
             selected_tree = command_tree->tree;
 
-          if (type == HCTT_string)
+          if (type == HTT_string)
             {
               ELEMENT *tree_root_string;
 
@@ -3689,12 +3876,8 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
 
               tree_root_string = new_element_added (string_tree, ET__string);
 
-              add_to_element_contents (tree_root_string, selected_tree);
+              add_to_contents_as_array (tree_root_string, selected_tree);
               tree_root = tree_root_string;
-          /* TODO tree_root_string/tree_root probably remains in tree_to_build
-                  as it is not removed nor set to string_tree->tree */
-              fprintf (stderr, "TODO: command_text check tree_to_build '%s'\n",
-                               convert_to_texinfo (tree_root));
               add_to_element_list (&self->tree_to_build, tree_root);
             }
           else
@@ -3714,8 +3897,9 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
 
           html_pop_document_context (self);
 
-          if (type == HCTT_string)
+          if (type == HTT_string)
             {
+              remove_element_from_list (&self->tree_to_build, tree_root);
               destroy_tree_added_elements (self, string_tree);
             }
           return strdup (target_info->command_text[type]);
@@ -3736,6 +3920,157 @@ html_command_text (CONVERTER *self, const ELEMENT 
*command,
   return 0;
 }
 
+/* return value to be freed by caller */
+char *
+from_element_direction (CONVERTER *self, int direction,
+                        enum  html_text_type type,
+                        const OUTPUT_UNIT *source_unit,
+                        const char *source_filename,
+                        const ELEMENT *source_command)
+{
+  const char *filename_from;
+  const OUTPUT_UNIT *target_unit = 0;
+  ELEMENT *command = 0;
+  HTML_TARGET *target = 0;
+
+  if (!source_unit)
+    source_unit = self->current_output_unit;
+
+  if (source_filename)
+    filename_from = source_filename;
+  else
+    filename_from = self->current_filename.filename;
+
+  if (direction < D_direction_Space)
+    target_unit = self->global_units_directions[direction];
+  else if (direction > NON_SPECIAL_DIRECTIONS_NR - 1)
+    target_unit
+       = self->global_units_directions
+           [D_direction_Last + direction - NON_SPECIAL_DIRECTIONS_NR +1];
+  else if ((!source_unit || unit_is_top_output_unit (self, source_unit))
+           && self->conf->TOP_NODE_UP_URL
+           && (direction == D_direction_Up || direction == D_direction_NodeUp))
+    {
+      if (type == HTT_href)
+        return strdup (self->conf->TOP_NODE_UP_URL);
+      else if (type == HTT_text || type == HTT_node
+               || type == HTT_string || type == HTT_section)
+        return strdup (self->conf->TOP_NODE_UP);
+      else
+        {
+          char *msg;
+          xasprintf (&msg, "type %d not available for TOP_NODE_UP\n", type);
+          fatal (msg);
+          free (msg);
+        }
+    }
+  else if (!target_unit && source_unit
+           && source_unit->directions[direction - (D_direction_Space +1)])
+    {
+      target_unit = source_unit->directions[direction - (D_direction_Space 
+1)];
+    }
+
+  if (target_unit)
+    {
+      if (target_unit->unit_type == OU_external_node_unit)
+        {
+          ELEMENT *external_node_element = target_unit->unit_command;
+          if (type == HTT_href)
+            return html_command_href (self, external_node_element,
+                                      filename_from, source_command, 0);
+          else if (type == HTT_text || type == HTT_node)
+            return html_command_text (self, external_node_element, 0);
+          else if (type == HTT_string)
+            return html_command_text (self, external_node_element,
+                                      HTT_string);
+        }
+      else if (type == HTT_node)
+        {
+          if (target_unit->unit_command)
+            {
+              if (target_unit->unit_command->cmd == CM_node)
+                command = target_unit->unit_command;
+              else
+                {
+                  ELEMENT *associated_node
+                    = lookup_extra_element (target_unit->unit_command,
+                                            "associated_node");
+                  if (associated_node)
+                    command = associated_node;
+                }
+            }
+          if (command)
+            {
+              target
+                = find_element_target (&self->html_targets, command);
+            }
+          type = HTT_text;
+        }
+      else if (type == HTT_section)
+        {
+          if (target_unit->unit_command)
+            {
+              if (target_unit->unit_command->cmd != CM_node)
+                command = target_unit->unit_command;
+              else
+                {
+                  ELEMENT *associated_section
+                    = lookup_extra_element (target_unit->unit_command,
+                                            "associated_section");
+                  if (associated_section)
+                    command = associated_section;
+                }
+            }
+          if (command)
+            {
+              target
+                = find_element_target (&self->html_targets, command);
+            }
+          type = HTT_text_nonumber;
+        }
+      else
+        {
+          command = target_unit->unit_command;
+          if (type == HTT_href)
+            {
+              if (command)
+                return html_command_href (self, command,
+                                          filename_from, 0, 0);
+              else
+                return strdup ("");
+            }
+
+          if (command)
+            {
+              target
+                = find_element_target (&self->html_targets, command);
+            }
+        }
+    }
+  else
+    return 0;
+
+  if (type == HTT_target)
+    {
+      if (target && target->target)
+        return (strdup (target->target));
+      else
+        return 0;
+    }
+
+  if (target && target->command_text[type])
+    return strdup (target->command_text[type]);
+  else if (command)
+    return html_command_text (self, command, type);
+
+  /*
+    We end up here if there is a target element, but not of the expected
+    'type'.  For example, if type is section but there is no section associated
+    to the target element node.
+   */
+  return 0;
+}
+
 static int
 compare_selector_style (const void *a, const void *b)
 {
@@ -3900,6 +4235,15 @@ html_get_css_elements_classes (CONVERTER *self, const 
char *filename)
   return result;
 }
 
+void
+close_html_lone_element (CONVERTER *self, TEXT *result)
+{
+  if (self->conf->USE_XML_SYNTAX > 0)
+    text_append_n (result, "/>", 2);
+  else
+    text_append_n (result, ">", 1);
+}
+
 static char *
 protect_class_name (const char *class_name)
 {
@@ -5708,19 +6052,427 @@ format_end_file (CONVERTER *self, const char *filename,
     }
 }
 
+typedef struct BEGIN_FILE_INFORMATION {
+    char *title;
+    char *description;
+    char *encoding;
+    char *css_lines;
+    char *root_html_element_attributes;
+    char *bodytext;
+    char *generator;
+    char *extra_head;
+} BEGIN_FILE_INFORMATION;
+
+void
+destroy_begin_file_information (BEGIN_FILE_INFORMATION *begin_info)
+{
+  free (begin_info->title);
+  free (begin_info->description);
+  free (begin_info->encoding);
+  free (begin_info->css_lines);
+  free (begin_info->root_html_element_attributes);
+  free (begin_info->bodytext);
+  free (begin_info->generator);
+  free (begin_info->extra_head);
+}
+
+static char *
+convert_string_tree_new_formatting_context (CONVERTER *self,
+                                            ELEMENT *tree,
+                                   char *context_string, char *multiple_pass)
+{
+  TREE_ADDED_ELEMENTS *string_tree = 0;
+  ELEMENT *tree_root_string;
+  char *result;
+
+  string_tree = new_tree_added_elements (tree_added_status_elements_added);
+  tree_root_string = new_element_added (string_tree, ET__string);
+  add_to_contents_as_array (tree_root_string, tree);
+
+  add_to_element_list (&self->tree_to_build, tree_root_string);
+
+  result = convert_tree_new_formatting_context (self, tree_root_string,
+                                       context_string, multiple_pass, 0, 0);
+
+  remove_element_from_list (&self->tree_to_build, tree_root_string);
+  destroy_tree_added_elements (self, string_tree);
+  return result;
+}
+
+void
+format_css_lines (CONVERTER *self, const char *filename, TEXT *result)
+{
+  FORMATTING_REFERENCE *formatting_reference
+   = &self->current_formatting_references[FR_format_css_lines];
+  /*
+  if (formatting_reference->status == FRS_status_default_set)
+    {
+      html_default_format_css_lines (self, filename, result);
+    }
+  else
+   */
+    {
+      char *css_lines
+        = call_formatting_function_format_css_lines (self,
+                                  formatting_reference, filename);
+      text_append (result, css_lines);
+      free (css_lines);
+    }
+}
+
+static char *root_html_element_attributes_string (CONVERTER *self)
+{
+  if (self->conf->HTML_ROOT_ELEMENT_ATTRIBUTES
+      && strlen (self->conf->HTML_ROOT_ELEMENT_ATTRIBUTES))
+    {
+      char *result;
+      xasprintf (&result, " %s", self->conf->HTML_ROOT_ELEMENT_ATTRIBUTES);
+      return result;
+    }
+  return 0;
+}
+
+/* This is used for normal output files and other files, like
+   redirection file headers.  $COMMAND is the tree element for
+   a @node that is being output in the file. */
+static BEGIN_FILE_INFORMATION *
+file_header_information (CONVERTER *self, ELEMENT *command,
+                         const char *filename)
+{
+  BEGIN_FILE_INFORMATION *begin_info = (BEGIN_FILE_INFORMATION *)
+                          malloc (sizeof (BEGIN_FILE_INFORMATION));
+  char *description = self->documentdescription_string;
+  int status;
+  TEXT text;
+  char *root_html_element_attributes
+    = root_html_element_attributes_string (self);
+
+  text_init (&text);
+
+  memset (begin_info, 0, sizeof (BEGIN_FILE_INFORMATION));
+
+  if (command)
+    {
+      char *command_string = html_command_text (self, command, HTT_string);
+      if (command_string && strlen (command_string)
+          && strcmp (command_string, self->title_string))
+        {
+          TREE_ADDED_ELEMENTS *element_tree = 0;
+          TREE_ADDED_ELEMENTS *new_element_tree = 0;
+          NAMED_STRING_ELEMENT_LIST *substrings
+                                   = new_named_string_element_list ();
+          ELEMENT *title_tree_copy = copy_tree (self->title_tree);
+          ELEMENT *element_tree_copy;
+          ELEMENT *title_tree;
+
+          if (self->conf->SECTION_NAME_IN_TITLE > 0)
+            {
+              ELEMENT *associated_section
+                = lookup_extra_element (command, "associated_section");
+              if (associated_section && associated_section->args.number > 0)
+                {
+                  new_element_tree
+                   = new_tree_added_elements (tree_added_status_reused_tree);
+                  new_element_tree->tree = associated_section->args.list[0];
+                  element_tree = new_element_tree;
+                }
+            }
+          if (!element_tree)
+            element_tree = html_command_tree (self, command, 0);
+
+          element_tree_copy = copy_tree (element_tree->tree);
+          if (new_element_tree)
+            destroy_tree_added_elements (self, new_element_tree);
+
+          add_element_to_named_string_element_list (substrings, "title",
+                                                    title_tree_copy);
+          add_element_to_named_string_element_list (substrings, "element_text",
+                                                    element_tree_copy);
+
+          /* TRANSLATORS: sectioning element title for the page header */
+          title_tree
+            = html_gdt_tree ("{element_text} ({title})",
+                                   self->document, self, substrings, 0, 0);
+
+          add_to_element_list (&self->tree_to_build, title_tree);
+
+          begin_info->title
+                 = convert_string_tree_new_formatting_context (self,
+                          title_tree, element_command_name (command),
+                          "element_title");
+
+          remove_element_from_list (&self->tree_to_build, title_tree);
+          destroy_element_and_children (title_tree);
+        }
+    }
+  if (!begin_info->title)
+    begin_info->title = strdup (self->title_string);
+
+  if (!description || !strlen (description))
+    description = begin_info->title;
+
+  if (description && strlen (description))
+    {
+      text_printf (&text,
+                   "<meta name=\"description\" content=\"%s\"", description);
+      close_html_lone_element (self, &text);
+      begin_info->description = strdup (text.text);
+    }
+
+  text_reset (&text);
+  if (self->conf->OUTPUT_ENCODING_NAME
+      && strlen (self->conf->OUTPUT_ENCODING_NAME))
+    {
+      text_printf (&text,
+        "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"",
+                   self->conf->OUTPUT_ENCODING_NAME);
+      close_html_lone_element (self, &text);
+      begin_info->encoding = strdup (text.text);
+    }
+
+  text_reset (&text);
+  text_append (&text, "");
+  format_css_lines (self, filename, &text);
+  begin_info->css_lines = strdup (text.text);
+
+  if (root_html_element_attributes)
+    {
+      begin_info->root_html_element_attributes
+       = root_html_element_attributes;
+    }
+  else
+    begin_info->root_html_element_attributes = strdup ("");
+
+  text_reset (&text);
+  text_append (&text, self->conf->BODYTEXT);
+  if (self->conf->HTML_MATH && !strcmp (self->conf->HTML_MATH, "mathjax")
+      && html_get_file_information (self, "mathjax", filename, &status) > 0)
+    {
+      text_append_n (&text, " class=\"tex2jax_ignore\"", 23);
+    }
+
+  begin_info->bodytext = strdup (text.text);
+
+  text_reset (&text);
+  if (self->conf->PROGRAM && strlen (self->conf->PROGRAM))
+    {
+      text_printf (&text, "<meta name=\"Generator\" content=\"%s\"",
+                   self->conf->PROGRAM);
+      close_html_lone_element (self, &text);
+      text_append_n (&text, "\n", 1);
+      begin_info->generator = strdup (text.text);
+      text_reset (&text);
+    }
+
+  if (self->conf->EXTRA_HEAD)
+    text_append (&text, self->conf->EXTRA_HEAD);
+  if (self->conf->INFO_JS_DIR)
+    {
+      if (!self->conf->SPLIT || !strlen (self->conf->SPLIT))
+        {
+          message_list_document_error (&self->error_messages, self->conf,
+                     "%s not meaningful for non-split output", "INFO_JS_DIR");
+        }
+      else
+        {
+          char *jsdir;
+          char *protected_jsdir;
+          if (!strcmp (self->conf->INFO_JS_DIR, "."))
+            jsdir = strdup ("");
+          else
+            {
+              size_t len;
+              jsdir = strdup (self->conf->INFO_JS_DIR);
+              len = strlen (jsdir);
+              if (len > 0)
+                {
+                  if (jsdir[len - 1] != '/')
+                    {
+                      char *tmp;
+                      xasprintf (&tmp, "%s/", jsdir);
+                      free (jsdir);
+                      jsdir = tmp;
+                    }
+                  else if (len > 1 && jsdir[len - 2] == '/')
+                    {
+                      while (1)
+                        {
+                          len--;
+                          if (len <= 1 || jsdir[len - 2] != '/')
+                            break;
+                        }
+                      jsdir[len] = '\0';
+                    }
+                }
+            }
+          protected_jsdir = url_protect_url_text (self, jsdir);
+          free (jsdir);
+          text_printf (&text, "<link rel=\"stylesheet\" type=\"text/css\" "
+                              "href=\"%sinfo.css\"", protected_jsdir);
+          close_html_lone_element (self, &text);
+          text_printf (&text, "\n<script src=\"%smodernizr.js\" "
+                              "type=\"text/javascript\"></script>\n"
+            "<script src=\"%sinfo.js\" type=\"text/javascript\"></script>",
+                              protected_jsdir, protected_jsdir);
+          free (protected_jsdir);
+        }
+    }
+  if (self->conf->HTML_MATH && !strcmp (self->conf->HTML_MATH, "mathjax")
+      && (html_get_file_information (self, "mathjax", filename, &status) > 0
+          || (self->conf->SPLIT && strlen (self->conf->SPLIT))))
+    {
+      char *mathjax_script = url_protect_url_text (self,
+                                    self->conf->MATHJAX_SCRIPT);
+      text_printf (&text, "<script type='text/javascript'>\n"
+"MathJax = {\n"
+"  options: {\n"
+"    skipHtmlTags: {'[-]': ['pre']},\n"
+"    ignoreHtmlClass: 'tex2jax_ignore',\n"
+"    processHtmlClass: 'tex2jax_process'\n"
+"  },\n"
+"};\n"
+"</script><script type=\"text/javascript\" id=\"MathJax-script\" async\n"
+"  src=\"%s\">\n"
+"</script>", mathjax_script);
+    }
+  begin_info->extra_head = text.text;
+  return begin_info;
+}
+
+static void
+get_links (CONVERTER* self, const char *filename,
+           const OUTPUT_UNIT *output_unit,
+           const ELEMENT *node_command, TEXT *result)
+{
+  if (self->conf->USE_LINKS > 0)
+    {
+      int i;
+      BUTTON_SPECIFICATION_LIST *link_buttons = self->conf->LINKS_BUTTONS;
+      for (i = 0; i < link_buttons->number; i++)
+        {
+          const BUTTON_SPECIFICATION *link = &link_buttons->list[i];
+          char *link_href;
+          if (link->type != BST_direction)
+            fatal ("LINKS_BUTTONS should only contain directions");
+          link_href = from_element_direction (self, link->direction,
+                                              HTT_href, output_unit,
+                                              filename, node_command);
+          if (link_href && strlen (link_href))
+            {
+              char *link_string
+                = from_element_direction (self, link->direction, HTT_string,
+                                          output_unit, 0, 0);
+              char *button_rel
+                = direction_string (self, link->direction, TDS_type_rel,
+                                    TDS_context_string);
+              text_printf (result, "<link href=\"%s\"", link_href);
+              if (button_rel)
+                {
+                  text_printf (result, " rel=\"%s\"", button_rel);
+                }
+              if (link_string)
+                {
+                  text_printf (result, " title=\"%s\"", link_string);
+                  free (link_string);
+                }
+              close_html_lone_element (self, result);
+              text_append_n (result, "\n", 1);
+            }
+          free (link_href);
+        }
+    }
+}
+
+char *
+html_default_format_begin_file (CONVERTER *self, const char *filename,
+                                const OUTPUT_UNIT *output_unit)
+{
+  ELEMENT *element_command = 0;
+  ELEMENT *node_command = 0;
+  ELEMENT *command_for_title = 0;
+  BEGIN_FILE_INFORMATION *begin_info;
+  TEXT result;
+
+  if (output_unit)
+    {
+      element_command = output_unit->unit_command;
+      if (element_command && element_command->cmd != CM_node)
+        {
+          node_command = lookup_extra_element (element_command,
+                                               "associated_node");
+        }
+      if (!node_command)
+        node_command = element_command;
+
+      if (self->conf->SPLIT && strlen (self->conf->SPLIT) && element_command)
+        command_for_title = element_command;
+    }
+
+  begin_info = file_header_information (self, command_for_title, filename);
+
+  text_init (&result);
+
+  text_append (&result, self->conf->DOCTYPE);
+  text_append_n (&result, "\n", 1);
+  text_printf (&result, "<html%s>\n", 
begin_info->root_html_element_attributes);
+  text_printf (&result, "<!-- Created by %s, %s -->\n<head>\n",
+                        self->conf->PACKAGE_AND_VERSION_OPTION,
+                        self->conf->PACKAGE_URL_OPTION);
+  if (begin_info->encoding)
+    text_append (&result, begin_info->encoding);
+  text_append_n (&result, "\n", 1);
+  if (self->copying_comment)
+    text_append (&result, self->copying_comment);
+  text_printf (&result, "<title>%s</title>\n\n", begin_info->title);
+  if (begin_info->description)
+    text_append (&result, begin_info->description);
+  text_append_n (&result, "\n", 1);
+  text_printf (&result, "<meta name=\"keywords\" content=\"%s\"",
+               begin_info->title);
+  close_html_lone_element (self, &result);
+  text_append_n (&result, "\n", 1);
+  text_append (&result, "<meta name=\"resource-type\" content=\"document\"");
+  close_html_lone_element (self, &result);
+  text_append_n (&result, "\n", 1);
+  text_append (&result, "<meta name=\"distribution\" content=\"global\"");
+  close_html_lone_element (self, &result);
+  text_append_n (&result, "\n", 1);
+  if (begin_info->generator)
+    text_append (&result, begin_info->generator);
+  if (self->date_in_header)
+    text_append (&result, self->date_in_header);
+  text_append (&result,
+    "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"");
+  close_html_lone_element (self, &result);
+  text_append_n (&result, "\n\n", 2);
+
+  get_links (self, filename, output_unit, node_command, &result);
+
+  text_append (&result, begin_info->css_lines);
+  text_append_n (&result, "\n", 1);
+  if (begin_info->extra_head)
+    text_append (&result, begin_info->extra_head);
+  text_append_n (&result, "\n</head>\n\n", 10);
+  text_printf (&result, "<body %s>\n", begin_info->bodytext);
+  if (self->conf->AFTER_BODY_OPEN)
+    text_append (&result, self->conf->AFTER_BODY_OPEN);
+
+  destroy_begin_file_information (begin_info);
+
+  return result.text;
+}
+
 char *
 format_begin_file (CONVERTER *self, const char *filename,
                  const OUTPUT_UNIT *output_unit)
 {
   FORMATTING_REFERENCE *formatting_reference
    = &self->current_formatting_references[FR_format_begin_file];
-/*
   if (formatting_reference->status == FRS_status_default_set)
     {
       return html_default_format_begin_file (self, filename, output_unit);
     }
   else
-*/
     {
       return call_formatting_function_format_begin_file (self,
                                                      formatting_reference,
@@ -5780,7 +6532,7 @@ html_default_format_navigation_panel (CONVERTER *self,
       else if (button->type == BST_direction)
         direction = button->direction;
 
-      if (direction >= 0 && direction == D_button_space
+      if (direction >= 0 && direction == D_direction_Space
           && nr_of_buttons_shown == 0)
         continue;
 
@@ -7325,7 +8077,7 @@ mini_toc_internal (CONVERTER *self, const ELEMENT 
*element, TEXT *result)
         the same result for the other files, as the formatting is done in a
         global context, while taking the tree first and calling convert_tree
         converts in the current page context.
-         text = html_command_text(self, section, HCTT_text_nonumber);
+         text = html_command_text(self, section, HTT_text_nonumber);
       */
           TREE_ADDED_ELEMENTS *command_tree
              = html_command_tree (self, section, 1);
@@ -7399,7 +8151,7 @@ convert_heading_command (CONVERTER *self, const enum 
command_id cmd,
     {
       if (element->cmd != CM_node)
         {
-          char *heading = html_command_text (self, element, HCTT_string);
+          char *heading = html_command_text (self, element, HTT_string);
           text_append (result, heading);
           text_append_n (result, "\n", 1);
           free (heading);
@@ -8256,22 +9008,10 @@ html_prepare_converted_output_info (CONVERTER *self)
 
   if (fulltitle_tree)
     {
-      TREE_ADDED_ELEMENTS *string_tree = 0;
-      ELEMENT *tree_root_string;
-
       self->title_tree = fulltitle_tree;
 
-      string_tree = new_tree_added_elements (tree_added_status_elements_added);
-      tree_root_string = new_element_added (string_tree, ET__string);
-      add_to_contents_as_array (tree_root_string, fulltitle_tree);
-
-      add_to_element_list (&self->tree_to_build, tree_root_string);
-
-      html_title_string = convert_tree_new_formatting_context (self,
-                            tree_root_string, "title_string", 0, 0, 0);
-
-      remove_element_from_list (&self->tree_to_build, tree_root_string);
-      destroy_tree_added_elements (self, string_tree);
+      html_title_string = convert_string_tree_new_formatting_context (self,
+                                       fulltitle_tree, "title_string", 0);
       if (html_title_string[strspn (html_title_string, whitespace_chars)]
            == '\0')
         {
@@ -8282,26 +9022,14 @@ html_prepare_converted_output_info (CONVERTER *self)
 
   if (!html_title_string)
     {
-      TREE_ADDED_ELEMENTS *string_tree = 0;
-      ELEMENT *tree_root_string;
       ELEMENT *default_title = html_gdt_tree ("Untitled Document",
                                          self->document, self, 0, 0, 0);
       SOURCE_INFO cmd_source_info;
 
       self->title_tree = default_title;
 
-      string_tree = new_tree_added_elements (tree_added_status_elements_added);
-      tree_root_string = new_element_added (string_tree, ET__string);
-
-      add_to_contents_as_array (tree_root_string, default_title);
-
-      add_to_element_list (&self->tree_to_build, tree_root_string);
-
-      html_title_string = convert_tree_new_formatting_context (self,
-                            tree_root_string, "title_string", 0, 0, 0);
-
-      remove_element_from_list (&self->tree_to_build, tree_root_string);
-      destroy_tree_added_elements (self, string_tree);
+      html_title_string = convert_string_tree_new_formatting_context (self,
+                                       default_title, "title_string", 0);
 
       self->added_title_tree = 1;
 
@@ -8346,8 +9074,6 @@ html_prepare_converted_output_info (CONVERTER *self)
      = strdup (self->conf->documentdescription);
   else if (self->document->global_commands->documentdescription)
     {
-      TREE_ADDED_ELEMENTS *string_tree = 0;
-      ELEMENT *tree_root_string;
       ELEMENT *tmp = new_element (ET_NONE);
       char *documentdescription_string;
       size_t documentdescription_string_len;
@@ -8355,19 +9081,9 @@ html_prepare_converted_output_info (CONVERTER *self)
       tmp->contents
         = self->document->global_commands->documentdescription->contents;
 
-      string_tree = new_tree_added_elements (tree_added_status_elements_added);
-      tree_root_string = new_element_added (string_tree, ET__string);
-
-      add_to_element_contents (tree_root_string, tmp);
-
-      add_to_element_list (&self->tree_to_build, tree_root_string);
-
       documentdescription_string
-               = convert_tree_new_formatting_context (self,
-                            tree_root_string, "documentdescription", 0, 0, 0);
-
-      remove_element_from_list (&self->tree_to_build, tree_root_string);
-      destroy_tree_added_elements (self, string_tree);
+                 = convert_string_tree_new_formatting_context (self,
+                                       tmp, "documentdescription", 0);
 
       tmp->contents.list = 0;
       destroy_element (tmp);
@@ -8845,7 +9561,7 @@ reset_html_targets (CONVERTER *self, HTML_TARGET_LIST 
*targets)
           free (html_target->contents_target);
           free (html_target->shortcontents_target);
 
-          for (j = 0; j < HCTT_string_nonumber+1; j++)
+          for (j = 0; j < HTT_string_nonumber+1; j++)
             free (html_target->command_text[j]);
 
           free_tree_added_elements (self, &html_target->tree);
@@ -9002,6 +9718,8 @@ html_reset_converter (CONVERTER *self)
   self->documentdescription_string = 0;
   free (self->copying_comment);
   self->copying_comment = 0;
+  free (self->date_in_header);
+  self->date_in_header = 0;
 
   if (self->added_title_tree)
     {
@@ -9050,6 +9768,8 @@ html_reset_converter (CONVERTER *self)
   clear_output_files_information (&self->output_files_information);
   clear_output_unit_files (&self->output_unit_files);
 
+  clear_strings_list (&self->check_htmlxref_already_warned);
+
   free (self->page_name_number.list);
   memset (&self->page_name_number, 0, sizeof (PAGE_NAME_NUMBER_LIST));
 
@@ -9112,6 +9832,10 @@ void
 html_free_converter (CONVERTER *self)
 {
   int i;
+  int nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
+                     + self->special_unit_varieties.number;
+  int nr_dir_str_contexts = TDS_context_string + 1;
+
 
   if (self->error_messages.number)
     {
@@ -9139,6 +9863,8 @@ html_free_converter (CONVERTER *self)
 
   free (self->added_targets.list);
 
+  free_strings_list (&self->check_htmlxref_already_warned);
+
   for (i = 0; i < SUIT_type_heading+1; i++)
     {/* we assume that reset_translated_special_unit_info_tree
         has already been called */
@@ -9186,6 +9912,35 @@ html_free_converter (CONVERTER *self)
       free (self->special_unit_info[i]);
     }
 
+  for (i = 0; i < TDS_TYPE_MAX_NR; i++)
+    {
+      int j;
+      char ***type_dir_strings = self->directions_strings[i];
+      clear_direction_string_type (self, type_dir_strings);
+      for (j = 0; j < nr_string_directions; j++)
+        {
+          free (type_dir_strings[j]);
+        }
+      free (type_dir_strings);
+    }
+
+  for (i = 0; i < TDS_TRANSLATED_MAX_NR; i++)
+    {
+      int j;
+      HTML_DIRECTION_STRING_TRANSLATED *translated
+        = self->translated_direction_strings[i];
+      for (j = 0; j < nr_string_directions; j++)
+        {
+          int k;
+          free (translated[j].to_convert);
+          for (k = 0; k < nr_dir_str_contexts; k++)
+            {
+              free (translated[j].converted[k]);
+            }
+        }
+      free (translated);
+    }
+
   for (i = 0; i < self->htmlxref.number; i++)
     {
       int j;
@@ -9365,9 +10120,10 @@ html_translate_names (CONVERTER *self)
     }
 
   /* reset strings such that they are translated when needed. */
-  #define tds_type(name) self->directions_strings[TDS_type_ ## name] = 0;
-   TDS_TRANSLATED_TYPES_LIST
-  #undef tds_type
+  for (j = 0; j < TDS_TRANSLATED_MAX_NR; j++)
+    {
+      clear_direction_string_type (self, self->directions_strings[j]);
+    }
 
   /* reset trees such that they are created based on Texinfo code string
      translation on-demand */
@@ -9401,10 +10157,10 @@ html_translate_names (CONVERTER *self)
           not be freed, but need to be reset to trigger the creation of the
           special_unit_info_tree tree when needed */
                        clear_tree_added_elements (self, &target_info->tree);
-                       free (target_info->command_text[HCTT_string]);
-                       target_info->command_text[HCTT_string] = 0;
-                       free (target_info->command_text[HCTT_text]);
-                       target_info->command_text[HCTT_text] = 0;
+                       free (target_info->command_text[HTT_string]);
+                       target_info->command_text[HTT_string] = 0;
+                       free (target_info->command_text[HTT_text]);
+                       target_info->command_text[HTT_text] = 0;
                        /* gather elements to pass information to perl */
                        add_to_element_list (&self->reset_target_commands,
                                             command);
@@ -10693,6 +11449,29 @@ html_convert_output (CONVERTER *self, const ELEMENT 
*root,
   text_init (&result);
   text_init (&text);
 
+  if (self->conf->DATE_IN_HEADER > 0)
+    {
+      ELEMENT *today_element = new_element (ET_NONE);
+      char *today;
+
+      today_element->cmd = CM_today;
+
+      add_to_element_list (&self->tree_to_build, today_element);
+      today = convert_tree_new_formatting_context (self, today_element,
+                                                   "DATE_IN_HEADER", 0, 0, 0);
+      remove_element_from_list (&self->tree_to_build, today_element);
+      destroy_element (today_element);
+
+      text_printf (&text,
+                   "<meta name=\"date\" content=\"%s\"", today);
+      free (today);
+      close_html_lone_element (self, &text);
+      text_append_n (&text, "\n", 1);
+      self->date_in_header = strdup (text.text);
+    }
+
+  text_reset (&text);
+
   text_append (&result, "");
 
 
diff --git a/tp/Texinfo/XS/convert/convert_html.h 
b/tp/Texinfo/XS/convert/convert_html.h
index fe027b65a2..d247bbdd28 100644
--- a/tp/Texinfo/XS/convert/convert_html.h
+++ b/tp/Texinfo/XS/convert/convert_html.h
@@ -23,6 +23,9 @@ extern TRANSLATED_SUI_ASSOCIATION 
translated_special_unit_info[];
 extern const char *special_unit_info_type_names[SUI_type_heading + 1];
 extern const char *htmlxref_split_type_names[htmlxref_split_type_chapter + 1];
 
+extern const char *direction_string_type_names[];
+extern const char *direction_string_context_names[];
+
 extern char *count_elements_in_filename_type_names[];
 
 void html_format_init (void);
@@ -109,6 +112,10 @@ char *html_get_associated_formatted_inline_content 
(CONVERTER *self,
                                               const ELEMENT *element,
                                               void *hv);
 
+size_t html_check_htmlxref_already_warned (CONVERTER *self,
+                                           const char *manual_name,
+                                           const SOURCE_INFO *source_info);
+
 void html_merge_index_entries (CONVERTER *self);
 
 void html_prepare_conversion_units (CONVERTER *self,
diff --git a/tp/Texinfo/XS/convert/get_html_perl_info.c 
b/tp/Texinfo/XS/convert/get_html_perl_info.c
index a36bcb3953..281204b494 100644
--- a/tp/Texinfo/XS/convert/get_html_perl_info.c
+++ b/tp/Texinfo/XS/convert/get_html_perl_info.c
@@ -153,13 +153,20 @@ html_converter_initialize_sv (SV *converter_sv,
   SV **code_types_sv;
   SV **upper_case_commands_sv;
   SV **pre_class_types_sv;
+  SV **directions_strings_sv;
+  SV **translated_direction_strings_sv;
   HV *formatting_function_hv;
   HV *commands_open_hv;
   HV *commands_conversion_hv;
   HV *types_open_hv;
   HV *types_conversion_hv;
   HV *output_units_conversion_hv;
+  HV *directions_strings_hv;
+  HV *translated_direction_strings_hv;
   CONVERTER *converter;
+  int nr_string_directions;
+  int nr_dir_str_contexts = TDS_context_string +1;
+  enum direction_string_type DS_type;
 
   dTHX;
 
@@ -818,6 +825,164 @@ html_converter_initialize_sv (SV *converter_sv,
         }
     }
 
+  nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
+                     + converter->special_unit_varieties.number;
+
+  FETCH(directions_strings)
+
+  if (directions_strings_sv)
+    directions_strings_hv = (HV *) SvRV (*directions_strings_sv);
+
+  for (DS_type = 0; DS_type < TDS_TYPE_MAX_NR; DS_type++)
+    {
+      int i;
+      SV **direction_sv = 0;
+      HV *direction_hv = 0;
+
+      converter->directions_strings[DS_type] = (char ***)
+        malloc (nr_string_directions * sizeof (char **));
+      memset (converter->directions_strings[DS_type], 0,
+              nr_string_directions * sizeof (char **));
+
+      if (directions_strings_sv)
+        {
+          const char *type_name = direction_string_type_names[DS_type];
+          direction_sv = hv_fetch (directions_strings_hv, type_name,
+                                   strlen (type_name), 0);
+          if (direction_sv)
+            direction_hv = (HV *) SvRV (*direction_sv);
+        }
+
+      for (i = 0; i < nr_string_directions; i++)
+        {
+          converter->directions_strings[DS_type][i] = (char **)
+               malloc (nr_dir_str_contexts * sizeof (char *));
+          memset (converter->directions_strings[DS_type][i], 0,
+                          nr_dir_str_contexts * sizeof (char *));
+
+          if (direction_sv)
+            {
+              const char *direction_name;
+              SV **context_sv;
+
+              if (i < FIRSTINFILE_MIN_IDX)
+                direction_name = html_button_direction_names[i];
+              else
+                direction_name
+                  = converter->special_unit_info[SUI_type_direction]
+                                   [i - FIRSTINFILE_MIN_IDX];
+
+              context_sv = hv_fetch (direction_hv, direction_name,
+                                          strlen (direction_name), 0);
+
+              if (context_sv)
+                {
+                  int j;
+                  HV *context_hv = (HV *) SvRV (*context_sv);
+
+                  for (j = 0; j < nr_dir_str_contexts; j++)
+                    {
+                      const char *context_name
+                        = direction_string_context_names[j];
+
+                      SV **value_sv = hv_fetch (context_hv, context_name,
+                                                strlen (context_name), 0);
+
+                      if (value_sv)
+                        {
+                           converter->directions_strings[DS_type][i][j]
+                             = strdup ((char *) SvPVutf8_nolen (*value_sv));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+  FETCH(translated_direction_strings)
+
+  if (translated_direction_strings_sv)
+    translated_direction_strings_hv
+        = (HV *) SvRV (*translated_direction_strings_sv);
+
+  for (DS_type = 0; DS_type < TDS_TRANSLATED_MAX_NR; DS_type++)
+    {
+      converter->translated_direction_strings[DS_type]
+       = (HTML_DIRECTION_STRING_TRANSLATED *) malloc
+        (nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED));
+      memset (converter->translated_direction_strings[DS_type], 0,
+         nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED));
+
+      if (translated_direction_strings_sv)
+        {
+          const char *type_name = direction_string_type_names[DS_type];
+          SV **direction_sv = hv_fetch (translated_direction_strings_hv,
+                                        type_name, strlen (type_name), 0);
+
+          if (direction_sv)
+            {
+              int i;
+              HV *direction_hv = (HV *) SvRV (*direction_sv);
+
+              for (i = 0; i < nr_string_directions; i++)
+                {
+                  const char *direction_name;
+                  SV **spec_sv;
+
+                  if (i < FIRSTINFILE_MIN_IDX)
+                    direction_name = html_button_direction_names[i];
+                  else
+                    direction_name
+                      = converter->special_unit_info[SUI_type_direction]
+                                       [i - FIRSTINFILE_MIN_IDX];
+
+                  spec_sv = hv_fetch (direction_hv, direction_name,
+                                              strlen (direction_name), 0);
+
+                  if (spec_sv)
+                    {
+                      HV *spec_hv = (HV *) SvRV (*spec_sv);
+
+                      SV **to_convert_sv = hv_fetch (spec_hv, "to_convert",
+                                                     strlen ("to_convert"), 0);
+                      if (to_convert_sv)
+                        {
+                          converter
+                           
->translated_direction_strings[DS_type][i].to_convert
+                            = strdup ((char *) SvPVutf8_nolen 
(*to_convert_sv));
+                        }
+                      else
+                        {
+                          SV **context_sv = hv_fetch (spec_hv, "converted",
+                                                     strlen ("converted"), 0);
+                          if (context_sv)
+                            {
+                              HV *context_hv = (HV *) SvRV (*context_sv);
+                              int j;
+
+                              for (j = 0; j < nr_dir_str_contexts; j++)
+                                {
+                                  const char *context_name
+                                    = direction_string_context_names[j];
+
+                                  SV **value_sv
+                                    = hv_fetch (context_hv, context_name,
+                                                  strlen (context_name), 0);
+
+                                  if (value_sv)
+                                    {
+                                      converter
+                     ->translated_direction_strings[DS_type][i].converted[j]
+                               = strdup ((char *) SvPVutf8_nolen (*value_sv));
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
   html_converter_initialize (converter);
 }
 
diff --git a/tp/Texinfo/XS/main/converter_types.h 
b/tp/Texinfo/XS/main/converter_types.h
index f062c01cd6..3183e710e6 100644
--- a/tp/Texinfo/XS/main/converter_types.h
+++ b/tp/Texinfo/XS/main/converter_types.h
@@ -99,13 +99,21 @@ enum special_target_type {
   tds_type(example) \
   tds_type(rel)
 
-enum direction_string {
+enum direction_string_type {
   #define tds_type(name) TDS_type_ ## name,
    TDS_TRANSLATED_TYPES_LIST
    TDS_NON_TRANSLATED_TYPES_LIST
   #undef tds_type
 };
 
+#define TDS_TRANSLATED_MAX_NR TDS_type_text +1
+#define TDS_TYPE_MAX_NR TDS_type_rel +1
+
+enum direction_string_context {
+  TDS_context_normal,
+  TDS_context_string,
+};
+
 /* %default_formatting_references in Texinfo::HTML */
 #define HTML_FORMATTING_REFERENCES_LIST \
   html_fr_reference(format_begin_file) \
@@ -162,11 +170,16 @@ enum html_special_character {
    SC_non_breaking_space,
 };
 
-enum html_command_text_type {
-   HCTT_text,
-   HCTT_text_nonumber,
-   HCTT_string,
-   HCTT_string_nonumber, /* not sure that it is set/used */
+enum html_text_type {
+   HTT_text,
+   HTT_text_nonumber,
+   HTT_string,
+   HTT_string_nonumber, /* not sure that it is set/used */
+   /* not only used for element text, also for direction text */
+   HTT_href,
+   HTT_target,
+   HTT_node,
+   HTT_section,
 };
 
 enum htmlxref_split_type {
@@ -236,7 +249,7 @@ typedef struct HTML_TARGET {
     char *contents_target;
     char *shortcontents_target;
 
-    char *command_text[HCTT_string_nonumber+1];
+    char *command_text[HTT_string_nonumber+1];
     TREE_ADDED_ELEMENTS tree;
     TREE_ADDED_ELEMENTS tree_nonumber;
     FILE_NUMBER_NAME file_number_name;
@@ -298,6 +311,11 @@ typedef struct HTML_COMMAND_CONVERSION {
     char *translated_to_convert;
 } HTML_COMMAND_CONVERSION;
 
+typedef struct HTML_DIRECTION_STRING_TRANSLATED {
+    char *to_convert;
+    char *converted[TDS_context_string +1];
+} HTML_DIRECTION_STRING_TRANSLATED;
+
 typedef struct COMMAND_ID_LIST {
     size_t number;
     enum command_id *list;
@@ -586,17 +604,6 @@ typedef struct HTMLXREF_MANUAL_LIST {
     HTMLXREF_MANUAL *list;
 } HTMLXREF_MANUAL_LIST;
 
-typedef struct HTMLXREF_MANUAL_ELEMENT_WARNED {
-    const ELEMENT *element;
-    char *manual;
-} HTMLXREF_MANUAL_ELEMENT_WARNED;
-
-typedef struct HTMLXREF_MANUAL_ELEMENT_WARNED_LIST {
-    size_t number;
-    size_t space;
-    HTMLXREF_MANUAL_ELEMENT_WARNED *list;
-} HTMLXREF_MANUAL_ELEMENT_WARNED_LIST;
-
 typedef struct ASSOCIATED_INFO_LIST {
     size_t number;
     ASSOCIATED_INFO *list;
@@ -680,8 +687,10 @@ typedef struct CONVERTER {
     COMMAND_CONVERSION_FUNCTION 
css_string_command_conversion_function[BUILTIN_CMD_NUMBER];
     OUTPUT_UNIT_CONVERSION_FUNCTION 
output_unit_conversion_function[OU_special_unit+1];
     SPECIAL_UNIT_BODY_FORMATTING *special_unit_body_formatting;
+    HTML_DIRECTION_STRING_TRANSLATED 
*translated_direction_strings[TDS_TRANSLATED_MAX_NR];
     /* set for a converter, modified in a document */
     HTML_COMMAND_CONVERSION 
html_command_conversion[BUILTIN_CMD_NUMBER][HCC_type_css_string+1];
+    char ***directions_strings[TDS_TYPE_MAX_NR];
 
     /* set for a document */
     enum htmlxref_split_type document_htmlxref_split_type;
@@ -691,7 +700,6 @@ typedef struct CONVERTER {
     STRING_LIST seen_ids;
     HTML_TARGET_LIST html_targets;
     HTML_TARGET_LIST html_special_targets[ST_footnote_location+1];
-    char **directions_strings[TDS_type_rel+1];
     JSLICENSE_CATEGORY_LIST jslicenses;
     /* associate cmd and index in special_unit_varieties STRING_LIST */
     /* number in sync with command_special_unit_variety, +1 for trailing 0 */
@@ -725,6 +733,7 @@ typedef struct CONVERTER {
     COMMAND_CONVERSION_FUNCTION *current_commands_conversion_function;
     void (* current_format_protect_text) (const char *text, TEXT *result);
     int added_title_tree;
+    char *date_in_header;
 
     /* state common with perl converter */
     int document_global_context;
@@ -742,7 +751,7 @@ typedef struct CONVERTER {
     HTML_PENDING_FOOTNOTE_STACK pending_footnotes;
     HTML_ASSOCIATED_INLINE_CONTENT_LIST associated_inline_content;
     PAGES_CSS_LIST page_css;
-    HTMLXREF_MANUAL_ELEMENT_WARNED_LIST check_htmlxref_already_warned;
+    STRING_LIST check_htmlxref_already_warned;
     ASSOCIATED_INFO_LIST html_files_information;
     /* state common with perl converter, not transmitted to perl */
     int use_unicode_text;
@@ -759,20 +768,27 @@ typedef struct TRANSLATED_SUI_ASSOCIATION {
    sometime to have an enum name for a direction */
 /* special units are put after these fixed types, they are dynamically
    determined from perl input */
-enum button_unit_direction {
-  #define hgdt_name(name) D_button_ ## name,
+enum direction_unit_direction {
+  #define hgdt_name(name) D_direction_ ## name,
    HTML_GLOBAL_DIRECTIONS_LIST
   #undef hgdt_name
-   D_button_space,
-  #define rud_type(name) D_button_ ## name,
+   D_direction_Space,
+  #define rud_type(name) D_direction_ ## name,
    RUD_DIRECTIONS_TYPES_LIST
    RUD_FILE_DIRECTIONS_TYPES
   #undef rud_type
-  #define rud_type(name) D_button_FirstInFile## name,
+  #define rud_type(name) D_direction_FirstInFile## name,
    RUD_DIRECTIONS_TYPES_LIST
   #undef rud_type
 };
 
+#define FIRSTINFILE_MIN_IDX D_direction_FirstInFileThis
+#define FIRSTINFILE_MAX_IDX D_direction_FirstInFileNodeUp
+#define FIRSTINFILE_OFFSET (D_direction_This - D_direction_FirstInFileThis)
+#define FIRSTINFILE_NR (FIRSTINFILE_MAX_IDX - FIRSTINFILE_MIN_IDX +1)
+
+#define NON_SPECIAL_DIRECTIONS_NR (FIRSTINFILE_MAX_IDX +1)
+
 enum button_specification_type {
   BST_direction,
   BST_function,
diff --git a/tp/Texinfo/XS/main/get_perl_info.c 
b/tp/Texinfo/XS/main/get_perl_info.c
index 67d37fa4c1..705daa3d74 100644
--- a/tp/Texinfo/XS/main/get_perl_info.c
+++ b/tp/Texinfo/XS/main/get_perl_info.c
@@ -842,12 +842,12 @@ html_get_direction_index (CONVERTER *converter, const 
char *direction)
 {
   int i;
   int idx;
-  for (i = 0; i < D_button_FirstInFileNodeUp +1; i++)
+  for (i = 0; i < FIRSTINFILE_MAX_IDX +1; i++)
     {
       if (!strcmp (direction, html_button_direction_names[i]))
         return i;
     }
-  idx = D_button_FirstInFileNodeUp +1;
+  idx = FIRSTINFILE_MAX_IDX +1;
   if (converter)
     {
       for (i = 0; i < converter->special_unit_varieties.number; i++)



reply via email to

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