texinfo-commits
[Top][All Lists]
Advanced

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

[no subject]


From: Patrice Dumas
Date: Mon, 4 Dec 2023 18:42:15 -0500 (EST)

branch: master
commit 6f520a48677399dae24882903462ea878fa4f84c
Author: Patrice Dumas <pertusus@free.fr>
AuthorDate: Tue Dec 5 00:39:24 2023 +0100

    * tp/Texinfo/XS/main/converter_types.h (FILE_NUMBER_NAME): rename
    CURRENT_FILE_INFO as FILE_NUMBER_NAME.
    
    * tp/Texinfo/XS/convert/build_html_perl_state.c
    (build_html_formatting_state), tp/Texinfo/XS/convert/convert_html.c
    (html_command_text, html_free_converter),
    tp/Texinfo/XS/main/converter_types.h (CONVERTER): make
    referred_command_stack a stack instead of a list.
    
    * tp/Texinfo/XS/main/get_perl_info.c (converter_initialize): add and
    use FETCH macro.
    
    * tp/Texinfo/XS/convert/build_html_perl_state.c
    (build_html_formatting_state), tp/Texinfo/XS/convert/convert_html.c
    (html_free_converter, get_shared_conversion_state_integer)
    (register_modified_shared_conversion_state_integer),
    tp/Texinfo/XS/main/converter_types.h (CONVERTER): add functions to
    pass shared_conversion_state modified integers to perl.
    
    * tp/Texinfo/XS/convert/convert_html.c (format_protect_text)
    (url_protect_url_text, url_protect_file_text),
    tp/Texinfo/XS/convert/call_html_perl_function.c
    (call_formatting_function_format_protect_text): add.
    
    * tp/Texinfo/XS/convert/convert_html.c (html_command_id):
    add some const.  Rename target as target_info.
    
    * tp/Texinfo/XS/convert/convert_html.c (html_get_tree_root_element)
    (new_sectioning_command_target, get_target): use
    element_builtin_data_cmd and variables for the flags to get the
    expected result even with user-defined @-commands.
    
    * tp/Texinfo/XS/main/get_perl_info.c (converter_initialize),
    tp/Texinfo/XS/main/converter_types.h (CONVERTER): add output_format
    and pass from perl.
    
    * tp/Texinfo/XS/convert/convert_html.c (external_node_href)
    (htmlxref_split_type_names, htmlxref_entries)
    (compare_htmlxref_manual, find_htmlxref_manual)
    (check_htmlxref_already_warned, html_initialize_output_state)
    (html_free_converter), tp/Texinfo/XS/convert/call_html_perl_function.c
    (call_file_id_setting_external_target_split_name)
    (call_file_id_setting_external_target_non_split_name),
    tp/Texinfo/XS/main/converter_types.h (enum htmlxref_split_type)
    (HTMLXREF_MANUAL, HTMLXREF_MANUAL_LIST)
    (HTMLXREF_MANUAL_ELEMENT_WARNED, HTMLXREF_MANUAL_ELEMENT_WARNED_LIST)
    (CONVERTER): get htmlxref from perl, add related functions, set
    self->document_htmlxref_split_type, sort htmlxref information, add
    functions to get and warn if htmlxref not found.  Implement
    external_node_href.
    
    * tp/Texinfo/XS/convert/call_html_perl_function.c: use SvPVutf8 more.
    
    * tp/Texinfo/XS/convert/convert_html.c (html_command_filename)
    (command_root_element_command, html_command_href)
    (html_command_contents_target, html_command_contents_href)
    (mini_toc_internal, contents_inline_element, convert_contents_command)
    (convert_heading_command),
    tp/Texinfo/XS/convert/call_html_perl_function.c
    (call_formatting_function_format_contents)
    (call_formatting_function_format_separate_anchor)
    (call_formatting_function_format_element_header)
    tp/Texinfo/XS/convert/call_html_perl_function.h
    (TARGET_DIRECTORY_FILENAME),: implement html_command_filename,
    command_root_element_command, html_command_href,
    html_command_contents_target, html_command_contents_href,
    mini_toc_internal contents_inline_element . Add conversion functions
    convert_contents_command and convert_heading_command. Add perl
    functions call.
---
 ChangeLog                                       |   72 +
 tp/Texinfo/XS/convert/build_html_perl_state.c   |   48 +-
 tp/Texinfo/XS/convert/call_html_perl_function.c |  453 ++-
 tp/Texinfo/XS/convert/call_html_perl_function.h |   27 +-
 tp/Texinfo/XS/convert/convert_html.c            | 4495 +++++++++++++++--------
 tp/Texinfo/XS/convert/convert_html.h            |    1 +
 tp/Texinfo/XS/convert/converter.c               |    3 +-
 tp/Texinfo/XS/convert/converter.h               |    2 +-
 tp/Texinfo/XS/convert/get_html_perl_info.c      |   50 +
 tp/Texinfo/XS/main/converter_types.h            |   61 +-
 tp/Texinfo/XS/main/get_perl_info.c              |   43 +-
 11 files changed, 3713 insertions(+), 1542 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 963ea76dae..89f121084f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,75 @@
+2023-12-04  Patrice Dumas  <pertusus@free.fr>
+
+       * tp/Texinfo/XS/main/converter_types.h (FILE_NUMBER_NAME): rename
+       CURRENT_FILE_INFO as FILE_NUMBER_NAME.
+
+       * tp/Texinfo/XS/convert/build_html_perl_state.c
+       (build_html_formatting_state), tp/Texinfo/XS/convert/convert_html.c
+       (html_command_text, html_free_converter),
+       tp/Texinfo/XS/main/converter_types.h (CONVERTER): make
+       referred_command_stack a stack instead of a list.
+
+       * tp/Texinfo/XS/main/get_perl_info.c (converter_initialize): add and
+       use FETCH macro.
+
+       * tp/Texinfo/XS/convert/build_html_perl_state.c
+       (build_html_formatting_state), tp/Texinfo/XS/convert/convert_html.c
+       (html_free_converter, get_shared_conversion_state_integer)
+       (register_modified_shared_conversion_state_integer),
+       tp/Texinfo/XS/main/converter_types.h (CONVERTER): add functions to
+       pass shared_conversion_state modified integers to perl.
+
+       * tp/Texinfo/XS/convert/convert_html.c (format_protect_text)
+       (url_protect_url_text, url_protect_file_text),
+       tp/Texinfo/XS/convert/call_html_perl_function.c
+       (call_formatting_function_format_protect_text): add.
+
+       * tp/Texinfo/XS/convert/convert_html.c (html_command_id):
+       add some const.  Rename target as target_info.
+
+       * tp/Texinfo/XS/convert/convert_html.c (html_get_tree_root_element)
+       (new_sectioning_command_target, get_target): use
+       element_builtin_data_cmd and variables for the flags to get the
+       expected result even with user-defined @-commands.
+
+       * tp/Texinfo/XS/main/get_perl_info.c (converter_initialize),
+       tp/Texinfo/XS/main/converter_types.h (CONVERTER): add output_format
+       and pass from perl.
+
+       * tp/Texinfo/XS/convert/convert_html.c (external_node_href)
+       (htmlxref_split_type_names, htmlxref_entries)
+       (compare_htmlxref_manual, find_htmlxref_manual)
+       (check_htmlxref_already_warned, html_initialize_output_state)
+       (html_free_converter), tp/Texinfo/XS/convert/call_html_perl_function.c
+       (call_file_id_setting_external_target_split_name)
+       (call_file_id_setting_external_target_non_split_name),
+       tp/Texinfo/XS/main/converter_types.h (enum htmlxref_split_type)
+       (HTMLXREF_MANUAL, HTMLXREF_MANUAL_LIST)
+       (HTMLXREF_MANUAL_ELEMENT_WARNED, HTMLXREF_MANUAL_ELEMENT_WARNED_LIST)
+       (CONVERTER): get htmlxref from perl, add related functions, set
+       self->document_htmlxref_split_type, sort htmlxref information, add
+       functions to get and warn if htmlxref not found.  Implement
+       external_node_href.
+
+       * tp/Texinfo/XS/convert/call_html_perl_function.c: use SvPVutf8 more.
+
+       * tp/Texinfo/XS/convert/convert_html.c (html_command_filename)
+       (command_root_element_command, html_command_href)
+       (html_command_contents_target, html_command_contents_href)
+       (mini_toc_internal, contents_inline_element, convert_contents_command)
+       (convert_heading_command),
+       tp/Texinfo/XS/convert/call_html_perl_function.c
+       (call_formatting_function_format_contents)
+       (call_formatting_function_format_separate_anchor)
+       (call_formatting_function_format_element_header)
+       tp/Texinfo/XS/convert/call_html_perl_function.h
+       (TARGET_DIRECTORY_FILENAME),: implement html_command_filename,
+       command_root_element_command, html_command_href,
+       html_command_contents_target, html_command_contents_href,
+       mini_toc_internal contents_inline_element . Add conversion functions
+       convert_contents_command and convert_heading_command. Add perl
+       functions call.
+
 2023-12-04  Patrice Dumas  <pertusus@free.fr>
 
        * tp/maintain/setup_converters_code_tables.pl: add include
diff --git a/tp/Texinfo/XS/convert/build_html_perl_state.c 
b/tp/Texinfo/XS/convert/build_html_perl_state.c
index 326031d3fc..6df4d70572 100644
--- a/tp/Texinfo/XS/convert/build_html_perl_state.c
+++ b/tp/Texinfo/XS/convert/build_html_perl_state.c
@@ -34,6 +34,7 @@
 #include "converter_types.h"
 /* for fatal HMSF_* */
 #include "utils.h"
+#include "extra.h"
 /* for debugging */
 #include "debug.h"
 #include "convert_to_texinfo.h"
@@ -776,9 +777,9 @@ build_html_formatting_state (CONVERTER *converter, unsigned 
long flags)
           av_clear (referred_command_stack_av);
         }
 
-      for (i = 0; i < converter->referred_command_stack.number; i++)
+      for (i = 0; i < converter->referred_command_stack.top; i++)
         {
-          ELEMENT *referred_e = converter->referred_command_stack.list[i];
+          const ELEMENT *referred_e = 
converter->referred_command_stack.stack[i];
           av_push (referred_command_stack_av,
                    newRV_inc ((SV *) referred_e->hv));
         }
@@ -829,6 +830,49 @@ build_html_formatting_state (CONVERTER *converter, 
unsigned long flags)
       converter->added_targets.number = 0;
     }
 
+  if (flags & HMSF_shared_conversion_state_integer)
+    {
+      int j;
+      SV **shared_conversion_state_sv;
+      HV *shared_conversion_state_hv;
+
+      FETCH(shared_conversion_state)
+
+      if (!shared_conversion_state_sv)
+        {
+          shared_conversion_state_hv = newHV ();
+          STORE("shared_conversion_state",
+             newRV_noinc ((SV *) shared_conversion_state_hv));
+        }
+      else
+        shared_conversion_state_hv
+          = (HV *) SvRV (*shared_conversion_state_sv);
+
+      for (j = 0; j < converter->shared_conversion_state_integer.number; j++)
+        {
+          const char *key = converter->shared_conversion_state_integer.list[j];
+          KEY_PAIR *k
+            = lookup_associated_info (
+                 &converter->shared_conversion_state.integers, key);
+
+          SV **int_key_sv = hv_fetch (shared_conversion_state_hv,
+                                  key, strlen (key), 0);
+          if (!int_key_sv)
+            {
+              SV *int_value_sv = newSViv ((IV) k->integer);
+              SV *int_sv = newRV_noinc (int_value_sv);
+              hv_store (shared_conversion_state_hv, key,
+                        strlen (key), int_sv, 0);
+            }
+          else
+            {
+              SV *int_value_sv = SvRV (*int_key_sv);
+              sv_setiv (int_value_sv, (IV) k->integer);
+            }
+        }
+      clear_strings_list (&converter->shared_conversion_state_integer);
+    }
+
   /*
   files_information_sv = hv_fetch (hv, "files_information",
                                   strlen ("files_information"), 0);
diff --git a/tp/Texinfo/XS/convert/call_html_perl_function.c 
b/tp/Texinfo/XS/convert/call_html_perl_function.c
index 9be480c296..35edb15597 100644
--- a/tp/Texinfo/XS/convert/call_html_perl_function.c
+++ b/tp/Texinfo/XS/convert/call_html_perl_function.c
@@ -510,6 +510,190 @@ call_file_id_setting_unit_file_name (CONVERTER *self, 
OUTPUT_UNIT *output_unit,
   return 0;
 }
 
+TARGET_DIRECTORY_FILENAME *
+call_file_id_setting_external_target_split_name (CONVERTER *self,
+                     const char *normalized, const ELEMENT *element,
+                     const char *target, const char *directory,
+                     const char *file_name)
+{
+  SV **file_id_setting_sv;
+
+  dTHX;
+
+  if (!self->hv)
+    return 0;
+
+  file_id_setting_sv = hv_fetch (self->hv, "file_id_setting",
+                                 strlen ("file_id_setting"), 0);
+  if (file_id_setting_sv)
+    {
+      SV **external_target_split_name_sv;
+      HV *file_id_setting_hv = (HV *)SvRV(*file_id_setting_sv);
+      external_target_split_name_sv = hv_fetch (file_id_setting_hv,
+                                   "external_target_split_name",
+                                   strlen ("external_target_split_name"), 0);
+
+      if (external_target_split_name_sv)
+        {
+          int count;
+          SV *target_sv;
+          SV *filename_sv;
+          SV *directory_sv;
+          TARGET_DIRECTORY_FILENAME *result = (TARGET_DIRECTORY_FILENAME *)
+              malloc (sizeof (TARGET_DIRECTORY_FILENAME));
+          memset (result, 0, sizeof (TARGET_DIRECTORY_FILENAME));
+
+          dSP;
+
+          ENTER;
+          SAVETMPS;
+
+          PUSHMARK(SP);
+          EXTEND(SP, 6);
+
+          PUSHs(sv_2mortal (newRV_inc (self->hv)));
+          PUSHs(sv_2mortal (newSVpv (normalized, 0)));
+          PUSHs(sv_2mortal (newRV_inc (element->hv)));
+          /* FIXME encoding */
+          PUSHs(sv_2mortal (newSVpv (target, 0)));
+          PUSHs(sv_2mortal (newSVpv (directory, 0)));
+          PUSHs(sv_2mortal (newSVpv (file_name, 0)));
+          PUTBACK;
+
+          count = call_sv (*external_target_split_name_sv, G_ARRAY);
+
+          SPAGAIN;
+
+          if (count != 3)
+            croak("external_target_split_name should return 3 items\n");
+
+          filename_sv = POPs;
+          if (SvOK (filename_sv))
+            {
+              STRLEN len;
+              char *filename_ret = SvPV (filename_sv, len);
+              result->filename = strdup (filename_ret);
+            }
+          else
+            result->filename = strdup ("");
+
+          directory_sv = POPs;
+          if (SvOK (directory_sv))
+            {
+              STRLEN len;
+              char *directory_ret = SvPV (directory_sv, len);
+              result->directory = strdup (directory_ret);
+            }
+          else
+            result->directory = strdup ("");
+
+          target_sv = POPs;
+          if (SvOK (target_sv))
+            {
+              STRLEN len;
+              char *target_ret = SvPV (target_sv, len);
+              result->target = strdup (target_ret);
+            }
+          else
+            result->target = strdup ("");
+
+          PUTBACK;
+
+          FREETMPS;
+          LEAVE;
+
+          return result;
+        }
+    }
+  return 0;
+}
+
+TARGET_FILENAME *
+call_file_id_setting_external_target_non_split_name (CONVERTER *self,
+                     const char *normalized, const ELEMENT *element,
+                     const char *target, const char *file)
+{
+  SV **file_id_setting_sv;
+
+  dTHX;
+
+  if (!self->hv)
+    return 0;
+
+  file_id_setting_sv = hv_fetch (self->hv, "file_id_setting",
+                                 strlen ("file_id_setting"), 0);
+  if (file_id_setting_sv)
+    {
+      SV **external_target_non_split_name_sv;
+      HV *file_id_setting_hv = (HV *)SvRV(*file_id_setting_sv);
+      external_target_non_split_name_sv = hv_fetch (file_id_setting_hv,
+                                   "external_target_non_split_name",
+                                strlen ("external_target_non_split_name"), 0);
+
+      if (external_target_non_split_name_sv)
+        {
+          int count;
+          SV *target_sv;
+          SV *file_sv;
+          TARGET_FILENAME *result
+            = (TARGET_FILENAME *) malloc (sizeof (TARGET_FILENAME));
+          result->filename = 0;
+
+          dSP;
+
+          ENTER;
+          SAVETMPS;
+
+          PUSHMARK(SP);
+          EXTEND(SP, 5);
+
+          PUSHs(sv_2mortal (newRV_inc (self->hv)));
+          PUSHs(sv_2mortal (newSVpv (normalized, 0)));
+          PUSHs(sv_2mortal (newRV_inc (element->hv)));
+          /* FIXME encoding */
+          PUSHs(sv_2mortal (newSVpv (target, 0)));
+          PUSHs(sv_2mortal (newSVpv (file, 0)));
+          PUTBACK;
+
+          count = call_sv (*external_target_non_split_name_sv, G_ARRAY);
+
+          SPAGAIN;
+
+          if (count != 2)
+            croak("external_target_non_split_name should return 2 items\n");
+
+          file_sv = POPs;
+          if (SvOK (file_sv))
+            {
+              STRLEN len;
+              char *file_ret = SvPV (file_sv, len);
+              result->filename = strdup (file_ret);
+            }
+
+          target_sv = POPs;
+          if (SvOK (target_sv))
+            {
+              STRLEN len;
+              char *target_ret = SvPV (target_sv, len);
+              result->target = strdup (target_ret);
+            }
+
+          PUTBACK;
+
+          FREETMPS;
+          LEAVE;
+
+          return result;
+        }
+    }
+  return 0;
+}
+
+
+
+
+
+
 char *
 call_formatting_function_format_title_titlepage (CONVERTER *self)
 {
@@ -554,8 +738,66 @@ call_formatting_function_format_title_titlepage (CONVERTER 
*self)
     croak("format_title_titlepage should return 1 item\n");
 
   result_sv = POPs;
-  /* FIXME encoding */
-  result_ret = SvPV (result_sv, len);
+  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_protect_text (CONVERTER *self,
+                                              const char *text)
+{
+  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
+    = self->formatting_references[FR_format_protect_text].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, 1);
+
+  PUSHs(sv_2mortal (newRV_inc (self->hv)));
+  PUSHs(sv_2mortal (newSVpv_utf8 (text, 0)));
+  PUTBACK;
+
+  count = call_sv (formatting_reference_sv,
+                   G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("format_protect_text should return 1 item\n");
+
+  result_sv = POPs;
+  result_ret = SvPVutf8 (result_sv, len);
   result = strdup (result_ret);
 
   PUTBACK;
@@ -612,8 +854,7 @@ call_formatting_function_format_footnotes_segment 
(CONVERTER *self)
     croak("format_footnotes_segment should return 1 item\n");
 
   result_sv = POPs;
-  /* FIXME encoding */
-  result_ret = SvPV (result_sv, len);
+  result_ret = SvPVutf8 (result_sv, len);
   result = strdup (result_ret);
 
   PUTBACK;
@@ -670,8 +911,7 @@ call_formatting_function_format_footnotes_sequence 
(CONVERTER *self)
     croak("format_footnotes_sequence should return 1 item\n");
 
   result_sv = POPs;
-  /* FIXME encoding */
-  result_ret = SvPV (result_sv, len);
+  result_ret = SvPVutf8 (result_sv, len);
   result = strdup (result_ret);
 
   PUTBACK;
@@ -737,8 +977,7 @@ call_formatting_function_format_end_file (CONVERTER *self, 
char *filename,
     croak("format_end_file should return 1 item\n");
 
   result_sv = POPs;
-  /* FIXME encoding */
-  result_ret = SvPV (result_sv, len);
+  result_ret = SvPVutf8 (result_sv, len);
   result = strdup (result_ret);
 
   PUTBACK;
@@ -804,8 +1043,7 @@ call_formatting_function_format_begin_file (CONVERTER 
*self, char *filename,
     croak("format_begin_file should return 1 item\n");
 
   result_sv = POPs;
-  /* FIXME encoding */
-  result_ret = SvPV (result_sv, len);
+  result_ret = SvPVutf8 (result_sv, len);
   result = strdup (result_ret);
 
   PUTBACK;
@@ -956,7 +1194,7 @@ call_formatting_function_format_heading_text (CONVERTER 
*self,
                                   const STRING_LIST *classes,
                                   const char *text,
                                   int level, const char *id,
-                                  ELEMENT *element, const char *target)
+                                  const ELEMENT *element, const char *target)
 {
   int count;
   char *result = 0;
@@ -1044,6 +1282,199 @@ call_formatting_function_format_heading_text (CONVERTER 
*self,
   return result;
 }
 
+char *
+call_formatting_function_format_contents (CONVERTER *self,
+                              const char *cmdname, const ELEMENT *command,
+                              const char *filename)
+{
+  int count;
+  char *result = 0;
+  char *result_ret;
+  STRLEN len;
+  SV *result_sv;
+  SV *formatting_reference_sv;
+
+  dTHX;
+
+  if (!self->hv)
+    return 0;
+
+  formatting_reference_sv
+    = self->formatting_references[
+         FR_format_contents].sv_reference;
+
+  if (self->modified_state)
+    {
+      build_html_formatting_state (self, self->modified_state);
+      self->modified_state = 0;
+    }
+
+  build_tree_to_build (&self->tree_to_build);
+
+  dSP;
+
+  ENTER;
+  SAVETMPS;
+
+  PUSHMARK(SP);
+  EXTEND(SP, 4);
+
+  PUSHs(sv_2mortal (newRV_inc (self->hv)));
+  PUSHs(sv_2mortal (newSVpv (cmdname, 0)));
+  PUSHs(sv_2mortal (newRV_inc (command->hv)));
+  PUSHs(sv_2mortal (newSVpv (filename, 0)));
+  PUTBACK;
+
+  count = call_sv (formatting_reference_sv,
+                   G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("format_contents 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_separate_anchor (CONVERTER *self,
+                                   const char *id, const char *class)
+{
+  int count;
+  char *result = 0;
+  char *result_ret;
+  STRLEN len;
+  SV *result_sv;
+  SV *formatting_reference_sv;
+
+  dTHX;
+
+  if (!self->hv)
+    return 0;
+
+  formatting_reference_sv
+    = self->formatting_references[
+         FR_format_separate_anchor].sv_reference;
+
+  if (self->modified_state)
+    {
+      build_html_formatting_state (self, self->modified_state);
+      self->modified_state = 0;
+    }
+
+  build_tree_to_build (&self->tree_to_build);
+
+  dSP;
+
+  ENTER;
+  SAVETMPS;
+
+  PUSHMARK(SP);
+  EXTEND(SP, 4);
+
+  PUSHs(sv_2mortal (newRV_inc (self->hv)));
+  PUSHs(sv_2mortal (newSVpv_utf8 (id, 0)));
+  PUSHs(sv_2mortal (newSVpv_utf8 (class, 0)));
+  PUTBACK;
+
+  count = call_sv (formatting_reference_sv,
+                   G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("format_separate_anchor 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_element_header (CONVERTER *self,
+                              const char *cmdname, const ELEMENT *command,
+                              const OUTPUT_UNIT *output_unit)
+{
+  int count;
+  char *result = 0;
+  char *result_ret;
+  STRLEN len;
+  SV *result_sv;
+  SV *formatting_reference_sv;
+
+  dTHX;
+
+  if (!self->hv)
+    return 0;
+
+  formatting_reference_sv
+    = self->formatting_references[
+         FR_format_element_header].sv_reference;
+
+  if (self->modified_state)
+    {
+      build_html_formatting_state (self, self->modified_state);
+      self->modified_state = 0;
+    }
+
+  build_tree_to_build (&self->tree_to_build);
+
+  dSP;
+
+  ENTER;
+  SAVETMPS;
+
+  PUSHMARK(SP);
+  EXTEND(SP, 4);
+
+  PUSHs(sv_2mortal (newRV_inc (self->hv)));
+  PUSHs(sv_2mortal (newSVpv (cmdname, 0)));
+  PUSHs(sv_2mortal (newRV_inc (command->hv)));
+  PUSHs(sv_2mortal (newRV_inc (output_unit->hv)));
+  PUTBACK;
+
+  count = call_sv (formatting_reference_sv,
+                   G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("format_element_header 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_element_footer (CONVERTER *self,
                               const enum output_unit_type unit_type,
diff --git a/tp/Texinfo/XS/convert/call_html_perl_function.h 
b/tp/Texinfo/XS/convert/call_html_perl_function.h
index 76c2807c69..0a9cef509d 100644
--- a/tp/Texinfo/XS/convert/call_html_perl_function.h
+++ b/tp/Texinfo/XS/convert/call_html_perl_function.h
@@ -13,6 +13,12 @@ typedef struct FILE_NAME_PATH {
     char *filepath;
 } FILE_NAME_PATH;
 
+typedef struct TARGET_DIRECTORY_FILENAME {
+    char *filename;
+    char *directory;
+    char *target;
+} TARGET_DIRECTORY_FILENAME;
+
 typedef struct TARGET_CONTENTS_FILENAME {
     char *target;
     char *filename;
@@ -37,8 +43,19 @@ TARGET_CONTENTS_FILENAME * 
call_file_id_setting_sectioning_command_target_name
 FILE_NAME_PATH *call_file_id_setting_unit_file_name (CONVERTER *self,
                                                      OUTPUT_UNIT *output_unit,
                                                char *filename, char *filepath);
+TARGET_DIRECTORY_FILENAME *call_file_id_setting_external_target_split_name
+                    (CONVERTER *self,
+                     const char *normalized, const ELEMENT *element,
+                     const char *target, const char *directory,
+                     const char *file_name);
+TARGET_FILENAME *call_file_id_setting_external_target_non_split_name
+                    (CONVERTER *self,
+                     const char *normalized, const ELEMENT *element,
+                     const char *target, const char *file);
 
 char *call_formatting_function_format_title_titlepage (CONVERTER *self);
+char *call_formatting_function_format_protect_text (CONVERTER *self,
+                                                    const char *text);
 char *call_formatting_function_format_footnotes_sequence (CONVERTER *self);
 char *call_formatting_function_format_footnotes_segment (CONVERTER *self);
 char *call_formatting_function_format_end_file (CONVERTER *self,
@@ -56,7 +73,15 @@ char *call_formatting_function_format_heading_text 
(CONVERTER *self,
                                   const STRING_LIST *classes,
                                   const char *text,
                                   int level, const char *id,
-                                  ELEMENT *element, const char *target);
+                                  const ELEMENT *element, const char *target);
+char *call_formatting_function_format_element_header (CONVERTER *self,
+                              const char *cmdname, const ELEMENT *command,
+                              const OUTPUT_UNIT *output_unit);
+char *call_formatting_function_format_contents (CONVERTER *self,
+                              const char *cmdname, const ELEMENT *command,
+                              const char *filename);
+char *call_formatting_function_format_separate_anchor (CONVERTER *self,
+                                   const char *id, const char *class);
 char *call_formatting_function_format_element_footer (CONVERTER *self,
                               const enum output_unit_type unit_type,
                               const OUTPUT_UNIT *output_unit,
diff --git a/tp/Texinfo/XS/convert/convert_html.c 
b/tp/Texinfo/XS/convert/convert_html.c
index 0ebcf71d4f..02626e9e65 100644
--- a/tp/Texinfo/XS/convert/convert_html.c
+++ b/tp/Texinfo/XS/convert/convert_html.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
+#include <ctype.h>
 
 #include "global_commands_types.h"
 #include "tree_types.h"
@@ -46,6 +47,8 @@
 /* for OTXI_UNICODE_TEXT_CASES */
 #include "unicode.h"
 #include "manipulate_tree.h"
+/* for new_complete_menu_master_menu */
+#include "structuring.h"
 #include "convert_html.h"
 
 enum count_elements_in_filename_type {
@@ -131,6 +134,19 @@ const char *special_unit_info_type_names[SUI_type_heading 
+ 1] =
   #undef sui_type
 };
 
+const char *htmlxref_split_type_names[htmlxref_split_type_chapter + 1] =
+{
+  "mono", "node", "section", "chapter"
+};
+
+enum htmlxref_split_type htmlxref_entries[htmlxref_split_type_chapter + 
1][htmlxref_split_type_chapter + 1] = {
+ { htmlxref_split_type_mono, htmlxref_split_type_chapter, 
htmlxref_split_type_section, htmlxref_split_type_node },
+ { htmlxref_split_type_node, htmlxref_split_type_section, 
htmlxref_split_type_chapter, htmlxref_split_type_mono },
+ { htmlxref_split_type_section, htmlxref_split_type_chapter, 
htmlxref_split_type_node, htmlxref_split_type_mono },
+ { htmlxref_split_type_chapter, htmlxref_split_type_section, 
htmlxref_split_type_node, htmlxref_split_type_mono },
+};
+
+
 TRANSLATED_SUI_ASSOCIATION translated_special_unit_info[] = {
   {SUIT_type_heading, SUI_type_heading},
   /* these special types end the list */
@@ -282,6 +298,9 @@ html_get_tree_root_element (CONVERTER *self, const ELEMENT 
*command,
 
   while (1)
     {
+      enum command_id data_cmd = element_builtin_data_cmd (current);
+      unsigned long flags = builtin_command_data[data_cmd].flags;
+
       if (current->type == ET_special_unit_element)
         {
           ROOT_AND_UNIT *result = malloc (sizeof (ROOT_AND_UNIT));
@@ -290,14 +309,14 @@ html_get_tree_root_element (CONVERTER *self, const 
ELEMENT *command,
           return result;
         }
 
-      if (current->cmd && (builtin_command_flags(current) & CF_root))
+      if (data_cmd && (flags & CF_root))
         root_command = current;
-      else if (current->cmd && (builtin_command_flags(current) & CF_block)
-               && builtin_command_data[current->cmd].data == BLOCK_region)
+      else if (data_cmd && (flags & CF_block)
+               && builtin_command_data[data_cmd].data == BLOCK_region)
         {
           const OUTPUT_UNIT_LIST *output_units
              = retrieve_output_units (self->document_units_descriptor);
-          if (current->cmd == CM_copying
+          if (data_cmd == CM_copying
               && self->document->global_commands->insertcopying.number > 0)
             {
               const ELEMENT_LIST global_insertcopying
@@ -313,7 +332,7 @@ html_get_tree_root_element (CONVERTER *self, const ELEMENT 
*command,
                     return cur_result;
                 }
             }
-          else if (current->cmd == CM_titlepage
+          else if (data_cmd == CM_titlepage
                    && self->conf->USE_TITLEPAGE_FOR_TITLE > 0
                    && self->conf->SHOW_TITLE > 0
                    && output_units->number > 0)
@@ -330,7 +349,7 @@ html_get_tree_root_element (CONVERTER *self, const ELEMENT 
*command,
           return result;
         }
       else if (find_container
-               && html_commands_data[current->cmd].flags & HF_special_variety)
+               && html_commands_data[data_cmd].flags & HF_special_variety)
         {
           int j;
           for (j = 0; self->command_special_variety_name_index[j].cmd; j++)
@@ -338,7 +357,7 @@ html_get_tree_root_element (CONVERTER *self, const ELEMENT 
*command,
      /* @footnote and possibly @*contents when a separate element is set */
               COMMAND_ID_INDEX cmd_variety_index
                 = self->command_special_variety_name_index[j];
-              if (cmd_variety_index.cmd == current->cmd)
+              if (cmd_variety_index.cmd == data_cmd)
                 {
                   char *special_unit_variety
                 = self->special_unit_varieties.list[cmd_variety_index.index];
@@ -772,10 +791,11 @@ html_register_footnote (CONVERTER *self, const ELEMENT 
*command,
 {
   HTML_PENDING_FOOTNOTE_STACK *stack;
   HTML_PENDING_FOOTNOTE *pending_footnote;
+
   KEY_PAIR *k = lookup_associated_info 
(&self->shared_conversion_state.integers,
                                         "in_skipped_node_top");
 
-  if (k && k->integer == 1)
+  if (k && k->integer > 0)
     return;
 
   stack = &self->pending_footnotes;
@@ -1602,6 +1622,12 @@ new_sectioning_command_target (CONVERTER *self, const 
ELEMENT *command)
   TARGET_FILENAME *target_filename
     = normalized_sectioning_command_filename (self, command);
 
+  /* should not be needed for a sectioning command, as it should not
+     be possible for that command to be a user-defined command,
+     but it is better to be consistent, and it may change in the future */
+  enum command_id data_cmd = element_builtin_data_cmd (command);
+  unsigned long flags = builtin_command_data[data_cmd].flags;
+
   normalized_name = target_filename->target;
   filename = target_filename->filename;
 
@@ -1624,7 +1650,7 @@ new_sectioning_command_target (CONVERTER *self, const 
ELEMENT *command)
   free (target_base);
 
   if (strlen (target)
-      && (builtin_command_flags(command) & CF_sectioning_heading))
+      && (flags & CF_sectioning_heading))
     {
       char *target_base_contents;
       char *target_base_shortcontents;
@@ -1984,6 +2010,136 @@ html_default_format_protect_text (const char *text, 
TEXT *result)
   OTXI_CONVERT_TEXT ( , )
 }
 
+void
+format_protect_text (CONVERTER *self, const char *text, TEXT *result)
+{
+  if (self->formatting_references[FR_format_protect_text].status
+                                             == FRS_status_default_set)
+    {
+      html_default_format_protect_text (text, result);
+    }
+  else
+    {
+      char *protected_text
+        = call_formatting_function_format_protect_text (self, text);
+      text_append (result, protected_text);
+      free (protected_text);
+    }
+}
+
+static const char *reserved_unreserved_percent = "-_.!~*'()$&+,/:;=?@[]#%";
+
+static char *
+url_protect_url_text (CONVERTER *self, const char *input_string)
+{
+  TEXT text;
+  TEXT result;
+  text_init (&text);
+  const char *p = input_string;
+
+  text_append (&text, "");
+
+  /* protect 'ligntly', do not protect unreserved and reserved characters
+     + the % itself */
+  while (*p)
+    {
+      if (isascii_alnum (*p) || isascii_alpha (*p) || isascii_digit (*p))
+        {
+          text_append_n (&text, p, 1);
+          p++;
+        }
+      else
+        {
+          int n = strspn (p, reserved_unreserved_percent);
+          if (n)
+            {
+              text_append_n (&text, p, n);
+              p += n;
+            }
+          else
+            {
+              int i;
+              int char_len = 1;
+              if (!isascii (*p))
+                {
+                  /* Protect UTF-8 with continuation bytes. */
+                  while ((p[char_len] & 0xC0) == 0x80)
+                    char_len++;
+                }
+              for (i = 0; i < char_len; i++)
+                {
+                  text_printf (&text, "%%%02x", *p);
+                  p += 1;
+                }
+            }
+        }
+    }
+  text_init (&result);
+  format_protect_text (self, text.text, &result);
+  free (text.text);
+  return (result.text);
+}
+
+static const char *file_path_punct = "-_.~/:";
+
+/*
+ protect a file path used in an url.  Characters appearing in file paths
+ are not protected.   All the other characters that can be percent
+ protected are protected, including characters with specific meaning in url.
+ */
+/* TODO readd static when the function is used
+static 
+*/
+char *
+url_protect_file_text (CONVERTER *self, const char *input_string)
+{
+  TEXT text;
+  TEXT result;
+  text_init (&text);
+  const char *p = input_string;
+
+  text_append (&text, "");
+
+  while (*p)
+    {
+      if (isascii_alnum (*p) || isascii_alpha (*p) || isascii_digit (*p))
+        {
+          text_append_n (&text, p, 1);
+          p++;
+        }
+      else
+        {
+          int n = strspn (p, file_path_punct);
+          if (n)
+            {
+              text_append_n (&text, p, n);
+              p += n;
+            }
+          else
+            {
+              int i;
+              int char_len = 1;
+              if (!isascii (*p))
+                {
+                  /* Protect UTF-8 with continuation bytes. */
+                  while ((p[char_len] & 0xC0) == 0x80)
+                    char_len++;
+                }
+              for (i = 0; i < char_len; i++)
+                {
+                  text_printf (&text, "%%%02x", *p);
+                  p += 1;
+                }
+            }
+        }
+    }
+
+  text_init (&result);
+  format_protect_text (self, text.text, &result);
+  free (text.text);
+  return (result.text);
+}
+
 static TREE_ADDED_ELEMENTS *
 new_tree_added_elements ()
 {
@@ -2168,7 +2324,7 @@ convert_tree_new_formatting_context (CONVERTER *self, 
const ELEMENT *tree,
     }
 
   if (self->conf->DEBUG > 0)
-    fprintf (stderr, "new_fmt_ctx %s%s\n", context_string_str.text,
+    fprintf (stderr, "XS|new_fmt_ctx %s%s\n", context_string_str.text,
                                            multiple_pass_str);
 
   xasprintf (&explanation, "new_fmt_ctx %s", context_string_str.text);
@@ -2210,9 +2366,12 @@ get_target (CONVERTER *self, const ELEMENT *element)
 {
   HTML_TARGET *result
    = find_element_target (&self->html_targets, element);
-  if (!result && element->cmd
-      && builtin_command_flags(element) & CF_sectioning_heading
-      && !(builtin_command_flags(element) & CF_root))
+  enum command_id data_cmd = element_builtin_data_cmd (element);
+  unsigned long flags = builtin_command_data[data_cmd].flags;
+
+  if (!result && data_cmd
+      && flags & CF_sectioning_heading
+      && !(flags & CF_root))
     {
       new_sectioning_command_target (self, element);
 
@@ -2224,1822 +2383,3129 @@ get_target (CONVERTER *self, const ELEMENT *element)
   return result;
 }
 
-char *html_command_id (CONVERTER *self, ELEMENT *command)
+char *html_command_id (CONVERTER *self, const ELEMENT *command)
 {
-  HTML_TARGET *target = get_target (self, command);
-  if (target)
-    return target->target;
+  HTML_TARGET *target_info = get_target (self, command);
+  if (target_info)
+    return target_info->target;
   else
     return 0;
 }
 
-ELEMENT *
-new_element_added (TREE_ADDED_ELEMENTS *added_elements, enum element_type type)
+static int
+compare_htmlxref_manual (const void *a, const void *b)
 {
-  ELEMENT *new = new_element (type);
-  add_to_element_list (&added_elements->added, new);
-  return new;
+  const HTMLXREF_MANUAL *hxfm_a = (const HTMLXREF_MANUAL *) a;
+  const HTMLXREF_MANUAL *hxfm_b = (const HTMLXREF_MANUAL *) b;
+
+  return strcmp (hxfm_a->manual, hxfm_b->manual);
 }
 
-TREE_ADDED_ELEMENTS *
-html_command_tree (CONVERTER *self, ELEMENT *command, int no_number)
+HTMLXREF_MANUAL *
+find_htmlxref_manual
+     (const HTMLXREF_MANUAL_LIST *htmlxref_manuals, const char *manual)
 {
-  TREE_ADDED_ELEMENTS *tree;
-  HTML_TARGET *target;
+  HTMLXREF_MANUAL *result = 0;
+  static HTMLXREF_MANUAL searched_manual;
+  /* remove const with a cast, it is more efficient than duplicating */
+  searched_manual.manual = (char *) manual;
 
-  ELEMENT *manual_content = lookup_extra_element (command,
-                                                  "manual_content");
-  if (manual_content)
+  result = (HTMLXREF_MANUAL *) bsearch (&searched_manual,
+                htmlxref_manuals->list,
+                htmlxref_manuals->number, sizeof(HTMLXREF_MANUAL),
+                compare_htmlxref_manual);
+
+  return result;
+}
+
+size_t
+check_htmlxref_already_warned (CONVERTER *self, const char *manual_name,
+                               const ELEMENT *source_command)
+{
+  size_t i;
+  HTMLXREF_MANUAL_ELEMENT_WARNED *htmlxref_warned;
+  HTMLXREF_MANUAL_ELEMENT_WARNED_LIST *htmlxref_warned_list
+    = &self->check_htmlxref_already_warned;
+
+  for (i = 0; i < htmlxref_warned_list->number; i++)
     {
-      ELEMENT *root_code;
-      ELEMENT *open_p;
-      ELEMENT *close_p;
+      htmlxref_warned = &htmlxref_warned_list->list[i];
+      if (!strcmp (htmlxref_warned->manual, manual_name)
+          && source_command == htmlxref_warned->element)
+        return i+1;
+    }
 
-      ELEMENT *node_content = lookup_extra_element (command,
-                                                    "node_content");
+  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];
 
-      tree = new_tree_added_elements ();
+  htmlxref_warned->element = source_command;
+  htmlxref_warned->manual = strdup (manual_name);
 
-      root_code = new_element_added (tree, ET__code);
-      open_p = new_element_added (tree, ET_NONE);
-      close_p = new_element_added (tree, ET_NONE);
+  htmlxref_warned_list->number++;
+  return 0;
+}
 
-      text_append_n (&open_p->text, "(", 1);
-      text_append_n (&close_p->text, ")", 1);
+char *
+external_node_href (CONVERTER *self, const ELEMENT *external_node,
+                    const char *source_filename, /* unused */
+                    const ELEMENT *source_command) /* for messages only */
+{
+  TEXT result;
+  char *target;
+  char *target_filebase;
+  /* used if !target_split */
+  char *file = 0;
+  /* used if target_split */
+  char *directory = 0;
+  char *extension = 0;
+  int target_split = 0;
+  char *normalized = lookup_extra_string (external_node, "normalized");
+  ELEMENT *node_contents = lookup_extra_element (external_node, 
"node_content");
+  ELEMENT *manual_content = lookup_extra_element (external_node,
+                                                  "manual_content");
 
-      add_to_element_contents (root_code, open_p);
-      add_to_contents_as_array (root_code, manual_content);
-      add_to_element_contents (root_code, close_p);
-      if (node_content)
-        add_to_contents_as_array (root_code, node_content);
+  TARGET_FILENAME *target_filename =
+    normalized_label_id_file (self, normalized, node_contents);
 
-      tree->tree = root_code;
-      add_to_element_list (&self->tree_to_build, root_code);
-      return tree;
-    }
+  /* undef if conversion is called through convert() */
+  if (self->conf->EXTERNAL_CROSSREF_SPLIT
+      && strlen (self->conf->EXTERNAL_CROSSREF_SPLIT))
+    /* initialize to EXTERNAL_CROSSREF_SPLIT */
+    target_split = 1;
 
-  target = get_target (self, command);
-  if (target)
+  if (self->conf->EXTERNAL_CROSSREF_EXTENSION)
+    extension = self->conf->EXTERNAL_CROSSREF_EXTENSION;
+  else if (self->conf->EXTENSION)
+    extension = self->conf->EXTENSION;
+
+  /* both to be freed before return */
+  target = target_filename->target;
+  target_filebase = target_filename->filename;
+  free (target_filename);
+
+  if (manual_content)
     {
-      if (!target->tree.status)
+      char *manual_name;
+      char *manual_base = 0;
+      char *p;
+      char *q = 0;
+      char *htmlxref_href = 0;
+      char saved;
+      enum htmlxref_split_type split_found = htmlxref_split_type_none;
+      int manual_len;
+      HTMLXREF_MANUAL *htmlxref_manual;
+      TEXT_OPTIONS *text_conv_options = copy_options_for_convert_text (self, 
0);
+      text_conv_options->code_state = 1;
+
+      manual_name = convert_to_text (manual_content, text_conv_options);
+
+      free (text_conv_options);
+
+      if (self->conf->IGNORE_REF_TO_TOP_NODE_UP && !strlen (target))
         {
-          tree = &target->tree;
-          tree->status = tree_added_status_normal;
-          if (command->type == ET_special_unit_element)
+          char *top_node_up = self->conf->TOP_NODE_UP;
+          if (top_node_up)
             {
-              const char *special_unit_variety
-                = command->associated_unit->special_unit_variety;
-              ELEMENT *heading_tree = special_unit_info_tree (self,
-                                   SUIT_type_heading, special_unit_variety);
-              tree->tree = heading_tree;
+              char *parentheses_manual_name;
+              xasprintf (&parentheses_manual_name, "(%s)", manual_name);
+              if (!strcmp (top_node_up, parentheses_manual_name))
+                {
+                  free (parentheses_manual_name);
+                  free (manual_name);
+                  free (target);
+                  free (target_filebase);
+                  return strdup ("");
+                }
             }
-          else if (command->cmd == CM_node || command->cmd == CM_anchor)
+        }
+      p = strrchr (manual_name, '/');
+      if (!p)
+        p = manual_name;
+      manual_len = strlen (manual_name);
+      if (manual_len >= 5
+          && !memcmp (manual_name +manual_len - 5, ".info", 5))
+        q = manual_name +manual_len - 5;
+      else if (manual_len >= 4
+               && !memcmp (manual_name +manual_len - 4, ".inf", 4))
+        q = manual_name +manual_len - 4;
+      if (q)
+        {
+          saved = *q;
+          *q = '\0';
+        }
+      manual_base = strdup (p);
+      if (q)
+        *q = saved;
+
+      htmlxref_manual = find_htmlxref_manual (&self->htmlxref, manual_base);
+
+      if (htmlxref_manual)
+        {
+          enum htmlxref_split_type *ordered_split_types
+             = htmlxref_entries[self->document_htmlxref_split_type];
+          int i;
+          for (i = 0; i < htmlxref_split_type_chapter +1; i++)
             {
-              ELEMENT *root_code = new_element_added (tree, ET__code);
-              add_to_contents_as_array (root_code, command->args.list[0]);
-              tree->tree = root_code;
-              add_to_element_list (&self->tree_to_build, tree->tree);
+              enum htmlxref_split_type split_ordered = ordered_split_types[i];
+              if (htmlxref_manual->urlprefix[split_ordered])
+                {
+                  split_found = split_ordered;
+                  htmlxref_href = url_protect_url_text (self,
+                                  htmlxref_manual->urlprefix[split_ordered]);
+                  break;
+                }
             }
-          else if (command->cmd == CM_float)
+        }
+      if (split_found != htmlxref_split_type_none)
+        {
+          if (split_found == htmlxref_split_type_mono)
+            target_split = 0;
+          else
+            target_split = 1;
+        }
+      else
+        { /* nothing specified for that manual, use default */
+          if (self->conf->CHECK_HTMLXREF > 0)
             {
-              tree->tree = float_type_number (self, command);
-              tree->status = tree_added_status_new_tree;
-              add_to_element_list (&self->tree_to_build, tree->tree);
+              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))
+                    {
+                      message_list_command_warn (&self->error_messages,
+                                                 source_command,
+                             "no htmlxref.cnf entry found for `%s'",
+                                                 manual_name);
+                    }
+                }
+              else
+                {
+                  if (!check_htmlxref_already_warned (self, manual_name, 0))
+                    {
+                      message_list_document_warn (&self->error_messages,
+                        self->conf,
+                        "no htmlxref.cnf entry found for `%s'", manual_name);
+                    }
+                }
             }
-          else if (command->args.number <= 0
-                   || command->args.list[0]->contents.number <= 0)
-            { /* no argument, nothing to do */
-              /* TODO check if possible */
-              tree->status = tree_added_status_no_tree;
+        }
+      free (manual_name);
+
+      if (target_split)
+        {
+          char *directory_part;
+          if (htmlxref_href)
+            {
+              directory_part = htmlxref_href;
             }
           else
             {
-              char *section_number
-                = lookup_extra_string (command, "section_number");
-              if (section_number && !self->conf->NUMBER_SECTIONS == 0)
+              TEXT dir_path;
+              char *url_encoded_path;
+              text_init (&dir_path);
+              if (self->conf->EXTERNAL_DIR)
                 {
-                  NAMED_STRING_ELEMENT_LIST *replaced_substrings
-                    = new_named_string_element_list ();
-                  ELEMENT *e_number = new_element (ET_NONE);
-                  ELEMENT *section_title_copy
-                     = copy_tree (command->args.list[0]);
-
-                  add_element_to_named_string_element_list (
-                              replaced_substrings, "section_title",
-                              section_title_copy);
-                  text_append (&e_number->text, section_number);
-                  add_element_to_named_string_element_list (
-                              replaced_substrings, "number", e_number);
-
-                  if (command->cmd == CM_appendix)
-                    {
-                      int status;
-                      int section_level = lookup_extra_integer (command,
-                                               "section_level", &status);
-                      if (section_level == 1)
-                        {
-                          tree->tree
-                            = gdt_tree ("Appendix {number} {section_title}",
-                                        self->document, self->conf,
-                                        replaced_substrings, 0, 0);
-                        }
-                    }
-                  if (!tree->tree)
-                    /* TRANSLATORS: numbered section title */
-                    tree->tree = gdt_tree ("{number} {section_title}",
-                                            self->document, self->conf,
-                                            replaced_substrings, 0, 0);
+                  text_printf (&dir_path, "%s/%s", self->conf->EXTERNAL_DIR,
+                                           manual_base);
+                }
+              else if (self->conf->SPLIT && strlen (self->conf->SPLIT))
+                {
+                  text_append_n (&dir_path, "../", 3);
+                  text_append (&dir_path, manual_base);
+                }
+              if (self->output_format && strlen (self->output_format))
+                {
+                  text_append_n (&dir_path, "_", 1);
+                  text_append (&dir_path, self->output_format);
+                }
+              url_encoded_path = url_protect_url_text (self, dir_path.text);
+              free (dir_path.text);
+              directory_part = url_encoded_path;
+            }
+          xasprintf (&directory, "%s/", directory_part);
+          free (directory_part);
+        }
+      else
+        { /* target not split */
+          if (htmlxref_href)
+            {
+              file = htmlxref_href;
+            }
+          else
+            {
+              TEXT file_path;
+              text_init (&file_path);
 
-                  destroy_named_string_element_list (replaced_substrings);
-                  tree->status = tree_added_status_new_tree;
-                  add_to_element_list (&self->tree_to_build, tree->tree);
+              if (self->conf->EXTERNAL_DIR)
+                {
+                  text_printf (&file_path, "%s/%s", self->conf->EXTERNAL_DIR,
+                                           manual_base);
                 }
-              else
+              else if (self->conf->SPLIT && strlen (self->conf->SPLIT))
                 {
-                  tree->status = tree_added_status_reused_tree;
-                  tree->tree = command->args.list[0];
+                  text_append_n (&file_path, "../", 3);
+                  text_append (&file_path, manual_base);
                 }
+              else
+                text_append (&file_path, manual_base);
+              if (extension)
+                text_printf (&file_path, ".%s", extension);
 
-              target->tree_nonumber.tree = command->args.list[0];
-              target->tree_nonumber.status = tree_added_status_reused_tree;
+              file = url_protect_url_text (self, file_path.text);
+              free (file_path.text);
             }
         }
-
-      if (no_number && target->tree_nonumber.tree)
-        return &target->tree_nonumber;
-      else
-        return &target->tree;
+      free (manual_base);
     }
 
-  return 0;
-}
-
-/* keep in sync with enum html_command_text_type */
-static char *html_command_text_type_name[] = {
-  "text", "text_nonumber", "string", "string_nonumber"
-};
+  text_init (&result);
 
-char *
-html_command_text (CONVERTER *self, ELEMENT *command,
-                   const enum html_command_text_type type)
-{
-  char *result;
-  HTML_TARGET *target;
-  ELEMENT *tree_root;
-  ELEMENT *manual_content = lookup_extra_element (command,
-                                                  "manual_content");
-  if (manual_content)
+  if (target_split)
     {
-      TREE_ADDED_ELEMENTS *command_tree = html_command_tree (self, command, 0);
-      TREE_ADDED_ELEMENTS *string_tree = 0;
-      if (type == HCTT_string)
-        {
-          ELEMENT *tree_root_string;
+      char *file_name = 0;
+      TARGET_DIRECTORY_FILENAME *target_dir_filename;
 
-          string_tree = new_tree_added_elements ();
+      if ((!strcmp (target, "Top") || !strlen (target))
+          && self->conf->TOP_NODE_FILE_TARGET)
+        {
+          file_name = strdup (self->conf->TOP_NODE_FILE_TARGET);
+        }
+      else
+        {
+          if (extension)
+            xasprintf (&file_name, "%s.%s", target_filebase, extension);
+          else
+            file_name = strdup (target_filebase);
+        }
+      target_dir_filename
+       = call_file_id_setting_external_target_split_name (self,
+          normalized, external_node, target, directory, file_name);
+      if (target_dir_filename)
+        {
+          free (directory);
+          directory = target_dir_filename->directory;
+          free (file_name);
+          file_name = target_dir_filename->filename;
+          free (target);
+          target = target_dir_filename->target;
+          free (target_dir_filename);
+        }
+      text_append (&result, directory);
+      text_append (&result, file_name);
+      if (strlen (target))
+        {
+          text_append_n (&result, "#", 1);
+          text_append (&result, target);
+        }
 
-          tree_root_string = new_element_added (string_tree, ET__string);
+      free (file_name);
+      free (directory);
+    }
+  else
+    {
+      TARGET_FILENAME *target_filename;
 
-          add_to_element_contents (tree_root_string, command_tree->tree);
-          tree_root = tree_root_string;
-          add_to_element_list (&self->tree_to_build, tree_root);
+      if (!strlen (target))
+        {
+          free (target);
+          target = strdup ("Top");
         }
-      else
-        tree_root = command_tree->tree;
 
-      result = convert_tree_new_formatting_context (self, tree_root,
-                                     element_command_name(command), 0,
-                                     "command_text-manual_content", 0);
+      target_filename
+        = call_file_id_setting_external_target_non_split_name (self,
+                           normalized, external_node, target, file);
 
-      if (type == HCTT_string)
-        destroy_tree_added_elements (self, string_tree);
-      return result;
+      if (target_filename)
+        {
+          free (file);
+          file = target_filename->filename;
+          free (target);
+          target = target_filename->target;
+          free (target_filename);
+        }
+      text_append (&result, file);
+      if (strlen (target))
+        {
+          text_append_n (&result, "#", 1);
+          text_append (&result, target);
+        }
+
+      free (file);
     }
 
-  target = get_target (self, command);
-  if (target)
+  free (target);
+  free (target_filebase);
+  return result.text;
+}
+
+FILE_NUMBER_NAME *
+html_command_filename (CONVERTER *self, const ELEMENT *command)
+{
+  HTML_TARGET *target_info;
+
+  target_info = get_target (self, command);
+
+  if (target_info)
     {
-      if (target->command_text[type])
-        return target->command_text[type];
-      else
+      ROOT_AND_UNIT *root_unit;
+
+      if (target_info->filename_set)
+        return &target_info->file_number_name;
+
+   /* this finds a special element for footnote command if such an element
+      exists.  This is best, the special element filename is the footnote
+      filename. */
+
+      root_unit
+        = html_get_tree_root_element (self, command, 1);
+
+      if (root_unit && root_unit->output_unit
+          && root_unit->output_unit->unit_filename)
         {
-          TREE_ADDED_ELEMENTS *string_tree = 0;
-          char *explanation = 0;
-          char *context_name;
-          ELEMENT *selected_tree;
-          TREE_ADDED_ELEMENTS *command_tree
-            = html_command_tree (self, command, 0);
+          size_t file_index
+            = self->output_unit_file_indices[root_unit->output_unit->index];
+          target_info->file_number_name.file_number
+             = file_index +1;
+          target_info->file_number_name.filename
+               = root_unit->output_unit->unit_filename;
+        }
+      target_info->filename_set = 1;
 
-          if (!command_tree->tree)
-            return 0;
+      free (root_unit);
 
-          if (command->cmd)
+      return &target_info->file_number_name;
+    }
+
+  return 0;
+}
+
+ELEMENT *
+command_root_element_command (CONVERTER *self, const ELEMENT *command)
+{
+  HTML_TARGET *target_info;
+
+  target_info = get_target (self, command);
+  if (target_info)
+    {
+      if (!target_info->root_element_command_set)
+        {
+     /* in contrast with command_filename() we find the root element through
+        the location holding the @footnote command.  It is better, as the
+        footnote special element is not associated with a root command,
+        it is better to stay in the document to find a root element. */
+          ROOT_AND_UNIT *root_unit
+           = html_get_tree_root_element (self, command, 0);
+
+          if (root_unit && root_unit->output_unit
+              && root_unit->output_unit->unit_type == OU_unit)
             {
-              char *command_name = element_command_name(command);
-              context_name = command_name;
-              xasprintf (&explanation, "command_text:%s @%s",
-                         html_command_text_type_name[type],
-                         command_name);
+              target_info->root_element_command
+                = root_unit->output_unit->unit_command;
             }
           else
-            {
-              context_name = element_type_names[command->type];
-              if (command->type == ET_special_unit_element)
-                {
-                  char *special_unit_variety
-                    = command->associated_unit->special_unit_variety;
-                  xasprintf (&explanation, "command_text %s",
-                             special_unit_variety);
-                }
-            }
-          html_new_document_context (self, context_name, explanation, 0);
+            target_info->root_element_command = 0;
 
-          if ((type == HCTT_text_nonumber || type == HCTT_string_nonumber)
-              && target->tree_nonumber.tree)
-            selected_tree = target->tree_nonumber.tree;
-          else
-            selected_tree = command_tree->tree;
+          target_info->root_element_command_set = 1;
 
-          if (type == HCTT_string)
-            {
-              ELEMENT *tree_root_string;
+          free (root_unit);
 
-              string_tree = new_tree_added_elements ();
+          return target_info->root_element_command;
+        }
+    }
+  return 0;
+}
 
-              tree_root_string = new_element_added (string_tree, ET__string);
+/* Return string for linking to $COMMAND with <a href> */
+char *html_command_href (CONVERTER *self, const ELEMENT *command,
+                         const char *source_filename,
+                         const ELEMENT *source_command, /* for messages only */
+            const char *specified_target) /* to specify explicitly the target 
*/
+{
+  TEXT href;
+  const char *filename_from;
+  HTML_TARGET *target_info;
+  const char *target = 0;
+  FILE_NUMBER_NAME *target_filename;
+  int target_filename_to_be_freed = 0;
+  ELEMENT *manual_content = lookup_extra_element (command,
+                                                  "manual_content");
+  if (source_filename)
+    filename_from = source_filename;
+  else
+    filename_from = self->current_filename.filename;
 
-              add_to_element_contents (tree_root_string, selected_tree);
-              tree_root = tree_root_string;
-              add_to_element_list (&self->tree_to_build, tree_root);
-            }
-          else
-            tree_root = selected_tree;
+  if (manual_content)
+    {
+      return external_node_href (self, command, filename_from,
+                                 source_command);
+    }
 
-          self->ignore_notice++;
-          add_to_element_list (&self->referred_command_stack, command);
-          self->modified_state |= HMSF_referred_command_stack
-                                   | HMSF_converter_state;
-          target->command_text[type]
-            = html_convert_tree (self, tree_root, explanation);
-          free (explanation);
-          remove_from_element_list (&self->referred_command_stack, -1);
-          self->ignore_notice--;
-          self->modified_state |= HMSF_referred_command_stack
-                                   | HMSF_converter_state;
+  if (specified_target)
+    target = specified_target;
+  else
+    {
+      const ELEMENT *target_command = command;
+      /* for sectioning command prefer the associated node */
+      const ELEMENT *associated_node = lookup_extra_element (command,
+                                                       "associated_node");
+      if (associated_node)
+        target_command = associated_node;
+      target_info = get_target (self, target_command);
+      if (target_info)
+        target = target_info->target;
+    }
+  if (!target)
+    return strdup ("");
 
-          html_pop_document_context (self);
+  text_init (&href);
+  text_append (&href, "");
+
+  target_filename = html_command_filename (self, command);
+  if (!target_filename)
+    {
+   /* Happens if there are no pages, for example if OUTPUT is set to ''
+      as in the test cases.  Also for things in @titlepage when
+      titlepage is not output. */
+      const OUTPUT_UNIT_LIST *output_units
+         = retrieve_output_units (self->document_units_descriptor);
+      if (output_units && output_units->number > 0
+          && output_units->list[0]->unit_filename)
+        { /* In that case use the first page. */
+          target_filename = (FILE_NUMBER_NAME *)
+            malloc (sizeof (FILE_NUMBER_NAME));
+          target_filename->filename = output_units->list[0]->unit_filename;
+          target_filename->file_number
+              = self->output_unit_file_indices[0] +1;
+          target_filename_to_be_freed = 1;
+        }
+    }
 
-          if (type == HCTT_string)
+  if (target_filename && target_filename->filename)
+    {
+      if (!filename_from
+          || strcmp (target_filename->filename, filename_from))
+        {
+          ELEMENT *command_root_element
+             = command_root_element_command (self, command);
+          text_append (&href, target_filename->filename);
+     /* omit target if the command is an element command, there is only
+        one element in file and there is a file in the href */
+          if (filename_from && command_root_element)
             {
-              destroy_tree_added_elements (self, string_tree);
+              ELEMENT *associated_section
+                = lookup_extra_element (command_root_element,
+                                        "associated_section");
+              if (command_root_element == command
+                  || (associated_section
+                      && associated_section == command))
+                {
+                  if (target_filename->file_number > 0)
+                    {
+                      size_t count_in_file
+                       = count_elements_in_filename (self, CEFT_total,
+                                                  
target_filename->file_number);
+                      if (count_in_file == 1)
+                        target = "";
+                    }
+                }
             }
-          return target->command_text[type];
         }
     }
 
- /*
-    Can happen
-    * if USE_NODES is 0 and there are no sectioning commands.
-    * if a special element target was set to undef in user defined code.
-    * for @*ref with missing targets (maybe @novalidate needed in that case).
-    * for @node header if the node consist only in spaces (example in 
sectioning
-      in_menu_only_special_ascii_spaces_node).
-    * for multiple targets with the same name, eg both @node and @anchor
-    * with @inforef with node argument only, without manual argument.
-  */
+  if (strlen (target))
+    {
+      text_append_n (&href, "#", 1);
+      text_append (&href, target);
+    }
 
-  return 0;
+  if (target_filename_to_be_freed)
+    free (target_filename);
+
+  return href.text;
 }
 
-static int
-compare_page_name_number (const void *a, const void *b)
+char *
+html_command_contents_target (CONVERTER *self, const ELEMENT *command,
+                              enum command_id contents_or_shortcontents)
 {
-  const PAGE_NAME_NUMBER *pnn_a = (const PAGE_NAME_NUMBER *) a;
-  const PAGE_NAME_NUMBER *pnn_b = (const PAGE_NAME_NUMBER *) b;
+  HTML_TARGET *target_info;
 
-  return strcmp (pnn_a->page_name, pnn_b->page_name);
+  if (contents_or_shortcontents == CM_summarycontents)
+    contents_or_shortcontents = CM_shortcontents;
+
+  target_info = get_target (self, command);
+  if (target_info)
+    {
+      if (contents_or_shortcontents == CM_summarycontents)
+        return target_info->shortcontents_target;
+      else if (contents_or_shortcontents == CM_contents)
+        return target_info->contents_target;
+    }
+  return 0;
 }
 
-size_t
-find_page_name_number
-     (const PAGE_NAME_NUMBER_LIST *page_name_number,
-                                          const char *page_name)
+/* Return string for linking to CONTENTS_OR_SHORTCONTENTS associated
+   element from $COMMAND with <a href> */
+char *
+html_command_contents_href (CONVERTER *self, const ELEMENT *command,
+                            enum command_id contents_or_shortcontents,
+                            const char *source_filename)
 {
-  PAGE_NAME_NUMBER *result = 0;
-  static PAGE_NAME_NUMBER searched_page_name;
-  searched_page_name.page_name = page_name;
+  int j;
+  const char *filename_from;
+  char *target = html_command_contents_target (self, command,
+                                               contents_or_shortcontents);
 
-  result = (PAGE_NAME_NUMBER *) bsearch (&searched_page_name,
-                page_name_number->list,
-                page_name_number->number, sizeof(PAGE_NAME_NUMBER),
-                compare_page_name_number);
-  return result->number;
+  if (source_filename)
+    filename_from = source_filename;
+  else
+    filename_from = self->current_filename.filename;
+
+  for (j = 0; self->command_special_variety_name_index[j].cmd; j++)
+    {
+      COMMAND_ID_INDEX cmd_variety_index
+            = self->command_special_variety_name_index[j];
+      if (cmd_variety_index.cmd == contents_or_shortcontents)
+        {
+          TEXT href;
+          FILE_NUMBER_NAME *target_filename = 0;
+          char *special_unit_variety
+            = self->special_unit_varieties.list[cmd_variety_index.index];
+          int special_unit_direction_index
+                = special_unit_variety_direction_index (self,
+                                           special_unit_variety);
+          const OUTPUT_UNIT *special_unit
+            = self->global_units_directions[special_unit_direction_index];
+          if (special_unit)
+            {
+              target_filename = html_command_filename (self,
+                                               special_unit->unit_command);
+            }
+
+          text_init (&href);
+          text_append (&href, "");
+
+          if (target_filename
+              && (!filename_from
+                  || strcmp (target_filename->filename, filename_from)))
+            text_append (&href, target_filename->filename);
+
+          if (target && strlen (target))
+            {
+              text_append_n (&href, "#", 1);
+              text_append (&href, target);
+            }
+
+          return href.text;
+        }
+    }
+  return 0;
 }
 
-static int
-compare_selector_style (const void *a, const void *b)
+int *
+get_shared_conversion_state_integer (CONVERTER *self, char *key,
+                                     int value)
 {
-  const CSS_SELECTOR_STYLE *css_a = (const CSS_SELECTOR_STYLE *) a;
-  const CSS_SELECTOR_STYLE *css_b = (const CSS_SELECTOR_STYLE *) b;
+  KEY_PAIR *k
+    = lookup_associated_info (&self->shared_conversion_state.integers, key);
 
-  return strcmp (css_a->selector, css_b->selector);
+  if (!k)
+    {
+      add_associated_info_integer (&self->shared_conversion_state.integers,
+                                   key, value);
+      k = lookup_associated_info (&self->shared_conversion_state.integers, 
key);
+    }
+  return &k->integer;
 }
 
-CSS_SELECTOR_STYLE *
-find_css_selector_style
-     (const CSS_SELECTOR_STYLE_LIST *css_element_class_styles,
-                                           const char *selector)
+static void
+register_modified_shared_conversion_state_integer (CONVERTER *self,
+                                                   const char *key)
 {
-  CSS_SELECTOR_STYLE *result = 0;
-  static CSS_SELECTOR_STYLE searched_selector;
-  /* remove const with a cast, it is more efficient than duplicating */
-  searched_selector.selector = (char *) selector;
-
-  result = (CSS_SELECTOR_STYLE *) bsearch (&searched_selector,
-                css_element_class_styles->list,
-                css_element_class_styles->number, sizeof(CSS_SELECTOR_STYLE),
-                compare_selector_style);
+  self->modified_state |= HMSF_shared_conversion_state_integer;
+  if (!find_string (&self->shared_conversion_state_integer, key))
+    add_string (key, &self->shared_conversion_state_integer);
+}
 
-  return result;
+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
-collect_css_element_class (CONVERTER *self, const char *selector)
+TREE_ADDED_ELEMENTS *
+html_command_tree (CONVERTER *self, const ELEMENT *command, int no_number)
 {
-  CSS_SELECTOR_STYLE *selector_style
-    = find_css_selector_style (&self->css_element_class_styles, selector);
-  if (selector_style)
+  TREE_ADDED_ELEMENTS *tree;
+  HTML_TARGET *target_info;
+
+  ELEMENT *manual_content = lookup_extra_element (command,
+                                                  "manual_content");
+  if (manual_content)
     {
-      size_t i;
-      size_t css_files_index;
-      CSS_LIST *page_css_list;
-      if (self->document_global_context)
-        {
-          css_files_index = 0;
-        }
-      else
-        {
-          css_files_index = self->current_filename.file_number;
-        }
-      page_css_list = &self->page_css.list[css_files_index];
-      for (i = 0; i < page_css_list->number; i++)
-        {
-          if (!strcmp (page_css_list->list[i], selector))
-            return;
-        }
-      if (page_css_list->number == page_css_list->space)
-        {
-          page_css_list->list
-            = realloc (page_css_list->list,
-                   (page_css_list->space += 5) * sizeof (char *));
-        }
-      page_css_list->list[page_css_list->number] = strdup (selector);
-      page_css_list->number++;
-    }
-}
-
-int
-compare_strings (const void *a, const void *b)
-{
-  const char **str_a = (const char **) a;
-  const char **str_b = (const char **) b;
+      ELEMENT *root_code;
+      ELEMENT *open_p;
+      ELEMENT *close_p;
 
-  return strcmp (*str_a, *str_b);
-}
+      ELEMENT *node_content = lookup_extra_element (command,
+                                                    "node_content");
 
-STRING_LIST *
-html_get_css_elements_classes (CONVERTER *self, const char *filename)
-{
-  int j;
-  size_t page_number;
-  STRING_LIST *result;
-  const char **selectors;
-  size_t selector_nr = 0;
+      tree = new_tree_added_elements ();
 
-  if (self->page_css.number <= 0)
-    return 0;
+      root_code = new_element_added (tree, ET__code);
+      open_p = new_element_added (tree, ET_NONE);
+      close_p = new_element_added (tree, ET_NONE);
 
-  CSS_LIST *global_context_css_list = &self->page_css.list[0];
+      text_append_n (&open_p->text, "(", 1);
+      text_append_n (&close_p->text, ")", 1);
 
-  if (filename)
-    {
-      CSS_LIST *css_list;
-      page_number = find_page_name_number (&self->page_name_number,
-                                           filename);
-      if (!page_number)
-        fatal ("Could not find page number of file name");
+      add_to_element_contents (root_code, open_p);
+      add_to_contents_as_array (root_code, manual_content);
+      add_to_element_contents (root_code, close_p);
+      if (node_content)
+        add_to_contents_as_array (root_code, node_content);
 
-      css_list = &self->page_css.list[page_number];
-      if (css_list->number)
-        {
-          /* +1 for 'span:hover a.copiable-link' */
-          size_t space = css_list->number + global_context_css_list->number +1;
-          selectors = (const char **) malloc (sizeof (char *) * space);
-          memcpy (selectors, css_list->list,
-                  css_list->number * sizeof (char *));
-          selector_nr = css_list->number;
-        }
+      tree->tree = root_code;
+      add_to_element_list (&self->tree_to_build, root_code);
+      return tree;
     }
 
-  if (selector_nr <= 0)
-    {
-      if (global_context_css_list->number)
-        {
-          /* +1 for 'span:hover a.copiable-link' */
-          size_t space = global_context_css_list->number +1;
-          selectors = (const char **) malloc (sizeof (char *) * space);
-          selector_nr = global_context_css_list->number;
-        }
-      else
-        return 0;
-    }
-  else if (global_context_css_list->number)
+  target_info = get_target (self, command);
+  if (target_info)
     {
-      int i;
-      size_t file_selector_nr = selector_nr;
-      /* add global context selectors if not already present */
-      for (i = 0; i < global_context_css_list->number; i++)
+      if (!target_info->tree.status)
         {
-          int j;
-          const char *global_selector = global_context_css_list->list[i];
-          int found = 0;
-          for (j = 0; j < file_selector_nr; j++)
+          tree = &target_info->tree;
+          tree->status = tree_added_status_normal;
+          if (command->type == ET_special_unit_element)
             {
-              if (!strcmp (global_selector, selectors[j]))
-                {
-                  found = 1;
-                  break;
-                }
+              const char *special_unit_variety
+                = command->associated_unit->special_unit_variety;
+              ELEMENT *heading_tree = special_unit_info_tree (self,
+                                   SUIT_type_heading, special_unit_variety);
+              tree->tree = heading_tree;
             }
-          if (!found)
+          else if (command->cmd == CM_node || command->cmd == CM_anchor)
             {
-              selectors[selector_nr] = global_selector;
-              selector_nr++;
+              ELEMENT *root_code = new_element_added (tree, ET__code);
+              add_to_contents_as_array (root_code, command->args.list[0]);
+              tree->tree = root_code;
+              add_to_element_list (&self->tree_to_build, tree->tree);
             }
-        }
-    }
-  for (j = 0; j < selector_nr; j++)
-    {
-      if (!strcmp ("a.copiable-link", selectors[j]))
-         {
-           selectors[selector_nr] = "span:hover a.copiable-link";
-           selector_nr++;
-           break;
-         }
-    }
-
-  qsort (selectors, selector_nr, sizeof (char *), compare_strings);
+          else if (command->cmd == CM_float)
+            {
+              tree->tree = float_type_number (self, command);
+              tree->status = tree_added_status_new_tree;
+              add_to_element_list (&self->tree_to_build, tree->tree);
+            }
+          else if (command->args.number <= 0
+                   || command->args.list[0]->contents.number <= 0)
+            { /* no argument, nothing to do */
+              /* TODO check if possible */
+              tree->status = tree_added_status_no_tree;
+            }
+          else
+            {
+              char *section_number
+                = lookup_extra_string (command, "section_number");
+              if (section_number && !self->conf->NUMBER_SECTIONS == 0)
+                {
+                  NAMED_STRING_ELEMENT_LIST *replaced_substrings
+                    = new_named_string_element_list ();
+                  ELEMENT *e_number = new_element (ET_NONE);
+                  ELEMENT *section_title_copy
+                     = copy_tree (command->args.list[0]);
 
-  result = (STRING_LIST *) malloc (sizeof (STRING_LIST));
-  memset (result, 0, sizeof (STRING_LIST));
-  for (j = 0; j < selector_nr; j++)
-    add_string (selectors[j], result);
+                  add_element_to_named_string_element_list (
+                              replaced_substrings, "section_title",
+                              section_title_copy);
+                  text_append (&e_number->text, section_number);
+                  add_element_to_named_string_element_list (
+                              replaced_substrings, "number", e_number);
 
-  free (selectors);
+                  if (command->cmd == CM_appendix)
+                    {
+                      int status;
+                      int section_level = lookup_extra_integer (command,
+                                               "section_level", &status);
+                      if (section_level == 1)
+                        {
+                          tree->tree
+                            = gdt_tree ("Appendix {number} {section_title}",
+                                        self->document, self->conf,
+                                        replaced_substrings, 0, 0);
+                        }
+                    }
+                  if (!tree->tree)
+                    /* TRANSLATORS: numbered section title */
+                    tree->tree = gdt_tree ("{number} {section_title}",
+                                            self->document, self->conf,
+                                            replaced_substrings, 0, 0);
 
-  return result;
-}
+                  destroy_named_string_element_list (replaced_substrings);
+                  tree->status = tree_added_status_new_tree;
+                  add_to_element_list (&self->tree_to_build, tree->tree);
+                }
+              else
+                {
+                  tree->status = tree_added_status_reused_tree;
+                  tree->tree = command->args.list[0];
+                }
 
-static char *
-protect_class_name (const char *class_name)
-{
-  TEXT result;
-  TEXT space_protected;
-  text_init (&result);
-  text_init (&space_protected);
-  const char *p = class_name;
-  while (*p)
-    {
-      int n = strcspn (p, " ");
-      if (n)
-        {
-          text_append_n (&space_protected, p, n);
-          p += n;
-        }
-      if (*p)
-        {
-          int n = strspn (p, " ");
-          if (n)
-            {
-              int i;
-              for (i = 0; i < n; i++)
-                text_append_n (&space_protected, "-", 1);
-              p += n;
+              target_info->tree_nonumber.tree = command->args.list[0];
+              target_info->tree_nonumber.status = 
tree_added_status_reused_tree;
             }
         }
+
+      if (no_number && target_info->tree_nonumber.tree)
+        return &target_info->tree_nonumber;
+      else
+        return &target_info->tree;
     }
 
-  /* do not use the customization API as in perl */
-  html_default_format_protect_text (space_protected.text, &result);
-  free (space_protected.text);
-  return result.text;
+  return 0;
 }
 
+/* keep in sync with enum html_command_text_type */
+static char *html_command_text_type_name[] = {
+  "text", "text_nonumber", "string", "string_nonumber"
+};
+
+/* cached, not to be freed */
 char *
-html_attribute_class (CONVERTER *self, const char *element,
-                      const STRING_LIST *classes)
+html_command_text (CONVERTER *self, const ELEMENT *command,
+                   const enum html_command_text_type type)
 {
-  TEXT result;
-  char *style = 0;
-  int i;
-  int class_nr = 0;
-  if (!classes  || classes->number <= 0
-      || self->conf->NO_CSS > 0)
+  char *result;
+  HTML_TARGET *target_info;
+  ELEMENT *tree_root;
+  ELEMENT *manual_content = lookup_extra_element (command,
+                                                  "manual_content");
+  if (manual_content)
     {
-      if (!strcmp (element, "span"))
-        return strdup ("");
-      else
+      TREE_ADDED_ELEMENTS *command_tree = html_command_tree (self, command, 0);
+      TREE_ADDED_ELEMENTS *string_tree = 0;
+      if (type == HCTT_string)
         {
-          char *result;
-          xasprintf (&result, "<%s", element);
-          return result;
-        }
-    }
+          ELEMENT *tree_root_string;
 
-  if (self->conf->INLINE_CSS_STYLE > 0)
-    {
-      int i;
-      TEXT inline_styles;
-      text_init (&inline_styles);
-      int style_nr = 0;
-      for (i = 0; i < classes->number; i++)
-        {
-          const char *style_class = classes->list[i];
-          char *selector;
-          CSS_SELECTOR_STYLE *selector_style;
+          string_tree = new_tree_added_elements ();
 
-          xasprintf (&selector, "%s.%s", element, style_class);
-          selector_style
-            = find_css_selector_style (&self->css_element_class_styles,
-                                       selector);
-          free (selector);
-          if (selector_style)
-            {
-              if (style_nr)
-                 text_printf (&inline_styles, ";%s", selector_style->style);
-              else
-                 text_append (&inline_styles, selector_style->style);
-              style_nr++;
-            }
-        }
-      if (inline_styles.end)
-        {
-          xasprintf (&style, " style=\"%s\"", inline_styles.text);
-        }
-      free (inline_styles.text);
-    }
-  else
-    {
-      int i;
-      for (i = 0; i < classes->number; i++)
-        {
-          const char *style_class = classes->list[i];
-          char *selector;
+          tree_root_string = new_element_added (string_tree, ET__string);
 
-          xasprintf (&selector, "%s.%s", element, style_class);
-          collect_css_element_class (self, selector);
-          free (selector);
+          add_to_element_contents (tree_root_string, command_tree->tree);
+          tree_root = tree_root_string;
+          add_to_element_list (&self->tree_to_build, tree_root);
         }
-    }
-  text_init (&result);
-  text_printf (&result, "<%s class=\"", element);
-  for (i = 0; i < classes->number; i++)
-    {
-      const char *class_name = classes->list[i];
-      char *protected_class = protect_class_name (class_name);
-      if (class_nr)
-        text_printf (&result, " %s", protected_class);
       else
-        text_append (&result, protected_class);
-      free (protected_class);
-      class_nr++;
-    }
-  text_append_n (&result, "\"", 1);
-  if (style)
-    {
-      text_append (&result, style);
-      free (style);
-    }
-  return result.text;
-}
-
-void
-html_merge_index_entries (CONVERTER *self)
-{
-  if (self->document->index_names)
-    {
-      INDEX **index_names = self->document->index_names;
-      MERGED_INDEX *merged_index_entries = merge_indices (index_names);
-      self->index_entries = merged_index_entries;
-    }
-}
+        tree_root = command_tree->tree;
 
-int
-compare_index_name (const void *a, const void *b)
-{
-  const INDEX **idx_a = (const INDEX **) a;
-  const INDEX **idx_b = (const INDEX **) b;
+      result = convert_tree_new_formatting_context (self, tree_root,
+                                     element_command_name(command),
+                                     "command_text-manual_content", 0, 0);
 
-  return strcmp ((*idx_a)->name, (*idx_b)->name);
-}
+      if (type == HCTT_string)
+        destroy_tree_added_elements (self, string_tree);
+      return result;
+    }
 
-void
-prepare_index_entries_targets (CONVERTER *self)
-{
-  if (self->document->index_names)
+  target_info = get_target (self, command);
+  if (target_info)
     {
-      INDEX **i, *idx;
-      INDEX **index_names = self->document->index_names;
-      INDEX **sorted_index_names;
-      int index_nr = 0;
-
-      /* TODO sort indices by name before? when registering in document?
-         In parser?
-         Depending on size?  And use bsearch in some places?
-       */
-      for (i = index_names; (idx = *i); i++)
-        index_nr++;
-
-      sorted_index_names = (INDEX **) malloc ((index_nr+1) * sizeof (INDEX *));
+      if (target_info->command_text[type])
+        return strdup (target_info->command_text[type]);
+      else
+        {
+          TREE_ADDED_ELEMENTS *string_tree = 0;
+          char *explanation = 0;
+          char *context_name;
+          ELEMENT *selected_tree;
+          TREE_ADDED_ELEMENTS *command_tree
+            = html_command_tree (self, command, 0);
 
-      memcpy (sorted_index_names, index_names, (index_nr+1) * sizeof (INDEX 
*));
-      qsort (sorted_index_names, index_nr, sizeof (INDEX *),
-             compare_index_name);
+          if (!command_tree->tree)
+            return 0;
 
-      for (i = sorted_index_names; (idx = *i); i++)
-        {
-          if (idx->entries_number > 0)
+          if (command->cmd)
             {
-              int j;
-              for (j = 0; j < idx->entries_number; j++)
+              char *command_name = element_command_name(command);
+              context_name = command_name;
+              xasprintf (&explanation, "command_text:%s @%s",
+                         html_command_text_type_name[type],
+                         command_name);
+            }
+          else
+            {
+              context_name = element_type_names[command->type];
+              if (command->type == ET_special_unit_element)
                 {
-                  INDEX_ENTRY *index_entry;
-                  const ELEMENT *main_entry_element;
-                  const ELEMENT *seeentry;
-                  const ELEMENT *seealso;
-                  ELEMENT *entry_reference_content_element;
-                  ELEMENT *normalize_index_element;
-                  ELEMENT_LIST *subentries_tree;
-                  const ELEMENT *target_element;
-                  TEXT target_base;
-                  char *normalized_index;
-                  char *region = 0;
-                  char *target;
+                  char *special_unit_variety
+                    = command->associated_unit->special_unit_variety;
+                  xasprintf (&explanation, "command_text %s",
+                             special_unit_variety);
+                }
+            }
+          html_new_document_context (self, context_name, explanation, 0);
 
-                  index_entry = &idx->index_entries[j];
-                  main_entry_element = index_entry->entry_element;
-                  seeentry = lookup_extra_element (main_entry_element,
-                                                   "seeentry");
-                  if (seeentry)
-                    continue;
-                  seealso = lookup_extra_element (main_entry_element,
-                                                  "seealso");
-                  if (seealso)
-                    continue;
+          if ((type == HCTT_text_nonumber || type == HCTT_string_nonumber)
+              && target_info->tree_nonumber.tree)
+            selected_tree = target_info->tree_nonumber.tree;
+          else
+            selected_tree = command_tree->tree;
 
-                  region = lookup_extra_string (main_entry_element,
-                                                "element_region");
-                  entry_reference_content_element
-                   = index_content_element (main_entry_element, 1);
-        /* construct element to convert to a normalized identifier to use as
-           hrefs target */
-                  normalize_index_element = new_element (ET_NONE);
-                  add_to_element_contents (normalize_index_element,
-                                           entry_reference_content_element);
+          if (type == HCTT_string)
+            {
+              ELEMENT *tree_root_string;
 
-                  subentries_tree
-                   = comma_index_subentries_tree (main_entry_element, " ");
-                  if (subentries_tree)
-                    {
-                      insert_list_slice_into_contents (normalize_index_element,
-                                       
normalize_index_element->contents.number,
-                                       subentries_tree, 0,
-                                       subentries_tree->number);
-                    }
-                  normalized_index
-                    = normalize_transliterate_texinfo (normalize_index_element,
-                                                       (self->conf->TEST > 0));
+              string_tree = new_tree_added_elements ();
 
-                  destroy_element (normalize_index_element);
-                  if (subentries_tree)
-                    free_comma_index_subentries_tree (subentries_tree);
+              tree_root_string = new_element_added (string_tree, ET__string);
 
-                  text_init (&target_base);
-                  text_append (&target_base, "index-");
-                  if (region)
-                    {
-                      text_append (&target_base, region);
-                      text_append (&target_base, "-");
-                    }
-                  text_append (&target_base, normalized_index);
-                  free (normalized_index);
-                  target = unique_target (self, target_base.text);
-                  free (target_base.text);
-                  if (index_entry->entry_associated_element)
-                    target_element = index_entry->entry_associated_element;
-                  else
-                    target_element = main_entry_element;
+              add_to_element_contents (tree_root_string, selected_tree);
+              tree_root = tree_root_string;
+              add_to_element_list (&self->tree_to_build, tree_root);
+            }
+          else
+            tree_root = selected_tree;
 
-                  add_element_target (self, target_element, target);
-                  add_string (target, &self->seen_ids);
+          self->ignore_notice++;
+          push_stack_element (&self->referred_command_stack, command);
+          self->modified_state |= HMSF_referred_command_stack
+                                   | HMSF_converter_state;
+          target_info->command_text[type]
+            = html_convert_tree (self, tree_root, explanation);
+          free (explanation);
+          pop_stack_element (&self->referred_command_stack);
+          self->ignore_notice--;
+          self->modified_state |= HMSF_referred_command_stack
+                                   | HMSF_converter_state;
 
-                  free (target);
-                }
+          html_pop_document_context (self);
+
+          if (type == HCTT_string)
+            {
+              destroy_tree_added_elements (self, string_tree);
             }
+          return strdup (target_info->command_text[type]);
         }
-      free (sorted_index_names);
     }
-}
 
-static const char *footid_base = "FOOT";
-static const char *docid_base = "DOCF";
+ /*
+    Can happen
+    * if USE_NODES is 0 and there are no sectioning commands.
+    * if a special element target was set to undef in user defined code.
+    * for @*ref with missing targets (maybe @novalidate needed in that case).
+    * for @node header if the node consist only in spaces (example in 
sectioning
+      in_menu_only_special_ascii_spaces_node).
+    * for multiple targets with the same name, eg both @node and @anchor
+    * with @inforef with node argument only, without manual argument.
+  */
 
-static void
-prepare_footnotes_targets (CONVERTER *self)
-{
-  const ELEMENT_LIST *global_footnotes = 
&self->document->global_commands->footnotes;
-  if (global_footnotes->number > 0)
-    {
-      int i;
-      for (i = 0; i < global_footnotes->number; i++)
-        {
-          const ELEMENT *footnote = global_footnotes->list[i];
-          TEXT footid;
-          TEXT docid;
-          int nr = i+1;
+  return 0;
+}
 
-          text_init (&footid);
-          text_init (&docid);
-          text_printf (&footid, "%s%d", footid_base, nr);
-          text_printf (&docid, "%s%d", docid_base, nr);
+static int
+compare_page_name_number (const void *a, const void *b)
+{
+  const PAGE_NAME_NUMBER *pnn_a = (const PAGE_NAME_NUMBER *) a;
+  const PAGE_NAME_NUMBER *pnn_b = (const PAGE_NAME_NUMBER *) b;
 
-          while (1)
-            {
-              int j;
-              int non_unique = 0;
-              for (j = 0; j < self->seen_ids.number; j++)
-                {
-                  if (!strcmp (footid.text, self->seen_ids.list[j])
-                      || !strcmp (docid.text, self->seen_ids.list[j]))
-                    {
-                      non_unique = 1;
-                      break;
-                    }
-                }
-              if (non_unique)
-                {
-                  nr++;
-                  if (nr == 0)
-                    fatal ("overflow footnote target nr");
+  return strcmp (pnn_a->page_name, pnn_b->page_name);
+}
 
-                  text_init (&footid);
-                  text_init (&docid);
-                  text_printf (&footid, "%s%d", footid_base, nr);
-                  text_printf (&docid, "%s%d", docid_base, nr);
-                }
-              else
-                break;
-            }
-          add_string (footid.text, &self->seen_ids);
-          add_string (docid.text, &self->seen_ids);
-          add_element_target (self, footnote, footid.text);
-          add_special_target (self, ST_footnote_location, footnote,
-                              docid.text);
+size_t
+find_page_name_number
+     (const PAGE_NAME_NUMBER_LIST *page_name_number,
+                                          const char *page_name)
+{
+  PAGE_NAME_NUMBER *result = 0;
+  static PAGE_NAME_NUMBER searched_page_name;
+  searched_page_name.page_name = page_name;
 
-          if (self->conf->DEBUG > 0)
-            {
-              char *footnote_txi = convert_to_texinfo (footnote);
-              fprintf (stderr, "Enter footnote: target %s, nr %d\n%s\n",
-                       footid.text, nr, footnote_txi);
-              free (footnote_txi);
-            }
-          free (footid.text);
-          free (docid.text);
-        }
-    }
+  result = (PAGE_NAME_NUMBER *) bsearch (&searched_page_name,
+                page_name_number->list,
+                page_name_number->number, sizeof(PAGE_NAME_NUMBER),
+                compare_page_name_number);
+  return result->number;
 }
 
-/* for conversion units except for associated special units that require
-   files for document units to be set */
-void
-html_prepare_conversion_units_targets (CONVERTER *self,
-                                       const char *document_name,
-                                       int output_units_descriptor,
-                                       int special_units_descriptor,
-                                       int associated_special_units_descriptor)
+static int
+compare_selector_style (const void *a, const void *b)
 {
-  /*
-   Do that before the other elements, to be sure that special page ids
-   are registered before elements id are.
-   */
-  set_special_units_targets_files (self, special_units_descriptor,
-                                   document_name);
+  const CSS_SELECTOR_STYLE *css_a = (const CSS_SELECTOR_STYLE *) a;
+  const CSS_SELECTOR_STYLE *css_b = (const CSS_SELECTOR_STYLE *) b;
 
-  prepare_associated_special_units_targets (self,
-                                  associated_special_units_descriptor);
+  return strcmp (css_a->selector, css_b->selector);
+}
 
-  set_root_commands_targets_node_files (self);
+CSS_SELECTOR_STYLE *
+find_css_selector_style
+     (const CSS_SELECTOR_STYLE_LIST *css_element_class_styles,
+                                           const char *selector)
+{
+  CSS_SELECTOR_STYLE *result = 0;
+  static CSS_SELECTOR_STYLE searched_selector;
+  /* remove const with a cast, it is more efficient than duplicating */
+  searched_selector.selector = (char *) selector;
 
-  prepare_index_entries_targets (self);
-  prepare_footnotes_targets (self);
+  result = (CSS_SELECTOR_STYLE *) bsearch (&searched_selector,
+                css_element_class_styles->list,
+                css_element_class_styles->number, sizeof(CSS_SELECTOR_STYLE),
+                compare_selector_style);
+
+  return result;
 }
 
-/* Associate output units to the global targets, First, Last, Top, Index.
-   and special output units */
-void
-html_prepare_output_units_global_targets (CONVERTER *self,
-                                        int output_units_descriptor,
-                                        int special_units_descriptor,
-                                        int 
associated_special_units_descriptor)
+static void
+collect_css_element_class (CONVERTER *self, const char *selector)
 {
-  int i;
-  int all_special_units_nr = 0;
-  int s;
-  const OUTPUT_UNIT_LIST *output_units
-    = retrieve_output_units (output_units_descriptor);
+  CSS_SELECTOR_STYLE *selector_style
+    = find_css_selector_style (&self->css_element_class_styles, selector);
+  if (selector_style)
+    {
+      size_t i;
+      size_t css_files_index;
+      CSS_LIST *page_css_list;
+      if (self->document_global_context)
+        {
+          css_files_index = 0;
+        }
+      else
+        {
+          css_files_index = self->current_filename.file_number;
+        }
+      page_css_list = &self->page_css.list[css_files_index];
+      for (i = 0; i < page_css_list->number; i++)
+        {
+          if (!strcmp (page_css_list->list[i], selector))
+            return;
+        }
+      if (page_css_list->number == page_css_list->space)
+        {
+          page_css_list->list
+            = realloc (page_css_list->list,
+                   (page_css_list->space += 5) * sizeof (char *));
+        }
+      page_css_list->list[page_css_list->number] = strdup (selector);
+      page_css_list->number++;
+    }
+}
 
-  const OUTPUT_UNIT *top_output_unit = get_top_unit (self->document,
-                                                     output_units);
+int
+compare_strings (const void *a, const void *b)
+{
+  const char **str_a = (const char **) a;
+  const char **str_b = (const char **) b;
 
-  int special_output_units_lists[2] = {special_units_descriptor,
-                                       associated_special_units_descriptor};
+  return strcmp (*str_a, *str_b);
+}
 
-  self->global_units_directions[D_First] = output_units->list[0];
-  self->global_units_directions[D_Last]
-    = output_units->list[output_units->number - 1];
+STRING_LIST *
+html_get_css_elements_classes (CONVERTER *self, const char *filename)
+{
+  int j;
+  size_t page_number;
+  STRING_LIST *result;
+  const char **selectors;
+  size_t selector_nr = 0;
 
-  self->global_units_directions[D_Top] = top_output_unit;
+  if (self->page_css.number <= 0)
+    return 0;
 
-  /* It is always the first printindex, even if it is not output (for example
-     it is in @copying and @titlepage, which are certainly wrong constructs).
-   */
-  if (self->document->global_commands->printindex.number > 0)
+  CSS_LIST *global_context_css_list = &self->page_css.list[0];
+
+  if (filename)
     {
-      const ELEMENT *printindex
-        = self->document->global_commands->printindex.list[0];
-      ROOT_AND_UNIT *root_unit
-        = html_get_tree_root_element (self, printindex, 0);
-      if (root_unit->output_unit)
-        {
-          const OUTPUT_UNIT *document_unit = root_unit->output_unit;
-          const ELEMENT *root_command = root_unit->root;
-          if (root_command && root_command->cmd == CM_node)
-            {
-              const ELEMENT *associated_section
-                = lookup_extra_element (root_command, "associated_section");
-              if (associated_section)
-                root_command = associated_section;
-            }
-       /* find the first level 1 sectioning element to associate the printindex
-           with */
-          if (root_command && root_command->cmd != CM_node)
-            {
-              while (1)
-                {
-                  int status;
-                  int section_level
-                    = lookup_extra_integer (root_command, "section_level",
-                                                               &status);
-                  if (!status && section_level <= 1)
-                    break;
+      CSS_LIST *css_list;
+      page_number = find_page_name_number (&self->page_name_number,
+                                           filename);
+      if (!page_number)
+        fatal ("Could not find page number of file name");
 
-                  const ELEMENT *up_section_directions
-                    = lookup_extra_element (root_command, 
"section_directions");
-                  if (up_section_directions
-                      && up_section_directions->contents.list[D_up]
-                      && up_section_directions->contents.list[D_up]
-                                     ->associated_unit)
-                    {
-                      root_command = 
up_section_directions->contents.list[D_up];
-                      document_unit = root_command->associated_unit;
-                    }
-                  else
-                    break;
-                }
-            }
-          self->global_units_directions[D_Index] = document_unit;
+      css_list = &self->page_css.list[page_number];
+      if (css_list->number)
+        {
+          /* +1 for 'span:hover a.copiable-link' */
+          size_t space = css_list->number + global_context_css_list->number +1;
+          selectors = (const char **) malloc (sizeof (char *) * space);
+          memcpy (selectors, css_list->list,
+                  css_list->number * sizeof (char *));
+          selector_nr = css_list->number;
         }
-      free (root_unit);
     }
 
-  if (self->conf->DEBUG > 0)
+  if (selector_nr <= 0)
     {
-      int i;
-      fprintf (stderr, "GLOBAL DIRECTIONS:\n");
-      for (i = 0; i < D_Last+1; i++)
+      if (global_context_css_list->number)
         {
-          if (self->global_units_directions[i])
-            {
-              const OUTPUT_UNIT *global_unit = 
self->global_units_directions[i];
-              char *unit_texi = output_unit_texi (global_unit);
-              fprintf (stderr, " %s: %s\n", 
html_global_unit_direction_names[i],
-                                            unit_texi);
-              free (unit_texi);
-            }
+          /* +1 for 'span:hover a.copiable-link' */
+          size_t space = global_context_css_list->number +1;
+          selectors = (const char **) malloc (sizeof (char *) * space);
+          selector_nr = global_context_css_list->number;
         }
-      fprintf (stderr, "\n");
-    }
-
-  /* determine total number of special output units and fill
-     special_units_directions_name_unit.  Used to simplify building perl
-     directions */
-  for (i = 0; i < 2; i++)
-    {
-      int special_units_descriptor = special_output_units_lists[i];
-      const OUTPUT_UNIT_LIST *units_list
-       = retrieve_output_units (special_units_descriptor);
-      if (units_list && units_list->number)
-        all_special_units_nr += units_list->number;
+      else
+        return 0;
     }
-
-  self->special_units_direction_name = (SPECIAL_UNIT_DIRECTION *)
-   malloc (sizeof (SPECIAL_UNIT_DIRECTION) * (all_special_units_nr+1));
-  memset (self->special_units_direction_name, 0,
-          sizeof (SPECIAL_UNIT_DIRECTION) * (all_special_units_nr+1));
-
-  s = 0;
-  for (i = 0; i < 2; i++)
+  else if (global_context_css_list->number)
     {
-      int special_units_descriptor = special_output_units_lists[i];
-      OUTPUT_UNIT_LIST *units_list
-       = retrieve_output_units (special_units_descriptor);
-      if (units_list && units_list->number)
+      int i;
+      size_t file_selector_nr = selector_nr;
+      /* add global context selectors if not already present */
+      for (i = 0; i < global_context_css_list->number; i++)
         {
           int j;
-          for (j = 0; j < units_list->number; j++)
+          const char *global_selector = global_context_css_list->list[i];
+          int found = 0;
+          for (j = 0; j < file_selector_nr; j++)
             {
-              const OUTPUT_UNIT *special_unit = units_list->list[j];
-              char *special_unit_variety = special_unit->special_unit_variety;
-              int special_unit_direction_index
-                = special_unit_variety_direction_index (self,
-                                                special_unit_variety);
-              self->global_units_directions[special_unit_direction_index]
-                = special_unit;
-
-              self->special_units_direction_name[s].output_unit = special_unit;
-              self->special_units_direction_name[s].direction
-                = special_unit_info (self, SUI_type_direction,
-                                           special_unit_variety);
-              s++;
+              if (!strcmp (global_selector, selectors[j]))
+                {
+                  found = 1;
+                  break;
+                }
+            }
+          if (!found)
+            {
+              selectors[selector_nr] = global_selector;
+              selector_nr++;
             }
         }
     }
-}
-
-static void
-set_file_source_info (FILE_SOURCE_INFO *file_source_info,
-                          char *file_info_type, char *file_info_name,
-                          const ELEMENT *file_info_element, char *filepath)
-{
-  file_source_info->type = file_info_type;
-  file_source_info->name = file_info_name;
-  file_source_info->element = file_info_element;
-  file_source_info->path = filepath;
-}
-
-static FILE_SOURCE_INFO *
-add_to_files_source_info (FILE_SOURCE_INFO_LIST *files_source_info,
-                          char *filename,
-                          char *file_info_type, char *file_info_name,
-                          const ELEMENT *file_info_element, char *filepath)
-{
-  FILE_SOURCE_INFO *new_file_source_info;
-  if (files_source_info->number == files_source_info->space)
+  for (j = 0; j < selector_nr; j++)
     {
-      files_source_info->list = realloc (files_source_info->list,
-               (files_source_info->space += 5) * sizeof (FILE_SOURCE_INFO));
-      if (!files_source_info->list)
-       fatal ("realloc failed");
+      if (!strcmp ("a.copiable-link", selectors[j]))
+         {
+           selectors[selector_nr] = "span:hover a.copiable-link";
+           selector_nr++;
+           break;
+         }
     }
 
-  new_file_source_info =
-   &files_source_info->list[files_source_info->number];
+  qsort (selectors, selector_nr, sizeof (char *), compare_strings);
 
-  new_file_source_info->filename = strdup (filename);
-  set_file_source_info (new_file_source_info, file_info_type,
-                        file_info_name, file_info_element, filepath);
+  result = (STRING_LIST *) malloc (sizeof (STRING_LIST));
+  memset (result, 0, sizeof (STRING_LIST));
+  for (j = 0; j < selector_nr; j++)
+    add_string (selectors[j], result);
 
-  files_source_info->number++;
+  free (selectors);
 
-  return new_file_source_info;
+  return result;
 }
 
-static FILE_SOURCE_INFO *
-find_file_source_info (FILE_SOURCE_INFO_LIST *files_source_info,
-                            char *filename)
+static char *
+protect_class_name (const char *class_name)
 {
-  int i;
-  for (i = 0; i < files_source_info->number; i++)
+  TEXT result;
+  TEXT space_protected;
+  text_init (&result);
+  text_init (&space_protected);
+  const char *p = class_name;
+  while (*p)
     {
-      FILE_SOURCE_INFO *file_source_info = &files_source_info->list[i];
-      if (!strcmp (file_source_info->filename, filename))
-        return file_source_info;
+      int n = strcspn (p, " ");
+      if (n)
+        {
+          text_append_n (&space_protected, p, n);
+          p += n;
+        }
+      if (*p)
+        {
+          int n = strspn (p, " ");
+          if (n)
+            {
+              int i;
+              for (i = 0; i < n; i++)
+                text_append_n (&space_protected, "-", 1);
+              p += n;
+            }
+        }
     }
-  return 0;
+
+  /* do not use the customization API as in perl */
+  html_default_format_protect_text (space_protected.text, &result);
+  free (space_protected.text);
+  return result.text;
 }
 
-void
-html_destroy_files_source_info (FILE_SOURCE_INFO_LIST *files_source_info)
+char *
+html_attribute_class (CONVERTER *self, const char *element,
+                      const STRING_LIST *classes)
 {
+  TEXT result;
+  char *style = 0;
   int i;
-  for (i = 0; i < files_source_info->number; i++)
+  int class_nr = 0;
+  if (!classes  || classes->number <= 0
+      || self->conf->NO_CSS > 0)
     {
-      free (files_source_info->list[i].filename);
+      if (!strcmp (element, "span"))
+        return strdup ("");
+      else
+        {
+          char *result;
+          xasprintf (&result, "<%s", element);
+          return result;
+        }
     }
-  free (files_source_info->list);
-  free (files_source_info);
-}
-
-static char *
-add_to_unit_file_name_paths (char **unit_file_name_paths,
-                             char *filename, OUTPUT_UNIT *output_unit)
-{
-  char **new_output_unit_file_name
-                  = &unit_file_name_paths[output_unit->index];
-  *new_output_unit_file_name = strdup (filename);
-
-  return *new_output_unit_file_name;
-}
-
-static FILE_SOURCE_INFO_LIST *
-html_set_pages_files (CONVERTER *self, OUTPUT_UNIT_LIST *output_units,
-                      OUTPUT_UNIT_LIST *special_units,
-                      OUTPUT_UNIT_LIST *associated_special_units,
-                      char *output_file,
-                      char *destination_directory, char *output_filename,
-                      char *document_name)
-{
-  FILE_SOURCE_INFO_LIST *files_source_info = 0;
-  char **unit_file_name_paths;
-  int i;
-
-  if (!output_units || !output_units->number)
-    return 0;
-
-  initialize_output_units_files (self);
-
-  files_source_info = (FILE_SOURCE_INFO_LIST *)
-    malloc (sizeof (FILE_SOURCE_INFO_LIST));
-  memset (files_source_info, 0, sizeof (FILE_SOURCE_INFO_LIST));
-
-  unit_file_name_paths = (char **)
-   malloc (output_units->number * sizeof (char *));
-  memset (unit_file_name_paths, 0,
-          output_units->number * sizeof (char *));
 
-  if (!self->conf->SPLIT || !strlen (self->conf->SPLIT))
+  if (self->conf->INLINE_CSS_STYLE > 0)
     {
       int i;
-      add_to_files_source_info (files_source_info, output_filename,
-                                "special_file", "non_split", 0,
-                                output_file);
-      for (i = 0; i < output_units->number; i++)
+      TEXT inline_styles;
+      text_init (&inline_styles);
+      int style_nr = 0;
+      for (i = 0; i < classes->number; i++)
         {
-          add_to_unit_file_name_paths (unit_file_name_paths,
-                                       output_filename,
-                                       output_units->list[i]);
+          const char *style_class = classes->list[i];
+          char *selector;
+          CSS_SELECTOR_STYLE *selector_style;
+
+          xasprintf (&selector, "%s.%s", element, style_class);
+          selector_style
+            = find_css_selector_style (&self->css_element_class_styles,
+                                       selector);
+          free (selector);
+          if (selector_style)
+            {
+              if (style_nr)
+                 text_printf (&inline_styles, ";%s", selector_style->style);
+              else
+                 text_append (&inline_styles, selector_style->style);
+              style_nr++;
+            }
+        }
+      if (inline_styles.end)
+        {
+          xasprintf (&style, " style=\"%s\"", inline_styles.text);
         }
+      free (inline_styles.text);
     }
   else
     {
-      char *top_node_filename_str;
-      OUTPUT_UNIT *node_top_output_unit = 0;
-      char *extension = 0;
-      const ELEMENT *node_top = 0;
-      int file_nr = 0;
       int i;
+      for (i = 0; i < classes->number; i++)
+        {
+          const char *style_class = classes->list[i];
+          char *selector;
 
-      /* first determine the top node file name. */
-      if (self->document->identifiers_target)
-        node_top = find_identifier_target (self->document->identifiers_target,
-                                           "Top");
+          xasprintf (&selector, "%s.%s", element, style_class);
+          collect_css_element_class (self, selector);
+          free (selector);
+        }
+    }
+  text_init (&result);
+  text_printf (&result, "<%s class=\"", element);
+  for (i = 0; i < classes->number; i++)
+    {
+      const char *class_name = classes->list[i];
+      char *protected_class = protect_class_name (class_name);
+      if (class_nr)
+        text_printf (&result, " %s", protected_class);
+      else
+        text_append (&result, protected_class);
+      free (protected_class);
+      class_nr++;
+    }
+  text_append_n (&result, "\"", 1);
+  if (style)
+    {
+      text_append (&result, style);
+      free (style);
+    }
+  return result.text;
+}
 
-      top_node_filename_str = top_node_filename (self, document_name);
+void
+html_merge_index_entries (CONVERTER *self)
+{
+  if (self->document->index_names)
+    {
+      INDEX **index_names = self->document->index_names;
+      MERGED_INDEX *merged_index_entries = merge_indices (index_names);
+      self->index_entries = merged_index_entries;
+    }
+}
 
-      if (node_top && top_node_filename_str)
-        {
-          int i;
-          node_top_output_unit = node_top->associated_unit;
-          for (i = 0; i < output_units->number; i++)
-            if (output_units->list[i] == node_top_output_unit)
-              break;
-          add_to_files_source_info (files_source_info, top_node_filename_str,
-                                    "special_file", "Top", 0, 0);
-          add_to_unit_file_name_paths (unit_file_name_paths,
-                                       top_node_filename_str,
-                                       node_top_output_unit);
-        }
+int
+compare_index_name (const void *a, const void *b)
+{
+  const INDEX **idx_a = (const INDEX **) a;
+  const INDEX **idx_b = (const INDEX **) b;
 
-      if (self->conf->EXTENSION && strlen (self->conf->EXTENSION))
-        extension = self->conf->EXTENSION;
+  return strcmp ((*idx_a)->name, (*idx_b)->name);
+}
 
-      for (i = 0; i < output_units->number; i++)
-        {
-          OUTPUT_UNIT *output_unit = output_units->list[i];
-          OUTPUT_UNIT *file_output_unit;
-          char *output_unit_file_name;
-          /* For Top node. */
-          if (node_top_output_unit && node_top_output_unit == output_unit)
-            continue;
+void
+prepare_index_entries_targets (CONVERTER *self)
+{
+  if (self->document->index_names)
+    {
+      INDEX **i, *idx;
+      INDEX **index_names = self->document->index_names;
+      INDEX **sorted_index_names;
+      int index_nr = 0;
 
-          file_output_unit = output_unit->first_in_page;
-          output_unit_file_name
-           = unit_file_name_paths[file_output_unit->index];
-          if (!output_unit_file_name)
+      /* TODO sort indices by name before? when registering in document?
+         In parser?
+         Depending on size?  And use bsearch in some places?
+       */
+      for (i = index_names; (idx = *i); i++)
+        index_nr++;
+
+      sorted_index_names = (INDEX **) malloc ((index_nr+1) * sizeof (INDEX *));
+
+      memcpy (sorted_index_names, index_names, (index_nr+1) * sizeof (INDEX 
*));
+      qsort (sorted_index_names, index_nr, sizeof (INDEX *),
+             compare_index_name);
+
+      for (i = sorted_index_names; (idx = *i); i++)
+        {
+          if (idx->entries_number > 0)
             {
-              char *node_filename = 0;
               int j;
-              for (j = 0; j < file_output_unit->unit_contents.number; j++)
+              for (j = 0; j < idx->entries_number; j++)
                 {
-                  const ELEMENT *root_command
-                     = file_output_unit->unit_contents.list[j];
-                  if (root_command->cmd == CM_node)
+                  INDEX_ENTRY *index_entry;
+                  const ELEMENT *main_entry_element;
+                  const ELEMENT *seeentry;
+                  const ELEMENT *seealso;
+                  ELEMENT *entry_reference_content_element;
+                  ELEMENT *normalize_index_element;
+                  ELEMENT_LIST *subentries_tree;
+                  const ELEMENT *target_element;
+                  TEXT target_base;
+                  char *normalized_index;
+                  char *region = 0;
+                  char *target;
+
+                  index_entry = &idx->index_entries[j];
+                  main_entry_element = index_entry->entry_element;
+                  seeentry = lookup_extra_element (main_entry_element,
+                                                   "seeentry");
+                  if (seeentry)
+                    continue;
+                  seealso = lookup_extra_element (main_entry_element,
+                                                  "seealso");
+                  if (seealso)
+                    continue;
+
+                  region = lookup_extra_string (main_entry_element,
+                                                "element_region");
+                  entry_reference_content_element
+                   = index_content_element (main_entry_element, 1);
+        /* construct element to convert to a normalized identifier to use as
+           hrefs target */
+                  normalize_index_element = new_element (ET_NONE);
+                  add_to_element_contents (normalize_index_element,
+                                           entry_reference_content_element);
+
+                  subentries_tree
+                   = comma_index_subentries_tree (main_entry_element, " ");
+                  if (subentries_tree)
                     {
-                      const ELEMENT *node_target = 0;
-                      char *normalized = lookup_extra_string (root_command,
-                                                              "normalized");
-                      if (normalized)
-                        node_target
-                         = find_identifier_target (
-                                  self->document->identifiers_target,
-                                  normalized);
-                   /* double node are not normalized, they are handled here */
-                      if (!node_target)
-                        {
-                          FILE_SOURCE_INFO *file_source_info = 0;
+                      insert_list_slice_into_contents (normalize_index_element,
+                                       
normalize_index_element->contents.number,
+                                       subentries_tree, 0,
+                                       subentries_tree->number);
+                    }
+                  normalized_index
+                    = normalize_transliterate_texinfo (normalize_index_element,
+                                                       (self->conf->TEST > 0));
 
-                          TEXT file_name_text;
-                          text_init (&file_name_text);
-                          text_append (&file_name_text, "unknown_node");
-                          if (extension)
-                            {
-                              text_append (&file_name_text, ".");
-                              text_append (&file_name_text, extension);
-                            }
-                          file_source_info
-                            = find_file_source_info (files_source_info,
-                                                         file_name_text.text);
-                          if (!file_source_info)
-                            {
-                              file_source_info
-                                = add_to_files_source_info (files_source_info,
-                                                file_name_text.text, "node", 0,
-                                                              root_command, 0);
-                              node_filename = file_source_info->filename;
-                            }
-                          free (file_name_text.text);
-                        }
-                      else
-                        {
-          /* Nodes with {'extra'}->{'is_target'} should always be in
-            'identifiers_target', and thus in targets.  It is a bug otherwise. 
*/
-                          FILE_SOURCE_INFO *file_source_info = 0;
-                          HTML_TARGET *node_target
-                            = find_element_target (&self->html_targets,
-                                                   root_command);
-                          node_filename = node_target->node_filename;
+                  destroy_element (normalize_index_element);
+                  if (subentries_tree)
+                    free_comma_index_subentries_tree (subentries_tree);
 
-                          file_source_info
-                            = find_file_source_info (files_source_info,
-                                                          node_filename);
-                          if (file_source_info)
-                            {
-                              if (!strcmp (file_source_info->type,
-                                           "stand_in_file"))
-                                {/* NOTE we keep the order, as in perl */
-                                  set_file_source_info (file_source_info,
-                                                        "node",
-                                                        0, root_command, 0);
-                                }
-                            }
-                          else
-                            add_to_files_source_info (files_source_info,
-                                                 node_filename, "node", 0,
-                                                 root_command, 0);
-                        }
-                      output_unit_file_name
-                        = add_to_unit_file_name_paths (unit_file_name_paths,
-                                                       node_filename,
-                                                       file_output_unit);
-                      break;
-                    }
-                }
-              if (!node_filename)
-                {
-                  /* use section to do the file name if there is no node */
-                  const ELEMENT *command = file_output_unit->unit_command;
-                  if (command)
-                    {
-                      if (command->cmd == CM_top && !node_top
-                          && top_node_filename_str)
-                        {
-                   /* existing top_node_filename can happen, see
-                      html_tests.t top_file_name_and_node_name_collision */
-                          FILE_SOURCE_INFO *file_source_info
-                            = find_file_source_info (files_source_info,
-                                                  top_node_filename_str);
-                          if (file_source_info)
-                            {/* NOTE we keep the order, as in perl */
-                              set_file_source_info (file_source_info,
-                                                    "special_file", "Top",
-                                                    0, 0);
-                            }
-                          else
-                            {
-                              add_to_files_source_info (files_source_info,
-                                                       top_node_filename_str,
-                                                       "special_file", "Top",
-                                                       0, 0);
-                            }
-                          output_unit_file_name
-                           = add_to_unit_file_name_paths (unit_file_name_paths,
-                                                          
top_node_filename_str,
-                                                           file_output_unit);
-                        }
-                      else
-                        {
-                          HTML_TARGET *section_target
-                            = find_element_target (&self->html_targets,
-                                                   command);
-                          char *section_filename
-                            = section_target->section_filename;
-
-                          FILE_SOURCE_INFO *file_source_info
-                            = find_file_source_info (files_source_info,
-                                                     section_filename);
-                          if (file_source_info)
-                            {
-                              if (!strcmp (file_source_info->type,
-                                           "stand_in_file"))
-                                {/* NOTE we keep the order, as in perl */
-                                  set_file_source_info (file_source_info,
-                                                        "section",
-                                                        0, command, 0);
-                                }
-                            }
-                          else
-                            add_to_files_source_info (files_source_info,
-                                                 section_filename, "section", 
0,
-                                                 command, 0);
-                          output_unit_file_name
-                            = add_to_unit_file_name_paths 
(unit_file_name_paths,
-                                                           section_filename,
-                                                           file_output_unit);
-                        }
-                    }
-                  else
-                    {
-                      /* when everything else has failed */
-                      if (file_nr == 0 && !node_top
-                          && top_node_filename_str)
-                        {
-                          FILE_SOURCE_INFO *file_source_info
-                            = find_file_source_info (files_source_info,
-                                                  top_node_filename_str);
-                          if (!file_source_info)
-                            {
-                              add_to_files_source_info (files_source_info,
-                                                       top_node_filename_str,
-                                                       "stand_in_file", "Top",
-                                                       0, 0);
-                            }
-                          output_unit_file_name
-                           = add_to_unit_file_name_paths (unit_file_name_paths,
-                                                         top_node_filename_str,
-                                                           file_output_unit);
-                        }
-                      else
-                        {
-                          FILE_SOURCE_INFO *file_source_info;
-
-                          TEXT file_name_text;
-                          text_init (&file_name_text);
-                          text_printf (&file_name_text, "%s_%d", document_name,
-                                                                file_nr);
-                          if (extension)
-                            {
-                              text_append (&file_name_text, ".");
-                              text_append (&file_name_text, extension);
-                            }
-                          file_source_info
-                            = find_file_source_info (files_source_info,
-                                                  file_name_text.text);
-                          if (!file_source_info)
-                            {
-                              add_to_files_source_info (files_source_info,
-                                                       file_name_text.text,
-                                                       "stand_in_file",
-                                                       "unknown",
-                                                       0, 0);
-                            }
-                          output_unit_file_name
-                           = add_to_unit_file_name_paths (unit_file_name_paths,
-                                                          file_name_text.text,
-                                                           file_output_unit);
-                          free (file_name_text.text);
-                        }
-                      file_nr++;
+                  text_init (&target_base);
+                  text_append (&target_base, "index-");
+                  if (region)
+                    {
+                      text_append (&target_base, region);
+                      text_append (&target_base, "-");
                     }
+                  text_append (&target_base, normalized_index);
+                  free (normalized_index);
+                  target = unique_target (self, target_base.text);
+                  free (target_base.text);
+                  if (index_entry->entry_associated_element)
+                    target_element = index_entry->entry_associated_element;
+                  else
+                    target_element = main_entry_element;
+
+                  add_element_target (self, target_element, target);
+                  add_string (target, &self->seen_ids);
+
+                  free (target);
                 }
             }
-          if (output_unit != file_output_unit)
-            add_to_unit_file_name_paths (unit_file_name_paths,
-                                         output_unit_file_name,
-                                         output_unit);
         }
-      free (top_node_filename_str);
+      free (sorted_index_names);
     }
+}
 
-  self->output_unit_file_indices = (size_t *)
-    malloc (output_units->number * sizeof (size_t));
+static const char *footid_base = "FOOT";
+static const char *docid_base = "DOCF";
 
-  for (i = 0; i < output_units->number; i++)
+static void
+prepare_footnotes_targets (CONVERTER *self)
+{
+  const ELEMENT_LIST *global_footnotes = 
&self->document->global_commands->footnotes;
+  if (global_footnotes->number > 0)
     {
-      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];
-      char *filename = strdup (output_unit_file_name);
-      FILE_SOURCE_INFO *file_source_info
-        = find_file_source_info (files_source_info, output_unit_file_name);
-      char *filepath = file_source_info->path;
-
-      FILE_NAME_PATH *file_name_path
-        = call_file_id_setting_unit_file_name (self, output_unit,
-                                         output_unit_file_name, filepath);
-      if (file_name_path)
+      int i;
+      for (i = 0; i < global_footnotes->number; i++)
         {
-          if (file_name_path->filename)
+          const ELEMENT *footnote = global_footnotes->list[i];
+          TEXT footid;
+          TEXT docid;
+          int nr = i+1;
+
+          text_init (&footid);
+          text_init (&docid);
+          text_printf (&footid, "%s%d", footid_base, nr);
+          text_printf (&docid, "%s%d", docid_base, nr);
+
+          while (1)
             {
-              FILE_SOURCE_INFO *file_source_info
-               = find_file_source_info (files_source_info,
-                                        file_name_path->filename);
-              if (file_source_info)
+              int j;
+              int non_unique = 0;
+              for (j = 0; j < self->seen_ids.number; j++)
                 {
-                  if (file_source_info->path && file_name_path->filepath
-                      && strcmp (file_source_info->path,
-                                 file_name_path->filepath))
+                  if (!strcmp (footid.text, self->seen_ids.list[j])
+                      || !strcmp (docid.text, self->seen_ids.list[j]))
                     {
-                      message_list_document_warn (&self->error_messages,
-                                                  self->conf,
-                                     "resetting %s file path %s to %s",
-                                           file_name_path->filename,
-                                           file_source_info->path,
-                                           file_name_path->filepath);
+                      non_unique = 1;
+                      break;
                     }
-                  set_file_source_info (file_source_info, "special_file",
-                                "user_defined", 0, file_name_path->filepath);
+                }
+              if (non_unique)
+                {
+                  nr++;
+                  if (nr == 0)
+                    fatal ("overflow footnote target nr");
+
+                  text_init (&footid);
+                  text_init (&docid);
+                  text_printf (&footid, "%s%d", footid_base, nr);
+                  text_printf (&docid, "%s%d", docid_base, nr);
                 }
               else
-                add_to_files_source_info (files_source_info,
-                                          file_name_path->filename,
-                                          "special_file", "user_defined",
-                                           0, file_name_path->filepath);
-              free (filename);
-              filename = file_name_path->filename;
+                break;
             }
-          free (file_name_path);
+          add_string (footid.text, &self->seen_ids);
+          add_string (docid.text, &self->seen_ids);
+          add_element_target (self, footnote, footid.text);
+          add_special_target (self, ST_footnote_location, footnote,
+                              docid.text);
+
+          if (self->conf->DEBUG > 0)
+            {
+              char *footnote_txi = convert_to_texinfo (footnote);
+              fprintf (stderr, "Enter footnote: target %s, nr %d\n%s\n",
+                       footid.text, nr, footnote_txi);
+              free (footnote_txi);
+            }
+          free (footid.text);
+          free (docid.text);
         }
-      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),
-                 output_unit->unit_filename, output_unit_file->counter);
-      free (filename);
-      free (output_unit_file_name);
     }
+}
 
-  free (unit_file_name_paths);
+/* for conversion units except for associated special units that require
+   files for document units to be set */
+void
+html_prepare_conversion_units_targets (CONVERTER *self,
+                                       const char *document_name,
+                                       int output_units_descriptor,
+                                       int special_units_descriptor,
+                                       int associated_special_units_descriptor)
+{
+  /*
+   Do that before the other elements, to be sure that special page ids
+   are registered before elements id are.
+   */
+  set_special_units_targets_files (self, special_units_descriptor,
+                                   document_name);
 
-  if (special_units && special_units->number)
-    {
-      int i;
-      self->special_unit_file_indices = (size_t *)
-        malloc (special_units->number * sizeof (size_t));
-      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];
-          const ELEMENT *unit_command = special_unit->unit_command;
-          const HTML_TARGET *special_unit_target
-            = find_element_target (&self->html_targets, unit_command);
-          char *filename = special_unit_target->special_unit_filename;
+  prepare_associated_special_units_targets (self,
+                                  associated_special_units_descriptor);
 
-        /* Associate the special elements that have no page with the main page.
-           This may only happen if not split. */
-          if (!filename && special_units->number
-              && output_units && output_units->number
-              && output_units->list[0]->unit_filename)
-            {
-              filename = output_units->list[0]->unit_filename;
-            }
+  set_root_commands_targets_node_files (self);
 
-          if (filename)
+  prepare_index_entries_targets (self);
+  prepare_footnotes_targets (self);
+}
+
+/* Associate output units to the global targets, First, Last, Top, Index.
+   and special output units */
+void
+html_prepare_output_units_global_targets (CONVERTER *self,
+                                        int output_units_descriptor,
+                                        int special_units_descriptor,
+                                        int 
associated_special_units_descriptor)
+{
+  int i;
+  int all_special_units_nr = 0;
+  int s;
+  const OUTPUT_UNIT_LIST *output_units
+    = retrieve_output_units (output_units_descriptor);
+
+  const OUTPUT_UNIT *top_output_unit = get_top_unit (self->document,
+                                                     output_units);
+
+  int special_output_units_lists[2] = {special_units_descriptor,
+                                       associated_special_units_descriptor};
+
+  self->global_units_directions[D_First] = output_units->list[0];
+  self->global_units_directions[D_Last]
+    = output_units->list[output_units->number - 1];
+
+  self->global_units_directions[D_Top] = top_output_unit;
+
+  /* It is always the first printindex, even if it is not output (for example
+     it is in @copying and @titlepage, which are certainly wrong constructs).
+   */
+  if (self->document->global_commands->printindex.number > 0)
+    {
+      const ELEMENT *printindex
+        = self->document->global_commands->printindex.list[0];
+      ROOT_AND_UNIT *root_unit
+        = html_get_tree_root_element (self, printindex, 0);
+      if (root_unit->output_unit)
+        {
+          const OUTPUT_UNIT *document_unit = root_unit->output_unit;
+          const ELEMENT *root_command = root_unit->root;
+          if (root_command && root_command->cmd == CM_node)
             {
-              FILE_SOURCE_INFO *file_source_info
-               = find_file_source_info (files_source_info, filename);
-              if (file_source_info)
+              const ELEMENT *associated_section
+                = lookup_extra_element (root_command, "associated_section");
+              if (associated_section)
+                root_command = associated_section;
+            }
+       /* find the first level 1 sectioning element to associate the printindex
+           with */
+          if (root_command && root_command->cmd != CM_node)
+            {
+              while (1)
                 {
-                  if (!strcmp (file_source_info->type, "stand_in_file"))
-                    {/* NOTE we keep the order, as in perl */
-                      set_file_source_info (file_source_info, "special_unit",
-                                            0, unit_command, 0);
+                  int status;
+                  int section_level
+                    = lookup_extra_integer (root_command, "section_level",
+                                                               &status);
+                  if (!status && section_level <= 1)
+                    break;
+
+                  const ELEMENT *up_section_directions
+                    = lookup_extra_element (root_command, 
"section_directions");
+                  if (up_section_directions
+                      && up_section_directions->contents.list[D_up]
+                      && up_section_directions->contents.list[D_up]
+                                     ->associated_unit)
+                    {
+                      root_command = 
up_section_directions->contents.list[D_up];
+                      document_unit = root_command->associated_unit;
                     }
+                  else
+                    break;
                 }
-              else
-                add_to_files_source_info (files_source_info, filename,
-                                          "special_unit", 0, unit_command, 0);
             }
-          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);
+          self->global_units_directions[D_Index] = document_unit;
+        }
+      free (root_unit);
+    }
+
+  if (self->conf->DEBUG > 0)
+    {
+      int i;
+      fprintf (stderr, "GLOBAL DIRECTIONS:\n");
+      for (i = 0; i < D_Last+1; i++)
+        {
+          if (self->global_units_directions[i])
+            {
+              const OUTPUT_UNIT *global_unit = 
self->global_units_directions[i];
+              char *unit_texi = output_unit_texi (global_unit);
+              fprintf (stderr, " %s: %s\n", 
html_global_unit_direction_names[i],
+                                            unit_texi);
+              free (unit_texi);
+            }
+        }
+      fprintf (stderr, "\n");
+    }
+
+  /* determine total number of special output units and fill
+     special_units_directions_name_unit.  Used to simplify building perl
+     directions */
+  for (i = 0; i < 2; i++)
+    {
+      int special_units_descriptor = special_output_units_lists[i];
+      const OUTPUT_UNIT_LIST *units_list
+       = retrieve_output_units (special_units_descriptor);
+      if (units_list && units_list->number)
+        all_special_units_nr += units_list->number;
+    }
+
+  self->special_units_direction_name = (SPECIAL_UNIT_DIRECTION *)
+   malloc (sizeof (SPECIAL_UNIT_DIRECTION) * (all_special_units_nr+1));
+  memset (self->special_units_direction_name, 0,
+          sizeof (SPECIAL_UNIT_DIRECTION) * (all_special_units_nr+1));
+
+  s = 0;
+  for (i = 0; i < 2; i++)
+    {
+      int special_units_descriptor = special_output_units_lists[i];
+      OUTPUT_UNIT_LIST *units_list
+       = retrieve_output_units (special_units_descriptor);
+      if (units_list && units_list->number)
+        {
+          int j;
+          for (j = 0; j < units_list->number; j++)
+            {
+              const OUTPUT_UNIT *special_unit = units_list->list[j];
+              char *special_unit_variety = special_unit->special_unit_variety;
+              int special_unit_direction_index
+                = special_unit_variety_direction_index (self,
+                                                special_unit_variety);
+              self->global_units_directions[special_unit_direction_index]
+                = special_unit;
+
+              self->special_units_direction_name[s].output_unit = special_unit;
+              self->special_units_direction_name[s].direction
+                = special_unit_info (self, SUI_type_direction,
+                                           special_unit_variety);
+              s++;
+            }
+        }
+    }
+}
+
+static void
+set_file_source_info (FILE_SOURCE_INFO *file_source_info,
+                          char *file_info_type, char *file_info_name,
+                          const ELEMENT *file_info_element, char *filepath)
+{
+  file_source_info->type = file_info_type;
+  file_source_info->name = file_info_name;
+  file_source_info->element = file_info_element;
+  file_source_info->path = filepath;
+}
+
+static FILE_SOURCE_INFO *
+add_to_files_source_info (FILE_SOURCE_INFO_LIST *files_source_info,
+                          char *filename,
+                          char *file_info_type, char *file_info_name,
+                          const ELEMENT *file_info_element, char *filepath)
+{
+  FILE_SOURCE_INFO *new_file_source_info;
+  if (files_source_info->number == files_source_info->space)
+    {
+      files_source_info->list = realloc (files_source_info->list,
+               (files_source_info->space += 5) * sizeof (FILE_SOURCE_INFO));
+      if (!files_source_info->list)
+       fatal ("realloc failed");
+    }
+
+  new_file_source_info =
+   &files_source_info->list[files_source_info->number];
+
+  new_file_source_info->filename = strdup (filename);
+  set_file_source_info (new_file_source_info, file_info_type,
+                        file_info_name, file_info_element, filepath);
+
+  files_source_info->number++;
+
+  return new_file_source_info;
+}
+
+static FILE_SOURCE_INFO *
+find_file_source_info (FILE_SOURCE_INFO_LIST *files_source_info,
+                            char *filename)
+{
+  int i;
+  for (i = 0; i < files_source_info->number; i++)
+    {
+      FILE_SOURCE_INFO *file_source_info = &files_source_info->list[i];
+      if (!strcmp (file_source_info->filename, filename))
+        return file_source_info;
+    }
+  return 0;
+}
+
+void
+html_destroy_files_source_info (FILE_SOURCE_INFO_LIST *files_source_info)
+{
+  int i;
+  for (i = 0; i < files_source_info->number; i++)
+    {
+      free (files_source_info->list[i].filename);
+    }
+  free (files_source_info->list);
+  free (files_source_info);
+}
+
+static char *
+add_to_unit_file_name_paths (char **unit_file_name_paths,
+                             char *filename, OUTPUT_UNIT *output_unit)
+{
+  /* FIXME simplify */
+  char **new_output_unit_file_name
+                  = &unit_file_name_paths[output_unit->index];
+  *new_output_unit_file_name = strdup (filename);
+
+  return *new_output_unit_file_name;
+}
+
+static FILE_SOURCE_INFO_LIST *
+html_set_pages_files (CONVERTER *self, OUTPUT_UNIT_LIST *output_units,
+                      OUTPUT_UNIT_LIST *special_units,
+                      OUTPUT_UNIT_LIST *associated_special_units,
+                      char *output_file,
+                      char *destination_directory, char *output_filename,
+                      char *document_name)
+{
+  FILE_SOURCE_INFO_LIST *files_source_info = 0;
+  char **unit_file_name_paths;
+  int i;
+
+  if (!output_units || !output_units->number)
+    return 0;
+
+  initialize_output_units_files (self);
+
+  files_source_info = (FILE_SOURCE_INFO_LIST *)
+    malloc (sizeof (FILE_SOURCE_INFO_LIST));
+  memset (files_source_info, 0, sizeof (FILE_SOURCE_INFO_LIST));
+
+  unit_file_name_paths = (char **)
+   malloc (output_units->number * sizeof (char *));
+  memset (unit_file_name_paths, 0,
+          output_units->number * sizeof (char *));
+
+  if (!self->conf->SPLIT || !strlen (self->conf->SPLIT))
+    {
+      int i;
+      add_to_files_source_info (files_source_info, output_filename,
+                                "special_file", "non_split", 0,
+                                output_file);
+      for (i = 0; i < output_units->number; i++)
+        {
+          add_to_unit_file_name_paths (unit_file_name_paths,
+                                       output_filename,
+                                       output_units->list[i]);
+        }
+    }
+  else
+    {
+      char *top_node_filename_str;
+      OUTPUT_UNIT *node_top_output_unit = 0;
+      char *extension = 0;
+      const ELEMENT *node_top = 0;
+      int file_nr = 0;
+      int i;
+
+      /* first determine the top node file name. */
+      if (self->document->identifiers_target)
+        node_top = find_identifier_target (self->document->identifiers_target,
+                                           "Top");
+
+      top_node_filename_str = top_node_filename (self, document_name);
+
+      if (node_top && top_node_filename_str)
+        {
+          int i;
+          node_top_output_unit = node_top->associated_unit;
+          for (i = 0; i < output_units->number; i++)
+            if (output_units->list[i] == node_top_output_unit)
+              break;
+          add_to_files_source_info (files_source_info, top_node_filename_str,
+                                    "special_file", "Top", 0, 0);
+          add_to_unit_file_name_paths (unit_file_name_paths,
+                                       top_node_filename_str,
+                                       node_top_output_unit);
+        }
+
+      if (self->conf->EXTENSION && strlen (self->conf->EXTENSION))
+        extension = self->conf->EXTENSION;
+
+      for (i = 0; i < output_units->number; i++)
+        {
+          OUTPUT_UNIT *output_unit = output_units->list[i];
+          OUTPUT_UNIT *file_output_unit;
+          char *output_unit_file_name;
+          /* For Top node. */
+          if (node_top_output_unit && node_top_output_unit == output_unit)
+            continue;
+
+          file_output_unit = output_unit->first_in_page;
+          output_unit_file_name
+           = unit_file_name_paths[file_output_unit->index];
+          if (!output_unit_file_name)
+            {
+              char *node_filename = 0;
+              int j;
+              for (j = 0; j < file_output_unit->unit_contents.number; j++)
+                {
+                  const ELEMENT *root_command
+                     = file_output_unit->unit_contents.list[j];
+                  if (root_command->cmd == CM_node)
+                    {
+                      const ELEMENT *node_target = 0;
+                      char *normalized = lookup_extra_string (root_command,
+                                                              "normalized");
+                      if (normalized)
+                        node_target
+                         = find_identifier_target (
+                                  self->document->identifiers_target,
+                                  normalized);
+                   /* double node are not normalized, they are handled here */
+                      if (!node_target)
+                        {
+                          FILE_SOURCE_INFO *file_source_info = 0;
+
+                          TEXT file_name_text;
+                          text_init (&file_name_text);
+                          text_append (&file_name_text, "unknown_node");
+                          if (extension)
+                            {
+                              text_append (&file_name_text, ".");
+                              text_append (&file_name_text, extension);
+                            }
+                          file_source_info
+                            = find_file_source_info (files_source_info,
+                                                         file_name_text.text);
+                          if (!file_source_info)
+                            {
+                              file_source_info
+                                = add_to_files_source_info (files_source_info,
+                                                file_name_text.text, "node", 0,
+                                                              root_command, 0);
+                              node_filename = file_source_info->filename;
+                            }
+                          free (file_name_text.text);
+                        }
+                      else
+                        {
+          /* Nodes with {'extra'}->{'is_target'} should always be in
+            'identifiers_target', and thus in targets.  It is a bug otherwise. 
*/
+                          FILE_SOURCE_INFO *file_source_info = 0;
+                          HTML_TARGET *node_target
+                            = find_element_target (&self->html_targets,
+                                                   root_command);
+                          node_filename = node_target->node_filename;
+
+                          file_source_info
+                            = find_file_source_info (files_source_info,
+                                                          node_filename);
+                          if (file_source_info)
+                            {
+                              if (!strcmp (file_source_info->type,
+                                           "stand_in_file"))
+                                {/* NOTE we keep the order, as in perl */
+                                  set_file_source_info (file_source_info,
+                                                        "node",
+                                                        0, root_command, 0);
+                                }
+                            }
+                          else
+                            add_to_files_source_info (files_source_info,
+                                                 node_filename, "node", 0,
+                                                 root_command, 0);
+                        }
+                      output_unit_file_name
+                        = add_to_unit_file_name_paths (unit_file_name_paths,
+                                                       node_filename,
+                                                       file_output_unit);
+                      break;
+                    }
+                }
+              if (!node_filename)
+                {
+                  /* use section to do the file name if there is no node */
+                  const ELEMENT *command = file_output_unit->unit_command;
+                  if (command)
+                    {
+                      if (command->cmd == CM_top && !node_top
+                          && top_node_filename_str)
+                        {
+                   /* existing top_node_filename can happen, see
+                      html_tests.t top_file_name_and_node_name_collision */
+                          FILE_SOURCE_INFO *file_source_info
+                            = find_file_source_info (files_source_info,
+                                                  top_node_filename_str);
+                          if (file_source_info)
+                            {/* NOTE we keep the order, as in perl */
+                              set_file_source_info (file_source_info,
+                                                    "special_file", "Top",
+                                                    0, 0);
+                            }
+                          else
+                            {
+                              add_to_files_source_info (files_source_info,
+                                                       top_node_filename_str,
+                                                       "special_file", "Top",
+                                                       0, 0);
+                            }
+                          output_unit_file_name
+                           = add_to_unit_file_name_paths (unit_file_name_paths,
+                                                          
top_node_filename_str,
+                                                           file_output_unit);
+                        }
+                      else
+                        {
+                          HTML_TARGET *section_target
+                            = find_element_target (&self->html_targets,
+                                                   command);
+                          char *section_filename
+                            = section_target->section_filename;
+
+                          FILE_SOURCE_INFO *file_source_info
+                            = find_file_source_info (files_source_info,
+                                                     section_filename);
+                          if (file_source_info)
+                            {
+                              if (!strcmp (file_source_info->type,
+                                           "stand_in_file"))
+                                {/* NOTE we keep the order, as in perl */
+                                  set_file_source_info (file_source_info,
+                                                        "section",
+                                                        0, command, 0);
+                                }
+                            }
+                          else
+                            add_to_files_source_info (files_source_info,
+                                                 section_filename, "section", 
0,
+                                                 command, 0);
+                          output_unit_file_name
+                            = add_to_unit_file_name_paths 
(unit_file_name_paths,
+                                                           section_filename,
+                                                           file_output_unit);
+                        }
+                    }
+                  else
+                    {
+                      /* when everything else has failed */
+                      if (file_nr == 0 && !node_top
+                          && top_node_filename_str)
+                        {
+                          FILE_SOURCE_INFO *file_source_info
+                            = find_file_source_info (files_source_info,
+                                                  top_node_filename_str);
+                          if (!file_source_info)
+                            {
+                              add_to_files_source_info (files_source_info,
+                                                       top_node_filename_str,
+                                                       "stand_in_file", "Top",
+                                                       0, 0);
+                            }
+                          output_unit_file_name
+                           = add_to_unit_file_name_paths (unit_file_name_paths,
+                                                         top_node_filename_str,
+                                                           file_output_unit);
+                        }
+                      else
+                        {
+                          FILE_SOURCE_INFO *file_source_info;
+
+                          TEXT file_name_text;
+                          text_init (&file_name_text);
+                          text_printf (&file_name_text, "%s_%d", document_name,
+                                                                file_nr);
+                          if (extension)
+                            {
+                              text_append (&file_name_text, ".");
+                              text_append (&file_name_text, extension);
+                            }
+                          file_source_info
+                            = find_file_source_info (files_source_info,
+                                                  file_name_text.text);
+                          if (!file_source_info)
+                            {
+                              add_to_files_source_info (files_source_info,
+                                                       file_name_text.text,
+                                                       "stand_in_file",
+                                                       "unknown",
+                                                       0, 0);
+                            }
+                          output_unit_file_name
+                           = add_to_unit_file_name_paths (unit_file_name_paths,
+                                                          file_name_text.text,
+                                                           file_output_unit);
+                          free (file_name_text.text);
+                        }
+                      file_nr++;
+                    }
+                }
+            }
+          if (output_unit != file_output_unit)
+            add_to_unit_file_name_paths (unit_file_name_paths,
+                                         output_unit_file_name,
+                                         output_unit);
+        }
+      free (top_node_filename_str);
+    }
+
+  self->output_unit_file_indices = (size_t *)
+    malloc (output_units->number * sizeof (size_t));
+
+  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];
+      char *filename = strdup (output_unit_file_name);
+      FILE_SOURCE_INFO *file_source_info
+        = find_file_source_info (files_source_info, output_unit_file_name);
+      char *filepath = file_source_info->path;
+
+      FILE_NAME_PATH *file_name_path
+        = call_file_id_setting_unit_file_name (self, output_unit,
+                                         output_unit_file_name, filepath);
+      if (file_name_path)
+        {
+          if (file_name_path->filename)
+            {
+              FILE_SOURCE_INFO *file_source_info
+               = find_file_source_info (files_source_info,
+                                        file_name_path->filename);
+              if (file_source_info)
+                {
+                  if (file_source_info->path && file_name_path->filepath
+                      && strcmp (file_source_info->path,
+                                 file_name_path->filepath))
+                    {
+                      message_list_document_warn (&self->error_messages,
+                                                  self->conf,
+                                     "resetting %s file path %s to %s",
+                                           file_name_path->filename,
+                                           file_source_info->path,
+                                           file_name_path->filepath);
+                    }
+                  set_file_source_info (file_source_info, "special_file",
+                                "user_defined", 0, file_name_path->filepath);
+                }
+              else
+                add_to_files_source_info (files_source_info,
+                                          file_name_path->filename,
+                                          "special_file", "user_defined",
+                                           0, file_name_path->filepath);
+              free (filename);
+              filename = file_name_path->filename;
+            }
+          free (file_name_path);
+        }
+      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),
+                 output_unit->unit_filename, output_unit_file->counter);
+      free (filename);
+      free (output_unit_file_name);
+    }
+
+  free (unit_file_name_paths);
+
+  if (special_units && special_units->number)
+    {
+      int i;
+      self->special_unit_file_indices = (size_t *)
+        malloc (special_units->number * sizeof (size_t));
+      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];
+          const ELEMENT *unit_command = special_unit->unit_command;
+          const HTML_TARGET *special_unit_target
+            = find_element_target (&self->html_targets, unit_command);
+          char *filename = special_unit_target->special_unit_filename;
+
+        /* Associate the special elements that have no page with the main page.
+           This may only happen if not split. */
+          if (!filename && special_units->number
+              && output_units && output_units->number
+              && output_units->list[0]->unit_filename)
+            {
+              filename = output_units->list[0]->unit_filename;
+            }
+
+          if (filename)
+            {
+              FILE_SOURCE_INFO *file_source_info
+               = find_file_source_info (files_source_info, filename);
+              if (file_source_info)
+                {
+                  if (!strcmp (file_source_info->type, "stand_in_file"))
+                    {/* NOTE we keep the order, as in perl */
+                      set_file_source_info (file_source_info, "special_unit",
+                                            0, unit_command, 0);
+                    }
+                }
+              else
+                add_to_files_source_info (files_source_info, filename,
+                                          "special_unit", 0, unit_command, 0);
+            }
+          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);
+        }
+    }
+
+  for (i = 0; i < files_source_info->number; i++)
+    {
+      FILE_SOURCE_INFO *file_source_info = &files_source_info->list[i];
+      set_file_path (self, file_source_info->filename, file_source_info->path,
+                     destination_directory);
+    }
+
+  /*
+   to be able to associate to the output unit file the associated
+   output units will be output into, this is done after document output
+   units got files.
+   In practice only used for contents and shortcontents.
+   */
+  if (associated_special_units && associated_special_units->number > 0)
+    {
+      int i;
+      for (i = 0; i < associated_special_units->number; i++)
+        {
+          char *filename = 0;
+          OUTPUT_UNIT *special_unit = associated_special_units->list[i];
+          const OUTPUT_UNIT *associated_output_unit
+            = special_unit->associated_document_unit;
+          const ELEMENT *unit_command = special_unit->unit_command;
+          HTML_TARGET *element_target
+            = find_element_target (&self->html_targets, unit_command);
+
+          if (element_target->special_unit_filename)
+            filename = element_target->special_unit_filename;
+          else
+            {
+              if (associated_output_unit)
+                filename = strdup (associated_output_unit->unit_filename);
+              element_target->special_unit_filename = filename;
+            }
+
+   /* set here the file name, but do not increment the counter as it is
+      already set for the output unit the special output unit is in. */
+          if (filename)
+            set_output_unit_file (self, special_unit, filename, 0);
+        }
+    }
+
+  /* initialize data that requires output_unit_files number */
+  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));
+
+  /* 0 is for document_global_context_css, the remaining indices
+     for the output unit files */
+  self->page_css.number = self->output_unit_files.number +1;
+  self->page_css.list = (CSS_LIST *)
+       malloc (self->page_css.number * sizeof (CSS_LIST));
+  memset (self->page_css.list, 0,
+          self->page_css.number * sizeof (CSS_LIST));
+
+  return files_source_info;
+}
+
+/* setup a page (+global context) in case there are no files, ie called
+   with convert or output with an empty string as filename. */
+void
+setup_output_simple_page (CONVERTER *self, const char *output_filename)
+{
+  PAGE_NAME_NUMBER *page_name_number;
+  self->page_css.number = 1+1;
+  self->page_css.list = (CSS_LIST *)
+       malloc (self->page_css.number * sizeof (CSS_LIST));
+  memset (self->page_css.list, 0,
+          self->page_css.number * sizeof (CSS_LIST));
+
+  self->page_name_number.number = 1;
+  self->page_name_number.list = (PAGE_NAME_NUMBER *)
+      malloc (self->page_name_number.number * sizeof (PAGE_NAME_NUMBER));
+
+  page_name_number = &self->page_name_number.list[0];
+  page_name_number->number = 1;
+  page_name_number->page_name = output_filename;
+}
+
+static void
+prepare_special_units_directions (CONVERTER *self,
+                                  OUTPUT_UNIT_LIST *special_units)
+{
+  int i;
+
+  if (!special_units)
+    return;
+
+  for (i = 0; i < special_units->number; i++)
+    {
+      OUTPUT_UNIT *special_unit = special_units->list[i];
+      special_unit->directions[RUD_type_This] = special_unit;
+    }
+}
+
+FILE_SOURCE_INFO_LIST *
+html_prepare_units_directions_files (CONVERTER *self,
+          int output_units_descriptor,
+          int special_units_descriptor, int 
associated_special_units_descriptor,
+          char *output_file, char *destination_directory, char 
*output_filename,
+          char *document_name)
+{
+  int i;
+  FILE_SOURCE_INFO_LIST *files_source_info = 0;
+  OUTPUT_UNIT_LIST *output_units
+    = retrieve_output_units (output_units_descriptor);
+  OUTPUT_UNIT_LIST *special_units
+    = retrieve_output_units (special_units_descriptor);
+  OUTPUT_UNIT_LIST *associated_special_units
+    = retrieve_output_units (associated_special_units_descriptor);
+
+  html_prepare_output_units_global_targets (self, output_units_descriptor,
+                                             special_units_descriptor,
+                                       associated_special_units_descriptor);
+
+  split_pages (output_units, self->conf->SPLIT);
+
+  if (strlen (output_file))
+    {
+      files_source_info =
+        html_set_pages_files (self, output_units, special_units,
+                        associated_special_units, output_file,
+                        destination_directory, output_filename, document_name);
+    }
+  else
+    setup_output_simple_page (self, output_filename);
+
+
+  units_directions (self->conf, self->document->identifiers_target,
+                    output_units);
+
+  prepare_special_units_directions (self, special_units);
+
+  units_file_directions (output_units);
+
+ /* elements_in_file_count is only set in HTML, not in
+    Texinfo::Convert::Converter */
+  if (self->output_unit_files.number)
+    {
+      /* set elements_in_file_count and prepare page_name_number
+         for sorting */
+      self->page_name_number.number = self->output_unit_files.number;
+      self->page_name_number.list = (PAGE_NAME_NUMBER *)
+        malloc (self->page_name_number.number * sizeof (PAGE_NAME_NUMBER));
+
+      for (i = 0; i < self->output_unit_files.number; i++)
+        {
+          FILE_NAME_PATH_COUNTER *file_counter
+            = &self->output_unit_files.list[i];
+          PAGE_NAME_NUMBER *page_name_number = &self->page_name_number.list[i];
+
+          /* counter is dynamic, decreased when the element is encountered
+             elements_in_file_count is not modified afterwards */
+          file_counter->elements_in_file_count = file_counter->counter;
+
+          page_name_number->number = i+1;
+          page_name_number->page_name = file_counter->filename;
+        }
+
+      qsort (self->page_name_number.list,
+             self->page_name_number.number,
+             sizeof (PAGE_NAME_NUMBER), compare_page_name_number);
+    }
+
+  return files_source_info;
+}
+
+void
+convert_text (CONVERTER *self, const enum element_type type,
+              const ELEMENT *element, const char *content,
+              TEXT *result)
+{
+  char *content_used;
+  int contents_used_to_be_freed = 0;
+
+  if (html_in_verbatim (self))
+    {
+      /* do not use the customization API as in perl */
+      html_default_format_protect_text (content, result);
+      return;
+    }
+  else if (html_in_raw (self))
+    {
+      text_append (result, content);
+      return;
+    }
+
+  if (html_in_upper_case (self))
+    {
+      content_used = to_upper_or_lower_multibyte (content, 1);
+      contents_used_to_be_freed = 1;
+    }
+  else
+    /* cast needed to avoid a compiler warning */
+    content_used = (char *) content;
+
+  if (html_in_preformatted_context (self))
+    {
+      OTXI_ALL_CONVERT_TEXT ( , )
+    }
+  else if (html_in_non_breakable_space (self))
+    {
+      OTXI_ALL_CONVERT_TEXT (" \n", OTXI_NO_BREAK_CASES(p))
+    }
+  else if (html_in_space_protected (self))
+    {
+      OTXI_ALL_CONVERT_TEXT (" \n", OTXI_SPACE_PROTECTION_CASES(p))
+    }
+  else
+    {
+      OTXI_ALL_CONVERT_TEXT ( , )
+    }
+
+  if (contents_used_to_be_freed)
+    free (content_used);
+}
+
+void
+default_format_footnotes_segment (CONVERTER *self, TEXT *result)
+{
+  char *class_base;
+  char *attribute_class;
+  char *class;
+  STRING_LIST *classes;
+  ELEMENT *footnote_heading_tree;
+  char *footnote_heading;
+  int level;
+  char *formatted_heading;
+  char *foot_lines = call_formatting_function_format_footnotes_sequence (self);
+
+  if (!foot_lines || !strlen (foot_lines))
+    {
+      free (foot_lines);
+      return;
+    }
+
+  classes = (STRING_LIST *) malloc (sizeof (STRING_LIST));
+  memset (classes, 0, sizeof (STRING_LIST));
+
+  class_base = special_unit_info (self, SUI_type_class,
+                                  "footnotes");
+  xasprintf (&class, "%s-segment", class_base);
+
+  add_string (class, classes);
+  free (class);
+  attribute_class = html_attribute_class (self, "div", classes);
+  clear_strings_list (classes);
+
+  text_append (result, attribute_class);
+  free (attribute_class);
+
+  text_append_n (result, ">\n", 2);
+
+  if (self->conf->DEFAULT_RULE && strlen (self->conf->DEFAULT_RULE))
+    {
+      text_append (result, self->conf->DEFAULT_RULE);
+      text_append_n (result, "\n", 1);
+    }
+
+  footnote_heading_tree = special_unit_info_tree (self,
+                              SUIT_type_heading, "footnotes");
+  if (footnote_heading_tree)
+    {
+      footnote_heading = html_convert_tree (self, footnote_heading_tree,
+                                    "convert footnotes special heading");
+    }
+  else
+    {
+      footnote_heading = "";
+    }
+
+  level = self->conf->FOOTNOTE_END_HEADER_LEVEL;
+
+  xasprintf (&class, "%s-heading", class_base);
+
+  add_string (class, classes);
+  free (class);
+
+  formatted_heading
+    = call_formatting_function_format_heading_text (self, 0, classes,
+                                                    footnote_heading,
+                                                    level, 0, 0, 0);
+  destroy_strings_list (classes);
+  text_append (result, formatted_heading);
+  text_append_n (result, "\n", 1);
+
+  free (formatted_heading);
+
+  if (footnote_heading_tree)
+    free (footnote_heading);
+
+  text_append (result, foot_lines);
+  free (foot_lines);
+  text_append (result, "</div>\n");
+}
+
+void
+format_footnotes_segment (CONVERTER *self, TEXT *result)
+{
+  if (self->formatting_references[FR_format_footnotes_segment].status
+                                             == FRS_status_default_set)
+    {
+      default_format_footnotes_segment (self, result);
+    }
+  else
+    {
+      char *footnotes_segment
+        = call_formatting_function_format_footnotes_segment (self);
+      if (footnotes_segment)
+        {
+          text_append (result, footnotes_segment);
+          free (footnotes_segment);
         }
     }
+}
 
-  for (i = 0; i < files_source_info->number; i++)
+void
+convert_table_term_type (CONVERTER *self, const enum element_type type,
+                        const ELEMENT *element, const char *content,
+                        TEXT *result)
+{
+  if (content)
     {
-      FILE_SOURCE_INFO *file_source_info = &files_source_info->list[i];
-      set_file_path (self, file_source_info->filename, file_source_info->path,
-                     destination_directory);
+      text_append (result, "<dt>");
+      text_append (result, content);
     }
+}
 
-  /*
-   to be able to associate to the output unit file the associated
-   output units will be output into, this is done after document output
-   units got files.
-   In practice only used for contents and shortcontents.
-   */
-  if (associated_special_units && associated_special_units->number > 0)
+void
+convert_row_type (CONVERTER *self, const enum element_type type,
+                  const ELEMENT *element, const char *content,
+                  TEXT *result)
+{
+  if (html_in_string (self))
     {
-      int i;
-      for (i = 0; i < associated_special_units->number; i++)
-        {
-          char *filename = 0;
-          OUTPUT_UNIT *special_unit = associated_special_units->list[i];
-          const OUTPUT_UNIT *associated_output_unit
-            = special_unit->associated_document_unit;
-          const ELEMENT *unit_command = special_unit->unit_command;
-          HTML_TARGET *element_target
-            = find_element_target (&self->html_targets, unit_command);
-
-          if (element_target->special_unit_filename)
-            filename = element_target->special_unit_filename;
-          else
-            {
-              if (associated_output_unit)
-                filename = strdup (associated_output_unit->unit_filename);
-              element_target->special_unit_filename = filename;
-            }
-
-   /* set here the file name, but do not increment the counter as it is
-      already set for the output unit the special output unit is in. */
-          if (filename)
-            set_output_unit_file (self, special_unit, filename, 0);
-        }
+      if (content)
+        text_append (result, content);
     }
 
-  /* initialize data that requires output_unit_files number */
-  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));
-
-  /* 0 is for document_global_context_css, the remaining indices
-     for the output unit files */
-  self->page_css.number = self->output_unit_files.number +1;
-  self->page_css.list = (CSS_LIST *)
-       malloc (self->page_css.number * sizeof (CSS_LIST));
-  memset (self->page_css.list, 0,
-          self->page_css.number * sizeof (CSS_LIST));
+  if (!content || content[strspn (content, whitespace_chars)] == '\0')
+    return;
+  else
+    {
+      text_append (result, "<tr>");
+      text_append (result, content);
+      text_append (result, "</tr>");
 
-  return files_source_info;
+      if (element->contents.number > 0
+          && element->contents.list[0]->cmd != CM_headitem)
+      /* if headitem, end of line added in _convert_multitable_head_type */
+        text_append (result, "\n");
+    }
 }
 
-/* setup a page (+global context) in case there are no files, ie called
-   with convert or output with an empty string as filename. */
 void
-setup_output_simple_page (CONVERTER *self, const char *output_filename)
+convert_w_command (CONVERTER *self, const enum command_id cmd,
+                    const ELEMENT *element,
+                    const HTML_ARGS_FORMATTED *args_formatted,
+                    const char *content, TEXT *result)
 {
-  PAGE_NAME_NUMBER *page_name_number;
-  self->page_css.number = 1+1;
-  self->page_css.list = (CSS_LIST *)
-       malloc (self->page_css.number * sizeof (CSS_LIST));
-  memset (self->page_css.list, 0,
-          self->page_css.number * sizeof (CSS_LIST));
-
-  self->page_name_number.number = 1;
-  self->page_name_number.list = (PAGE_NAME_NUMBER *)
-      malloc (self->page_name_number.number * sizeof (PAGE_NAME_NUMBER));
-
-  page_name_number = &self->page_name_number.list[0];
-  page_name_number->number = 1;
-  page_name_number->page_name = output_filename;
+  if (args_formatted && args_formatted->number > 0)
+    {
+      if (args_formatted->args[0].formatted[AFT_type_normal])
+        text_append (result,
+                     args_formatted->args[0].formatted[AFT_type_normal]);
+    }
+  if (!html_in_string (self))
+    {
+      text_append (result, "<!-- /@w -->");
+    }
 }
 
-static void
-prepare_special_units_directions (CONVERTER *self,
-                                  OUTPUT_UNIT_LIST *special_units)
+static char *
+contents_inline_element (CONVERTER *self, const enum command_id cmd,
+                         const ELEMENT *command)
 {
-  int i;
+  char *content;
 
-  if (!special_units)
-    return;
+  if (self->conf->DEBUG > 0)
+    fprintf (stderr, "CONTENTS_INLINE %s\n", builtin_command_name (cmd));
 
-  for (i = 0; i < special_units->number; i++)
+  content = call_formatting_function_format_contents (self,
+                        builtin_command_name (cmd), command, 0);
+  if (content && strlen (content))
     {
-      OUTPUT_UNIT *special_unit = special_units->list[i];
-      special_unit->directions[RUD_type_This] = special_unit;
-    }
-}
+      int j;
+      for (j = 0; self->command_special_variety_name_index[j].cmd; j++)
+        {
+          COMMAND_ID_INDEX cmd_variety_index
+                = self->command_special_variety_name_index[j];
+          if (cmd_variety_index.cmd == cmd)
+            {
+              char *heading = 0;
+              char *formatted_heading;
+              TEXT result;
+              STRING_LIST *classes;
+              char *class_base;
+              char *class;
+              char *attribute_class;
+
+              char *special_unit_variety
+                = self->special_unit_varieties.list[cmd_variety_index.index];
+              int special_unit_direction_index
+                    = special_unit_variety_direction_index (self,
+                                                special_unit_variety);
+              const OUTPUT_UNIT *special_unit
+                = self->global_units_directions[special_unit_direction_index];
 
-FILE_SOURCE_INFO_LIST *
-html_prepare_units_directions_files (CONVERTER *self,
-          int output_units_descriptor,
-          int special_units_descriptor, int 
associated_special_units_descriptor,
-          char *output_file, char *destination_directory, char 
*output_filename,
-          char *document_name)
-{
-  int i;
-  FILE_SOURCE_INFO_LIST *files_source_info = 0;
-  OUTPUT_UNIT_LIST *output_units
-    = retrieve_output_units (output_units_descriptor);
-  OUTPUT_UNIT_LIST *special_units
-    = retrieve_output_units (special_units_descriptor);
-  OUTPUT_UNIT_LIST *associated_special_units
-    = retrieve_output_units (associated_special_units_descriptor);
+              text_init (&result);
 
-  html_prepare_output_units_global_targets (self, output_units_descriptor,
-                                             special_units_descriptor,
-                                       associated_special_units_descriptor);
+              classes = (STRING_LIST *) malloc (sizeof (STRING_LIST));
+              memset (classes, 0, sizeof (STRING_LIST));
 
-  split_pages (output_units, self->conf->SPLIT);
+              class_base = special_unit_info (self, SUI_type_class,
+                                              special_unit_variety);
+              xasprintf (&class, "element-%s", class_base);
 
-  if (strlen (output_file))
-    {
-      files_source_info =
-        html_set_pages_files (self, output_units, special_units,
-                        associated_special_units, output_file,
-                        destination_directory, output_filename, document_name);
-    }
-  else
-    setup_output_simple_page (self, output_filename);
+              add_string (class, classes);
+              free (class);
+              attribute_class = html_attribute_class (self, "div", classes);
+              clear_strings_list (classes);
 
+              text_append (&result, attribute_class);
+              free (attribute_class);
 
-  units_directions (self->conf, self->document->identifiers_target,
-                    output_units);
+              if (special_unit)
+                {
+                  ELEMENT *command = special_unit->unit_command;
+                  char *id = html_command_id (self, command);
+                  if (id && strlen (id))
+                    text_printf (&result, " id=\"%s\"", id);
+                  heading = html_command_text (self, command, 0);
+                  if (!heading)
+                    heading = strdup ("");
+                }
+              else
+                { /* happens when called as convert() and not output() */
+                  ELEMENT *heading_tree = special_unit_info_tree (self,
+                                   SUIT_type_heading, special_unit_variety);
+                  if (heading_tree)
+                    {
+                      char *explanation;
+                      xasprintf (&explanation, "convert %s special heading",
+                                               builtin_command_name (cmd));
+                      heading = html_convert_tree (self, heading_tree,
+                                                   explanation);
+                      free (explanation);
+                    }
+                }
+              text_append_n (&result, ">\n", 2);
 
-  prepare_special_units_directions (self, special_units);
+              xasprintf (&class, "%s-heading", class_base);
 
-  units_file_directions (output_units);
+              add_string (class, classes);
+              free (class);
 
- /* elements_in_file_count is only set in HTML, not in
-    Texinfo::Convert::Converter */
-  if (self->output_unit_files.number)
-    {
-      /* set elements_in_file_count and prepare page_name_number
-         for sorting */
-      self->page_name_number.number = self->output_unit_files.number;
-      self->page_name_number.list = (PAGE_NAME_NUMBER *)
-        malloc (self->page_name_number.number * sizeof (PAGE_NAME_NUMBER));
+              formatted_heading
+               = call_formatting_function_format_heading_text (self, 0, 
classes,
+                            heading, self->conf->CHAPTER_HEADER_LEVEL, 0, 0, 
0);
+              destroy_strings_list (classes);
 
-      for (i = 0; i < self->output_unit_files.number; i++)
-        {
-          FILE_NAME_PATH_COUNTER *file_counter
-            = &self->output_unit_files.list[i];
-          PAGE_NAME_NUMBER *page_name_number = &self->page_name_number.list[i];
+              free (heading);
 
-          /* counter is dynamic, decreased when the element is encountered
-             elements_in_file_count is not modified afterwards */
-          file_counter->elements_in_file_count = file_counter->counter;
+              text_append (&result, formatted_heading);
+              free (formatted_heading);
+              text_append_n (&result, "\n", 1);
 
-          page_name_number->number = i+1;
-          page_name_number->page_name = file_counter->filename;
-        }
+              text_append (&result, content);
+              text_append_n (&result, "</div>\n", 7);
 
-      qsort (self->page_name_number.list,
-             self->page_name_number.number,
-             sizeof (PAGE_NAME_NUMBER), compare_page_name_number);
+              free (content);
+              return result.text;
+            }
+        }
     }
-
-  return files_source_info;
+  return 0;
 }
 
+/* Output a list of the nodes immediately below this one */
 void
-convert_text (CONVERTER *self, const enum element_type type,
-              const ELEMENT *element, const char *content,
-              TEXT *result)
+mini_toc_internal (CONVERTER *self, const ELEMENT *element, TEXT *result)
 {
-  char *content_used;
-  int contents_used_to_be_freed = 0;
+  int entry_index = 0;
 
-  if (html_in_verbatim (self))
-    {
-      html_default_format_protect_text (content, result);
-      return;
-    }
-  else if (html_in_raw (self))
+  /* drop the const with a cast, but we know that it is not modified, with
+     0 as the third argument */
+  ELEMENT_LIST *section_childs = lookup_extra_contents ((ELEMENT *) element,
+                                                        "section_childs", 0);
+  if (section_childs && section_childs->number > 0)
     {
-      text_append (result, content);
-      return;
+      STRING_LIST *classes;
+      char *attribute_class;
+      size_t i;
+
+      classes = (STRING_LIST *) malloc (sizeof (STRING_LIST));
+      memset (classes, 0, sizeof (STRING_LIST));
+      add_string ("mini-toc", classes);
+      attribute_class = html_attribute_class (self, "ul", classes);
+      destroy_strings_list (classes);
+
+      text_append (result, attribute_class);
+      free (attribute_class);
+      text_append_n (result, ">\n", 2);
+
+      for (i = 0; i < section_childs->number; i++)
+        {
+          ELEMENT *section = section_childs->list[i];
+     /* using command_text leads to the same HTML formatting, but does not give
+        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);
+      */
+          TREE_ADDED_ELEMENTS *command_tree
+             = html_command_tree (self, section, 1);
+          char *explanation;
+          char *accesskey;
+          char *text;
+          char *href = html_command_href (self, section, 0, 0, 0);
+
+          xasprintf (&explanation, "mini_toc @%s",
+                     element_command_name (section));
+          text = html_convert_tree (self, command_tree->tree, explanation);
+          free (explanation);
+
+          entry_index++;
+
+          if (self->conf->USE_ACCESSKEY > 0 && entry_index < 10)
+            {
+              xasprintf (&accesskey, " accesskey=\"%d\"", entry_index);
+            }
+          else
+            accesskey = strdup ("");
+
+          if (strlen (text))
+            {
+              if (strlen (href))
+                {
+                  text_printf (result, "<li><a href=\"%s\"%s>%s</a>",
+                               href, accesskey, text);
+                }
+              else
+                text_printf (result, "<li>%s", text);
+
+              text_append_n (result, "</li>\n", 6);
+            }
+          free (text);
+          free (href);
+          free (accesskey);
+        }
+      text_append_n (result, "</ul>\n", 6);
     }
+}
 
-  if (html_in_upper_case (self))
+void
+convert_heading_command (CONVERTER *self, const enum command_id cmd,
+                    const ELEMENT *element,
+                    const HTML_ARGS_FORMATTED *args_formatted,
+                    const char *content, TEXT *result)
+{
+  char *element_id;
+  OUTPUT_UNIT *output_unit = 0;
+  char *element_header = 0;
+  /* could use only one, but this is more similar to perl code */
+  TEXT tables_of_contents;
+  TEXT mini_toc_or_auto_menu;
+  enum command_id level_corrected_cmd;
+  int status;
+  char *heading;
+  int heading_level = -1;
+  int do_heading;
+  char *heading_id = 0;
+  char *level_set_class = 0;
+
+  const ELEMENT *opening_section = 0;
+  enum command_id level_corrected_opening_section_cmd = 0;
+
+  enum command_id data_cmd = element_builtin_data_cmd (element);
+  unsigned long flags = builtin_command_data[data_cmd].flags;
+
+  /* No situation where this could happen */
+  if (html_in_string (self))
     {
-      content_used = to_upper_or_lower_multibyte (content, 1);
-      contents_used_to_be_freed = 1;
+      if (element->cmd != CM_node)
+        {
+          char *heading = html_command_text (self, element, HCTT_string);
+          text_append (result, heading);
+          text_append_n (result, "\n", 1);
+          free (heading);
+        }
+      if (content)
+        text_append (result, content);
+      return;
     }
-  else
-    /* cast needed to avoid a compiler warning */
-    content_used = (char *) content;
 
-  if (html_in_preformatted_context (self))
+  element_id = html_command_id (self, element);
+
+  if (self->conf->DEBUG > 0)
+    fprintf (stderr, "CONVERT elt heading %s\n",
+                     root_heading_command_to_texinfo (element));
+
+  if (flags & CF_root && element->associated_unit)
+    output_unit = element->associated_unit;
+
+  if (output_unit)
+    element_header
+      = call_formatting_function_format_element_header (self,
+                                         element_command_name (element),
+                                         element, output_unit);
+  text_init (&tables_of_contents);
+  text_append (&tables_of_contents, "");
+  if (element->cmd == CM_top
+      && !strcmp (self->conf->CONTENTS_OUTPUT_LOCATION, "after_top")
+      && self->document->sections_list
+      && self->document->sections_list->number > 1)
     {
-      OTXI_ALL_CONVERT_TEXT ( , )
+      enum command_id contents_cmds[2] = {CM_shortcontents, CM_contents};
+      int i;
+      for (i = 0; i < 2; i++)
+        {
+          int contents_set = 0;
+          enum command_id cmd = contents_cmds[i];
+          COMMAND_OPTION_REF *contents_option_ref
+             = get_command_option (self->conf, cmd);
+          if (*(contents_option_ref->int_ref) > 0)
+            contents_set = 1;
+          free (contents_option_ref);
+          if (contents_set)
+            {
+              char *contents_text
+                = contents_inline_element (self, cmd, 0);
+              if (contents_text)
+                {
+                  text_append (&tables_of_contents, contents_text);
+                  free (contents_text);
+                }
+            }
+        }
     }
-  else if (html_in_non_breakable_space (self))
+
+  text_init (&mini_toc_or_auto_menu);
+  text_append (&mini_toc_or_auto_menu, "");
+  if (tables_of_contents.end <= 0
+      && (flags & CF_sectioning_heading))
     {
-      OTXI_ALL_CONVERT_TEXT (" \n", OTXI_NO_BREAK_CASES(p))
+      if (!strcmp (self->conf->FORMAT_MENU, "sectiontoc"))
+        {
+          mini_toc_internal (self, element, &mini_toc_or_auto_menu);
+        }
+      else if (!strcmp (self->conf->FORMAT_MENU, "menu"))
+        {
+          ELEMENT *node
+            = lookup_extra_element (element, "associated_node");
+          if (node)
+            {
+              int automatic_directions = (node->args.number <= 1);
+              ELEMENT_LIST *menus = lookup_extra_contents (node, "menus", 0);
+              if (!menus && automatic_directions)
+                {
+                  ELEMENT *menu_node
+                   = new_complete_menu_master_menu (self->conf,
+                             self->document->identifiers_target, node);
+
+                  /* FIXME probably need to rebuild menu_node tree */
+                  if (menu_node)
+                    {
+                      convert_to_html_internal (self, menu_node,
+                                                &mini_toc_or_auto_menu, 0);
+                    }
+                }
+            }
+        }
     }
-  else if (html_in_space_protected (self))
+
+  if (self->conf->NO_TOP_NODE_OUTPUT > 0
+      && builtin_command_data[cmd].flags & CF_root)
     {
-      OTXI_ALL_CONVERT_TEXT (" \n", OTXI_SPACE_PROTECTION_CASES(p))
+      const ELEMENT *node_element = 0;
+      int *in_skipped_node_top
+        = get_shared_conversion_state_integer (self, "in_skipped_node_top", 0);
+
+      if (cmd == CM_node)
+        node_element = element;
+      else if (cmd == CM_part)
+        {
+          ELEMENT *part_following_node
+            = lookup_extra_element (element, "part_following_node");
+          if (part_following_node)
+            node_element = part_following_node;
+        }
+      if (node_element || cmd == CM_part)
+        {
+          int node_is_top = 0;
+          if (node_element)
+            {
+              char *normalized = lookup_extra_string (node_element,
+                                                      "normalized");
+              if (normalized && !strcmp (normalized, "Top"))
+                {
+                  node_is_top = 1;
+                  *in_skipped_node_top = 1;
+                  register_modified_shared_conversion_state_integer (self,
+                                                       "in_skipped_node_top");
+                }
+            }
+          if (!node_is_top && *in_skipped_node_top == 1)
+            {
+              *in_skipped_node_top = -1;
+              register_modified_shared_conversion_state_integer (self,
+                                                     "in_skipped_node_top");
+            }
+        }
+      if (*in_skipped_node_top == 1)
+        {
+          char *anchor
+            = call_formatting_function_format_separate_anchor (self,
+                                element_id, builtin_command_name(cmd));
+          text_append (result, anchor);
+          free (anchor);
+          text_append (result, element_header);
+          free (element_header);
+          text_append (result, tables_of_contents.text);
+          free (tables_of_contents.text);
+          text_append (result, mini_toc_or_auto_menu.text);
+          free (mini_toc_or_auto_menu.text);
+          return;
+        }
     }
-  else
+
+  lookup_extra_integer (element, "section_level", &status);
+  level_corrected_cmd = cmd;
+  if (status >= 0)
     {
-      OTXI_ALL_CONVERT_TEXT ( , )
+      /* if the level was changed, use a consistent command name */
+      level_corrected_cmd = section_level_adjusted_command_name (element);
+      if (level_corrected_cmd != cmd)
+        {
+          xasprintf (&level_set_class, "%s-level-set-%s",
+                     builtin_command_name(cmd),
+                     builtin_command_name (level_corrected_cmd));
+        }
     }
 
-  if (contents_used_to_be_freed)
-    free (content_used);
-}
-
-void
-default_format_footnotes_segment (CONVERTER *self, TEXT *result)
-{
-  char *class_base;
-  char *attribute_class;
-  char *class;
-  STRING_LIST *classes;
-  ELEMENT *footnote_heading_tree;
-  char *footnote_heading;
-  int level;
-  char *formatted_heading;
-  char *foot_lines = call_formatting_function_format_footnotes_sequence (self);
+ /* find the section starting here, can be through the associated node
+    preceding the section, or the section itself */
 
-  if (!foot_lines || !strlen (foot_lines))
+  if (cmd == CM_node)
     {
-      free (foot_lines);
-      return;
+      opening_section
+       = lookup_extra_element (element, "associated_section");
+      if (opening_section)
+        level_corrected_opening_section_cmd
+          = section_level_adjusted_command_name (opening_section);
     }
+  else
+    {
+      ELEMENT *associated_node
+        = lookup_extra_element (element, "associated_node");
 
-  classes = (STRING_LIST *) malloc (sizeof (STRING_LIST));
-  memset (classes, 0, sizeof (STRING_LIST));
-
-  class_base = special_unit_info (self, SUI_type_class,
-                                  "footnotes");
-  xasprintf (&class, "%s-segment", class_base);
-
-  add_string (class, classes);
-  free (class);
-  attribute_class = html_attribute_class (self, "div", classes);
-  clear_strings_list (classes);
+       /* if there is an associated node, it is not a section opening
+        the section was opened before when the node was encountered */
+      if (!associated_node
+          /* to avoid *heading* @-commands */
+          && (builtin_command_data[cmd].flags & CF_root))
+        {
+          opening_section = element;
+          level_corrected_opening_section_cmd = level_corrected_cmd;
+        }
+    }
 
-  text_append (result, attribute_class);
-  free (attribute_class);
+  /*
+   could use empty args information also, to avoid calling command_text
+   my $empty_heading = (!scalar(@$args) or !defined($args->[0]));
+   */
 
-  text_append_n (result, ">\n", 2);
 
-  if (self->conf->DEFAULT_RULE && strlen (self->conf->DEFAULT_RULE))
-    {
-      text_append (result, self->conf->DEFAULT_RULE);
-      text_append_n (result, "\n", 1);
-    }
+ /* heading not defined may happen if the command is a @node, for example
+    if there is an error in the node. */
+  heading = html_command_text (self, element, 0);
 
-  footnote_heading_tree = special_unit_info_tree (self,
-                              SUIT_type_heading, "footnotes");
-  if (footnote_heading_tree)
+  if (cmd == CM_node)
     {
-      footnote_heading = html_convert_tree (self, footnote_heading_tree,
-                                    "convert footnotes special heading");
+      ELEMENT *associated_section
+        = lookup_extra_element (element, "associated_section");
+      char *normalized = lookup_extra_string (element, "normalized");
+      if ((!output_unit
+           || (output_unit->unit_command
+               && output_unit->unit_command == element
+               && !associated_section))
+          && normalized)
+        {
+          if (!strcmp (normalized, "Top"))
+            heading_level = 0;
+          else
+            {
+              int use_next_heading = 0;
+              if (self->conf->USE_NEXT_HEADING_FOR_LONE_NODE > 0)
+                {
+                  ELEMENT *next_heading
+                    = find_root_command_next_heading_command (element,
+                                                        self->expanded_formats,
+                    (!strcmp (self->conf->CONTENTS_OUTPUT_LOCATION, "inline")),
+                            0);
+                  if (next_heading)
+                    use_next_heading = 1;
+                }
+              if (!use_next_heading)
+                /* use node */
+                heading_level = 3;
+            }
+        }
     }
   else
     {
-      footnote_heading = "";
+      int status;
+      int level = lookup_extra_integer (element, "section_level", &status);
+      if (status >= 0)
+        {
+          heading_level = level;
+        }
+      else
+        {
+          heading_level = section_level (element);
+        }
     }
+  do_heading = (heading && strlen (heading) && heading_level >= 0);
 
-  level = self->conf->FOOTNOTE_END_HEADER_LEVEL;
+  /* if set, the id is associated to the heading text */
+  if (opening_section)
+    {
+      char *class;
+      STRING_LIST *classes;
+      char *attribute_class;
+      int status;
+      int level
+        = lookup_extra_integer (opening_section, "section_level", &status);
+      STRING_LIST *closed_strings;
 
-  xasprintf (&class, "%s-heading", class_base);
+      closed_strings = html_close_registered_sections_level (self, level);
 
-  add_string (class, classes);
-  free (class);
+      if (closed_strings->number)
+        {
+          int i;
+          for (i = 0; i < closed_strings->number; i++)
+            {
+              text_append (result, closed_strings->list[i]);
+              free (closed_strings->list[i]);
+            }
+        }
+      free (closed_strings);
 
-  formatted_heading
-    = call_formatting_function_format_heading_text (self, 0, classes,
-                                                    footnote_heading,
-                                                    level, 0, 0, 0);
-  destroy_strings_list (classes);
-  text_append (result, formatted_heading);
-  text_append_n (result, "\n", 1);
+      html_register_opened_section_level (self, level, "</div>\n");
 
-  free (formatted_heading);
+    /* use a specific class name to mark that this is the start of
+       the section extent. It is not necessary where the section is. */
 
-  if (footnote_heading_tree)
-    free (footnote_heading);
+      classes = (STRING_LIST *) malloc (sizeof (STRING_LIST));
+      memset (classes, 0, sizeof (STRING_LIST));
 
-  text_append (result, foot_lines);
-  free (foot_lines);
-  text_append (result, "</div>\n");
-}
+      xasprintf (&class, "%s-level-extent",
+                 builtin_command_name (level_corrected_opening_section_cmd));
 
-void
-format_footnotes_segment (CONVERTER *self, TEXT *result)
-{
-  if (self->formatting_references[FR_format_footnotes_segment].status
-                                             == FRS_status_default_set)
+      add_string (class, classes);
+      free (class);
+      attribute_class = html_attribute_class (self, "div", classes);
+      clear_strings_list (classes);
+
+      text_append (result, attribute_class);
+      free (attribute_class);
+
+      if (element_id && strlen (element_id))
+        text_printf (result, " id=\"%s\"", element_id);
+      text_append (result, ">\n");
+   }
+  else if (element_id && strlen (element_id))
+   {
+     if (element_header && strlen (element_header))
+       {
+         char *anchor;
+     /* case of a @node without sectioning command and with a header.
+        put the node element anchor before the header.
+        Set the class name to the command name if there is no heading,
+        else the class will be with the heading element. */
+
+         char *id_class = 0;
+         if (do_heading)
+           {
+             xasprintf (&id_class, "%s-id", builtin_command_name (cmd));
+           }
+         else
+           id_class = builtin_command_name (cmd);
+
+         anchor
+            = call_formatting_function_format_separate_anchor (self,
+                                                    element_id, id_class);
+         text_append (result, anchor);
+         free (anchor);
+
+         if (do_heading)
+           free (id_class);
+       }
+     else
+       heading_id = element_id;
+   }
+
+  if (element_header)
     {
-      default_format_footnotes_segment (self, result);
+      text_append (result, element_header);
+      free (element_header);
     }
-  else
+
+  if (do_heading)
     {
-      char *footnotes_segment
-        = call_formatting_function_format_footnotes_segment (self);
-      if (footnotes_segment)
+      STRING_LIST *heading_classes;
+      if (self->conf->TOC_LINKS > 0
+          && (builtin_command_data[cmd].flags & CF_root)
+          && (builtin_command_data[cmd].flags & CF_sectioning_heading))
         {
-          text_append (result, footnotes_segment);
-          free (footnotes_segment);
+          char *content_href = html_command_contents_href (self, element,
+                                                           CM_contents, 0);
+          if (content_href)
+            {
+              if (strlen (content_href))
+                {
+                  char *heading_tmp = strdup (heading);
+                  free (heading);
+                  xasprintf (&heading, "<a href=\"%s\">%s</a>",
+                                       content_href, heading_tmp);
+                  free (heading_tmp);
+                }
+              free (content_href);
+            }
         }
-    }
-}
 
-void
-convert_table_term_type (CONVERTER *self, const enum element_type type,
-                        const ELEMENT *element, const char *content,
-                        TEXT *result)
-{
-  if (content)
-    {
-      text_append (result, "<dt>");
-      text_append (result, content);
-    }
-}
+      heading_classes = (STRING_LIST *) malloc (sizeof (STRING_LIST));
+      memset (heading_classes, 0, sizeof (STRING_LIST));
+      add_string (builtin_command_name (level_corrected_cmd), heading_classes);
+      if (level_set_class)
+        add_string (level_set_class, heading_classes);
+      if (html_in_preformatted_context (self))
+        {
+          char *attribute_class;
+          char *id_str = 0;
+          if (heading_id)
+            {
+              xasprintf (&id_str, " id=\"%s\"", heading_id);
+            }
+          else
+            id_str = strdup ("");
 
-void
-convert_row_type (CONVERTER *self, const enum element_type type,
-                  const ELEMENT *element, const char *content,
-                  TEXT *result)
-{
-  if (html_in_string (self))
+          attribute_class = html_attribute_class (self, "strong",
+                                                  heading_classes);
+          text_append (result, attribute_class);
+          free (attribute_class);
+          text_printf (result, "%s>%s</strong>\n", id_str, heading);
+
+          free (id_str);
+        }
+      else
+        {
+          char *formatted_heading
+            = call_formatting_function_format_heading_text (self,
+                    builtin_command_name (level_corrected_cmd),
+                    heading_classes, heading,
+                    heading_level + self->conf->CHAPTER_HEADER_LEVEL -1,
+                    heading_id, element, element_id);
+          text_append (result, formatted_heading);
+          free (formatted_heading);
+        }
+      destroy_strings_list (heading_classes);
+    }
+  else if (heading_id)
     {
-      if (content)
-        text_append (result, content);
+   /* case of a lone node and no header, and case of an empty @top */
+      char *anchor
+        = call_formatting_function_format_separate_anchor (self,
+                            heading_id, builtin_command_name(cmd));
+      text_append (result, anchor);
+      free (anchor);
     }
 
-  if (!content || content[strspn (content, whitespace_chars)] == '\0')
-    return;
-  else
-    {
-      text_append (result, "<tr>");
-      text_append (result, content);
-      text_append (result, "</tr>");
+  free (heading);
+  free (level_set_class);
 
-      if (element->contents.number > 0
-          && element->contents.list[0]->cmd != CM_headitem)
-      /* if headitem, end of line added in _convert_multitable_head_type */
-        text_append (result, "\n");
-    }
+  if (content)
+    text_append (result, content);
+
+  text_append (result, tables_of_contents.text);
+  free (tables_of_contents.text);
+  text_append (result, mini_toc_or_auto_menu.text);
+  free (mini_toc_or_auto_menu.text);
 }
 
 void
-convert_w_command (CONVERTER *self, const enum command_id cmd,
+convert_contents_command (CONVERTER *self, const enum command_id cmd,
                     const ELEMENT *element,
                     const HTML_ARGS_FORMATTED *args_formatted,
                     const char *content, TEXT *result)
 {
-  if (args_formatted && args_formatted->number > 0)
-    {
-      if (args_formatted->args[0].formatted[AFT_type_normal])
-        text_append (result,
-                     args_formatted->args[0].formatted[AFT_type_normal]);
-    }
-  if (!html_in_string (self))
+  if (html_in_string (self))
+    return;
+
+  enum command_id used_cmd;
+
+  if (cmd == CM_summarycontents)
+    used_cmd = CM_shortcontents;
+  else
+    used_cmd = cmd;
+
+  set_informative_command_value (self->conf, element);
+
+  if (!strcmp (self->conf->CONTENTS_OUTPUT_LOCATION, "inline")
+      && ((used_cmd == CM_contents && self->conf->contents > 0)
+          || (used_cmd == CM_shortcontents && self->conf->shortcontents > 0))
+      && self->document->sections_list
+      && self->document->sections_list->number > 1)
     {
-      text_append (result, "<!-- /@w -->");
+      char *contents = contents_inline_element(self, used_cmd, element);
+      if (contents)
+        {
+          text_append (result, contents);
+          free (contents);
+        }
     }
 }
 
+
 /* associate command to the C function implementing the conversion */
 static COMMAND_INTERNAL_CONVERSION commands_internal_conversion_table[] = {
   {CM_w, &convert_w_command},
+  {CM_contents, &convert_contents_command},
+  {CM_shortcontents, &convert_contents_command},
+  {CM_summarycontents, &convert_contents_command},
+  {CM_node, convert_heading_command},
+  {CM_section, convert_heading_command},
+  /*
+  {CM_heading, convert_heading_command},
+   */
   {0, 0},
 };
 
@@ -4104,10 +5570,8 @@ static TYPE_INTERNAL_CONVERSION 
types_internal_conversion_table[] = {
 
 /* associate type to the C function implementing the opening */
 static TYPE_INTERNAL_OPEN types_internal_open_table[] = {
-  /*
   {ET_paragraph, &open_inline_container_type},
   {ET_preformatted, &open_inline_container_type},
-   */
   {0, 0},
 };
 
@@ -4763,6 +6227,9 @@ html_converter_initialize (CONVERTER *self)
                                   (&self->special_unit_body_formatting[i],
           self->special_unit_varieties.list[i], &self->special_unit_body[i]);
     }
+
+  qsort (self->htmlxref.list, self->htmlxref.number,
+         sizeof (HTMLXREF_MANUAL), compare_htmlxref_manual);
 }
 
 /* called in the end of html_converter_prepare_output_sv */
@@ -4808,6 +6275,22 @@ reset_html_targets (CONVERTER *self, HTML_TARGET_LIST 
*targets)
 void
 html_initialize_output_state (CONVERTER *self, char *context)
 {
+  /* set the htmlxref type split of the document */
+  self->document_htmlxref_split_type = htmlxref_split_type_mono;
+
+  if (self->conf->SPLIT && strlen (self->conf->SPLIT))
+    {
+      int i;
+      for (i = 1; i < htmlxref_split_type_chapter+1; i++)
+        {
+          if (!strcmp (self->conf->SPLIT, htmlxref_split_type_names[i]))
+            {
+              self->document_htmlxref_split_type = i;
+              break;
+            }
+        }
+    }
+
   /* directions */
   memset (self->global_units_directions, 0,
     (D_Last + self->special_unit_varieties.number+1) * sizeof (OUTPUT_UNIT));
@@ -5030,6 +6513,18 @@ html_free_converter (CONVERTER *self)
       free (self->special_unit_info[i]);
     }
 
+  for (i = 0; i < self->htmlxref.number; i++)
+    {
+      int j;
+      HTMLXREF_MANUAL *htmlxref_manual = &self->htmlxref.list[i];
+      free (htmlxref_manual->manual);
+      for (j = 0; j < htmlxref_split_type_chapter +1; j++)
+        {
+          free (htmlxref_manual->urlprefix[j]);
+        }
+    }
+  free (self->htmlxref.list);
+
   free (self->no_arg_formatted_cmd.list);
 
   free (self->pending_closes.stack);
@@ -5037,13 +6532,15 @@ html_free_converter (CONVERTER *self)
 
   free (self->associated_inline_content.list);
 
+  free_strings_list (&self->shared_conversion_state_integer);
+
   destroy_associated_info (&self->shared_conversion_state.integers);
 
   free (self->no_arg_formatted_cmd_translated.list);
   free (self->reset_target_commands.list);
   free (self->file_changed_counter.list);
 
-  free (self->referred_command_stack.list);
+  free (self->referred_command_stack.stack);
 
   free (self->html_document_context.stack);
 
@@ -5261,18 +6758,18 @@ html_translate_names (CONVERTER *self)
                ELEMENT *command = special_unit->unit_command;
                if (command)
                  {
-                   HTML_TARGET *target
+                   HTML_TARGET *target_info
                      = find_element_target (&self->html_targets, command);
-                   if (target)
+                   if (target_info)
                      {
        /* the tree is a reference to special_unit_info_tree, so it should
           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->tree);
-                       free (target->command_text[HCTT_string]);
-                       target->command_text[HCTT_string] = 0;
-                       free (target->command_text[HCTT_text]);
-                       target->command_text[HCTT_text] = 0;
+                       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;
                        /* gather elements to pass information to perl */
                        add_to_element_list (&self->reset_target_commands,
                                             command);
@@ -6546,7 +8043,7 @@ html_prepare_title_titlepage (CONVERTER *self, int 
output_units_descriptor,
   title_titlepage
     = call_formatting_function_format_title_titlepage (self);
   self->title_titlepage = title_titlepage;
-  memset (&self->current_filename, 0, sizeof (CURRENT_FILE_INFO));
+  memset (&self->current_filename, 0, sizeof (FILE_NUMBER_NAME));
   self->modified_state |= HMSF_current_filename;
 }
 
@@ -6682,7 +8179,7 @@ html_convert_output (CONVERTER *self, const ELEMENT *root,
               unit_nr++;
             }
         }
-      memset (&self->current_filename, 0, sizeof (CURRENT_FILE_INFO));
+      memset (&self->current_filename, 0, sizeof (FILE_NUMBER_NAME));
       self->modified_state |= HMSF_current_filename;
     }
 
diff --git a/tp/Texinfo/XS/convert/convert_html.h 
b/tp/Texinfo/XS/convert/convert_html.h
index 950c61a086..a3af23ab68 100644
--- a/tp/Texinfo/XS/convert/convert_html.h
+++ b/tp/Texinfo/XS/convert/convert_html.h
@@ -15,6 +15,7 @@ extern char *html_formatting_reference_names[];
 
 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];
 
 void html_format_init (void);
 
diff --git a/tp/Texinfo/XS/convert/converter.c 
b/tp/Texinfo/XS/convert/converter.c
index 1c9c7fb4e1..e28a08b71a 100644
--- a/tp/Texinfo/XS/convert/converter.c
+++ b/tp/Texinfo/XS/convert/converter.c
@@ -580,6 +580,7 @@ free_generic_converter (CONVERTER *self)
       destroy_translated_commands (self->translated_commands);
     }
 
+  free (self->output_format);
   free (self->expanded_formats);
 
   if (self->init_conf)
@@ -649,7 +650,7 @@ xml_protect_text (const char *text, TEXT *result)
 }
 
 ELEMENT *
-float_type_number (CONVERTER *self, ELEMENT *float_e)
+float_type_number (CONVERTER *self, const ELEMENT *float_e)
 {
   int have_float_number;
   ELEMENT *tree = 0;
diff --git a/tp/Texinfo/XS/convert/converter.h 
b/tp/Texinfo/XS/convert/converter.h
index eef23706ad..0fb4de449d 100644
--- a/tp/Texinfo/XS/convert/converter.h
+++ b/tp/Texinfo/XS/convert/converter.h
@@ -89,7 +89,7 @@ void free_comma_index_subentries_tree (ELEMENT_LIST *element);
 
 char *top_node_filename (CONVERTER *self, char *document_name);
 
-ELEMENT *float_type_number (CONVERTER *self, ELEMENT *float_e);
+ELEMENT *float_type_number (CONVERTER *self, const ELEMENT *float_e);
 
 void initialize_output_units_files (CONVERTER *self);
 size_t set_output_unit_file (CONVERTER *self, OUTPUT_UNIT *output_unit,
diff --git a/tp/Texinfo/XS/convert/get_html_perl_info.c 
b/tp/Texinfo/XS/convert/get_html_perl_info.c
index 37e9026004..b9152f7f39 100644
--- a/tp/Texinfo/XS/convert/get_html_perl_info.c
+++ b/tp/Texinfo/XS/convert/get_html_perl_info.c
@@ -140,6 +140,7 @@ html_converter_initialize_sv (SV *converter_sv,
   HV *default_types_conversion_hv;
   HV *default_css_string_types_conversion_hv;
   HV *default_output_units_conversion_hv;
+  SV **htmlxref_sv;
   SV **formatting_function_sv;
   SV **sorted_special_unit_varieties_sv;
   SV **no_arg_commands_formatting_sv;
@@ -176,6 +177,55 @@ html_converter_initialize_sv (SV *converter_sv,
     = (HV *)SvRV (default_css_string_formatting_references);
 
 #define FETCH(key) key##_sv = hv_fetch (converter_hv, #key, strlen(#key), 0);
+  FETCH(htmlxref)
+
+  if (htmlxref_sv)
+    {
+      I32 hv_number;
+      I32 i;
+      HV *htmlxref_hv = (HV *) SvRV (*htmlxref_sv);
+
+      hv_number = hv_iterinit (htmlxref_hv);
+
+      converter->htmlxref.number = hv_number;
+
+      if (hv_number > 0)
+        {
+          converter->htmlxref.list = (HTMLXREF_MANUAL *)
+            malloc (hv_number * sizeof (HTMLXREF_MANUAL));
+          memset (converter->htmlxref.list, 0,
+                  hv_number * sizeof (HTMLXREF_MANUAL));
+
+          for (i = 0; i < hv_number; i++)
+            {
+              int j;
+              HTMLXREF_MANUAL *htmlxref_manual = &converter->htmlxref.list[i];
+              HE *next = hv_iternext (htmlxref_hv);
+              SV *selector_sv = hv_iterkeysv (next);
+              char *selector = (char *) SvPVutf8_nolen (selector_sv);
+              SV *split_type_sv = HeVAL(next);
+              HV *split_type_hv = (HV *) SvRV (split_type_sv);
+
+              htmlxref_manual->manual = strdup (selector);
+
+              for (j = 0; j < htmlxref_split_type_chapter +1; j++)
+                {
+                  const char *split_type_name = htmlxref_split_type_names[j];
+                  SV **urlprefix_sv = hv_fetch (split_type_hv, split_type_name,
+                                                strlen (split_type_name), 0);
+                  /* can be undef if there is an entry in the htmlxref.cnf file
+                     without the urlprefix.  We ignore completely, in perl
+                     it is ignored later on when checking an external href */
+                  if (urlprefix_sv && SvOK (*urlprefix_sv))
+                    {
+                      char *urlprefix = SvPVutf8_nolen (*urlprefix_sv);
+                      htmlxref_manual->urlprefix[j] = strdup (urlprefix);
+                    }
+                }
+            }
+        }
+    }
+
   FETCH(formatting_function);
 
   /* no need to check if it exists */
diff --git a/tp/Texinfo/XS/main/converter_types.h 
b/tp/Texinfo/XS/main/converter_types.h
index 949a006e58..02f1e81b6d 100644
--- a/tp/Texinfo/XS/main/converter_types.h
+++ b/tp/Texinfo/XS/main/converter_types.h
@@ -168,6 +168,15 @@ enum html_command_text_type {
    HCTT_string_nonumber, /* not sure that it is set/used */
 };
 
+enum htmlxref_split_type {
+   htmlxref_split_type_none = -1,
+
+   htmlxref_split_type_mono,
+   htmlxref_split_type_node,
+   htmlxref_split_type_section,
+   htmlxref_split_type_chapter,
+};
+
 typedef struct {
     enum command_id *stack;
     size_t top;   /* One above last pushed command. */
@@ -201,6 +210,17 @@ typedef struct {
     size_t space;
 } INTEGER_STACK;
 
+typedef struct ELEMENT_STACK {
+    const ELEMENT **stack;
+    size_t top;
+    size_t space;
+} ELEMENT_STACK;
+
+typedef struct FILE_NUMBER_NAME {
+    size_t file_number;
+    char *filename;
+} FILE_NUMBER_NAME;
+
 typedef struct VARIETY_DIRECTION_INDEX {
     char *special_unit_variety;
     int direction_index;
@@ -218,10 +238,12 @@ typedef struct HTML_TARGET {
     char *command_text[HCTT_string_nonumber+1];
     TREE_ADDED_ELEMENTS tree;
     TREE_ADDED_ELEMENTS tree_nonumber;
-    char *filename;
+    FILE_NUMBER_NAME file_number_name;
+    int filename_set;
+    ELEMENT *root_element_command;
+    int root_element_command_set;
     /*
     ELEMENT *node_command;
-    ELEMENT *root_element_command;
     */
 } HTML_TARGET;
 
@@ -337,11 +359,6 @@ typedef struct FILE_NAME_PATH_COUNTER_LIST {
     FILE_NAME_PATH_COUNTER *list;
 } FILE_NAME_PATH_COUNTER_LIST;
 
-typedef struct CURRENT_FILE_INFO {
-    size_t file_number;
-    char *filename;
-} CURRENT_FILE_INFO;
-
 typedef struct FILE_STREAM {
     char *file_path;
     FILE *stream;
@@ -560,6 +577,27 @@ typedef struct HTML_ASSOCIATED_INLINE_CONTENT_LIST {
     HTML_ASSOCIATED_INLINE_CONTENT *list;
 } HTML_ASSOCIATED_INLINE_CONTENT_LIST;
 
+typedef struct HTMLXREF_MANUAL {
+    char *manual;
+    char *urlprefix[htmlxref_split_type_chapter +1];
+} HTMLXREF_MANUAL;
+
+typedef struct HTMLXREF_MANUAL_LIST {
+    size_t number;
+    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 CONVERTER {
     int converter_descriptor;
   /* perl converter. This should be HV *hv,
@@ -568,6 +606,7 @@ typedef struct CONVERTER {
 
     struct OPTIONS *conf;
     struct OPTIONS *init_conf;
+    char *output_format;
     EXPANDED_FORMAT *expanded_formats;
     TRANSLATED_COMMAND *translated_commands;
 
@@ -613,6 +652,7 @@ typedef struct CONVERTER {
     FORMATTING_REFERENCE *special_unit_body;
     STRING_LIST special_unit_varieties;
     char **special_unit_info[SUI_type_heading+1];
+    HTMLXREF_MANUAL_LIST htmlxref;
     TYPE_CONVERSION_FUNCTION type_conversion_function[TXI_TREE_TYPES_NUMBER];
     TYPE_CONVERSION_FUNCTION 
css_string_type_conversion_function[TXI_TREE_TYPES_NUMBER];
     TYPE_OPEN_FUNCTION type_open_function[TXI_TREE_TYPES_NUMBER];
@@ -625,6 +665,7 @@ typedef struct CONVERTER {
     HTML_COMMAND_CONVERSION 
html_command_conversion[BUILTIN_CMD_NUMBER][HCC_type_css_string+1];
 
     /* set for a document */
+    enum htmlxref_split_type document_htmlxref_split_type;
     const OUTPUT_UNIT **global_units_directions;
     SPECIAL_UNIT_DIRECTION *special_units_direction_name;
     ELEMENT **special_unit_info_tree[SUIT_type_heading+1];
@@ -639,6 +680,7 @@ typedef struct CONVERTER {
               each position corresponding to an output unit. */
     size_t *special_unit_file_indices;  /* same for special output units */
     PAGES_CSS_LIST page_css;
+    HTMLXREF_MANUAL_ELEMENT_WARNED_LIST check_htmlxref_already_warned;
 
     /* state only in C converter */
     unsigned long modified_state; /* specifies which perl state to rebuild */
@@ -652,6 +694,7 @@ typedef struct CONVERTER {
     ARRAY_INDEX_LIST file_changed_counter;  /* index of files in
                                  output_unit_files with changed counter */
     HTML_ADDED_TARGET_LIST added_targets; /* targets added */
+    STRING_LIST shared_conversion_state_integer; /* modified */
     /* next three allow to switch from normal HTML formatting to css strings
        formatting */
     FORMATTING_REFERENCE *current_formatting_references;
@@ -667,8 +710,8 @@ typedef struct CONVERTER {
     HTML_DOCUMENT_CONTEXT_STACK html_document_context;
     STRING_STACK multiple_pass;
     STRING_STACK pending_closes;
-    CURRENT_FILE_INFO current_filename;
-    ELEMENT_LIST referred_command_stack;
+    FILE_NUMBER_NAME current_filename;
+    ELEMENT_STACK referred_command_stack;
     HTML_SHARED_CONVERSION_STATE shared_conversion_state;
     HTML_INLINE_CONTENT_STACK pending_inline_content;
     HTML_PENDING_FOOTNOTE_STACK pending_footnotes;
diff --git a/tp/Texinfo/XS/main/get_perl_info.c 
b/tp/Texinfo/XS/main/get_perl_info.c
index fe58c6f056..6b08197c1a 100644
--- a/tp/Texinfo/XS/main/get_perl_info.c
+++ b/tp/Texinfo/XS/main/get_perl_info.c
@@ -341,12 +341,14 @@ set_translated_commands (CONVERTER *converter, HV *hv_in)
     }
 }
 
+#define FETCH(key) key##_sv = hv_fetch (hv_in, #key, strlen(#key), 0);
 void
 converter_initialize (SV *converter_sv, CONVERTER *converter)
 {
   HV *hv_in;
-  SV **converter_conf_sv;
+  SV **conf_sv;
   SV **converter_init_conf_sv;
+  SV **output_format_sv;
   DOCUMENT *document;
 
   dTHX;
@@ -356,18 +358,15 @@ converter_initialize (SV *converter_sv, CONVERTER 
*converter)
   document = get_sv_document_document (converter_sv, 0);
   converter->document = document;
 
-  converter_conf_sv = hv_fetch (hv_in, "conf",
-                                strlen ("conf"), 0);
+  FETCH(conf)
 
-  if (converter_conf_sv && SvOK (*converter_conf_sv))
+  if (conf_sv && SvOK (*conf_sv))
     {
       converter->conf
-         = copy_sv_options (*converter_conf_sv);
+         = copy_sv_options (*conf_sv);
     }
 
-  converter_init_conf_sv
-    = hv_fetch (hv_in, "converter_init_conf",
-                strlen ("converter_init_conf"), 0);
+  FETCH(converter_init_conf)
 
   if (converter_init_conf_sv && SvOK (*converter_init_conf_sv))
     {
@@ -375,6 +374,14 @@ converter_initialize (SV *converter_sv, CONVERTER 
*converter)
          = copy_sv_options (*converter_init_conf_sv);
     }
 
+  FETCH(output_format)
+
+  if (output_format_sv && SvOK (*output_format_sv))
+    {
+      converter->output_format
+         = strdup (SvPVutf8_nolen (*output_format_sv));
+    }
+
   set_translated_commands (converter, hv_in);
 
   get_expanded_formats (hv_in, &converter->expanded_formats);
@@ -385,8 +392,8 @@ CONVERTER *
 set_output_converter_sv (SV *sv_in, char *warn_string)
 {
   HV *hv_in;
-  SV **converter_conf_sv;
-  SV **converter_init_conf_sv;
+  SV **conf_sv;
+  SV **output_init_conf_sv;
   CONVERTER *converter = 0;
 
   dTHX;
@@ -394,34 +401,34 @@ set_output_converter_sv (SV *sv_in, char *warn_string)
   converter = get_sv_converter (sv_in, warn_string);
 
   hv_in = (HV *)SvRV (sv_in);
-  converter_conf_sv = hv_fetch (hv_in, "conf",
-                                   strlen ("conf"), 0);
 
-  if (converter_conf_sv)
+  FETCH(conf)
+
+  if (conf_sv)
     {
       if (converter->conf)
         free_options (converter->conf);
       free (converter->conf);
 
       converter->conf
-         = copy_sv_options (*converter_conf_sv);
+         = copy_sv_options (*conf_sv);
     }
 
-  converter_init_conf_sv = hv_fetch (hv_in, "output_init_conf",
-                                   strlen ("output_init_conf"), 0);
+  FETCH(output_init_conf)
 
-  if (converter_init_conf_sv && SvOK(*converter_init_conf_sv))
+  if (output_init_conf_sv && SvOK(*output_init_conf_sv))
     {
       if (converter->init_conf)
         free_options (converter->init_conf);
       free (converter->init_conf);
 
       converter->init_conf
-         = copy_sv_options (*converter_init_conf_sv);
+         = copy_sv_options (*output_init_conf_sv);
     }
 
   return converter;
 }
+#undef FETCH
 
 /* code in comments allow to sort the index names to have a fixed order
    in the data structure.  Not clear that it is useful or not, not enabled



reply via email to

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