texinfo-commits
[Top][All Lists]
Advanced

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

[no subject]


From: Patrice Dumas
Date: Fri, 4 Oct 2024 11:08:47 -0400 (EDT)

branch: master
commit 0cca30902bcc1e2fd380081084c95c85b9cdd6bf
Author: Patrice Dumas <pertusus@free.fr>
AuthorDate: Wed Aug 14 13:38:57 2024 +0200

    * tp/Texinfo/XS/convert/html_converter_types.h
    (SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD, COMMAND_ARGS_SPECIFICATION)
    (MAX_COMMAND_ARGS_NR, html_command_args_flags)
    (special_list_mark_css_string_no_arg_command, no_arg_formatted_cmd)
    (style_formatted_cmd, accent_cmd, format_raw_cmd),
    tp/Texinfo/XS/convert/convert_html.c: rename command_args_flags as
    html_command_args_flags, substitute_html_non_breaking_space as
    html_substitute_non_breaking_space.  Declare in
    html_converter_types.h.
    
    * tp/Texinfo/XS/convert/convert_html.h (ROOT_AND_UNIT),
    tp/Texinfo/XS/convert/convert_html.c (html_get_tree_root_element)
    (html_after_escaped_characters, html_substitute_non_breaking_space)
    (html_special_unit_info, html_default_format_protect_text)
    (html_complete_no_arg_commands_formatting): declare ROOT_AND_UNIT and
    html_get_tree_root_element in header file.  Rename
    substitute_html_non_breaking_space as
    html_substitute_non_breaking_space.  Prepend html_ for some functions
    and declare them in header file.  Update callers.
    
    * tp/Texinfo/XS/convert/convert_html.h,
    tp/Texinfo/XS/convert/convert_html.c: prepend html_ for
    css_string_convert*, convert_*command, command_conversion_external,
    command_open_external, open_*command, type_conversion_external,
    convert_*type, css_string_convert_*type, type_open_external,
    output_unit_conversion_external, convert_*unit,
    special_unit_body_formatting_external, default_format_special_body_*
    and declare in header file.
    
    * tp/Texinfo/XS/Makefile.am (libtexinfo_convert_la_SOURCES),
    tp/Texinfo/XS/convert/html_prepare_converter.c (html_format_setup)
    (html_converter_customize, html_initialize_output_state)
    (new_directions_strings_type, new_directions_strings_translated_type)
    (CMD_VARIETY, COMMAND_INTERNAL_CONVERSION, COMMAND_INTERNAL_OPEN)
    (TYPE_INTERNAL_CONVERSION, TYPE_INTERNAL_OPEN)
    (OUTPUT_UNIT_INTERNAL_CONVERSION)
    (SPECIAL_UNIT_BODY_INTERNAL_CONVERSION)
    (base_default_css_element_class_styles, default_code_types)
    (default_pre_class_types, quoted_style_commands, ignored_types)
    (htmlxref_split_type_names, command_special_unit_variety)
    (default_no_arg_commands_formatting, special_characters_formatting)
    (default_style_commands_formatting, html_commands_data)
    (default_commands_args, register_special_unit, SPECIAL_UNIT_ORDER)
    (compare_special_units, prepare_special_units)
    (additional_format_context_cmd, HTML_align_cmd)
    (register_format_context_command, register_pre_class_command)
    (conf_for_special_units, html_prepare_conversion_units)
    (compare_index_name, html_prepare_conversion_units): move code related
    to converter and conversion preparation to a new file,
    html_prepare_converter.c.
    
    * tp/Texinfo/XS/convert/convert_html.c,
    tp/Texinfo/XS/convert/html_prepare_converter.c
    (html_converter_init_special_unit): move
    html_converter_init_special_unit to html_prepare_converter.c.
---
 ChangeLog                                      |    58 +
 tp/Texinfo/XS/Makefile.am                      |     2 +
 tp/Texinfo/XS/convert/ConvertXS.xs             |     2 +-
 tp/Texinfo/XS/convert/convert_html.c           | 11278 +++++++++--------------
 tp/Texinfo/XS/convert/convert_html.h           |   219 +-
 tp/Texinfo/XS/convert/get_html_perl_info.c     |     2 +
 tp/Texinfo/XS/convert/html_converter_types.h   |    37 +
 tp/Texinfo/XS/convert/html_prepare_converter.c |  2421 +++++
 tp/Texinfo/XS/convert/html_prepare_converter.h |    20 +
 tp/Texinfo/XS/convert/texinfo.c                |     1 +
 10 files changed, 7199 insertions(+), 6841 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 66c4731179..6cef1f61f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,61 @@
+2024-08-14  Patrice Dumas  <pertusus@free.fr>
+
+       * tp/Texinfo/XS/convert/html_converter_types.h
+       (SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD, COMMAND_ARGS_SPECIFICATION)
+       (MAX_COMMAND_ARGS_NR, html_command_args_flags)
+       (special_list_mark_css_string_no_arg_command, no_arg_formatted_cmd)
+       (style_formatted_cmd, accent_cmd, format_raw_cmd),
+       tp/Texinfo/XS/convert/convert_html.c: rename command_args_flags as
+       html_command_args_flags, substitute_html_non_breaking_space as
+       html_substitute_non_breaking_space.  Declare in
+       html_converter_types.h.
+
+       * tp/Texinfo/XS/convert/convert_html.h (ROOT_AND_UNIT),
+       tp/Texinfo/XS/convert/convert_html.c (html_get_tree_root_element)
+       (html_after_escaped_characters, html_substitute_non_breaking_space)
+       (html_special_unit_info, html_default_format_protect_text)
+       (html_complete_no_arg_commands_formatting): declare ROOT_AND_UNIT and
+       html_get_tree_root_element in header file.  Rename
+       substitute_html_non_breaking_space as
+       html_substitute_non_breaking_space.  Prepend html_ for some functions
+       and declare them in header file.  Update callers.
+
+       * tp/Texinfo/XS/convert/convert_html.h,
+       tp/Texinfo/XS/convert/convert_html.c: prepend html_ for
+       css_string_convert*, convert_*command, command_conversion_external,
+       command_open_external, open_*command, type_conversion_external,
+       convert_*type, css_string_convert_*type, type_open_external,
+       output_unit_conversion_external, convert_*unit,
+       special_unit_body_formatting_external, default_format_special_body_*
+       and declare in header file.
+
+       * tp/Texinfo/XS/Makefile.am (libtexinfo_convert_la_SOURCES),
+       tp/Texinfo/XS/convert/html_prepare_converter.c (html_format_setup)
+       (html_converter_customize, html_initialize_output_state)
+       (new_directions_strings_type, new_directions_strings_translated_type)
+       (CMD_VARIETY, COMMAND_INTERNAL_CONVERSION, COMMAND_INTERNAL_OPEN)
+       (TYPE_INTERNAL_CONVERSION, TYPE_INTERNAL_OPEN)
+       (OUTPUT_UNIT_INTERNAL_CONVERSION)
+       (SPECIAL_UNIT_BODY_INTERNAL_CONVERSION)
+       (base_default_css_element_class_styles, default_code_types)
+       (default_pre_class_types, quoted_style_commands, ignored_types)
+       (htmlxref_split_type_names, command_special_unit_variety)
+       (default_no_arg_commands_formatting, special_characters_formatting)
+       (default_style_commands_formatting, html_commands_data)
+       (default_commands_args, register_special_unit, SPECIAL_UNIT_ORDER)
+       (compare_special_units, prepare_special_units)
+       (additional_format_context_cmd, HTML_align_cmd)
+       (register_format_context_command, register_pre_class_command)
+       (conf_for_special_units, html_prepare_conversion_units)
+       (compare_index_name, html_prepare_conversion_units): move code related
+       to converter and conversion preparation to a new file,
+       html_prepare_converter.c.
+
+       * tp/Texinfo/XS/convert/convert_html.c,
+       tp/Texinfo/XS/convert/html_prepare_converter.c
+       (html_converter_init_special_unit): move
+       html_converter_init_special_unit to html_prepare_converter.c.
+
 2024-10-04  Patrice Dumas  <pertusus@free.fr>
 
        * tp/Texinfo/XS/convert/html_conversion_state.h: declare
diff --git a/tp/Texinfo/XS/Makefile.am b/tp/Texinfo/XS/Makefile.am
index 01dc869f01..19a99f4aa5 100644
--- a/tp/Texinfo/XS/Makefile.am
+++ b/tp/Texinfo/XS/Makefile.am
@@ -394,6 +394,8 @@ C_libtexinfo_convert_sources = \
                        convert/html_converter_finish.h \
                        convert/html_conversion_state.c \
                        convert/html_conversion_state.h \
+                       convert/html_prepare_converter.c \
+                       convert/html_prepare_converter.h \
                        convert/texinfo.c \
                        convert/texinfo.h
 
diff --git a/tp/Texinfo/XS/convert/ConvertXS.xs 
b/tp/Texinfo/XS/convert/ConvertXS.xs
index 47132775b2..49cf4ee2ca 100644
--- a/tp/Texinfo/XS/convert/ConvertXS.xs
+++ b/tp/Texinfo/XS/convert/ConvertXS.xs
@@ -56,7 +56,7 @@
 #include "build_html_perl_state.h"
 #include "html_conversion_state.h"
 #include "convert_html.h"
-#include "get_converter_perl_info.h"
+#include "html_prepare_converter.h"
 #include "get_html_perl_info.h"
 
  /* See the NOTE in build_perl_info.c on use of functions related to
diff --git a/tp/Texinfo/XS/convert/convert_html.c 
b/tp/Texinfo/XS/convert/convert_html.c
index f4225e805d..31588db641 100644
--- a/tp/Texinfo/XS/convert/convert_html.c
+++ b/tp/Texinfo/XS/convert/convert_html.c
@@ -54,6 +54,7 @@
 #include "translations.h"
 /* for OTXI_UNICODE_TEXT_CASES utf8_from_string string_from_utf8 */
 #include "unicode.h"
+/* convert_to_text */
 #include "convert_to_text.h"
 #include "convert_utils.h"
 #include "call_perl_function.h"
@@ -68,91 +69,6 @@
 #include "html_conversion_state.h"
 #include "convert_html.h"
 
-typedef struct ROOT_AND_UNIT {
-    const OUTPUT_UNIT *output_unit;
-    const ELEMENT *root;
-} ROOT_AND_UNIT;
-
-typedef struct CMD_VARIETY {
-    enum command_id cmd;
-    char *variety;
-} CMD_VARIETY;
-
-typedef struct COMMAND_INTERNAL_CONVERSION {
-    enum command_id cmd;
-    void (* command_conversion) (struct CONVERTER *self,
-                                 const enum command_id cmd,
-                                 const ELEMENT *element,
-                                 const HTML_ARGS_FORMATTED *args_formatted,
-                                 const char *content, TEXT *result);
-} COMMAND_INTERNAL_CONVERSION;
-
-typedef struct COMMAND_INTERNAL_OPEN {
-    enum command_id cmd;
-    void (* command_open) (CONVERTER *self, const enum command_id cmd,
-                           const ELEMENT *element, TEXT *result);
-} COMMAND_INTERNAL_OPEN;
-
-typedef struct TYPE_INTERNAL_CONVERSION {
-    enum element_type type;
-    void (* type_conversion) (CONVERTER *self, const enum element_type type,
-                              const ELEMENT *element, const char *content,
-                              TEXT *result);
-} TYPE_INTERNAL_CONVERSION;
-
-typedef struct TYPE_INTERNAL_OPEN {
-    enum element_type type;
-    void (* type_open) (CONVERTER *self, const enum element_type type,
-                        const ELEMENT *element, TEXT *result);
-} TYPE_INTERNAL_OPEN;
-
-typedef struct OUTPUT_UNIT_INTERNAL_CONVERSION {
-    enum output_unit_type type;
-    void (* output_unit_conversion) (CONVERTER *self,
-                        const enum output_unit_type unit_type,
-                        const OUTPUT_UNIT *output_unit, const char *content,
-                        TEXT *result);
-} OUTPUT_UNIT_INTERNAL_CONVERSION;
-
-typedef struct SPECIAL_UNIT_BODY_INTERNAL_CONVERSION {
-    char *special_unit_variety;
-    void (* special_unit_body_formatting) (CONVERTER *self,
-                               const size_t special_unit_number,
-                               const char *special_unit_variety,
-                               const OUTPUT_UNIT *output_unit,
-                               TEXT *result);
-} SPECIAL_UNIT_BODY_INTERNAL_CONVERSION;
-
-/* in main/conversion_data.c */
-extern const CSS_SELECTOR_STYLE base_default_css_element_class_styles[];
-
-/* types that are in code style in the default case.  '_code' is not
-   a type that can appear in the tree built from Texinfo code, it is used
-   to format a tree fragment as if it was in a @code @-command.  */
-static enum element_type default_code_types[] = {
-  ET__code, 0,
-};
-
-const PRE_CLASS_TYPE_INFO default_pre_class_types[] = {
-  {ET_menu_comment, "menu-comment"},
-  {0, 0}
-};
-
-static enum command_id quoted_style_commands[] = {
-  CM_samp, 0
-};
-
-static enum element_type ignored_types[] = {
-    ET_ignorable_spaces_after_command,
-    ET_postamble_after_end,
-    ET_preamble_before_beginning,
-    ET_preamble_before_setfilename,
-    ET_spaces_at_end,
-    ET_spaces_before_paragraph,
-    ET_spaces_after_close_brace, 0
-};
-
-
 const char *html_global_unit_direction_names[] = {
   #define hgdt_name(name) #name,
    HTML_GLOBAL_DIRECTIONS_LIST
@@ -197,11 +113,6 @@ const char *direction_string_context_names[] =
   "normal", "string"
 };
 
-const char *htmlxref_split_type_names[htmlxref_split_type_chapter + 1] =
-{
-  "mono", "node", "section", "chapter"
-};
-
 const 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 },
@@ -209,87 +120,6 @@ const enum htmlxref_split_type 
htmlxref_entries[htmlxref_split_type_chapter + 1]
  { htmlxref_split_type_chapter, htmlxref_split_type_section, 
htmlxref_split_type_node, htmlxref_split_type_mono },
 };
 
-CMD_VARIETY command_special_unit_variety[] = {
-                                {CM_contents, "contents"},
-                                {CM_shortcontents, "shortcontents"},
-                                {CM_summarycontents, "shortcontents"},
-                                {CM_footnote, "footnotes"},
-                                {0, 0},
-};
-
-static HTML_NO_ARG_COMMAND_CONVERSION default_no_arg_commands_formatting
-                              [BUILTIN_CMD_NUMBER][NO_ARG_COMMAND_CONTEXT_NR];
-
-static HTML_STYLE_COMMAND_CONVERSION default_style_commands_formatting
-                               [BUILTIN_CMD_NUMBER][STYLE_COMMAND_CONTEXT_NR];
-
-/* used to set flags to non-zero with a flag that does nothing */
-#define F_AFT_none              0x0001
-
-#define F_AFT_normal            0x0002
-#define F_AFT_string            0x0004
-#define F_AFT_monospace         0x0008
-#define F_AFT_monospacetext     0x0010
-#define F_AFT_monospacestring   0x0020
-#define F_AFT_filenametext      0x0040
-#define F_AFT_url               0x0080
-#define F_AFT_raw               0x0100
-
-HTML_COMMAND_STRUCT html_commands_data[BUILTIN_CMD_NUMBER];
-
-/* should correspond to enum html_special_character */
-/* HTML textual entity, UTF-8 encoded, unicode point, HTML numeric entity */
-char *special_characters_formatting[SC_non_breaking_space+1][4] = {
-  {"&para;", "\xC2\xB6", "00B6", "&#182;"},
-  {"&lsquo;", "\xE2\x80\x98", "2018", "&#8216;"},
-  {"&rsquo;", "\xE2\x80\x99", "2019", "&#8217;"},
-  {"&bull;", "\xE2\x80\xA2", "2022", "&#8226;"},
-  {"&nbsp;", "\xC2\xA0", "00A0", "&#160;"},
-};
-
-/* in specification of args.  Number max +1 for a trailing 0 */
-#define MAX_COMMAND_ARGS_NR 6
-
-typedef struct COMMAND_ID_ARGS_SPECIFICATION {
-    enum command_id cmd;
-    unsigned long flags[MAX_COMMAND_ARGS_NR];
-} COMMAND_ID_ARGS_SPECIFICATION;
-
-static COMMAND_ID_ARGS_SPECIFICATION default_commands_args[] = {
-  {CM_anchor, {F_AFT_monospacestring}},
-  {CM_email, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal}},
-  {CM_footnote, {F_AFT_none}}, /* no flag */
-  {CM_printindex, {F_AFT_none}}, /* no flag */
-  {CM_uref, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal, F_AFT_normal}},
-  {CM_url, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal, F_AFT_normal}},
-  {CM_sp, {F_AFT_none}}, /* no flag */
-  {CM_inforef, {F_AFT_monospace, F_AFT_normal, F_AFT_filenametext}},
-  {CM_xref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, 
F_AFT_normal}},
-  {CM_pxref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, 
F_AFT_normal}},
-  {CM_ref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, 
F_AFT_normal}},
-  {CM_link, {F_AFT_monospace, F_AFT_normal, F_AFT_filenametext}},
-  {CM_image, {F_AFT_url | F_AFT_filenametext | F_AFT_monospacestring, 
F_AFT_filenametext, F_AFT_filenametext, F_AFT_string | F_AFT_normal, 
F_AFT_filenametext}},
-  {CM_inlinefmt, {F_AFT_monospacetext, F_AFT_normal}},
-  {CM_inlinefmtifelse, {F_AFT_monospacetext, F_AFT_normal, F_AFT_normal}},
-  {CM_inlineraw, {F_AFT_monospacetext, F_AFT_raw}},
-  {CM_inlineifclear, {F_AFT_monospacetext, F_AFT_normal}},
-  {CM_inlineifset, {F_AFT_monospacetext, F_AFT_normal}},
-  {CM_item, {F_AFT_none}}, /* no flag */
-  {CM_itemx, {F_AFT_none}}, /* no flag */
-  {CM_value, {F_AFT_monospacestring}},
-  {CM_abbr, {F_AFT_normal, F_AFT_string | F_AFT_normal}},
-  {CM_acronym, {F_AFT_normal, F_AFT_string | F_AFT_normal}},
-};
-
-typedef struct COMMAND_ARGS_SPECIFICATION {
-    int status;
-    unsigned long flags[MAX_COMMAND_ARGS_NR];
-} COMMAND_ARGS_SPECIFICATION;
-
-static COMMAND_ARGS_SPECIFICATION command_args_flags[BUILTIN_CMD_NUMBER];
-
-
-
 static void convert_to_html_internal (CONVERTER *self, const ELEMENT *e,
                                       TEXT *result, const char *explanation);
 
@@ -361,7 +191,7 @@ html_special_unit_variety_direction_index (const CONVERTER 
*self,
   and not set to the element at the tree root
  */
 /* NOTE should not be called with a text element */
-static ROOT_AND_UNIT *
+ROOT_AND_UNIT *
 html_get_tree_root_element (CONVERTER *self, const ELEMENT *command,
                             int find_container)
 {
@@ -504,6 +334,29 @@ get_element_root_command_element (CONVERTER *self, const 
ELEMENT *command)
   return root_unit;
 }
 
+/* same as matching the regex /^\\[a-zA-Z0-9]+ /
+ */
+char *
+html_after_escaped_characters (char *text)
+{
+  char *p = text;
+  if (*p != '\\')
+    return 0;
+
+  p++;
+
+  if (!isascii_alnum (*p))
+    return 0;
+
+  while (isascii_alnum (*p))
+    p++;
+
+  if (*p == ' ')
+    return p+1;
+
+  return 0;
+}
+
 /* this number should be safe to use even after targets list has been
    reallocated */
 
@@ -754,7 +607,7 @@ static const char *xml_named_entity_nbsp = "&nbsp;";
 static const char *html_default_entity_nbsp = "&nbsp;";
 
 char *
-substitute_html_non_breaking_space (CONVERTER *self, const char *text)
+html_substitute_non_breaking_space (CONVERTER *self, const char *text)
 {
   TEXT result;
   text_init (&result);
@@ -824,8 +677,9 @@ special_unit_info_tree (CONVERTER *self, const enum 
special_unit_info_tree type,
 }
 
 const char *
-special_unit_info (const CONVERTER *self, enum special_unit_info_type type,
-                   const char *special_unit_variety)
+html_special_unit_info (const CONVERTER *self,
+                        enum special_unit_info_type type,
+                        const char *special_unit_variety)
 {
   /* number is index +1 */
   size_t number = find_string (&self->special_unit_varieties,
@@ -835,250 +689,6 @@ special_unit_info (const CONVERTER *self, enum 
special_unit_info_type type,
   return self->special_unit_info[type][i];
 }
 
-OUTPUT_UNIT *
-register_special_unit (CONVERTER *self, char *special_unit_variety)
-{
-  ELEMENT *unit_command = new_element (ET_special_unit_element);
-  OUTPUT_UNIT *special_unit = new_output_unit (OU_special_unit);
-
-  special_unit->special_unit_variety = special_unit_variety;
-  unit_command->e.c->associated_unit = special_unit;
-  special_unit->uc.special_unit_command = unit_command;
-
-  return special_unit;
-}
-
-typedef struct SPECIAL_UNIT_ORDER {
-    const char *order;
-    const char *variety;
-} SPECIAL_UNIT_ORDER;
-
-int
-compare_special_units (const void *a, const void *b)
-{
-  const SPECIAL_UNIT_ORDER *spu_order_a = (const SPECIAL_UNIT_ORDER *) a;
-  const SPECIAL_UNIT_ORDER *spu_order_b = (const SPECIAL_UNIT_ORDER *) b;
-
-  int result = strcmp (spu_order_a->order, spu_order_b->order);
-  if (result != 0)
-    return result;
-  return strcmp (spu_order_a->variety, spu_order_b->variety);
-}
-
-void
-prepare_special_units (CONVERTER *self, size_t output_units_descriptor)
-{
-  size_t i;
-  STRING_LIST *special_unit_varieties = &self->special_unit_varieties;
-  SPECIAL_UNIT_ORDER *special_units_order;
-  OUTPUT_UNIT *previous_output_unit = 0;
-
-  size_t special_units_descriptor
-         = new_output_units_descriptor (self->document);
-  size_t associated_special_units_descriptor
-         = new_output_units_descriptor (self->document);
-
-  /* retrieve after reallocating */
-
-  OUTPUT_UNIT_LIST *special_units
-    = retrieve_output_units (self->document, special_units_descriptor);
-
-  OUTPUT_UNIT_LIST *associated_special_units
-    = retrieve_output_units (self->document,
-                             associated_special_units_descriptor);
-
-  OUTPUT_UNIT_LIST *output_units
-    = retrieve_output_units (self->document, output_units_descriptor);
-
-  /* for separate special output units */
-  STRING_LIST *do_special = new_string_list ();
-
-  self->output_units_descriptors[OUDT_special_units] = 
special_units_descriptor;
-  self->output_units_descriptors[OUDT_associated_special_units]
-     = associated_special_units_descriptor;
-
-  if (self->document->sections_list
-      && self->document->sections_list->number > 1)
-    {
-      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];
-          OPTION *contents_option_ref = get_command_option (self->conf, cmd);
-          if (contents_option_ref->o.integer > 0)
-            contents_set = 1;
-          if (contents_set)
-            {
-              int j;
-              char *special_unit_variety = 0;
-              char *contents_location
-                = self->conf->CONTENTS_OUTPUT_LOCATION.o.string;
-
-              for (j = 0; command_special_unit_variety[j].cmd; j++)
-                {
-                  if (command_special_unit_variety[j].cmd == cmd)
-                    {
-                      special_unit_variety
-                        = command_special_unit_variety[j].variety;
-                      break;
-                    }
-                }
-              if (contents_location
-                  && !strcmp (contents_location, "separate_element"))
-                add_string (special_unit_variety, do_special);
-              else
-                {
-                  OUTPUT_UNIT *special_output_unit = 0;
-                  const OUTPUT_UNIT *associated_output_unit = 0;
-                  if (contents_location
-                      && !strcmp (contents_location, "after_title"))
-                    {
-                      associated_output_unit = output_units->list[0];
-                    }
-                  else if (contents_location
-                           && !strcmp (contents_location, "after_top"))
-                    {
-                      if (self->document->global_commands.top)
-                        {/* note that top is a uniq command */
-                          const ELEMENT *section_top
-                             = self->document->global_commands.top;
-
-                          if (section_top->e.c->associated_unit)
-                            associated_output_unit
-                                 = section_top->e.c->associated_unit;
-                        }
-                      if (!associated_output_unit)
-                        continue;
-                    }
-                  else if (contents_location
-                           && !strcmp (contents_location, "inline"))
-                    {
-                      const ELEMENT_LIST *global_command
-                       = get_cmd_global_multi_command (
-                                      &self->document->global_commands, cmd);
-                      if (global_command->number > 0)
-                        {
-                          size_t i;
-                          for (i = 0; i < global_command->number; i++)
-                            {
-                              const ELEMENT *command = global_command->list[i];
-                              ROOT_AND_UNIT *root_unit
-                               = html_get_tree_root_element (self, command, 0);
-                              if (root_unit->output_unit)
-                                associated_output_unit = 
root_unit->output_unit;
-                              free (root_unit);
-                              if (associated_output_unit)
-                                break;
-                            }
-                        }
-                      else
-                        continue;
-                    }
-                  else /* should not happen */
-                    continue;
-
-                  special_output_unit
-                    = register_special_unit (self, special_unit_variety);
-                  special_output_unit->associated_document_unit
-                    = associated_output_unit;
-                  add_to_output_unit_list (associated_special_units,
-                                           special_output_unit);
-                }
-            }
-        }
-    }
-
-  if (self->document->global_commands.footnotes.number > 0
-      && self->conf->footnotestyle.o.string
-      && !strcmp (self->conf->footnotestyle.o.string, "separate")
-      && output_units->number > 1)
-    add_string ("footnotes", do_special);
-
-  if ((self->conf->DO_ABOUT.o.integer < 0
-       && output_units->number > 1
-       && ((self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string))
-           || self->conf->HEADERS.o.integer > 0))
-      || self->conf->DO_ABOUT.o.integer > 0)
-    add_string ("about", do_special);
-
-  special_units_order = (SPECIAL_UNIT_ORDER *)
-    malloc (sizeof (SPECIAL_UNIT_ORDER) * do_special->number);
-  for (i = 0; i < do_special->number; i++)
-    {
-      char *special_unit_variety = do_special->list[i];
-      special_units_order[i].order = special_unit_info (self, SUI_type_order,
-                                                        special_unit_variety);
-      special_units_order[i].variety = special_unit_variety;
-    }
-
-  qsort (special_units_order, do_special->number, sizeof (SPECIAL_UNIT_ORDER),
-         compare_special_units);
-
-  previous_output_unit = output_units->list[output_units->number-1];
-
-  for (i = 0; i < do_special->number; i++)
-    {
-      /* take the string from special_unit_varieties */
-      char *special_unit_variety;
-      /* number is index +1 */
-      size_t number = find_string (special_unit_varieties,
-                                   special_units_order[i].variety);
-      int special_unit_varieties_idx = number -1;
-      if (special_unit_varieties_idx < 0)
-        {
-          char *msg;
-          xasprintf (&msg, "special_unit_varieties not found: %s\n",
-                           special_units_order[i].variety);
-          bug (msg);
-        }
-
-      special_unit_variety
-        = special_unit_varieties->list[special_unit_varieties_idx];
-      OUTPUT_UNIT *special_output_unit
-                    = register_special_unit (self, special_unit_variety);
-      add_to_output_unit_list (special_units,
-                               special_output_unit);
-
-      if (previous_output_unit)
-        {
-          special_output_unit->tree_unit_directions[D_prev]
-             = previous_output_unit;
-          previous_output_unit->tree_unit_directions[D_next]
-             = special_output_unit;
-        }
-      previous_output_unit = special_output_unit;
-    }
-
-  free (special_units_order);
-  destroy_strings_list (do_special);
-}
-
-static enum command_id additional_format_context_cmd[] = {
-   CM_tab, CM_item, CM_itemx, CM_headitem, 0
- };
-
-static enum command_id HTML_align_cmd[] = {
-   CM_raggedright, CM_flushleft, CM_flushright, CM_center, 0
-};
-
-void
-register_format_context_command (enum command_id cmd)
-{
-  html_commands_data[cmd].flags |= HF_format_context;
-}
-
-void register_pre_class_command (enum command_id cmd, enum command_id main_cmd)
-{
-  if (main_cmd)
-    html_commands_data[cmd].pre_class_cmd = main_cmd;
-  else
-    html_commands_data[cmd].pre_class_cmd = cmd;
-
-  html_commands_data[cmd].flags |= HF_pre_class;
-}
-
 /* used for diverse elements: tree units, indices, footnotes, special
   elements, contents elements... */
 static HTML_TARGET *
@@ -1126,41 +736,9 @@ add_special_target (CONVERTER *self, enum 
special_target_type type,
 static const enum command_id contents_elements_options[]
              = {CM_contents, CM_shortcontents, CM_summarycontents, 0};
 
-static const enum command_id conf_for_special_units[]
-                          = {CM_footnotestyle, 0};
-
 static const enum command_id conf_for_documentlanguage[]
                           = {CM_documentlanguage, 0};
 
-void
-html_prepare_conversion_units (CONVERTER *self)
-{
-  size_t output_units_descriptor;
-
-  if (self->conf->USE_NODES.o.integer > 0)
-    output_units_descriptor = split_by_node (self->document);
-  else
-    output_units_descriptor = split_by_section (self->document);
-  self->output_units_descriptors[OUDT_units] = output_units_descriptor;
-
-  /* configuration used to determine if a special element is to be done
-     (in addition to contents) */
-  set_global_document_commands (self, CL_last, conf_for_special_units);
-  /*
-    NOTE if the last value of footnotestyle is separate, all the footnotes
-    formatted text are set to the special element set in _prepare_special_units
-    as _html_get_tree_root_element uses the Footnote direction for every 
footnote.
-    Therefore if @footnotestyle separate is set late in the document the 
current
-    value may not be consistent with the link obtained for the footnote
-    formatted text.  This is not an issue, as the manual says that
-    @footnotestyle should only appear in the preamble, and it makes sense
-    to have something consistent in the whole document for footnotes position.
-   */
-  prepare_special_units (self, output_units_descriptor);
-
-  /* reset to the default */
-  set_global_document_commands (self, CL_before, conf_for_special_units);
-}
 
 /* calls customization function requiring output units */
 void
@@ -1186,8 +764,8 @@ set_special_units_targets_files (CONVERTER *self, const 
char *document_name)
       const char *special_unit_variety = special_unit->special_unit_variety;
 
       /* refers to self->special_unit_info */
-      const char *target = special_unit_info (self, SUI_type_target,
-                                              special_unit_variety);
+      const char *target = html_special_unit_info (self, SUI_type_target,
+                                                   special_unit_variety);
 
       if (!target)
         continue;
@@ -1198,8 +776,8 @@ set_special_units_targets_files (CONVERTER *self, const 
char *document_name)
           && document_name)
         {
           const char *special_unit_file_string
-            = special_unit_info (self, SUI_type_file_string,
-                                 special_unit_variety);
+            = html_special_unit_info (self, SUI_type_file_string,
+                                      special_unit_variety);
           text_reset (&text_name);
           if (!special_unit_file_string)
             special_unit_file_string = "";
@@ -1275,8 +853,8 @@ prepare_associated_special_units_targets (CONVERTER *self)
           char *special_unit_variety = special_unit->special_unit_variety;
 
           /* it may be undef'ined in user customization code */
-          const char *target = special_unit_info (self, SUI_type_target,
-                                                 special_unit_variety);
+          const char *target = html_special_unit_info (self, SUI_type_target,
+                                                       special_unit_variety);
           target_filename
             = call_file_id_setting_special_unit_target_file_name (
                                self, special_unit, target, filename);
@@ -1685,8 +1263,6 @@ run_stage_handlers (CONVERTER *self, enum 
html_stage_handler_stage_type stage)
           break;
 
 
-#define ADDN(str,nr) text_append_n (result, str, nr)
-
 /* this function allows to call a conversion function associated to
    a COMMAND_CONVERSION different from the ELEMENT and CMD arguments
    associated command conversion */
@@ -1715,6 +1291,8 @@ conversion_function_cmd_conversion (CONVERTER *self,
     }
 }
 
+#define ADDN(str,nr) text_append_n (result, str, nr)
+
 void
 default_css_string_format_protect_text (const char *text, TEXT *result)
 {
@@ -1743,93 +1321,6 @@ default_css_string_format_protect_text (const char 
*text, TEXT *result)
     }
 }
 
-void
-html_css_string_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_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_code (self) || html_in_math (self))
-    {
-      default_css_string_format_protect_text (content_used, result);
-      goto out;
-    }
-
-  const char *p = content_used;
-  while (*p)
-    {
-      int before_sep_nr = strcspn (p, "\\-`'");
-      if (before_sep_nr)
-        {
-          text_append_n (result, p, before_sep_nr);
-          p += before_sep_nr;
-        }
-      if (!*p)
-        break;
-      switch (*p)
-        {
-        case '-':
-          if (*(p+1) && !memcmp (p, "---", 3))
-            {
-              ADDN("\\2014 ",6);
-              p += 3;
-            }
-          else if (!memcmp (p, "--", 2))
-            {
-              ADDN("\\2013 ",6);
-              p += 2;
-            }
-          else
-            {
-              text_append_n (result, "-", 1);
-              p++;
-            }
-          break;
-        case '`':
-          if (!memcmp (p, "``", 2))
-            {
-              ADDN("\\201C ",6);
-              p += 2;
-            }
-          else
-            {
-              ADDN("\\2018 ",6);
-              p++;
-            }
-          break;
-        case '\'':
-          if (!memcmp (p, "''", 2))
-            {
-              ADDN("\\201D ",6);
-              p += 2;
-            }
-          else
-            {
-              ADDN("\\2019 ",6);
-              p++;
-            }
-          break;
-        case '\\':
-          ADDN("\\\\", 2);
-          p++;
-          break;
-        }
-    }
- out:
-  if (contents_used_to_be_freed)
-    free (content_used);
-}
 #undef ADDN
 
 #define OTXI_ISO_ENTITY_TEXT_CASES(var) \
@@ -2349,7 +1840,7 @@ direction_string (CONVERTER *self, int direction,
               char *translated_string
                 = html_cdt_string (context_converted_string, self, 0, 0);
               char *result_string
-                = substitute_html_non_breaking_space (self, translated_string);
+                = html_substitute_non_breaking_space (self, translated_string);
               self->directions_strings[string_type][direction][context]
                 = result_string;
               free (translated_string);
@@ -3970,15 +3461,6 @@ get_copiable_anchor (CONVERTER *self, const char *id)
   return 0;
 }
 
-int
-compare_index_name (const void *a, const void *b)
-{
-  const INDEX **idx_a = (const INDEX **) a;
-  const INDEX **idx_b = (const INDEX **) b;
-
-  return strcmp ((*idx_a)->name, (*idx_b)->name);
-}
-
 void
 prepare_index_entries_targets (CONVERTER *self)
 {
@@ -4445,8 +3927,8 @@ html_prepare_output_units_global_targets (CONVERTER *self)
 
               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);
+                = html_special_unit_info (self, SUI_type_direction,
+                                          special_unit_variety);
               s++;
             }
         }
@@ -5244,58 +4726,8 @@ html_prepare_units_directions_files (CONVERTER *self,
 }
 
 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 drop const 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
-html_default_format_separate_anchor (CONVERTER *self, const char *id,
-                                     const char *class, TEXT *result)
+html_default_format_separate_anchor (CONVERTER *self, const char *id,
+                                     const char *class, TEXT *result)
 {
   char *attribute_class;
 
@@ -5949,8 +5381,8 @@ default_format_footnotes_segment (CONVERTER *self, TEXT 
*result)
 
   classes = new_string_list ();
 
-  class_base = special_unit_info (self, SUI_type_class,
-                                  "footnotes");
+  class_base = html_special_unit_info (self, SUI_type_class,
+                                       "footnotes");
   xasprintf (&class, "%s-segment", class_base);
 
   add_string (class, classes);
@@ -7925,23 +7357,6 @@ format_node_redirection_page (CONVERTER *self, const 
ELEMENT *element,
    }
 }
 
-/* return string to be freed by the caller */
-char *
-html_prepare_node_redirection_page (CONVERTER *self, const ELEMENT *element,
-                                    const char *filename)
-{
-  char *result;
-
-  self->current_filename.filename = filename;
-  self->current_filename.file_number = 0;
-
-  result = format_node_redirection_page (self, element, filename);
-
-  self->current_filename.filename = 0;
-
-  return result;
-}
-
 static void
 text_element_conversion (CONVERTER *self,
                          const HTML_NO_ARG_COMMAND_CONVERSION *specification,
@@ -7973,10 +7388,35 @@ text_element_conversion (CONVERTER *self,
 }
 
 void
-convert_no_arg_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_command_conversion_external (CONVERTER *self, const enum command_id cmd,
+                                  const ELEMENT *element,
+                                  const HTML_ARGS_FORMATTED *args_formatted,
+                                  const char *content, TEXT *result)
+{
+  /* XS specific debug message */
+  /*
+  if (self->conf->DEBUG.o.integer > 0)
+    fprintf (stderr, "DEBUG: command conversion %s '%s'\n",
+             builtin_command_data[cmd].cmdname, content);
+   */
+
+  const FORMATTING_REFERENCE *formatting_reference
+    = self->current_commands_conversion_function[cmd].formatting_reference;
+
+  /* NOTE it should always be true as in the main loop a formatting
+     function is called only if command_conversion is set, which should
+     not be if formatting_reference status is 0 */
+  if (formatting_reference->status > 0)
+    call_commands_conversion (self, cmd, formatting_reference,
+                              element, args_formatted, content,
+                              result);
+}
+
+void
+html_convert_no_arg_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
 {
   enum command_id formatted_cmd = cmd;
   enum conversion_context context;
@@ -8022,11 +7462,11 @@ convert_no_arg_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-css_string_convert_no_arg_command (CONVERTER *self,
-                    const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_css_string_convert_no_arg_command (CONVERTER *self,
+                                const enum command_id cmd,
+                                const ELEMENT *element,
+                                const HTML_ARGS_FORMATTED *args_formatted,
+                                const char *content, TEXT *result)
 {
   enum command_id formatted_cmd = cmd;
   if (cmd == CM_click)
@@ -8056,10 +7496,10 @@ css_string_convert_no_arg_command (CONVERTER *self,
 }
 
 void
-convert_today_command (CONVERTER *self, const enum command_id cmd,
-                       const ELEMENT *element,
-                       const HTML_ARGS_FORMATTED *args_formatted,
-                       const char *content, TEXT *result)
+html_convert_today_command (CONVERTER *self, const enum command_id cmd,
+                            const ELEMENT *element,
+                            const HTML_ARGS_FORMATTED *args_formatted,
+                            const char *content, TEXT *result)
 {
   ELEMENT *today_element = expand_today (self->conf);
 
@@ -8072,10 +7512,10 @@ convert_today_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_style_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_style_command (CONVERTER *self, const enum command_id cmd,
+                            const ELEMENT *element,
+                            const HTML_ARGS_FORMATTED *args_formatted,
+                            const char *content, TEXT *result)
 {
   enum command_id style_cmd = cmd;
   const HTML_STYLE_COMMAND_CONVERSION *formatting_spec;
@@ -8157,10 +7597,10 @@ convert_style_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_w_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_w_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)
     {
@@ -8175,10 +7615,10 @@ convert_w_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_value_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_value_command (CONVERTER *self, const enum command_id cmd,
+                            const ELEMENT *element,
+                            const HTML_ARGS_FORMATTED *args_formatted,
+                            const char *content, TEXT *result)
 {
   ELEMENT *tree;
   ELEMENT *value_text = new_text_element (ET_normal_text);
@@ -8202,10 +7642,10 @@ convert_value_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_email_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_email_command (CONVERTER *self, const enum command_id cmd,
+                            const ELEMENT *element,
+                            const HTML_ARGS_FORMATTED *args_formatted,
+                            const char *content, TEXT *result)
 {
   const char *mail = 0;
   const char *mail_string = 0;
@@ -8268,10 +7708,10 @@ convert_email_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_explained_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_explained_command (CONVERTER *self, const enum command_id cmd,
+                                const ELEMENT *element,
+                                const HTML_ARGS_FORMATTED *args_formatted,
+                                const char *content, TEXT *result)
 {
   TEXT explained_string;
   TEXT *text_result;
@@ -8389,10 +7829,10 @@ convert_explained_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_anchor_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_anchor_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
 {
   if (!html_in_multi_expanded (self) && !html_in_string (self))
     {
@@ -8405,10 +7845,10 @@ convert_anchor_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_footnote_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_footnote_command (CONVERTER *self, const enum command_id cmd,
+                               const ELEMENT *element,
+                               const HTML_ARGS_FORMATTED *args_formatted,
+                               const char *content, TEXT *result)
 {
   static const char *target_prefix = "t_f";
   char *footnote_mark;
@@ -8531,10 +7971,10 @@ convert_footnote_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_uref_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_uref_command (CONVERTER *self, const enum command_id cmd,
+                           const ELEMENT *element,
+                           const HTML_ARGS_FORMATTED *args_formatted,
+                           const char *content, TEXT *result)
 {
   const char *url = 0;
   const char *url_string = 0;
@@ -8725,10 +8165,10 @@ html_image_file_location_name (CONVERTER *self, const 
enum command_id cmd,
 }
 
 void
-convert_image_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_image_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
@@ -8819,10 +8259,10 @@ convert_image_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_math_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_math_command (CONVERTER *self, const enum command_id cmd,
+                           const ELEMENT *element,
+                           const HTML_ARGS_FORMATTED *args_formatted,
+                           const char *content, TEXT *result)
 {
   char *attribute_class;
   STRING_LIST *classes;
@@ -8964,10 +8404,10 @@ html_accent_entities_numeric_entities_accent (CONVERTER 
*self,
 }
 
 void
-convert_accent_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_accent_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
 {
   char *accent_text;
   char *(*format_accents)(CONVERTER *self, const char *text,
@@ -8988,29 +8428,6 @@ convert_accent_command (CONVERTER *self, const enum 
command_id cmd,
   free (accent_text);
 }
 
-/* same as matching the regex /^\\[a-zA-Z0-9]+ /
- */
-static char *
-after_escaped_characters (char *text)
-{
-  char *p = text;
-  if (*p != '\\')
-    return 0;
-
-  p++;
-
-  if (!isascii_alnum (*p))
-    return 0;
-
-  while (isascii_alnum (*p))
-    p++;
-
-  if (*p == ' ')
-    return p+1;
-
-  return 0;
-}
-
 char *
 css_string_accent (CONVERTER *self, const char *text,
                          const ELEMENT *element, int set_case)
@@ -9046,7 +8463,7 @@ css_string_accent (CONVERTER *self, const char *text,
           /* we consider that letters are either characters or
              escaped characters as they appear in CSS strings */
           /* p non NUL corresponds to escaped characters */
-          char *p = after_escaped_characters (text_set);
+          char *p = html_after_escaped_characters (text_set);
           char *next_text = 0;
           ucs4_t first_char;
           const uint8_t *next = 0;
@@ -9072,7 +8489,7 @@ css_string_accent (CONVERTER *self, const char *text,
           if (next_text)
             {
               ucs4_t second_char;
-              const char *q = after_escaped_characters (next_text);
+              const char *q = html_after_escaped_characters (next_text);
 
               if (!q)
                 {
@@ -9184,10 +8601,11 @@ css_string_accent (CONVERTER *self, const char *text,
 }
 
 void
-css_string_convert_accent_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_css_string_convert_accent_command (CONVERTER *self,
+                         const enum command_id cmd,
+                         const ELEMENT *element,
+                         const HTML_ARGS_FORMATTED *args_formatted,
+                         const char *content, TEXT *result)
 {
   char *accent_text;
   char *(*format_accents)(CONVERTER *self, const char *text,
@@ -9205,12 +8623,11 @@ css_string_convert_accent_command (CONVERTER *self, 
const enum command_id cmd,
   free (accent_text);
 }
 
-
 void
-convert_indicateurl_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_indicateurl_command (CONVERTER *self, const enum command_id cmd,
+                           const ELEMENT *element,
+                           const HTML_ARGS_FORMATTED *args_formatted,
+                           const char *content, TEXT *result)
 {
   /* happens with bogus @-commands without argument, like @strong something */
   if (!args_formatted || args_formatted->number <= 0
@@ -9245,10 +8662,10 @@ convert_indicateurl_command (CONVERTER *self, const 
enum command_id cmd,
 }
 
 void
-convert_titlefont_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_titlefont_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
       && args_formatted->args[0].formatted[AFT_type_normal]
@@ -9264,10 +8681,10 @@ convert_titlefont_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_U_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_U_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
       && args_formatted->args[0].formatted[AFT_type_normal]
@@ -9319,8 +8736,8 @@ contents_inline_element (CONVERTER *self, const enum 
command_id cmd,
               text_init (&result);
 
               classes = new_string_list ();
-              class_base = special_unit_info (self, SUI_type_class,
-                                              special_unit_variety);
+              class_base = html_special_unit_info (self, SUI_type_class,
+                                                   special_unit_variety);
               xasprintf (&class, "region-%s", class_base);
 
               add_string (class, classes);
@@ -9444,10 +8861,10 @@ mini_toc_internal (CONVERTER *self, const ELEMENT 
*element, TEXT *result)
 }
 
 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)
+html_convert_heading_command (CONVERTER *self, const enum command_id cmd,
+                              const ELEMENT *element,
+                              const HTML_ARGS_FORMATTED *args_formatted,
+                              const char *content, TEXT *result)
 {
   const char *element_id;
   const OUTPUT_UNIT *output_unit = 0;
@@ -9844,32 +9261,10 @@ convert_heading_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_raw_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  if (cmd == CM_html)
-    {
-      if (content)
-        text_append (result, content);
-      return;
-    }
-
-  if (!self->multiple_conversions)
-    {
-      message_list_command_warn (&self->error_messages, self->conf,
-                   element, 0, "raw format %s is not converted",
-                     element_command_name (element));
-    }
-  format_protect_text (self, content, result);
-}
-
-void
-convert_inline_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_inline_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
 {
   const char *format;
   size_t arg_index = 0;
@@ -9910,752 +9305,864 @@ convert_inline_command (CONVERTER *self, const enum 
command_id cmd,
     }
 }
 
-/* strings in extra_classes strings are transferred and later on
-   free'd, but not extra_classes themselves */
-static void
-indent_with_table (CONVERTER *self, const enum command_id cmd,
-                   const char *content, const STRING_LIST *extra_classes,
-                   TEXT *result)
-{
-  char *attribute_class;
-
-  STRING_LIST *classes = new_string_list ();
-  add_string (builtin_command_name (cmd), classes);
-
-  if (extra_classes)
-    merge_strings (classes, extra_classes);
-
-  attribute_class = html_attribute_class (self, "table", classes);
-  text_append (result, attribute_class);
-  text_append_n (result, "><tr><td>", 9);
-  text_append_n (result,
-                self->special_character[SC_non_breaking_space].string,
-                self->special_character[SC_non_breaking_space].len);
-  text_append_n (result, "</td><td>", 9);
-  text_append (result, content);
-  text_append_n (result, "</td></tr></table>\n", 19);
-  free (attribute_class);
-  destroy_strings_list (classes);
-}
-
 void
-convert_preformatted_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_xref_command (CONVERTER *self, const enum command_id cmd,
+                           const ELEMENT *element,
+                           const HTML_ARGS_FORMATTED *args_formatted,
+                           const char *content, TEXT *result)
 {
-  STRING_LIST *additional_classes;
-  enum command_id main_cmd = 0;
+  char *name = 0;
+  HTML_ARG_FORMATTED *file_arg = 0;
+  char *file = 0;
+  const char *book = 0;
+  const ELEMENT *arg_node = 0;
+  const ELEMENT *target_node = 0;
+  ELEMENT *tree = 0;
 
-  if (!content || !strlen (content))
+  ELEMENT *book_element = 0;
+  ELEMENT *reference_element = 0;
+
+  /* happens with bogus @-commands without argument, maybe only
+     at the end of a document */
+  if (!args_formatted)
     return;
 
-  if (html_in_string (self))
+  if (cmd != CM_link && cmd != CM_inforef && args_formatted->number > 2
+      && args_formatted->args[2].formatted[AFT_type_normal]
+      && strlen (args_formatted->args[2].formatted[AFT_type_normal]))
     {
-      text_append (result, content);
-      return;
+      name = strdup (args_formatted->args[2].formatted[AFT_type_normal]);
+    }
+  else if (args_formatted->number > 1
+           && args_formatted->args[1].formatted[AFT_type_normal]
+           && strlen (args_formatted->args[1].formatted[AFT_type_normal]))
+    {
+      name = strdup (args_formatted->args[1].formatted[AFT_type_normal]);
     }
 
-  additional_classes = new_string_list ();
-
-  if (html_commands_data[cmd].flags & HF_small_block_command)
+  if (cmd == CM_link || cmd == CM_inforef)
     {
-      int i;
-      for (i = 0; small_block_associated_command[i][0]; i++)
-        {
-          enum command_id small_cmd = small_block_associated_command[i][0];
-          if (small_cmd == cmd)
-            {
-              main_cmd = small_block_associated_command[i][1];
-              add_string (builtin_command_name (cmd), additional_classes);
-              break;
-            }
-        }
+      if (args_formatted->number > 2)
+        file_arg = &args_formatted->args[2];
     }
-  else
-    main_cmd = cmd;
+  else if (args_formatted->number > 3)
+    file_arg = &args_formatted->args[3];
 
-  if (cmd == CM_example)
+  if (file_arg && file_arg->formatted[AFT_type_filenametext]
+      && strlen (file_arg->formatted[AFT_type_filenametext]))
     {
-      if (element->e.c->args.number > 0)
-        {
-          size_t i;
-          for (i = 0; i < element->e.c->args.number; i++)
-            {
-              const ELEMENT *example_arg = element->e.c->args.list[i];
-       /* convert or remove all @-commands, using simple ascii and unicode
-          characters */
-              char *converted_arg = convert_to_normalized (example_arg);
-              if (strlen (converted_arg))
-                {
-                  char *class_name;
-                  xasprintf (&class_name, "user-%s", converted_arg);
-                  add_string (class_name, additional_classes);
-                  free (class_name);
-                }
-              free (converted_arg);
-            }
-        }
-    }
-  else if (main_cmd == CM_lisp)
-    {
-      add_string (builtin_command_name (main_cmd), additional_classes);
-      main_cmd = CM_example;
+      file = strdup (file_arg->formatted[AFT_type_filenametext]);
     }
 
-  if (self->conf->INDENTED_BLOCK_COMMANDS_IN_TABLE.o.integer > 0
-      && html_commands_data[cmd].flags & HF_indented_preformatted)
-    {
-      indent_with_table (self, cmd, content,
-                         additional_classes, result);
-    }
-  else
-    {
-      char *attribute_class;
+  if (args_formatted->number > 4
+      && args_formatted->args[4].formatted[AFT_type_normal]
+      && strlen (args_formatted->args[4].formatted[AFT_type_normal]))
+    book = args_formatted->args[4].formatted[AFT_type_normal];
 
-      STRING_LIST *classes = new_string_list ();
-      add_string (builtin_command_name (main_cmd), classes);
-      merge_strings (classes, additional_classes);
+  if (element->e.c->args.number > 0)
+    arg_node = element->e.c->args.list[0];
 
-      attribute_class = html_attribute_class (self, "div", classes);
-      text_append (result, attribute_class);
-      text_printf (result, ">\n%s</div>\n", content);
-      free (attribute_class);
-      destroy_strings_list (classes);
+  /* check for internal reference */
+  if (cmd != CM_inforef && !book && !file && arg_node)
+    {
+      const char *normalized = lookup_extra_string (arg_node, 
AI_key_normalized);
+      const ELEMENT *manual_content = lookup_extra_container (arg_node,
+                                                      AI_key_manual_content);
+      if (normalized && !manual_content)
+        {
+          target_node = find_identifier_target (
+                                  &self->document->identifiers_target,
+                                  normalized);
+        }
     }
 
-  free (additional_classes->list);
-  free (additional_classes);
-}
-
-void
-convert_indented_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  enum command_id main_cmd = 0;
-  STRING_LIST *additional_classes;
-
-  if (!content || !strlen (content))
-    return;
-
-  if (html_in_string (self))
+  /* internal reference */
+  if (target_node)
     {
-      text_append (result, content);
-      return;
-    }
+      char *href = 0;
+      STRING_LIST *classes = 0;
+     /* This is the node if USE_NODES, otherwise this may be the sectioning
+        command (if the sectioning command is really associated to the node) */
+      const ELEMENT *target_root
+             = html_command_root_element_command (self, target_node);
+      const ELEMENT *associated_section = lookup_extra_element (target_node,
+                                                   AI_key_associated_section);
+      reference_element = new_text_element (ET__converted);
+      NAMED_STRING_ELEMENT_LIST *substrings
+                                       = new_named_string_element_list ();
 
-  additional_classes = new_string_list ();
+      if (!associated_section || associated_section != target_root)
+        target_root = target_node;
 
-  if (html_commands_data[cmd].flags & HF_small_block_command)
-    {
-      int i;
-      for (i = 0; small_block_associated_command[i][0]; i++)
+      if (!html_in_string (self))
+        href = html_command_href (self, target_root, 0, element, 0);
+
+      if (!name)
         {
-          enum command_id small_cmd = small_block_associated_command[i][0];
-          if (small_cmd == cmd)
+          if (self->conf->xrefautomaticsectiontitle.o.string
+              && !strcmp (self->conf->xrefautomaticsectiontitle.o.string, "on")
+              && associated_section
+        /* this condition avoids infinite recursions, indeed in that case
+           the node will be used and not the section.  There should not be
+           @*ref in nodes, and even if there are, it does not seems to be
+           possible to construct an infinite recursion with nodes only
+           as the node must both be a reference target and refer to a specific
+           target at the same time, which is not possible.
+         */
+             && !command_is_in_referred_command_stack (
+                   &self->referred_command_stack, associated_section, 0))
             {
-              main_cmd = small_block_associated_command[i][1];
-              add_string (builtin_command_name (cmd), additional_classes);
-              break;
+              target_root = associated_section;
+              if (html_in_string (self))
+                name = html_command_text (self, target_root, HTT_string);
+              else
+                name = html_command_text (self, target_root, 
HTT_text_nonumber);
+            }
+          else if (target_node->e.c->cmd == CM_float)
+            {
+              if (self->conf->XREF_USE_FLOAT_LABEL.o.integer <= 0)
+                {
+                  if (html_in_string (self))
+                    name = html_command_text (self, target_root, HTT_string);
+                  else
+                    name = html_command_text (self, target_root, 0);
+                }
+              if (!name || !strlen (name))
+                {
+                  if (args_formatted->number > 1
+                      && args_formatted->args[0].formatted[AFT_type_monospace])
+                    {
+                      name
+                       = strdup (
+                          
args_formatted->args[0].formatted[AFT_type_monospace]);
+                    }
+                  else
+                    name = strdup ("");
+                }
+            }
+          else if (self->conf->XREF_USE_NODE_NAME_ARG.o.integer <= 0
+                   && (self->conf->XREF_USE_NODE_NAME_ARG.o.integer == 0
+                       || !html_in_preformatted_context (self))
+        /* this condition avoids infinite recursions, example with
+           USE_NODES=0 and node referring to the section and section referring
+           to the node */
+                   && !command_is_in_referred_command_stack (
+                         &self->referred_command_stack, target_root, 0))
+            {
+              if (html_in_string (self))
+                name = html_command_text (self, target_root, HTT_string);
+              else
+                name = html_command_text (self, target_root, 
HTT_text_nonumber);
+            }
+          else if (args_formatted->number > 0
+                   && args_formatted->args[0].formatted[AFT_type_monospace])
+            {
+              name
+               = strdup 
(args_formatted->args[0].formatted[AFT_type_monospace]);
             }
+          else
+            name = strdup ("");
         }
-    }
-  else
-    main_cmd = cmd;
-
-  if (self->conf->INDENTED_BLOCK_COMMANDS_IN_TABLE.o.integer > 0)
-    {
-      indent_with_table (self, main_cmd, content,
-                         additional_classes, result);
-    }
-  else
-    {
-      char *attribute_class;
 
-      STRING_LIST *classes = new_string_list ();
-      add_string (builtin_command_name (main_cmd), classes);
-      merge_strings (classes, additional_classes);
+      if (href)
+        {
+          char *attribute_class;
 
-      attribute_class = html_attribute_class (self, "blockquote", classes);
-      text_append (result, attribute_class);
-      text_printf (result, ">\n%s</blockquote>\n", content);
-      free (attribute_class);
-      destroy_strings_list (classes);
-    }
+          classes = new_string_list ();
+          add_string (builtin_command_name (cmd), classes);
 
-  free (additional_classes->list);
-  free (additional_classes);
-}
+          attribute_class = html_attribute_class (self, "a", classes);
+          text_append (reference_element->e.text, attribute_class);
+          text_printf (reference_element->e.text, " href=\"%s\">%s</a>",
+                                                href, name);
+          free (attribute_class);
+          destroy_strings_list (classes);
+        }
+      else
+        {
+          text_append (reference_element->e.text, name);
+        }
+      free (href);
 
-void
-convert_verbatim_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  if (html_in_string (self))
-    {
-      if (content)
-        text_append (result, content);
+      add_element_to_named_string_element_list (substrings,
+                          "reference_name", reference_element);
+      if (cmd == CM_pxref)
+        {
+          tree = html_cdt_tree ("see {reference_name}",
+                                self, substrings, 0);
+        }
+      else if (cmd == CM_xref)
+        {
+          tree = html_cdt_tree ("See {reference_name}",
+                                self, substrings, 0);
+        }
+      else if (cmd == CM_ref || cmd == CM_link)
+        {
+          tree = html_cdt_tree ("{reference_name}",
+                                self, substrings, 0);
+        }
+      destroy_named_string_element_list (substrings);
     }
   else
     {
-      char *attribute_class;
+     /* external reference */
+      char *href = 0;
+      char *reference = 0;
+      char *book_reference = 0;
 
-      STRING_LIST *classes = new_string_list ();
-      add_string (builtin_command_name (cmd), classes);
+      NAMED_STRING_ELEMENT_LIST *substrings
+                                       = new_named_string_element_list ();
 
-      attribute_class = html_attribute_class (self, "pre", classes);
-      text_append (result, attribute_class);
-      text_append_n (result, ">", 1);
-      if (content)
-        text_append (result, content);
-      text_append_n (result, "</pre>", 6);
-      free (attribute_class);
-      destroy_strings_list (classes);
-   }
-}
+ /* We setup a label_element based on the node argument and not directly the
+    node argument to be able to use the $file argument */
 
-void
-convert_displaymath_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  char *attribute_class;
-  STRING_LIST *classes;
-  int use_mathjax;
-
-  if (html_in_string (self))
-    {
-      if (content)
-        text_append (result, content);
-      return;
-    }
-
-  use_mathjax = (self->conf->HTML_MATH.o.string
-      && !strcmp (self->conf->HTML_MATH.o.string, "mathjax"));
-
-  classes = new_string_list ();
-  add_string (builtin_command_name (cmd), classes);
-
-  if (use_mathjax)
-    {
-      html_register_file_information (self, "mathjax", 1);
-      add_string ("tex2jax_process", classes);
-    }
-
-  attribute_class = html_attribute_class (self, "pre", classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  text_append_n (result, ">", 1);
+      ELEMENT *label_element = 0;
+      ELEMENT *manual_content = 0;
+      ELEMENT *node_content = 0;
 
-  destroy_strings_list (classes);
+      if (arg_node)
+        {
+          node_content = lookup_extra_container (arg_node, 
AI_key_node_content);
+          if (node_content)
+            {
+              const char *normalized = lookup_extra_string (arg_node,
+                                                            AI_key_normalized);
+              label_element = new_element (ET_NONE);
+              add_extra_container (label_element, AI_key_node_content,
+                                   copy_container_contents (node_content));
+              if (normalized)
+                add_extra_string_dup (label_element, AI_key_normalized,
+                                      normalized);
+            }
+        }
 
-  if (use_mathjax)
-    text_printf (result, "\\[%s\\]", content);
-  else
-    text_printf (result, "%s", content);
+ /* file argument takes precedence over the file in the node (file)node entry 
*/
+      if (file)
+        {
+          if (!label_element)
+            label_element = new_element (ET_NONE);
+          add_extra_container (label_element, AI_key_manual_content,
+                               copy_container_contents (file_arg->arg_tree));
+        }
+      else
+        {
+          manual_content = lookup_extra_container (arg_node,
+                                                 AI_key_manual_content);
+        }
 
-  text_append_n (result, "</pre>", 6);
-}
+      if (manual_content)
+        {
+          ELEMENT *root_code;
 
-void
-convert_verbatiminclude_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  ELEMENT *verbatim_include_verbatim
-    = expand_verbatiminclude (&self->error_messages, self->conf,
-                              &self->document->global_info, element);
+          if (!label_element)
+            label_element = new_element (ET_NONE);
 
-  if (verbatim_include_verbatim)
-    {
-      add_tree_to_build (self, verbatim_include_verbatim);
-      convert_to_html_internal (self, verbatim_include_verbatim,
-                                result, "convert verbatiminclude");
-      remove_tree_to_build (self, verbatim_include_verbatim);
-      destroy_element_and_children (verbatim_include_verbatim);
-    }
-}
+          add_extra_container (label_element, AI_key_manual_content,
+                               copy_container_contents (manual_content));
 
-void
-convert_command_simple_block (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  char *attribute_class;
-  STRING_LIST *classes;
+          /* convert the manual part to file string */
+          root_code = new_element (ET__code);
 
-  if (!content)
-    return;
+          add_to_contents_as_array (root_code, manual_content);
 
-  classes = new_string_list ();
-  add_string (builtin_command_name (cmd), classes);
+          add_tree_to_build (self, root_code);
+          file = html_convert_tree (self, root_code, "node file in ref");
+          remove_tree_to_build (self, root_code);
+          destroy_element (root_code);
+        }
 
-  attribute_class = html_attribute_class (self, "div", classes);
-  text_append (result, attribute_class);
-  text_append_n (result, ">", 1);
-  text_append (result, content);
-  text_append_n (result, "</div>", 6);
-  free (attribute_class);
-  destroy_strings_list (classes);
-}
+      if (!name)
+        {
+          if (book)
+            {
+              if (node_content)
+                {
+                  char *node_name;
+                  ELEMENT *node_no_file_tree = new_element (ET__code);
+                  add_to_contents_as_array (node_no_file_tree, node_content);
 
-void
-convert_sp_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  const STRING_LIST *misc_args = lookup_extra_misc_args (element,
-                                                AI_key_misc_args);
-  unsigned int sp_nr = 1;
-  unsigned int i;
+                  add_tree_to_build (self, node_no_file_tree);
+                  node_name = html_convert_tree (self, node_no_file_tree,
+                                                 "node in ref");
+                  remove_tree_to_build (self, node_no_file_tree);
+                  destroy_element (node_no_file_tree);
 
-  if (misc_args && misc_args->number > 0)
-    {
-      const char *sp_number_string = misc_args->list[0];
-      sp_nr = strtoul (sp_number_string, NULL, 10);
-    }
+                  if (node_name && strcmp (node_name, "Top"))
+                    name = node_name;
+                  else
+                    free (node_name);
+                }
+            }
+          else
+            {
+              if (label_element)
+                name = html_command_text (self, label_element, 0);
+              if (!name && args_formatted->number > 0
+                  && args_formatted->args[0].formatted[AFT_type_monospace]
+                  && strcmp 
(args_formatted->args[0].formatted[AFT_type_monospace],
+                             "Top"))
+                name
+               = strdup 
(args_formatted->args[0].formatted[AFT_type_monospace]);
+            }
+        }
 
-  if (html_in_preformatted_context (self) || html_in_string (self))
-    {
-      for (i = 0; i < sp_nr; i++)
-        text_append_n (result, "\n", 1);
-    }
-  else
-    {
-      for (i = 0; i < sp_nr; i++)
+      if (label_element)
         {
-          text_append_n (result, self->line_break_element.string,
-                                 self->line_break_element.len);
-         text_append_n (result, "\n", 1);
+          if (!html_in_string (self))
+            href = html_command_href (self, label_element, 0, element, 0);
+          destroy_element (label_element);
         }
-    }
-}
-
-void
-convert_exdent_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  char *pending_formatted = html_get_pending_formatted_inline_content (self);
-  const char *arg = 0;
-  char *attribute_class;
-  STRING_LIST *classes;
-
-  /* args_formatted null does not seems to be possible in practice */
-  if (args_formatted && args_formatted->number > 0
-      && args_formatted->args[0].formatted[AFT_type_normal]
-      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
-    arg = args_formatted->args[0].formatted[AFT_type_normal];
 
-  if (html_in_string (self))
-    {
-      if (pending_formatted)
+      if (href)
         {
-          text_append (result, pending_formatted);
-          free (pending_formatted);
+       /* attribute to distiguish links to Texinfo manuals from other links
+          and to provide manual name of target */
+          TEXT manual_name_attribute;
+          text_init (&manual_name_attribute);
+          text_append (&manual_name_attribute, "");
+          if (file && self->conf->NO_CUSTOM_HTML_ATTRIBUTE.o.integer <= 0)
+            {
+              text_append_n (&manual_name_attribute, "data-manual=\"", 13);
+              format_protect_text (self, file, &manual_name_attribute);
+              text_append_n (&manual_name_attribute, "\" ", 2);
+            }
+          if (name)
+            {
+              xasprintf (&reference, "<a %shref=\"%s\">%s</a>",
+                         manual_name_attribute.text, href, name);
+            }
+          else if (book)
+            {
+              xasprintf (&book_reference, "<a %shref=\"%s\">%s</a>",
+                         manual_name_attribute.text, href, book);
+            }
+          free (manual_name_attribute.text);
+          free (href);
         }
-      if (arg)
-        text_append (result, arg);
-      text_append_n (result, "\n", 1);
-      return;
-    }
-
-  classes = new_string_list ();
-  add_string (builtin_command_name (cmd), classes);
-
-  if (html_in_preformatted_context (self))
-    attribute_class = html_attribute_class (self, "pre", classes);
-  else
-    attribute_class = html_attribute_class (self, "p", classes);
 
-  text_append (result, attribute_class);
-  text_append_n (result, ">", 1);
-  if (pending_formatted)
-    {
-      text_append (result, pending_formatted);
-      free (pending_formatted);
-    }
-  if (arg)
-    text_append (result, arg);
-  text_append_n (result, "\n", 1);
-  if (html_in_preformatted_context (self))
-    text_append_n (result, "</pre>", 6);
-  else
-    text_append_n (result, "</p>", 4);
 
-  free (attribute_class);
-  destroy_strings_list (classes);
-}
+      if (book && reference)
+        {
+          book_element = new_text_element (ET__converted);
+          text_append (book_element->e.text, book);
+          reference_element = new_text_element (ET__converted);
+          text_append (reference_element->e.text, reference);
 
-void
-convert_center_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  char *arg = 0;
-  char *attribute_class;
-  STRING_LIST *classes;
+          add_element_to_named_string_element_list (substrings,
+                                          "book", book_element);
+          add_element_to_named_string_element_list (substrings,
+                                         "reference", reference_element);
+          if (cmd == CM_pxref)
+            {
+              tree = html_cdt_tree ("see {reference} in @cite{{book}}",
+                                    self, substrings, 0);
+            }
+          else if (cmd == CM_xref)
+            {
+              tree = html_cdt_tree ("See {reference} in @cite{{book}}",
+                                    self, substrings, 0);
+            }
+          else /* @ref */
+            {
+              tree = html_cdt_tree ("{reference} in @cite{{book}}",
+                                    self, substrings, 0);
+            }
+        }
+      else if (book_reference)
+        {
+          book_element = new_text_element (ET__converted);
+          text_append (book_element->e.text, book_reference);
 
-  /* args_formatted null does not seems to be possible in practice */
-  if (args_formatted && args_formatted->number > 0
-      && args_formatted->args[0].formatted[AFT_type_normal]
-      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
-    arg = args_formatted->args[0].formatted[AFT_type_normal];
-  else
-    return;
+          add_element_to_named_string_element_list (substrings,
+                                          "book_reference", book_element);
+          if (cmd == CM_pxref)
+            {
+              tree = html_cdt_tree ("see @cite{{book_reference}}",
+                                    self, substrings, 0);
+            }
+          else if (cmd == CM_xref || cmd == CM_inforef)
+            {
+              tree = html_cdt_tree ("See @cite{{book_reference}}",
+                                    self, substrings, 0);
+            }
+          else /* @ref */
+            {
+              tree = html_cdt_tree ("@cite{{book_reference}}",
+                                    self, substrings, 0);
+            }
+        }
+      else if (book && name)
+        {
+          book_element = new_text_element (ET__converted);
+          text_append (book_element->e.text, book);
+          reference_element = new_text_element (ET__converted);
+          text_append (reference_element->e.text, name);
 
-  if (html_in_string (self))
+          add_element_to_named_string_element_list (substrings,
+                                          "book", book_element);
+          add_element_to_named_string_element_list (substrings,
+                                         "section", reference_element);
+          if (cmd == CM_pxref)
+            {
+              tree = html_cdt_tree ("see `{section}' in @cite{{book}}",
+                                    self, substrings, 0);
+            }
+          else if (cmd == CM_xref || cmd == CM_inforef)
+            {
+              tree = html_cdt_tree ("See `{section}' in @cite{{book}}",
+                                    self, substrings, 0);
+            }
+          else /* @ref */
+            {
+              tree = html_cdt_tree ("`{section}' in @cite{{book}}",
+                                    self, substrings, 0);
+            }
+        }
+      else if (book)
+        {
+          book_element = new_text_element (ET__converted);
+          text_append (book_element->e.text, book);
+
+          add_element_to_named_string_element_list (substrings,
+                                          "book", book_element);
+          if (cmd == CM_pxref)
+            {
+              tree = html_cdt_tree ("see @cite{{book}}",
+                                    self, substrings, 0);
+            }
+          else if (cmd == CM_xref || cmd == CM_inforef)
+            {
+              tree = html_cdt_tree ("See @cite{{book}}",
+                                    self, substrings, 0);
+            }
+          else /* @ref */
+            {
+              tree = html_cdt_tree ("@cite{{book}}",
+                                    self, substrings, 0);
+            }
+        }
+      else if (reference)
+        {
+          reference_element = new_text_element (ET__converted);
+          text_append (reference_element->e.text, reference);
+
+          add_element_to_named_string_element_list (substrings,
+                                         "reference", reference_element);
+          if (cmd == CM_pxref)
+            {
+              tree = html_cdt_tree ("see {reference}",
+                                    self, substrings, 0);
+            }
+          else if (cmd == CM_xref || cmd == CM_inforef)
+            {
+              tree = html_cdt_tree ("See {reference}",
+                                    self, substrings, 0);
+            }
+          else /* @ref */
+            {
+              tree = html_cdt_tree ("{reference}",
+                                    self, substrings, 0);
+            }
+        }
+      else if (name)
+        {
+          reference_element = new_text_element (ET__converted);
+          text_append (reference_element->e.text, name);
+
+          add_element_to_named_string_element_list (substrings,
+                                         "section", reference_element);
+          if (cmd == CM_pxref)
+            {
+              tree = html_cdt_tree ("see `{section}'",
+                                    self, substrings, 0);
+            }
+          else if (cmd == CM_xref || cmd == CM_inforef)
+            {
+              tree = html_cdt_tree ("See `{section}'",
+                                    self, substrings, 0);
+            }
+          else /* @ref */
+            {
+              tree = html_cdt_tree ("`{section}'",
+                                    self, substrings, 0);
+            }
+        }
+      free (reference);
+      free (book_reference);
+      destroy_named_string_element_list (substrings);
+    }
+
+  if (tree)
     {
-      text_append (result, arg);
-      text_append_n (result, "\n", 1);
+      char *context_str;
+      xasprintf (&context_str, "convert xref %s", builtin_command_name (cmd));
+      add_tree_to_build (self, tree);
+      convert_to_html_internal (self, tree, result, context_str);
+      remove_tree_to_build (self, tree);
+      free (context_str);
+      /* should destroy reference_element and book_element */
+      destroy_element_and_children (tree);
+    }
+
+  free (file);
+  free (name);
+}
+
+void
+html_convert_raw_command (CONVERTER *self, const enum command_id cmd,
+                          const ELEMENT *element,
+                          const HTML_ARGS_FORMATTED *args_formatted,
+                          const char *content, TEXT *result)
+{
+  if (cmd == CM_html)
+    {
+      if (content)
+        text_append (result, content);
       return;
     }
 
-  classes = new_string_list ();
+  if (!self->multiple_conversions)
+    {
+      message_list_command_warn (&self->error_messages, self->conf,
+                   element, 0, "raw format %s is not converted",
+                     element_command_name (element));
+    }
+  format_protect_text (self, content, result);
+}
+
+/* strings in extra_classes strings are transferred and later on
+   free'd, but not extra_classes themselves */
+static void
+indent_with_table (CONVERTER *self, const enum command_id cmd,
+                   const char *content, const STRING_LIST *extra_classes,
+                   TEXT *result)
+{
+  char *attribute_class;
+
+  STRING_LIST *classes = new_string_list ();
   add_string (builtin_command_name (cmd), classes);
 
-  attribute_class = html_attribute_class (self, "div", classes);
-  text_append (result, attribute_class);
-  text_append_n (result, ">", 1);
-  text_append (result, arg);
-  text_append_n (result, "\n", 1);
-  text_append_n (result, "</div>", 6);
+  if (extra_classes)
+    merge_strings (classes, extra_classes);
 
+  attribute_class = html_attribute_class (self, "table", classes);
+  text_append (result, attribute_class);
+  text_append_n (result, "><tr><td>", 9);
+  text_append_n (result,
+                self->special_character[SC_non_breaking_space].string,
+                self->special_character[SC_non_breaking_space].len);
+  text_append_n (result, "</td><td>", 9);
+  text_append (result, content);
+  text_append_n (result, "</td></tr></table>\n", 19);
   free (attribute_class);
   destroy_strings_list (classes);
 }
 
 void
-convert_author_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_preformatted_command (CONVERTER *self, const enum command_id cmd,
+                                   const ELEMENT *element,
+                                   const HTML_ARGS_FORMATTED *args_formatted,
+                                   const char *content, TEXT *result)
 {
-  const char *arg = 0;
-  char *attribute_class;
-  STRING_LIST *classes;
-
-  const ELEMENT *titlepage = lookup_extra_element (element, AI_key_titlepage);
-
-  if (!titlepage)
-    return;
+  STRING_LIST *additional_classes;
+  enum command_id main_cmd = 0;
 
-  if (args_formatted->number > 0
-      && args_formatted->args[0].formatted[AFT_type_normal]
-      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
-    arg = args_formatted->args[0].formatted[AFT_type_normal];
-  else
+  if (!content || !strlen (content))
     return;
 
   if (html_in_string (self))
     {
-      text_append (result, arg);
-      text_append_n (result, "\n", 1);
+      text_append (result, content);
       return;
     }
 
-  classes = new_string_list ();
-  add_string (builtin_command_name (cmd), classes);
-
-  attribute_class = html_attribute_class (self, "strong", classes);
-  text_append (result, attribute_class);
-  text_append_n (result, ">", 1);
-  text_append (result, arg);
-  text_append_n (result, "</strong>", 9);
-  text_append_n (result, self->line_break_element.string,
-                         self->line_break_element.len);
-  text_append_n (result, "\n", 1);
-
-  free (attribute_class);
-  destroy_strings_list (classes);
-}
+  additional_classes = new_string_list ();
 
-void
-convert_title_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  const char *arg = 0;
-  char *attribute_class;
-  STRING_LIST *classes;
+  if (html_commands_data[cmd].flags & HF_small_block_command)
+    {
+      int i;
+      for (i = 0; small_block_associated_command[i][0]; i++)
+        {
+          enum command_id small_cmd = small_block_associated_command[i][0];
+          if (small_cmd == cmd)
+            {
+              main_cmd = small_block_associated_command[i][1];
+              add_string (builtin_command_name (cmd), additional_classes);
+              break;
+            }
+        }
+    }
+  else
+    main_cmd = cmd;
 
-  if (args_formatted->number > 0
-      && args_formatted->args[0].formatted[AFT_type_normal]
-      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
-    arg = args_formatted->args[0].formatted[AFT_type_normal];
+  if (cmd == CM_example)
+    {
+      if (element->e.c->args.number > 0)
+        {
+          size_t i;
+          for (i = 0; i < element->e.c->args.number; i++)
+            {
+              const ELEMENT *example_arg = element->e.c->args.list[i];
+       /* convert or remove all @-commands, using simple ascii and unicode
+          characters */
+              char *converted_arg = convert_to_normalized (example_arg);
+              if (strlen (converted_arg))
+                {
+                  char *class_name;
+                  xasprintf (&class_name, "user-%s", converted_arg);
+                  add_string (class_name, additional_classes);
+                  free (class_name);
+                }
+              free (converted_arg);
+            }
+        }
+    }
+  else if (main_cmd == CM_lisp)
+    {
+      add_string (builtin_command_name (main_cmd), additional_classes);
+      main_cmd = CM_example;
+    }
+
+  if (self->conf->INDENTED_BLOCK_COMMANDS_IN_TABLE.o.integer > 0
+      && html_commands_data[cmd].flags & HF_indented_preformatted)
+    {
+      indent_with_table (self, cmd, content,
+                         additional_classes, result);
+    }
   else
+    {
+      char *attribute_class;
+
+      STRING_LIST *classes = new_string_list ();
+      add_string (builtin_command_name (main_cmd), classes);
+      merge_strings (classes, additional_classes);
+
+      attribute_class = html_attribute_class (self, "div", classes);
+      text_append (result, attribute_class);
+      text_printf (result, ">\n%s</div>\n", content);
+      free (attribute_class);
+      destroy_strings_list (classes);
+    }
+
+  free (additional_classes->list);
+  free (additional_classes);
+}
+
+void
+html_convert_indented_command (CONVERTER *self, const enum command_id cmd,
+                               const ELEMENT *element,
+                               const HTML_ARGS_FORMATTED *args_formatted,
+                               const char *content, TEXT *result)
+{
+  enum command_id main_cmd = 0;
+  STRING_LIST *additional_classes;
+
+  if (!content || !strlen (content))
     return;
 
   if (html_in_string (self))
     {
-      text_append (result, arg);
+      text_append (result, content);
+      return;
+    }
+
+  additional_classes = new_string_list ();
+
+  if (html_commands_data[cmd].flags & HF_small_block_command)
+    {
+      int i;
+      for (i = 0; small_block_associated_command[i][0]; i++)
+        {
+          enum command_id small_cmd = small_block_associated_command[i][0];
+          if (small_cmd == cmd)
+            {
+              main_cmd = small_block_associated_command[i][1];
+              add_string (builtin_command_name (cmd), additional_classes);
+              break;
+            }
+        }
+    }
+  else
+    main_cmd = cmd;
+
+  if (self->conf->INDENTED_BLOCK_COMMANDS_IN_TABLE.o.integer > 0)
+    {
+      indent_with_table (self, main_cmd, content,
+                         additional_classes, result);
+    }
+  else
+    {
+      char *attribute_class;
+
+      STRING_LIST *classes = new_string_list ();
+      add_string (builtin_command_name (main_cmd), classes);
+      merge_strings (classes, additional_classes);
+
+      attribute_class = html_attribute_class (self, "blockquote", classes);
+      text_append (result, attribute_class);
+      text_printf (result, ">\n%s</blockquote>\n", content);
+      free (attribute_class);
+      destroy_strings_list (classes);
+    }
+
+  free (additional_classes->list);
+  free (additional_classes);
+}
+
+void
+html_convert_verbatim_command (CONVERTER *self, const enum command_id cmd,
+                               const ELEMENT *element,
+                               const HTML_ARGS_FORMATTED *args_formatted,
+                               const char *content, TEXT *result)
+{
+  if (html_in_string (self))
+    {
+      if (content)
+        text_append (result, content);
+    }
+  else
+    {
+      char *attribute_class;
+
+      STRING_LIST *classes = new_string_list ();
+      add_string (builtin_command_name (cmd), classes);
+
+      attribute_class = html_attribute_class (self, "pre", classes);
+      text_append (result, attribute_class);
+      text_append_n (result, ">", 1);
+      if (content)
+        text_append (result, content);
+      text_append_n (result, "</pre>", 6);
+      free (attribute_class);
+      destroy_strings_list (classes);
+   }
+}
+
+void
+html_convert_displaymath_command (CONVERTER *self, const enum command_id cmd,
+                                  const ELEMENT *element,
+                                  const HTML_ARGS_FORMATTED *args_formatted,
+                                  const char *content, TEXT *result)
+{
+  char *attribute_class;
+  STRING_LIST *classes;
+  int use_mathjax;
+
+  if (html_in_string (self))
+    {
+      if (content)
+        text_append (result, content);
       return;
     }
 
+  use_mathjax = (self->conf->HTML_MATH.o.string
+      && !strcmp (self->conf->HTML_MATH.o.string, "mathjax"));
+
   classes = new_string_list ();
   add_string (builtin_command_name (cmd), classes);
 
-  attribute_class = html_attribute_class (self, "h1", classes);
+  if (use_mathjax)
+    {
+      html_register_file_information (self, "mathjax", 1);
+      add_string ("tex2jax_process", classes);
+    }
+
+  attribute_class = html_attribute_class (self, "pre", classes);
   text_append (result, attribute_class);
+  free (attribute_class);
   text_append_n (result, ">", 1);
-  text_append (result, arg);
-  text_append_n (result, "</h1>", 5);
-  text_append_n (result, "\n", 1);
 
+  destroy_strings_list (classes);
+
+  if (use_mathjax)
+    text_printf (result, "\\[%s\\]", content);
+  else
+    text_printf (result, "%s", content);
+
+  text_append_n (result, "</pre>", 6);
+}
+
+void
+html_convert_simple_block_command (CONVERTER *self, const enum command_id cmd,
+                                   const ELEMENT *element,
+                                   const HTML_ARGS_FORMATTED *args_formatted,
+                                   const char *content, TEXT *result)
+{
+  char *attribute_class;
+  STRING_LIST *classes;
+
+  if (!content)
+    return;
+
+  classes = new_string_list ();
+  add_string (builtin_command_name (cmd), classes);
+
+  attribute_class = html_attribute_class (self, "div", classes);
+  text_append (result, attribute_class);
+  text_append_n (result, ">", 1);
+  text_append (result, content);
+  text_append_n (result, "</div>", 6);
   free (attribute_class);
   destroy_strings_list (classes);
 }
 
 void
-convert_subtitle_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_menu_command (CONVERTER *self, const enum command_id cmd,
+                           const ELEMENT *element,
+                           const HTML_ARGS_FORMATTED *args_formatted,
+                           const char *content, TEXT *result)
 {
-  const char *arg = 0;
   char *attribute_class;
   STRING_LIST *classes;
 
-  if (args_formatted->number > 0
-      && args_formatted->args[0].formatted[AFT_type_normal]
-      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
-    arg = args_formatted->args[0].formatted[AFT_type_normal];
-  else
+  if (cmd == CM_detailmenu)
+    {
+      if (content)
+        text_append (result, content);
+      return;
+    }
+
+  self->shared_conversion_state.html_menu_entry_index = 0;
+
+  if (!content || content[strspn (content, whitespace_chars)] == '\0')
     return;
 
   if (html_in_string (self))
     {
-      text_append (result, arg);
+      text_append (result, content);
       return;
     }
 
   classes = new_string_list ();
   add_string (builtin_command_name (cmd), classes);
 
-  attribute_class = html_attribute_class (self, "h3", classes);
+  attribute_class = html_attribute_class (self, "table", classes);
   text_append (result, attribute_class);
   text_append_n (result, ">", 1);
-  text_append (result, arg);
-  text_append_n (result, "</h3>", 5);
+  if (html_inside_preformatted (self))
+    text_append_n (result, "<tr><td>", 8);
   text_append_n (result, "\n", 1);
+  text_append (result, content);
+  if (html_inside_preformatted (self))
+    text_append_n (result, "</td></tr>", 10);
+  text_append_n (result, "</table>\n", 9);
 
   free (attribute_class);
   destroy_strings_list (classes);
 }
 
+static char *type_number_float_array[] = {"type-number-float"};
+static const STRING_LIST type_number_float_classes
+  = {type_number_float_array, 1, 1};
+
 void
-convert_insertcopying_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_float_command (CONVERTER *self, const enum command_id cmd,
+                            const ELEMENT *element,
+                            const HTML_ARGS_FORMATTED *args_formatted,
+                            const char *content, TEXT *result)
 {
-  if (self->document->global_commands.copying)
-    {
-      ELEMENT *tmp = new_element (ET_NONE);
-      tmp->e.c->contents = 
self->document->global_commands.copying->e.c->contents;
-      convert_to_html_internal (self, tmp, result, "convert insertcopying");
-      tmp->e.c->contents.list = 0;
-      destroy_element (tmp);
-    }
-}
-
-static char *caption_in_listoffloats_array[] = {"caption-in-listoffloats"};
-static const STRING_LIST caption_in_listoffloats_classes
-  = {caption_in_listoffloats_array, 1, 1};
-static char *shortcaption_in_listoffloats_array[]
-  = {"shortcaption-in-listoffloats"};
-static const STRING_LIST shortcaption_in_listoffloats_classes
-  = {shortcaption_in_listoffloats_array, 1, 1};
-
-void
-convert_listoffloats_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  const LISTOFFLOATS_TYPE_LIST *listoffloats;
-  const char *listoffloats_name;
-  size_t i;
-
-  if (html_in_string (self))
-    return;
-
-  listoffloats = &self->document->listoffloats;
-
-  if (!listoffloats->number)
-    return;
-
-  listoffloats_name = lookup_extra_string (element, AI_key_float_type);
-
-  for (i = 0; i < listoffloats->number; i++)
-    {
-      const LISTOFFLOATS_TYPE *float_types = &listoffloats->float_types[i];
-      if (!strcmp (float_types->type, listoffloats_name))
-        {
-          char *attribute_class;
-          char *multiple_pass_str;
-          STRING_LIST *classes;
-          size_t j;
-          int *formatted_listoffloats_nr;
-
-          if (float_types->float_list.number <= 0)
-            return;
-
-          formatted_listoffloats_nr
-            = &self->shared_conversion_state.formatted_listoffloats_nr[i];
-          (*formatted_listoffloats_nr)++;
-          if (*formatted_listoffloats_nr > 1)
-            xasprintf (&multiple_pass_str, "listoffloats-%d",
-                                           (*formatted_listoffloats_nr) - 1);
-          else
-            multiple_pass_str = "listoffloats";
-
-          classes = new_string_list ();
-          add_string (builtin_command_name (cmd), classes);
-
-          attribute_class = html_attribute_class (self, "dl", classes);
-          text_append (result, attribute_class);
-          text_append_n (result, ">\n", 2);
-
-          for (j = 0; j < float_types->float_list.number; j++)
-            {
-              char *caption_attribute_class;
-              const ELEMENT *caption_element;
-              const STRING_LIST *caption_classes = 0;
-              const ELEMENT *float_elt = float_types->float_list.list[j];
-              char *float_href = html_command_href (self, float_elt, 0, 0, 0);
-              char *float_text;
-
-              if (!float_href)
-                continue;
-
-              text_append_n (result, "<dt>", 4);
-              float_text = html_command_text (self, float_elt, 0);
-              if (float_text && strlen (float_text))
-                {
-                  if (strlen (float_href))
-                    {
-                      text_printf (result, "<a href=\"%s\">%s</a>",
-                                   float_href, float_text);
-                    }
-                  else /* not sure that it can happen */
-                    {
-                      text_append (result, float_text);
-                    }
-                }
-
-              text_append_n (result, "</dt>", 5);
-
-              free (float_text);
-              free (float_href);
-
-              caption_element = lookup_extra_element (float_elt,
-                                                      AI_key_shortcaption);
-              if (caption_element)
-                caption_classes = &shortcaption_in_listoffloats_classes;
-              else
-                {
-                  caption_element = lookup_extra_element (float_elt,
-                                                          AI_key_caption);
-                  if (caption_element)
-                    caption_classes = &caption_in_listoffloats_classes;
-                }
-
-              caption_attribute_class = html_attribute_class (self, "dd",
-                                                              caption_classes);
-              text_append (result, caption_attribute_class);
-              free (caption_attribute_class);
-              text_append_n (result, ">", 1);
-              if (caption_element)
-                {
-                  char *caption_text
-                    = convert_tree_new_formatting_context (self,
-                        caption_element->e.c->args.list[0],
-                        builtin_command_name (cmd),
-                        multiple_pass_str, 0, 0);
-                  text_append (result, caption_text);
-                  free (caption_text);
-                }
-              text_append_n (result, "</dd>\n", 6);
-            }
-          text_append_n (result, "</dl>\n", 6);
-
-          if (*formatted_listoffloats_nr > 1)
-            free (multiple_pass_str);
-          free (attribute_class);
-          destroy_strings_list (classes);
-        }
-    }
-}
-
-void
-convert_menu_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  char *attribute_class;
-  STRING_LIST *classes;
-
-  if (cmd == CM_detailmenu)
-    {
-      if (content)
-        text_append (result, content);
-      return;
-    }
-
-  self->shared_conversion_state.html_menu_entry_index = 0;
-
-  if (!content || content[strspn (content, whitespace_chars)] == '\0')
-    return;
-
-  if (html_in_string (self))
-    {
-      text_append (result, content);
-      return;
-    }
-
-  classes = new_string_list ();
-  add_string (builtin_command_name (cmd), classes);
-
-  attribute_class = html_attribute_class (self, "table", classes);
-  text_append (result, attribute_class);
-  text_append_n (result, ">", 1);
-  if (html_inside_preformatted (self))
-    text_append_n (result, "<tr><td>", 8);
-  text_append_n (result, "\n", 1);
-  text_append (result, content);
-  if (html_inside_preformatted (self))
-    text_append_n (result, "</td></tr>", 10);
-  text_append_n (result, "</table>\n", 9);
-
-  free (attribute_class);
-  destroy_strings_list (classes);
-}
-
-static char *type_number_float_array[] = {"type-number-float"};
-static const STRING_LIST type_number_float_classes
-  = {type_number_float_array, 1, 1};
-
-void
-convert_float_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  char *attribute_class;
-  STRING_LIST *classes;
+  char *attribute_class;
+  STRING_LIST *classes;
 
   const char *id;
   char *prepended_text = 0;
@@ -10815,10 +10322,10 @@ convert_float_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_quotation_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_quotation_command (CONVERTER *self, const enum command_id cmd,
+                                const ELEMENT *element,
+                                const HTML_ARGS_FORMATTED *args_formatted,
+                                const char *content, TEXT *result)
 {
   const CONST_ELEMENT_LIST *authors;
 
@@ -10891,10 +10398,10 @@ convert_quotation_command (CONVERTER *self, const 
enum command_id cmd,
 }
 
 void
-convert_cartouche_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_cartouche_command (CONVERTER *self, const enum command_id cmd,
+                                const ELEMENT *element,
+                                const HTML_ARGS_FORMATTED *args_formatted,
+                                const char *content, TEXT *result)
 {
   char *attribute_class;
   STRING_LIST *classes;
@@ -11000,13 +10507,7 @@ html_convert_css_string (CONVERTER *self, const 
ELEMENT *element,
   return result;
 }
 
-typedef struct SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD {
-    enum command_id cmd;
-    char *string;
-    char *saved;
-} SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD;
-
-static SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD
+SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD
             special_list_mark_css_string_no_arg_command[] = {
  {CM_minus, "\\2212 ", 0},
  {0, 0, 0},
@@ -11041,10 +10542,10 @@ html_convert_css_string_for_list_mark (CONVERTER 
*self, const ELEMENT *element,
 }
 
 void
-convert_itemize_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_itemize_command (CONVERTER *self, const enum command_id cmd,
+                              const ELEMENT *element,
+                              const HTML_ARGS_FORMATTED *args_formatted,
+                              const char *content, TEXT *result)
 {
   const ELEMENT *command_as_argument;
   const char *command_as_argument_name = 0;
@@ -11124,10 +10625,10 @@ convert_itemize_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_enumerate_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_enumerate_command (CONVERTER *self, const enum command_id cmd,
+                                const ELEMENT *element,
+                                const HTML_ARGS_FORMATTED *args_formatted,
+                                const char *content, TEXT *result)
 {
   STRING_LIST *classes;
   char *attribute_class;
@@ -11211,10 +10712,10 @@ convert_enumerate_command (CONVERTER *self, const 
enum command_id cmd,
 }
 
 void
-convert_multitable_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_multitable_command (CONVERTER *self, const enum command_id cmd,
+                                 const ELEMENT *element,
+                                 const HTML_ARGS_FORMATTED *args_formatted,
+                                 const char *content, TEXT *result)
 {
   STRING_LIST *classes;
   char *attribute_class;
@@ -11240,10 +10741,10 @@ convert_multitable_command (CONVERTER *self, const 
enum command_id cmd,
 }
 
 void
-convert_xtable_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_xtable_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
 {
   STRING_LIST *classes;
   char *attribute_class;
@@ -11268,53 +10769,322 @@ convert_xtable_command (CONVERTER *self, const enum 
command_id cmd,
   text_append_n (result, "</dl>\n", 6);
 }
 
-static char *table_term_preformatted_code_array[]
-  = {"table-term-preformatted-code"};
-static const STRING_LIST table_term_preformatted_code_classes
-  = {table_term_preformatted_code_array, 1, 1};
+void
+html_convert_verbatiminclude_command (CONVERTER *self,
+                                      const enum command_id cmd,
+                                      const ELEMENT *element,
+                                  const HTML_ARGS_FORMATTED *args_formatted,
+                                  const char *content, TEXT *result)
+{
+  ELEMENT *verbatim_include_verbatim
+    = expand_verbatiminclude (&self->error_messages, self->conf,
+                              &self->document->global_info, element);
+
+  if (verbatim_include_verbatim)
+    {
+      add_tree_to_build (self, verbatim_include_verbatim);
+      convert_to_html_internal (self, verbatim_include_verbatim,
+                                result, "convert verbatiminclude");
+      remove_tree_to_build (self, verbatim_include_verbatim);
+      destroy_element_and_children (verbatim_include_verbatim);
+    }
+}
 
 void
-convert_item_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_sp_command (CONVERTER *self, const enum command_id cmd,
+                         const ELEMENT *element,
+                         const HTML_ARGS_FORMATTED *args_formatted,
+                         const char *content, TEXT *result)
 {
-  if (html_in_string (self))
+  const STRING_LIST *misc_args = lookup_extra_misc_args (element,
+                                                AI_key_misc_args);
+  unsigned int sp_nr = 1;
+  unsigned int i;
+
+  if (misc_args && misc_args->number > 0)
     {
-      if (content)
-        text_append (result, content);
-      return;
+      const char *sp_number_string = misc_args->list[0];
+      sp_nr = strtoul (sp_number_string, NULL, 10);
     }
 
- if (element->parent && element_builtin_cmd (element->parent) == CM_itemize)
+  if (html_in_preformatted_context (self) || html_in_string (self))
     {
-      if (content
-          && content[strspn (content, whitespace_chars)] != '\0')
-        {
-          text_printf (result, "<li>%s</li>", content);
-        }
+      for (i = 0; i < sp_nr; i++)
+        text_append_n (result, "\n", 1);
     }
-  else if (element->parent
-           && element_builtin_cmd (element->parent) == CM_enumerate)
+  else
     {
-      if (content
-          && content[strspn (content, whitespace_chars)] != '\0')
+      for (i = 0; i < sp_nr; i++)
         {
-          text_printf (result, "<li> %s</li>", content);
+          text_append_n (result, self->line_break_element.string,
+                                 self->line_break_element.len);
+         text_append_n (result, "\n", 1);
         }
     }
-  else if (element->parent && element->parent->type == ET_table_term)
-    {
-      if (element->e.c->args.number > 0
-          && element->e.c->args.list[0]->e.c->contents.number > 0)
-        {
-          ELEMENT *converted_e;
-          TREE_ADDED_ELEMENTS *tree;
-          char *anchor = 0;
-          const char *index_entry_id;
-          const char *pre_class_close = 0;
+}
 
-          if (cmd != CM_item)
+void
+html_convert_exdent_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
+{
+  char *pending_formatted = html_get_pending_formatted_inline_content (self);
+  const char *arg = 0;
+  char *attribute_class;
+  STRING_LIST *classes;
+
+  /* args_formatted null does not seems to be possible in practice */
+  if (args_formatted && args_formatted->number > 0
+      && args_formatted->args[0].formatted[AFT_type_normal]
+      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
+    arg = args_formatted->args[0].formatted[AFT_type_normal];
+
+  if (html_in_string (self))
+    {
+      if (pending_formatted)
+        {
+          text_append (result, pending_formatted);
+          free (pending_formatted);
+        }
+      if (arg)
+        text_append (result, arg);
+      text_append_n (result, "\n", 1);
+      return;
+    }
+
+  classes = new_string_list ();
+  add_string (builtin_command_name (cmd), classes);
+
+  if (html_in_preformatted_context (self))
+    attribute_class = html_attribute_class (self, "pre", classes);
+  else
+    attribute_class = html_attribute_class (self, "p", classes);
+
+  text_append (result, attribute_class);
+  text_append_n (result, ">", 1);
+  if (pending_formatted)
+    {
+      text_append (result, pending_formatted);
+      free (pending_formatted);
+    }
+  if (arg)
+    text_append (result, arg);
+  text_append_n (result, "\n", 1);
+  if (html_in_preformatted_context (self))
+    text_append_n (result, "</pre>", 6);
+  else
+    text_append_n (result, "</p>", 4);
+
+  free (attribute_class);
+  destroy_strings_list (classes);
+}
+
+void
+html_convert_center_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
+{
+  char *arg = 0;
+  char *attribute_class;
+  STRING_LIST *classes;
+
+  /* args_formatted null does not seems to be possible in practice */
+  if (args_formatted && args_formatted->number > 0
+      && args_formatted->args[0].formatted[AFT_type_normal]
+      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
+    arg = args_formatted->args[0].formatted[AFT_type_normal];
+  else
+    return;
+
+  if (html_in_string (self))
+    {
+      text_append (result, arg);
+      text_append_n (result, "\n", 1);
+      return;
+    }
+
+  classes = new_string_list ();
+  add_string (builtin_command_name (cmd), classes);
+
+  attribute_class = html_attribute_class (self, "div", classes);
+  text_append (result, attribute_class);
+  text_append_n (result, ">", 1);
+  text_append (result, arg);
+  text_append_n (result, "\n", 1);
+  text_append_n (result, "</div>", 6);
+
+  free (attribute_class);
+  destroy_strings_list (classes);
+}
+
+void
+html_convert_author_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element,
+                             const HTML_ARGS_FORMATTED *args_formatted,
+                             const char *content, TEXT *result)
+{
+  const char *arg = 0;
+  char *attribute_class;
+  STRING_LIST *classes;
+
+  const ELEMENT *titlepage = lookup_extra_element (element, AI_key_titlepage);
+
+  if (!titlepage)
+    return;
+
+  if (args_formatted->number > 0
+      && args_formatted->args[0].formatted[AFT_type_normal]
+      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
+    arg = args_formatted->args[0].formatted[AFT_type_normal];
+  else
+    return;
+
+  if (html_in_string (self))
+    {
+      text_append (result, arg);
+      text_append_n (result, "\n", 1);
+      return;
+    }
+
+  classes = new_string_list ();
+  add_string (builtin_command_name (cmd), classes);
+
+  attribute_class = html_attribute_class (self, "strong", classes);
+  text_append (result, attribute_class);
+  text_append_n (result, ">", 1);
+  text_append (result, arg);
+  text_append_n (result, "</strong>", 9);
+  text_append_n (result, self->line_break_element.string,
+                         self->line_break_element.len);
+  text_append_n (result, "\n", 1);
+
+  free (attribute_class);
+  destroy_strings_list (classes);
+}
+
+void
+html_convert_title_command (CONVERTER *self, const enum command_id cmd,
+                            const ELEMENT *element,
+                            const HTML_ARGS_FORMATTED *args_formatted,
+                            const char *content, TEXT *result)
+{
+  const char *arg = 0;
+  char *attribute_class;
+  STRING_LIST *classes;
+
+  if (args_formatted->number > 0
+      && args_formatted->args[0].formatted[AFT_type_normal]
+      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
+    arg = args_formatted->args[0].formatted[AFT_type_normal];
+  else
+    return;
+
+  if (html_in_string (self))
+    {
+      text_append (result, arg);
+      return;
+    }
+
+  classes = new_string_list ();
+  add_string (builtin_command_name (cmd), classes);
+
+  attribute_class = html_attribute_class (self, "h1", classes);
+  text_append (result, attribute_class);
+  text_append_n (result, ">", 1);
+  text_append (result, arg);
+  text_append_n (result, "</h1>", 5);
+  text_append_n (result, "\n", 1);
+
+  free (attribute_class);
+  destroy_strings_list (classes);
+}
+
+void
+html_convert_subtitle_command (CONVERTER *self, const enum command_id cmd,
+                               const ELEMENT *element,
+                               const HTML_ARGS_FORMATTED *args_formatted,
+                               const char *content, TEXT *result)
+{
+  const char *arg = 0;
+  char *attribute_class;
+  STRING_LIST *classes;
+
+  if (args_formatted->number > 0
+      && args_formatted->args[0].formatted[AFT_type_normal]
+      && strlen (args_formatted->args[0].formatted[AFT_type_normal]))
+    arg = args_formatted->args[0].formatted[AFT_type_normal];
+  else
+    return;
+
+  if (html_in_string (self))
+    {
+      text_append (result, arg);
+      return;
+    }
+
+  classes = new_string_list ();
+  add_string (builtin_command_name (cmd), classes);
+
+  attribute_class = html_attribute_class (self, "h3", classes);
+  text_append (result, attribute_class);
+  text_append_n (result, ">", 1);
+  text_append (result, arg);
+  text_append_n (result, "</h3>", 5);
+  text_append_n (result, "\n", 1);
+
+  free (attribute_class);
+  destroy_strings_list (classes);
+}
+
+static char *table_term_preformatted_code_array[]
+  = {"table-term-preformatted-code"};
+static const STRING_LIST table_term_preformatted_code_classes
+  = {table_term_preformatted_code_array, 1, 1};
+
+void
+html_convert_item_command (CONVERTER *self, const enum command_id cmd,
+                           const ELEMENT *element,
+                           const HTML_ARGS_FORMATTED *args_formatted,
+                           const char *content, TEXT *result)
+{
+  if (html_in_string (self))
+    {
+      if (content)
+        text_append (result, content);
+      return;
+    }
+
+ if (element->parent && element_builtin_cmd (element->parent) == CM_itemize)
+    {
+      if (content
+          && content[strspn (content, whitespace_chars)] != '\0')
+        {
+          text_printf (result, "<li>%s</li>", content);
+        }
+    }
+  else if (element->parent
+           && element_builtin_cmd (element->parent) == CM_enumerate)
+    {
+      if (content
+          && content[strspn (content, whitespace_chars)] != '\0')
+        {
+          text_printf (result, "<li> %s</li>", content);
+        }
+    }
+  else if (element->parent && element->parent->type == ET_table_term)
+    {
+      if (element->e.c->args.number > 0
+          && element->e.c->args.list[0]->e.c->contents.number > 0)
+        {
+          ELEMENT *converted_e;
+          TREE_ADDED_ELEMENTS *tree;
+          char *anchor = 0;
+          const char *index_entry_id;
+          const char *pre_class_close = 0;
+
+          if (cmd != CM_item)
             text_append_n (result, "<dt>", 4);
 
           index_entry_id = html_command_id (self, element);
@@ -11415,10 +11185,10 @@ trim_trailing_content (const char *content)
 }
 
 void
-convert_tab_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_tab_command (CONVERTER *self, const enum command_id cmd,
+                          const ELEMENT *element,
+                          const HTML_ARGS_FORMATTED *args_formatted,
+                          const char *content, TEXT *result)
 {
   char *trimmed_content;
   size_t cell_nr;
@@ -11482,5588 +11252,3876 @@ convert_tab_command (CONVERTER *self, const enum 
command_id cmd,
 }
 
 void
-convert_xref_commands (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+html_convert_insertcopying_command (CONVERTER *self, const enum command_id cmd,
+                                    const ELEMENT *element,
+                                    const HTML_ARGS_FORMATTED *args_formatted,
+                                    const char *content, TEXT *result)
 {
-  char *name = 0;
-  HTML_ARG_FORMATTED *file_arg = 0;
-  char *file = 0;
-  const char *book = 0;
-  const ELEMENT *arg_node = 0;
-  const ELEMENT *target_node = 0;
-  ELEMENT *tree = 0;
-
-  ELEMENT *book_element = 0;
-  ELEMENT *reference_element = 0;
-
-  /* happens with bogus @-commands without argument, maybe only
-     at the end of a document */
-  if (!args_formatted)
-    return;
-
-  if (cmd != CM_link && cmd != CM_inforef && args_formatted->number > 2
-      && args_formatted->args[2].formatted[AFT_type_normal]
-      && strlen (args_formatted->args[2].formatted[AFT_type_normal]))
-    {
-      name = strdup (args_formatted->args[2].formatted[AFT_type_normal]);
-    }
-  else if (args_formatted->number > 1
-           && args_formatted->args[1].formatted[AFT_type_normal]
-           && strlen (args_formatted->args[1].formatted[AFT_type_normal]))
+  if (self->document->global_commands.copying)
     {
-      name = strdup (args_formatted->args[1].formatted[AFT_type_normal]);
+      ELEMENT *tmp = new_element (ET_NONE);
+      tmp->e.c->contents = 
self->document->global_commands.copying->e.c->contents;
+      convert_to_html_internal (self, tmp, result, "convert insertcopying");
+      tmp->e.c->contents.list = 0;
+      destroy_element (tmp);
     }
+}
 
-  if (cmd == CM_link || cmd == CM_inforef)
-    {
-      if (args_formatted->number > 2)
-        file_arg = &args_formatted->args[2];
-    }
-  else if (args_formatted->number > 3)
-    file_arg = &args_formatted->args[3];
+static char *caption_in_listoffloats_array[] = {"caption-in-listoffloats"};
+static const STRING_LIST caption_in_listoffloats_classes
+  = {caption_in_listoffloats_array, 1, 1};
+static char *shortcaption_in_listoffloats_array[]
+  = {"shortcaption-in-listoffloats"};
+static const STRING_LIST shortcaption_in_listoffloats_classes
+  = {shortcaption_in_listoffloats_array, 1, 1};
 
-  if (file_arg && file_arg->formatted[AFT_type_filenametext]
-      && strlen (file_arg->formatted[AFT_type_filenametext]))
-    {
-      file = strdup (file_arg->formatted[AFT_type_filenametext]);
-    }
+void
+html_convert_listoffloats_command (CONVERTER *self, const enum command_id cmd,
+                                   const ELEMENT *element,
+                                   const HTML_ARGS_FORMATTED *args_formatted,
+                                   const char *content, TEXT *result)
+{
+  const LISTOFFLOATS_TYPE_LIST *listoffloats;
+  const char *listoffloats_name;
+  size_t i;
 
-  if (args_formatted->number > 4
-      && args_formatted->args[4].formatted[AFT_type_normal]
-      && strlen (args_formatted->args[4].formatted[AFT_type_normal]))
-    book = args_formatted->args[4].formatted[AFT_type_normal];
+  if (html_in_string (self))
+    return;
 
-  if (element->e.c->args.number > 0)
-    arg_node = element->e.c->args.list[0];
+  listoffloats = &self->document->listoffloats;
 
-  /* check for internal reference */
-  if (cmd != CM_inforef && !book && !file && arg_node)
+  if (!listoffloats->number)
+    return;
+
+  listoffloats_name = lookup_extra_string (element, AI_key_float_type);
+
+  for (i = 0; i < listoffloats->number; i++)
     {
-      const char *normalized = lookup_extra_string (arg_node, 
AI_key_normalized);
-      const ELEMENT *manual_content = lookup_extra_container (arg_node,
-                                                      AI_key_manual_content);
-      if (normalized && !manual_content)
+      const LISTOFFLOATS_TYPE *float_types = &listoffloats->float_types[i];
+      if (!strcmp (float_types->type, listoffloats_name))
         {
-          target_node = find_identifier_target (
-                                  &self->document->identifiers_target,
-                                  normalized);
-        }
-    }
+          char *attribute_class;
+          char *multiple_pass_str;
+          STRING_LIST *classes;
+          size_t j;
+          int *formatted_listoffloats_nr;
 
-  /* internal reference */
-  if (target_node)
-    {
-      char *href = 0;
-      STRING_LIST *classes = 0;
-     /* This is the node if USE_NODES, otherwise this may be the sectioning
-        command (if the sectioning command is really associated to the node) */
-      const ELEMENT *target_root
-             = html_command_root_element_command (self, target_node);
-      const ELEMENT *associated_section = lookup_extra_element (target_node,
-                                                   AI_key_associated_section);
-      reference_element = new_text_element (ET__converted);
-      NAMED_STRING_ELEMENT_LIST *substrings
-                                       = new_named_string_element_list ();
+          if (float_types->float_list.number <= 0)
+            return;
 
-      if (!associated_section || associated_section != target_root)
-        target_root = target_node;
+          formatted_listoffloats_nr
+            = &self->shared_conversion_state.formatted_listoffloats_nr[i];
+          (*formatted_listoffloats_nr)++;
+          if (*formatted_listoffloats_nr > 1)
+            xasprintf (&multiple_pass_str, "listoffloats-%d",
+                                           (*formatted_listoffloats_nr) - 1);
+          else
+            multiple_pass_str = "listoffloats";
 
-      if (!html_in_string (self))
-        href = html_command_href (self, target_root, 0, element, 0);
+          classes = new_string_list ();
+          add_string (builtin_command_name (cmd), classes);
 
-      if (!name)
-        {
-          if (self->conf->xrefautomaticsectiontitle.o.string
-              && !strcmp (self->conf->xrefautomaticsectiontitle.o.string, "on")
-              && associated_section
-        /* this condition avoids infinite recursions, indeed in that case
-           the node will be used and not the section.  There should not be
-           @*ref in nodes, and even if there are, it does not seems to be
-           possible to construct an infinite recursion with nodes only
-           as the node must both be a reference target and refer to a specific
-           target at the same time, which is not possible.
-         */
-             && !command_is_in_referred_command_stack (
-                   &self->referred_command_stack, associated_section, 0))
-            {
-              target_root = associated_section;
-              if (html_in_string (self))
-                name = html_command_text (self, target_root, HTT_string);
-              else
-                name = html_command_text (self, target_root, 
HTT_text_nonumber);
-            }
-          else if (target_node->e.c->cmd == CM_float)
+          attribute_class = html_attribute_class (self, "dl", classes);
+          text_append (result, attribute_class);
+          text_append_n (result, ">\n", 2);
+
+          for (j = 0; j < float_types->float_list.number; j++)
             {
-              if (self->conf->XREF_USE_FLOAT_LABEL.o.integer <= 0)
-                {
-                  if (html_in_string (self))
-                    name = html_command_text (self, target_root, HTT_string);
-                  else
-                    name = html_command_text (self, target_root, 0);
-                }
-              if (!name || !strlen (name))
+              char *caption_attribute_class;
+              const ELEMENT *caption_element;
+              const STRING_LIST *caption_classes = 0;
+              const ELEMENT *float_elt = float_types->float_list.list[j];
+              char *float_href = html_command_href (self, float_elt, 0, 0, 0);
+              char *float_text;
+
+              if (!float_href)
+                continue;
+
+              text_append_n (result, "<dt>", 4);
+              float_text = html_command_text (self, float_elt, 0);
+              if (float_text && strlen (float_text))
                 {
-                  if (args_formatted->number > 1
-                      && args_formatted->args[0].formatted[AFT_type_monospace])
+                  if (strlen (float_href))
                     {
-                      name
-                       = strdup (
-                          
args_formatted->args[0].formatted[AFT_type_monospace]);
+                      text_printf (result, "<a href=\"%s\">%s</a>",
+                                   float_href, float_text);
+                    }
+                  else /* not sure that it can happen */
+                    {
+                      text_append (result, float_text);
                     }
-                  else
-                    name = strdup ("");
                 }
-            }
-          else if (self->conf->XREF_USE_NODE_NAME_ARG.o.integer <= 0
-                   && (self->conf->XREF_USE_NODE_NAME_ARG.o.integer == 0
-                       || !html_in_preformatted_context (self))
-        /* this condition avoids infinite recursions, example with
-           USE_NODES=0 and node referring to the section and section referring
-           to the node */
-                   && !command_is_in_referred_command_stack (
-                         &self->referred_command_stack, target_root, 0))
-            {
-              if (html_in_string (self))
-                name = html_command_text (self, target_root, HTT_string);
-              else
-                name = html_command_text (self, target_root, 
HTT_text_nonumber);
-            }
-          else if (args_formatted->number > 0
-                   && args_formatted->args[0].formatted[AFT_type_monospace])
-            {
-              name
-               = strdup 
(args_formatted->args[0].formatted[AFT_type_monospace]);
-            }
-          else
-            name = strdup ("");
-        }
 
-      if (href)
-        {
-          char *attribute_class;
+              text_append_n (result, "</dt>", 5);
 
-          classes = new_string_list ();
-          add_string (builtin_command_name (cmd), classes);
+              free (float_text);
+              free (float_href);
 
-          attribute_class = html_attribute_class (self, "a", classes);
-          text_append (reference_element->e.text, attribute_class);
-          text_printf (reference_element->e.text, " href=\"%s\">%s</a>",
-                                                href, name);
+              caption_element = lookup_extra_element (float_elt,
+                                                      AI_key_shortcaption);
+              if (caption_element)
+                caption_classes = &shortcaption_in_listoffloats_classes;
+              else
+                {
+                  caption_element = lookup_extra_element (float_elt,
+                                                          AI_key_caption);
+                  if (caption_element)
+                    caption_classes = &caption_in_listoffloats_classes;
+                }
+
+              caption_attribute_class = html_attribute_class (self, "dd",
+                                                              caption_classes);
+              text_append (result, caption_attribute_class);
+              free (caption_attribute_class);
+              text_append_n (result, ">", 1);
+              if (caption_element)
+                {
+                  char *caption_text
+                    = convert_tree_new_formatting_context (self,
+                        caption_element->e.c->args.list[0],
+                        builtin_command_name (cmd),
+                        multiple_pass_str, 0, 0);
+                  text_append (result, caption_text);
+                  free (caption_text);
+                }
+              text_append_n (result, "</dd>\n", 6);
+            }
+          text_append_n (result, "</dl>\n", 6);
+
+          if (*formatted_listoffloats_nr > 1)
+            free (multiple_pass_str);
           free (attribute_class);
           destroy_strings_list (classes);
         }
-      else
-        {
-          text_append (reference_element->e.text, name);
-        }
-      free (href);
+    }
+}
 
-      add_element_to_named_string_element_list (substrings,
-                          "reference_name", reference_element);
-      if (cmd == CM_pxref)
-        {
-          tree = html_cdt_tree ("see {reference_name}",
-                                self, substrings, 0);
-        }
-      else if (cmd == CM_xref)
-        {
-          tree = html_cdt_tree ("See {reference_name}",
-                                self, substrings, 0);
-        }
-      else if (cmd == CM_ref || cmd == CM_link)
-        {
-          tree = html_cdt_tree ("{reference_name}",
-                                self, substrings, 0);
-        }
-      destroy_named_string_element_list (substrings);
+#define SUBENTRIES_MAX_LEVEL 2
+
+static void
+clear_normalized_entry_levels (char **normalized_entry_levels)
+{
+  int i;
+  for (i = 0; i < SUBENTRIES_MAX_LEVEL; i++)
+    {
+      free (normalized_entry_levels[i]);
+      normalized_entry_levels[i] = 0;
     }
-  else
+}
+
+static char *
+normalized_upper_case (ELEMENT *e)
+{
+  char *normalized = convert_to_normalized (e);
+  char *result = to_upper_or_lower_multibyte (normalized, 1);
+  free (normalized);
+  return result;
+}
+
+static void
+printindex_letters_head_foot_internal (CONVERTER *self, const char *index_name,
+                           const enum command_id cmd,
+                           STRING_LIST *entry_classes,
+                           const char *head_or_foot,
+                           const char *letters_header_explanation,
+                           const char *alpha_text,
+                           const char *non_alpha_text, TEXT *result)
+{
+  char *index_name_cmd_class;
+  char *attribute_class;
+
+  xasprintf (&index_name_cmd_class, "%s-letters-%s-%s",
+             index_name, head_or_foot, builtin_command_name (cmd));
+  add_string (index_name_cmd_class, entry_classes);
+  free (index_name_cmd_class);
+  attribute_class = html_attribute_class (self, "table", entry_classes);
+  clear_strings_list (entry_classes);
+  text_append (result, attribute_class);
+  free (attribute_class);
+  text_append_n (result, "><tr><th>", 9);
+
+  /* TRANSLATORS: before list of letters and symbols grouping index entries */
+  translate_convert_to_html_internal ("Jump to", self, 0, 0, result,
+                                      letters_header_explanation);
+  text_append_n (result, ": ", 2);
+  text_append_n (result,
+                 self->special_character[SC_non_breaking_space].string,
+                 self->special_character[SC_non_breaking_space].len);
+  text_append_n (result, " </th><td>", 10);
+  if (non_alpha_text)
+    text_append (result, non_alpha_text);
+  if (non_alpha_text && alpha_text)
     {
-     /* external reference */
-      char *href = 0;
-      char *reference = 0;
-      char *book_reference = 0;
+      text_append_n (result, " ", 1);
+      text_append_n (result,
+                     self->special_character[SC_non_breaking_space].string,
+                     self->special_character[SC_non_breaking_space].len);
+      text_append_n (result, " \n", 2);
+      text_append_n (result,
+                     self->line_break_element.string,
+                     self->line_break_element.len);
+      text_append_n (result, "\n", 1);
+    }
+  if (alpha_text)
+    text_append (result, alpha_text);
+  text_append_n (result, "</td></tr></table>\n", 19);
+}
 
-      NAMED_STRING_ELEMENT_LIST *substrings
-                                       = new_named_string_element_list ();
+void
+html_convert_printindex_command (CONVERTER *self, const enum command_id cmd,
+                                 const ELEMENT *element,
+                                 const HTML_ARGS_FORMATTED *args_formatted,
+                                 const char *content, TEXT *result)
+{
+  const STRING_LIST *misc_args;
+  const char *index_name;
+  const INDEX_SORTED_BY_LETTER *idx;
+  const INDEX_SORTED_BY_LETTER *index_sorted = 0;
+  const char *index_element_id = 0;
+  char **letter_id;
+  char **alpha;
+  char **non_alpha;
+  size_t non_alpha_nr = 0;
+  size_t alpha_nr = 0;
+  int *letter_is_symbol;
+  char **formatted_letters;
+  size_t symbol_idx = 0;
+  size_t normalized_letter_idx = 0;
+  size_t i;
+  char *entry_class_seeentry;
+  char *section_class_seeentry;
+  char *cmd_index_entry_class;
+  char *section_class_seealso;
+  char *cmd_index_section_class;
+  char *summary_letter_cmd;
+  char *attribute_class;
+  TEXT entries_text;
+  TEXT result_index_entries;
+  char *index_name_cmd_class;
+  char *alpha_text = 0;
+  char *non_alpha_text = 0;
+  char *language;
+  INDEX_SORTED_BY_LETTER *index_entries_by_letter
+    = get_converter_indices_sorted_by_letter (self, &language);
 
- /* We setup a label_element based on the node argument and not directly the
-    node argument to be able to use the $file argument */
+  if (!index_entries_by_letter)
+    return;
 
-      ELEMENT *label_element = 0;
-      ELEMENT *manual_content = 0;
-      ELEMENT *node_content = 0;
+  if (html_in_string (self))
+    return;
 
-      if (arg_node)
+  misc_args = lookup_extra_misc_args (element, AI_key_misc_args);
+  if (misc_args && misc_args->number > 0)
+    index_name = misc_args->list[0];
+  else
+    return;
+
+  for (idx = index_entries_by_letter; idx->name; idx++)
+    {
+      if (!strcmp (idx->name, index_name))
         {
-          node_content = lookup_extra_container (arg_node, 
AI_key_node_content);
-          if (node_content)
-            {
-              const char *normalized = lookup_extra_string (arg_node,
-                                                            AI_key_normalized);
-              label_element = new_element (ET_NONE);
-              add_extra_container (label_element, AI_key_node_content,
-                                   copy_container_contents (node_content));
-              if (normalized)
-                add_extra_string_dup (label_element, AI_key_normalized,
-                                      normalized);
-            }
+          index_sorted = idx;
+          break;
         }
+    }
+  if (!index_sorted || !index_sorted->letter_number)
+    return;
 
- /* file argument takes precedence over the file in the node (file)node entry 
*/
-      if (file)
+  if (self->current_output_unit
+      && self->current_output_unit->uc.unit_command)
+    index_element_id
+      = html_command_id (self, self->current_output_unit->uc.unit_command);
+
+  if (!index_element_id)
+    {
+      ROOT_AND_UNIT *root_unit
+        = get_element_root_command_element (self, element);
+      if (root_unit && root_unit->root)
         {
-          if (!label_element)
-            label_element = new_element (ET_NONE);
-          add_extra_container (label_element, AI_key_manual_content,
-                               copy_container_contents (file_arg->arg_tree));
+          index_element_id = html_command_id (self, root_unit->root);
         }
-      else
+      if (!index_element_id)
+    /* to avoid duplicate names, use a prefix that cannot happen in anchors */
+        index_element_id = "t_i";
+      free (root_unit);
+    }
+
+  letter_id = (char **) malloc (index_sorted->letter_number * sizeof (char *));
+  /* we allocate twice as needed here, but it is more practical */
+  alpha = (char **) malloc ((index_sorted->letter_number +1) * sizeof (char 
*));
+  non_alpha = (char **)
+     malloc ((index_sorted->letter_number +1) * sizeof (char *));
+  memset (alpha, 0, (index_sorted->letter_number +1) * sizeof (char *));
+  memset (non_alpha, 0, (index_sorted->letter_number +1) * sizeof (char *));
+  letter_is_symbol
+    = (int *) malloc (index_sorted->letter_number * sizeof (int));
+  formatted_letters = (char **) malloc
+      (index_sorted->letter_number * sizeof (char *));
+
+  for (i = 0; i < index_sorted->letter_number; i++)
+    {
+      const char *letter = index_sorted->letter_entries[i].letter;
+      uint8_t *encoded_u8 = utf8_from_string (letter);
+      ucs4_t next_char;
+      u8_next (&next_char, encoded_u8);
+      letter_is_symbol[i]
+          = !(uc_is_property (next_char, UC_PROPERTY_ALPHABETIC));
+      free (encoded_u8);
+      if (letter_is_symbol[i])
         {
-          manual_content = lookup_extra_container (arg_node,
-                                                 AI_key_manual_content);
+          symbol_idx++;
+          xasprintf (&letter_id[i], "%s_%s_symbol-%zu", index_element_id,
+                                   index_name, symbol_idx);
         }
-
-      if (manual_content)
+      else
         {
-          ELEMENT *root_code;
+          char *normalized_letter;
+          ELEMENT *letter_text = new_text_element (ET_normal_text);
+          text_append (letter_text->e.text, letter);
+          normalized_letter = normalize_transliterate_texinfo (letter_text,
+                                             (self->conf->TEST.o.integer > 0));
+          destroy_element (letter_text);
 
-          if (!label_element)
-            label_element = new_element (ET_NONE);
+          if (strcmp (letter, normalized_letter))
+            {
+              char *tmp_normalized_letter;
+   /* disambiguate, as it could be another letter, case of @l, for example */
+              normalized_letter_idx++;
+              xasprintf (&tmp_normalized_letter, "%s-%zu", normalized_letter,
+                                                 normalized_letter_idx);
+              free (normalized_letter);
+              normalized_letter = tmp_normalized_letter;
+            }
 
-          add_extra_container (label_element, AI_key_manual_content,
-                               copy_container_contents (manual_content));
+          xasprintf (&letter_id[i], "%s_%s_letter-%s", index_element_id,
+                                     index_name, normalized_letter);
+          free (normalized_letter);
+        }
+    }
 
-          /* convert the manual part to file string */
-          root_code = new_element (ET__code);
+  html_new_document_context (self, builtin_command_name (cmd), 0, 0);
 
-          add_to_contents_as_array (root_code, manual_content);
+  STRING_LIST *entry_classes = new_string_list ();
+  STRING_LIST *section_classes  = new_string_list ();
 
-          add_tree_to_build (self, root_code);
-          file = html_convert_tree (self, root_code, "node file in ref");
-          remove_tree_to_build (self, root_code);
-          destroy_element (root_code);
-        }
+  xasprintf (&entry_class_seeentry, "%s-index-see-entry",
+                                    builtin_command_name (cmd));
+  xasprintf (&section_class_seeentry, "%s-index-see-entry-section",
+                                    builtin_command_name (cmd));
+  xasprintf (&cmd_index_entry_class, "%s-index-entry",
+                                   builtin_command_name (cmd));
+  xasprintf (&section_class_seealso, "%s-index-see-also",
+                                   builtin_command_name (cmd));
+  xasprintf (&cmd_index_section_class, "%s-index-section",
+                                   builtin_command_name (cmd));
+  xasprintf (&summary_letter_cmd, "summary-letter-%s",
+                                   builtin_command_name (cmd));
 
-      if (!name)
-        {
-          if (book)
-            {
-              if (node_content)
-                {
-                  char *node_name;
-                  ELEMENT *node_no_file_tree = new_element (ET__code);
-                  add_to_contents_as_array (node_no_file_tree, node_content);
+  text_init (&entries_text);
+  text_init (&result_index_entries);
 
-                  add_tree_to_build (self, node_no_file_tree);
-                  node_name = html_convert_tree (self, node_no_file_tree,
-                                                 "node in ref");
-                  remove_tree_to_build (self, node_no_file_tree);
-                  destroy_element (node_no_file_tree);
+  /* Next do the entries to determine the letters that are not empty */
+  for (i = 0; i < index_sorted->letter_number; i++)
+    {
+      const INDEX_ENTRY *first_entry = 0;
+      const LETTER_INDEX_ENTRIES *letter_entry
+         = &index_sorted->letter_entries[i];
+      const char *letter = letter_entry->letter;
+      size_t entry_nr = 0;
+    /* since we normalize, a different formatting will not trigger a new
+       formatting of the main entry or a subentry level.  This is the
+       same for Texinfo TeX */
+      size_t j;
 
-                  if (node_name && strcmp (node_name, "Top"))
-                    name = node_name;
-                  else
-                    free (node_name);
-                }
-            }
-          else
-            {
-              if (label_element)
-                name = html_command_text (self, label_element, 0);
-              if (!name && args_formatted->number > 0
-                  && args_formatted->args[0].formatted[AFT_type_monospace]
-                  && strcmp 
(args_formatted->args[0].formatted[AFT_type_monospace],
-                             "Top"))
-                name
-               = strdup 
(args_formatted->args[0].formatted[AFT_type_monospace]);
-            }
-        }
+      char *prev_normalized_entry_levels[SUBENTRIES_MAX_LEVEL+1];
+      memset (prev_normalized_entry_levels, 0,
+              sizeof (char *) * (SUBENTRIES_MAX_LEVEL +1));
+      text_reset (&entries_text);
 
-      if (label_element)
+      for (j = 0; j < letter_entry->entries_number; j++)
         {
-          if (!html_in_string (self))
-            href = html_command_href (self, label_element, 0, element, 0);
-          destroy_element (label_element);
-        }
+          int level;
+          int in_code;
+          int *formatted_index_entry_nr;
+          char *multiple_pass_str;
+          size_t entry_index_nr;
+          const INDEX *entry_index;
+          const ELEMENT *seeentry;
+          const ELEMENT *seealso;
+          char *new_normalized_entry_levels[SUBENTRIES_MAX_LEVEL +1];
+          ELEMENT *entry_trees[SUBENTRIES_MAX_LEVEL +1];
+          int last_entry_level;
+          char *convert_info;
+          ELEMENT *target_element;
+          const ELEMENT *associated_command = 0;
+          char *entry_href;
+          ELEMENT *entry_tree;
+          const ELEMENT *subentry;
+          ELEMENT_LIST *other_subentries_tree = 0;
+          int subentry_level = 1;
+          ELEMENT *entry_content_element;
+          ELEMENT *entry_ref_tree;
+          INDEX_ENTRY *index_entry_ref = letter_entry->entries[j];
+          ELEMENT *main_entry_element = index_entry_ref->entry_element;
+          const INDEX_ENTRY_LOCATION *index_entry_info
+             = lookup_extra_index_entry (main_entry_element,
+                                         AI_key_index_entry);
+          int entry_number = index_entry_info->number;
+          entry_nr++;
 
-      if (href)
-        {
-       /* attribute to distiguish links to Texinfo manuals from other links
-          and to provide manual name of target */
-          TEXT manual_name_attribute;
-          text_init (&manual_name_attribute);
-          text_append (&manual_name_attribute, "");
-          if (file && self->conf->NO_CUSTOM_HTML_ATTRIBUTE.o.integer <= 0)
-            {
-              text_append_n (&manual_name_attribute, "data-manual=\"", 13);
-              format_protect_text (self, file, &manual_name_attribute);
-              text_append_n (&manual_name_attribute, "\" ", 2);
-            }
-          if (name)
-            {
-              xasprintf (&reference, "<a %shref=\"%s\">%s</a>",
-                         manual_name_attribute.text, href, name);
-            }
-          else if (book)
+          if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0)
             {
-              xasprintf (&book_reference, "<a %shref=\"%s\">%s</a>",
-                         manual_name_attribute.text, href, book);
+              const ELEMENT *element_node
+                = lookup_extra_element (main_entry_element,
+                                        AI_key_element_node);
+              if (element_node)
+                {
+                  const char *normalized = lookup_extra_string (element_node,
+                                                           AI_key_normalized);
+                  if (normalized && !strcmp (normalized, "Top"))
+                    continue;
+                }
             }
-          free (manual_name_attribute.text);
-          free (href);
-        }
 
+          memset (new_normalized_entry_levels, 0,
+                  sizeof (char *) * (SUBENTRIES_MAX_LEVEL +1));
 
-      if (book && reference)
-        {
-          book_element = new_text_element (ET__converted);
-          text_append (book_element->e.text, book);
-          reference_element = new_text_element (ET__converted);
-          text_append (reference_element->e.text, reference);
+          entry_content_element = index_content_element (main_entry_element, 
0);
+          entry_index_nr
+             = index_number_index_by_name (&self->sorted_index_names,
+                                           index_entry_ref->index_name);
+          entry_index = self->sorted_index_names.list[entry_index_nr-1];
 
-          add_element_to_named_string_element_list (substrings,
-                                          "book", book_element);
-          add_element_to_named_string_element_list (substrings,
-                                         "reference", reference_element);
-          if (cmd == CM_pxref)
-            {
-              tree = html_cdt_tree ("see {reference} in @cite{{book}}",
-                                    self, substrings, 0);
-            }
-          else if (cmd == CM_xref)
-            {
-              tree = html_cdt_tree ("See {reference} in @cite{{book}}",
-                                    self, substrings, 0);
-            }
-          else /* @ref */
-            {
-              tree = html_cdt_tree ("{reference} in @cite{{book}}",
-                                    self, substrings, 0);
-            }
-        }
-      else if (book_reference)
-        {
-          book_element = new_text_element (ET__converted);
-          text_append (book_element->e.text, book_reference);
+ /* to avoid double error messages, call convert_tree_new_formatting_context
+    below with a multiple_pass argument if an entry was already formatted once,
+    for example if there are multiple printindex. */
+          formatted_index_entry_nr
+            = &self->shared_conversion_state
+               .formatted_index_entries[entry_index_nr -1][entry_number -1];
+          (*formatted_index_entry_nr)++;
 
-          add_element_to_named_string_element_list (substrings,
-                                          "book_reference", book_element);
-          if (cmd == CM_pxref)
-            {
-              tree = html_cdt_tree ("see @cite{{book_reference}}",
-                                    self, substrings, 0);
-            }
-          else if (cmd == CM_xref || cmd == CM_inforef)
-            {
-              tree = html_cdt_tree ("See @cite{{book_reference}}",
-                                    self, substrings, 0);
-            }
-          else /* @ref */
-            {
-              tree = html_cdt_tree ("@cite{{book_reference}}",
-                                    self, substrings, 0);
-            }
-        }
-      else if (book && name)
-        {
-          book_element = new_text_element (ET__converted);
-          text_append (book_element->e.text, book);
-          reference_element = new_text_element (ET__converted);
-          text_append (reference_element->e.text, name);
+          if (*formatted_index_entry_nr > 1)
+            xasprintf (&multiple_pass_str, "index-formatted-%d",
+                       *formatted_index_entry_nr);
 
-          add_element_to_named_string_element_list (substrings,
-                                          "book", book_element);
-          add_element_to_named_string_element_list (substrings,
-                                         "section", reference_element);
-          if (cmd == CM_pxref)
-            {
-              tree = html_cdt_tree ("see `{section}' in @cite{{book}}",
-                                    self, substrings, 0);
-            }
-          else if (cmd == CM_xref || cmd == CM_inforef)
-            {
-              tree = html_cdt_tree ("See `{section}' in @cite{{book}}",
-                                    self, substrings, 0);
-            }
-          else /* @ref */
-            {
-              tree = html_cdt_tree ("`{section}' in @cite{{book}}",
-                                    self, substrings, 0);
-            }
-        }
-      else if (book)
-        {
-          book_element = new_text_element (ET__converted);
-          text_append (book_element->e.text, book);
+          in_code = entry_index->in_code;
 
-          add_element_to_named_string_element_list (substrings,
-                                          "book", book_element);
-          if (cmd == CM_pxref)
-            {
-              tree = html_cdt_tree ("see @cite{{book}}",
-                                    self, substrings, 0);
-            }
-          else if (cmd == CM_xref || cmd == CM_inforef)
-            {
-              tree = html_cdt_tree ("See @cite{{book}}",
-                                    self, substrings, 0);
-            }
-          else /* @ref */
-            {
-              tree = html_cdt_tree ("@cite{{book}}",
-                                    self, substrings, 0);
-            }
-        }
-      else if (reference)
-        {
-          reference_element = new_text_element (ET__converted);
-          text_append (reference_element->e.text, reference);
+          if (in_code)
+            entry_ref_tree = new_element (ET__code);
+          else
+            entry_ref_tree = new_element (ET_NONE);
 
-          add_element_to_named_string_element_list (substrings,
-                                         "reference", reference_element);
-          if (cmd == CM_pxref)
-            {
-              tree = html_cdt_tree ("see {reference}",
-                                    self, substrings, 0);
-            }
-          else if (cmd == CM_xref || cmd == CM_inforef)
-            {
-              tree = html_cdt_tree ("See {reference}",
-                                    self, substrings, 0);
-            }
-          else /* @ref */
-            {
-              tree = html_cdt_tree ("{reference}",
-                                    self, substrings, 0);
-            }
-        }
-      else if (name)
-        {
-          reference_element = new_text_element (ET__converted);
-          text_append (reference_element->e.text, name);
-
-          add_element_to_named_string_element_list (substrings,
-                                         "section", reference_element);
-          if (cmd == CM_pxref)
-            {
-              tree = html_cdt_tree ("see `{section}'",
-                                    self, substrings, 0);
-            }
-          else if (cmd == CM_xref || cmd == CM_inforef)
-            {
-              tree = html_cdt_tree ("See `{section}'",
-                                    self, substrings, 0);
-            }
-          else /* @ref */
-            {
-              tree = html_cdt_tree ("`{section}'",
-                                    self, substrings, 0);
-            }
-        }
-      free (reference);
-      free (book_reference);
-      destroy_named_string_element_list (substrings);
-    }
+          add_to_contents_as_array (entry_ref_tree, entry_content_element);
 
-  if (tree)
-    {
-      char *context_str;
-      xasprintf (&context_str, "convert xref %s", builtin_command_name (cmd));
-      add_tree_to_build (self, tree);
-      convert_to_html_internal (self, tree, result, context_str);
-      remove_tree_to_build (self, tree);
-      free (context_str);
-      /* should destroy reference_element and book_element */
-      destroy_element_and_children (tree);
-    }
+          /* index entry with @seeentry or @seealso */
+          seeentry = lookup_extra_element (main_entry_element, 
AI_key_seeentry);
+          seealso = lookup_extra_element (main_entry_element, AI_key_seealso);
 
-  free (file);
-  free (name);
-}
+          memset (entry_trees, 0, sizeof (ELEMENT *) * SUBENTRIES_MAX_LEVEL);
 
-#define SUBENTRIES_MAX_LEVEL 2
+     /* determine the trees and normalized main entry and subentries, to be
+        compared with the previous line normalized entries to determine
+        what is already formatted as part of the previous lines and
+        what levels should be added.  The last level is always formatted. */
+          new_normalized_entry_levels[0]
+            = normalized_upper_case (entry_ref_tree);
+          entry_trees[0] = entry_ref_tree;
+          subentry = index_entry_ref->entry_element;
 
-static void
-clear_normalized_entry_levels (char **normalized_entry_levels)
-{
-  int i;
-  for (i = 0; i < SUBENTRIES_MAX_LEVEL; i++)
-    {
-      free (normalized_entry_levels[i]);
-      normalized_entry_levels[i] = 0;
-    }
-}
+          while (subentry_level <= SUBENTRIES_MAX_LEVEL)
+            {
+              const ELEMENT *new_subentry = lookup_extra_element (subentry,
+                                                            AI_key_subentry);
+              ELEMENT *subentry_tree = 0;
+              if (!new_subentry)
+                break;
 
-static char *
-normalized_upper_case (ELEMENT *e)
-{
-  char *normalized = convert_to_normalized (e);
-  char *result = to_upper_or_lower_multibyte (normalized, 1);
-  free (normalized);
-  return result;
-}
+              subentry = new_subentry;
 
-static void
-printindex_letters_head_foot_internal (CONVERTER *self, const char *index_name,
-                           const enum command_id cmd,
-                           STRING_LIST *entry_classes,
-                           const char *head_or_foot,
-                           const char *letters_header_explanation,
-                           const char *alpha_text,
-                           const char *non_alpha_text, TEXT *result)
-{
-  char *index_name_cmd_class;
-  char *attribute_class;
+              if (subentry->e.c->args.number > 0
+                  && subentry->e.c->args.list[0]->e.c->contents.number > 0)
+                {
+                  if (in_code)
+                    subentry_tree = new_element (ET__code);
+                  else
+                    subentry_tree = new_element (ET_NONE);
 
-  xasprintf (&index_name_cmd_class, "%s-letters-%s-%s",
-             index_name, head_or_foot, builtin_command_name (cmd));
-  add_string (index_name_cmd_class, entry_classes);
-  free (index_name_cmd_class);
-  attribute_class = html_attribute_class (self, "table", entry_classes);
-  clear_strings_list (entry_classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  text_append_n (result, "><tr><th>", 9);
+                  add_to_contents_as_array (subentry_tree,
+                                            subentry->e.c->args.list[0]);
+                }
 
-  /* TRANSLATORS: before list of letters and symbols grouping index entries */
-  translate_convert_to_html_internal ("Jump to", self, 0, 0, result,
-                                      letters_header_explanation);
-  text_append_n (result, ": ", 2);
-  text_append_n (result,
-                 self->special_character[SC_non_breaking_space].string,
-                 self->special_character[SC_non_breaking_space].len);
-  text_append_n (result, " </th><td>", 10);
-  if (non_alpha_text)
-    text_append (result, non_alpha_text);
-  if (non_alpha_text && alpha_text)
-    {
-      text_append_n (result, " ", 1);
-      text_append_n (result,
-                     self->special_character[SC_non_breaking_space].string,
-                     self->special_character[SC_non_breaking_space].len);
-      text_append_n (result, " \n", 2);
-      text_append_n (result,
-                     self->line_break_element.string,
-                     self->line_break_element.len);
-      text_append_n (result, "\n", 1);
-    }
-  if (alpha_text)
-    text_append (result, alpha_text);
-  text_append_n (result, "</td></tr></table>\n", 19);
-}
+              if (subentry_level >= SUBENTRIES_MAX_LEVEL)
+                {
+                  /* at the max, concatenate the remaining subentries */
+                  other_subentries_tree
+                    = comma_index_subentries_tree (subentry, 0);
+                  if (other_subentries_tree)
+                    {
+                      if (!subentry_tree)
+                        {
+                          if (in_code)
+                            subentry_tree = new_element (ET__code);
+                          else
+                            subentry_tree = new_element (ET_NONE);
+                        }
+                      insert_list_slice_into_contents (subentry_tree,
+                           subentry_tree->e.c->contents.number,
+                           other_subentries_tree, 0,
+                           other_subentries_tree->number);
+                    }
+                }
+              else if (subentry_tree)
+                {
+                  new_normalized_entry_levels[subentry_level]
+                    = normalized_upper_case (subentry_tree);
 
-void
-convert_printindex_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  const STRING_LIST *misc_args;
-  const char *index_name;
-  const INDEX_SORTED_BY_LETTER *idx;
-  const INDEX_SORTED_BY_LETTER *index_sorted = 0;
-  const char *index_element_id = 0;
-  char **letter_id;
-  char **alpha;
-  char **non_alpha;
-  size_t non_alpha_nr = 0;
-  size_t alpha_nr = 0;
-  int *letter_is_symbol;
-  char **formatted_letters;
-  size_t symbol_idx = 0;
-  size_t normalized_letter_idx = 0;
-  size_t i;
-  char *entry_class_seeentry;
-  char *section_class_seeentry;
-  char *cmd_index_entry_class;
-  char *section_class_seealso;
-  char *cmd_index_section_class;
-  char *summary_letter_cmd;
-  char *attribute_class;
-  TEXT entries_text;
-  TEXT result_index_entries;
-  char *index_name_cmd_class;
-  char *alpha_text = 0;
-  char *non_alpha_text = 0;
-  char *language;
-  INDEX_SORTED_BY_LETTER *index_entries_by_letter
-    = get_converter_indices_sorted_by_letter (self, &language);
+                }
+              entry_trees[subentry_level] = subentry_tree;
+              subentry_level++;
+            }
+          /* level/index of the last entry */
+          last_entry_level = subentry_level - 1;
 
-  if (!index_entries_by_letter)
-    return;
+    /* format the leading entries when there are subentries (all entries
+       except the last one), and when there is not such a subentry already
+       formatted on the previous lines.
+       Each on a line with increasing indentation, no hyperlink. */
+          if (last_entry_level > 0)
+            {
+              int with_new_formatted_entry = 0;
+              for (level = 0; level < last_entry_level; level++)
+                {
+                  char *convert_info;
+                  char *entry;
 
-  if (html_in_string (self))
-    return;
+                  if (!with_new_formatted_entry
+                      && prev_normalized_entry_levels[level]
+                      && !strcmp (prev_normalized_entry_levels[level],
+                                  new_normalized_entry_levels[level]))
+                    {
+                      if (level > 0)
+                        destroy_element (entry_trees[level]);
+                      continue;
+                    }
 
-  misc_args = lookup_extra_misc_args (element, AI_key_misc_args);
-  if (misc_args && misc_args->number > 0)
-    index_name = misc_args->list[0];
-  else
-    return;
+                  with_new_formatted_entry = 1;
+                  xasprintf (&convert_info,
+                             "index %s l %s index entry %zu subentry %d",
+                             index_name, letter, entry_nr -1, level);
+                  if (level > 0)
+                    add_tree_to_build (self, entry_trees[level]);
+                  if (*formatted_index_entry_nr > 1)
+                    {
+                      /* call with multiple_pass argument */
+                      entry = convert_tree_new_formatting_context (self,
+                                           entry_trees[level], convert_info,
+                                           multiple_pass_str, 0, 0);
+                    }
+                  else
+                    {
+                      entry = html_convert_tree (self, entry_trees[level],
+                                                 convert_info);
+                    }
+                  if (level > 0)
+                    {
+                      remove_tree_to_build (self, entry_trees[level]);
+                      destroy_element (entry_trees[level]);
+                    }
+                  free (convert_info);
 
-  for (idx = index_entries_by_letter; idx->name; idx++)
-    {
-      if (!strcmp (idx->name, index_name))
-        {
-          index_sorted = idx;
-          break;
-        }
-    }
-  if (!index_sorted || !index_sorted->letter_number)
-    return;
-
-  if (self->current_output_unit
-      && self->current_output_unit->uc.unit_command)
-    index_element_id
-      = html_command_id (self, self->current_output_unit->uc.unit_command);
-
-  if (!index_element_id)
-    {
-      ROOT_AND_UNIT *root_unit
-        = get_element_root_command_element (self, element);
-      if (root_unit && root_unit->root)
-        {
-          index_element_id = html_command_id (self, root_unit->root);
-        }
-      if (!index_element_id)
-    /* to avoid duplicate names, use a prefix that cannot happen in anchors */
-        index_element_id = "t_i";
-      free (root_unit);
-    }
-
-  letter_id = (char **) malloc (index_sorted->letter_number * sizeof (char *));
-  /* we allocate twice as needed here, but it is more practical */
-  alpha = (char **) malloc ((index_sorted->letter_number +1) * sizeof (char 
*));
-  non_alpha = (char **)
-     malloc ((index_sorted->letter_number +1) * sizeof (char *));
-  memset (alpha, 0, (index_sorted->letter_number +1) * sizeof (char *));
-  memset (non_alpha, 0, (index_sorted->letter_number +1) * sizeof (char *));
-  letter_is_symbol
-    = (int *) malloc (index_sorted->letter_number * sizeof (int));
-  formatted_letters = (char **) malloc
-      (index_sorted->letter_number * sizeof (char *));
+                  add_string (cmd_index_entry_class, entry_classes);
+                  if (level > 0)
+                    {
+                      /* indent */
+                      char *index_entry_level;
+                      xasprintf (&index_entry_level, "index-entry-level-%d",
+                                                     level);
+                      add_string (index_entry_level, entry_classes);
+                      free (index_entry_level);
+                    }
+                  text_append_n (&entries_text, "<tr><td></td>", 13);
+                  attribute_class = html_attribute_class (self, "td",
+                                                          entry_classes);
+                  text_append (&entries_text, attribute_class);
+                  clear_strings_list (entry_classes);
+                  free (attribute_class);
+                  text_append_n (&entries_text, ">", 1);
 
-  for (i = 0; i < index_sorted->letter_number; i++)
-    {
-      const char *letter = index_sorted->letter_entries[i].letter;
-      uint8_t *encoded_u8 = utf8_from_string (letter);
-      ucs4_t next_char;
-      u8_next (&next_char, encoded_u8);
-      letter_is_symbol[i]
-          = !(uc_is_property (next_char, UC_PROPERTY_ALPHABETIC));
-      free (encoded_u8);
-      if (letter_is_symbol[i])
-        {
-          symbol_idx++;
-          xasprintf (&letter_id[i], "%s_%s_symbol-%zu", index_element_id,
-                                   index_name, symbol_idx);
-        }
-      else
-        {
-          char *normalized_letter;
-          ELEMENT *letter_text = new_text_element (ET_normal_text);
-          text_append (letter_text->e.text, letter);
-          normalized_letter = normalize_transliterate_texinfo (letter_text,
-                                             (self->conf->TEST.o.integer > 0));
-          destroy_element (letter_text);
+                  if (in_code)
+                    text_append_n (&entries_text, "<code>", 6);
+                  text_append (&entries_text, entry);
+                  free (entry);
+                  if (in_code)
+                    text_append_n (&entries_text, "</code>", 7);
+                  text_append_n (&entries_text, "</td>", 5);
+                  /* empty cell, no section for this line */
+                  text_append_n (&entries_text, "<td></td></tr>\n", 15);
+                }
+            }
+      /* last entry, always converted, associated to chapter/node and
+         with an hyperlink or to seeentry/seealso */
+          entry_tree = entry_trees[last_entry_level];
 
-          if (strcmp (letter, normalized_letter))
+          if (seeentry || seealso)
             {
-              char *tmp_normalized_letter;
-   /* disambiguate, as it could be another letter, case of @l, for example */
-              normalized_letter_idx++;
-              xasprintf (&tmp_normalized_letter, "%s-%zu", normalized_letter,
-                                                 normalized_letter_idx);
-              free (normalized_letter);
-              normalized_letter = tmp_normalized_letter;
-            }
+              NAMED_STRING_ELEMENT_LIST *substrings
+                                       = new_named_string_element_list ();
+              ELEMENT *referred_tree;
+              const ELEMENT *referred_entry;
+              char *entry;
+              char *reference = 0;
 
-          xasprintf (&letter_id[i], "%s_%s_letter-%s", index_element_id,
-                                     index_name, normalized_letter);
-          free (normalized_letter);
-        }
-    }
+              if (seeentry)
+                referred_entry = seeentry;
+              else
+                referred_entry = seealso;
 
-  html_new_document_context (self, builtin_command_name (cmd), 0, 0);
+              if (in_code)
+                referred_tree = new_element (ET__code);
+              else
+                referred_tree = new_element (ET_NONE);
 
-  STRING_LIST *entry_classes = new_string_list ();
-  STRING_LIST *section_classes  = new_string_list ();
+              if (referred_entry->e.c->args.number > 0
+                  && referred_entry->e.c->args.list[0]->e.c->contents.number > 
0)
+                {
+                  ELEMENT *referred_copy
+                    = copy_tree (referred_entry->e.c->args.list[0]);
+                  add_to_contents_as_array (referred_tree, referred_copy);
+                }
 
-  xasprintf (&entry_class_seeentry, "%s-index-see-entry",
-                                    builtin_command_name (cmd));
-  xasprintf (&section_class_seeentry, "%s-index-see-entry-section",
-                                    builtin_command_name (cmd));
-  xasprintf (&cmd_index_entry_class, "%s-index-entry",
-                                   builtin_command_name (cmd));
-  xasprintf (&section_class_seealso, "%s-index-see-also",
-                                   builtin_command_name (cmd));
-  xasprintf (&cmd_index_section_class, "%s-index-section",
-                                   builtin_command_name (cmd));
-  xasprintf (&summary_letter_cmd, "summary-letter-%s",
-                                   builtin_command_name (cmd));
+              if (seeentry)
+                {
+                  char *convert_info;
+                  ELEMENT *result_tree;
+                  ELEMENT *entry_tree_copy = copy_tree (entry_tree);
+                  add_element_to_named_string_element_list (substrings,
+                                    "main_index_entry", entry_tree_copy);
+                  add_element_to_named_string_element_list (substrings,
+                                             "seeentry", referred_tree);
+                  if (in_code)
+                    {
+       /* TRANSLATORS: redirect to another index entry */
+       /* TRANSLATORS: @: is discardable and is used to avoid a msgfmt error */
+                      result_tree = html_cdt_tree (
+       "@code{{main_index_entry}}, @emph{See@:} @code{{seeentry}}",
+                                      self, substrings, 0);
+                    }
+                  else
+                    {
+        /* TRANSLATORS: redirect to another index entry */
+        /* TRANSLATORS: @: is discardable and used to avoid a msgfmt error */
+                      result_tree = html_cdt_tree (
+                    "{main_index_entry}, @emph{See@:} {seeentry}",
+                                      self, substrings, 0);
+                    }
+                  xasprintf (&convert_info,
+                             "index %s l %s index entry %zu seeentry",
+                             index_name, letter, entry_nr -1);
+                  add_tree_to_build (self, result_tree);
+                  if (*formatted_index_entry_nr > 1)
+                    {
+                      /* call with multiple_pass argument */
+                      entry = convert_tree_new_formatting_context (self,
+                             result_tree, convert_info, multiple_pass_str, 0, 
0);
+                    }
+                  else
+                    {
+                      entry = html_convert_tree (self, result_tree,
+                                                 convert_info);
+                    }
+                  remove_tree_to_build (self, result_tree);
+                  destroy_element_and_children (result_tree);
+                  free (convert_info);
 
-  text_init (&entries_text);
-  text_init (&result_index_entries);
+                  add_string (entry_class_seeentry, entry_classes);
+                  add_string (section_class_seeentry, section_classes);
+                }
+              else
+                {
+                  /* TRANSLATORS: refer to another index entry */
+                  ELEMENT *reference_tree;
+                  char *conv_str_entry;
+                  char *conv_str_reference;
 
-  /* Next do the entries to determine the letters that are not empty */
-  for (i = 0; i < index_sorted->letter_number; i++)
-    {
-      const INDEX_ENTRY *first_entry = 0;
-      const LETTER_INDEX_ENTRIES *letter_entry
-         = &index_sorted->letter_entries[i];
-      const char *letter = letter_entry->letter;
-      size_t entry_nr = 0;
-    /* since we normalize, a different formatting will not trigger a new
-       formatting of the main entry or a subentry level.  This is the
-       same for Texinfo TeX */
-      size_t j;
+                  add_element_to_named_string_element_list (substrings,
+                                             "see_also_entry", referred_tree);
+                  reference_tree = html_cdt_tree (
+                                  "@emph{See also} {see_also_entry}",
+                                      self, substrings, 0);
 
-      char *prev_normalized_entry_levels[SUBENTRIES_MAX_LEVEL+1];
-      memset (prev_normalized_entry_levels, 0,
-              sizeof (char *) * (SUBENTRIES_MAX_LEVEL +1));
-      text_reset (&entries_text);
+                  xasprintf (&conv_str_entry,
+                             "index %s l %s index entry %zu (with seealso)",
+                             index_name, letter, entry_nr -1);
+                  xasprintf (&conv_str_reference,
+                             "index %s l %s index entry %zu seealso",
+                             index_name, letter, entry_nr -1);
 
-      for (j = 0; j < letter_entry->entries_number; j++)
-        {
-          int level;
-          int in_code;
-          int *formatted_index_entry_nr;
-          char *multiple_pass_str;
-          size_t entry_index_nr;
-          const INDEX *entry_index;
-          const ELEMENT *seeentry;
-          const ELEMENT *seealso;
-          char *new_normalized_entry_levels[SUBENTRIES_MAX_LEVEL +1];
-          ELEMENT *entry_trees[SUBENTRIES_MAX_LEVEL +1];
-          int last_entry_level;
-          char *convert_info;
-          ELEMENT *target_element;
-          const ELEMENT *associated_command = 0;
-          char *entry_href;
-          ELEMENT *entry_tree;
-          const ELEMENT *subentry;
-          ELEMENT_LIST *other_subentries_tree = 0;
-          int subentry_level = 1;
-          ELEMENT *entry_content_element;
-          ELEMENT *entry_ref_tree;
-          INDEX_ENTRY *index_entry_ref = letter_entry->entries[j];
-          ELEMENT *main_entry_element = index_entry_ref->entry_element;
-          const INDEX_ENTRY_LOCATION *index_entry_info
-             = lookup_extra_index_entry (main_entry_element,
-                                         AI_key_index_entry);
-          int entry_number = index_entry_info->number;
-          entry_nr++;
-
-          if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0)
-            {
-              const ELEMENT *element_node
-                = lookup_extra_element (main_entry_element,
-                                        AI_key_element_node);
-              if (element_node)
-                {
-                  const char *normalized = lookup_extra_string (element_node,
-                                                           AI_key_normalized);
-                  if (normalized && !strcmp (normalized, "Top"))
-                    continue;
-                }
-            }
-
-          memset (new_normalized_entry_levels, 0,
-                  sizeof (char *) * (SUBENTRIES_MAX_LEVEL +1));
-
-          entry_content_element = index_content_element (main_entry_element, 
0);
-          entry_index_nr
-             = index_number_index_by_name (&self->sorted_index_names,
-                                           index_entry_ref->index_name);
-          entry_index = self->sorted_index_names.list[entry_index_nr-1];
-
- /* to avoid double error messages, call convert_tree_new_formatting_context
-    below with a multiple_pass argument if an entry was already formatted once,
-    for example if there are multiple printindex. */
-          formatted_index_entry_nr
-            = &self->shared_conversion_state
-               .formatted_index_entries[entry_index_nr -1][entry_number -1];
-          (*formatted_index_entry_nr)++;
-
-          if (*formatted_index_entry_nr > 1)
-            xasprintf (&multiple_pass_str, "index-formatted-%d",
-                       *formatted_index_entry_nr);
-
-          in_code = entry_index->in_code;
-
-          if (in_code)
-            entry_ref_tree = new_element (ET__code);
-          else
-            entry_ref_tree = new_element (ET_NONE);
-
-          add_to_contents_as_array (entry_ref_tree, entry_content_element);
-
-          /* index entry with @seeentry or @seealso */
-          seeentry = lookup_extra_element (main_entry_element, 
AI_key_seeentry);
-          seealso = lookup_extra_element (main_entry_element, AI_key_seealso);
-
-          memset (entry_trees, 0, sizeof (ELEMENT *) * SUBENTRIES_MAX_LEVEL);
-
-     /* determine the trees and normalized main entry and subentries, to be
-        compared with the previous line normalized entries to determine
-        what is already formatted as part of the previous lines and
-        what levels should be added.  The last level is always formatted. */
-          new_normalized_entry_levels[0]
-            = normalized_upper_case (entry_ref_tree);
-          entry_trees[0] = entry_ref_tree;
-          subentry = index_entry_ref->entry_element;
-
-          while (subentry_level <= SUBENTRIES_MAX_LEVEL)
-            {
-              const ELEMENT *new_subentry = lookup_extra_element (subentry,
-                                                            AI_key_subentry);
-              ELEMENT *subentry_tree = 0;
-              if (!new_subentry)
-                break;
-
-              subentry = new_subentry;
-
-              if (subentry->e.c->args.number > 0
-                  && subentry->e.c->args.list[0]->e.c->contents.number > 0)
-                {
-                  if (in_code)
-                    subentry_tree = new_element (ET__code);
-                  else
-                    subentry_tree = new_element (ET_NONE);
-
-                  add_to_contents_as_array (subentry_tree,
-                                            subentry->e.c->args.list[0]);
-                }
-
-              if (subentry_level >= SUBENTRIES_MAX_LEVEL)
-                {
-                  /* at the max, concatenate the remaining subentries */
-                  other_subentries_tree
-                    = comma_index_subentries_tree (subentry, 0);
-                  if (other_subentries_tree)
-                    {
-                      if (!subentry_tree)
-                        {
-                          if (in_code)
-                            subentry_tree = new_element (ET__code);
-                          else
-                            subentry_tree = new_element (ET_NONE);
-                        }
-                      insert_list_slice_into_contents (subentry_tree,
-                           subentry_tree->e.c->contents.number,
-                           other_subentries_tree, 0,
-                           other_subentries_tree->number);
-                    }
-                }
-              else if (subentry_tree)
-                {
-                  new_normalized_entry_levels[subentry_level]
-                    = normalized_upper_case (subentry_tree);
-
-                }
-              entry_trees[subentry_level] = subentry_tree;
-              subentry_level++;
-            }
-          /* level/index of the last entry */
-          last_entry_level = subentry_level - 1;
-
-    /* format the leading entries when there are subentries (all entries
-       except the last one), and when there is not such a subentry already
-       formatted on the previous lines.
-       Each on a line with increasing indentation, no hyperlink. */
-          if (last_entry_level > 0)
-            {
-              int with_new_formatted_entry = 0;
-              for (level = 0; level < last_entry_level; level++)
-                {
-                  char *convert_info;
-                  char *entry;
-
-                  if (!with_new_formatted_entry
-                      && prev_normalized_entry_levels[level]
-                      && !strcmp (prev_normalized_entry_levels[level],
-                                  new_normalized_entry_levels[level]))
-                    {
-                      if (level > 0)
-                        destroy_element (entry_trees[level]);
-                      continue;
-                    }
-
-                  with_new_formatted_entry = 1;
-                  xasprintf (&convert_info,
-                             "index %s l %s index entry %zu subentry %d",
-                             index_name, letter, entry_nr -1, level);
-                  if (level > 0)
-                    add_tree_to_build (self, entry_trees[level]);
+                  add_tree_to_build (self, entry_tree);
+                  add_tree_to_build (self, reference_tree);
                   if (*formatted_index_entry_nr > 1)
                     {
                       /* call with multiple_pass argument */
                       entry = convert_tree_new_formatting_context (self,
-                                           entry_trees[level], convert_info,
-                                           multiple_pass_str, 0, 0);
-                    }
-                  else
-                    {
-                      entry = html_convert_tree (self, entry_trees[level],
-                                                 convert_info);
-                    }
-                  if (level > 0)
-                    {
-                      remove_tree_to_build (self, entry_trees[level]);
-                      destroy_element (entry_trees[level]);
-                    }
-                  free (convert_info);
-
-                  add_string (cmd_index_entry_class, entry_classes);
-                  if (level > 0)
-                    {
-                      /* indent */
-                      char *index_entry_level;
-                      xasprintf (&index_entry_level, "index-entry-level-%d",
-                                                     level);
-                      add_string (index_entry_level, entry_classes);
-                      free (index_entry_level);
-                    }
-                  text_append_n (&entries_text, "<tr><td></td>", 13);
-                  attribute_class = html_attribute_class (self, "td",
-                                                          entry_classes);
-                  text_append (&entries_text, attribute_class);
-                  clear_strings_list (entry_classes);
-                  free (attribute_class);
-                  text_append_n (&entries_text, ">", 1);
-
-                  if (in_code)
-                    text_append_n (&entries_text, "<code>", 6);
-                  text_append (&entries_text, entry);
-                  free (entry);
-                  if (in_code)
-                    text_append_n (&entries_text, "</code>", 7);
-                  text_append_n (&entries_text, "</td>", 5);
-                  /* empty cell, no section for this line */
-                  text_append_n (&entries_text, "<td></td></tr>\n", 15);
-                }
-            }
-      /* last entry, always converted, associated to chapter/node and
-         with an hyperlink or to seeentry/seealso */
-          entry_tree = entry_trees[last_entry_level];
-
-          if (seeentry || seealso)
-            {
-              NAMED_STRING_ELEMENT_LIST *substrings
-                                       = new_named_string_element_list ();
-              ELEMENT *referred_tree;
-              const ELEMENT *referred_entry;
-              char *entry;
-              char *reference = 0;
-
-              if (seeentry)
-                referred_entry = seeentry;
-              else
-                referred_entry = seealso;
-
-              if (in_code)
-                referred_tree = new_element (ET__code);
-              else
-                referred_tree = new_element (ET_NONE);
-
-              if (referred_entry->e.c->args.number > 0
-                  && referred_entry->e.c->args.list[0]->e.c->contents.number > 
0)
-                {
-                  ELEMENT *referred_copy
-                    = copy_tree (referred_entry->e.c->args.list[0]);
-                  add_to_contents_as_array (referred_tree, referred_copy);
-                }
-
-              if (seeentry)
-                {
-                  char *convert_info;
-                  ELEMENT *result_tree;
-                  ELEMENT *entry_tree_copy = copy_tree (entry_tree);
-                  add_element_to_named_string_element_list (substrings,
-                                    "main_index_entry", entry_tree_copy);
-                  add_element_to_named_string_element_list (substrings,
-                                             "seeentry", referred_tree);
-                  if (in_code)
-                    {
-       /* TRANSLATORS: redirect to another index entry */
-       /* TRANSLATORS: @: is discardable and is used to avoid a msgfmt error */
-                      result_tree = html_cdt_tree (
-       "@code{{main_index_entry}}, @emph{See@:} @code{{seeentry}}",
-                                      self, substrings, 0);
+                                          entry_tree, conv_str_entry,
+                                          multiple_pass_str, 0, 0);
+                      reference = convert_tree_new_formatting_context (self,
+                                          reference_tree, conv_str_reference,
+                                          multiple_pass_str, 0, 0);
                     }
                   else
                     {
-        /* TRANSLATORS: redirect to another index entry */
-        /* TRANSLATORS: @: is discardable and used to avoid a msgfmt error */
-                      result_tree = html_cdt_tree (
-                    "{main_index_entry}, @emph{See@:} {seeentry}",
-                                      self, substrings, 0);
-                    }
-                  xasprintf (&convert_info,
-                             "index %s l %s index entry %zu seeentry",
-                             index_name, letter, entry_nr -1);
-                  add_tree_to_build (self, result_tree);
-                  if (*formatted_index_entry_nr > 1)
-                    {
-                      /* call with multiple_pass argument */
-                      entry = convert_tree_new_formatting_context (self,
-                             result_tree, convert_info, multiple_pass_str, 0, 
0);
-                    }
-                  else
-                    {
-                      entry = html_convert_tree (self, result_tree,
-                                                 convert_info);
-                    }
-                  remove_tree_to_build (self, result_tree);
-                  destroy_element_and_children (result_tree);
-                  free (convert_info);
-
-                  add_string (entry_class_seeentry, entry_classes);
-                  add_string (section_class_seeentry, section_classes);
-                }
-              else
-                {
-                  /* TRANSLATORS: refer to another index entry */
-                  ELEMENT *reference_tree;
-                  char *conv_str_entry;
-                  char *conv_str_reference;
-
-                  add_element_to_named_string_element_list (substrings,
-                                             "see_also_entry", referred_tree);
-                  reference_tree = html_cdt_tree (
-                                  "@emph{See also} {see_also_entry}",
-                                      self, substrings, 0);
-
-                  xasprintf (&conv_str_entry,
-                             "index %s l %s index entry %zu (with seealso)",
-                             index_name, letter, entry_nr -1);
-                  xasprintf (&conv_str_reference,
-                             "index %s l %s index entry %zu seealso",
-                             index_name, letter, entry_nr -1);
-
-                  add_tree_to_build (self, entry_tree);
-                  add_tree_to_build (self, reference_tree);
-                  if (*formatted_index_entry_nr > 1)
-                    {
-                      /* call with multiple_pass argument */
-                      entry = convert_tree_new_formatting_context (self,
-                                          entry_tree, conv_str_entry,
-                                          multiple_pass_str, 0, 0);
-                      reference = convert_tree_new_formatting_context (self,
-                                          reference_tree, conv_str_reference,
-                                          multiple_pass_str, 0, 0);
-                    }
-                  else
-                    {
-                      entry = html_convert_tree (self, entry_tree,
-                                                 conv_str_entry);
-                      reference = html_convert_tree (self, reference_tree,
-                                                    conv_str_reference);
+                      entry = html_convert_tree (self, entry_tree,
+                                                 conv_str_entry);
+                      reference = html_convert_tree (self, reference_tree,
+                                                    conv_str_reference);
                     }
                   remove_tree_to_build (self, entry_tree);
                   remove_tree_to_build (self, reference_tree);
-                  destroy_element_and_children (reference_tree);
-
-                  free (conv_str_entry);
-                  free (conv_str_reference);
-
-                  add_string (cmd_index_entry_class, entry_classes);
-                  add_string (section_class_seealso, section_classes);
-                }
-
-              destroy_named_string_element_list (substrings);
-
-              text_append_n (&entries_text, "<tr><td></td>", 13);
-              if (last_entry_level > 0)
-                {
-                  char *index_entry_level;
-                  xasprintf (&index_entry_level, "index-entry-level-%d",
-                                                 last_entry_level);
-                  add_string (index_entry_level, entry_classes);
-                  free (index_entry_level);
-                }
-              attribute_class = html_attribute_class (self, "td", 
entry_classes);
-              text_append (&entries_text, attribute_class);
-              clear_strings_list (entry_classes);
-              free (attribute_class);
-              text_append_n (&entries_text, ">", 1);
-
-              if (!seeentry && in_code)
-                text_append_n (&entries_text, "<code>", 6);
-              text_append (&entries_text, entry);
-              free (entry);
-              if (!seeentry)
-                {
-                  if (in_code)
-                    text_append_n (&entries_text, "</code>", 7);
-                  if (self->conf->INDEX_ENTRY_COLON.o.string)
-                    text_append (&entries_text,
-                                 self->conf->INDEX_ENTRY_COLON.o.string);
-                }
-              text_append_n (&entries_text, "</td>", 5);
-
-              attribute_class
-                 = html_attribute_class (self, "td", section_classes);
-              text_append (&entries_text, attribute_class);
-              clear_strings_list (section_classes);
-              free (attribute_class);
-              text_append_n (&entries_text, ">", 1);
-              if (reference)
-                {
-                  text_append (&entries_text, reference);
-                  free (reference);
-                }
-              text_append_n (&entries_text, "</td></tr>\n", 11);
-
-            }
-          else
-            {
-              char *entry = 0;
-              if (entry_tree)
-                {
-                  xasprintf (&convert_info, "index %s l %s index entry %zu",
-                             index_name, letter, entry_nr -1);
-
-                  if (last_entry_level > 0)
-                    add_tree_to_build (self, entry_tree);
-                  if (*formatted_index_entry_nr > 1)
-                    {
-                      /* call with multiple_pass argument */
-                      entry = convert_tree_new_formatting_context (self,
-                                           entry_tree, convert_info,
-                                           multiple_pass_str, 0, 0);
-                    }
-                  else
-                    {
-                      entry = html_convert_tree (self, entry_tree,
-                                                 convert_info);
-                    }
-                  if (last_entry_level > 0)
-                    remove_tree_to_build (self, entry_tree);
-                  free (convert_info);
-                }
-
-              if (last_entry_level == 0
-                  && (!entry
-                      || entry[strspn (entry, whitespace_chars)] == '\0'))
-                {
-                  free (entry);
-                  free (new_normalized_entry_levels[0]);
-                  new_normalized_entry_levels[0] = 0;
-                }
-              else
-                {
-                  if (!first_entry)
-                    first_entry = index_entry_ref;
-
-                  if (index_entry_ref->entry_associated_element)
-                    target_element = index_entry_ref->entry_associated_element;
-                  else
-                    target_element = main_entry_element;
-
-                  entry_href
-                    = html_command_href (self, target_element, 0, 0, 0);
-
-                  add_string (cmd_index_entry_class, entry_classes);
-                  if (last_entry_level > 0)
-                    {
-                      char *index_entry_level;
-                      xasprintf (&index_entry_level, "index-entry-level-%d",
-                                                     last_entry_level);
-                      add_string (index_entry_level, entry_classes);
-                      free (index_entry_level);
-                    }
-                  text_append_n (&entries_text, "<tr><td></td>", 13);
-                  attribute_class = html_attribute_class (self, "td",
-                                                          entry_classes);
-                  text_append (&entries_text, attribute_class);
-                  clear_strings_list (entry_classes);
-                  free (attribute_class);
-                  text_append_n (&entries_text, ">", 1);
-
-                  text_printf (&entries_text, "<a href=\"%s\">", entry_href);
-                  free (entry_href);
-                  if (in_code)
-                    text_append_n (&entries_text, "<code>", 6);
-                  if (entry)
-                    {
-                      text_append (&entries_text, entry);
-                      free (entry);
-                    }
-                  if (in_code)
-                    text_append_n (&entries_text, "</code>", 7);
-                  text_append_n (&entries_text, "</a>", 4);
-                  if (self->conf->INDEX_ENTRY_COLON.o.string)
-                    text_append (&entries_text,
-                                 self->conf->INDEX_ENTRY_COLON.o.string);
-                  text_append_n (&entries_text, "</td>", 5);
-
-                  if (self->conf->NODE_NAME_IN_INDEX.o.integer > 0)
-                    {
-                      associated_command
-                          = lookup_extra_element (main_entry_element,
-                                                    AI_key_element_node);
-                      if (!associated_command)
-                        associated_command
-                          = html_command_node (self, target_element);
-
-                      if (!associated_command
-                          && *formatted_index_entry_nr == 1)
-                        {
-                          char *element_region
-                           = lookup_extra_string (main_entry_element,
-                                                  AI_key_element_region);
-        /* do not warn if the entry is in a special region, like titlepage */
-                          if (!element_region)
-                            {
-     /* NOTE $self->in_multiple_conversions() is not checked as printindex
-        should not happen in multiple tree conversion, but the error message
-        is printed for the first entry formatting only. */
-                              message_list_command_warn (&self->error_messages,
-                                      self->conf,
-                                      main_entry_element, 0,
-                "entry for index `%s' for @printindex %s outside of any node",
-                                  entry_index->name, index_name);
-                            }
-                        }
-                    }
-
-                  if (!associated_command)
-                    {
-                      associated_command
-                         = html_command_root_element_command (self,
-                                                              target_element);
-                      if (!associated_command)
-                        {
-                          associated_command
-                     = self->global_units_directions[D_Top]->uc.unit_command;
-
-         /* NOTE the warning here catches the most relevant cases of
-            index entry that is not associated to the right command, which
-            are very few in the test suite.  There is also a warning in the
-            parser with a much broader scope with possible overlap, but the
-            overlap is not a problem.
-            NODE_NAME_IN_INDEX may be undef even with USE_NODES set if the
-            converter is called as convert() as in the test suite */
-
-                          if (self->conf->NODE_NAME_IN_INDEX.o.integer == 0
-                              && *formatted_index_entry_nr == 1)
-                            {
-                              char *element_region
-                               = lookup_extra_string (main_entry_element,
-                                                      AI_key_element_region);
-        /* do not warn if the entry is in a special region, like titlepage */
-                              if (!element_region)
-                                {
-      /* NOTE $self->in_multiple_conversions() is not checked as printindex
-         should not happen in multiple tree conversion, but the error message
-         is printed for the first entry formatting only.
-         NOTE the index entry may be associated to a node in that case. */
-                              message_list_command_warn (&self->error_messages,
-                                      self->conf,
-                                      main_entry_element, 0,
-             "entry for index `%s' for @printindex %s outside of any section",
-                                  entry_index->name, index_name);
-                                }
-                            }
-                        }
-                    }
-
-                  add_string (cmd_index_section_class, section_classes);
-                  attribute_class
-                     = html_attribute_class (self, "td", section_classes);
-                  text_append (&entries_text, attribute_class);
-                  free (attribute_class);
-                  clear_strings_list (section_classes);
-                  text_append_n (&entries_text, ">", 1);
-
-                  if (associated_command)
-                    {
-                      char *associated_command_href
-                       = html_command_href (self, associated_command, 0, 0, 0);
-                      char *associated_command_text
-                       = html_command_text (self, associated_command, 0);
-
-                      if (associated_command_href)
-                        {
-                          text_printf (&entries_text, "<a href=\"%s\">%s</a>",
-                            associated_command_href, associated_command_text);
-                        }
-                      else
-                        {
-                          text_append (&entries_text, associated_command_text);
-                        }
-
-                      free (associated_command_text);
-                      free (associated_command_href);
-                    }
-                  text_append_n (&entries_text, "</td></tr>\n", 11);
-                }
-            }
-
-          if (new_normalized_entry_levels[0] != 0)
-            {
-              for (level = 0; level < SUBENTRIES_MAX_LEVEL; level++)
-                {
-                  free (prev_normalized_entry_levels[level]);
-                  prev_normalized_entry_levels[level]
-                    = new_normalized_entry_levels[level];
-                }
-            }
-
-          if (last_entry_level > 0 && entry_tree)
-            destroy_element (entry_tree);
-
-          if (other_subentries_tree)
-            free_comma_index_subentries_tree (other_subentries_tree);
-
-          destroy_element (entry_ref_tree);
-
-          if (*formatted_index_entry_nr > 1)
-            free (multiple_pass_str);
-        }
-      clear_normalized_entry_levels (prev_normalized_entry_levels);
-
-      if (entries_text.end > 0)
-        {
-          char *formatted_letter;
-          const ELEMENT *letter_command = 0;
-          enum command_id letter_cmd = 0;
-
-          if (first_entry)
-            {
-              INDEX_ENTRY_TEXT_OR_COMMAND *entry_text_or_command
-                = index_entry_first_letter_text_or_command (first_entry);
-
-              if (entry_text_or_command)
-                {
-                  letter_command = entry_text_or_command->command;
-
-                  free (entry_text_or_command->text);
-                  free (entry_text_or_command);
-
-                  if (letter_command)
-                    letter_cmd = element_builtin_data_cmd (letter_command);
-                }
-            }
-
-          if (letter_command
-              && (!(builtin_command_data[letter_cmd].flags & CF_accent))
-              && letter_cmd != CM_U
-            /* special case, the uppercasing of that command is not done
-               if as a command, while it is done correctly in letter */
-              && letter_cmd != CM_ss)
-            {
-              ELEMENT *formatted_command = 0;
-              char *explanation;
-              if (html_commands_data[letter_cmd].upper_case_cmd)
-                {
-                   formatted_command
-                     = new_command_element (ET_brace_noarg_command,
-                             html_commands_data[letter_cmd].upper_case_cmd);
-                }
-
-              xasprintf (&explanation, "index letter %s command", letter);
-              if (formatted_command)
-                {
-                  add_tree_to_build (self, formatted_command);
-                  formatted_letter
-                    = html_convert_tree (self, formatted_command, explanation);
-                  remove_tree_to_build (self, formatted_command);
-                  destroy_element (formatted_command);
-                }
-              else
-                formatted_letter
-                  = html_convert_tree (self, letter_command, explanation);
-              free (explanation);
-            }
-          else
-            {
-              TEXT text_letter;
-              text_init (&text_letter);
-              text_append (&text_letter, "");
-              format_protect_text (self, letter, &text_letter);
-              formatted_letter = text_letter.text;
-            }
-
-          formatted_letters[i] = formatted_letter;
-
-          text_append_n (&result_index_entries, "<tr>", 4);
-          text_printf (&result_index_entries, "<th id=\"%s\">", letter_id[i]);
-          text_append (&result_index_entries, formatted_letter);
-          text_append_n (&result_index_entries, "</th></tr>\n", 11);
-          text_append (&result_index_entries, entries_text.text);
-          text_append_n (&result_index_entries, "<tr><td colspan=\"3\">", 20);
-          if (self->conf->DEFAULT_RULE.o.string)
-            text_append (&result_index_entries,
-                         self->conf->DEFAULT_RULE.o.string);
-          text_append_n (&result_index_entries, "</td></tr>\n", 11);
-        }
-      else
-        {
-          formatted_letters[i] = 0;
-        }
-    }
-
-  add_string (summary_letter_cmd, entry_classes);
-  attribute_class = html_attribute_class (self, "a", entry_classes);
-  for (i = 0; i < index_sorted->letter_number; i++)
-    {
-      if (formatted_letters[i])
-        {
-          text_reset (&entries_text);
-
-          text_append (&entries_text, attribute_class);
-          text_printf (&entries_text, " href=\"#%s\"><b>", letter_id[i]);
-          text_append (&entries_text, formatted_letters[i]);
-          text_append_n (&entries_text, "</b></a>", 8);
-
-          if (letter_is_symbol[i])
-            {
-              non_alpha[non_alpha_nr] = strdup (entries_text.text);
-              non_alpha_nr++;
-            }
-          else
-            {
-              alpha[alpha_nr] = strdup (entries_text.text);
-              alpha_nr++;
-            }
-
-          free (formatted_letters[i]);
-        }
-    }
-  free (attribute_class);
-
-  free (letter_is_symbol);
-  free (formatted_letters);
-
-  for (i = 0; i < index_sorted->letter_number; i++)
-    free (letter_id[i]);
-  free (letter_id);
-
-  free (entry_class_seeentry);
-  free (section_class_seeentry);
-  free (cmd_index_entry_class);
-  free (section_class_seealso);
-  free (cmd_index_section_class);
-  free (summary_letter_cmd);
-
-  destroy_strings_list (section_classes);
-
-  if (non_alpha_nr + alpha_nr <= 0)
-    {
-      free (alpha);
-      free (non_alpha);
-      html_pop_document_context (self);
-      free (entries_text.text);
-      free (result_index_entries.text);
-      destroy_strings_list (entry_classes);
-      return;
-    }
-
-  clear_strings_list (entry_classes);
-
-  add_string (builtin_command_name (cmd), entry_classes);
-  xasprintf (&index_name_cmd_class, "%s-%s",
-             index_name, builtin_command_name (cmd));
-  add_string (index_name_cmd_class, entry_classes);
-  free (index_name_cmd_class);
-  attribute_class = html_attribute_class (self, "div", entry_classes);
-  clear_strings_list (entry_classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  text_append_n (result, ">\n", 2);
-
-  /* Format the summary letters */
-  if (non_alpha_nr + alpha_nr > 1)
-    {
-      if (non_alpha_nr > 0)
-        {
-          text_reset (&entries_text);
-          text_append (&entries_text, non_alpha[0]);
-          if (non_alpha_nr > 1)
-            {
-              for (i = 1; i < non_alpha_nr; i++)
-                {
-                  text_append_n (&entries_text, "\n ", 2);
-                  text_append_n (&entries_text,
-                        self->special_character[SC_non_breaking_space].string,
-                        self->special_character[SC_non_breaking_space].len);
-                  text_append_n (&entries_text, " \n", 2);
-                  text_append (&entries_text, non_alpha[i]);
-                }
-            }
-          text_append_n (&entries_text, "\n", 1);
-          non_alpha_text = strdup (entries_text.text);
-        }
-      if (alpha_nr > 0)
-        {
-          text_reset (&entries_text);
-          for (i = 0; i < alpha_nr; i++)
-            {
-              text_append (&entries_text, alpha[i]);
-              text_append_n (&entries_text, "\n ", 2);
-              text_append_n (&entries_text,
-                 self->special_character[SC_non_breaking_space].string,
-                 self->special_character[SC_non_breaking_space].len);
-              text_append_n (&entries_text, " \n", 2);
-            }
-          alpha_text = strdup (entries_text.text);
-        }
-
-      /* format the summary */
-      printindex_letters_head_foot_internal (self, index_name, cmd,
-                                             entry_classes, "header",
-                                             "Tr letters header text",
-                                         alpha_text, non_alpha_text, result);
-    }
-
-  if (non_alpha_nr > 0)
-    {
-      for (i = 0; i < non_alpha_nr; i++)
-        free (non_alpha[i]);
-    }
-  free (non_alpha);
-
-  if (alpha_nr > 0)
-    {
-      for (i = 0; i < alpha_nr; i++)
-        free (alpha[i]);
-    }
-  free (alpha);
-
-  /* now format the index entries */
-  xasprintf (&index_name_cmd_class, "%s-entries-%s",
-             index_name, builtin_command_name (cmd));
-  add_string (index_name_cmd_class, entry_classes);
-  free (index_name_cmd_class);
-  attribute_class = html_attribute_class (self, "table", entry_classes);
-  clear_strings_list (entry_classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  text_append_n (result, ">\n<tr><td></td>", 15);
-
-  xasprintf (&index_name_cmd_class, "entries-header-%s",
-             builtin_command_name (cmd));
-  add_string (index_name_cmd_class, entry_classes);
-  free (index_name_cmd_class);
-  attribute_class = html_attribute_class (self, "th", entry_classes);
-  clear_strings_list (entry_classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  text_append_n (result, ">", 1);
-  /* TRANSLATORS: index entries column header in index formatting */
-  translate_convert_to_html_internal ("Index Entry", self, 0, 0, result,
-                                      "Tr th idx entries 1");
-  text_append_n (result, "</th>", 5);
-
-  xasprintf (&index_name_cmd_class, "sections-header-%s",
-             builtin_command_name (cmd));
-  add_string (index_name_cmd_class, entry_classes);
-  free (index_name_cmd_class);
-  attribute_class = html_attribute_class (self, "th", entry_classes);
-  clear_strings_list (entry_classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  text_append_n (result, ">", 1);
-  /* TRANSLATORS: section of index entry column header in index formatting */
-  translate_convert_to_html_internal ("Section", self, 0, 0, result,
-                                      "Tr th idx entries 2");
-  text_append_n (result, "</th></tr>\n", 11);
-  text_append_n (result, "<tr><td colspan=\"3\">", 20);
-  if (self->conf->DEFAULT_RULE.o.string)
-    text_append (result, self->conf->DEFAULT_RULE.o.string);
-  text_append_n (result, "</td></tr>\n", 11);
-  text_append (result, result_index_entries.text);
-  text_append_n (result, "</table>\n", 9);
-
-
-  html_pop_document_context (self);
-
-  if (non_alpha_nr + alpha_nr > 1)
-    {
-      printindex_letters_head_foot_internal (self, index_name, cmd,
-                                             entry_classes, "footer",
-                                             "Tr letters footer text",
-                                         alpha_text, non_alpha_text, result);
-
-      if (non_alpha_nr > 0)
-        free (non_alpha_text);
-      if (alpha_nr > 0)
-        free (alpha_text);
-    }
-
-  text_append_n (result, "</div>\n", 7);
-
-  free (entries_text.text);
-  free (result_index_entries.text);
-
-  destroy_strings_list (entry_classes);
-}
-
-void
-convert_informative_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  if (html_in_string (self))
-    return;
-
-  set_informative_command_value (self->conf, element);
-}
-
-void
-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 (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 (self->conf->CONTENTS_OUTPUT_LOCATION.o.string
-      && !strcmp (self->conf->CONTENTS_OUTPUT_LOCATION.o.string, "inline")
-      && ((used_cmd == CM_contents && self->conf->contents.o.integer > 0)
-          || (used_cmd == CM_shortcontents
-              && self->conf->shortcontents.o.integer > 0))
-      && self->document->sections_list
-      && self->document->sections_list->number > 1)
-    {
-      char *contents = contents_inline_element (self, used_cmd, element);
-      if (contents)
-        {
-          text_append (result, contents);
-          free (contents);
-        }
-    }
-}
-
-void
-convert_def_line_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result);
-
-void
-convert_def_command (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
-{
-  STRING_LIST *classes;
-  enum command_id original_cmd = cmd;
-  char *class;
-
-  if (builtin_command_data[cmd].flags & CF_line)
-    {
-      convert_def_line_type (self, ET_def_line, element, content, result);
-      return;
-    }
-
-  if (html_in_string (self))
-    {
-      if (content)
-        text_append (result, content);
-      return;
-    }
-
-  classes = new_string_list ();
-
-  if (cmd != CM_defblock)
-    {
-      if (builtin_command_data[cmd].flags & CF_def_alias)
-        {
-          int i;
-          for (i = 0; def_aliases[i].alias ; i++)
-            {
-              if (def_aliases[i].alias == cmd)
-                {
-                  original_cmd = def_aliases[i].command;
-                  break;
-                }
-            }
-        }
-
-      xasprintf (&class, "first-%s", builtin_command_name (original_cmd));
-      add_string (class, classes);
-      free (class);
-
-      if (cmd != original_cmd)
-        {
-          xasprintf (&class, "first-%s-alias-first-%s",
-                             builtin_command_name (cmd),
-                             builtin_command_name (original_cmd));
-          add_string (class, classes);
-          free (class);
-        }
-    }
-  else
-    add_string (builtin_command_name (cmd), classes);
-
-  add_string ("def-block", classes);
-
-  if (self->conf->DEF_TABLE.o.integer <= 0)
-    {
-      open_element_with_class (self, "dl", classes, result);
-      text_append_n (result, "\n", 1);
-      if (content)
-        text_append (result, content);
-      text_append_n (result, "</dl>\n", 6);
-    }
-  else
-    {
-      open_element_with_class (self, "table", classes, result);
-      text_append_n (result, "\n", 1);
-      if (content)
-        text_append (result, content);
-      text_append_n (result, "</table>\n", 9);
-    }
-
-  destroy_strings_list (classes);
-}
-
-
-/* associate command to the C function implementing the conversion */
-static const COMMAND_INTERNAL_CONVERSION commands_internal_conversion_table[] 
= {
-  {CM_w, &convert_w_command},
-  {CM_today, &convert_today_command},
-  {CM_value, &convert_value_command},
-  {CM_email, &convert_email_command},
-  {CM_abbr, &convert_explained_command},
-  {CM_acronym, &convert_explained_command},
-  {CM_anchor, &convert_anchor_command},
-  {CM_footnote, &convert_footnote_command},
-  {CM_uref, &convert_uref_command},
-  {CM_url, &convert_uref_command},
-  {CM_image, &convert_image_command},
-  {CM_math, &convert_math_command},
-  {CM_titlefont, &convert_titlefont_command},
-  {CM_U, &convert_U_command},
-  /* note that if indicateurl had been in style_formatted_cmd this
-     would have prevented indicateurl to be associated to
-     convert_style_command */
-  {CM_indicateurl, &convert_indicateurl_command},
-
-  {CM_inlineraw, &convert_inline_command},
-  {CM_inlinefmt, &convert_inline_command},
-  {CM_inlinefmtifelse, &convert_inline_command},
-  {CM_inlineifclear, &convert_inline_command},
-  {CM_inlineifset, &convert_inline_command},
-
-  {CM_indentedblock, &convert_indented_command},
-  {CM_smallindentedblock, &convert_indented_command},
-  {CM_verbatim, &convert_verbatim_command},
-  {CM_displaymath, &convert_displaymath_command},
-  {CM_raggedright, &convert_command_simple_block},
-  {CM_flushleft, &convert_command_simple_block},
-  {CM_flushright, &convert_command_simple_block},
-  {CM_group, &convert_command_simple_block},
-  {CM_menu, &convert_menu_command},
-  {CM_detailmenu, &convert_menu_command},
-  {CM_float, &convert_float_command},
-  {CM_quotation, &convert_quotation_command},
-  {CM_smallquotation, &convert_quotation_command},
-  {CM_cartouche, &convert_cartouche_command},
-  {CM_itemize, convert_itemize_command},
-  {CM_enumerate, convert_enumerate_command},
-  {CM_multitable, &convert_multitable_command},
-  {CM_table, &convert_xtable_command},
-  {CM_ftable, &convert_xtable_command},
-  {CM_vtable, &convert_xtable_command},
-  /* other @def* commands are handled together */
-  {CM_defblock, &convert_def_command},
-
-  {CM_verbatiminclude, &convert_verbatiminclude_command},
-  {CM_sp, &convert_sp_command},
-  {CM_exdent, &convert_exdent_command},
-  {CM_center, &convert_center_command},
-  {CM_author, &convert_author_command},
-  {CM_title, &convert_title_command},
-  {CM_subtitle, &convert_subtitle_command},
-  {CM_item, &convert_item_command},
-  {CM_headitem, &convert_item_command},
-  {CM_itemx, &convert_item_command},
-  {CM_tab, &convert_tab_command},
-
-  {CM_insertcopying, &convert_insertcopying_command},
-  {CM_listoffloats, &convert_listoffloats_command},
-  {CM_printindex, &convert_printindex_command},
-  /* @informative_global_commands in perl */
-  {CM_documentlanguage, &convert_informative_command},
-  {CM_footnotestyle, &convert_informative_command},
-  {CM_xrefautomaticsectiontitle, &convert_informative_command},
-  {CM_deftypefnnewline, &convert_informative_command},
-
-  {CM_contents, &convert_contents_command},
-  {CM_shortcontents, &convert_contents_command},
-  {CM_summarycontents, &convert_contents_command},
-
-  {CM_node, &convert_heading_command},
-  {CM_top, &convert_heading_command},
-  {CM_chapter, &convert_heading_command},
-  {CM_unnumbered, &convert_heading_command},
-  {CM_chapheading, &convert_heading_command},
-  {CM_appendix, &convert_heading_command},
-  {CM_section, &convert_heading_command},
-  {CM_unnumberedsec, &convert_heading_command},
-  {CM_heading, &convert_heading_command},
-  {CM_appendixsec, &convert_heading_command},
-  {CM_subsection, &convert_heading_command},
-  {CM_unnumberedsubsec, &convert_heading_command},
-  {CM_subheading, &convert_heading_command},
-  {CM_appendixsubsec, &convert_heading_command},
-  {CM_subsubsection, &convert_heading_command},
-  {CM_unnumberedsubsubsec, &convert_heading_command},
-  {CM_subsubheading, &convert_heading_command},
-  {CM_appendixsubsubsec, &convert_heading_command},
-  {CM_part, &convert_heading_command},
-  {CM_appendixsection, &convert_heading_command},
-  {CM_majorheading, &convert_heading_command},
-  {CM_centerchap, &convert_heading_command},
-
-  {CM_html, &convert_raw_command},
-  {CM_tex, &convert_raw_command},
-  {CM_xml, &convert_raw_command},
-  {CM_docbook, &convert_raw_command},
-  {CM_latex, &convert_raw_command},
-
-  {CM_inforef, &convert_xref_commands},
-  {CM_link, &convert_xref_commands},
-  {CM_xref, &convert_xref_commands},
-  {CM_ref, &convert_xref_commands},
-  {CM_pxref, &convert_xref_commands},
-
-  {0, 0},
-};
-
-void
-open_node_part_command (CONVERTER *self, const enum command_id cmd,
-                        const ELEMENT *element, TEXT *result)
-{
-  if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0)
-    {
-      const ELEMENT *node_element = 0;
-      int in_skipped_node_top
-        = self->shared_conversion_state.in_skipped_node_top;
-
-      if (cmd == CM_node)
-        node_element = element;
-      else if (cmd == CM_part)
-        {
-          const ELEMENT *part_following_node
-            = lookup_extra_element (element, AI_key_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)
-            {
-              const char *normalized = lookup_extra_string (node_element,
-                                                        AI_key_normalized);
-              if (normalized && !strcmp (normalized, "Top"))
-                {
-                  node_is_top = 1;
-                  in_skipped_node_top = 1;
-                  self->shared_conversion_state.in_skipped_node_top
-                    = in_skipped_node_top;
-                }
-            }
-          if (!node_is_top && in_skipped_node_top == 1)
-            {
-              in_skipped_node_top = -1;
-              self->shared_conversion_state.in_skipped_node_top
-                = in_skipped_node_top;
-            }
-        }
-    }
-}
-
-void
-open_quotation_command (CONVERTER *self, const enum command_id cmd,
-                        const ELEMENT *element, TEXT *result)
-{
-  const char *cmdname = element_command_name (element);
-  char *formatted_quotation_arg_to_prepend = 0;
-  if (element->e.c->args.number > 0
-      && element->e.c->args.list[0]->e.c->contents.number > 0)
-    {
-      ELEMENT *tree;
-      char *explanation;
-      NAMED_STRING_ELEMENT_LIST *substrings
-                                       = new_named_string_element_list ();
-      ELEMENT *quotation_arg_copy = copy_tree (element->e.c->args.list[0]);
-      add_element_to_named_string_element_list (substrings,
-                          "quotation_arg", quotation_arg_copy);
-      tree = html_cdt_tree ("@b{{quotation_arg}:} ",
-                           self, substrings, 0);
-      destroy_named_string_element_list (substrings);
-      xasprintf (&explanation, "open %s prepended arg", cmdname);
-      add_tree_to_build (self, tree);
-      formatted_quotation_arg_to_prepend
-        = html_convert_tree (self, tree, explanation);
-      remove_tree_to_build (self, tree);
-      destroy_element_and_children (tree);
-      free (explanation);
-    }
-  html_register_pending_formatted_inline_content (self, cmdname,
-                                  formatted_quotation_arg_to_prepend);
-  free (formatted_quotation_arg_to_prepend);
-}
-
-void
-open_inline_container_type (CONVERTER *self, const enum element_type type,
-                            const ELEMENT *element, TEXT *result)
-{
-  char *pending_formatted = html_get_pending_formatted_inline_content (self);
-  if (pending_formatted)
-    {
-      html_associate_pending_formatted_inline_content (self, element, 0,
-                                                       pending_formatted);
-      free (pending_formatted);
-    }
-}
-
-/* associate command to the C function implementing the opening */
-static const COMMAND_INTERNAL_OPEN commands_internal_open_table[] = {
-  {CM_node, &open_node_part_command},
-  {CM_part, &open_node_part_command},
-  {CM_quotation, &open_quotation_command},
-  {CM_smallquotation, &open_quotation_command},
-  {0, 0},
-};
-
-/* associate type to the C function implementing the opening */
-static const TYPE_INTERNAL_OPEN types_internal_open_table[] = {
-  {ET_paragraph, &open_inline_container_type},
-  {ET_preformatted, &open_inline_container_type},
-  {0, 0},
-};
-
-void
-convert_paragraph_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  TEXT content_text;
-  enum command_id align_cmd;
-  char *associated_content
-   = html_get_associated_formatted_inline_content (self, element, 0);
-
-  text_init (&content_text);
-
-  if (associated_content)
-    {
-      text_append (&content_text, associated_content);
-      free (associated_content);
-    }
-  if (content)
-    text_append (&content_text, content);
-
-  if (content_text.end <= 0)
-    {
-      free (content_text.text);
-      return;
-    }
-
-  if (html_paragraph_number (self) == 1)
-    {
-      enum command_id in_format_cmd = html_top_block_command (self);
-      if (in_format_cmd)
-        {
-          /* no first paragraph in those environment to avoid extra spacing */
-          if (in_format_cmd == CM_itemize
-              || in_format_cmd == CM_enumerate
-              || in_format_cmd == CM_multitable
-          /* this should only happen if in @nodedescriptionblock, otherwise
-             there are no paragraphs, but preformatted */
-              || in_format_cmd == CM_menu)
-            {
-              text_append (result, content_text.text);
-              free (content_text.text);
-              return;
-            }
-        }
-    }
-
-  if (html_in_string (self))
-    {
-      text_append (result, content_text.text);
-      free (content_text.text);
-      return;
-    }
-
-  if (content_text.text[strspn (content_text.text, whitespace_chars)] == '\0')
-    {
-      free (content_text.text);
-      return;
-    }
-
-  align_cmd = html_in_align (self);
-
-  if (align_cmd)
-    {
-      char *attribute_class;
-      char *class;
-      STRING_LIST *classes = new_string_list ();
-
-      xasprintf (&class, "%s-paragraph", builtin_command_name (align_cmd));
-      add_string (class, classes);
-      free (class);
-
-      attribute_class = html_attribute_class (self, "p", classes);
-      text_append (result, attribute_class);
-      text_append_n (result, ">", 1);
-      free (attribute_class);
-      destroy_strings_list (classes);
-    }
-  else
-    text_append_n (result, "<p>", 3);
-
-  text_append (result, content_text.text);
-  free (content_text.text);
-
-  text_append_n (result, "</p>", 4);
-}
-
-static char *
-preformatted_class (const CONVERTER *self)
-{
-  const COMMAND_OR_TYPE *cur_pre_class = 0;
-  const COMMAND_OR_TYPE_STACK *pre_classes
-        = html_preformatted_classes_stack (self);
-  size_t i;
-  for (i = 0; i < pre_classes->top; i++)
-    {
-      const COMMAND_OR_TYPE *cmd_or_type = &pre_classes->stack[i];
-      if (!(cur_pre_class
-            && (cur_pre_class->variety == CTV_type_command
-                && builtin_command_data[cur_pre_class->ct.cmd].flags
-                                   & CF_preformatted_code)
-            && (!((cmd_or_type->variety == CTV_type_command
-                   && builtin_command_data[cmd_or_type->ct.cmd].flags
-                                     & CF_preformatted_code)
-                  || cmd_or_type->ct.cmd == CM_menu))))
-         cur_pre_class = cmd_or_type;
-    }
-  if (cur_pre_class)
-    {
-      char *pre_class = 0;
-      if (cur_pre_class->variety == CTV_type_command)
-        {
-          xasprintf (&pre_class, "%s-preformatted",
-                     builtin_command_name (cur_pre_class->ct.cmd));
-        }
-      else if (cur_pre_class->variety == CTV_type_type)
-
-        {
-          xasprintf (&pre_class, "%s-preformatted",
-                     self->pre_class_types[cur_pre_class->ct.type]);
-        }
-      if (pre_class)
-        return pre_class;
-    }
-  /* should not happen */
-  return 0;
-}
-
-void
-convert_preformatted_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  TEXT content_text;
-  char *trimmed_content;
-  enum command_id in_format_cmd;
-  char *pre_class = 0;
-  int in_def = 0;
-  char *attribute_class;
-  STRING_LIST *classes;
-
-  char *associated_content
-   = html_get_associated_formatted_inline_content (self, element, 0);
-
-  text_init (&content_text);
-
-  if (associated_content)
-    {
-      text_append (&content_text, associated_content);
-      free (associated_content);
-    }
-  if (content)
-    text_append (&content_text, content);
-
-  if (content_text.end <= 0)
-    {
-      free (content_text.text);
-      return;
-    }
-
-  in_format_cmd = html_top_block_command (self);
-  if (in_format_cmd == CM_multitable)
-    {
-      const char *p = content_text.text;
-      p += strspn (p, whitespace_chars);
-      trimmed_content = trim_trailing_content (p);
-      free (content_text.text);
-    }
-  else
-    trimmed_content = content_text.text;
-
-  if (html_in_string (self))
-    {
-      text_append (result, trimmed_content);
-      free (trimmed_content);
-      return;
-    }
-
-  /* menu_entry_description is always in a preformatted container
-     in the tree, as the whole menu is meant to be an
-     environment where spaces and newlines are preserved. */
-  if (element->parent && element->parent->type == ET_menu_entry_description)
-    {
-      if (!html_inside_preformatted (self))
-        {
- /* If not in preformatted block command,
-    we don't preserve spaces and newlines in menu_entry_description,
-    instead the whole menu_entry is in a table, so no <pre> in that situation
-  */
-          text_append (result, trimmed_content);
-          free (trimmed_content);
-          return;
-        }
-      else
-        {
-     /* if directly in description, we want to avoid the linebreak that
-        comes with pre being a block level element, so set a special class */
-          pre_class = strdup ("menu-entry-description-preformatted");
-        }
-    }
-
-  if (!pre_class)
-    pre_class = preformatted_class (self);
-
-  /* this may happen with lines without textual content
-     between a def* and def*x. */
-  if (element->parent)
-    {
-      enum command_id p_cmd = element_builtin_cmd (element->parent);
-      if (builtin_command_data[p_cmd].flags & CF_def || p_cmd == CM_defblock)
-        {
-          in_def = 1;
-          text_append_n (result, "<dd>", 4);
-        }
-    }
-
-  classes = new_string_list ();
-  if (pre_class)
-    {
-      add_string (pre_class, classes);
-      free (pre_class);
-    }
-  attribute_class = html_attribute_class (self, "pre", classes);
-  text_append (result, attribute_class);
-  text_append_n (result, ">", 1);
-  free (attribute_class);
-  destroy_strings_list (classes);
-
-  /* a newline immediately after a <pre> is ignored. */
-  if (trimmed_content[0] == '\n')
-    text_append_n (result, "\n", 1);
-  text_append (result, trimmed_content);
-  free (trimmed_content);
-
-  text_append_n (result, "</pre>", 6);
-
-  if (in_def)
-    text_append_n (result, "</dd>", 5);
-}
-
-void
-convert_balanced_braces_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  if (content)
-    text_append (result, content);
-}
-
-void
-convert_index_entry_command_type (CONVERTER *self, const enum element_type 
type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  const char *index_id;
-
-  if (html_in_string (self) || html_in_multi_expanded (self))
-    return;
-
-  index_id = html_command_id (self, element);
-
-  if (index_id && strlen (index_id))
-    {
-      format_separate_anchor (self, index_id, "index-entry-id", result);
-      if (!html_in_preformatted_context (self))
-        text_append_n (result, "\n", 1);
-    }
-}
-
-void
-convert_definfoenclose_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  const char *begin = lookup_extra_string (element, AI_key_begin);
-  const char *end = lookup_extra_string (element, AI_key_end);
-
-  if (begin)
-    format_protect_text (self, begin, result);
-  if (content)
-    text_append (result, content);
-  if (end)
-    format_protect_text (self, end, result);
-}
-
-void
-convert_untranslated_def_line_arg_type
-                      (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  const char *category_text = element->e.c->contents.list[0]->e.text->text;
-  const char *translation_context
-    = lookup_extra_string (element, AI_key_translation_context);
-  ELEMENT *translated = html_cdt_tree (category_text,
-                                       self, 0, translation_context);
-
-  add_tree_to_build (self, translated);
-
-  convert_to_html_internal (self, translated, result,
-                            "translated TEXT");
-
-  remove_tree_to_build (self, translated);
-  destroy_element_and_children (translated);
-}
-
-void
-convert_row_type (CONVERTER *self, const enum element_type type,
-                  const ELEMENT *element, const char *content,
-                  TEXT *result)
-{
-  if (html_in_string (self))
-    {
-      if (content)
-        text_append (result, content);
-    }
-
-  if (!content || content[strspn (content, whitespace_chars)] == '\0')
-    return;
-  else
-    {
-      text_append_n (result, "<tr>", 4);
-      text_append (result, content);
-      text_append_n (result, "</tr>", 5);
-
-      if (element->e.c->contents.number > 0)
-        {
-          enum command_id first_cmd
-            = element_builtin_cmd (element->e.c->contents.list[0]);
-          if (first_cmd != CM_headitem)
-      /* if headitem, end of line added in _convert_multitable_head_type */
-            text_append (result, "\n");
-        }
-    }
-}
-
-void
-convert_multitable_head_type (CONVERTER *self, const enum element_type type,
-                  const ELEMENT *element, const char *content,
-                  TEXT *result)
-{
-  if (html_in_string (self))
-    {
-      if (content)
-        text_append (result, content);
-    }
-
-  if (!content || content[strspn (content, whitespace_chars)] == '\0')
-    return;
-  else
-    {
-      text_append_n (result, "<thead>", 7);
-      text_append (result, content);
-      text_append_n (result, "</thead>\n", 9);
-    }
-}
-
-void
-convert_multitable_body_type (CONVERTER *self, const enum element_type type,
-                  const ELEMENT *element, const char *content,
-                  TEXT *result)
-{
-  if (html_in_string (self))
-    {
-      if (content)
-        text_append (result, content);
-    }
-
-  if (!content || content[strspn (content, whitespace_chars)] == '\0')
-    return;
-  else
-    {
-      text_append_n (result, "<tbody>", 7);
-      text_append (result, content);
-      text_append_n (result, "</tbody>\n", 9);
-    }
-}
-
-static char *menu_entry_destination_array[] = {"menu-entry-destination"};
-static const STRING_LIST menu_entry_destination_classes
-  = {menu_entry_destination_array, 1, 1};
-static char *menu_entry_description_array[] = {"menu-entry-description"};
-static const STRING_LIST menu_entry_description_classes
-  = {menu_entry_description_array, 1, 1};
-
-static void
-menu_entry_a (const CONVERTER *self, const char *href, int isindex,
-              int html_menu_entry_index, TEXT *result)
-{
-  text_printf (result, "<a href=\"%s\"", href);
-  if (isindex)
-    text_append_n (result, " rel=\"index\"", 12);
-  if (self->conf->USE_ACCESSKEY.o.integer > 0 && html_menu_entry_index < 10)
-    text_printf (result, " accesskey=\"%d\"", html_menu_entry_index);
-  text_append_n (result, ">", 1);
-}
-
-void
-convert_menu_entry_type (CONVERTER *self, const enum element_type type,
-                  const ELEMENT *element, const char *content,
-                  TEXT *result)
-{
-  const ELEMENT *name_entry = 0;
-  const ELEMENT *menu_description = 0;
-  ELEMENT *menu_entry_node = 0;
-  const ELEMENT *menu_entry_leading_text = 0;
-  const ELEMENT *menu_entry_separators[2];
-  const ELEMENT *manual_content;
-  const ELEMENT *node_description = 0;
-  const ELEMENT *section = 0;
-  size_t i;
-  int entry_separators_nr = 0;
-  int entry_separators_idx = 0;
-  char *href = 0;
-  int isindex = 0;
-  int formatted_nodedescription_nr = 0;
-  int in_string = html_in_string (self);
-  int html_menu_entry_index;
+                  destroy_element_and_children (reference_tree);
 
-  memset (menu_entry_separators, 0, 2 * sizeof (const ELEMENT *));
+                  free (conv_str_entry);
+                  free (conv_str_reference);
 
-  for (i = 0; i < element->e.c->contents.number; i++)
-    {
-      ELEMENT *arg = element->e.c->contents.list[i];
-      if (arg->type == ET_menu_entry_leading_text)
-        menu_entry_leading_text = arg;
-      else if (arg->type == ET_menu_entry_name)
-        name_entry = arg;
-      else if (arg->type == ET_menu_entry_description)
-        menu_description = arg;
-      else if (arg->type == ET_menu_entry_separator)
-        {
-          menu_entry_separators[entry_separators_nr] = arg;
-          entry_separators_nr++;
-        }
-      else if (arg->type == ET_menu_entry_node)
-        menu_entry_node = arg;
-    }
+                  add_string (cmd_index_entry_class, entry_classes);
+                  add_string (section_class_seealso, section_classes);
+                }
 
-  manual_content = lookup_extra_container (menu_entry_node,
-                                         AI_key_manual_content);
+              destroy_named_string_element_list (substrings);
 
-  if (manual_content)
-    href = html_command_href (self, menu_entry_node, 0, element, 0);
-  else
-    {
-  /* may not be defined in case of menu entry node consisting only of spaces */
-      const char *normalized = lookup_extra_string (menu_entry_node,
-                                                    AI_key_normalized);
-      if (normalized)
-        {
-          const ELEMENT *node
-           = find_identifier_target (&self->document->identifiers_target,
-                                     normalized);
-          if (node)
-            {
-              node_description
-                 = lookup_extra_element (node, AI_key_node_description);
+              text_append_n (&entries_text, "<tr><td></td>", 13);
+              if (last_entry_level > 0)
+                {
+                  char *index_entry_level;
+                  xasprintf (&index_entry_level, "index-entry-level-%d",
+                                                 last_entry_level);
+                  add_string (index_entry_level, entry_classes);
+                  free (index_entry_level);
+                }
+              attribute_class = html_attribute_class (self, "td", 
entry_classes);
+              text_append (&entries_text, attribute_class);
+              clear_strings_list (entry_classes);
+              free (attribute_class);
+              text_append_n (&entries_text, ">", 1);
 
-   /* if !NODE_NAME_IN_MENU, we pick the associated section, except if
-      the node is the element command */
-              if (self->conf->NODE_NAME_IN_MENU.o.integer <= 0)
+              if (!seeentry && in_code)
+                text_append_n (&entries_text, "<code>", 6);
+              text_append (&entries_text, entry);
+              free (entry);
+              if (!seeentry)
                 {
-                  const ELEMENT *associated_section = lookup_extra_element 
(node,
-                                                       
AI_key_associated_section);
-                  if (associated_section)
+                  if (in_code)
+                    text_append_n (&entries_text, "</code>", 7);
+                  if (self->conf->INDEX_ENTRY_COLON.o.string)
+                    text_append (&entries_text,
+                                 self->conf->INDEX_ENTRY_COLON.o.string);
+                }
+              text_append_n (&entries_text, "</td>", 5);
+
+              attribute_class
+                 = html_attribute_class (self, "td", section_classes);
+              text_append (&entries_text, attribute_class);
+              clear_strings_list (section_classes);
+              free (attribute_class);
+              text_append_n (&entries_text, ">", 1);
+              if (reference)
+                {
+                  text_append (&entries_text, reference);
+                  free (reference);
+                }
+              text_append_n (&entries_text, "</td></tr>\n", 11);
+
+            }
+          else
+            {
+              char *entry = 0;
+              if (entry_tree)
+                {
+                  xasprintf (&convert_info, "index %s l %s index entry %zu",
+                             index_name, letter, entry_nr -1);
+
+                  if (last_entry_level > 0)
+                    add_tree_to_build (self, entry_tree);
+                  if (*formatted_index_entry_nr > 1)
                     {
-                      const ELEMENT *associated_command
-                       = html_command_root_element_command (self, node);
-                      if (associated_command != node)
-                        {
-                          section = associated_section;
-                          href = html_command_href (self, section,
-                                                    0, element, 0);
-                        }
+                      /* call with multiple_pass argument */
+                      entry = convert_tree_new_formatting_context (self,
+                                           entry_tree, convert_info,
+                                           multiple_pass_str, 0, 0);
+                    }
+                  else
+                    {
+                      entry = html_convert_tree (self, entry_tree,
+                                                 convert_info);
                     }
+                  if (last_entry_level > 0)
+                    remove_tree_to_build (self, entry_tree);
+                  free (convert_info);
                 }
-              if (!href)
-                href = html_command_href (self, node, 0, element, 0);
-
-          /* will mark the target as an index with rel index.  See
-  http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions
-           */
-              isindex = (node->flags & EF_isindex);
 
-              if (node_description
-                  /* not menu_description probably cannot happen */
-                  && (!menu_description
-                         /* empty description */
-           || (menu_description->e.c->contents.number <= 0
-               || (menu_description->e.c->contents.number == 1
-                   && 
(menu_description->e.c->contents.list[0]->e.c->contents.number <= 0
-                       || 
(menu_description->e.c->contents.list[0]->e.c->contents.number == 1
-                           && 
menu_description->e.c->contents.list[0]->e.c->contents.list[0]->type == 
ET_normal_text
-   && 
menu_description->e.c->contents.list[0]->e.c->contents.list[0]->e.text->text[
-       strspn 
(menu_description->e.c->contents.list[0]->e.c->contents.list[0]->e.text->text,
-             whitespace_chars)] == '\0'))))))
+              if (last_entry_level == 0
+                  && (!entry
+                      || entry[strspn (entry, whitespace_chars)] == '\0'))
                 {
-        /* update the number of time the node description was formatted */
-                  HTML_TARGET *target_info
-                     = find_element_target (self->html_targets, node);
-                  target_info->formatted_nodedescription_nr++;
-                  formatted_nodedescription_nr
-                    = target_info->formatted_nodedescription_nr;
+                  free (entry);
+                  free (new_normalized_entry_levels[0]);
+                  new_normalized_entry_levels[0] = 0;
                 }
-            }
-        }
-    }
-  self->shared_conversion_state.html_menu_entry_index++;
-  html_menu_entry_index = self->shared_conversion_state.html_menu_entry_index;
+              else
+                {
+                  if (!first_entry)
+                    first_entry = index_entry_ref;
 
-  if (html_inside_preformatted (self) || in_string)
-    {
-      const char *leading_text = menu_entry_leading_text->e.text->text;
-      const char *menu_symbol;
-      if (!in_string)
-        {
-          char *pre_class = preformatted_class (self);
-          char *attribute_class;
-          STRING_LIST *classes = new_string_list ();
-          if (pre_class)
-            {
-              add_string (pre_class, classes);
-              free (pre_class);
-            }
-          attribute_class = html_attribute_class (self, "pre", classes);
-          text_append (result, attribute_class);
-          text_append_n (result, ">", 1);
-          free (attribute_class);
-          destroy_strings_list (classes);
-        }
+                  if (index_entry_ref->entry_associated_element)
+                    target_element = index_entry_ref->entry_associated_element;
+                  else
+                    target_element = main_entry_element;
 
-      /* add leading text, replacing "*" by MENU_SYMBOL */
-      menu_symbol = strchr (leading_text, '*');
-      if (menu_symbol - leading_text > 0)
-        {
-          text_append_n (result, leading_text, menu_symbol - leading_text);
-          leading_text = menu_symbol;
-        }
-      if (self->conf->MENU_SYMBOL.o.string)
-        text_append (result, self->conf->MENU_SYMBOL.o.string);
-      /* past "*" */
-      leading_text++;
-      text_append (result, leading_text);
+                  entry_href
+                    = html_command_href (self, target_element, 0, 0, 0);
 
-      if (name_entry)
-        {
-          convert_to_html_internal (self, name_entry, result,
-                                  "menu_arg menu_entry_name preformatted");
-          convert_to_html_internal (self,
-                     menu_entry_separators[entry_separators_idx],
-                     result, "menu_arg name separator preformatted");
-          entry_separators_idx++;
-        }
+                  add_string (cmd_index_entry_class, entry_classes);
+                  if (last_entry_level > 0)
+                    {
+                      char *index_entry_level;
+                      xasprintf (&index_entry_level, "index-entry-level-%d",
+                                                     last_entry_level);
+                      add_string (index_entry_level, entry_classes);
+                      free (index_entry_level);
+                    }
+                  text_append_n (&entries_text, "<tr><td></td>", 13);
+                  attribute_class = html_attribute_class (self, "td",
+                                                          entry_classes);
+                  text_append (&entries_text, attribute_class);
+                  clear_strings_list (entry_classes);
+                  free (attribute_class);
+                  text_append_n (&entries_text, ">", 1);
 
-      if (menu_entry_node)
-        {
-          ELEMENT *root_code = new_element (ET__code);
-          if (!in_string && href)
-            {
-              menu_entry_a (self, href, isindex, html_menu_entry_index,
-                            result);
-            }
+                  text_printf (&entries_text, "<a href=\"%s\">", entry_href);
+                  free (entry_href);
+                  if (in_code)
+                    text_append_n (&entries_text, "<code>", 6);
+                  if (entry)
+                    {
+                      text_append (&entries_text, entry);
+                      free (entry);
+                    }
+                  if (in_code)
+                    text_append_n (&entries_text, "</code>", 7);
+                  text_append_n (&entries_text, "</a>", 4);
+                  if (self->conf->INDEX_ENTRY_COLON.o.string)
+                    text_append (&entries_text,
+                                 self->conf->INDEX_ENTRY_COLON.o.string);
+                  text_append_n (&entries_text, "</td>", 5);
 
-          add_to_contents_as_array (root_code, menu_entry_node);
+                  if (self->conf->NODE_NAME_IN_INDEX.o.integer > 0)
+                    {
+                      associated_command
+                          = lookup_extra_element (main_entry_element,
+                                                    AI_key_element_node);
+                      if (!associated_command)
+                        associated_command
+                          = html_command_node (self, target_element);
 
-          add_tree_to_build (self, root_code);
+                      if (!associated_command
+                          && *formatted_index_entry_nr == 1)
+                        {
+                          char *element_region
+                           = lookup_extra_string (main_entry_element,
+                                                  AI_key_element_region);
+        /* do not warn if the entry is in a special region, like titlepage */
+                          if (!element_region)
+                            {
+     /* NOTE $self->in_multiple_conversions() is not checked as printindex
+        should not happen in multiple tree conversion, but the error message
+        is printed for the first entry formatting only. */
+                              message_list_command_warn (&self->error_messages,
+                                      self->conf,
+                                      main_entry_element, 0,
+                "entry for index `%s' for @printindex %s outside of any node",
+                                  entry_index->name, index_name);
+                            }
+                        }
+                    }
 
-          convert_to_html_internal (self, root_code, result,
-                               "menu_arg menu_entry_node preformatted");
+                  if (!associated_command)
+                    {
+                      associated_command
+                         = html_command_root_element_command (self,
+                                                              target_element);
+                      if (!associated_command)
+                        {
+                          associated_command
+                     = self->global_units_directions[D_Top]->uc.unit_command;
 
-          remove_tree_to_build (self, root_code);
+         /* NOTE the warning here catches the most relevant cases of
+            index entry that is not associated to the right command, which
+            are very few in the test suite.  There is also a warning in the
+            parser with a much broader scope with possible overlap, but the
+            overlap is not a problem.
+            NODE_NAME_IN_INDEX may be undef even with USE_NODES set if the
+            converter is called as convert() as in the test suite */
 
-          destroy_element (root_code);
+                          if (self->conf->NODE_NAME_IN_INDEX.o.integer == 0
+                              && *formatted_index_entry_nr == 1)
+                            {
+                              char *element_region
+                               = lookup_extra_string (main_entry_element,
+                                                      AI_key_element_region);
+        /* do not warn if the entry is in a special region, like titlepage */
+                              if (!element_region)
+                                {
+      /* NOTE $self->in_multiple_conversions() is not checked as printindex
+         should not happen in multiple tree conversion, but the error message
+         is printed for the first entry formatting only.
+         NOTE the index entry may be associated to a node in that case. */
+                              message_list_command_warn (&self->error_messages,
+                                      self->conf,
+                                      main_entry_element, 0,
+             "entry for index `%s' for @printindex %s outside of any section",
+                                  entry_index->name, index_name);
+                                }
+                            }
+                        }
+                    }
 
-          if (!in_string && href)
-            text_append_n (result, "</a>", 4);
-        }
+                  add_string (cmd_index_section_class, section_classes);
+                  attribute_class
+                     = html_attribute_class (self, "td", section_classes);
+                  text_append (&entries_text, attribute_class);
+                  free (attribute_class);
+                  clear_strings_list (section_classes);
+                  text_append_n (&entries_text, ">", 1);
 
-      if (entry_separators_idx < entry_separators_nr)
-        {
-          convert_to_html_internal (self,
-                     menu_entry_separators[entry_separators_idx],
-                     result, "menu_arg node separator preformatted");
-          entry_separators_idx++;
-        }
+                  if (associated_command)
+                    {
+                      char *associated_command_href
+                       = html_command_href (self, associated_command, 0, 0, 0);
+                      char *associated_command_text
+                       = html_command_text (self, associated_command, 0);
 
-      if (!in_string)
-        text_append_n (result, "</pre>", 6);
+                      if (associated_command_href)
+                        {
+                          text_printf (&entries_text, "<a href=\"%s\">%s</a>",
+                            associated_command_href, associated_command_text);
+                        }
+                      else
+                        {
+                          text_append (&entries_text, associated_command_text);
+                        }
 
-      if (formatted_nodedescription_nr > 0)
-        {
-          char *multiple_formatted = 0;
-          char *description;
-          ELEMENT *description_element;
-          if (formatted_nodedescription_nr > 1)
-            {
-              xasprintf (&multiple_formatted,
-                         "preformatted-node-description-%d",
-                         formatted_nodedescription_nr);
+                      free (associated_command_text);
+                      free (associated_command_href);
+                    }
+                  text_append_n (&entries_text, "</td></tr>\n", 11);
+                }
             }
 
-          if (node_description->e.c->cmd == CM_nodedescription)
-            description_element = node_description->e.c->args.list[0];
-          else
+          if (new_normalized_entry_levels[0] != 0)
             {
-              description_element = new_element (ET_NONE);
-              description_element->e.c->contents = 
node_description->e.c->contents;
-              add_tree_to_build (self, description_element);
+              for (level = 0; level < SUBENTRIES_MAX_LEVEL; level++)
+                {
+                  free (prev_normalized_entry_levels[level]);
+                  prev_normalized_entry_levels[level]
+                    = new_normalized_entry_levels[level];
+                }
             }
 
-          description
-            = convert_tree_new_formatting_context (self, description_element,
-                 "menu_arg node description preformatted", multiple_formatted,
-                 0, CM_menu);
+          if (last_entry_level > 0 && entry_tree)
+            destroy_element (entry_tree);
 
-          if (description)
-            {
-              text_append (result, description);
-              free (description);
-            }
+          if (other_subentries_tree)
+            free_comma_index_subentries_tree (other_subentries_tree);
 
-          if (formatted_nodedescription_nr > 1)
-            free (multiple_formatted);
-          if (node_description->e.c->cmd != CM_nodedescription)
-            {
-              remove_tree_to_build (self, description_element);
-              description_element->e.c->contents.list = 0;
-              destroy_element (description_element);
-            }
-        }
-      else if (menu_description)
-        {
-          convert_to_html_internal (self, menu_description, result,
-                                    "menu_arg description preformatted");
-        }
-    }
-  else
-    {
-      char *description = 0;
-      char *name_no_number = 0;
-      text_append_n (result, "<tr>", 4);
-      open_element_with_class (self, "td",
-                               &menu_entry_destination_classes, result);
+          destroy_element (entry_ref_tree);
 
-      if (section && href)
-        {
-          char *name = html_command_text (self, section, 0);
-          if (name && strlen (name))
-            {
-              name_no_number = html_command_text (self, section,
-                                                  HTT_text_nonumber);
-              menu_entry_a (self, href, isindex, html_menu_entry_index,
-                            result);
-              text_append (result, name);
-              text_append_n (result, "</a>", 4);
-            }
-          free (name);
+          if (*formatted_index_entry_nr > 1)
+            free (multiple_pass_str);
         }
-      if (!name_no_number)
+      clear_normalized_entry_levels (prev_normalized_entry_levels);
+
+      if (entries_text.end > 0)
         {
-          char *name = 0;
-          if (name_entry)
+          char *formatted_letter;
+          const ELEMENT *letter_command = 0;
+          enum command_id letter_cmd = 0;
+
+          if (first_entry)
             {
-              name = html_convert_tree (self, name_entry,
-                                        "convert menu_entry_name");
-              if (name)
+              INDEX_ENTRY_TEXT_OR_COMMAND *entry_text_or_command
+                = index_entry_first_letter_text_or_command (first_entry);
+
+              if (entry_text_or_command)
                 {
-                  if (!strlen (name))
-                    {
-                      free (name);
-                      name = 0;
-                    }
+                  letter_command = entry_text_or_command->command;
+
+                  free (entry_text_or_command->text);
+                  free (entry_text_or_command);
+
+                  if (letter_command)
+                    letter_cmd = element_builtin_data_cmd (letter_command);
                 }
             }
-          if (!name)
+
+          if (letter_command
+              && (!(builtin_command_data[letter_cmd].flags & CF_accent))
+              && letter_cmd != CM_U
+            /* special case, the uppercasing of that command is not done
+               if as a command, while it is done correctly in letter */
+              && letter_cmd != CM_ss)
             {
-              const ELEMENT *manual_content
-                           = lookup_extra_container (menu_entry_node,
-                                                   AI_key_manual_content);
-              ELEMENT *node_content
-                         = lookup_extra_container (menu_entry_node,
-                                                 AI_key_node_content);
-              if (manual_content)
+              ELEMENT *formatted_command = 0;
+              char *explanation;
+              if (html_commands_data[letter_cmd].upper_case_cmd)
                 {
-                  name = html_command_text (self, menu_entry_node, 0);
+                   formatted_command
+                     = new_command_element (ET_brace_noarg_command,
+                             html_commands_data[letter_cmd].upper_case_cmd);
                 }
-              else if (node_content)
-                {
-                  ELEMENT *root_code = new_element (ET__code);
 
-                  add_to_contents_as_array (root_code, node_content);
-
-                  add_tree_to_build (self, root_code);
+              xasprintf (&explanation, "index letter %s command", letter);
+              if (formatted_command)
+                {
+                  add_tree_to_build (self, formatted_command);
+                  formatted_letter
+                    = html_convert_tree (self, formatted_command, explanation);
+                  remove_tree_to_build (self, formatted_command);
+                  destroy_element (formatted_command);
+                }
+              else
+                formatted_letter
+                  = html_convert_tree (self, letter_command, explanation);
+              free (explanation);
+            }
+          else
+            {
+              TEXT text_letter;
+              text_init (&text_letter);
+              text_append (&text_letter, "");
+              format_protect_text (self, letter, &text_letter);
+              formatted_letter = text_letter.text;
+            }
 
-                  name = html_convert_tree (self, root_code,
-                                            "menu_arg name");
+          formatted_letters[i] = formatted_letter;
 
-                  remove_tree_to_build (self, root_code);
+          text_append_n (&result_index_entries, "<tr>", 4);
+          text_printf (&result_index_entries, "<th id=\"%s\">", letter_id[i]);
+          text_append (&result_index_entries, formatted_letter);
+          text_append_n (&result_index_entries, "</th></tr>\n", 11);
+          text_append (&result_index_entries, entries_text.text);
+          text_append_n (&result_index_entries, "<tr><td colspan=\"3\">", 20);
+          if (self->conf->DEFAULT_RULE.o.string)
+            text_append (&result_index_entries,
+                         self->conf->DEFAULT_RULE.o.string);
+          text_append_n (&result_index_entries, "</td></tr>\n", 11);
+        }
+      else
+        {
+          formatted_letters[i] = 0;
+        }
+    }
 
-                  destroy_element (root_code);
-                }
-            }
+  add_string (summary_letter_cmd, entry_classes);
+  attribute_class = html_attribute_class (self, "a", entry_classes);
+  for (i = 0; i < index_sorted->letter_number; i++)
+    {
+      if (formatted_letters[i])
+        {
+          text_reset (&entries_text);
 
-          if (self->conf->MENU_SYMBOL.o.string)
-            text_append (result, self->conf->MENU_SYMBOL.o.string);
-          text_append_n (result, " ", 1);
+          text_append (&entries_text, attribute_class);
+          text_printf (&entries_text, " href=\"#%s\"><b>", letter_id[i]);
+          text_append (&entries_text, formatted_letters[i]);
+          text_append_n (&entries_text, "</b></a>", 8);
 
-          if (href)
+          if (letter_is_symbol[i])
             {
-              menu_entry_a (self, href, isindex, html_menu_entry_index,
-                            result);
+              non_alpha[non_alpha_nr] = strdup (entries_text.text);
+              non_alpha_nr++;
             }
-
-          if (name)
+          else
             {
-              size_t n = strspn (name, whitespace_chars);
-              if (n)
-                {
-                  name_no_number = strdup (name + n);
-                  free (name);
-                }
-              else
-                name_no_number = name;
-
-              text_append (result, name_no_number);
+              alpha[alpha_nr] = strdup (entries_text.text);
+              alpha_nr++;
             }
 
-          if (href)
-            text_append_n (result, "</a>", 4);
+          free (formatted_letters[i]);
         }
+    }
+  free (attribute_class);
 
-      if (self->conf->MENU_ENTRY_COLON.o.string)
-        text_append (result, self->conf->MENU_ENTRY_COLON.o.string);
-      text_append_n (result, "</td>", 5);
+  free (letter_is_symbol);
+  free (formatted_letters);
 
-      open_element_with_class (self, "td",
-                               &menu_entry_description_classes, result);
+  for (i = 0; i < index_sorted->letter_number; i++)
+    free (letter_id[i]);
+  free (letter_id);
 
-      if (formatted_nodedescription_nr > 0)
-        {
-          char *multiple_formatted = 0;
-          ELEMENT *description_element;
-          if (formatted_nodedescription_nr > 1)
-            {
-              xasprintf (&multiple_formatted,
-                         "node-description-%d",
-                         formatted_nodedescription_nr);
-            }
+  free (entry_class_seeentry);
+  free (section_class_seeentry);
+  free (cmd_index_entry_class);
+  free (section_class_seealso);
+  free (cmd_index_section_class);
+  free (summary_letter_cmd);
 
-          if (node_description->e.c->cmd == CM_nodedescription)
-            description_element = node_description->e.c->args.list[0];
-          else
-            {
-              description_element = new_element (ET_NONE);
-              description_element->e.c->contents = 
node_description->e.c->contents;
-              add_tree_to_build (self, description_element);
-            }
+  destroy_strings_list (section_classes);
 
-          description
-            = convert_tree_new_formatting_context (self, description_element,
-                 "menu_arg node description", multiple_formatted,
-                 0, CM_menu);
+  if (non_alpha_nr + alpha_nr <= 0)
+    {
+      free (alpha);
+      free (non_alpha);
+      html_pop_document_context (self);
+      free (entries_text.text);
+      free (result_index_entries.text);
+      destroy_strings_list (entry_classes);
+      return;
+    }
 
-          if (formatted_nodedescription_nr > 1)
-            free (multiple_formatted);
-          if (node_description->e.c->cmd != CM_nodedescription)
+  clear_strings_list (entry_classes);
+
+  add_string (builtin_command_name (cmd), entry_classes);
+  xasprintf (&index_name_cmd_class, "%s-%s",
+             index_name, builtin_command_name (cmd));
+  add_string (index_name_cmd_class, entry_classes);
+  free (index_name_cmd_class);
+  attribute_class = html_attribute_class (self, "div", entry_classes);
+  clear_strings_list (entry_classes);
+  text_append (result, attribute_class);
+  free (attribute_class);
+  text_append_n (result, ">\n", 2);
+
+  /* Format the summary letters */
+  if (non_alpha_nr + alpha_nr > 1)
+    {
+      if (non_alpha_nr > 0)
+        {
+          text_reset (&entries_text);
+          text_append (&entries_text, non_alpha[0]);
+          if (non_alpha_nr > 1)
             {
-              remove_tree_to_build (self, description_element);
-              description_element->e.c->contents.list = 0;
-              destroy_element (description_element);
+              for (i = 1; i < non_alpha_nr; i++)
+                {
+                  text_append_n (&entries_text, "\n ", 2);
+                  text_append_n (&entries_text,
+                        self->special_character[SC_non_breaking_space].string,
+                        self->special_character[SC_non_breaking_space].len);
+                  text_append_n (&entries_text, " \n", 2);
+                  text_append (&entries_text, non_alpha[i]);
+                }
             }
+          text_append_n (&entries_text, "\n", 1);
+          non_alpha_text = strdup (entries_text.text);
         }
-      else if (menu_description)
+      if (alpha_nr > 0)
         {
-          description = html_convert_tree (self, menu_description,
-                                           "menu_arg description");
+          text_reset (&entries_text);
+          for (i = 0; i < alpha_nr; i++)
+            {
+              text_append (&entries_text, alpha[i]);
+              text_append_n (&entries_text, "\n ", 2);
+              text_append_n (&entries_text,
+                 self->special_character[SC_non_breaking_space].string,
+                 self->special_character[SC_non_breaking_space].len);
+              text_append_n (&entries_text, " \n", 2);
+            }
+          alpha_text = strdup (entries_text.text);
         }
 
-      if (description)
-        {
-          text_append (result, description);
-          free (description);
-        }
+      /* format the summary */
+      printindex_letters_head_foot_internal (self, index_name, cmd,
+                                             entry_classes, "header",
+                                             "Tr letters header text",
+                                         alpha_text, non_alpha_text, result);
+    }
 
-      free (name_no_number);
-      text_append_n (result, "</td></tr>\n", 11);
+  if (non_alpha_nr > 0)
+    {
+      for (i = 0; i < non_alpha_nr; i++)
+        free (non_alpha[i]);
     }
+  free (non_alpha);
 
-  free (href);
-}
+  if (alpha_nr > 0)
+    {
+      for (i = 0; i < alpha_nr; i++)
+        free (alpha[i]);
+    }
+  free (alpha);
 
-static char *menu_comment_array[] = {"menu-comment"};
-static const STRING_LIST menu_comment_classes = {menu_comment_array, 1, 1};
+  /* now format the index entries */
+  xasprintf (&index_name_cmd_class, "%s-entries-%s",
+             index_name, builtin_command_name (cmd));
+  add_string (index_name_cmd_class, entry_classes);
+  free (index_name_cmd_class);
+  attribute_class = html_attribute_class (self, "table", entry_classes);
+  clear_strings_list (entry_classes);
+  text_append (result, attribute_class);
+  free (attribute_class);
+  text_append_n (result, ">\n<tr><td></td>", 15);
 
-void
-convert_menu_comment_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  char *attribute_class;
+  xasprintf (&index_name_cmd_class, "entries-header-%s",
+             builtin_command_name (cmd));
+  add_string (index_name_cmd_class, entry_classes);
+  free (index_name_cmd_class);
+  attribute_class = html_attribute_class (self, "th", entry_classes);
+  clear_strings_list (entry_classes);
+  text_append (result, attribute_class);
+  free (attribute_class);
+  text_append_n (result, ">", 1);
+  /* TRANSLATORS: index entries column header in index formatting */
+  translate_convert_to_html_internal ("Index Entry", self, 0, 0, result,
+                                      "Tr th idx entries 1");
+  text_append_n (result, "</th>", 5);
 
-  if (html_inside_preformatted (self) || html_in_string (self))
+  xasprintf (&index_name_cmd_class, "sections-header-%s",
+             builtin_command_name (cmd));
+  add_string (index_name_cmd_class, entry_classes);
+  free (index_name_cmd_class);
+  attribute_class = html_attribute_class (self, "th", entry_classes);
+  clear_strings_list (entry_classes);
+  text_append (result, attribute_class);
+  free (attribute_class);
+  text_append_n (result, ">", 1);
+  /* TRANSLATORS: section of index entry column header in index formatting */
+  translate_convert_to_html_internal ("Section", self, 0, 0, result,
+                                      "Tr th idx entries 2");
+  text_append_n (result, "</th></tr>\n", 11);
+  text_append_n (result, "<tr><td colspan=\"3\">", 20);
+  if (self->conf->DEFAULT_RULE.o.string)
+    text_append (result, self->conf->DEFAULT_RULE.o.string);
+  text_append_n (result, "</td></tr>\n", 11);
+  text_append (result, result_index_entries.text);
+  text_append_n (result, "</table>\n", 9);
+
+
+  html_pop_document_context (self);
+
+  if (non_alpha_nr + alpha_nr > 1)
     {
-      if (content)
-        text_append (result, content);
-      return;
+      printindex_letters_head_foot_internal (self, index_name, cmd,
+                                             entry_classes, "footer",
+                                             "Tr letters footer text",
+                                         alpha_text, non_alpha_text, result);
+
+      if (non_alpha_nr > 0)
+        free (non_alpha_text);
+      if (alpha_nr > 0)
+        free (alpha_text);
     }
 
-  text_append_n (result, "<tr>", 4);
-  attribute_class = html_attribute_class (self, "th",
-                                &menu_comment_classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  text_append_n (result, " colspan=\"2\">", 13);
+  text_append_n (result, "</div>\n", 7);
 
-  if (content)
-    text_append (result, content);
+  free (entries_text.text);
+  free (result_index_entries.text);
 
-  text_append_n (result, "</th></tr>", 10);
+  destroy_strings_list (entry_classes);
 }
 
 void
-convert_before_item_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
+html_convert_informative_command (CONVERTER *self, const enum command_id cmd,
+                                  const ELEMENT *element,
+                                  const HTML_ARGS_FORMATTED *args_formatted,
+                                  const char *content, TEXT *result)
 {
-  enum command_id in_format_cmd;
-
-  if (!content || content[strspn (content, whitespace_chars)] == '\0')
+  if (html_in_string (self))
     return;
 
+  set_informative_command_value (self->conf, element);
+}
+
+void
+html_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 (html_in_string (self))
-    {
-      text_append (result, content);
-      return;
-    }
+    return;
 
-  in_format_cmd = html_top_block_command (self);
+  enum command_id used_cmd;
 
-  if (in_format_cmd == CM_itemize || in_format_cmd == CM_enumerate)
-    {
-      text_append_n (result, "<li>", 4);
-      text_append (result, content);
-      text_append_n (result, "</li>", 5);
-    }
-  else if (in_format_cmd == CM_table || in_format_cmd == CM_vtable
-           || in_format_cmd == CM_ftable)
-    {
-      text_append_n (result, "<dd>", 4);
-      text_append (result, content);
-      text_append_n (result, "</dd>\n", 6);
-    }
-  else if (in_format_cmd == CM_multitable)
-    {
-      char *trimmed_content;
-      const char *p = content;
-      p += strspn (p, whitespace_chars);
-      trimmed_content = trim_trailing_content (p);
+  if (cmd == CM_summarycontents)
+    used_cmd = CM_shortcontents;
+  else
+    used_cmd = cmd;
 
-      text_append_n (result, "<tr><td>", 8);
-      text_append (result, trimmed_content);
-      free (trimmed_content);
-      text_append_n (result, "</td></tr>\n", 11);
-    }
-}
+  set_informative_command_value (self->conf, element);
 
-void
-convert_table_term_type (CONVERTER *self, const enum element_type type,
-                        const ELEMENT *element, const char *content,
-                        TEXT *result)
-{
-  if (content)
+  if (self->conf->CONTENTS_OUTPUT_LOCATION.o.string
+      && !strcmp (self->conf->CONTENTS_OUTPUT_LOCATION.o.string, "inline")
+      && ((used_cmd == CM_contents && self->conf->contents.o.integer > 0)
+          || (used_cmd == CM_shortcontents
+              && self->conf->shortcontents.o.integer > 0))
+      && self->document->sections_list
+      && self->document->sections_list->number > 1)
     {
-      text_append (result, "<dt>");
-      text_append (result, content);
+      char *contents = contents_inline_element (self, used_cmd, element);
+      if (contents)
+        {
+          text_append (result, contents);
+          free (contents);
+        }
     }
 }
 
-#define static_class(name, class) \
-static char * name ##_array[] = {#class}; \
-static const STRING_LIST name ##_classes = {name ##_array, 1, 1};
-
-static_class(def_type, def-type)
-static_class(def_name, def-name)
-static_class(def_code_arguments, def-code-arguments)
-static_class(def_var_arguments, def-var-arguments)
-static_class(call_def, call-def)
-static_class(category_def, category-def)
-
-#undef static_class
+void
+html_convert_def_line_type (CONVERTER *self, const enum element_type type,
+                            const ELEMENT *element, const char *content,
+                            TEXT *result);
 
 void
-convert_def_line_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
+html_convert_def_command (CONVERTER *self, const enum command_id cmd,
+                          const ELEMENT *element,
+                          const HTML_ARGS_FORMATTED *args_formatted,
+                          const char *content, TEXT *result)
 {
-  const char *index_id;
-  PARSED_DEF *parsed_def;
   STRING_LIST *classes;
-  char *attribute_class;
-  char *alias_class = 0;
-  enum command_id original_def_cmd;
-  enum command_id def_cmd;
-  enum command_id original_cmd = 0;
-  enum command_id base_cmd = 0;
-  TEXT def_call;
-  char *anchor;
+  enum command_id original_cmd = cmd;
+  char *class;
 
-  if (html_in_string (self))
+  if (builtin_command_data[cmd].flags & CF_line)
     {
-      /* should probably never happen */
-      char *text;
-      text = convert_to_text (element, self->convert_text_options);
-      format_protect_text (self, text, result);
-      free (text);
+      html_convert_def_line_type (self, ET_def_line, element, content, result);
+      return;
     }
 
-  index_id = html_command_id (self, element);
-
-  parsed_def = definition_arguments_content (element);
-
-  if (element->e.c->cmd)
-    original_def_cmd = element->e.c->cmd;
-  else
-    original_def_cmd = element->parent->e.c->cmd;
-
-  if (builtin_command_data[original_def_cmd].flags & CF_def_alias)
+  if (html_in_string (self))
     {
-      int i;
-      for (i = 0; def_aliases[i].alias ; i++)
-        {
-          if (def_aliases[i].alias == original_def_cmd)
-            {
-              original_cmd = def_aliases[i].command;
-              break;
-            }
-        }
-
-      xasprintf (&alias_class, "%s-alias-%s",
-                    builtin_command_name (original_def_cmd),
-                    builtin_command_name (original_cmd));
+      if (content)
+        text_append (result, content);
+      return;
     }
-  else
-    original_cmd = original_def_cmd;
 
-  /* parent is defblock, we do not put it in class */
-  if (element->e.c->cmd == CM_defline || element->e.c->cmd == CM_deftypeline)
-    def_cmd = element->e.c->cmd;
-  else
-  /* the parent is the def both for def* def_line and def*x */
-    def_cmd = element->parent->e.c->cmd;
+  classes = new_string_list ();
 
-  if (builtin_command_data[def_cmd].flags & CF_def_alias)
+  if (cmd != CM_defblock)
     {
-      int i;
-      for (i = 0; def_aliases[i].alias ; i++)
+      if (builtin_command_data[cmd].flags & CF_def_alias)
         {
-          if (def_aliases[i].alias == def_cmd)
+          int i;
+          for (i = 0; def_aliases[i].alias ; i++)
             {
-              base_cmd = def_aliases[i].command;
-              break;
+              if (def_aliases[i].alias == cmd)
+                {
+                  original_cmd = def_aliases[i].command;
+                  break;
+                }
             }
         }
-    }
-  else
-    base_cmd = def_cmd;
 
-  classes = new_string_list ();
-
-  add_string (builtin_command_name (original_cmd), classes);
-  if (alias_class)
-    {
-      add_string (alias_class, classes);
-      free (alias_class);
-    }
-  if (base_cmd != original_cmd)
-    {
-      char *class;
-      xasprintf (&class, "def-cmd-%s", builtin_command_name (base_cmd));
+      xasprintf (&class, "first-%s", builtin_command_name (original_cmd));
       add_string (class, classes);
       free (class);
-    }
-
-  add_string ("def-line", classes);
-
-  text_init (&def_call);
-  text_append (&def_call, "");
-  if (parsed_def->type)
-    {
-      char *type_text;
-      size_t type_text_len;
-      ELEMENT *root_code = new_element (ET__code);
-      char *explanation;
-
-      xasprintf (&explanation, "DEF_TYPE %s", builtin_command_name (def_cmd));
-
-      add_to_contents_as_array (root_code, parsed_def->type);
-
-      add_tree_to_build (self, root_code);
-
-      type_text = html_convert_tree (self, root_code, explanation);
-
-      remove_tree_to_build (self, root_code);
-
-      destroy_element (root_code);
-      free (explanation);
-
-      type_text_len = strlen (type_text);
 
-      if (type_text_len > 0)
-        {
-          char *attribute_class = html_attribute_class (self, "code",
-                                                        &def_type_classes);
-          text_append (&def_call, attribute_class);
-          free (attribute_class);
-          text_append_n (&def_call, ">", 1);
-          text_append_n (&def_call, type_text, type_text_len);
-          text_append_n (&def_call, "</code>", 7);
-        }
-      if ((base_cmd == CM_deftypefn || base_cmd == CM_deftypeop)
-          && self->conf->deftypefnnewline.o.string
-          && !strcmp (self->conf->deftypefnnewline.o.string, "on"))
+      if (cmd != original_cmd)
         {
-          text_append_n (&def_call, self->line_break_element.string,
-                                    self->line_break_element.len);
-          text_append_n (&def_call, " ", 1);
-        }
-      else if (type_text_len > 0)
-        text_append_n (&def_call, " ", 1);
-      free (type_text);
-    }
-
-  if (parsed_def->name)
-    {
-      char *attribute_class = html_attribute_class (self, "strong",
-                                                    &def_name_classes);
-      char *explanation;
-      xasprintf (&explanation, "DEF_NAME %s", builtin_command_name (def_cmd));
-
-      ELEMENT *root_code = new_element (ET__code);
-
-      add_to_contents_as_array (root_code, parsed_def->name);
-
-      add_tree_to_build (self, root_code);
-
-      text_append (&def_call, attribute_class);
-      free (attribute_class);
-      text_append_n (&def_call, ">", 1);
-
-      convert_to_html_internal (self, root_code, &def_call, explanation);
+          xasprintf (&class, "first-%s-alias-first-%s",
+                             builtin_command_name (cmd),
+                             builtin_command_name (original_cmd));
+          add_string (class, classes);
+          free (class);
+        }
+    }
+  else
+    add_string (builtin_command_name (cmd), classes);
 
-      remove_tree_to_build (self, root_code);
-      destroy_element (root_code);
+  add_string ("def-block", classes);
 
-      text_append_n (&def_call, "</strong>", 9);
-      free (explanation);
+  if (self->conf->DEF_TABLE.o.integer <= 0)
+    {
+      open_element_with_class (self, "dl", classes, result);
+      text_append_n (result, "\n", 1);
+      if (content)
+        text_append (result, content);
+      text_append_n (result, "</dl>\n", 6);
     }
-
-  if (parsed_def->args)
+  else
     {
-      char *args_formatted;
-      char *explanation;
-      xasprintf (&explanation, "DEF_ARGS %s", builtin_command_name (def_cmd));
-   /* arguments not only metasyntactic variables
-      (deftypefn, deftypevr, deftypeop, deftypecv) */
-      /* Texinfo::Common::def_no_var_arg_commands{$base_command_name} */
-      if (strlen (builtin_command_name (base_cmd)) >= 7
-          && !memcmp (builtin_command_name (base_cmd), "deftype", 7))
-        {
-          ELEMENT *root_code = new_element (ET__code);
+      open_element_with_class (self, "table", classes, result);
+      text_append_n (result, "\n", 1);
+      if (content)
+        text_append (result, content);
+      text_append_n (result, "</table>\n", 9);
+    }
 
-          add_to_contents_as_array (root_code, parsed_def->args);
+  destroy_strings_list (classes);
+}
 
-          add_tree_to_build (self, root_code);
 
-          args_formatted = html_convert_tree (self, root_code, explanation);
+void
+html_command_open_external (CONVERTER *self, const enum command_id cmd,
+                            const ELEMENT *element, TEXT *result)
+{
+  if (self->commands_open[cmd].status > 0)
+    call_commands_open (self, cmd, element, result);
+}
 
-          remove_tree_to_build (self, root_code);
-          destroy_element (root_code);
+void
+html_open_node_part_command (CONVERTER *self, const enum command_id cmd,
+                        const ELEMENT *element, TEXT *result)
+{
+  if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0)
+    {
+      const ELEMENT *node_element = 0;
+      int in_skipped_node_top
+        = self->shared_conversion_state.in_skipped_node_top;
 
-          if (args_formatted[strspn (args_formatted, whitespace_chars)] != 
'\0')
-            {
-              char *attribute_class = html_attribute_class (self, "code",
-                                              &def_code_arguments_classes);
-              int omit_def_name_space = (element->flags & 
EF_omit_def_name_space);
-              if (!omit_def_name_space)
-                text_append_n (&def_call, " ", 1);
-              text_append (&def_call, attribute_class);
-              free (attribute_class);
-              text_append_n (&def_call, ">", 1);
-              text_append (&def_call, args_formatted);
-              text_append_n (&def_call, "</code>", 7);
-            }
+      if (cmd == CM_node)
+        node_element = element;
+      else if (cmd == CM_part)
+        {
+          const ELEMENT *part_following_node
+            = lookup_extra_element (element, AI_key_part_following_node);
+          if (part_following_node)
+            node_element = part_following_node;
         }
-      else
+      if (node_element || cmd == CM_part)
         {
-          html_set_code_context (self, 0);
-          args_formatted = html_convert_tree (self, parsed_def->args,
-                                              explanation);
-          html_pop_code_context (self);
-          if (args_formatted[strspn (args_formatted, whitespace_chars)] != 
'\0')
+          int node_is_top = 0;
+          if (node_element)
             {
-              char *attribute_class = html_attribute_class (self, "var",
-                                              &def_var_arguments_classes);
-              int omit_def_name_space = (element->flags & 
EF_omit_def_name_space);
-              if (!omit_def_name_space)
-                text_append_n (&def_call, " ", 1);
-              text_append (&def_call, attribute_class);
-              free (attribute_class);
-              text_append_n (&def_call, ">", 1);
-              text_append (&def_call, args_formatted);
-              text_append_n (&def_call, "</var>", 6);
+              const char *normalized = lookup_extra_string (node_element,
+                                                        AI_key_normalized);
+              if (normalized && !strcmp (normalized, "Top"))
+                {
+                  node_is_top = 1;
+                  in_skipped_node_top = 1;
+                  self->shared_conversion_state.in_skipped_node_top
+                    = in_skipped_node_top;
+                }
+            }
+          if (!node_is_top && in_skipped_node_top == 1)
+            {
+              in_skipped_node_top = -1;
+              self->shared_conversion_state.in_skipped_node_top
+                = in_skipped_node_top;
             }
         }
-      free (explanation);
-      free (args_formatted);
     }
+}
 
-  if (self->conf->DEF_TABLE.o.integer > 0)
+void
+html_open_quotation_command (CONVERTER *self, const enum command_id cmd,
+                             const ELEMENT *element, TEXT *result)
+{
+  const char *cmdname = element_command_name (element);
+  char *formatted_quotation_arg_to_prepend = 0;
+  if (element->e.c->args.number > 0
+      && element->e.c->args.list[0]->e.c->contents.number > 0)
     {
-      ELEMENT *category_tree
-         = definition_category_tree (self->conf, element);
+      ELEMENT *tree;
+      char *explanation;
+      NAMED_STRING_ELEMENT_LIST *substrings
+                                       = new_named_string_element_list ();
+      ELEMENT *quotation_arg_copy = copy_tree (element->e.c->args.list[0]);
+      add_element_to_named_string_element_list (substrings,
+                          "quotation_arg", quotation_arg_copy);
+      tree = html_cdt_tree ("@b{{quotation_arg}:} ",
+                           self, substrings, 0);
+      destroy_named_string_element_list (substrings);
+      xasprintf (&explanation, "open %s prepended arg", cmdname);
+      add_tree_to_build (self, tree);
+      formatted_quotation_arg_to_prepend
+        = html_convert_tree (self, tree, explanation);
+      remove_tree_to_build (self, tree);
+      destroy_element_and_children (tree);
+      free (explanation);
+    }
+  html_register_pending_formatted_inline_content (self, cmdname,
+                                  formatted_quotation_arg_to_prepend);
+  free (formatted_quotation_arg_to_prepend);
+}
 
-      attribute_class = html_attribute_class (self, "tr", classes);
-      destroy_strings_list (classes);
-      text_append (result, attribute_class);
-      free (attribute_class);
-      if (index_id && strlen (index_id) && !html_in_multi_expanded (self))
-        text_printf (result, " id=\"%s\"", index_id);
-      text_append_n (result, ">", 1);
 
-      attribute_class = html_attribute_class (self, "td",
-                                               &call_def_classes);
-      text_append (result, attribute_class);
-      free (attribute_class);
-      text_append_n (result, ">", 1);
-      text_append_n (result, def_call.text, def_call.end);
-      free (def_call.text);
-      text_append_n (result, "</td>", 5);
+void
+html_type_conversion_external (CONVERTER *self, const enum element_type type,
+                               const ELEMENT *element, const char *content,
+                               TEXT *result)
+{
+  const FORMATTING_REFERENCE *formatting_reference
+    = self->current_types_conversion_function[type].formatting_reference;
+  /* NOTE it should always be true, as in the main loop a formatting
+     function is called only if type_conversion is set, which should not
+     be if formatting_reference status is 0 */
+  if (formatting_reference->status > 0)
+    call_types_conversion (self, type, formatting_reference,
+                           element, content, result);
+}
 
-      attribute_class = html_attribute_class (self, "td",
-                                              &category_def_classes);
-      text_append (result, attribute_class);
-      free (attribute_class);
-      text_append_n (result, ">[", 2);
+/* associate command to the C function implementing the opening */
+static const COMMAND_INTERNAL_OPEN commands_internal_open_table[] = {
+  {CM_node, &html_open_node_part_command},
+  {CM_part, &html_open_node_part_command},
+  {CM_quotation, &html_open_quotation_command},
+  {CM_smallquotation, &html_open_quotation_command},
+  {0, 0},
+};
 
-      if (category_tree)
-        {
-          add_tree_to_build (self, category_tree);
-          convert_to_html_internal (self, category_tree, result, 0);
-          remove_tree_to_build (self, category_tree);
-          destroy_element_and_children (category_tree);
-        }
-      text_append_n (result, "]</td></tr>\n", 12);
+void
+html_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;
 
-      destroy_parsed_def (parsed_def);
+  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;
     }
 
-  attribute_class = html_attribute_class (self, "dt", classes);
-  destroy_strings_list (classes);
-  text_append (result, attribute_class);
-  free (attribute_class);
-  if (index_id && strlen (index_id) && !html_in_multi_expanded (self))
-    text_printf (result, " id=\"%s\"", index_id);
-  text_append_n (result, ">", 1);
+  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 drop const 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);
+}
+
+#define ADDN(str,nr) text_append_n (result, str, nr)
+void
+html_css_string_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_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 (parsed_def->category)
+  if (html_in_code (self) || html_in_math (self))
     {
-      ELEMENT *category_tree = 0;
-      NAMED_STRING_ELEMENT_LIST *substrings
-                                   = new_named_string_element_list ();
-      ELEMENT *category_copy = copy_tree (parsed_def->category);
+      default_css_string_format_protect_text (content_used, result);
+      goto out;
+    }
 
-      add_element_to_named_string_element_list (substrings,
-                                            "category", category_copy);
-      if (parsed_def->class)
+  const char *p = content_used;
+  while (*p)
+    {
+      int before_sep_nr = strcspn (p, "\\-`'");
+      if (before_sep_nr)
         {
-          ELEMENT *class_copy = copy_tree (parsed_def->class);
-          add_element_to_named_string_element_list (substrings,
-                                            "class", class_copy);
-
-          if (base_cmd == CM_deftypeop && parsed_def->type
-              && self->conf->deftypefnnewline.o.string
-              && !strcmp (self->conf->deftypefnnewline.o.string, "on"))
+          text_append_n (result, p, before_sep_nr);
+          p += before_sep_nr;
+        }
+      if (!*p)
+        break;
+      switch (*p)
+        {
+        case '-':
+          if (*(p+1) && !memcmp (p, "---", 3))
             {
-               category_tree
-                  = html_cdt_tree ("{category} on @code{{class}}:@* ",
-                                   self, substrings, 0);
+              ADDN("\\2014 ",6);
+              p += 3;
             }
-          else if (base_cmd == CM_defop || base_cmd == CM_deftypeop)
+          else if (!memcmp (p, "--", 2))
             {
-               category_tree
-                  = html_cdt_tree ("{category} on @code{{class}}: ",
-                                   self, substrings, 0);
+              ADDN("\\2013 ",6);
+              p += 2;
             }
-          else if (base_cmd == CM_defcv || base_cmd == CM_deftypecv)
+          else
             {
-               category_tree
-                  = html_cdt_tree ("{category} of @code{{class}}: ",
-                                   self, substrings, 0);
+              text_append_n (result, "-", 1);
+              p++;
             }
-        }
-      else
-        {
-          if ((base_cmd == CM_deftypefn || base_cmd == CM_deftypeop)
-              && parsed_def->type
-              && self->conf->deftypefnnewline.o.string
-              && !strcmp (self->conf->deftypefnnewline.o.string, "on"))
+          break;
+        case '`':
+          if (!memcmp (p, "``", 2))
             {
-              category_tree
-                  = html_cdt_tree ("{category}:@* ",
-                                   self, substrings, 0);
+              ADDN("\\201C ",6);
+              p += 2;
             }
           else
             {
-              category_tree
-                  = html_cdt_tree ("{category}: ",
-                                   self, substrings, 0);
+              ADDN("\\2018 ",6);
+              p++;
             }
-        }
-      destroy_named_string_element_list (substrings);
-
-      if (category_tree)
-        {
-          char *attribute_open = html_attribute_class (self, "span",
-                                             &category_def_classes);
-          size_t open_len = strlen (attribute_open);
-          char *explanation;
-
-          xasprintf (&explanation, "DEF_CATEGORY %s",
-                     builtin_command_name (def_cmd));
-
-          if (open_len)
+          break;
+        case '\'':
+          if (!memcmp (p, "''", 2))
             {
-              text_append_n (result, attribute_open, open_len);
-              text_append_n (result, ">", 1);
+              ADDN("\\201D ",6);
+              p += 2;
             }
-          free (attribute_open);
-          add_tree_to_build (self, category_tree);
-          convert_to_html_internal (self, category_tree, result, explanation);
-          remove_tree_to_build (self, category_tree);
-          destroy_element_and_children (category_tree);
-          if (open_len)
-            text_append_n (result, "</span>", 7);
-          free (explanation);
+          else
+            {
+              ADDN("\\2019 ",6);
+              p++;
+            }
+          break;
+        case '\\':
+          ADDN("\\\\", 2);
+          p++;
+          break;
         }
     }
+ out:
+  if (contents_used_to_be_freed)
+    free (content_used);
+}
+#undef ADDN
 
-  destroy_parsed_def (parsed_def);
-
-  anchor = get_copiable_anchor (self, index_id);
+void
+html_convert_paragraph_type (CONVERTER *self, const enum element_type type,
+                             const ELEMENT *element, const char *content,
+                             TEXT *result)
+{
+  TEXT content_text;
+  enum command_id align_cmd;
+  char *associated_content
+   = html_get_associated_formatted_inline_content (self, element, 0);
 
-  if (anchor)
-    text_append_n (result, "<span>", 6);
+  text_init (&content_text);
 
-  text_append_n (result, def_call.text, def_call.end);
-  free (def_call.text);
-  if (anchor)
+  if (associated_content)
     {
-      text_append (result, anchor);
-      text_append_n (result, "</span>", 7);
+      text_append (&content_text, associated_content);
+      free (associated_content);
     }
+  if (content)
+    text_append (&content_text, content);
 
-  text_append_n (result, "</dt>\n", 6);
-
-  free (anchor);
-}
+  if (content_text.end <= 0)
+    {
+      free (content_text.text);
+      return;
+    }
 
-void
-convert_def_item_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  if (!content)
-    return;
+  if (html_paragraph_number (self) == 1)
+    {
+      enum command_id in_format_cmd = html_top_block_command (self);
+      if (in_format_cmd)
+        {
+          /* no first paragraph in those environment to avoid extra spacing */
+          if (in_format_cmd == CM_itemize
+              || in_format_cmd == CM_enumerate
+              || in_format_cmd == CM_multitable
+          /* this should only happen if in @nodedescriptionblock, otherwise
+             there are no paragraphs, but preformatted */
+              || in_format_cmd == CM_menu)
+            {
+              text_append (result, content_text.text);
+              free (content_text.text);
+              return;
+            }
+        }
+    }
 
   if (html_in_string (self))
-    text_append (result, content);
-
-  if (content[strspn (content, whitespace_chars)] == '\0')
-    return;
-
-  if (self->conf->DEF_TABLE.o.integer <= 0)
     {
-      text_append_n (result, "<dd>", 4);
-      text_append (result, content);
-      text_append_n (result, "</dd>", 5);
+      text_append (result, content_text.text);
+      free (content_text.text);
+      return;
     }
-  else
+
+  if (content_text.text[strspn (content_text.text, whitespace_chars)] == '\0')
     {
-      text_append_n (result, "<tr><td colspan=\"2\">", 20);
-      text_append (result, content);
-      text_append_n (result, "</td></tr>", 10);
+      free (content_text.text);
+      return;
     }
-}
-
-void
-convert_table_definition_type (CONVERTER *self, const enum element_type type,
-                       const ELEMENT *element, const char *content,
-                       TEXT *result)
-{
-  if (!content)
-    return;
 
-  if (html_in_string (self))
-    text_append (result, content);
+  align_cmd = html_in_align (self);
 
-  if (content[strspn (content, whitespace_chars)] == '\0')
-    return;
+  if (align_cmd)
+    {
+      char *attribute_class;
+      char *class;
+      STRING_LIST *classes = new_string_list ();
 
-  text_append_n (result, "<dd>", 4);
-  text_append (result, content);
-  text_append_n (result, "</dd>\n", 6);
-}
+      xasprintf (&class, "%s-paragraph", builtin_command_name (align_cmd));
+      add_string (class, classes);
+      free (class);
 
-/* associate type to the C function implementing the conversion */
-static const TYPE_INTERNAL_CONVERSION types_internal_conversion_table[] = {
-  {ET_balanced_braces, &convert_balanced_braces_type},
-  {ET_before_item, &convert_before_item_type},
-  {ET_def_item, &convert_def_item_type},
-  {ET_inter_def_item, &convert_def_item_type},
-  {ET_before_defline, &convert_def_item_type},
-  {ET_def_line, &convert_def_line_type},
-  {ET_definfoenclose_command, &convert_definfoenclose_type},
-  {ET_index_entry_command, &convert_index_entry_command_type},
-  {ET_table_definition, &convert_table_definition_type},
-  {ET_inter_item, &convert_table_definition_type},
-  {ET_menu_comment, &convert_menu_comment_type},
-  {ET_menu_entry, &convert_menu_entry_type},
-  {ET_multitable_body, &convert_multitable_body_type},
-  {ET_multitable_head, &convert_multitable_head_type},
-  {ET_paragraph, &convert_paragraph_type},
-  {ET_preformatted, &convert_preformatted_type},
-  {ET_row, &convert_row_type},
-  {ET_table_term, &convert_table_term_type},
-  {ET_text, &convert_text},
-  {ET_untranslated_def_line_arg,
-   &convert_untranslated_def_line_arg_type},
-  {0, 0},
-};
+      attribute_class = html_attribute_class (self, "p", classes);
+      text_append (result, attribute_class);
+      text_append_n (result, ">", 1);
+      free (attribute_class);
+      destroy_strings_list (classes);
+    }
+  else
+    text_append_n (result, "<p>", 3);
 
-void
-convert_unit_type (CONVERTER *self, const enum output_unit_type unit_type,
-                   const OUTPUT_UNIT *output_unit, const char *content,
-                   TEXT *result)
-{
-  STRING_LIST *closed_strings;
-  const ELEMENT *unit_command;
+  text_append (result, content_text.text);
+  free (content_text.text);
 
-  if (html_in_string (self))
-    return;
+  text_append_n (result, "</p>", 4);
+}
 
-  if (!output_unit->tree_unit_directions[D_prev])
+static char *
+preformatted_class (const CONVERTER *self)
+{
+  const COMMAND_OR_TYPE *cur_pre_class = 0;
+  const COMMAND_OR_TYPE_STACK *pre_classes
+        = html_preformatted_classes_stack (self);
+  size_t i;
+  for (i = 0; i < pre_classes->top; i++)
+    {
+      const COMMAND_OR_TYPE *cmd_or_type = &pre_classes->stack[i];
+      if (!(cur_pre_class
+            && (cur_pre_class->variety == CTV_type_command
+                && builtin_command_data[cur_pre_class->ct.cmd].flags
+                                   & CF_preformatted_code)
+            && (!((cmd_or_type->variety == CTV_type_command
+                   && builtin_command_data[cmd_or_type->ct.cmd].flags
+                                     & CF_preformatted_code)
+                  || cmd_or_type->ct.cmd == CM_menu))))
+         cur_pre_class = cmd_or_type;
+    }
+  if (cur_pre_class)
     {
-      text_append (result, self->title_titlepage);
-      if (!output_unit->tree_unit_directions[D_next])
+      char *pre_class = 0;
+      if (cur_pre_class->variety == CTV_type_command)
         {
-          /* only one unit, use simplified formatting */
-          if (content)
-            text_append (result, content);
-   /*  if there is one unit it also means that there is no formatting
-       of footnotes in a separate unit.  And if footnotestyle is end
-       the footnotes won't be done in format_element_footer either. */
-          format_footnotes_segment (self, result);
-          if (self->conf->DEFAULT_RULE.o.string
-              && self->conf->PROGRAM_NAME_IN_FOOTER.o.integer > 0)
-            {
-              text_append (result, self->conf->DEFAULT_RULE.o.string);
-              text_append_n (result, "\n", 1);
-            }
-
-    /* do it here, as it is won't be done at end of page in
-       format_element_footer */
-          closed_strings = html_close_registered_sections_level (self,
-                                   self->current_filename.file_number, 0);
+          xasprintf (&pre_class, "%s-preformatted",
+                     builtin_command_name (cur_pre_class->ct.cmd));
+        }
+      else if (cur_pre_class->variety == CTV_type_type)
 
-          if (closed_strings->number)
-            {
-              size_t i;
-              for (i = 0; i < closed_strings->number; i++)
-                {
-                  text_append (result, closed_strings->list[i]);
-                  free (closed_strings->list[i]);
-                }
-              free (closed_strings->list);
-            }
-          free (closed_strings);
-          return;
+        {
+          xasprintf (&pre_class, "%s-preformatted",
+                     self->pre_class_types[cur_pre_class->ct.type]);
         }
+      if (pre_class)
+        return pre_class;
     }
-
-  if (content)
-    text_append (result, content);
-
-  unit_command = output_unit->uc.unit_command;
-
-  format_element_footer (self, unit_type, output_unit, content, unit_command,
-                         result);
+  /* should not happen */
+  return 0;
 }
 
 void
-convert_special_unit_type (CONVERTER *self,
-                        const enum output_unit_type unit_type,
-                        const OUTPUT_UNIT *output_unit, const char *content,
-                        TEXT *result)
+html_convert_preformatted_type (CONVERTER *self, const enum element_type type,
+                                const ELEMENT *element, const char *content,
+                                TEXT *result)
 {
-  char *heading;
-  size_t number;
-  TEXT special_unit_body;
-  const ELEMENT *unit_command;
-  const char *id;
-  const char *class_base;
+  TEXT content_text;
+  char *trimmed_content;
+  enum command_id in_format_cmd;
+  char *pre_class = 0;
+  int in_def = 0;
   char *attribute_class;
-  char *class;
   STRING_LIST *classes;
 
-  const char *special_unit_variety;
-  STRING_LIST *closed_strings;
-  size_t count_in_file = 0;
-  int level;
+  char *associated_content
+   = html_get_associated_formatted_inline_content (self, element, 0);
 
-  if (html_in_string (self))
-    return;
+  text_init (&content_text);
 
-  special_unit_variety = output_unit->special_unit_variety;
-  number = find_string (&self->special_unit_varieties,
-                        special_unit_variety);
+  if (associated_content)
+    {
+      text_append (&content_text, associated_content);
+      free (associated_content);
+    }
+  if (content)
+    text_append (&content_text, content);
 
-  closed_strings = html_close_registered_sections_level (self,
-                                self->current_filename.file_number, 0);
+  if (content_text.end <= 0)
+    {
+      free (content_text.text);
+      return;
+    }
 
-  if (closed_strings->number)
+  in_format_cmd = html_top_block_command (self);
+  if (in_format_cmd == CM_multitable)
     {
-      size_t i;
-      for (i = 0; i < closed_strings->number; i++)
+      const char *p = content_text.text;
+      p += strspn (p, whitespace_chars);
+      trimmed_content = trim_trailing_content (p);
+      free (content_text.text);
+    }
+  else
+    trimmed_content = content_text.text;
+
+  if (html_in_string (self))
+    {
+      text_append (result, trimmed_content);
+      free (trimmed_content);
+      return;
+    }
+
+  /* menu_entry_description is always in a preformatted container
+     in the tree, as the whole menu is meant to be an
+     environment where spaces and newlines are preserved. */
+  if (element->parent && element->parent->type == ET_menu_entry_description)
+    {
+      if (!html_inside_preformatted (self))
         {
-          text_append (result, closed_strings->list[i]);
-          free (closed_strings->list[i]);
+ /* If not in preformatted block command,
+    we don't preserve spaces and newlines in menu_entry_description,
+    instead the whole menu_entry is in a table, so no <pre> in that situation
+  */
+          text_append (result, trimmed_content);
+          free (trimmed_content);
+          return;
+        }
+      else
+        {
+     /* if directly in description, we want to avoid the linebreak that
+        comes with pre being a block level element, so set a special class */
+          pre_class = strdup ("menu-entry-description-preformatted");
         }
-      free (closed_strings->list);
     }
-  free (closed_strings);
 
-  text_init (&special_unit_body);
-  text_append (&special_unit_body, "");
-
-  (*self->special_unit_body_formatting[number -1].special_unit_body_formatting)
-         (self, number, special_unit_variety, output_unit, &special_unit_body);
+  if (!pre_class)
+    pre_class = preformatted_class (self);
 
-  /* This may happen with footnotes in regions that are not expanded,
-     like @copying or @titlepage */
-  if (special_unit_body.end == 0)
+  /* this may happen with lines without textual content
+     between a def* and def*x. */
+  if (element->parent)
     {
-      free (special_unit_body.text);
-      return;
+      enum command_id p_cmd = element_builtin_cmd (element->parent);
+      if (builtin_command_data[p_cmd].flags & CF_def || p_cmd == CM_defblock)
+        {
+          in_def = 1;
+          text_append_n (result, "<dd>", 4);
+        }
     }
 
   classes = new_string_list ();
+  if (pre_class)
+    {
+      add_string (pre_class, classes);
+      free (pre_class);
+    }
+  attribute_class = html_attribute_class (self, "pre", classes);
+  text_append (result, attribute_class);
+  text_append_n (result, ">", 1);
+  free (attribute_class);
+  destroy_strings_list (classes);
 
-  unit_command = output_unit->uc.special_unit_command;
-  id = html_command_id (self, unit_command);
-  class_base = special_unit_info (self, SUI_type_class,
-                                  special_unit_variety);
-  xasprintf (&class, "element-%s", class_base);
+  /* a newline immediately after a <pre> is ignored. */
+  if (trimmed_content[0] == '\n')
+    text_append_n (result, "\n", 1);
+  text_append (result, trimmed_content);
+  free (trimmed_content);
 
-  add_string (class, classes);
-  free (class);
-  attribute_class = html_attribute_class (self, "div", classes);
-  clear_strings_list (classes);
+  text_append_n (result, "</pre>", 6);
 
-  text_append (result, attribute_class);
-  free (attribute_class);
+  if (in_def)
+    text_append_n (result, "</dd>", 5);
+}
 
-  if (id && strlen (id))
-    text_printf (result, " id=\"%s\"", id);
-  text_append (result, ">\n");
+void
+html_convert_balanced_braces_type (CONVERTER *self,
+                                   const enum element_type type,
+                                   const ELEMENT *element, const char *content,
+                                   TEXT *result)
+{
+  if (content)
+    text_append (result, content);
+}
 
-  if (output_unit->unit_filename)
-    {
-      size_t file_index = self->special_unit_file_indices[output_unit->index];
-      count_in_file
-        = count_elements_in_file_number (self, CEFT_current, file_index +1);
-    }
+void
+html_convert_index_entry_command_type (CONVERTER *self,
+                                       const enum element_type type,
+                                       const ELEMENT *element,
+                                       const char *content,
+                                       TEXT *result)
+{
+  const char *index_id;
 
-  if (self->conf->HEADERS.o.integer > 0
-      /* first in page */
-      || count_in_file == 1)
+  if (html_in_string (self) || html_in_multi_expanded (self))
+    return;
+
+  index_id = html_command_id (self, element);
+
+  if (index_id && strlen (index_id))
     {
-      format_navigation_header (self, self->conf->MISC_BUTTONS.o.buttons, 0,
-                                unit_command, result);
+      format_separate_anchor (self, index_id, "index-entry-id", result);
+      if (!html_in_preformatted_context (self))
+        text_append_n (result, "\n", 1);
     }
+}
 
-  heading = html_command_text (self, unit_command, 0);
-  level = self->conf->CHAPTER_HEADER_LEVEL.o.integer;
-  if (!strcmp (special_unit_variety, "footnotes"))
-    level = self->conf->FOOTNOTE_SEPARATE_HEADER_LEVEL.o.integer;
-
-  xasprintf (&class, "%s-heading", class_base);
+void
+html_convert_definfoenclose_type (CONVERTER *self, const enum element_type 
type,
+                                  const ELEMENT *element, const char *content,
+                                  TEXT *result)
+{
+  const char *begin = lookup_extra_string (element, AI_key_begin);
+  const char *end = lookup_extra_string (element, AI_key_end);
 
-  add_string (class, classes);
-  free (class);
+  if (begin)
+    format_protect_text (self, begin, result);
+  if (content)
+    text_append (result, content);
+  if (end)
+    format_protect_text (self, end, result);
+}
 
-  format_heading_text (self, 0, classes, heading, level, 0, 0, 0, result);
-  free (heading);
-  destroy_strings_list (classes);
-  text_append_n (result, "\n", 1);
+void
+html_convert_untranslated_def_line_arg_type
+                         (CONVERTER *self, const enum element_type type,
+                          const ELEMENT *element, const char *content,
+                          TEXT *result)
+{
+  const char *category_text = element->e.c->contents.list[0]->e.text->text;
+  const char *translation_context
+    = lookup_extra_string (element, AI_key_translation_context);
+  ELEMENT *translated = html_cdt_tree (category_text,
+                                       self, 0, translation_context);
 
-  text_append (result, special_unit_body.text);
-  free (special_unit_body.text);
-  text_append (result, "</div>");
+  add_tree_to_build (self, translated);
 
-  format_element_footer (self, unit_type, output_unit, content, unit_command,
-                         result);
-}
+  convert_to_html_internal (self, translated, result,
+                            "translated TEXT");
 
-static const OUTPUT_UNIT_INTERNAL_CONVERSION 
output_units_internal_conversion_table[] = {
-  {OU_special_unit, &convert_special_unit_type},
-  {OU_unit, &convert_unit_type},
-  {0, 0},
-};
+  remove_tree_to_build (self, translated);
+  destroy_element_and_children (translated);
+}
 
 void
-contents_shortcontents_in_title (CONVERTER *self, TEXT *result)
+html_convert_row_type (CONVERTER *self, const enum element_type type,
+                       const ELEMENT *element, const char *content,
+                       TEXT *result)
 {
-  if (self->document->sections_list
-      && self->document->sections_list->number > 0
-      && self->conf->CONTENTS_OUTPUT_LOCATION.o.string
-      && !strcmp (self->conf->CONTENTS_OUTPUT_LOCATION.o.string, 
"after_title"))
+  if (html_in_string (self))
     {
-      enum command_id contents_cmds[2] = {CM_shortcontents, CM_contents};
-      int i;
-      for (i = 0; i < 2; i++)
+      if (content)
+        text_append (result, content);
+    }
+
+  if (!content || content[strspn (content, whitespace_chars)] == '\0')
+    return;
+  else
+    {
+      text_append_n (result, "<tr>", 4);
+      text_append (result, content);
+      text_append_n (result, "</tr>", 5);
+
+      if (element->e.c->contents.number > 0)
         {
-          int contents_set = 0;
-          enum command_id cmd = contents_cmds[i];
-          const OPTION *contents_option_ref
-                           = get_command_option (self->conf, cmd);
-          if (contents_option_ref->o.integer > 0)
-            contents_set = 1;
-          if (contents_set)
-            {
-              char *contents_text
-                = contents_inline_element (self, cmd, 0);
-              if (contents_text)
-                {
-                  text_append (result, contents_text);
-                  if (self->conf->DEFAULT_RULE.o.string)
-                    {
-                      text_append (result, self->conf->DEFAULT_RULE.o.string);
-                      text_append_n (result, "\n", 1);
-                    }
-                  free (contents_text);
-                }
-            }
+          enum command_id first_cmd
+            = element_builtin_cmd (element->e.c->contents.list[0]);
+          if (first_cmd != CM_headitem)
+      /* if headitem, end of line added in _convert_multitable_head_type */
+            text_append (result, "\n");
         }
     }
 }
 
-static void
-format_simpletitle (CONVERTER *self, TEXT *result)
+void
+html_convert_multitable_head_type (CONVERTER *self,
+                                   const enum element_type type,
+                                   const ELEMENT *element, const char *content,
+                                   TEXT *result)
 {
-  char *title_text;
-  char *context_str;
-  enum command_id cmd = self->simpletitle_cmd;
-
-  STRING_LIST *classes = new_string_list ();
-  add_string (builtin_command_name (cmd), classes);
+  if (html_in_string (self))
+    {
+      if (content)
+        text_append (result, content);
+    }
 
-  xasprintf (&context_str, "%s simpletitle",
-             builtin_command_name (cmd));
-  title_text
-    = convert_tree_new_formatting_context (self,
-        self->simpletitle_tree, context_str, 0, 0, 0);
-  free (context_str);
-  format_heading_text (self, cmd, classes, title_text,
-                                    0, 0, 0, 0, result);
-  destroy_strings_list (classes);
-  free (title_text);
+  if (!content || content[strspn (content, whitespace_chars)] == '\0')
+    return;
+  else
+    {
+      text_append_n (result, "<thead>", 7);
+      text_append (result, content);
+      text_append_n (result, "</thead>\n", 9);
+    }
 }
 
-/* Convert @titlepage.  Falls back to simpletitle. */
-char *
-html_default_format_titlepage (CONVERTER *self)
+void
+html_convert_multitable_body_type (CONVERTER *self,
+                                   const enum element_type type,
+                                   const ELEMENT *element, const char *content,
+                                   TEXT *result)
 {
-  int titlepage_text = 0;
-  TEXT result;
-  text_init (&result);
-  text_append (&result, "");
-  if (self->document->global_commands.titlepage)
-    {
-      ELEMENT *tmp = new_element (ET_NONE);
-      tmp->e.c->contents = 
self->document->global_commands.titlepage->e.c->contents;
-      convert_to_html_internal (self, tmp, &result, "convert titlepage");
-      tmp->e.c->contents.list = 0;
-      destroy_element (tmp);
-      titlepage_text = 1;
-    }
-  else if (self->simpletitle_tree)
+  if (html_in_string (self))
     {
-      format_simpletitle (self, &result);
-      titlepage_text = 1;
+      if (content)
+        text_append (result, content);
     }
-  if (titlepage_text && self->conf->DEFAULT_RULE.o.string)
+
+  if (!content || content[strspn (content, whitespace_chars)] == '\0')
+    return;
+  else
     {
-      text_append (&result, self->conf->DEFAULT_RULE.o.string);
-      text_append_n (&result, "\n", 1);
+      text_append_n (result, "<tbody>", 7);
+      text_append (result, content);
+      text_append_n (result, "</tbody>\n", 9);
     }
-  contents_shortcontents_in_title (self, &result);
-  return result.text;
 }
 
-char *
-format_titlepage (CONVERTER *self)
+static char *menu_entry_destination_array[] = {"menu-entry-destination"};
+static const STRING_LIST menu_entry_destination_classes
+  = {menu_entry_destination_array, 1, 1};
+static char *menu_entry_description_array[] = {"menu-entry-description"};
+static const STRING_LIST menu_entry_description_classes
+  = {menu_entry_description_array, 1, 1};
+
+static void
+menu_entry_a (const CONVERTER *self, const char *href, int isindex,
+              int html_menu_entry_index, TEXT *result)
 {
-  const FORMATTING_REFERENCE *formatting_reference
-   = &self->current_formatting_references[FR_format_titlepage];
-  if (formatting_reference->status == FRS_status_default_set
-      || formatting_reference->status == FRS_status_none)
+  text_printf (result, "<a href=\"%s\"", href);
+  if (isindex)
+    text_append_n (result, " rel=\"index\"", 12);
+  if (self->conf->USE_ACCESSKEY.o.integer > 0 && html_menu_entry_index < 10)
+    text_printf (result, " accesskey=\"%d\"", html_menu_entry_index);
+  text_append_n (result, ">", 1);
+}
+
+void
+html_convert_menu_entry_type (CONVERTER *self, const enum element_type type,
+                              const ELEMENT *element, const char *content,
+                              TEXT *result)
+{
+  const ELEMENT *name_entry = 0;
+  const ELEMENT *menu_description = 0;
+  ELEMENT *menu_entry_node = 0;
+  const ELEMENT *menu_entry_leading_text = 0;
+  const ELEMENT *menu_entry_separators[2];
+  const ELEMENT *manual_content;
+  const ELEMENT *node_description = 0;
+  const ELEMENT *section = 0;
+  size_t i;
+  int entry_separators_nr = 0;
+  int entry_separators_idx = 0;
+  char *href = 0;
+  int isindex = 0;
+  int formatted_nodedescription_nr = 0;
+  int in_string = html_in_string (self);
+  int html_menu_entry_index;
+
+  memset (menu_entry_separators, 0, 2 * sizeof (const ELEMENT *));
+
+  for (i = 0; i < element->e.c->contents.number; i++)
     {
-      return html_default_format_titlepage (self);
+      ELEMENT *arg = element->e.c->contents.list[i];
+      if (arg->type == ET_menu_entry_leading_text)
+        menu_entry_leading_text = arg;
+      else if (arg->type == ET_menu_entry_name)
+        name_entry = arg;
+      else if (arg->type == ET_menu_entry_description)
+        menu_description = arg;
+      else if (arg->type == ET_menu_entry_separator)
+        {
+          menu_entry_separators[entry_separators_nr] = arg;
+          entry_separators_nr++;
+        }
+      else if (arg->type == ET_menu_entry_node)
+        menu_entry_node = arg;
     }
+
+  manual_content = lookup_extra_container (menu_entry_node,
+                                         AI_key_manual_content);
+
+  if (manual_content)
+    href = html_command_href (self, menu_entry_node, 0, element, 0);
   else
     {
-      return call_formatting_function_format_titlepage (self,
-                                               formatting_reference);
+  /* may not be defined in case of menu entry node consisting only of spaces */
+      const char *normalized = lookup_extra_string (menu_entry_node,
+                                                    AI_key_normalized);
+      if (normalized)
+        {
+          const ELEMENT *node
+           = find_identifier_target (&self->document->identifiers_target,
+                                     normalized);
+          if (node)
+            {
+              node_description
+                 = lookup_extra_element (node, AI_key_node_description);
+
+   /* if !NODE_NAME_IN_MENU, we pick the associated section, except if
+      the node is the element command */
+              if (self->conf->NODE_NAME_IN_MENU.o.integer <= 0)
+                {
+                  const ELEMENT *associated_section = lookup_extra_element 
(node,
+                                                       
AI_key_associated_section);
+                  if (associated_section)
+                    {
+                      const ELEMENT *associated_command
+                       = html_command_root_element_command (self, node);
+                      if (associated_command != node)
+                        {
+                          section = associated_section;
+                          href = html_command_href (self, section,
+                                                    0, element, 0);
+                        }
+                    }
+                }
+              if (!href)
+                href = html_command_href (self, node, 0, element, 0);
+
+          /* will mark the target as an index with rel index.  See
+  http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions
+           */
+              isindex = (node->flags & EF_isindex);
+
+              if (node_description
+                  /* not menu_description probably cannot happen */
+                  && (!menu_description
+                         /* empty description */
+           || (menu_description->e.c->contents.number <= 0
+               || (menu_description->e.c->contents.number == 1
+                   && 
(menu_description->e.c->contents.list[0]->e.c->contents.number <= 0
+                       || 
(menu_description->e.c->contents.list[0]->e.c->contents.number == 1
+                           && 
menu_description->e.c->contents.list[0]->e.c->contents.list[0]->type == 
ET_normal_text
+   && 
menu_description->e.c->contents.list[0]->e.c->contents.list[0]->e.text->text[
+       strspn 
(menu_description->e.c->contents.list[0]->e.c->contents.list[0]->e.text->text,
+             whitespace_chars)] == '\0'))))))
+                {
+        /* update the number of time the node description was formatted */
+                  HTML_TARGET *target_info
+                     = find_element_target (self->html_targets, node);
+                  target_info->formatted_nodedescription_nr++;
+                  formatted_nodedescription_nr
+                    = target_info->formatted_nodedescription_nr;
+                }
+            }
+        }
     }
-}
+  self->shared_conversion_state.html_menu_entry_index++;
+  html_menu_entry_index = self->shared_conversion_state.html_menu_entry_index;
 
-char *
-html_default_format_title_titlepage (CONVERTER *self)
-{
-  if (self->conf->SHOW_TITLE.o.integer > 0)
+  if (html_inside_preformatted (self) || in_string)
     {
-      if (self->conf->USE_TITLEPAGE_FOR_TITLE.o.integer)
+      const char *leading_text = menu_entry_leading_text->e.text->text;
+      const char *menu_symbol;
+      if (!in_string)
         {
-          return format_titlepage (self);
+          char *pre_class = preformatted_class (self);
+          char *attribute_class;
+          STRING_LIST *classes = new_string_list ();
+          if (pre_class)
+            {
+              add_string (pre_class, classes);
+              free (pre_class);
+            }
+          attribute_class = html_attribute_class (self, "pre", classes);
+          text_append (result, attribute_class);
+          text_append_n (result, ">", 1);
+          free (attribute_class);
+          destroy_strings_list (classes);
         }
-      else
-        {
-          TEXT result;
-          text_init (&result);
-          text_append (&result, "");
 
-          if (self->simpletitle_tree)
-            format_simpletitle (self, &result);
+      /* add leading text, replacing "*" by MENU_SYMBOL */
+      menu_symbol = strchr (leading_text, '*');
+      if (menu_symbol - leading_text > 0)
+        {
+          text_append_n (result, leading_text, menu_symbol - leading_text);
+          leading_text = menu_symbol;
+        }
+      if (self->conf->MENU_SYMBOL.o.string)
+        text_append (result, self->conf->MENU_SYMBOL.o.string);
+      /* past "*" */
+      leading_text++;
+      text_append (result, leading_text);
 
-          contents_shortcontents_in_title (self, &result);
-          return result.text;
+      if (name_entry)
+        {
+          convert_to_html_internal (self, name_entry, result,
+                                  "menu_arg menu_entry_name preformatted");
+          convert_to_html_internal (self,
+                     menu_entry_separators[entry_separators_idx],
+                     result, "menu_arg name separator preformatted");
+          entry_separators_idx++;
         }
-    }
-  return strdup ("");
-}
 
-char *
-format_title_titlepage (CONVERTER *self)
-{
-  const FORMATTING_REFERENCE *formatting_reference
-   = &self->current_formatting_references[FR_format_title_titlepage];
-  if (formatting_reference->status == FRS_status_default_set
-      || formatting_reference->status == FRS_status_none)
-    {
-      return html_default_format_title_titlepage (self);
-    }
-  else
-    {
-      return call_formatting_function_format_title_titlepage (self,
-                                                      formatting_reference);
-    }
-}
+      if (menu_entry_node)
+        {
+          ELEMENT *root_code = new_element (ET__code);
+          if (!in_string && href)
+            {
+              menu_entry_a (self, href, isindex, html_menu_entry_index,
+                            result);
+            }
 
-void
-default_format_special_body_contents (CONVERTER *self,
-                               const size_t special_unit_number,
-                               const char *special_unit_variety,
-                               const OUTPUT_UNIT *output_unit,
-                               TEXT *result)
-{
-  char *table_of_contents = format_contents (self, CM_contents, 0, 0);
-  text_append (result, table_of_contents);
-  free (table_of_contents);
-}
+          add_to_contents_as_array (root_code, menu_entry_node);
 
-void
-default_format_special_body_shortcontents (CONVERTER *self,
-                               const size_t special_unit_number,
-                               const char *special_unit_variety,
-                               const OUTPUT_UNIT *output_unit,
-                               TEXT *result)
-{
-  char *shortcontents = format_contents (self, CM_shortcontents, 0, 0);
-  text_append (result, shortcontents);
-  free (shortcontents);
-}
+          add_tree_to_build (self, root_code);
 
-void
-default_format_special_body_footnotes (CONVERTER *self,
-                               const size_t special_unit_number,
-                               const char *special_unit_variety,
-                               const OUTPUT_UNIT *output_unit,
-                               TEXT *result)
-{
-  format_footnotes_sequence (self, result);
-}
+          convert_to_html_internal (self, root_code, result,
+                               "menu_arg menu_entry_node preformatted");
 
-static char *direction_about_array[] = {"direction-about"};
-static const STRING_LIST direction_about_classes
-    = {direction_about_array, 1, 1};
+          remove_tree_to_build (self, root_code);
 
-static char *button_direction_about_array[] = {"button-direction-about"};
-static const STRING_LIST button_direction_about_classes
-    = {button_direction_about_array, 1, 1};
+          destroy_element (root_code);
 
-static char *name_direction_about_array[] = {"name-direction-about"};
-static const STRING_LIST name_direction_about_classes
-    = {name_direction_about_array, 1, 1};
+          if (!in_string && href)
+            text_append_n (result, "</a>", 4);
+        }
 
-static char *description_direction_about_array[]
-    = {"description-direction-about"};
-static const STRING_LIST description_direction_about_classes
-    = {description_direction_about_array, 1, 1};
+      if (entry_separators_idx < entry_separators_nr)
+        {
+          convert_to_html_internal (self,
+                     menu_entry_separators[entry_separators_idx],
+                     result, "menu_arg node separator preformatted");
+          entry_separators_idx++;
+        }
 
-static char *example_direction_about_array[] = {"example-direction-about"};
-static const STRING_LIST example_direction_about_classes
-    = {example_direction_about_array, 1, 1};
+      if (!in_string)
+        text_append_n (result, "</pre>", 6);
 
-void
-default_format_special_body_about (CONVERTER *self,
-                               const size_t special_unit_number,
-                               const char *special_unit_variety,
-                               const OUTPUT_UNIT *output_unit,
-                               TEXT *result)
-{
-  size_t i;
-  const BUTTON_SPECIFICATION_LIST *buttons
-           = self->conf->SECTION_BUTTONS.o.buttons;
+      if (formatted_nodedescription_nr > 0)
+        {
+          char *multiple_formatted = 0;
+          char *description;
+          ELEMENT *description_element;
+          if (formatted_nodedescription_nr > 1)
+            {
+              xasprintf (&multiple_formatted,
+                         "preformatted-node-description-%d",
+                         formatted_nodedescription_nr);
+            }
 
-  if (self->conf->PROGRAM_NAME_IN_ABOUT.o.integer > 0)
-    {
-      text_append_n (result, "<p>\n  ", 6);
-      format_program_string (self, result);
-      text_append_n (result, "\n</p>\n", 6);
+          if (node_description->e.c->cmd == CM_nodedescription)
+            description_element = node_description->e.c->args.list[0];
+          else
+            {
+              description_element = new_element (ET_NONE);
+              description_element->e.c->contents = 
node_description->e.c->contents;
+              add_tree_to_build (self, description_element);
+            }
+
+          description
+            = convert_tree_new_formatting_context (self, description_element,
+                 "menu_arg node description preformatted", multiple_formatted,
+                 0, CM_menu);
+
+          if (description)
+            {
+              text_append (result, description);
+              free (description);
+            }
+
+          if (formatted_nodedescription_nr > 1)
+            free (multiple_formatted);
+          if (node_description->e.c->cmd != CM_nodedescription)
+            {
+              remove_tree_to_build (self, description_element);
+              description_element->e.c->contents.list = 0;
+              destroy_element (description_element);
+            }
+        }
+      else if (menu_description)
+        {
+          convert_to_html_internal (self, menu_description, result,
+                                    "menu_arg description preformatted");
+        }
     }
+  else
+    {
+      char *description = 0;
+      char *name_no_number = 0;
+      text_append_n (result, "<tr>", 4);
+      open_element_with_class (self, "td",
+                               &menu_entry_destination_classes, result);
+
+      if (section && href)
+        {
+          char *name = html_command_text (self, section, 0);
+          if (name && strlen (name))
+            {
+              name_no_number = html_command_text (self, section,
+                                                  HTT_text_nonumber);
+              menu_entry_a (self, href, isindex, html_menu_entry_index,
+                            result);
+              text_append (result, name);
+              text_append_n (result, "</a>", 4);
+            }
+          free (name);
+        }
+      if (!name_no_number)
+        {
+          char *name = 0;
+          if (name_entry)
+            {
+              name = html_convert_tree (self, name_entry,
+                                        "convert menu_entry_name");
+              if (name)
+                {
+                  if (!strlen (name))
+                    {
+                      free (name);
+                      name = 0;
+                    }
+                }
+            }
+          if (!name)
+            {
+              const ELEMENT *manual_content
+                           = lookup_extra_container (menu_entry_node,
+                                                   AI_key_manual_content);
+              ELEMENT *node_content
+                         = lookup_extra_container (menu_entry_node,
+                                                 AI_key_node_content);
+              if (manual_content)
+                {
+                  name = html_command_text (self, menu_entry_node, 0);
+                }
+              else if (node_content)
+                {
+                  ELEMENT *root_code = new_element (ET__code);
+
+                  add_to_contents_as_array (root_code, node_content);
+
+                  add_tree_to_build (self, root_code);
 
-  text_append_n (result, "<p>\n", 4);
+                  name = html_convert_tree (self, root_code,
+                                            "menu_arg name");
 
-  if (!buttons)
-    {
-      translate_convert_to_html_internal (
-               "There are no buttons for this document.", self, 0, 0,
-                result, "ABOUT");
-      text_append_n (result, "\n</p>\n", 6);
-      return;
-    }
+                  remove_tree_to_build (self, root_code);
 
-  translate_convert_to_html_internal (
-   "  The buttons in the navigation panels have the following meaning:",
-                                      self, 0, 0, result, "ABOUT");
+                  destroy_element (root_code);
+                }
+            }
 
-  text_append_n (result, "\n</p>\n", 6);
-  open_element_with_class (self, "table", &direction_about_classes, result);
-  text_append (result, "\n  <tr>\n    ");
-  open_element_with_class (self, "th", &button_direction_about_classes,
-                           result);
-  text_append_n (result, " ", 1);
-  translate_convert_to_html_internal ("Button", self, 0, 0, result, "ABOUT");
-  text_append_n (result, " </th>\n    ", 11);
-  open_element_with_class (self, "th", &name_direction_about_classes,
-                           result);
-  text_append_n (result, " ", 1);
-  translate_convert_to_html_internal ("Name", self, 0, 0, result, "ABOUT");
-  text_append_n (result, " </th>\n    ", 11);
-  open_element_with_class (self, "th", &description_direction_about_classes,
-                           result);
-  text_append_n (result, " ", 1);
-  translate_convert_to_html_internal ("Go to", self, 0, 0, result, "ABOUT");
-  text_append_n (result, " </th>\n    ", 11);
-  open_element_with_class (self, "th", &example_direction_about_classes,
-                           result);
-  text_append_n (result, " ", 1);
-  translate_convert_to_html_internal ("From 1.2.3 go to", self, 0, 0,
-                                      result, "ABOUT");
-  text_append (result, "</th>\n  </tr>\n");
+          if (self->conf->MENU_SYMBOL.o.string)
+            text_append (result, self->conf->MENU_SYMBOL.o.string);
+          text_append_n (result, " ", 1);
 
-  for (i = 0; i < buttons->number; i++)
-    {
-      const BUTTON_SPECIFICATION *button = &buttons->list[i];
-      int direction = -1;
-      const char *button_name;
-      const char *button_description;
-      const char *button_example;
+          if (href)
+            {
+              menu_entry_a (self, href, isindex, html_menu_entry_index,
+                            result);
+            }
 
-      if (button->type == BST_direction_info)
-        direction = button->b.button_info->direction;
-      else if (button->type == BST_direction)
-        direction = button->b.direction;
+          if (name)
+            {
+              size_t n = strspn (name, whitespace_chars);
+              if (n)
+                {
+                  name_no_number = strdup (name + n);
+                  free (name);
+                }
+              else
+                name_no_number = name;
 
-      if (direction < 0 || direction == D_direction_Space)
-        continue;
+              text_append (result, name_no_number);
+            }
 
-      text_append_n (result, "  <tr>\n    ", 11);
-      open_element_with_class (self, "td", &button_direction_about_classes,
-                               result);
+          if (href)
+            text_append_n (result, "</a>", 4);
+        }
 
-   /* if the button spec is an array we do not know what the button
-      looks like, so we do not show the button but still show explanations. */
+      if (self->conf->MENU_ENTRY_COLON.o.string)
+        text_append (result, self->conf->MENU_ENTRY_COLON.o.string);
+      text_append_n (result, "</td>", 5);
 
-      if (button->type == BST_direction)
+      open_element_with_class (self, "td",
+                               &menu_entry_description_classes, result);
+
+      if (formatted_nodedescription_nr > 0)
         {
-          if (self->conf->ICONS.o.integer > 0
-              && self->conf->ACTIVE_ICONS.o.icons->number > 0
-              && self->conf->ACTIVE_ICONS.o.icons->list[direction]
-              && strlen (self->conf->ACTIVE_ICONS.o.icons->list[direction]))
+          char *multiple_formatted = 0;
+          ELEMENT *description_element;
+          if (formatted_nodedescription_nr > 1)
             {
-              const char *button_name_string
-                   = direction_string (self, direction,
-                                       TDS_type_button, TDS_context_string);
-              char *button = format_button_icon_img (self, button_name_string,
-                        self->conf->ACTIVE_ICONS.o.icons->list[direction], 0);
-              text_append (result, button);
-              free (button);
+              xasprintf (&multiple_formatted,
+                         "node-description-%d",
+                         formatted_nodedescription_nr);
             }
+
+          if (node_description->e.c->cmd == CM_nodedescription)
+            description_element = node_description->e.c->args.list[0];
           else
             {
-              const char *button_text = direction_string (self, direction,
-                                                          TDS_type_text, 0);
-              text_append_n (result, " [", 2);
-              if (button_text)
-                text_append (result, button_text);
-              text_append_n (result, "] ", 2);
+              description_element = new_element (ET_NONE);
+              description_element->e.c->contents = 
node_description->e.c->contents;
+              add_tree_to_build (self, description_element);
             }
-        }
-      text_append_n (result, "</td>\n    ", 10);
-      open_element_with_class (self, "td", &name_direction_about_classes,
-                               result);
 
-      button_name = direction_string (self, direction, TDS_type_button, 0);
-      if (button_name)
-        text_append (result, button_name);
-      text_append_n (result, "</td>\n    ", 10);
-      open_element_with_class (self, "td",
-                               &description_direction_about_classes,
-                               result);
-      button_description = direction_string (self, direction,
-                                             TDS_type_description, 0);
-      if (button_description)
-        text_append (result, button_description);
-      text_append_n (result, "</td>\n    ", 10);
-      open_element_with_class (self, "td", &example_direction_about_classes,
-                               result);
-      button_example = direction_string (self, direction, TDS_type_example, 0);
-      if (button_example)
-        text_append (result, button_example);
-      text_append_n (result, "</td>\n  </tr>\n", 14);
-    }
+          description
+            = convert_tree_new_formatting_context (self, description_element,
+                 "menu_arg node description", multiple_formatted,
+                 0, CM_menu);
 
-  text_append_n (result, "</table>\n\n<p>\n", 14);
+          if (formatted_nodedescription_nr > 1)
+            free (multiple_formatted);
+          if (node_description->e.c->cmd != CM_nodedescription)
+            {
+              remove_tree_to_build (self, description_element);
+              description_element->e.c->contents.list = 0;
+              destroy_element (description_element);
+            }
+        }
+      else if (menu_description)
+        {
+          description = html_convert_tree (self, menu_description,
+                                           "menu_arg description");
+        }
 
-  translate_convert_to_html_internal (
- "  where the @strong{ Example } assumes that the current position is at "
- "@strong{ Subsubsection One-Two-Three } of a document of the following "
- "structure:", self, 0, 0, result, "ABOUT");
+      if (description)
+        {
+          text_append (result, description);
+          free (description);
+        }
 
-  text_append_n (result, "\n</p>\n\n<ul>\n", 12);
-  text_append (result, "  <li> 1. ");
-  translate_convert_to_html_internal ("Section One",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "\n    <ul>\n      <li>1.1 ");
-  translate_convert_to_html_internal ("Subsection One-One",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "\n        <ul>\n          <li>...</li>\n"
-     "        </ul>\n      </li>\n      <li>1.2 ");
-  translate_convert_to_html_internal ("Subsection One-Two",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "\n        <ul>\n          <li>1.2.1 ");
-  translate_convert_to_html_internal ("Subsubsection One-Two-One",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "</li>\n          <li>1.2.2 ");
-  translate_convert_to_html_internal ("Subsubsection One-Two-Two",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "</li>\n          <li>1.2.3 ");
-  translate_convert_to_html_internal ("Subsubsection One-Two-Three",
-                                      self, 0, 0, result, "ABOUT");
-  text_append_n (result, " ", 1);
-  text_append_n (result,
-                self->special_character[SC_non_breaking_space].string,
-                self->special_character[SC_non_breaking_space].len);
-  text_append_n (result, " ", 1);
-  text_append_n (result,
-                self->special_character[SC_non_breaking_space].string,
-                self->special_character[SC_non_breaking_space].len);
-  text_append_n (result, "\n", 1);
+      free (name_no_number);
+      text_append_n (result, "</td></tr>\n", 11);
+    }
 
-  text_append (result, "            <strong>&lt;== ");
-  translate_convert_to_html_internal ("Current Position",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, " </strong></li>\n          <li>1.2.4 ");
-  translate_convert_to_html_internal ("Subsubsection One-Two-Four",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "</li>\n        </ul>\n      </li>\n      <li>1.3 ");
-  translate_convert_to_html_internal ("Subsection One-Three",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "\n        <ul>\n          <li>...</li>\n"
-  "        </ul>\n      </li>\n      <li>1.4 ");
-  translate_convert_to_html_internal ("Subsection One-Four",
-                                      self, 0, 0, result, "ABOUT");
-  text_append (result, "</li>\n    </ul>\n  </li>\n</ul>\n");
+  free (href);
 }
 
-static const SPECIAL_UNIT_BODY_INTERNAL_CONVERSION
-   special_unit_body_internal_formatting_table[] = {
-  {"contents", &default_format_special_body_contents},
-  {"shortcontents", &default_format_special_body_shortcontents},
-  {"footnotes", &default_format_special_body_footnotes},
-  {"about", &default_format_special_body_about},
-  {0, 0},
-};
+static char *menu_comment_array[] = {"menu-comment"};
+static const STRING_LIST menu_comment_classes = {menu_comment_array, 1, 1};
 
-static void
-command_conversion_external (CONVERTER *self, const enum command_id cmd,
-                    const ELEMENT *element,
-                    const HTML_ARGS_FORMATTED *args_formatted,
-                    const char *content, TEXT *result)
+void
+html_convert_menu_comment_type (CONVERTER *self, const enum element_type type,
+                                const ELEMENT *element, const char *content,
+                                TEXT *result)
 {
-  /* XS specific debug message */
-  /*
-  if (self->conf->DEBUG.o.integer > 0)
-    fprintf (stderr, "DEBUG: command conversion %s '%s'\n",
-             builtin_command_data[cmd].cmdname, content);
-   */
+  char *attribute_class;
 
-  const FORMATTING_REFERENCE *formatting_reference
-    = self->current_commands_conversion_function[cmd].formatting_reference;
+  if (html_inside_preformatted (self) || html_in_string (self))
+    {
+      if (content)
+        text_append (result, content);
+      return;
+    }
 
-  /* NOTE it should always be true as in the main loop a formatting
-     function is called only if command_conversion is set, which should
-     not be if formatting_reference status is 0 */
-  if (formatting_reference->status > 0)
-    call_commands_conversion (self, cmd, formatting_reference,
-                              element, args_formatted, content,
-                              result);
-}
+  text_append_n (result, "<tr>", 4);
+  attribute_class = html_attribute_class (self, "th",
+                                &menu_comment_classes);
+  text_append (result, attribute_class);
+  free (attribute_class);
+  text_append_n (result, " colspan=\"2\">", 13);
+
+  if (content)
+    text_append (result, content);
 
-static void
-command_open_external (CONVERTER *self, const enum command_id cmd,
-                       const ELEMENT *element, TEXT *result)
-{
-  if (self->commands_open[cmd].status > 0)
-    call_commands_open (self, cmd, element, result);
+  text_append_n (result, "</th></tr>", 10);
 }
 
-static void
-type_conversion_external (CONVERTER *self, const enum element_type type,
-                          const ELEMENT *element, const char *content,
-                          TEXT *result)
+void
+html_convert_before_item_type (CONVERTER *self, const enum element_type type,
+                               const ELEMENT *element, const char *content,
+                               TEXT *result)
 {
-  const FORMATTING_REFERENCE *formatting_reference
-    = self->current_types_conversion_function[type].formatting_reference;
-  /* NOTE it should always be true, as in the main loop a formatting
-     function is called only if type_conversion is set, which should not
-     be if formatting_reference status is 0 */
-  if (formatting_reference->status > 0)
-    call_types_conversion (self, type, formatting_reference,
-                           element, content, result);
-}
+  enum command_id in_format_cmd;
 
-static void
-type_open_external (CONVERTER *self, enum element_type type,
-                    const ELEMENT *element, TEXT *result)
-{
-  if (self->types_open[type].status > 0)
-    call_types_open (self, type, element, result);
-}
+  if (!content || content[strspn (content, whitespace_chars)] == '\0')
+    return;
 
-static void
-output_unit_conversion_external (CONVERTER *self,
-                        const enum output_unit_type unit_type,
-                        const OUTPUT_UNIT *output_unit, const char *content,
-                        TEXT *result)
-{
-  if (self->output_units_conversion[unit_type].status > 0)
-    call_output_units_conversion (self, unit_type, output_unit, content,
-                                  result);
+  if (html_in_string (self))
+    {
+      text_append (result, content);
+      return;
+    }
+
+  in_format_cmd = html_top_block_command (self);
+
+  if (in_format_cmd == CM_itemize || in_format_cmd == CM_enumerate)
+    {
+      text_append_n (result, "<li>", 4);
+      text_append (result, content);
+      text_append_n (result, "</li>", 5);
+    }
+  else if (in_format_cmd == CM_table || in_format_cmd == CM_vtable
+           || in_format_cmd == CM_ftable)
+    {
+      text_append_n (result, "<dd>", 4);
+      text_append (result, content);
+      text_append_n (result, "</dd>\n", 6);
+    }
+  else if (in_format_cmd == CM_multitable)
+    {
+      char *trimmed_content;
+      const char *p = content;
+      p += strspn (p, whitespace_chars);
+      trimmed_content = trim_trailing_content (p);
+
+      text_append_n (result, "<tr><td>", 8);
+      text_append (result, trimmed_content);
+      free (trimmed_content);
+      text_append_n (result, "</td></tr>\n", 11);
+    }
 }
 
-static void
-special_unit_body_formatting_external (CONVERTER *self,
-                               const size_t special_unit_number,
-                               const char *special_unit_variety,
-                               const OUTPUT_UNIT *output_unit,
-                               TEXT *result)
+void
+html_convert_table_term_type (CONVERTER *self, const enum element_type type,
+                              const ELEMENT *element, const char *content,
+                              TEXT *result)
 {
-  if (self->special_unit_body[special_unit_number -1].status > 0)
-    call_special_unit_body_formatting (self, special_unit_number,
-                                       special_unit_variety,
-                                       output_unit, result);
+  if (content)
+    {
+      text_append (result, "<dt>");
+      text_append (result, content);
+    }
 }
 
-static const enum command_id simpletitle_cmds[] =
- {CM_settitle, CM_shorttitlepage, 0};
+#define static_class(name, class) \
+static char * name ##_array[] = {#class}; \
+static const STRING_LIST name ##_classes = {name ##_array, 1, 1};
+
+static_class(def_type, def-type)
+static_class(def_name, def-name)
+static_class(def_code_arguments, def-code-arguments)
+static_class(def_var_arguments, def-var-arguments)
+static_class(call_def, call-def)
+static_class(category_def, category-def)
+
+#undef static_class
 
 void
-html_prepare_simpletitle (CONVERTER *self)
+html_convert_def_line_type (CONVERTER *self, const enum element_type type,
+                            const ELEMENT *element, const char *content,
+                            TEXT *result)
 {
-  int i;
-  for (i = 0; simpletitle_cmds[i]; i++)
+  const char *index_id;
+  PARSED_DEF *parsed_def;
+  STRING_LIST *classes;
+  char *attribute_class;
+  char *alias_class = 0;
+  enum command_id original_def_cmd;
+  enum command_id def_cmd;
+  enum command_id original_cmd = 0;
+  enum command_id base_cmd = 0;
+  TEXT def_call;
+  char *anchor;
+
+  if (html_in_string (self))
     {
-      enum command_id cmd = simpletitle_cmds[i];
-      const ELEMENT *command
-        = get_cmd_global_uniq_command (&self->document->global_commands, cmd);
-      if (command && command->e.c->args.number > 0
-          && command->e.c->args.list[0]->e.c->contents.number > 0)
+      /* should probably never happen */
+      char *text;
+      text = convert_to_text (element, self->convert_text_options);
+      format_protect_text (self, text, result);
+      free (text);
+    }
+
+  index_id = html_command_id (self, element);
+
+  parsed_def = definition_arguments_content (element);
+
+  if (element->e.c->cmd)
+    original_def_cmd = element->e.c->cmd;
+  else
+    original_def_cmd = element->parent->e.c->cmd;
+
+  if (builtin_command_data[original_def_cmd].flags & CF_def_alias)
+    {
+      int i;
+      for (i = 0; def_aliases[i].alias ; i++)
         {
-          self->simpletitle_tree = command->e.c->args.list[0];
-          self->simpletitle_cmd = cmd;
-          break;
+          if (def_aliases[i].alias == original_def_cmd)
+            {
+              original_cmd = def_aliases[i].command;
+              break;
+            }
         }
+
+      xasprintf (&alias_class, "%s-alias-%s",
+                    builtin_command_name (original_def_cmd),
+                    builtin_command_name (original_cmd));
     }
-}
+  else
+    original_cmd = original_def_cmd;
 
-static const enum command_id fulltitle_cmds[] =
- {CM_settitle, CM_title, CM_shorttitlepage, CM_top, 0};
+  /* parent is defblock, we do not put it in class */
+  if (element->e.c->cmd == CM_defline || element->e.c->cmd == CM_deftypeline)
+    def_cmd = element->e.c->cmd;
+  else
+  /* the parent is the def both for def* def_line and def*x */
+    def_cmd = element->parent->e.c->cmd;
 
+  if (builtin_command_data[def_cmd].flags & CF_def_alias)
+    {
+      int i;
+      for (i = 0; def_aliases[i].alias ; i++)
+        {
+          if (def_aliases[i].alias == def_cmd)
+            {
+              base_cmd = def_aliases[i].command;
+              break;
+            }
+        }
+    }
+  else
+    base_cmd = def_cmd;
 
-int
-html_prepare_converted_output_info (CONVERTER *self, const char *output_file,
-                                    const char *output_filename)
-{
-  int i;
-  ELEMENT *fulltitle_tree = 0;
-  char *html_title_string = 0;
-  char *default_document_language = 0;
-  char *preamble_document_language = 0;
-  int init_handler_status;
-  int handler_fatal_error_level
-     = self->conf->HANDLER_FATAL_ERROR_LEVEL.o.integer;
+  classes = new_string_list ();
 
-  int structure_handler_status = run_stage_handlers (self, 
HSHT_type_structure);
+  add_string (builtin_command_name (original_cmd), classes);
+  if (alias_class)
+    {
+      add_string (alias_class, classes);
+      free (alias_class);
+    }
+  if (base_cmd != original_cmd)
+    {
+      char *class;
+      xasprintf (&class, "def-cmd-%s", builtin_command_name (base_cmd));
+      add_string (class, classes);
+      free (class);
+    }
 
-  if (structure_handler_status < handler_fatal_error_level
-      && structure_handler_status > -handler_fatal_error_level)
-    {}
-  else
-    return 0;
+  add_string ("def-line", classes);
 
-  if (self->conf->documentlanguage.o.string)
-    default_document_language = strdup (self->conf->documentlanguage.o.string);
+  text_init (&def_call);
+  text_append (&def_call, "");
+  if (parsed_def->type)
+    {
+      char *type_text;
+      size_t type_text_len;
+      ELEMENT *root_code = new_element (ET__code);
+      char *explanation;
+
+      xasprintf (&explanation, "DEF_TYPE %s", builtin_command_name (def_cmd));
+
+      add_to_contents_as_array (root_code, parsed_def->type);
+
+      add_tree_to_build (self, root_code);
+
+      type_text = html_convert_tree (self, root_code, explanation);
+
+      remove_tree_to_build (self, root_code);
+
+      destroy_element (root_code);
+      free (explanation);
+
+      type_text_len = strlen (type_text);
+
+      if (type_text_len > 0)
+        {
+          char *attribute_class = html_attribute_class (self, "code",
+                                                        &def_type_classes);
+          text_append (&def_call, attribute_class);
+          free (attribute_class);
+          text_append_n (&def_call, ">", 1);
+          text_append_n (&def_call, type_text, type_text_len);
+          text_append_n (&def_call, "</code>", 7);
+        }
+      if ((base_cmd == CM_deftypefn || base_cmd == CM_deftypeop)
+          && self->conf->deftypefnnewline.o.string
+          && !strcmp (self->conf->deftypefnnewline.o.string, "on"))
+        {
+          text_append_n (&def_call, self->line_break_element.string,
+                                    self->line_break_element.len);
+          text_append_n (&def_call, " ", 1);
+        }
+      else if (type_text_len > 0)
+        text_append_n (&def_call, " ", 1);
+      free (type_text);
+    }
 
-  set_global_document_commands (self, CL_preamble, conf_for_documentlanguage);
+  if (parsed_def->name)
+    {
+      char *attribute_class = html_attribute_class (self, "strong",
+                                                    &def_name_classes);
+      char *explanation;
+      xasprintf (&explanation, "DEF_NAME %s", builtin_command_name (def_cmd));
 
-  if (self->conf->documentlanguage.o.string)
-    preamble_document_language = strdup 
(self->conf->documentlanguage.o.string);
+      ELEMENT *root_code = new_element (ET__code);
 
-  if (! (!default_document_language && !preamble_document_language)
-      && (!default_document_language || !preamble_document_language
-          || strcmp (default_document_language, preamble_document_language)))
-    html_translate_names (self);
+      add_to_contents_as_array (root_code, parsed_def->name);
 
-  /*
-   prepare title.  fulltitle uses more possibility than simpletitle for
-   title, including @-commands found in @titlepage only.  Therefore
-   simpletitle is more in line with what makeinfo in C did.
-   */
+      add_tree_to_build (self, root_code);
 
-  html_prepare_simpletitle (self);
+      text_append (&def_call, attribute_class);
+      free (attribute_class);
+      text_append_n (&def_call, ">", 1);
 
-  for (i = 0; fulltitle_cmds[i]; i++)
-    {
-      enum command_id cmd = fulltitle_cmds[i];
-      const ELEMENT *command
-        = get_cmd_global_uniq_command (&self->document->global_commands, cmd);
-      if (command && command->e.c->args.number > 0
-          && command->e.c->args.list[0]->e.c->contents.number > 0)
-        {
-          fulltitle_tree = command->e.c->args.list[0];
-          break;
-        }
-    }
+      convert_to_html_internal (self, root_code, &def_call, explanation);
 
-  if (!fulltitle_tree
-      && self->document->global_commands.titlefont.number > 0
-      && self->document->global_commands.titlefont.list[0]->e.c->args.number > 0
-      && self->document->global_commands.titlefont.list[0]->e.c->args.list[0]
-                                    ->e.c->contents.number > 0)
-    {
-      fulltitle_tree = self->document->global_commands.titlefont.list[0];
+      remove_tree_to_build (self, root_code);
+      destroy_element (root_code);
+
+      text_append_n (&def_call, "</strong>", 9);
+      free (explanation);
     }
 
-  if (fulltitle_tree)
+  if (parsed_def->args)
     {
-      self->title_tree = fulltitle_tree;
-
-      html_title_string = convert_string_tree_new_formatting_context (self,
-                                       fulltitle_tree, "title_string", 0);
-      if (html_title_string[strspn (html_title_string, whitespace_chars)]
-           == '\0')
+      char *args_formatted;
+      char *explanation;
+      xasprintf (&explanation, "DEF_ARGS %s", builtin_command_name (def_cmd));
+   /* arguments not only metasyntactic variables
+      (deftypefn, deftypevr, deftypeop, deftypecv) */
+      /* Texinfo::Common::def_no_var_arg_commands{$base_command_name} */
+      if (strlen (builtin_command_name (base_cmd)) >= 7
+          && !memcmp (builtin_command_name (base_cmd), "deftype", 7))
         {
-          free (html_title_string);
-          html_title_string = 0;
-        }
-    }
+          ELEMENT *root_code = new_element (ET__code);
 
-  if (!html_title_string)
-    {
-      ELEMENT *default_title = html_cdt_tree ("Untitled Document",
-                                              self, 0, 0);
-      SOURCE_INFO cmd_source_info;
+          add_to_contents_as_array (root_code, parsed_def->args);
 
-      self->title_tree = default_title;
+          add_tree_to_build (self, root_code);
 
-      html_title_string = convert_string_tree_new_formatting_context (self,
-                                       default_title, "title_string", 0);
+          args_formatted = html_convert_tree (self, root_code, explanation);
 
-      self->added_title_tree = 1;
+          remove_tree_to_build (self, root_code);
+          destroy_element (root_code);
 
-      if (self->document->global_info.input_file_name)
-        {
-          /* setup a source info with file only */
-          memset (&cmd_source_info, 0, sizeof (SOURCE_INFO));
-          cmd_source_info.file_name
-           = self->document->global_info.input_file_name;
-          /* this is more in line with the Perl function used, as DEBUG is
-             checked in the called function */
-          message_list_line_error_ext (&self->error_messages, self->conf,
-                                  MSG_warning, 0, &cmd_source_info,
-                      "must specify a title with a title command or @top");
+          if (args_formatted[strspn (args_formatted, whitespace_chars)] != 
'\0')
+            {
+              char *attribute_class = html_attribute_class (self, "code",
+                                              &def_code_arguments_classes);
+              int omit_def_name_space = (element->flags & 
EF_omit_def_name_space);
+              if (!omit_def_name_space)
+                text_append_n (&def_call, " ", 1);
+              text_append (&def_call, attribute_class);
+              free (attribute_class);
+              text_append_n (&def_call, ">", 1);
+              text_append (&def_call, args_formatted);
+              text_append_n (&def_call, "</code>", 7);
+            }
         }
       else
         {
-          message_list_document_warn (&self->error_messages, self->conf, 0,
-                      "must specify a title with a title command or @top");
+          html_set_code_context (self, 0);
+          args_formatted = html_convert_tree (self, parsed_def->args,
+                                              explanation);
+          html_pop_code_context (self);
+          if (args_formatted[strspn (args_formatted, whitespace_chars)] != 
'\0')
+            {
+              char *attribute_class = html_attribute_class (self, "var",
+                                              &def_var_arguments_classes);
+              int omit_def_name_space = (element->flags & 
EF_omit_def_name_space);
+              if (!omit_def_name_space)
+                text_append_n (&def_call, " ", 1);
+              text_append (&def_call, attribute_class);
+              free (attribute_class);
+              text_append_n (&def_call, ">", 1);
+              text_append (&def_call, args_formatted);
+              text_append_n (&def_call, "</var>", 6);
+            }
         }
+      free (explanation);
+      free (args_formatted);
     }
 
-  self->title_string = html_title_string;
-
-  /* copying comment */
-
-  if (self->document->global_commands.copying)
+  if (self->conf->DEF_TABLE.o.integer > 0)
     {
-      char *copying_comment;
-      ELEMENT *tmp = new_element (ET_NONE);
+      ELEMENT *category_tree
+         = definition_category_tree (self->conf, element);
 
-      tmp->e.c->contents = 
self->document->global_commands.copying->e.c->contents;
+      attribute_class = html_attribute_class (self, "tr", classes);
+      destroy_strings_list (classes);
+      text_append (result, attribute_class);
+      free (attribute_class);
+      if (index_id && strlen (index_id) && !html_in_multi_expanded (self))
+        text_printf (result, " id=\"%s\"", index_id);
+      text_append_n (result, ">", 1);
 
-      copying_comment = convert_to_text (tmp, self->convert_text_options);
+      attribute_class = html_attribute_class (self, "td",
+                                               &call_def_classes);
+      text_append (result, attribute_class);
+      free (attribute_class);
+      text_append_n (result, ">", 1);
+      text_append_n (result, def_call.text, def_call.end);
+      free (def_call.text);
+      text_append_n (result, "</td>", 5);
 
-      tmp->e.c->contents.list = 0;
-      destroy_element (tmp);
+      attribute_class = html_attribute_class (self, "td",
+                                              &category_def_classes);
+      text_append (result, attribute_class);
+      free (attribute_class);
+      text_append_n (result, ">[", 2);
 
-      if (copying_comment && strlen (copying_comment) > 0)
+      if (category_tree)
         {
-          self->copying_comment = format_comment (self, copying_comment);
+          add_tree_to_build (self, category_tree);
+          convert_to_html_internal (self, category_tree, result, 0);
+          remove_tree_to_build (self, category_tree);
+          destroy_element_and_children (category_tree);
         }
-      free (copying_comment);
+      text_append_n (result, "]</td></tr>\n", 12);
+
+      destroy_parsed_def (parsed_def);
+      return;
     }
 
-  /* documentdescription */
-  if (self->conf->documentdescription.o.string)
-    self->documentdescription_string
-     = strdup (self->conf->documentdescription.o.string);
-  else if (self->document->global_commands.documentdescription)
+  attribute_class = html_attribute_class (self, "dt", classes);
+  destroy_strings_list (classes);
+  text_append (result, attribute_class);
+  free (attribute_class);
+  if (index_id && strlen (index_id) && !html_in_multi_expanded (self))
+    text_printf (result, " id=\"%s\"", index_id);
+  text_append_n (result, ">", 1);
+
+  if (parsed_def->category)
     {
-      ELEMENT *tmp = new_element (ET_NONE);
-      char *documentdescription_string;
-      size_t documentdescription_string_len;
+      ELEMENT *category_tree = 0;
+      NAMED_STRING_ELEMENT_LIST *substrings
+                                   = new_named_string_element_list ();
+      ELEMENT *category_copy = copy_tree (parsed_def->category);
 
-      tmp->e.c->contents
-        = self->document->global_commands.documentdescription->e.c->contents;
+      add_element_to_named_string_element_list (substrings,
+                                            "category", category_copy);
+      if (parsed_def->class)
+        {
+          ELEMENT *class_copy = copy_tree (parsed_def->class);
+          add_element_to_named_string_element_list (substrings,
+                                            "class", class_copy);
 
-      documentdescription_string
-                 = convert_string_tree_new_formatting_context (self,
-                                       tmp, "documentdescription", 0);
+          if (base_cmd == CM_deftypeop && parsed_def->type
+              && self->conf->deftypefnnewline.o.string
+              && !strcmp (self->conf->deftypefnnewline.o.string, "on"))
+            {
+               category_tree
+                  = html_cdt_tree ("{category} on @code{{class}}:@* ",
+                                   self, substrings, 0);
+            }
+          else if (base_cmd == CM_defop || base_cmd == CM_deftypeop)
+            {
+               category_tree
+                  = html_cdt_tree ("{category} on @code{{class}}: ",
+                                   self, substrings, 0);
+            }
+          else if (base_cmd == CM_defcv || base_cmd == CM_deftypecv)
+            {
+               category_tree
+                  = html_cdt_tree ("{category} of @code{{class}}: ",
+                                   self, substrings, 0);
+            }
+        }
+      else
+        {
+          if ((base_cmd == CM_deftypefn || base_cmd == CM_deftypeop)
+              && parsed_def->type
+              && self->conf->deftypefnnewline.o.string
+              && !strcmp (self->conf->deftypefnnewline.o.string, "on"))
+            {
+              category_tree
+                  = html_cdt_tree ("{category}:@* ",
+                                   self, substrings, 0);
+            }
+          else
+            {
+              category_tree
+                  = html_cdt_tree ("{category}: ",
+                                   self, substrings, 0);
+            }
+        }
+      destroy_named_string_element_list (substrings);
 
-      tmp->e.c->contents.list = 0;
-      destroy_element (tmp);
+      if (category_tree)
+        {
+          char *attribute_open = html_attribute_class (self, "span",
+                                             &category_def_classes);
+          size_t open_len = strlen (attribute_open);
+          char *explanation;
 
-      documentdescription_string_len = strlen (documentdescription_string);
-      if (documentdescription_string_len > 0
-          && documentdescription_string[documentdescription_string_len -1]
-             == '\n')
-        documentdescription_string[documentdescription_string_len -1] = '\0';
+          xasprintf (&explanation, "DEF_CATEGORY %s",
+                     builtin_command_name (def_cmd));
 
-      self->documentdescription_string = documentdescription_string;
+          if (open_len)
+            {
+              text_append_n (result, attribute_open, open_len);
+              text_append_n (result, ">", 1);
+            }
+          free (attribute_open);
+          add_tree_to_build (self, category_tree);
+          convert_to_html_internal (self, category_tree, result, explanation);
+          remove_tree_to_build (self, category_tree);
+          destroy_element_and_children (category_tree);
+          if (open_len)
+            text_append_n (result, "</span>", 7);
+          free (explanation);
+        }
     }
 
-  init_handler_status = run_stage_handlers (self, HSHT_type_init);
+  destroy_parsed_def (parsed_def);
 
-  if (init_handler_status < handler_fatal_error_level
-      && init_handler_status > -handler_fatal_error_level)
-    {}
-  else
-    {
-      free (default_document_language);
-      free (preamble_document_language);
+  anchor = get_copiable_anchor (self, index_id);
 
-      return 0;
+  if (anchor)
+    text_append_n (result, "<span>", 6);
+
+  text_append_n (result, def_call.text, def_call.end);
+  free (def_call.text);
+  if (anchor)
+    {
+      text_append (result, anchor);
+      text_append_n (result, "</span>", 7);
     }
 
-  html_prepare_title_titlepage (self, output_file, output_filename);
+  text_append_n (result, "</dt>\n", 6);
 
-  set_global_document_commands (self, CL_before, conf_for_documentlanguage);
+  free (anchor);
+}
 
-  if (! (!default_document_language && !preamble_document_language)
-      && (!default_document_language || !preamble_document_language
-          || strcmp (default_document_language, preamble_document_language)))
-    html_translate_names (self);
+void
+html_convert_def_item_type (CONVERTER *self, const enum element_type type,
+                            const ELEMENT *element, const char *content,
+                            TEXT *result)
+{
+  if (!content)
+    return;
 
-  free (default_document_language);
-  free (preamble_document_language);
+  if (html_in_string (self))
+    text_append (result, content);
 
-  return 1;
+  if (content[strspn (content, whitespace_chars)] == '\0')
+    return;
+
+  if (self->conf->DEF_TABLE.o.integer <= 0)
+    {
+      text_append_n (result, "<dd>", 4);
+      text_append (result, content);
+      text_append_n (result, "</dd>", 5);
+    }
+  else
+    {
+      text_append_n (result, "<tr><td colspan=\"2\">", 20);
+      text_append (result, content);
+      text_append_n (result, "</td></tr>", 10);
+    }
 }
 
 void
-reset_translated_special_unit_info_tree (CONVERTER *self)
+html_convert_table_definition_type (CONVERTER *self,
+                                    const enum element_type type,
+                                    const ELEMENT *element, const char 
*content,
+                                    TEXT *result)
 {
-  STRING_LIST *special_unit_varieties = &self->special_unit_varieties;
-  int j;
-  for (j = 0; translated_special_unit_info[j].tree_type != SUIT_type_none; j++)
-    {
-      size_t i;
-      enum special_unit_info_tree tree_type
-        = translated_special_unit_info[j].tree_type;
-      for (i = 0; i < special_unit_varieties->number; i++)
-        {
-          if (self->special_unit_info_tree[tree_type][i])
-            {
-              remove_tree_to_build (self,
-                             self->special_unit_info_tree[tree_type][i]);
-              destroy_element_and_children (
-                self->special_unit_info_tree[tree_type][i]);
+  if (!content)
+    return;
 
-            }
-          self->special_unit_info_tree[tree_type][i] = 0;
-        }
-    }
-}
+  if (html_in_string (self))
+    text_append (result, content);
 
-static COMMAND_STACK preformatted_cmd_list;
-static COMMAND_STACK def_cmd_list;
+  if (content[strspn (content, whitespace_chars)] == '\0')
+    return;
 
-COMMAND_ID_LIST no_arg_formatted_cmd;
-COMMAND_ID_LIST style_formatted_cmd;
-COMMAND_ID_LIST accent_cmd;
-COMMAND_ID_LIST format_raw_cmd;
+  text_append_n (result, "<dd>", 4);
+  text_append (result, content);
+  text_append_n (result, "</dd>\n", 6);
+}
 
-static char *unicode_entities[BUILTIN_CMD_NUMBER];
 
-static CSS_SELECTOR_STYLE_LIST default_css_element_class_styles;
+void
+html_type_open_external (CONVERTER *self, enum element_type type,
+                         const ELEMENT *element, TEXT *result)
+{
+  if (self->types_open[type].status > 0)
+    call_types_open (self, type, element, result);
+}
 
-static char *
-get_special_list_mark_css_string_no_arg_command (enum command_id cmd)
+void
+html_open_inline_container_type (CONVERTER *self, const enum element_type type,
+                                 const ELEMENT *element, TEXT *result)
 {
-  int s;
-  for (s = 0;
-       special_list_mark_css_string_no_arg_command[s].cmd > 0;
-       s++)
+  char *pending_formatted = html_get_pending_formatted_inline_content (self);
+  if (pending_formatted)
     {
-       if (special_list_mark_css_string_no_arg_command[s].cmd == cmd)
-        {
-           return special_list_mark_css_string_no_arg_command[s].string;
-           break;
-        }
+      html_associate_pending_formatted_inline_content (self, element, 0,
+                                                       pending_formatted);
+      free (pending_formatted);
     }
-  return 0;
 }
 
-static void
-set_no_arg_commands_formatting (HTML_NO_ARG_COMMAND_CONVERSION *spec,
-                                char *text)
+void
+html_output_unit_conversion_external (CONVERTER *self,
+                               const enum output_unit_type unit_type,
+                         const OUTPUT_UNIT *output_unit, const char *content,
+                         TEXT *result)
 {
-  spec->text = text;
-  spec->unset = 0;
+  if (self->output_units_conversion[unit_type].status > 0)
+    call_output_units_conversion (self, unit_type, output_unit, content,
+                                  result);
 }
 
-/* set information that is independent of customization, only called once */
 void
-html_format_setup (void)
+html_convert_unit_type (CONVERTER *self, const enum output_unit_type unit_type,
+                        const OUTPUT_UNIT *output_unit, const char *content,
+                        TEXT *result)
 {
-  int i;
-  int no_arg_formatted_cmd_nr = 0;
-  int accent_cmd_nr = 0;
-  int style_formatted_cmd_nr = 0;
-  int format_raw_cmd_nr = 0;
-  int no_arg_formatted_cmd_idx;
-  int default_commands_args_nr
-    = sizeof (default_commands_args) / sizeof (default_commands_args[0]);
-  int max_args = MAX_COMMAND_ARGS_NR;
-  CSS_SELECTOR_STYLE *css_selector_style;
-  TEXT css_string_text;
-
-  enum command_id indented_format[] = {
-    CM_example, CM_display, CM_lisp, 0
-  };
-
-  initialize_css_selector_style_list (&default_css_element_class_styles,
-                                      BASE_DEFAULT_CSS_ELEMENT_CLASS_STYLE_NR);
-  for (i = 0; i < BASE_DEFAULT_CSS_ELEMENT_CLASS_STYLE_NR; i++)
-    {
-      CSS_SELECTOR_STYLE *selector_style
-        = &default_css_element_class_styles.list[i];
-      selector_style->selector
-        = base_default_css_element_class_styles[i].selector;
-      selector_style->style
-        = base_default_css_element_class_styles[i].style;
-    }
-  sort_css_element_class_styles (&default_css_element_class_styles);
-
-  css_selector_style
-    = find_css_selector_style (&default_css_element_class_styles,
-                               "pre.display-preformatted");
-  html_css_set_selector_style (&default_css_element_class_styles,
-                               "pre.format-preformatted",
-                               css_selector_style->style);
+  STRING_LIST *closed_strings;
+  const ELEMENT *unit_command;
 
-  for (i = 0; i < default_commands_args_nr; i++)
-    {
-      /* we file the status for specified commands, to distinguish them
-         but it is not actually used in the code, as we default to
-         normal for unspecified commands too */
-      enum command_id cmd = default_commands_args[i].cmd;
-      command_args_flags[cmd].status = 1;
-      memcpy (&command_args_flags[cmd].flags, &default_commands_args[i].flags,
-              max_args * sizeof (unsigned long));
-    }
+  if (html_in_string (self))
+    return;
 
-  for (i = 0; indented_format[i]; i++)
+  if (!output_unit->tree_unit_directions[D_prev])
     {
-      char *css_selector;
-      enum command_id cmd = indented_format[i];
-      html_commands_data[cmd].flags |= HF_indented_preformatted;
+      text_append (result, self->title_titlepage);
+      if (!output_unit->tree_unit_directions[D_next])
+        {
+          /* only one unit, use simplified formatting */
+          if (content)
+            text_append (result, content);
+   /*  if there is one unit it also means that there is no formatting
+       of footnotes in a separate unit.  And if footnotestyle is end
+       the footnotes won't be done in format_element_footer either. */
+          format_footnotes_segment (self, result);
+          if (self->conf->DEFAULT_RULE.o.string
+              && self->conf->PROGRAM_NAME_IN_FOOTER.o.integer > 0)
+            {
+              text_append (result, self->conf->DEFAULT_RULE.o.string);
+              text_append_n (result, "\n", 1);
+            }
 
-      xasprintf (&css_selector, "div.%s", builtin_command_name (cmd));
-      html_css_set_selector_style (&default_css_element_class_styles,
-                                  css_selector,
-                                  "margin-left: 3.2em");
-      free (css_selector);
-    }
-  /* output as div.example instead */
-  html_css_set_selector_style (&default_css_element_class_styles,
-                               "div.lisp", 0);
+    /* do it here, as it is won't be done at end of page in
+       format_element_footer */
+          closed_strings = html_close_registered_sections_level (self,
+                                   self->current_filename.file_number, 0);
 
-  for (i = 0; small_block_associated_command[i][0]; i++)
-    {
-      enum command_id small_cmd = small_block_associated_command[i][0];
-      enum command_id cmd = small_block_associated_command[i][1];
-      if (builtin_command_data[cmd].flags & CF_preformatted)
-        {
-          register_pre_class_command (small_cmd, cmd);
+          if (closed_strings->number)
+            {
+              size_t i;
+              for (i = 0; i < closed_strings->number; i++)
+                {
+                  text_append (result, closed_strings->list[i]);
+                  free (closed_strings->list[i]);
+                }
+              free (closed_strings->list);
+            }
+          free (closed_strings);
+          return;
         }
-      html_commands_data[small_cmd].flags |= HF_small_block_command;
-      if (html_commands_data[cmd].flags & HF_indented_preformatted)
-        html_commands_data[small_cmd].flags |= HF_indented_preformatted;
     }
 
-  for (i = 0; no_brace_command_accent_upper_case[i][0]; i++)
-    {
-      enum command_id cmd = no_brace_command_accent_upper_case[i][0];
-      enum command_id upper_case_cmd = 
no_brace_command_accent_upper_case[i][1];
-      html_commands_data[cmd].upper_case_cmd = upper_case_cmd;
-    }
+  if (content)
+    text_append (result, content);
 
-  /* set flags */
-  /* also count the number of no_arg_formatted_cmd, style_formatted_cmd and
-     accent_cmd commands and other types of commands */
-  for (i = 1; i < BUILTIN_CMD_NUMBER; i++)
-    {
-      if (xml_text_entity_no_arg_commands_formatting[i])
-        no_arg_formatted_cmd_nr++;
+  unit_command = output_unit->uc.unit_command;
 
-      if (html_style_commands_element[i]
-          || (builtin_command_data[i].flags & CF_brace
-              && (builtin_command_data[i].data == BRACE_style_other
-                  || builtin_command_data[i].data == BRACE_style_code
-                  || builtin_command_data[i].data == BRACE_style_no_code)))
-        {
-          html_commands_data[i].flags |= HF_style_command;
-          style_formatted_cmd_nr++;
-        }
+  format_element_footer (self, unit_type, output_unit, content, unit_command,
+                         result);
+}
+
+void
+html_convert_special_unit_type (CONVERTER *self,
+                                const enum output_unit_type unit_type,
+                                const OUTPUT_UNIT *output_unit,
+                                const char *content,
+                                TEXT *result)
+{
+  char *heading;
+  size_t number;
+  TEXT special_unit_body;
+  const ELEMENT *unit_command;
+  const char *id;
+  const char *class_base;
+  char *attribute_class;
+  char *class;
+  STRING_LIST *classes;
 
-      if (builtin_command_data[i].flags & CF_accent)
-        accent_cmd_nr++;
+  const char *special_unit_variety;
+  STRING_LIST *closed_strings;
+  size_t count_in_file = 0;
+  int level;
 
-      if ((builtin_command_data[i].flags & CF_block
-           && builtin_command_data[i].data != BLOCK_format_raw)
-          || builtin_command_data[i].flags & CF_root)
-        register_format_context_command (i);
+  if (html_in_string (self))
+    return;
 
-      if (builtin_command_data[i].flags & CF_preformatted
-          || builtin_command_data[i].flags & CF_root)
-        html_commands_data[i].flags |= HF_composition_context;
+  special_unit_variety = output_unit->special_unit_variety;
+  number = find_string (&self->special_unit_varieties,
+                        special_unit_variety);
 
-      if (builtin_command_data[i].flags & CF_block)
-        {
-          if (builtin_command_data[i].data == BLOCK_menu)
-            html_commands_data[i].flags |= HF_composition_context;
-          else if (builtin_command_data[i].data == BLOCK_format_raw)
-            {
-              html_commands_data[i].flags |= HF_format_raw;
-              format_raw_cmd_nr++;
-            }
-        }
+  closed_strings = html_close_registered_sections_level (self,
+                                self->current_filename.file_number, 0);
 
-      if (builtin_command_data[i].flags & CF_preformatted)
+  if (closed_strings->number)
+    {
+      size_t i;
+      for (i = 0; i < closed_strings->number; i++)
         {
-          if (!(html_commands_data[i].flags & HF_pre_class))
-            register_pre_class_command (i, 0);
-          push_command (&preformatted_cmd_list, i);
+          text_append (result, closed_strings->list[i]);
+          free (closed_strings->list[i]);
         }
-
-      if (builtin_command_data[i].flags & CF_def)
-        push_command (&def_cmd_list, i);
+      free (closed_strings->list);
     }
-  register_pre_class_command (CM_verbatim, 0);
-  register_pre_class_command (CM_menu, 0);
-  for (i = 0; additional_format_context_cmd[i]; i++)
-    register_format_context_command (additional_format_context_cmd[i]);
+  free (closed_strings);
+
+  text_init (&special_unit_body);
+  text_append (&special_unit_body, "");
 
-  /* do not consider indicateurl to be a style command, it has a different
-     formatting function */
-  html_commands_data[CM_indicateurl].flags &= ~HF_style_command;
+  (*self->special_unit_body_formatting[number -1].special_unit_body_formatting)
+         (self, number, special_unit_variety, output_unit, &special_unit_body);
 
-  for (i = 0; HTML_align_cmd[i]; i++)
+  /* This may happen with footnotes in regions that are not expanded,
+     like @copying or @titlepage */
+  if (special_unit_body.end == 0)
     {
-      enum command_id cmd = HTML_align_cmd[i];
-      html_commands_data[cmd].flags |= HF_HTML_align | HF_composition_context;
+      free (special_unit_body.text);
+      return;
     }
 
-  html_commands_data[CM_float].flags |= HF_composition_context;
+  classes = new_string_list ();
 
-  initialize_cmd_list (&no_arg_formatted_cmd, no_arg_formatted_cmd_nr,
-                       no_arg_formatted_cmd_nr);
-  initialize_cmd_list (&accent_cmd, accent_cmd_nr, 0);
-  initialize_cmd_list (&style_formatted_cmd, style_formatted_cmd_nr, 0);
-  initialize_cmd_list (&format_raw_cmd, format_raw_cmd_nr, 0);
+  unit_command = output_unit->uc.special_unit_command;
+  id = html_command_id (self, unit_command);
+  class_base = html_special_unit_info (self, SUI_type_class,
+                                       special_unit_variety);
+  xasprintf (&class, "element-%s", class_base);
 
-  for (i = 0; quoted_style_commands[i]; i++)
-    {
-      enum command_id cmd = quoted_style_commands[i];
-      enum conversion_context cctx;
-      for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
-        {
-          default_style_commands_formatting[cmd][cctx].quote = 1;
-        }
-    }
+  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 (id && strlen (id))
+    text_printf (result, " id=\"%s\"", id);
+  text_append (result, ">\n");
 
-  no_arg_formatted_cmd_idx = 0;
-  for (i = 0; i < BUILTIN_CMD_NUMBER; i++)
+  if (output_unit->unit_filename)
     {
-      if (builtin_command_data[i].flags & CF_accent)
-        {
-          accent_cmd.list[accent_cmd.number] = i;
-          accent_cmd.number++;
-        }
-      else if (html_commands_data[i].flags & HF_format_raw)
-        {
-          format_raw_cmd.list[format_raw_cmd.number] = i;
-          format_raw_cmd.number++;
-        }
-
-      if (html_commands_data[i].flags & HF_style_command)
-        {
-          style_formatted_cmd.list[style_formatted_cmd.number] = i;
-          style_formatted_cmd.number++;
+      size_t file_index = self->special_unit_file_indices[output_unit->index];
+      count_in_file
+        = count_elements_in_file_number (self, CEFT_current, file_index +1);
+    }
 
-          if (html_style_commands_element[i])
-            {
-              enum conversion_context cctx;
-              for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
-                {
-       /* the default_style_commands_formatting contains const char only
-          but the type is not const, so drop the const with a cast */
-                  default_style_commands_formatting[i][cctx].element
-                    = (char *) html_style_commands_element[i];
-                }
-            }
-        }
+  if (self->conf->HEADERS.o.integer > 0
+      /* first in page */
+      || count_in_file == 1)
+    {
+      format_navigation_header (self, self->conf->MISC_BUTTONS.o.buttons, 0,
+                                unit_command, result);
+    }
 
-      if (xml_text_entity_no_arg_commands_formatting[i])
-        {
-          /* the value is never modified but the struct field type is not const
-             so need to cast to drop const */
-          default_no_arg_commands_formatting[i][HCC_type_normal].text
-             = (char *)xml_text_entity_no_arg_commands_formatting[i];
+  heading = html_command_text (self, unit_command, 0);
+  level = self->conf->CHAPTER_HEADER_LEVEL.o.integer;
+  if (!strcmp (special_unit_variety, "footnotes"))
+    level = self->conf->FOOTNOTE_SEPARATE_HEADER_LEVEL.o.integer;
 
-          no_arg_formatted_cmd.list[no_arg_formatted_cmd_idx] = i;
-          no_arg_formatted_cmd_idx++;
+  xasprintf (&class, "%s-heading", class_base);
 
-          /* preset unset for string and preformatted contexts */
-          /* css_string coverage is checked when setting css string context
-             values below and unset is set explicitely if needed */
-          default_no_arg_commands_formatting[i][HCC_type_string].unset = 1;
-          default_no_arg_commands_formatting[i][HCC_type_preformatted].unset = 
1;
-        }
-    }
+  add_string (class, classes);
+  free (class);
 
-  default_style_commands_formatting[CM_sc][HCC_type_preformatted].element
-      = "span";
+  format_heading_text (self, 0, classes, heading, level, 0, 0, 0, result);
+  free (heading);
+  destroy_strings_list (classes);
+  text_append_n (result, "\n", 1);
 
-  /* modify normal context values and add other contexts values, removing
-     unset.  Should only be for commands with normal context already set.
-   */
-  default_no_arg_commands_formatting[CM_SPACE][HCC_type_normal].text = 
"&nbsp;";
-  default_no_arg_commands_formatting[CM_TAB][HCC_type_normal].text = "&nbsp;";
-  default_no_arg_commands_formatting[CM_NEWLINE][HCC_type_normal].text = 
"&nbsp;";
-
-  default_no_arg_commands_formatting[CM_enddots][HCC_type_normal].text = "...";
-  default_no_arg_commands_formatting[CM_enddots][HCC_type_normal].element
-     = "small";
-
-  set_no_arg_commands_formatting (
-   &default_no_arg_commands_formatting[CM_dots][HCC_type_preformatted], "...");
-  set_no_arg_commands_formatting (
-   &default_no_arg_commands_formatting[CM_enddots][HCC_type_preformatted], 
"...");
-
-  default_no_arg_commands_formatting[CM_ASTERISK][HCC_type_normal].text = 
"<br>";
-  set_no_arg_commands_formatting (
-   &default_no_arg_commands_formatting[CM_ASTERISK][HCC_type_preformatted], 
"\n");
-
-  for (i = 0; i < no_arg_formatted_cmd_nr; i++)
-    {
-      enum command_id cmd = no_arg_formatted_cmd.list[i];
-      /* prepare unicode numeric entities.  Freed at exit */
-      if (unicode_character_brace_no_arg_commands[cmd].codepoint)
-        {
-          unsigned long point_nr
-           = strtoul (unicode_character_brace_no_arg_commands[cmd].codepoint,
-                      NULL, 16);
-          char *entity;
-          xasprintf (&entity, "&#%lu;", point_nr);
-          unicode_entities[cmd] = entity;
-        }
-
-      /* css_strings */
-      if (cmd == CM_ASTERISK)
-        default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
-          = "\\A ";
-      else if (cmd == CM_error)
-        {
-          default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
-            = 0;
-          default_no_arg_commands_formatting[cmd][HCC_type_css_string].unset
-            = 1;
-        }
-      else if (unicode_character_brace_no_arg_commands[cmd].css_string)
-        {
-          unsigned long point_nr
-           = strtoul (unicode_character_brace_no_arg_commands[cmd].codepoint,
-                      NULL, 16);
-          if (point_nr < 128) /* 7bit ascii */
-            default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
-              = (char *)unicode_character_brace_no_arg_commands[cmd].text;
-          else
-          /* the value is never modified but the struct field type is not const
-             so need to cast to drop const */
-            default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
-              = (char 
*)unicode_character_brace_no_arg_commands[cmd].css_string;
-        }
-      else if (nobrace_symbol_text[cmd])
-        default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
-          /* the value is never modified but the struct field type is not const
-             so need to cast to drop const */
-          = (char *)nobrace_symbol_text[cmd];
-      else if (text_brace_no_arg_commands[cmd])
-          /* the value is never modified but the struct field type is not const
-             so need to cast to drop const */
-        default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
-          = (char *)text_brace_no_arg_commands[cmd];
-      else
-        fprintf (stderr, "BUG: %s: no css_string\n",
-                         builtin_command_data[cmd].cmdname);
-    }
+  text_append (result, special_unit_body.text);
+  free (special_unit_body.text);
+  text_append (result, "</div>");
 
-  /* w not in css_string, set the corresponding 
default_css_element_class_styles
-     especially, which also has none and not w in the class */
-  html_css_set_selector_style (&default_css_element_class_styles,
-                               "ul.mark-none", "list-style-type: none");
+  format_element_footer (self, unit_type, output_unit, content, unit_command,
+                         result);
+}
 
-  text_init (&css_string_text);
-  /* setup default_css_element_class_styles for mark commands based on
-     css strings */
-  for (i = 0; i < no_arg_formatted_cmd_nr; i++)
+void
+contents_shortcontents_in_title (CONVERTER *self, TEXT *result)
+{
+  if (self->document->sections_list
+      && self->document->sections_list->number > 0
+      && self->conf->CONTENTS_OUTPUT_LOCATION.o.string
+      && !strcmp (self->conf->CONTENTS_OUTPUT_LOCATION.o.string, 
"after_title"))
     {
-      enum command_id cmd = no_arg_formatted_cmd.list[i];
-      if (default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
-          && builtin_command_data[cmd].flags & CF_brace)
+      enum command_id contents_cmds[2] = {CM_shortcontents, CM_contents};
+      int i;
+      for (i = 0; i < 2; i++)
         {
-          char *selector;
-          text_append_n (&css_string_text, "list-style-type: ", 17);
-          if (cmd == CM_bullet)
-            text_append_n (&css_string_text, "disc", 4);
-          else
+          int contents_set = 0;
+          enum command_id cmd = contents_cmds[i];
+          const OPTION *contents_option_ref
+                           = get_command_option (self->conf, cmd);
+          if (contents_option_ref->o.integer > 0)
+            contents_set = 1;
+          if (contents_set)
             {
-              char *css_string;
-              const char *p;
-              char *special_list_mark_command
-                = get_special_list_mark_css_string_no_arg_command (cmd);
-              if (special_list_mark_command)
-                css_string = special_list_mark_command;
-              else
-                css_string
-        = default_no_arg_commands_formatting[cmd][HCC_type_css_string].text;
-              p = after_escaped_characters (css_string);
-              text_append_n (&css_string_text, "\"", 1);
-              if (p && !*p)
+              char *contents_text
+                = contents_inline_element (self, cmd, 0);
+              if (contents_text)
                 {
-                  text_append_n (&css_string_text, css_string,
-                                 p - css_string - 1);
+                  text_append (result, contents_text);
+                  if (self->conf->DEFAULT_RULE.o.string)
+                    {
+                      text_append (result, self->conf->DEFAULT_RULE.o.string);
+                      text_append_n (result, "\n", 1);
+                    }
+                  free (contents_text);
                 }
-              else
-                text_append (&css_string_text, css_string);
-              text_append_n (&css_string_text, "\"", 1);
             }
-          xasprintf (&selector, "ul.mark-%s", builtin_command_name (cmd));
-          html_css_set_selector_style (&default_css_element_class_styles,
-                                       selector, css_string_text.text);
-          free (selector);
-          text_reset (&css_string_text);
         }
     }
-  free (css_string_text.text);
 }
 
-static int
-register_type_conversion_function (TYPE_CONVERSION_FUNCTION *result,
-                                   enum element_type type,
-                                   FORMATTING_REFERENCE *formatting_reference)
+static void
+format_simpletitle (CONVERTER *self, TEXT *result)
 {
-  if (formatting_reference->status > 0)
-    {
-      result->status = formatting_reference->status;
-      if (formatting_reference->status != FRS_status_ignored)
-        {
-          result->type_conversion = &type_conversion_external;
-          result->formatting_reference = formatting_reference;
-          return 1;
-        }
-    }
-  return 0;
+  char *title_text;
+  char *context_str;
+  enum command_id cmd = self->simpletitle_cmd;
+
+  STRING_LIST *classes = new_string_list ();
+  add_string (builtin_command_name (cmd), classes);
+
+  xasprintf (&context_str, "%s simpletitle",
+             builtin_command_name (cmd));
+  title_text
+    = convert_tree_new_formatting_context (self,
+        self->simpletitle_tree, context_str, 0, 0, 0);
+  free (context_str);
+  format_heading_text (self, cmd, classes, title_text,
+                                    0, 0, 0, 0, result);
+  destroy_strings_list (classes);
+  free (title_text);
 }
 
-static int
-register_type_open_function (TYPE_OPEN_FUNCTION *result,
-                             enum element_type type,
-                             FORMATTING_REFERENCE *formatting_reference)
+/* Convert @titlepage.  Falls back to simpletitle. */
+char *
+html_default_format_titlepage (CONVERTER *self)
 {
-  if (formatting_reference->status > 0)
+  int titlepage_text = 0;
+  TEXT result;
+  text_init (&result);
+  text_append (&result, "");
+  if (self->document->global_commands.titlepage)
     {
-      result->status = formatting_reference->status;
-      if (formatting_reference->status != FRS_status_ignored)
-        {
-          result->type_open = &type_open_external;
-          result->formatting_reference = formatting_reference;
-          return 1;
-        }
+      ELEMENT *tmp = new_element (ET_NONE);
+      tmp->e.c->contents = 
self->document->global_commands.titlepage->e.c->contents;
+      convert_to_html_internal (self, tmp, &result, "convert titlepage");
+      tmp->e.c->contents.list = 0;
+      destroy_element (tmp);
+      titlepage_text = 1;
     }
-  return 0;
-}
-
-static int
-register_command_conversion_function (COMMAND_CONVERSION_FUNCTION *result,
-                         enum command_id cmd,
-                         FORMATTING_REFERENCE *formatting_reference)
-{
-  if (formatting_reference->status > 0)
+  else if (self->simpletitle_tree)
     {
-      result->status = formatting_reference->status;
-      if (formatting_reference->status != FRS_status_ignored)
-        {
-          result->command_conversion = &command_conversion_external;
-          result->formatting_reference = formatting_reference;
-          return 1;
-        }
+      format_simpletitle (self, &result);
+      titlepage_text = 1;
     }
-  return 0;
+  if (titlepage_text && self->conf->DEFAULT_RULE.o.string)
+    {
+      text_append (&result, self->conf->DEFAULT_RULE.o.string);
+      text_append_n (&result, "\n", 1);
+    }
+  contents_shortcontents_in_title (self, &result);
+  return result.text;
 }
 
-static int
-register_command_open_function (COMMAND_OPEN_FUNCTION *result,
-                                enum command_id cmd,
-                                FORMATTING_REFERENCE *formatting_reference)
+char *
+format_titlepage (CONVERTER *self)
 {
-  if (formatting_reference->status > 0)
+  const FORMATTING_REFERENCE *formatting_reference
+   = &self->current_formatting_references[FR_format_titlepage];
+  if (formatting_reference->status == FRS_status_default_set
+      || formatting_reference->status == FRS_status_none)
     {
-      result->status = formatting_reference->status;
-      if (formatting_reference->status != FRS_status_ignored)
-        {
-          result->command_open = &command_open_external;
-          result->formatting_reference = formatting_reference;
-          return 1;
-        }
+      return html_default_format_titlepage (self);
+    }
+  else
+    {
+      return call_formatting_function_format_titlepage (self,
+                                               formatting_reference);
     }
-  return 0;
 }
 
-static int
-register_output_unit_conversion_function
-                                  (OUTPUT_UNIT_CONVERSION_FUNCTION *result,
-                                   enum output_unit_type type,
-                                   FORMATTING_REFERENCE *formatting_reference)
+char *
+html_default_format_title_titlepage (CONVERTER *self)
 {
-  if (formatting_reference->status > 0)
+  if (self->conf->SHOW_TITLE.o.integer > 0)
     {
-      result->status = formatting_reference->status;
-      if (formatting_reference->status != FRS_status_ignored)
+      if (self->conf->USE_TITLEPAGE_FOR_TITLE.o.integer)
         {
-          result->output_unit_conversion = &output_unit_conversion_external;
-          result->formatting_reference = formatting_reference;
-          return 1;
+          return format_titlepage (self);
+        }
+      else
+        {
+          TEXT result;
+          text_init (&result);
+          text_append (&result, "");
+
+          if (self->simpletitle_tree)
+            format_simpletitle (self, &result);
+
+          contents_shortcontents_in_title (self, &result);
+          return result.text;
         }
     }
-  return 0;
+  return strdup ("");
 }
 
-static int
-register_special_unit_body_formatting_function
-                                  (SPECIAL_UNIT_BODY_FORMATTING *result,
-                                   const char *special_unit_variety,
-                                   FORMATTING_REFERENCE *formatting_reference)
+char *
+format_title_titlepage (CONVERTER *self)
 {
-  if (formatting_reference && formatting_reference->status > 0)
+  const FORMATTING_REFERENCE *formatting_reference
+   = &self->current_formatting_references[FR_format_title_titlepage];
+  if (formatting_reference->status == FRS_status_default_set
+      || formatting_reference->status == FRS_status_none)
     {
-      result->status = formatting_reference->status;
-      if (formatting_reference->status != FRS_status_ignored)
-        {
-          result->special_unit_body_formatting
-               = &special_unit_body_formatting_external;
-          result->formatting_reference = formatting_reference;
-          return 1;
-        }
+      return html_default_format_title_titlepage (self);
+    }
+  else
+    {
+      return call_formatting_function_format_title_titlepage (self,
+                                                      formatting_reference);
     }
-  return 0;
 }
 
-/* these constructors/initialization allow to use malloc from this
-   file and not from a file including Perl headers */
-
-/* NOTE relatively generic */
 void
-initialize_cmd_list (COMMAND_ID_LIST *cmd_list, size_t size, size_t number)
+html_special_unit_body_formatting_external (CONVERTER *self,
+                                    const size_t special_unit_number,
+                                    const char *special_unit_variety,
+                                    const OUTPUT_UNIT *output_unit,
+                                    TEXT *result)
 {
-  cmd_list->list = (enum command_id *) malloc
-    (size * sizeof (enum command_id));
-  cmd_list->number = number;
-  if (number)
-    {
-      memset (cmd_list->list, 0, number * sizeof (enum command_id));
-    }
+  if (self->special_unit_body[special_unit_number -1].status > 0)
+    call_special_unit_body_formatting (self, special_unit_number,
+                                       special_unit_variety,
+                                       output_unit, result);
 }
 
-HTMLXREF_MANUAL *
-new_htmlxref_manual_list (size_t size)
+void
+html_default_format_special_body_contents (CONVERTER *self,
+                                      const size_t special_unit_number,
+                                      const char *special_unit_variety,
+                                      const OUTPUT_UNIT *output_unit,
+                                      TEXT *result)
 {
-  HTMLXREF_MANUAL *result = (HTMLXREF_MANUAL *)
-        malloc (size * sizeof (HTMLXREF_MANUAL));
-  memset (result, 0, size * sizeof (HTMLXREF_MANUAL));
+  char *table_of_contents = format_contents (self, CM_contents, 0, 0);
+  text_append (result, table_of_contents);
+  free (table_of_contents);
+}
 
-  return result;
+void
+html_default_format_special_body_shortcontents (CONVERTER *self,
+                                        const size_t special_unit_number,
+                                        const char *special_unit_variety,
+                                        const OUTPUT_UNIT *output_unit,
+                                        TEXT *result)
+{
+  char *shortcontents = format_contents (self, CM_shortcontents, 0, 0);
+  text_append (result, shortcontents);
+  free (shortcontents);
 }
 
 void
-initialize_js_categories_list (JSLICENSE_CATEGORY_LIST *js_files_info,
-                              size_t size)
+html_default_format_special_body_footnotes (CONVERTER *self,
+                                       const size_t special_unit_number,
+                                       const char *special_unit_variety,
+                                       const OUTPUT_UNIT *output_unit,
+                                       TEXT *result)
 {
-  js_files_info->list = (JSLICENSE_FILE_INFO_LIST *)
-           malloc (size * sizeof (JSLICENSE_FILE_INFO_LIST));
-  memset (js_files_info->list, 0,
-                  size * sizeof (JSLICENSE_FILE_INFO_LIST));
-  js_files_info->number = size;
+  format_footnotes_sequence (self, result);
 }
 
+static char *direction_about_array[] = {"direction-about"};
+static const STRING_LIST direction_about_classes
+    = {direction_about_array, 1, 1};
+
+static char *button_direction_about_array[] = {"button-direction-about"};
+static const STRING_LIST button_direction_about_classes
+    = {button_direction_about_array, 1, 1};
+
+static char *name_direction_about_array[] = {"name-direction-about"};
+static const STRING_LIST name_direction_about_classes
+    = {name_direction_about_array, 1, 1};
+
+static char *description_direction_about_array[]
+    = {"description-direction-about"};
+static const STRING_LIST description_direction_about_classes
+    = {description_direction_about_array, 1, 1};
+
+static char *example_direction_about_array[] = {"example-direction-about"};
+static const STRING_LIST example_direction_about_classes
+    = {example_direction_about_array, 1, 1};
+
 void
-initialize_jslicense_files (JSLICENSE_FILE_INFO_LIST *jslicences_files_info,
-                            const char *category, size_t size)
+html_default_format_special_body_about (CONVERTER *self,
+                                        const size_t special_unit_number,
+                                        const char *special_unit_variety,
+                                        const OUTPUT_UNIT *output_unit,
+                                        TEXT *result)
 {
-  jslicences_files_info->category = strdup (category);
-  jslicences_files_info->list = (JSLICENSE_FILE_INFO *)
-              malloc (size * sizeof (JSLICENSE_FILE_INFO));
-  memset (jslicences_files_info->list, 0,
-          size * sizeof (JSLICENSE_FILE_INFO));
-  jslicences_files_info->number = size;
-}
+  size_t i;
+  const BUTTON_SPECIFICATION_LIST *buttons
+           = self->conf->SECTION_BUTTONS.o.buttons;
 
-HTML_DIRECTION_STRING_TRANSLATED *
-new_directions_strings_translated_type (int nr_string_directions)
-{
-  HTML_DIRECTION_STRING_TRANSLATED *result
-    = (HTML_DIRECTION_STRING_TRANSLATED *) malloc
-        (nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED));
-  memset (result, 0,
-          nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED));
-  return result;
-}
+  if (self->conf->PROGRAM_NAME_IN_ABOUT.o.integer > 0)
+    {
+      text_append_n (result, "<p>\n  ", 6);
+      format_program_string (self, result);
+      text_append_n (result, "\n</p>\n", 6);
+    }
 
-char ***
-new_directions_strings_type (int nr_string_directions,
-                             int nr_dir_str_contexts)
-{
-  int i;
-  char ***result = (char ***)
-        malloc (nr_string_directions * sizeof (char **));
-  memset (result, 0,
-          nr_string_directions * sizeof (char **));
+  text_append_n (result, "<p>\n", 4);
 
-  for (i = 0; i < nr_string_directions; i++)
+  if (!buttons)
     {
-      result[i] = (char **)
-         malloc (nr_dir_str_contexts * sizeof (char *));
-      memset (result[i], 0,
-              nr_dir_str_contexts * sizeof (char *));
+      translate_convert_to_html_internal (
+               "There are no buttons for this document.", self, 0, 0,
+                result, "ABOUT");
+      text_append_n (result, "\n</p>\n", 6);
+      return;
     }
 
-  return result;
-}
+  translate_convert_to_html_internal (
+   "  The buttons in the navigation panels have the following meaning:",
+                                      self, 0, 0, result, "ABOUT");
 
-FORMATTING_REFERENCE *
-new_special_unit_formatting_references (int special_units_varieties_nr)
-{
-  FORMATTING_REFERENCE *formatting_references = (FORMATTING_REFERENCE *)
-    malloc (special_units_varieties_nr * sizeof (FORMATTING_REFERENCE));
-  memset (formatting_references, 0,
-          special_units_varieties_nr * sizeof (FORMATTING_REFERENCE));
-  return formatting_references;
-}
+  text_append_n (result, "\n</p>\n", 6);
+  open_element_with_class (self, "table", &direction_about_classes, result);
+  text_append (result, "\n  <tr>\n    ");
+  open_element_with_class (self, "th", &button_direction_about_classes,
+                           result);
+  text_append_n (result, " ", 1);
+  translate_convert_to_html_internal ("Button", self, 0, 0, result, "ABOUT");
+  text_append_n (result, " </th>\n    ", 11);
+  open_element_with_class (self, "th", &name_direction_about_classes,
+                           result);
+  text_append_n (result, " ", 1);
+  translate_convert_to_html_internal ("Name", self, 0, 0, result, "ABOUT");
+  text_append_n (result, " </th>\n    ", 11);
+  open_element_with_class (self, "th", &description_direction_about_classes,
+                           result);
+  text_append_n (result, " ", 1);
+  translate_convert_to_html_internal ("Go to", self, 0, 0, result, "ABOUT");
+  text_append_n (result, " </th>\n    ", 11);
+  open_element_with_class (self, "th", &example_direction_about_classes,
+                           result);
+  text_append_n (result, " ", 1);
+  translate_convert_to_html_internal ("From 1.2.3 go to", self, 0, 0,
+                                      result, "ABOUT");
+  text_append (result, "</th>\n  </tr>\n");
 
-char **
-new_special_unit_info_type (int special_units_varieties_nr)
-{
-  char **special_unit_info = (char **)
-        malloc ((special_units_varieties_nr +1) * sizeof (char *));
-  memset (special_unit_info, 0,
-          (special_units_varieties_nr +1) * sizeof (char *));
-  return special_unit_info;
-}
+  for (i = 0; i < buttons->number; i++)
+    {
+      const BUTTON_SPECIFICATION *button = &buttons->list[i];
+      int direction = -1;
+      const char *button_name;
+      const char *button_description;
+      const char *button_example;
 
-void
-html_converter_init_special_unit (CONVERTER *self)
-{
-  size_t nr_special_units;
+      if (button->type == BST_direction_info)
+        direction = button->b.button_info->direction;
+      else if (button->type == BST_direction)
+        direction = button->b.direction;
 
-  /* NOTE if the special units can be customized, then
-     self->special_unit_varieties should be used directly instead.
-     Also default special units and special units indices should be
-     mapped instead of assuming that they are the same when setting
-     self->special_unit_info */
-  copy_strings (&self->special_unit_varieties, 
&default_special_unit_varieties);
+      if (direction < 0 || direction == D_direction_Space)
+        continue;
+
+      text_append_n (result, "  <tr>\n    ", 11);
+      open_element_with_class (self, "td", &button_direction_about_classes,
+                               result);
 
-  nr_special_units = self->special_unit_varieties.number;
+   /* if the button spec is an array we do not know what the button
+      looks like, so we do not show the button but still show explanations. */
 
-  /* special units info */
-  /* set to defaults */
-  if (nr_special_units > 0)
-    {
-      size_t i;
-      enum special_unit_info_type j;
-      for (j = 0; j < SPECIAL_UNIT_INFO_TYPE_NR; j++)
+      if (button->type == BST_direction)
         {
-          size_t k;
-
-          self->special_unit_info[j]
-            = new_special_unit_info_type (nr_special_units);
-          for (k = 0; k < nr_special_units; k++)
+          if (self->conf->ICONS.o.integer > 0
+              && self->conf->ACTIVE_ICONS.o.icons->number > 0
+              && self->conf->ACTIVE_ICONS.o.icons->list[direction]
+              && strlen (self->conf->ACTIVE_ICONS.o.icons->list[direction]))
             {
-              if (default_special_unit_info[j][k])
-                self->special_unit_info[j][k]
-                  = strdup (default_special_unit_info[j][k]);
+              const char *button_name_string
+                   = direction_string (self, direction,
+                                       TDS_type_button, TDS_context_string);
+              char *button = format_button_icon_img (self, button_name_string,
+                        self->conf->ACTIVE_ICONS.o.icons->list[direction], 0);
+              text_append (result, button);
+              free (button);
             }
-        }
-      /* apply customization */
-      for (i = 0; i < self->customized_special_unit_info.number; i++)
-        {
-          SPECIAL_UNIT_INFO *special_unit_info
-            = &self->customized_special_unit_info.list[i];
-          size_t variety_idx = special_unit_info->variety_nr -1;
-          enum special_unit_info_type type = special_unit_info->type;
-
-          free (self->special_unit_info[type][variety_idx]);
-
-          if (special_unit_info->value)
-            self->special_unit_info[type][variety_idx]
-              = strdup (special_unit_info->value);
           else
-            self->special_unit_info[type][variety_idx] = 0;
+            {
+              const char *button_text = direction_string (self, direction,
+                                                          TDS_type_text, 0);
+              text_append_n (result, " [", 2);
+              if (button_text)
+                text_append (result, button_text);
+              text_append_n (result, "] ", 2);
+            }
         }
+      text_append_n (result, "</td>\n    ", 10);
+      open_element_with_class (self, "td", &name_direction_about_classes,
+                               result);
+
+      button_name = direction_string (self, direction, TDS_type_button, 0);
+      if (button_name)
+        text_append (result, button_name);
+      text_append_n (result, "</td>\n    ", 10);
+      open_element_with_class (self, "td",
+                               &description_direction_about_classes,
+                               result);
+      button_description = direction_string (self, direction,
+                                             TDS_type_description, 0);
+      if (button_description)
+        text_append (result, button_description);
+      text_append_n (result, "</td>\n    ", 10);
+      open_element_with_class (self, "td", &example_direction_about_classes,
+                               result);
+      button_example = direction_string (self, direction, TDS_type_example, 0);
+      if (button_example)
+        text_append (result, button_example);
+      text_append_n (result, "</td>\n  </tr>\n", 14);
     }
+
+  text_append_n (result, "</table>\n\n<p>\n", 14);
+
+  translate_convert_to_html_internal (
+ "  where the @strong{ Example } assumes that the current position is at "
+ "@strong{ Subsubsection One-Two-Three } of a document of the following "
+ "structure:", self, 0, 0, result, "ABOUT");
+
+  text_append_n (result, "\n</p>\n\n<ul>\n", 12);
+  text_append (result, "  <li> 1. ");
+  translate_convert_to_html_internal ("Section One",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "\n    <ul>\n      <li>1.1 ");
+  translate_convert_to_html_internal ("Subsection One-One",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "\n        <ul>\n          <li>...</li>\n"
+     "        </ul>\n      </li>\n      <li>1.2 ");
+  translate_convert_to_html_internal ("Subsection One-Two",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "\n        <ul>\n          <li>1.2.1 ");
+  translate_convert_to_html_internal ("Subsubsection One-Two-One",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "</li>\n          <li>1.2.2 ");
+  translate_convert_to_html_internal ("Subsubsection One-Two-Two",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "</li>\n          <li>1.2.3 ");
+  translate_convert_to_html_internal ("Subsubsection One-Two-Three",
+                                      self, 0, 0, result, "ABOUT");
+  text_append_n (result, " ", 1);
+  text_append_n (result,
+                self->special_character[SC_non_breaking_space].string,
+                self->special_character[SC_non_breaking_space].len);
+  text_append_n (result, " ", 1);
+  text_append_n (result,
+                self->special_character[SC_non_breaking_space].string,
+                self->special_character[SC_non_breaking_space].len);
+  text_append_n (result, "\n", 1);
+
+  text_append (result, "            <strong>&lt;== ");
+  translate_convert_to_html_internal ("Current Position",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, " </strong></li>\n          <li>1.2.4 ");
+  translate_convert_to_html_internal ("Subsubsection One-Two-Four",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "</li>\n        </ul>\n      </li>\n      <li>1.3 ");
+  translate_convert_to_html_internal ("Subsection One-Three",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "\n        <ul>\n          <li>...</li>\n"
+  "        </ul>\n      </li>\n      <li>1.4 ");
+  translate_convert_to_html_internal ("Subsection One-Four",
+                                      self, 0, 0, result, "ABOUT");
+  text_append (result, "</li>\n    </ul>\n  </li>\n</ul>\n");
 }
 
-static int *
-determine_non_default_special_unit_directions (const CONVERTER *self)
+static const enum command_id simpletitle_cmds[] =
+ {CM_settitle, CM_shorttitlepage, 0};
+
+void
+html_prepare_simpletitle (CONVERTER *self)
 {
-  size_t i;
-  size_t nr_special_units = self->special_unit_varieties.number;
-  int *non_default_special_unit_directions = 0;
-  /* determine the special units directions that are not the same as
-     the default units directions.  If not the same as the defaults,
-     the default direction info should not be used as they are not for
-     the customized special unit direction */
-  if (nr_special_units > 0)
+  int i;
+  for (i = 0; simpletitle_cmds[i]; i++)
     {
-      non_default_special_unit_directions = (int *)
-                      malloc (nr_special_units * sizeof (int));
-      memset (non_default_special_unit_directions, 0,
-              nr_special_units * sizeof (int));
-
-      for (i = 0; i < nr_special_units; i++)
+      enum command_id cmd = simpletitle_cmds[i];
+      const ELEMENT *command
+        = get_cmd_global_uniq_command (&self->document->global_commands, cmd);
+      if (command && command->e.c->args.number > 0
+          && command->e.c->args.list[0]->e.c->contents.number > 0)
         {
-          if (strcmp (self->special_unit_info[SUI_type_direction][i],
-                  default_special_unit_info[SUI_type_direction][i]))
-            non_default_special_unit_directions[i] = 1;
+          self->simpletitle_tree = command->e.c->args.list[0];
+          self->simpletitle_cmd = cmd;
+          break;
         }
     }
-  return non_default_special_unit_directions;
 }
 
-/* When initializing from Perl, html_converter_initialize_sv is called first
-   to set customization information based on Perl data.  Then this function
-   is called for the remainder of initialization that requires customization
-   information.
- */
-void
-html_converter_customize (CONVERTER *self)
-{
-  size_t i;
-  int l;
-  enum direction_string_type DS_type;
-  size_t nr_special_units = self->special_unit_varieties.number;
-  /* The corresponding direction without FirstInFile are used instead
-     of FirstInFile*, so the directions_strings are not set */
-  int nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
-                             + (int) nr_special_units;
-  int nr_dir_str_contexts = TDS_context_string +1;
-  int *non_default_special_unit_directions =
-     determine_non_default_special_unit_directions (self);
-
-  /* counters of external formatting functions */
-  int external_special_unit_body_formatting_function = 0;
-  int external_output_unit_conversion_function = 0;
-  int external_command_conversion_function = 0;
-  int external_command_open_function = 0;
-  int external_type_conversion_function = 0;
-  int external_type_open_function = 0;
-  int external_formatting_function = 0;
+static const enum command_id fulltitle_cmds[] =
+ {CM_settitle, CM_title, CM_shorttitlepage, CM_top, 0};
 
-  if (self->ids_data_type == IDT_perl_hashmap)
-    init_registered_ids_hv (self);
-  else
-    self->registered_ids = new_string_list ();
 
-  /* for @sc */
-  for (l = 0; default_upper_case_commands[l]; l++)
-    self->upper_case[default_upper_case_commands[l]] = 1;
+int
+html_prepare_converted_output_info (CONVERTER *self, const char *output_file,
+                                    const char *output_filename)
+{
+  int i;
+  ELEMENT *fulltitle_tree = 0;
+  char *html_title_string = 0;
+  char *default_document_language = 0;
+  char *preamble_document_language = 0;
+  int init_handler_status;
+  int handler_fatal_error_level
+     = self->conf->HANDLER_FATAL_ERROR_LEVEL.o.integer;
+
+  int structure_handler_status = run_stage_handlers (self, 
HSHT_type_structure);
 
-  for (l = 0; default_code_types[l]; l++)
-    self->code_types[default_code_types[l]] = 1;
+  if (structure_handler_status < handler_fatal_error_level
+      && structure_handler_status > -handler_fatal_error_level)
+    {}
+  else
+    return 0;
 
-  for (l = 0; default_pre_class_types[l].type; l++)
-    {
-      const PRE_CLASS_TYPE_INFO *pre_class_type = &default_pre_class_types[l];
-      self->pre_class_types[pre_class_type->type]
-        = strdup (pre_class_type->pre_class);
-    }
+  if (self->conf->documentlanguage.o.string)
+    default_document_language = strdup (self->conf->documentlanguage.o.string);
 
-  for (l = 0; xml_accent_text_entities[l].cmd; l++)
-    {
-      enum command_id cmd = xml_accent_text_entities[l].cmd;
-      const ACCENT_ENTITY_INFO *xml_accent_info
-        = &xml_accent_text_entities[l].accent_entity_info;
-      ACCENT_ENTITY_INFO *accent_info = &self->accent_entities[cmd];
+  set_global_document_commands (self, CL_preamble, conf_for_documentlanguage);
 
-      if (xml_accent_info->entity)
-        accent_info->entity = strdup (xml_accent_info->entity);
-      if (xml_accent_info->characters)
-        accent_info->characters = strdup (xml_accent_info->characters);
-    }
+  if (self->conf->documentlanguage.o.string)
+    preamble_document_language = strdup 
(self->conf->documentlanguage.o.string);
 
-  for (i = 0; i < style_formatted_cmd.number; i++)
-    {
-      enum command_id cmd = style_formatted_cmd.list[i];
-      enum conversion_context cctx;
-      for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
-        {
-          HTML_STYLE_COMMAND_CONVERSION *format_spec
-            = &self->html_style_command_conversion[cmd][cctx];
-          HTML_STYLE_COMMAND_CONVERSION *default_spec
-            = &default_style_commands_formatting[cmd][cctx];
+  if (! (!default_document_language && !preamble_document_language)
+      && (!default_document_language || !preamble_document_language
+          || strcmp (default_document_language, preamble_document_language)))
+    html_translate_names (self);
 
-          if (default_spec->element)
-            format_spec->element = strdup (default_spec->element);
-          format_spec->quote = default_spec->quote;
-        }
-    }
+  /*
+   prepare title.  fulltitle uses more possibility than simpletitle for
+   title, including @-commands found in @titlepage only.  Therefore
+   simpletitle is more in line with what makeinfo in C did.
+   */
 
-  /* apply customization (from Perl) */
+  html_prepare_simpletitle (self);
 
-  if (self->html_customized_upper_case_commands)
+  for (i = 0; fulltitle_cmds[i]; i++)
     {
-      for (l = 0; self->html_customized_upper_case_commands[l].cmd; l++)
+      enum command_id cmd = fulltitle_cmds[i];
+      const ELEMENT *command
+        = get_cmd_global_uniq_command (&self->document->global_commands, cmd);
+      if (command && command->e.c->args.number > 0
+          && command->e.c->args.list[0]->e.c->contents.number > 0)
         {
-          COMMAND_INTEGER_INFORMATION *customized_upper
-            = &self->html_customized_upper_case_commands[l];
-          self->upper_case[customized_upper->cmd] = customized_upper->integer;
+          fulltitle_tree = command->e.c->args.list[0];
+          break;
         }
     }
 
-  if (self->html_customized_code_types)
+  if (!fulltitle_tree
+      && self->document->global_commands.titlefont.number > 0
+      && self->document->global_commands.titlefont.list[0]->e.c->args.number > 0
+      && self->document->global_commands.titlefont.list[0]->e.c->args.list[0]
+                                    ->e.c->contents.number > 0)
     {
-      for (l = 0; self->html_customized_code_types[l].type; l++)
-        {
-          TYPE_INTEGER_INFORMATION *customized_code
-            = &self->html_customized_code_types[l];
-          self->code_types[customized_code->type] = customized_code->integer;
-        }
+      fulltitle_tree = self->document->global_commands.titlefont.list[0];
     }
 
-  if (self->html_customized_pre_class_types)
+  if (fulltitle_tree)
     {
-      for (l = 0; self->html_customized_pre_class_types[l].type; l++)
-        {
-          PRE_CLASS_TYPE_INFO *customized_pre_class
-            = &self->html_customized_pre_class_types[l];
-          enum element_type type = customized_pre_class->type;
-
-          free (self->pre_class_types[type]);
-          self->pre_class_types[type]
-             = strdup (customized_pre_class->pre_class);
-        }
-    }
+      self->title_tree = fulltitle_tree;
 
-  if (self->html_customized_accent_entity_info)
-    {
-      for (l = 0; self->html_customized_accent_entity_info[l].cmd; l++)
+      html_title_string = convert_string_tree_new_formatting_context (self,
+                                       fulltitle_tree, "title_string", 0);
+      if (html_title_string[strspn (html_title_string, whitespace_chars)]
+           == '\0')
         {
-          enum command_id cmd = 
self->html_customized_accent_entity_info[l].cmd;
-          const ACCENT_ENTITY_INFO *custom_accent_info
-            = &self->html_customized_accent_entity_info[l].accent_entity_info;
-          ACCENT_ENTITY_INFO *accent_info = &self->accent_entities[cmd];
-
-          if (custom_accent_info->entity)
-            {
-              free (accent_info->entity);
-              if (strlen (custom_accent_info->entity))
-                accent_info->entity = strdup (custom_accent_info->entity);
-              else
-                accent_info->entity = 0;
-            }
-          if (custom_accent_info->characters)
-            {
-              free (accent_info->characters);
-              accent_info->characters = strdup 
(custom_accent_info->characters);
-            }
+          free (html_title_string);
+          html_title_string = 0;
         }
     }
 
-  if (self->html_customized_style_commands)
+  if (!html_title_string)
     {
-      for (l = 0; self->html_customized_style_commands[l].cmd; l++)
-        {
-          enum conversion_context cctx;
-          enum command_id cmd = self->html_customized_style_commands[l].cmd;
-          /* should not happen thanks to checks in perl
-          if (!(html_commands_data[cmd].flags & HF_style_command))
-            fprintf (stderr, "ERROR: %s: customized as style command\n",
-                     builtin_command_name (cmd));
-           */
-
-          for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
-            {
-              if (self->html_customized_style_commands[l].conversion[cctx])
-                {
-                  HTML_STYLE_COMMAND_CONVERSION *format_spec
-                    = &self->html_style_command_conversion[cmd][cctx];
-                  HTML_STYLE_COMMAND_CONVERSION *custom_spec
-                    = self->html_customized_style_commands[l].conversion[cctx];
-
-                  free (format_spec->element);
-
-                  if (custom_spec->element)
-                    format_spec->element = strdup (custom_spec->element);
-                  else
-                    format_spec->element = 0;
-                  format_spec->quote = custom_spec->quote;
-                }
-            }
-        }
-    }
-
-  /* initialization needing some information not available before.  Besides
-     customized information, mainly nr_special_units, which we
-     pretend could be customize (even though it cannot for now) */
+      ELEMENT *default_title = html_cdt_tree ("Untitled Document",
+                                              self, 0, 0);
+      SOURCE_INFO cmd_source_info;
 
-  self->direction_unit_direction_name = (const char **) malloc
-     ((nr_special_units + NON_SPECIAL_DIRECTIONS_NR +1) * sizeof (char *));
-  memcpy (self->direction_unit_direction_name, html_button_direction_names,
-          NON_SPECIAL_DIRECTIONS_NR * sizeof (char *));
-  memcpy (self->direction_unit_direction_name + NON_SPECIAL_DIRECTIONS_NR,
-          self->special_unit_info[SUI_type_direction],
-          nr_special_units * sizeof (char *));
-  self->direction_unit_direction_name[
-               nr_special_units + NON_SPECIAL_DIRECTIONS_NR] = 0;
-  /*
-  for (l = 0; self->direction_unit_direction_name[l]; l++)
-    fprintf (stderr, "DEBUG: direction unit names: %d '%s'\n", l,
-             self->direction_unit_direction_name[l]);
-   */
+      self->title_tree = default_title;
 
+      html_title_string = convert_string_tree_new_formatting_context (self,
+                                       default_title, "title_string", 0);
 
+      self->added_title_tree = 1;
 
-  /* setup translated_direction_strings */
-  for (DS_type = 0; DS_type < TDS_TRANSLATED_MAX_NR; DS_type++)
-    {
-      self->translated_direction_strings[DS_type]
-        = new_directions_strings_translated_type (nr_string_directions);
-      for (l = 0; l < nr_string_directions; l++)
+      if (self->document->global_info.input_file_name)
         {
-          HTML_DIRECTION_STRING_TRANSLATED *dir_string_translated
-            = &self->translated_direction_strings[DS_type][l];
-          if (self->customized_translated_direction_strings[DS_type]
-              && self->customized_translated_direction_strings[DS_type][l])
-            {
-              HTML_DIRECTION_STRING_TRANSLATED *custom_dir_translated
-                = self->customized_translated_direction_strings[DS_type][l];
-              if (custom_dir_translated->to_convert)
-                dir_string_translated->to_convert
-                  = strdup (custom_dir_translated->to_convert);
-              else
-                {
-                  int j;
-
-                  for (j = 0; j < nr_dir_str_contexts; j++)
-                    if (custom_dir_translated->converted[j])
-                      dir_string_translated->converted[j]
-                        = strdup (custom_dir_translated->converted[j]);
-                }
-            }
-          else if (l < NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
-                  || !non_default_special_unit_directions[
-                       l - (NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR)])
-            {
-              const HTML_DEFAULT_DIRECTION_STRING_TRANSLATED 
*default_dir_translated
-                = &default_translated_directions_strings[DS_type][l];
-              if (default_dir_translated->to_convert)
-                dir_string_translated->to_convert
-                  = strdup (default_dir_translated->to_convert);
-              else
-                {
-                  if (default_dir_translated->converted)
-                    {
-                      int j;
-
-                      for (j = 0; j < nr_dir_str_contexts; j++)
-                          dir_string_translated->converted[j]
-                            = strdup (default_dir_translated->converted);
-                    }
-                }
-            }
+          /* setup a source info with file only */
+          memset (&cmd_source_info, 0, sizeof (SOURCE_INFO));
+          cmd_source_info.file_name
+           = self->document->global_info.input_file_name;
+          /* this is more in line with the Perl function used, as DEBUG is
+             checked in the called function */
+          message_list_line_error_ext (&self->error_messages, self->conf,
+                                  MSG_warning, 0, &cmd_source_info,
+                      "must specify a title with a title command or @top");
+        }
+      else
+        {
+          message_list_document_warn (&self->error_messages, self->conf, 0,
+                      "must specify a title with a title command or @top");
         }
     }
 
-  free (non_default_special_unit_directions);
-
-
-  /* allocate space for translated tree types, they will be created
-     on-demand during the conversion */
-  for (l = 0; l < SUIT_type_heading+1; l++)
-    {
-      self->special_unit_info_tree[l] = (ELEMENT **)
-        malloc ((nr_special_units +1) * sizeof (ELEMENT *));
-      memset (self->special_unit_info_tree[l], 0,
-               (nr_special_units +1) * sizeof (ELEMENT *));
-    }
-
-  self->global_units_directions
-    = (const OUTPUT_UNIT **) malloc ((D_Last + nr_special_units+1)
-                               * sizeof (OUTPUT_UNIT));
+  self->title_string = html_title_string;
 
-  /* note that we allocate the same size as no_arg_formatted_cmd
-     even though in general there are much less translated commands,
-     for simplicity */
-  if (no_arg_formatted_cmd.number)
-    {
-      self->no_arg_formatted_cmd_translated.list = (enum command_id *)
-       malloc (no_arg_formatted_cmd.number * sizeof (enum command_id));
-      memset (self->no_arg_formatted_cmd_translated.list, 0,
-              no_arg_formatted_cmd.number * sizeof (enum command_id));
-    }
+  /* copying comment */
 
-  for (l = 0; command_special_unit_variety[l].cmd; l++)
+  if (self->document->global_commands.copying)
     {
-      char *special_unit_variety = command_special_unit_variety[l].variety;
-      /* number is index +1 */
-      size_t number = find_string (&self->special_unit_varieties,
-                                   special_unit_variety);
-      enum command_id cmd = command_special_unit_variety[l].cmd;
-      html_commands_data[cmd].flags |= HF_special_variety;
-      self->command_special_variety_name_index[l].cmd = cmd;
-      self->command_special_variety_name_index[l].index = number - 1;
-    }
+      char *copying_comment;
+      ELEMENT *tmp = new_element (ET_NONE);
 
-  for (l = 0; ignored_types[l]; l++)
-    {
-      enum element_type type = ignored_types[l];
-      self->type_conversion_function[type].status = FRS_status_ignored;
-    }
+      tmp->e.c->contents = 
self->document->global_commands.copying->e.c->contents;
 
-  for (l = 0; l < TXI_TREE_TYPES_NUMBER; l++)
-    {
-      int status = register_type_conversion_function (
-                                        &self->type_conversion_function[l],
-                                        l, &self->types_conversion[l]);
-      external_type_conversion_function += status;
+      copying_comment = convert_to_text (tmp, self->convert_text_options);
 
-      status = register_type_open_function (&self->type_open_function[l],
-                                   l, &self->types_open[l]);
-      external_type_open_function += status;
+      tmp->e.c->contents.list = 0;
+      destroy_element (tmp);
 
-      register_type_conversion_function (
-             &self->css_string_type_conversion_function[l], l,
-             &self->css_string_types_conversion[l]);
+      if (copying_comment && strlen (copying_comment) > 0)
+        {
+          self->copying_comment = format_comment (self, copying_comment);
+        }
+      free (copying_comment);
     }
 
-  for (l = 0; l < BUILTIN_CMD_NUMBER; l++)
+  /* documentdescription */
+  if (self->conf->documentdescription.o.string)
+    self->documentdescription_string
+     = strdup (self->conf->documentdescription.o.string);
+  else if (self->document->global_commands.documentdescription)
     {
-      int status = register_command_conversion_function (
-                            &self->command_conversion_function[l],
-                            l, &self->commands_conversion[l]);
-      external_command_conversion_function += status;
+      ELEMENT *tmp = new_element (ET_NONE);
+      char *documentdescription_string;
+      size_t documentdescription_string_len;
 
-      status = register_command_open_function (
-                            &self->command_open_function[l],
-                            l, &self->commands_open[l]);
-      external_command_open_function += status;
+      tmp->e.c->contents
+        = self->document->global_commands.documentdescription->e.c->contents;
 
-      register_command_conversion_function (
-            &self->css_string_command_conversion_function[l], l,
-             &self->css_string_commands_conversion[l]);
-    }
+      documentdescription_string
+                 = convert_string_tree_new_formatting_context (self,
+                                       tmp, "documentdescription", 0);
 
-  for (l = 0; l < OU_special_unit+1; l++)
-    {
-      int status = register_output_unit_conversion_function
-                                  (&self->output_unit_conversion_function[l],
-                                        l, &self->output_units_conversion[l]);
-      external_output_unit_conversion_function += status;
-    }
+      tmp->e.c->contents.list = 0;
+      destroy_element (tmp);
 
-  self->special_unit_body_formatting = (SPECIAL_UNIT_BODY_FORMATTING *)
-    malloc (nr_special_units * sizeof (SPECIAL_UNIT_BODY_FORMATTING));
-  memset (self->special_unit_body_formatting, 0,
-          nr_special_units * sizeof (SPECIAL_UNIT_BODY_FORMATTING));
+      documentdescription_string_len = strlen (documentdescription_string);
+      if (documentdescription_string_len > 0
+          && documentdescription_string[documentdescription_string_len -1]
+             == '\n')
+        documentdescription_string[documentdescription_string_len -1] = '\0';
 
-  for (i = 0; i < nr_special_units; i++)
-    {
-      int status;
-      FORMATTING_REFERENCE *formatting_reference = 0;
-      if (self->special_unit_body)
-        {
-          formatting_reference = &self->special_unit_body[i];
-        }
-      status = register_special_unit_body_formatting_function
-                                  (&self->special_unit_body_formatting[i],
-          self->special_unit_varieties.list[i], formatting_reference);
-      external_special_unit_body_formatting_function += status;
+      self->documentdescription_string = documentdescription_string;
     }
 
-  qsort (self->htmlxref.list, self->htmlxref.number,
-         sizeof (HTMLXREF_MANUAL), compare_htmlxref_manual);
+  init_handler_status = run_stage_handlers (self, HSHT_type_init);
 
-  /* set to customization such that it is not replaced by C functions */
-  if (self->conf->XS_EXTERNAL_FORMATTING.o.integer > 0)
-    {
-      for (l = 0; l < FR_format_translate_message+1; l++)
-        if (self->formatting_references[l].status == FRS_status_default_set)
-          {
-            self->formatting_references[l].status
-                                   = FRS_status_customization_set;
-            external_formatting_function++;
-          }
-    }
+  if (init_handler_status < handler_fatal_error_level
+      && init_handler_status > -handler_fatal_error_level)
+    {}
   else
-    { /* count the functions implemented in perl */
-      for (l = 0; l < FR_format_translate_message+1; l++)
-        if (self->formatting_references[l].status
-                  == FRS_status_customization_set)
-          external_formatting_function++;
-    }
+    {
+      free (default_document_language);
+      free (preamble_document_language);
 
-  /* remaining of the file is for the replacement of call to external
-     functions by internal functions in C.
-   */
-  if (self->conf->XS_EXTERNAL_CONVERSION.o.integer > 0)
-    goto out;
-
-  for (i = 0; types_internal_conversion_table[i].type_conversion; i++)
-    {
-      enum element_type type = types_internal_conversion_table[i].type;
-      TYPE_CONVERSION_FUNCTION *type_conversion
-         = &self->type_conversion_function[type];
-      TYPE_CONVERSION_FUNCTION *css_string_type_conversion
-         = &self->css_string_type_conversion_function[type];
-      if (type_conversion->status == FRS_status_default_set
-          || type_conversion->status == FRS_status_none)
-        {
-          type_conversion->formatting_reference = 0;
-          type_conversion->status = FRS_status_internal;
-          type_conversion->type_conversion
-              = types_internal_conversion_table[i].type_conversion;
-          if (type_conversion->status == FRS_status_default_set)
-            external_type_conversion_function--;
-        }
-      css_string_type_conversion->formatting_reference = 0;
-      css_string_type_conversion->status = FRS_status_internal;
-      if (type == ET_text)
-        css_string_type_conversion->type_conversion
-          = &html_css_string_convert_text;
-      else
-        css_string_type_conversion->type_conversion
-          = types_internal_conversion_table[i].type_conversion;
+      return 0;
     }
 
-  for (i = 0; types_internal_open_table[i].type_open; i++)
-    {
-      enum element_type type = types_internal_open_table[i].type;
-      TYPE_OPEN_FUNCTION *type_open = &self->type_open_function[type];
-      if (type_open->status == FRS_status_default_set
-          || type_open->status == FRS_status_none)
-        {
-          type_open->formatting_reference = 0;
-          type_open->status = FRS_status_internal;
-          type_open->type_open
-              = types_internal_open_table[i].type_open;
-          if (type_open->status == FRS_status_default_set)
-            external_type_open_function--;
-        }
-    }
+  html_prepare_title_titlepage (self, output_file, output_filename);
 
-  for (i = 0; commands_internal_conversion_table[i].command_conversion; i++)
-    {
-      enum command_id cmd = commands_internal_conversion_table[i].cmd;
-      COMMAND_CONVERSION_FUNCTION *command_conversion
-               = &self->command_conversion_function[cmd];
-      COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
-               = &self->css_string_command_conversion_function[cmd];
-      if (command_conversion->status == FRS_status_default_set
-          || command_conversion->status == FRS_status_none)
-        {
-          command_conversion->formatting_reference = 0;
-          command_conversion->status = FRS_status_internal;
-          command_conversion->command_conversion
-              = commands_internal_conversion_table[i].command_conversion;
-          if (command_conversion->status == FRS_status_default_set)
-            external_command_conversion_function--;
-        }
-      css_string_command_conversion->formatting_reference = 0;
-      css_string_command_conversion->status = FRS_status_internal;
-      css_string_command_conversion->command_conversion
-              = commands_internal_conversion_table[i].command_conversion;
-    }
+  set_global_document_commands (self, CL_before, conf_for_documentlanguage);
 
-  if (strcmp (self->conf->FORMAT_MENU.o.string, "menu"))
-    {
-      static enum command_id menu_cmd_list[]
-       = {CM_menu, CM_detailmenu, 0};
-      int j;
-      for (j = 0; menu_cmd_list[j]; j++)
-        {
-          enum command_id cmd = menu_cmd_list[j];
-          COMMAND_CONVERSION_FUNCTION *command_conversion
-               = &self->command_conversion_function[cmd];
-          if (command_conversion->status == FRS_status_internal)
-            command_conversion->status = FRS_status_ignored;
-        }
-    }
-  for (i = 0; i < format_raw_cmd.number; i++)
-    {
-      enum command_id cmd = format_raw_cmd.list[i];
-      COMMAND_CONVERSION_FUNCTION *command_conversion
-        = &self->command_conversion_function[cmd];
-      if (command_conversion->status == FRS_status_internal)
-        {
-          const char *format = builtin_command_name (cmd);
-          if (!format_expanded_p (self->expanded_formats, format))
-            command_conversion->status = FRS_status_ignored;
-        }
-    }
+  if (! (!default_document_language && !preamble_document_language)
+      && (!default_document_language || !preamble_document_language
+          || strcmp (default_document_language, preamble_document_language)))
+    html_translate_names (self);
 
-  /* all the no arg formatted commands are implemented in C */
-  for (i = 0; i < no_arg_formatted_cmd.number; i++)
-    {
-      enum command_id cmd = no_arg_formatted_cmd.list[i];
-      COMMAND_CONVERSION_FUNCTION *command_conversion
-           = &self->command_conversion_function[cmd];
-      COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
-           = &self->css_string_command_conversion_function[cmd];
-      if (command_conversion->status == FRS_status_default_set
-          || command_conversion->status == FRS_status_none)
-        {
-          command_conversion->formatting_reference = 0;
-          command_conversion->status = FRS_status_internal;
-          command_conversion->command_conversion
-            = &convert_no_arg_command;
-          if (command_conversion->status == FRS_status_default_set)
-            external_command_conversion_function--;
-        }
+  free (default_document_language);
+  free (preamble_document_language);
 
-      css_string_command_conversion->formatting_reference = 0;
-      css_string_command_conversion->status = FRS_status_internal;
-      css_string_command_conversion->command_conversion
-        = &css_string_convert_no_arg_command;
-    }
+  return 1;
+}
 
-  /* accents commands implemented in C */
-  if (accent_cmd.number)
+void
+reset_translated_special_unit_info_tree (CONVERTER *self)
+{
+  STRING_LIST *special_unit_varieties = &self->special_unit_varieties;
+  int j;
+  for (j = 0; translated_special_unit_info[j].tree_type != SUIT_type_none; j++)
     {
-      for (i = 0; i < accent_cmd.number; i++)
+      size_t i;
+      enum special_unit_info_tree tree_type
+        = translated_special_unit_info[j].tree_type;
+      for (i = 0; i < special_unit_varieties->number; i++)
         {
-          enum command_id cmd = accent_cmd.list[i];
-          COMMAND_CONVERSION_FUNCTION *command_conversion
-               = &self->command_conversion_function[cmd];
-          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
-               = &self->css_string_command_conversion_function[cmd];
-          if (command_conversion->status == FRS_status_default_set
-              || command_conversion->status == FRS_status_none)
+          if (self->special_unit_info_tree[tree_type][i])
             {
-              command_conversion->formatting_reference = 0;
-              command_conversion->status = FRS_status_internal;
-              command_conversion->command_conversion
-                = &convert_accent_command;
-              if (command_conversion->status == FRS_status_default_set)
-                external_command_conversion_function--;
-            }
-          css_string_command_conversion->formatting_reference = 0;
-          css_string_command_conversion->status = FRS_status_internal;
-          css_string_command_conversion->command_conversion
-            = &css_string_convert_accent_command;
-        }
-    }
-
-  /* all the commands in style_formatted_cmd are implemented in C.
-     It is not only the style commands, some others too.  indicateurl
-     is not in style_formatted_cmd for now either */
-  if (style_formatted_cmd.number)
-    {
-      for (i = 0; i < style_formatted_cmd.number; i++)
-        {
-          enum command_id cmd = style_formatted_cmd.list[i];
-          COMMAND_CONVERSION_FUNCTION *command_conversion
-               = &self->command_conversion_function[cmd];
-          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
-               = &self->css_string_command_conversion_function[cmd];
+              remove_tree_to_build (self,
+                             self->special_unit_info_tree[tree_type][i]);
+              destroy_element_and_children (
+                self->special_unit_info_tree[tree_type][i]);
 
-          if (command_conversion->status == FRS_status_default_set
-              || command_conversion->status == FRS_status_none)
-            {
-              command_conversion->formatting_reference = 0;
-              command_conversion->status = FRS_status_internal;
-              command_conversion->command_conversion
-                = &convert_style_command;
-              if (command_conversion->status == FRS_status_default_set)
-                external_command_conversion_function--;
             }
-
-          css_string_command_conversion->formatting_reference = 0;
-          css_string_command_conversion->status = FRS_status_internal;
-          css_string_command_conversion->command_conversion
-            = &convert_style_command;
+          self->special_unit_info_tree[tree_type][i] = 0;
         }
     }
+}
 
-  /* preformatted commands are implemented in C */
-  if (preformatted_cmd_list.top > 0)
-    {
-      for (i = 0; i < preformatted_cmd_list.top; i++)
-        {
-          enum command_id cmd = preformatted_cmd_list.stack[i];
-          COMMAND_CONVERSION_FUNCTION *command_conversion
-               = &self->command_conversion_function[cmd];
-          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
-               = &self->css_string_command_conversion_function[cmd];
-
-          if (command_conversion->status == FRS_status_default_set
-              || command_conversion->status == FRS_status_none)
-            {
-              command_conversion->formatting_reference = 0;
-              command_conversion->status = FRS_status_internal;
-              command_conversion->command_conversion
-                = &convert_preformatted_command;
-              if (command_conversion->status == FRS_status_default_set)
-                external_command_conversion_function--;
-            }
-
-          css_string_command_conversion->formatting_reference = 0;
-          css_string_command_conversion->status = FRS_status_internal;
-          css_string_command_conversion->command_conversion
-            = &convert_preformatted_command;
-        }
-    }
-  /* def commands are implemented in C */
-  if (def_cmd_list.top > 0)
-    {
-      for (i = 0; i < def_cmd_list.top; i++)
-        {
-          enum command_id cmd = def_cmd_list.stack[i];
-          COMMAND_CONVERSION_FUNCTION *command_conversion
-               = &self->command_conversion_function[cmd];
-          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
-               = &self->css_string_command_conversion_function[cmd];
 
-          if (command_conversion->status == FRS_status_default_set
-              || command_conversion->status == FRS_status_none)
-            {
-              command_conversion->formatting_reference = 0;
-              command_conversion->status = FRS_status_internal;
-              command_conversion->command_conversion
-                = &convert_def_command;
-              if (command_conversion->status == FRS_status_default_set)
-                external_command_conversion_function--;
-            }
+/* these constructors/initialization allow to use malloc from this
+   file and not from a file including Perl headers */
 
-          css_string_command_conversion->formatting_reference = 0;
-          css_string_command_conversion->status = FRS_status_internal;
-          css_string_command_conversion->command_conversion
-            = &convert_def_command;
-        }
-    }
+HTMLXREF_MANUAL *
+new_htmlxref_manual_list (size_t size)
+{
+  HTMLXREF_MANUAL *result = (HTMLXREF_MANUAL *)
+        malloc (size * sizeof (HTMLXREF_MANUAL));
+  memset (result, 0, size * sizeof (HTMLXREF_MANUAL));
 
-  for (i = 0; commands_internal_open_table[i].command_open; i++)
-    {
-      enum command_id cmd = commands_internal_open_table[i].cmd;
-      COMMAND_OPEN_FUNCTION *command_open = &self->command_open_function[cmd];
-      if (command_open->status == FRS_status_default_set
-          || command_open->status == FRS_status_none)
-        {
-          command_open->formatting_reference = 0;
-          command_open->status = FRS_status_internal;
-          command_open->command_open
-              = commands_internal_open_table[i].command_open;
-          if (command_open->status == FRS_status_default_set)
-            external_command_open_function--;
-        }
-    }
+  return result;
+}
 
-  for (i = 0;
-     output_units_internal_conversion_table[i].output_unit_conversion; i++)
-    {
-      enum output_unit_type type
-           = output_units_internal_conversion_table[i].type;
-      OUTPUT_UNIT_CONVERSION_FUNCTION *output_unit_conversion
-         = &self->output_unit_conversion_function[type];
-      if (output_unit_conversion->status == FRS_status_default_set
-          || output_unit_conversion->status == FRS_status_none)
-        {
-          output_unit_conversion->formatting_reference = 0;
-          output_unit_conversion->status = FRS_status_internal;
-          output_unit_conversion->output_unit_conversion
-           = output_units_internal_conversion_table[i].output_unit_conversion;
-          if (output_unit_conversion->status == FRS_status_default_set)
-            external_output_unit_conversion_function--;
-        }
-    }
+void
+initialize_js_categories_list (JSLICENSE_CATEGORY_LIST *js_files_info,
+                              size_t size)
+{
+  js_files_info->list = (JSLICENSE_FILE_INFO_LIST *)
+           malloc (size * sizeof (JSLICENSE_FILE_INFO_LIST));
+  memset (js_files_info->list, 0,
+                  size * sizeof (JSLICENSE_FILE_INFO_LIST));
+  js_files_info->number = size;
+}
 
-  for (i = 0;
-    special_unit_body_internal_formatting_table[i].special_unit_variety; i++)
-    {
-      const SPECIAL_UNIT_BODY_INTERNAL_CONVERSION *internal_conversion
-        = &special_unit_body_internal_formatting_table[i];
-      /* number is index +1 */
-      size_t number = find_string (&self->special_unit_varieties,
-                                   internal_conversion->special_unit_variety);
-      int j = number -1;
-      if (j >= 0)
-        {
-          SPECIAL_UNIT_BODY_FORMATTING *body_formatting
-            = &self->special_unit_body_formatting[j];
-          if (body_formatting->status == FRS_status_default_set
-              || body_formatting->status == FRS_status_none)
-            {
-              body_formatting->formatting_reference = 0;
-              body_formatting->status = FRS_status_internal;
-              body_formatting->special_unit_body_formatting
-                = internal_conversion->special_unit_body_formatting;
-              if (body_formatting->status == FRS_status_default_set)
-                external_special_unit_body_formatting_function--;
-            }
-        }
-    }
+void
+initialize_jslicense_files (JSLICENSE_FILE_INFO_LIST *jslicences_files_info,
+                            const char *category, size_t size)
+{
+  jslicences_files_info->category = strdup (category);
+  jslicences_files_info->list = (JSLICENSE_FILE_INFO *)
+              malloc (size * sizeof (JSLICENSE_FILE_INFO));
+  memset (jslicences_files_info->list, 0,
+          size * sizeof (JSLICENSE_FILE_INFO));
+  jslicences_files_info->number = size;
+}
 
- out:
-  self->external_references_number = self->conf->BIT_user_function_number
-        + self->file_id_setting_ref_number
-        + external_special_unit_body_formatting_function
-        + external_output_unit_conversion_function
-        + external_command_conversion_function
-        + external_command_open_function
-        + external_type_conversion_function
-        + external_type_open_function
-        + external_formatting_function;
+FORMATTING_REFERENCE *
+new_special_unit_formatting_references (int special_units_varieties_nr)
+{
+  FORMATTING_REFERENCE *formatting_references = (FORMATTING_REFERENCE *)
+    malloc (special_units_varieties_nr * sizeof (FORMATTING_REFERENCE));
+  memset (formatting_references, 0,
+          special_units_varieties_nr * sizeof (FORMATTING_REFERENCE));
+  return formatting_references;
+}
 
-   /*
-  fprintf (stderr, "TOTAL: %d. conf %zu fi %d sbf %d ouc %d cc %d co %d tc %d 
to %d f %d\n",
-           self->external_references_number,
-           self->conf->BIT_user_function_number,
-           self->file_id_setting_ref_number,
-           external_special_unit_body_formatting_function,
-           external_output_unit_conversion_function,
-           external_command_conversion_function,
-           external_command_open_function,
-           external_type_conversion_function,
-           external_type_open_function,
-           external_formatting_function);
-    */
+char **
+new_special_unit_info_type (int special_units_varieties_nr)
+{
+  char **special_unit_info = (char **)
+        malloc ((special_units_varieties_nr +1) * sizeof (char *));
+  memset (special_unit_info, 0,
+          (special_units_varieties_nr +1) * sizeof (char *));
+  return special_unit_info;
 }
 
 void
@@ -17244,9 +15302,9 @@ reset_unset_no_arg_commands_formatting_context 
(CONVERTER *self,
     }
 }
 
-static void
-complete_no_arg_commands_formatting (CONVERTER *self, enum command_id cmd,
-                                     int translate)
+void
+html_complete_no_arg_commands_formatting (CONVERTER *self, enum command_id cmd,
+                                          int translate)
 {
   reset_unset_no_arg_commands_formatting_context (self, cmd, HCC_type_normal,
                                                   -1, translate);
@@ -17259,453 +15317,6 @@ complete_no_arg_commands_formatting (CONVERTER *self, 
enum command_id cmd,
                                                   HCC_type_string, translate);
 }
 
-/* transform <hr> to <hr/>
-   main effect is s/^(<[a-zA-Z]+[^<>]*)>$/$1\/>/ */
-static char *
-xhtml_re_close_lone_element (const char *input)
-{
-  size_t len = strlen (input);
-  size_t n;
-  const char *p;
-  char *result;
-  if (len < 3 || input[len -1] != '>' || input[0] != '<'
-      || !isascii_alpha (input[1]))
-    return strdup (input);
-
-  /* before > */
-  p = input + len - 2;
-  while (p > input +1)
-    {
-      if (*p == '/')
-        /* already a closed lone element */
-        return strdup (input);
-      if (strchr (whitespace_chars, *p))
-        p--;
-      else
-        break;
-    }
-
-  p = input + 2;
-  n = strcspn (p, "<>");
-  if (n +2 != len -1)
-    return strdup (input);
-
-  result = (char *) malloc ((len +1 +1) * sizeof (char));
-  memcpy (result, input, (len -1) * sizeof (char));
-  result[len -1] = '/';
-  result[len] = '>';
-  result[len+1] = '\0';
-
-  return result;
-}
-
-static void
-close_lone_conf_element (OPTION *option)
-{
-  const char *variable_value = option->o.string;
-  if (variable_value)
-    {
-      char *closed_lone_element = xhtml_re_close_lone_element (variable_value);
-      if (strcmp (closed_lone_element, variable_value))
-        {
-          option_force_conf (option, 0, closed_lone_element);
-        }
-      free (closed_lone_element);
-    }
-}
-
-static void
-copy_html_no_arg_command_conversion (HTML_NO_ARG_COMMAND_CONVERSION *to,
-                                     HTML_NO_ARG_COMMAND_CONVERSION *from)
-{
-  if (from->element)
-    to->element = strdup (from->element);
-  to->unset = from->unset;
-  if (from->text)
-    to->text = strdup (from->text);
-  if (from->translated_converted)
-    to->translated_converted = strdup (from->translated_converted);
-  if (from->translated_to_convert)
-    to->translated_to_convert = strdup (from->translated_to_convert);
-}
-
-const enum command_id spaces_cmd[] = {CM_SPACE, CM_TAB, CM_NEWLINE, CM_tie};
-
-/* called very early in conversion functions, before updating
-   customization, before calling user-defined functions...  */
-void
-html_initialize_output_state (CONVERTER *self, const char *context)
-{
-  int i;
-  size_t j;
-  const char *output_encoding;
-  int nr_special_units = self->special_unit_varieties.number;
-  /* The corresponding direction without FirstInFile are used instead
-     of FirstInFile*, so the directions_strings are not set */
-  int nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
-                     + nr_special_units;
-  int nr_dir_str_contexts = TDS_context_string + 1;
-  enum direction_string_type DS_type;
-  const char *line_break_element;
-  int css_style_idx = 0;
-  int *non_default_special_unit_directions =
-     determine_non_default_special_unit_directions (self);
-
-  if (!self->document && self->conf->DEBUG.o.integer > 0)
-    {
-      fprintf (stderr, "REMARK: html_initialize_output_state: no document");
-    }
-
-  /* corresponds with default_no_arg_commands_formatting
-     + conf_default_no_arg_commands_formatting_normal in Perl */
-  HTML_NO_ARG_COMMAND_CONVERSION
-   output_no_arg_commands_formatting[BUILTIN_CMD_NUMBER]
-                                              [NO_ARG_COMMAND_CONTEXT_NR];
-
-  output_encoding = self->conf->OUTPUT_ENCODING_NAME.o.string;
-
-  for (i = 0; i < SC_non_breaking_space+1; i++)
-    {
-      const char *unicode_point = special_characters_formatting[i][2];
-      const char *entity = special_characters_formatting[i][0];
-      const char *encoded_string = special_characters_formatting[i][1];
-      const char *numeric_entity = special_characters_formatting[i][3];
-      const char *special_character_string;
-
-      if (self->conf->OUTPUT_CHARACTERS.o.integer > 0
-          && unicode_point_decoded_in_encoding (output_encoding,
-                                                unicode_point))
-        special_character_string = encoded_string;
-      else if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0)
-        special_character_string = numeric_entity;
-      else
-        special_character_string = entity;
-
-      self->special_character[i].string = special_character_string;
-      self->special_character[i].len = strlen (special_character_string);
-    }
-
-  if (!self->conf->OPEN_QUOTE_SYMBOL.o.string)
-    {
-      int set = option_set_conf (&self->conf->OPEN_QUOTE_SYMBOL, 0,
-                                 
self->special_character[SC_left_quote].string);
-      /* override undef set in init file/command line */
-      if (!set)
-        option_force_conf (&self->conf->OPEN_QUOTE_SYMBOL, 0, "");
-    }
-  if (!self->conf->CLOSE_QUOTE_SYMBOL.o.string)
-    {
-      int set = option_set_conf (&self->conf->CLOSE_QUOTE_SYMBOL, 0,
-                              self->special_character[SC_right_quote].string);
-      /* override undef set in init file/command line */
-      if (!set)
-        option_force_conf (&self->conf->CLOSE_QUOTE_SYMBOL, 0, "");
-    }
-  if (!self->conf->MENU_SYMBOL.o.string)
-    {
-      int set = option_set_conf (&self->conf->MENU_SYMBOL, 0,
-                                 self->special_character[SC_bullet].string);
-      /* override undef set in init file/command line */
-      if (!set)
-        option_force_conf (&self->conf->MENU_SYMBOL, 0, "");
-    }
-
-  if (self->conf->USE_XML_SYNTAX.o.integer > 0)
-    {
-      close_lone_conf_element (&self->conf->BIG_RULE);
-      close_lone_conf_element (&self->conf->DEFAULT_RULE);
-      line_break_element = "<br/>";
-    }
-  else
-    line_break_element = "<br>";
-
-  self->line_break_element.string = line_break_element;
-  self->line_break_element.len = strlen (line_break_element);
-
-  memcpy (output_no_arg_commands_formatting, 
default_no_arg_commands_formatting,
-          sizeof (default_no_arg_commands_formatting));
-
-  /* if not the textual entity */
-  if (strcmp(self->special_character[SC_non_breaking_space].string,
-             special_characters_formatting[SC_non_breaking_space][0]))
-    {
-      for (j = 0; j < sizeof (spaces_cmd) / sizeof (spaces_cmd[0]); j++)
-        {
-          enum command_id cmd = spaces_cmd[j];
-          /* cast to drop const */
-          output_no_arg_commands_formatting[cmd][HCC_type_normal].text
-            = (char *)self->special_character[SC_non_breaking_space].string;
-        }
-    }
-
-  if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0)
-    {
-      for (j = 0; j < no_arg_formatted_cmd.number; j++)
-        {
-          enum command_id cmd = no_arg_formatted_cmd.list[j];
-          if (unicode_entities[cmd])
-            output_no_arg_commands_formatting[cmd][HCC_type_normal].text
-              = unicode_entities[cmd];
-        }
-    }
-
-  /* cast to discard const */
-  output_no_arg_commands_formatting[CM_ASTERISK][HCC_type_normal].text
-    = (char *)self->line_break_element.string;
-
-  initialize_css_selector_style_list (&self->css_element_class_styles,
-                                      default_css_element_class_styles.number);
-  for (j = 0; j < default_css_element_class_styles.number; j++)
-    {
-      CSS_SELECTOR_STYLE *default_selector_style
-        = &default_css_element_class_styles.list[j];
-      if (default_selector_style->style)
-        {
-          CSS_SELECTOR_STYLE *selector_style
-            = &self->css_element_class_styles.list[css_style_idx];
-          selector_style->selector = strdup (default_selector_style->selector);
-          selector_style->style = strdup (default_selector_style->style);
-          css_style_idx++;
-        }
-      else
-        self->css_element_class_styles.number--;
-    }
-
-  for (j = 0; j < no_arg_formatted_cmd.number; j++)
-    {
-      enum command_id cmd = no_arg_formatted_cmd.list[j];
-      enum conversion_context cctx;
-      for (cctx = 0; cctx < NO_ARG_COMMAND_CONTEXT_NR; cctx++)
-        {
-          HTML_NO_ARG_COMMAND_CONVERSION *customized_no_arg_cmd
-            = self->customized_no_arg_commands_formatting[cmd][cctx];
-          HTML_NO_ARG_COMMAND_CONVERSION *result
-            = &self->html_no_arg_command_conversion[cmd][cctx];
-          if (customized_no_arg_cmd)
-            {
-              copy_html_no_arg_command_conversion (result,
-                                                   customized_no_arg_cmd);
-            }
-          else if (!output_no_arg_commands_formatting[cmd][cctx].unset)
-            {
-              const char *unicode_brace_no_arg_formatting = 0;
-              if (self->conf->OUTPUT_CHARACTERS.o.integer > 0)
-                {
-                  unicode_brace_no_arg_formatting
-                    = unicode_brace_no_arg_command (cmd,
-                         self->conf->OUTPUT_ENCODING_NAME.o.string);
-                }
-              if (unicode_brace_no_arg_formatting)
-                {
-                  memset (result, 0, sizeof (HTML_NO_ARG_COMMAND_CONVERSION));
-                  result->text
-                    = strdup (unicode_brace_no_arg_formatting);
-
-                  /* reset CSS for itemize command arguments */
-                  if (cctx == HCC_type_css_string
-                      && builtin_command_data[cmd].flags & CF_brace
-                      && cmd != CM_bullet && cmd != CM_w)
-                    {
-                      const char *special_list_mark_command
-                       = get_special_list_mark_css_string_no_arg_command (cmd);
-
-                      if (!special_list_mark_command)
-                        {
-                           char *selector;
-                           char *style;
-                           xasprintf (&selector, "ul.mark-%s",
-                                      builtin_command_name (cmd));
-                           xasprintf (&style, "list-style-type: \"%s\"",
-                                      result->text);
-                           html_css_set_selector_style (
-                                         &self->css_element_class_styles,
-                                                        selector, style);
-                           free (selector);
-                           free (style);
-                        }
-                    }
-                }
-              else
-                {
-                  copy_html_no_arg_command_conversion (result,
-                     &output_no_arg_commands_formatting[cmd][cctx]);
-                }
-            }
-          else
-            {
-              copy_html_no_arg_command_conversion (result,
-                &output_no_arg_commands_formatting[cmd][cctx]);
-            }
-        }
-    }
-
-  for (j = 0; j < no_arg_formatted_cmd.number; j++)
-    {
-      enum command_id cmd = no_arg_formatted_cmd.list[j];
-      COMMAND_CONVERSION_FUNCTION *command_conversion
-        = &self->command_conversion_function[cmd];
-      if (command_conversion->status == FRS_status_default_set
-          || command_conversion->status == FRS_status_internal)
-        {
-          complete_no_arg_commands_formatting (self, cmd, 0);
-        }
-    }
-
-  for (DS_type = 0; DS_type < TDS_TYPE_MAX_NR; DS_type++)
-    {
-      int i;
-      const char * const*default_converted_dir_str;
-      char ***customized_type_dir_strings;
-
-      self->directions_strings[DS_type]
-        = new_directions_strings_type (nr_string_directions,
-                                       nr_dir_str_contexts);
-
-      /* those will be determined from translatable strings */
-      if (DS_type < TDS_TRANSLATED_MAX_NR)
-        continue;
-
-      default_converted_dir_str =
-        default_converted_directions_strings[
-                                       DS_type - (TDS_TRANSLATED_MAX_NR)];
-      customized_type_dir_strings = self->customized_directions_strings[
-                                       DS_type - (TDS_TRANSLATED_MAX_NR)];
-      for (i = 0; i < nr_string_directions; i++)
-        {
-          if (customized_type_dir_strings && customized_type_dir_strings[i])
-            {
-              int j;
-              for (j = 0; j < nr_dir_str_contexts; j++)
-                {
-                  if (customized_type_dir_strings[i][j])
-                    self->directions_strings[DS_type][i][j]
-                      = substitute_html_non_breaking_space (self,
-                                     customized_type_dir_strings[i][j]);
-                }
-            }
-          else if (default_converted_dir_str[i])
-            {
-              if (i < NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
-                  || !non_default_special_unit_directions[
-                       i - (NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR)])
-                self->directions_strings[DS_type][i][TDS_context_normal]
-                  = substitute_html_non_breaking_space (self,
-                                            default_converted_dir_str[i]);
-            }
-
-          if (self->directions_strings[DS_type][i][TDS_context_normal]
-              && !self->directions_strings[DS_type][i][TDS_context_string])
-            {
-              self->directions_strings[DS_type][i][TDS_context_string]
-                 = strdup (
-               self->directions_strings[DS_type][i][TDS_context_normal]);
-            }
-        }
-    }
-
-  free (non_default_special_unit_directions);
-
-  sort_css_element_class_styles (&self->css_element_class_styles);
-
-  /* set the htmlxref type split of the document */
-  self->document_htmlxref_split_type = htmlxref_split_type_mono;
-
-  if (self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string))
-    {
-      int i;
-      for (i = 1; i < htmlxref_split_type_chapter+1; i++)
-        {
-          if (!strcmp (self->conf->SPLIT.o.string,
-                       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));
-
-  if (self->conf->NODE_NAME_IN_INDEX.o.integer < 0)
-    option_set_conf (&self->conf->NODE_NAME_IN_INDEX,
-                     self->conf->USE_NODES.o.integer, 0);
-
-  if (self->conf->HTML_MATH.o.string
-      && self->conf->CONVERT_TO_LATEX_IN_MATH.o.integer < 0)
-    {
-      option_set_conf (&self->conf->CONVERT_TO_LATEX_IN_MATH, 1, 0);
-    }
-
-  if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0
-      && self->conf->SHOW_TITLE.o.integer < 0)
-    option_set_conf (&self->conf->SHOW_TITLE, 1, 0);
-
-
-  self->current_formatting_references = &self->formatting_references[0];
-  self->current_commands_conversion_function
-     = &self->command_conversion_function[0];
-  self->current_types_conversion_function = &self->type_conversion_function[0];
-  self->current_format_protect_text = &html_default_format_protect_text;
-
-  html_new_document_context (self, context, 0, 0);
-
-  if (self->document && self->document->indices_info.number)
-    {
-      size_t i;
-      size_t j;
-      INDEX_LIST *indices_info = &self->document->indices_info;
-      const INDEX **sorted_index_names;
-      size_t index_nr = indices_info->number;
-      size_t non_empty_index_nr = 0;
-      size_t idx_non_empty = 0;
-
-      for (i = 0; i < index_nr; i++)
-        {
-          INDEX *idx = indices_info->list[i];
-          if (idx->entries_number > 0)
-            non_empty_index_nr++;
-        }
-
-      sorted_index_names = (const INDEX **) malloc (index_nr * sizeof (INDEX 
*));
-
-      memcpy (sorted_index_names, indices_info->list,
-              index_nr * sizeof (INDEX *));
-      qsort (sorted_index_names, index_nr, sizeof (INDEX *),
-             compare_index_name);
-
-      /* store only non empty indices in sorted_index_names */
-      self->sorted_index_names.number = non_empty_index_nr;
-      self->sorted_index_names.list = (const INDEX **)
-         malloc (self->sorted_index_names.number * sizeof (INDEX *));
-      for (j = 0; j < index_nr; j++)
-        {
-          if (sorted_index_names[j]->entries_number > 0)
-            {
-              self->sorted_index_names.list[idx_non_empty]
-                  = sorted_index_names[j];
-              idx_non_empty++;
-            }
-        }
-      free (sorted_index_names);
-    }
-
-  if (self->document)
-    {
-      const LISTOFFLOATS_TYPE_LIST *listoffloats
-         = &self->document->listoffloats;
-
-      if (listoffloats->number)
-        {
-          self->shared_conversion_state.formatted_listoffloats_nr
-           = (int *) malloc (listoffloats->number * sizeof (int));
-          memset (self->shared_conversion_state.formatted_listoffloats_nr,
-              0, listoffloats->number * sizeof (int));
-        }
-    }
-}
-
 void
 html_conversion_finalization (CONVERTER *self)
 {
@@ -19051,7 +16662,7 @@ html_translate_names (CONVERTER *self)
       for (j = 0; j < translated_nr; j++)
         {
           enum command_id cmd = translated_cmds->list[j];
-          complete_no_arg_commands_formatting (self, cmd, 1);
+          html_complete_no_arg_commands_formatting (self, cmd, 1);
         }
 
       /* not passed to Perl in that case, unset to avoid spurious error
@@ -19386,10 +16997,10 @@ convert_to_html_internal (CONVERTER *self, const 
ELEMENT *element,
                          have their flag reset to F_AFT_normal here, such that
                          their argument is not converter here */
                       if (arg_idx < MAX_COMMAND_ARGS_NR
-                          /* could check command_args_flags[cmd].status,
+                          /* could check html_command_args_flags[cmd].status,
                              but it is probably faster not to */
-                          && command_args_flags[cmd].flags[arg_idx])
-                        arg_flags = command_args_flags[cmd].flags[arg_idx];
+                          && html_command_args_flags[cmd].flags[arg_idx])
+                        arg_flags = 
html_command_args_flags[cmd].flags[arg_idx];
                       else
                         arg_flags = F_AFT_normal;
 
@@ -20550,6 +18161,23 @@ html_convert_output (CONVERTER *self, const ELEMENT 
*root,
     }
 }
 
+/* return string to be freed by the caller */
+char *
+html_prepare_node_redirection_page (CONVERTER *self, const ELEMENT *element,
+                                    const char *filename)
+{
+  char *result;
+
+  self->current_filename.filename = filename;
+  self->current_filename.file_number = 0;
+
+  result = format_node_redirection_page (self, element, filename);
+
+  self->current_filename.filename = 0;
+
+  return result;
+}
+
 int
 html_node_redirections (CONVERTER *self,
             const char *output_file, const char *destination_directory)
diff --git a/tp/Texinfo/XS/convert/convert_html.h 
b/tp/Texinfo/XS/convert/convert_html.h
index aca9adff5e..e63e9c3901 100644
--- a/tp/Texinfo/XS/convert/convert_html.h
+++ b/tp/Texinfo/XS/convert/convert_html.h
@@ -2,12 +2,31 @@
 #ifndef CONVERT_HTML_H
 #define CONVERT_HTML_H
 
+/* Copyright 2010-2024 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
 #include "command_ids.h"
 #include "element_types.h"
 #include "tree_types.h"
 #include "converter_types.h"
 #include "html_converter_types.h"
 
+typedef struct ROOT_AND_UNIT {
+    const OUTPUT_UNIT *output_unit;
+    const ELEMENT *root;
+} ROOT_AND_UNIT;
 
 extern const char *html_conversion_context_type_names[];
 extern const char *html_global_unit_direction_names[];
@@ -26,22 +45,24 @@ extern const char *direction_string_context_names[];
 extern const char *html_stage_handler_stage_type_names[];
 extern COMMAND_ID_LIST no_arg_formatted_cmd;
 
-void html_format_setup (void);
-
-void html_converter_init_special_unit (CONVERTER *self);
-void html_converter_customize (CONVERTER *self);
-
-void html_initialize_output_state (CONVERTER *self, const char *context);
 void init_conversion_after_setup_handler (CONVERTER *self);
 void html_conversion_finalization (CONVERTER *self);
 
 int html_setup_output (CONVERTER *self, char **paths);
 void html_setup_convert (CONVERTER *self);
 
-void initialize_cmd_list (COMMAND_ID_LIST *cmd_list, size_t size,
-                          size_t number);
+ROOT_AND_UNIT *html_get_tree_root_element (CONVERTER *self,
+                                           const ELEMENT *command,
+                                           int find_container);
+
+char *html_after_escaped_characters (char *text);
+char *html_substitute_non_breaking_space (CONVERTER *self, const char *text);
 
-char *substitute_html_non_breaking_space (CONVERTER *self, const char *text);
+const char *html_special_unit_info (const CONVERTER *self,
+                                    enum special_unit_info_type type,
+                                    const char *special_unit_variety);
+
+void html_default_format_protect_text (const char *text, TEXT *result);
 
 HTMLXREF_MANUAL *new_htmlxref_manual_list (size_t size);
 void initialize_js_categories_list (JSLICENSE_CATEGORY_LIST *js_files_info,
@@ -49,10 +70,6 @@ void initialize_js_categories_list (JSLICENSE_CATEGORY_LIST 
*js_files_info,
 void initialize_jslicense_files
                        (JSLICENSE_FILE_INFO_LIST *jslicences_files_info,
                                  const char *category, size_t size);
-HTML_DIRECTION_STRING_TRANSLATED *new_directions_strings_translated_type
-  (int nr_string_directions);
-char ***new_directions_strings_type (int nr_string_directions,
-                                     int nr_dir_str_contexts);
 FORMATTING_REFERENCE *new_special_unit_formatting_references
                                       (int special_units_varieties_nr);
 char **new_special_unit_info_type (int special_units_varieties_nr);
@@ -102,8 +119,6 @@ FOOTNOTE_ID_NUMBER *find_footnote_id_number (const 
CONVERTER *self,
 char *html_attribute_class (CONVERTER *self, const char *element,
                             const STRING_LIST *classes);
 
-void html_prepare_conversion_units (CONVERTER *self);
-
 void html_prepare_conversion_units_targets (CONVERTER *self,
                                      const char *document_name);
 
@@ -111,6 +126,175 @@ FILE_SOURCE_INFO_LIST * 
html_prepare_units_directions_files (CONVERTER *self,
           const char *output_file, const char *destination_directory,
           const char *output_filename, const char *document_name);
 
+
+void
+html_command_conversion_external (CONVERTER *self, const enum command_id cmd,
+                                  const ELEMENT *element,
+                                  const HTML_ARGS_FORMATTED *args_formatted,
+                                  const char *content, TEXT *result);
+
+#define COMMAND_CONVERSION_FUNCTION(name) \
+void html_convert_##name (CONVERTER *self, const enum command_id cmd, \
+                          const ELEMENT *element, \
+                          const HTML_ARGS_FORMATTED *args_formatted, \
+                          const char *content, TEXT *result);
+#define CSS_STRING_COMMAND_CONVERSION_FUNCTION(name) \
+void html_css_string_convert_##name (CONVERTER *self, const enum command_id 
cmd, \
+                          const ELEMENT *element, \
+                          const HTML_ARGS_FORMATTED *args_formatted, \
+                          const char *content, TEXT *result);
+
+COMMAND_CONVERSION_FUNCTION(no_arg_command)
+CSS_STRING_COMMAND_CONVERSION_FUNCTION(no_arg_command)
+COMMAND_CONVERSION_FUNCTION(today_command)
+COMMAND_CONVERSION_FUNCTION(style_command)
+COMMAND_CONVERSION_FUNCTION(w_command)
+COMMAND_CONVERSION_FUNCTION(value_command)
+COMMAND_CONVERSION_FUNCTION(email_command)
+COMMAND_CONVERSION_FUNCTION(explained_command)
+COMMAND_CONVERSION_FUNCTION(anchor_command)
+COMMAND_CONVERSION_FUNCTION(footnote_command)
+COMMAND_CONVERSION_FUNCTION(uref_command)
+COMMAND_CONVERSION_FUNCTION(image_command)
+COMMAND_CONVERSION_FUNCTION(math_command)
+COMMAND_CONVERSION_FUNCTION(accent_command)
+CSS_STRING_COMMAND_CONVERSION_FUNCTION(accent_command)
+COMMAND_CONVERSION_FUNCTION(indicateurl_command)
+COMMAND_CONVERSION_FUNCTION(titlefont_command)
+COMMAND_CONVERSION_FUNCTION(U_command)
+COMMAND_CONVERSION_FUNCTION(heading_command)
+COMMAND_CONVERSION_FUNCTION(inline_command)
+COMMAND_CONVERSION_FUNCTION(xref_command)
+
+COMMAND_CONVERSION_FUNCTION(raw_command)
+COMMAND_CONVERSION_FUNCTION(preformatted_command)
+COMMAND_CONVERSION_FUNCTION(indented_command)
+COMMAND_CONVERSION_FUNCTION(verbatim_command)
+COMMAND_CONVERSION_FUNCTION(displaymath_command)
+COMMAND_CONVERSION_FUNCTION(simple_block_command)
+COMMAND_CONVERSION_FUNCTION(menu_command)
+COMMAND_CONVERSION_FUNCTION(float_command)
+COMMAND_CONVERSION_FUNCTION(quotation_command)
+COMMAND_CONVERSION_FUNCTION(cartouche_command)
+COMMAND_CONVERSION_FUNCTION(itemize_command)
+COMMAND_CONVERSION_FUNCTION(enumerate_command)
+COMMAND_CONVERSION_FUNCTION(multitable_command)
+COMMAND_CONVERSION_FUNCTION(xtable_command)
+
+COMMAND_CONVERSION_FUNCTION(verbatiminclude_command)
+COMMAND_CONVERSION_FUNCTION(sp_command)
+COMMAND_CONVERSION_FUNCTION(exdent_command)
+COMMAND_CONVERSION_FUNCTION(center_command)
+COMMAND_CONVERSION_FUNCTION(author_command)
+COMMAND_CONVERSION_FUNCTION(title_command)
+COMMAND_CONVERSION_FUNCTION(subtitle_command)
+COMMAND_CONVERSION_FUNCTION(item_command)
+COMMAND_CONVERSION_FUNCTION(tab_command)
+COMMAND_CONVERSION_FUNCTION(insertcopying_command)
+COMMAND_CONVERSION_FUNCTION(listoffloats_command)
+COMMAND_CONVERSION_FUNCTION(printindex_command)
+COMMAND_CONVERSION_FUNCTION(informative_command)
+COMMAND_CONVERSION_FUNCTION(contents_command)
+COMMAND_CONVERSION_FUNCTION(def_command)
+
+#undef COMMAND_CONVERSION_FUNCTION
+
+void html_command_open_external (CONVERTER *self, const enum command_id cmd, 
+                                 const ELEMENT *element, TEXT *result);
+
+#define COMMAND_OPEN_FUNCTION(name) \
+void html_open_##name (CONVERTER *self, const enum command_id cmd, \
+                  const ELEMENT *element, TEXT *result);
+
+COMMAND_OPEN_FUNCTION(quotation_command)
+
+#undef COMMAND_OPEN_FUNCTION
+
+void html_type_conversion_external (CONVERTER *self,
+                                    const enum element_type type,
+                                    const ELEMENT *element, const char 
*content,
+                                    TEXT *result);
+
+#define ELEMENT_TYPE_CONVERSION_FUNCTION(name) \
+void html_convert_##name (CONVERTER *self, const enum element_type type, \
+                        const ELEMENT *element, const char *content, \
+                        TEXT *result);
+
+#define CSS_STRING_ELEMENT_TYPE_CONVERSION_FUNCTION(name) \
+void html_css_string_convert_##name (CONVERTER *self, const enum element_type 
type, \
+                        const ELEMENT *element, const char *content, \
+                        TEXT *result);
+
+ELEMENT_TYPE_CONVERSION_FUNCTION(text)
+CSS_STRING_ELEMENT_TYPE_CONVERSION_FUNCTION(text)
+ELEMENT_TYPE_CONVERSION_FUNCTION(paragraph_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(preformatted_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(balanced_braces_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(index_entry_command_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(definfoenclose_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(untranslated_def_line_arg_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(row_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(multitable_head_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(multitable_body_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(menu_entry_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(menu_comment_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(before_item_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(table_term_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(def_line_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(def_item_type)
+ELEMENT_TYPE_CONVERSION_FUNCTION(table_definition_type)
+
+#undef CSS_STRING_ELEMENT_TYPE_CONVERSION_FUNCTION
+#undef ELEMENT_TYPE_CONVERSION_FUNCTION
+
+void html_type_open_external (CONVERTER *self, enum element_type type,
+                              const ELEMENT *element, TEXT *result);
+
+#define ELEMENT_TYPE_OPEN_FUNCTION(name) \
+void html_open_##name (CONVERTER *self, const enum element_type type, \
+                       const ELEMENT *element, TEXT *result);
+
+ELEMENT_TYPE_OPEN_FUNCTION(inline_container_type)
+
+#undef ELEMENT_TYPE_OPEN_FUNCTION
+
+void html_output_unit_conversion_external (CONVERTER *self,
+                               const enum output_unit_type unit_type,
+                         const OUTPUT_UNIT *output_unit, const char *content,
+                         TEXT *result);
+
+#define UNIT_CONVERSION_FUNCTION(name) \
+void html_convert_##name (CONVERTER *self, \
+                          const enum output_unit_type unit_type, \
+                          const OUTPUT_UNIT *output_unit, const char *content, 
\
+                          TEXT *result);
+
+UNIT_CONVERSION_FUNCTION(unit_type);
+UNIT_CONVERSION_FUNCTION(special_unit_type);
+
+#undef UNIT_CONVERSION_FUNCTION
+
+void html_special_unit_body_formatting_external (CONVERTER *self,
+                                    const size_t special_unit_number,
+                                    const char *special_unit_variety,
+                                    const OUTPUT_UNIT *output_unit,
+                                    TEXT *result);
+
+#define SPECIAL_BODY_FORMATTING_FUNCTION(name) \
+void html_default_format_special_body_##name (CONVERTER *self, \
+                                      const size_t special_unit_number, \
+                                      const char *special_unit_variety, \
+                                      const OUTPUT_UNIT *output_unit, \
+                                      TEXT *result);
+
+SPECIAL_BODY_FORMATTING_FUNCTION(contents)
+SPECIAL_BODY_FORMATTING_FUNCTION(shortcontents)
+SPECIAL_BODY_FORMATTING_FUNCTION(footnotes)
+SPECIAL_BODY_FORMATTING_FUNCTION(about)
+
+#undef SPECIAL_BODY_FORMATTING_FUNCTION
+
+
 void html_prepare_output_units_global_targets (CONVERTER *self);
 void html_setup_global_units_direction_names (CONVERTER *self);
 const OUTPUT_UNIT *html_find_direction_name_global_unit (const CONVERTER *self,
@@ -151,4 +335,9 @@ int html_finish_output (CONVERTER *self, const char 
*output_file,
 void html_check_transfer_state_finalization (CONVERTER *self);
 void html_destroy_files_source_info (FILE_SOURCE_INFO_LIST *files_source_info);
 
+void html_complete_no_arg_commands_formatting (CONVERTER *self,
+                                               enum command_id cmd,
+                                               int translate);
+
+
 #endif
diff --git a/tp/Texinfo/XS/convert/get_html_perl_info.c 
b/tp/Texinfo/XS/convert/get_html_perl_info.c
index e29a84ced8..d87582bde5 100644
--- a/tp/Texinfo/XS/convert/get_html_perl_info.c
+++ b/tp/Texinfo/XS/convert/get_html_perl_info.c
@@ -44,6 +44,8 @@
 #include "output_unit.h"
 #include "converter.h"
 #include "html_conversion_state.h"
+/* new_directions_strings_type */
+#include "html_prepare_converter.h"
 #include "convert_html.h"
 #include "get_perl_info.h"
 #include "get_converter_perl_info.h"
diff --git a/tp/Texinfo/XS/convert/html_converter_types.h 
b/tp/Texinfo/XS/convert/html_converter_types.h
index 868c5ff676..9d40344261 100644
--- a/tp/Texinfo/XS/convert/html_converter_types.h
+++ b/tp/Texinfo/XS/convert/html_converter_types.h
@@ -31,6 +31,19 @@ enum css_info_type {
    CI_css_info_rules,
 };
 
+/* HTML arguments flags */
+/* used to set flags to non-zero with a flag that does nothing */
+#define F_AFT_none              0x0001
+
+#define F_AFT_normal            0x0002
+#define F_AFT_string            0x0004
+#define F_AFT_monospace         0x0008
+#define F_AFT_monospacetext     0x0010
+#define F_AFT_monospacestring   0x0020
+#define F_AFT_filenametext      0x0040
+#define F_AFT_url               0x0080
+#define F_AFT_raw               0x0100
+
 /* HTML command data flags */
 #define HF_composition_context  0x0001
 #define HF_format_context       0x0002
@@ -48,6 +61,30 @@ typedef struct HTML_COMMAND_STRUCT {
     enum command_id upper_case_cmd;
 } HTML_COMMAND_STRUCT;
 
+typedef struct SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD {
+    enum command_id cmd;
+    char *string;
+    char *saved;
+} SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD;
+
+/* in specification of args.  Number max +1 for a trailing 0 */
+#define MAX_COMMAND_ARGS_NR 6
+
+typedef struct COMMAND_ARGS_SPECIFICATION {
+    int status;
+    unsigned long flags[MAX_COMMAND_ARGS_NR];
+} COMMAND_ARGS_SPECIFICATION;
+
+extern COMMAND_ARGS_SPECIFICATION html_command_args_flags[BUILTIN_CMD_NUMBER];
+
 extern HTML_COMMAND_STRUCT html_commands_data[BUILTIN_CMD_NUMBER];
 
+extern SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD
+            special_list_mark_css_string_no_arg_command[];
+
+extern COMMAND_ID_LIST no_arg_formatted_cmd;
+extern COMMAND_ID_LIST style_formatted_cmd;
+extern COMMAND_ID_LIST accent_cmd;
+extern COMMAND_ID_LIST format_raw_cmd;
+
 #endif
diff --git a/tp/Texinfo/XS/convert/html_prepare_converter.c 
b/tp/Texinfo/XS/convert/html_prepare_converter.c
new file mode 100644
index 0000000000..d1a18fc128
--- /dev/null
+++ b/tp/Texinfo/XS/convert/html_prepare_converter.c
@@ -0,0 +1,2421 @@
+/* Copyright 2010-2024 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "text.h"
+#include "command_ids.h"
+#include "element_types.h"
+#include "tree_types.h"
+#include "converter_types.h"
+#include "conversion_data.h"
+/* new_element */
+#include "tree.h"
+#include "utils.h"
+/* unicode_character_brace_no_arg_commands */
+#include "unicode.h"
+#include "builtin_commands.h"
+#include "command_stack.h"
+#include "customization_options.h"
+/* nobrace_symbol_text text_brace_no_arg_commands */
+#include "convert_to_text.h"
+#include "output_unit.h"
+#include "html_conversion_state.h"
+/* no_brace_command_accent_upper_case
+   xml_text_entity_no_arg_commands_formatting */
+#include "converter.h"
+#include "call_html_perl_function.h"
+#include "convert_html.h"
+#include "html_prepare_converter.h"
+
+typedef struct CMD_VARIETY {
+    enum command_id cmd;
+    char *variety;
+} CMD_VARIETY;
+
+typedef struct COMMAND_ID_ARGS_SPECIFICATION {
+    enum command_id cmd;
+    unsigned long flags[MAX_COMMAND_ARGS_NR];
+} COMMAND_ID_ARGS_SPECIFICATION;
+
+/* in main/conversion_data.c */
+extern const CSS_SELECTOR_STYLE base_default_css_element_class_styles[];
+
+static CSS_SELECTOR_STYLE_LIST default_css_element_class_styles;
+
+COMMAND_ARGS_SPECIFICATION html_command_args_flags[BUILTIN_CMD_NUMBER];
+
+const char *htmlxref_split_type_names[htmlxref_split_type_chapter + 1] =
+{
+  "mono", "node", "section", "chapter"
+};
+
+static COMMAND_ID_ARGS_SPECIFICATION default_commands_args[] = {
+  {CM_anchor, {F_AFT_monospacestring}},
+  {CM_email, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal}},
+  {CM_footnote, {F_AFT_none}}, /* no flag */
+  {CM_printindex, {F_AFT_none}}, /* no flag */
+  {CM_uref, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal, F_AFT_normal}},
+  {CM_url, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal, F_AFT_normal}},
+  {CM_sp, {F_AFT_none}}, /* no flag */
+  {CM_inforef, {F_AFT_monospace, F_AFT_normal, F_AFT_filenametext}},
+  {CM_xref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, 
F_AFT_normal}},
+  {CM_pxref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, 
F_AFT_normal}},
+  {CM_ref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, 
F_AFT_normal}},
+  {CM_link, {F_AFT_monospace, F_AFT_normal, F_AFT_filenametext}},
+  {CM_image, {F_AFT_url | F_AFT_filenametext | F_AFT_monospacestring, 
F_AFT_filenametext, F_AFT_filenametext, F_AFT_string | F_AFT_normal, 
F_AFT_filenametext}},
+  {CM_inlinefmt, {F_AFT_monospacetext, F_AFT_normal}},
+  {CM_inlinefmtifelse, {F_AFT_monospacetext, F_AFT_normal, F_AFT_normal}},
+  {CM_inlineraw, {F_AFT_monospacetext, F_AFT_raw}},
+  {CM_inlineifclear, {F_AFT_monospacetext, F_AFT_normal}},
+  {CM_inlineifset, {F_AFT_monospacetext, F_AFT_normal}},
+  {CM_item, {F_AFT_none}}, /* no flag */
+  {CM_itemx, {F_AFT_none}}, /* no flag */
+  {CM_value, {F_AFT_monospacestring}},
+  {CM_abbr, {F_AFT_normal, F_AFT_string | F_AFT_normal}},
+  {CM_acronym, {F_AFT_normal, F_AFT_string | F_AFT_normal}},
+};
+
+/* types that are in code style in the default case.  '_code' is not
+   a type that can appear in the tree built from Texinfo code, it is used
+   to format a tree fragment as if it was in a @code @-command.  */
+static enum element_type default_code_types[] = {
+  ET__code, 0,
+};
+
+const PRE_CLASS_TYPE_INFO default_pre_class_types[] = {
+  {ET_menu_comment, "menu-comment"},
+  {0, 0}
+};
+
+static enum command_id quoted_style_commands[] = {
+  CM_samp, 0
+};
+
+static enum command_id HTML_align_cmd[] = {
+   CM_raggedright, CM_flushleft, CM_flushright, CM_center, 0
+};
+
+static enum command_id additional_format_context_cmd[] = {
+   CM_tab, CM_item, CM_itemx, CM_headitem, 0
+ };
+
+/* should correspond to enum html_special_character */
+/* HTML textual entity, UTF-8 encoded, unicode point, HTML numeric entity */
+char *special_characters_formatting[SC_non_breaking_space+1][4] = {
+  {"&para;", "\xC2\xB6", "00B6", "&#182;"},
+  {"&lsquo;", "\xE2\x80\x98", "2018", "&#8216;"},
+  {"&rsquo;", "\xE2\x80\x99", "2019", "&#8217;"},
+  {"&bull;", "\xE2\x80\xA2", "2022", "&#8226;"},
+  {"&nbsp;", "\xC2\xA0", "00A0", "&#160;"},
+};
+
+CMD_VARIETY command_special_unit_variety[] = {
+                                {CM_contents, "contents"},
+                                {CM_shortcontents, "shortcontents"},
+                                {CM_summarycontents, "shortcontents"},
+                                {CM_footnote, "footnotes"},
+                                {0, 0},
+};
+
+static const enum command_id conf_for_special_units[]
+                          = {CM_footnotestyle, 0};
+
+static enum element_type ignored_types[] = {
+    ET_ignorable_spaces_after_command,
+    ET_postamble_after_end,
+    ET_preamble_before_beginning,
+    ET_preamble_before_setfilename,
+    ET_spaces_at_end,
+    ET_spaces_before_paragraph,
+    ET_spaces_after_close_brace, 0
+};
+
+HTML_COMMAND_STRUCT html_commands_data[BUILTIN_CMD_NUMBER];
+
+static HTML_NO_ARG_COMMAND_CONVERSION default_no_arg_commands_formatting
+                              [BUILTIN_CMD_NUMBER][NO_ARG_COMMAND_CONTEXT_NR];
+
+static HTML_STYLE_COMMAND_CONVERSION default_style_commands_formatting
+                               [BUILTIN_CMD_NUMBER][STYLE_COMMAND_CONTEXT_NR];
+
+static COMMAND_STACK preformatted_cmd_list;
+static COMMAND_STACK def_cmd_list;
+
+static char *unicode_entities[BUILTIN_CMD_NUMBER];
+
+COMMAND_ID_LIST no_arg_formatted_cmd;
+COMMAND_ID_LIST style_formatted_cmd;
+COMMAND_ID_LIST accent_cmd;
+COMMAND_ID_LIST format_raw_cmd;
+
+void
+register_format_context_command (enum command_id cmd)
+{
+  html_commands_data[cmd].flags |= HF_format_context;
+}
+
+void register_pre_class_command (enum command_id cmd, enum command_id main_cmd)
+{
+  if (main_cmd)
+    html_commands_data[cmd].pre_class_cmd = main_cmd;
+  else
+    html_commands_data[cmd].pre_class_cmd = cmd;
+
+  html_commands_data[cmd].flags |= HF_pre_class;
+}
+
+/* NOTE relatively generic */
+static void
+initialize_cmd_list (COMMAND_ID_LIST *cmd_list, size_t size, size_t number)
+{
+  cmd_list->list = (enum command_id *) malloc
+    (size * sizeof (enum command_id));
+  cmd_list->number = number;
+  if (number)
+    {
+      memset (cmd_list->list, 0, number * sizeof (enum command_id));
+    }
+}
+
+static void
+set_no_arg_commands_formatting (HTML_NO_ARG_COMMAND_CONVERSION *spec,
+                                char *text)
+{
+  spec->text = text;
+  spec->unset = 0;
+}
+
+static char *
+get_special_list_mark_css_string_no_arg_command (enum command_id cmd)
+{
+  int s;
+  for (s = 0;
+       special_list_mark_css_string_no_arg_command[s].cmd > 0;
+       s++)
+    {
+       if (special_list_mark_css_string_no_arg_command[s].cmd == cmd)
+        {
+           return special_list_mark_css_string_no_arg_command[s].string;
+           break;
+        }
+    }
+  return 0;
+}
+
+/* set information that is independent of customization, only called once */
+void
+html_format_setup (void)
+{
+  int i;
+  int no_arg_formatted_cmd_nr = 0;
+  int accent_cmd_nr = 0;
+  int style_formatted_cmd_nr = 0;
+  int format_raw_cmd_nr = 0;
+  int no_arg_formatted_cmd_idx;
+  int default_commands_args_nr
+    = sizeof (default_commands_args) / sizeof (default_commands_args[0]);
+  int max_args = MAX_COMMAND_ARGS_NR;
+  CSS_SELECTOR_STYLE *css_selector_style;
+  TEXT css_string_text;
+
+  const enum command_id indented_format[] = {
+    CM_example, CM_display, CM_lisp, 0
+  };
+
+  initialize_css_selector_style_list (&default_css_element_class_styles,
+                                      BASE_DEFAULT_CSS_ELEMENT_CLASS_STYLE_NR);
+  for (i = 0; i < BASE_DEFAULT_CSS_ELEMENT_CLASS_STYLE_NR; i++)
+    {
+      CSS_SELECTOR_STYLE *selector_style
+        = &default_css_element_class_styles.list[i];
+      selector_style->selector
+        = base_default_css_element_class_styles[i].selector;
+      selector_style->style
+        = base_default_css_element_class_styles[i].style;
+    }
+  sort_css_element_class_styles (&default_css_element_class_styles);
+
+  css_selector_style
+    = find_css_selector_style (&default_css_element_class_styles,
+                               "pre.display-preformatted");
+  html_css_set_selector_style (&default_css_element_class_styles,
+                               "pre.format-preformatted",
+                               css_selector_style->style);
+
+  for (i = 0; i < default_commands_args_nr; i++)
+    {
+      /* we file the status for specified commands, to distinguish them
+         but it is not actually used in the code, as we default to
+         normal for unspecified commands too */
+      enum command_id cmd = default_commands_args[i].cmd;
+      html_command_args_flags[cmd].status = 1;
+      memcpy (&html_command_args_flags[cmd].flags,
+              &default_commands_args[i].flags,
+              max_args * sizeof (unsigned long));
+    }
+
+  for (i = 0; indented_format[i]; i++)
+    {
+      char *css_selector;
+      enum command_id cmd = indented_format[i];
+      html_commands_data[cmd].flags |= HF_indented_preformatted;
+
+      xasprintf (&css_selector, "div.%s", builtin_command_name (cmd));
+      html_css_set_selector_style (&default_css_element_class_styles,
+                                  css_selector,
+                                  "margin-left: 3.2em");
+      free (css_selector);
+    }
+  /* output as div.example instead */
+  html_css_set_selector_style (&default_css_element_class_styles,
+                               "div.lisp", 0);
+
+  for (i = 0; small_block_associated_command[i][0]; i++)
+    {
+      enum command_id small_cmd = small_block_associated_command[i][0];
+      enum command_id cmd = small_block_associated_command[i][1];
+      if (builtin_command_data[cmd].flags & CF_preformatted)
+        {
+          register_pre_class_command (small_cmd, cmd);
+        }
+      html_commands_data[small_cmd].flags |= HF_small_block_command;
+      if (html_commands_data[cmd].flags & HF_indented_preformatted)
+        html_commands_data[small_cmd].flags |= HF_indented_preformatted;
+    }
+
+  for (i = 0; no_brace_command_accent_upper_case[i][0]; i++)
+    {
+      enum command_id cmd = no_brace_command_accent_upper_case[i][0];
+      enum command_id upper_case_cmd = 
no_brace_command_accent_upper_case[i][1];
+      html_commands_data[cmd].upper_case_cmd = upper_case_cmd;
+    }
+
+  /* set flags */
+  /* also count the number of no_arg_formatted_cmd, style_formatted_cmd and
+     accent_cmd commands and other types of commands */
+  for (i = 1; i < BUILTIN_CMD_NUMBER; i++)
+    {
+      if (xml_text_entity_no_arg_commands_formatting[i])
+        no_arg_formatted_cmd_nr++;
+
+      if (html_style_commands_element[i]
+          || (builtin_command_data[i].flags & CF_brace
+              && (builtin_command_data[i].data == BRACE_style_other
+                  || builtin_command_data[i].data == BRACE_style_code
+                  || builtin_command_data[i].data == BRACE_style_no_code)))
+        {
+          html_commands_data[i].flags |= HF_style_command;
+          style_formatted_cmd_nr++;
+        }
+
+      if (builtin_command_data[i].flags & CF_accent)
+        accent_cmd_nr++;
+
+      if ((builtin_command_data[i].flags & CF_block
+           && builtin_command_data[i].data != BLOCK_format_raw)
+          || builtin_command_data[i].flags & CF_root)
+        register_format_context_command (i);
+
+      if (builtin_command_data[i].flags & CF_preformatted
+          || builtin_command_data[i].flags & CF_root)
+        html_commands_data[i].flags |= HF_composition_context;
+
+      if (builtin_command_data[i].flags & CF_block)
+        {
+          if (builtin_command_data[i].data == BLOCK_menu)
+            html_commands_data[i].flags |= HF_composition_context;
+          else if (builtin_command_data[i].data == BLOCK_format_raw)
+            {
+              html_commands_data[i].flags |= HF_format_raw;
+              format_raw_cmd_nr++;
+            }
+        }
+
+      if (builtin_command_data[i].flags & CF_preformatted)
+        {
+          if (!(html_commands_data[i].flags & HF_pre_class))
+            register_pre_class_command (i, 0);
+          push_command (&preformatted_cmd_list, i);
+        }
+
+      if (builtin_command_data[i].flags & CF_def)
+        push_command (&def_cmd_list, i);
+    }
+  register_pre_class_command (CM_verbatim, 0);
+  register_pre_class_command (CM_menu, 0);
+  for (i = 0; additional_format_context_cmd[i]; i++)
+    register_format_context_command (additional_format_context_cmd[i]);
+
+  /* do not consider indicateurl to be a style command, it has a different
+     formatting function */
+  html_commands_data[CM_indicateurl].flags &= ~HF_style_command;
+
+  for (i = 0; HTML_align_cmd[i]; i++)
+    {
+      enum command_id cmd = HTML_align_cmd[i];
+      html_commands_data[cmd].flags |= HF_HTML_align | HF_composition_context;
+    }
+
+  html_commands_data[CM_float].flags |= HF_composition_context;
+
+  initialize_cmd_list (&no_arg_formatted_cmd, no_arg_formatted_cmd_nr,
+                       no_arg_formatted_cmd_nr);
+  initialize_cmd_list (&accent_cmd, accent_cmd_nr, 0);
+  initialize_cmd_list (&style_formatted_cmd, style_formatted_cmd_nr, 0);
+  initialize_cmd_list (&format_raw_cmd, format_raw_cmd_nr, 0);
+
+  for (i = 0; quoted_style_commands[i]; i++)
+    {
+      enum command_id cmd = quoted_style_commands[i];
+      enum conversion_context cctx;
+      for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
+        {
+          default_style_commands_formatting[cmd][cctx].quote = 1;
+        }
+    }
+
+  no_arg_formatted_cmd_idx = 0;
+  for (i = 0; i < BUILTIN_CMD_NUMBER; i++)
+    {
+      if (builtin_command_data[i].flags & CF_accent)
+        {
+          accent_cmd.list[accent_cmd.number] = i;
+          accent_cmd.number++;
+        }
+      else if (html_commands_data[i].flags & HF_format_raw)
+        {
+          format_raw_cmd.list[format_raw_cmd.number] = i;
+          format_raw_cmd.number++;
+        }
+
+      if (html_commands_data[i].flags & HF_style_command)
+        {
+          style_formatted_cmd.list[style_formatted_cmd.number] = i;
+          style_formatted_cmd.number++;
+
+          if (html_style_commands_element[i])
+            {
+              enum conversion_context cctx;
+              for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
+                {
+       /* the default_style_commands_formatting contains const char only
+          but the type is not const, so drop the const with a cast */
+                  default_style_commands_formatting[i][cctx].element
+                    = (char *) html_style_commands_element[i];
+                }
+            }
+        }
+
+      if (xml_text_entity_no_arg_commands_formatting[i])
+        {
+          /* the value is never modified but the struct field type is not const
+             so need to cast to drop const */
+          default_no_arg_commands_formatting[i][HCC_type_normal].text
+             = (char *)xml_text_entity_no_arg_commands_formatting[i];
+
+          no_arg_formatted_cmd.list[no_arg_formatted_cmd_idx] = i;
+          no_arg_formatted_cmd_idx++;
+
+          /* preset unset for string and preformatted contexts */
+          /* css_string coverage is checked when setting css string context
+             values below and unset is set explicitely if needed */
+          default_no_arg_commands_formatting[i][HCC_type_string].unset = 1;
+          default_no_arg_commands_formatting[i][HCC_type_preformatted].unset = 
1;
+        }
+    }
+
+  default_style_commands_formatting[CM_sc][HCC_type_preformatted].element
+      = "span";
+
+  /* modify normal context values and add other contexts values, removing
+     unset.  Should only be for commands with normal context already set.
+   */
+  default_no_arg_commands_formatting[CM_SPACE][HCC_type_normal].text = 
"&nbsp;";
+  default_no_arg_commands_formatting[CM_TAB][HCC_type_normal].text = "&nbsp;";
+  default_no_arg_commands_formatting[CM_NEWLINE][HCC_type_normal].text = 
"&nbsp;";
+
+  default_no_arg_commands_formatting[CM_enddots][HCC_type_normal].text = "...";
+  default_no_arg_commands_formatting[CM_enddots][HCC_type_normal].element
+     = "small";
+
+  set_no_arg_commands_formatting (
+   &default_no_arg_commands_formatting[CM_dots][HCC_type_preformatted], "...");
+  set_no_arg_commands_formatting (
+   &default_no_arg_commands_formatting[CM_enddots][HCC_type_preformatted], 
"...");
+
+  default_no_arg_commands_formatting[CM_ASTERISK][HCC_type_normal].text = 
"<br>";
+  set_no_arg_commands_formatting (
+   &default_no_arg_commands_formatting[CM_ASTERISK][HCC_type_preformatted], 
"\n");
+
+  for (i = 0; i < no_arg_formatted_cmd_nr; i++)
+    {
+      enum command_id cmd = no_arg_formatted_cmd.list[i];
+      /* prepare unicode numeric entities.  Freed at exit */
+      if (unicode_character_brace_no_arg_commands[cmd].codepoint)
+        {
+          unsigned long point_nr
+           = strtoul (unicode_character_brace_no_arg_commands[cmd].codepoint,
+                      NULL, 16);
+          char *entity;
+          xasprintf (&entity, "&#%lu;", point_nr);
+          unicode_entities[cmd] = entity;
+        }
+
+      /* css_strings */
+      if (cmd == CM_ASTERISK)
+        default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
+          = "\\A ";
+      else if (cmd == CM_error)
+        {
+          default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
+            = 0;
+          default_no_arg_commands_formatting[cmd][HCC_type_css_string].unset
+            = 1;
+        }
+      else if (unicode_character_brace_no_arg_commands[cmd].css_string)
+        {
+          unsigned long point_nr
+           = strtoul (unicode_character_brace_no_arg_commands[cmd].codepoint,
+                      NULL, 16);
+          if (point_nr < 128) /* 7bit ascii */
+            default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
+              = (char *)unicode_character_brace_no_arg_commands[cmd].text;
+          else
+          /* the value is never modified but the struct field type is not const
+             so need to cast to drop const */
+            default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
+              = (char 
*)unicode_character_brace_no_arg_commands[cmd].css_string;
+        }
+      else if (nobrace_symbol_text[cmd])
+        default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
+          /* the value is never modified but the struct field type is not const
+             so need to cast to drop const */
+          = (char *)nobrace_symbol_text[cmd];
+      else if (text_brace_no_arg_commands[cmd])
+          /* the value is never modified but the struct field type is not const
+             so need to cast to drop const */
+        default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
+          = (char *)text_brace_no_arg_commands[cmd];
+      else
+        fprintf (stderr, "BUG: %s: no css_string\n",
+                         builtin_command_data[cmd].cmdname);
+    }
+
+  /* w not in css_string, set the corresponding 
default_css_element_class_styles
+     especially, which also has none and not w in the class */
+  html_css_set_selector_style (&default_css_element_class_styles,
+                               "ul.mark-none", "list-style-type: none");
+
+  text_init (&css_string_text);
+  /* setup default_css_element_class_styles for mark commands based on
+     css strings */
+  for (i = 0; i < no_arg_formatted_cmd_nr; i++)
+    {
+      enum command_id cmd = no_arg_formatted_cmd.list[i];
+      if (default_no_arg_commands_formatting[cmd][HCC_type_css_string].text
+          && builtin_command_data[cmd].flags & CF_brace)
+        {
+          char *selector;
+          text_append_n (&css_string_text, "list-style-type: ", 17);
+          if (cmd == CM_bullet)
+            text_append_n (&css_string_text, "disc", 4);
+          else
+            {
+              char *css_string;
+              const char *p;
+              char *special_list_mark_command
+                = get_special_list_mark_css_string_no_arg_command (cmd);
+              if (special_list_mark_command)
+                css_string = special_list_mark_command;
+              else
+                css_string
+        = default_no_arg_commands_formatting[cmd][HCC_type_css_string].text;
+              p = html_after_escaped_characters (css_string);
+              text_append_n (&css_string_text, "\"", 1);
+              if (p && !*p)
+                {
+                  text_append_n (&css_string_text, css_string,
+                                 p - css_string - 1);
+                }
+              else
+                text_append (&css_string_text, css_string);
+              text_append_n (&css_string_text, "\"", 1);
+            }
+          xasprintf (&selector, "ul.mark-%s", builtin_command_name (cmd));
+          html_css_set_selector_style (&default_css_element_class_styles,
+                                       selector, css_string_text.text);
+          free (selector);
+          text_reset (&css_string_text);
+        }
+    }
+  free (css_string_text.text);
+}
+
+void
+html_converter_init_special_unit (CONVERTER *self)
+{
+  size_t nr_special_units;
+
+  /* NOTE if the special units can be customized, then
+     self->special_unit_varieties should be used directly instead.
+     Also default special units and special units indices should be
+     mapped instead of assuming that they are the same when setting
+     self->special_unit_info */
+  copy_strings (&self->special_unit_varieties, 
&default_special_unit_varieties);
+
+  nr_special_units = self->special_unit_varieties.number;
+
+  /* special units info */
+  /* set to defaults */
+  if (nr_special_units > 0)
+    {
+      size_t i;
+      enum special_unit_info_type j;
+      for (j = 0; j < SPECIAL_UNIT_INFO_TYPE_NR; j++)
+        {
+          size_t k;
+
+          self->special_unit_info[j]
+            = new_special_unit_info_type (nr_special_units);
+          for (k = 0; k < nr_special_units; k++)
+            {
+              if (default_special_unit_info[j][k])
+                self->special_unit_info[j][k]
+                  = strdup (default_special_unit_info[j][k]);
+            }
+        }
+      /* apply customization */
+      for (i = 0; i < self->customized_special_unit_info.number; i++)
+        {
+          SPECIAL_UNIT_INFO *special_unit_info
+            = &self->customized_special_unit_info.list[i];
+          size_t variety_idx = special_unit_info->variety_nr -1;
+          enum special_unit_info_type type = special_unit_info->type;
+
+          free (self->special_unit_info[type][variety_idx]);
+
+          if (special_unit_info->value)
+            self->special_unit_info[type][variety_idx]
+              = strdup (special_unit_info->value);
+          else
+            self->special_unit_info[type][variety_idx] = 0;
+        }
+    }
+}
+
+static HTML_DIRECTION_STRING_TRANSLATED *
+new_directions_strings_translated_type (int nr_string_directions)
+{
+  HTML_DIRECTION_STRING_TRANSLATED *result
+    = (HTML_DIRECTION_STRING_TRANSLATED *) malloc
+        (nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED));
+  memset (result, 0,
+          nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED));
+  return result;
+}
+
+static int *
+determine_non_default_special_unit_directions (const CONVERTER *self)
+{
+  size_t i;
+  size_t nr_special_units = self->special_unit_varieties.number;
+  int *non_default_special_unit_directions = 0;
+  /* determine the special units directions that are not the same as
+     the default units directions.  If not the same as the defaults,
+     the default direction info should not be used as they are not for
+     the customized special unit direction */
+  if (nr_special_units > 0)
+    {
+      non_default_special_unit_directions = (int *)
+                      malloc (nr_special_units * sizeof (int));
+      memset (non_default_special_unit_directions, 0,
+              nr_special_units * sizeof (int));
+
+      for (i = 0; i < nr_special_units; i++)
+        {
+          if (strcmp (self->special_unit_info[SUI_type_direction][i],
+                  default_special_unit_info[SUI_type_direction][i]))
+            non_default_special_unit_directions[i] = 1;
+        }
+    }
+  return non_default_special_unit_directions;
+}
+
+static int
+register_type_conversion_function (TYPE_CONVERSION_FUNCTION *result,
+                                   enum element_type type,
+                                   FORMATTING_REFERENCE *formatting_reference)
+{
+  if (formatting_reference->status > 0)
+    {
+      result->status = formatting_reference->status;
+      if (formatting_reference->status != FRS_status_ignored)
+        {
+          result->type_conversion = &html_type_conversion_external;
+          result->formatting_reference = formatting_reference;
+          return 1;
+        }
+    }
+  return 0;
+}
+
+static int
+register_type_open_function (TYPE_OPEN_FUNCTION *result,
+                             enum element_type type,
+                             FORMATTING_REFERENCE *formatting_reference)
+{
+  if (formatting_reference->status > 0)
+    {
+      result->status = formatting_reference->status;
+      if (formatting_reference->status != FRS_status_ignored)
+        {
+          result->type_open = &html_type_open_external;
+          result->formatting_reference = formatting_reference;
+          return 1;
+        }
+    }
+  return 0;
+}
+
+static int
+register_command_conversion_function (COMMAND_CONVERSION_FUNCTION *result,
+                         enum command_id cmd,
+                         FORMATTING_REFERENCE *formatting_reference)
+{
+  if (formatting_reference->status > 0)
+    {
+      result->status = formatting_reference->status;
+      if (formatting_reference->status != FRS_status_ignored)
+        {
+          result->command_conversion = &html_command_conversion_external;
+          result->formatting_reference = formatting_reference;
+          return 1;
+        }
+    }
+  return 0;
+}
+
+static int
+register_command_open_function (COMMAND_OPEN_FUNCTION *result,
+                                enum command_id cmd,
+                                FORMATTING_REFERENCE *formatting_reference)
+{
+  if (formatting_reference->status > 0)
+    {
+      result->status = formatting_reference->status;
+      if (formatting_reference->status != FRS_status_ignored)
+        {
+          result->command_open = &html_command_open_external;
+          result->formatting_reference = formatting_reference;
+          return 1;
+        }
+    }
+  return 0;
+}
+
+static int
+register_output_unit_conversion_function
+                                  (OUTPUT_UNIT_CONVERSION_FUNCTION *result,
+                                   enum output_unit_type type,
+                                   FORMATTING_REFERENCE *formatting_reference)
+{
+  if (formatting_reference->status > 0)
+    {
+      result->status = formatting_reference->status;
+      if (formatting_reference->status != FRS_status_ignored)
+        {
+          result->output_unit_conversion
+            = &html_output_unit_conversion_external;
+          result->formatting_reference = formatting_reference;
+          return 1;
+        }
+    }
+  return 0;
+}
+
+static int
+register_special_unit_body_formatting_function
+                                  (SPECIAL_UNIT_BODY_FORMATTING *result,
+                                   const char *special_unit_variety,
+                                   FORMATTING_REFERENCE *formatting_reference)
+{
+  if (formatting_reference && formatting_reference->status > 0)
+    {
+      result->status = formatting_reference->status;
+      if (formatting_reference->status != FRS_status_ignored)
+        {
+          result->special_unit_body_formatting
+               = &html_special_unit_body_formatting_external;
+          result->formatting_reference = formatting_reference;
+          return 1;
+        }
+    }
+  return 0;
+}
+
+static int
+compare_htmlxref_manual (const void *a, const void *b)
+{
+  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);
+}
+
+typedef struct COMMAND_INTERNAL_CONVERSION {
+    enum command_id cmd;
+    void (* command_conversion) (struct CONVERTER *self,
+                                 const enum command_id cmd,
+                                 const ELEMENT *element,
+                                 const HTML_ARGS_FORMATTED *args_formatted,
+                                 const char *content, TEXT *result);
+} COMMAND_INTERNAL_CONVERSION;
+
+/* associate command to the C function implementing the conversion */
+static const COMMAND_INTERNAL_CONVERSION commands_internal_conversion_table[] 
= {
+  {CM_today, &html_convert_today_command},
+  {CM_w, &html_convert_w_command},
+  {CM_value, &html_convert_value_command},
+  {CM_email, &html_convert_email_command},
+  {CM_abbr, &html_convert_explained_command},
+  {CM_acronym, &html_convert_explained_command},
+  {CM_anchor, &html_convert_anchor_command},
+  {CM_footnote, &html_convert_footnote_command},
+  {CM_uref, &html_convert_uref_command},
+  {CM_url, &html_convert_uref_command},
+  {CM_image, &html_convert_image_command},
+  {CM_math, &html_convert_math_command},
+  /* note that if indicateurl had been in style_formatted_cmd this
+     would have prevented indicateurl to be associated to
+     convert_style_command */
+  {CM_indicateurl, &html_convert_indicateurl_command},
+  {CM_titlefont, &html_convert_titlefont_command},
+  {CM_U, &html_convert_U_command},
+
+  {CM_inlineraw, &html_convert_inline_command},
+  {CM_inlinefmt, &html_convert_inline_command},
+  {CM_inlinefmtifelse, &html_convert_inline_command},
+  {CM_inlineifclear, &html_convert_inline_command},
+  {CM_inlineifset, &html_convert_inline_command},
+
+  {CM_inforef, &html_convert_xref_command},
+  {CM_link, &html_convert_xref_command},
+  {CM_xref, &html_convert_xref_command},
+  {CM_ref, &html_convert_xref_command},
+  {CM_pxref, &html_convert_xref_command},
+
+  {CM_html, &html_convert_raw_command},
+  {CM_tex, &html_convert_raw_command},
+  {CM_xml, &html_convert_raw_command},
+  {CM_docbook, &html_convert_raw_command},
+  {CM_latex, &html_convert_raw_command},
+
+  {CM_indentedblock, &html_convert_indented_command},
+  {CM_smallindentedblock, &html_convert_indented_command},
+  {CM_verbatim, &html_convert_verbatim_command},
+  {CM_displaymath, &html_convert_displaymath_command},
+  {CM_raggedright, &html_convert_simple_block_command},
+  {CM_flushleft, &html_convert_simple_block_command},
+  {CM_flushright, &html_convert_simple_block_command},
+  {CM_group, &html_convert_simple_block_command},
+  {CM_menu, &html_convert_menu_command},
+  {CM_detailmenu, &html_convert_menu_command},
+  {CM_float, &html_convert_float_command},
+  {CM_quotation, &html_convert_quotation_command},
+  {CM_smallquotation, &html_convert_quotation_command},
+  {CM_cartouche, &html_convert_cartouche_command},
+  {CM_itemize, &html_convert_itemize_command},
+  {CM_enumerate, &html_convert_enumerate_command},
+  {CM_multitable, &html_convert_multitable_command},
+  {CM_table, &html_convert_xtable_command},
+  {CM_ftable, &html_convert_xtable_command},
+  {CM_vtable, &html_convert_xtable_command},
+
+  {CM_verbatiminclude, &html_convert_verbatiminclude_command},
+  {CM_sp, &html_convert_sp_command},
+  {CM_exdent, &html_convert_exdent_command},
+  {CM_center, &html_convert_center_command},
+  {CM_author, &html_convert_author_command},
+  {CM_title, &html_convert_title_command},
+  {CM_subtitle, &html_convert_subtitle_command},
+  {CM_item, &html_convert_item_command},
+  {CM_headitem, &html_convert_item_command},
+  {CM_itemx, &html_convert_item_command},
+  {CM_tab, &html_convert_tab_command},
+  {CM_insertcopying, &html_convert_insertcopying_command},
+  {CM_listoffloats, &html_convert_listoffloats_command},
+  {CM_printindex, &html_convert_printindex_command},
+  /* @informative_global_commands in perl */
+  {CM_documentlanguage, &html_convert_informative_command},
+  {CM_footnotestyle, &html_convert_informative_command},
+  {CM_xrefautomaticsectiontitle, &html_convert_informative_command},
+  {CM_deftypefnnewline, &html_convert_informative_command},
+
+  {CM_contents, &html_convert_contents_command},
+  {CM_shortcontents, &html_convert_contents_command},
+  {CM_summarycontents, &html_convert_contents_command},
+
+  /* other @def* commands are handled together */
+  {CM_defblock, &html_convert_def_command},
+
+  {CM_node, &html_convert_heading_command},
+  {CM_top, &html_convert_heading_command},
+  {CM_chapter, &html_convert_heading_command},
+  {CM_unnumbered, &html_convert_heading_command},
+  {CM_chapheading, &html_convert_heading_command},
+  {CM_appendix, &html_convert_heading_command},
+  {CM_section, &html_convert_heading_command},
+  {CM_unnumberedsec, &html_convert_heading_command},
+  {CM_heading, &html_convert_heading_command},
+  {CM_appendixsec, &html_convert_heading_command},
+  {CM_subsection, &html_convert_heading_command},
+  {CM_unnumberedsubsec, &html_convert_heading_command},
+  {CM_subheading, &html_convert_heading_command},
+  {CM_appendixsubsec, &html_convert_heading_command},
+  {CM_subsubsection, &html_convert_heading_command},
+  {CM_unnumberedsubsubsec, &html_convert_heading_command},
+  {CM_subsubheading, &html_convert_heading_command},
+  {CM_appendixsubsubsec, &html_convert_heading_command},
+  {CM_part, &html_convert_heading_command},
+  {CM_appendixsection, &html_convert_heading_command},
+  {CM_majorheading, &html_convert_heading_command},
+  {CM_centerchap, &html_convert_heading_command},
+
+  {0, 0},
+};
+
+typedef struct COMMAND_INTERNAL_OPEN {
+    enum command_id cmd;
+    void (* command_open) (CONVERTER *self, const enum command_id cmd,
+                           const ELEMENT *element, TEXT *result);
+} COMMAND_INTERNAL_OPEN;
+
+/* associate command to the C function implementing the opening */
+static const COMMAND_INTERNAL_OPEN commands_internal_open_table[] = {
+  {CM_quotation, &html_open_quotation_command},
+  {CM_smallquotation, &html_open_quotation_command},
+  {0, 0},
+};
+
+typedef struct TYPE_INTERNAL_CONVERSION {
+    enum element_type type;
+    void (* type_conversion) (CONVERTER *self, const enum element_type type,
+                              const ELEMENT *element, const char *content,
+                              TEXT *result);
+} TYPE_INTERNAL_CONVERSION;
+
+/* associate type to the C function implementing the conversion */
+static const TYPE_INTERNAL_CONVERSION types_internal_conversion_table[] = {
+  {ET_text, &html_convert_text},
+  {ET_paragraph, &html_convert_paragraph_type},
+  {ET_preformatted, &html_convert_preformatted_type},
+  {ET_balanced_braces, &html_convert_balanced_braces_type},
+  {ET_index_entry_command, &html_convert_index_entry_command_type},
+  {ET_definfoenclose_command, &html_convert_definfoenclose_type},
+  {ET_untranslated_def_line_arg,
+   &html_convert_untranslated_def_line_arg_type},
+  {ET_row, &html_convert_row_type},
+  {ET_multitable_head, &html_convert_multitable_head_type},
+  {ET_multitable_body, &html_convert_multitable_body_type},
+  {ET_menu_entry, &html_convert_menu_entry_type},
+  {ET_menu_comment, &html_convert_menu_comment_type},
+  {ET_before_item, &html_convert_before_item_type},
+  {ET_table_term, &html_convert_table_term_type},
+  {ET_def_line, &html_convert_def_line_type},
+  {ET_def_item, &html_convert_def_item_type},
+  {ET_inter_def_item, &html_convert_def_item_type},
+  {ET_before_defline, &html_convert_def_item_type},
+  {ET_table_definition, &html_convert_table_definition_type},
+  {ET_inter_item, &html_convert_table_definition_type},
+
+  {0, 0},
+};
+
+typedef struct TYPE_INTERNAL_OPEN {
+    enum element_type type;
+    void (* type_open) (CONVERTER *self, const enum element_type type,
+                        const ELEMENT *element, TEXT *result);
+} TYPE_INTERNAL_OPEN;
+
+/* associate type to the C function implementing the opening */
+static const TYPE_INTERNAL_OPEN types_internal_open_table[] = {
+  {ET_paragraph, &html_open_inline_container_type},
+  {ET_preformatted, &html_open_inline_container_type},
+  {0, 0},
+};
+
+typedef struct OUTPUT_UNIT_INTERNAL_CONVERSION {
+    enum output_unit_type type;
+    void (* output_unit_conversion) (CONVERTER *self,
+                        const enum output_unit_type unit_type,
+                        const OUTPUT_UNIT *output_unit, const char *content,
+                        TEXT *result);
+} OUTPUT_UNIT_INTERNAL_CONVERSION;
+
+static const OUTPUT_UNIT_INTERNAL_CONVERSION 
output_units_internal_conversion_table[] = {
+  {OU_special_unit, &html_convert_special_unit_type},
+  {OU_unit, &html_convert_unit_type},
+  {0, 0},
+};
+
+typedef struct SPECIAL_UNIT_BODY_INTERNAL_CONVERSION {
+    char *special_unit_variety;
+    void (* special_unit_body_formatting) (CONVERTER *self,
+                               const size_t special_unit_number,
+                               const char *special_unit_variety,
+                               const OUTPUT_UNIT *output_unit,
+                               TEXT *result);
+} SPECIAL_UNIT_BODY_INTERNAL_CONVERSION;
+
+static const SPECIAL_UNIT_BODY_INTERNAL_CONVERSION
+   special_unit_body_internal_formatting_table[] = {
+  {"contents", &html_default_format_special_body_contents},
+  {"shortcontents", &html_default_format_special_body_shortcontents},
+  {"footnotes", &html_default_format_special_body_footnotes},
+  {"about", &html_default_format_special_body_about},
+  {0, 0},
+};
+
+/* When initializing from Perl, html_converter_initialize_sv is called first
+   to set customization information based on Perl data.  Then this function
+   is called for the remainder of initialization that requires customization
+   information.
+ */
+void
+html_converter_customize (CONVERTER *self)
+{
+  size_t i;
+  int l;
+  enum direction_string_type DS_type;
+  size_t nr_special_units = self->special_unit_varieties.number;
+  /* The corresponding direction without FirstInFile are used instead
+     of FirstInFile*, so the directions_strings are not set */
+  int nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
+                             + (int) nr_special_units;
+  int nr_dir_str_contexts = TDS_context_string +1;
+  int *non_default_special_unit_directions =
+     determine_non_default_special_unit_directions (self);
+
+  /* counters of external formatting functions */
+  int external_special_unit_body_formatting_function = 0;
+  int external_output_unit_conversion_function = 0;
+  int external_command_conversion_function = 0;
+  int external_command_open_function = 0;
+  int external_type_conversion_function = 0;
+  int external_type_open_function = 0;
+  int external_formatting_function = 0;
+
+  if (self->ids_data_type == IDT_perl_hashmap)
+    init_registered_ids_hv (self);
+  else
+    self->registered_ids = new_string_list ();
+
+  /* for @sc */
+  for (l = 0; default_upper_case_commands[l]; l++)
+    self->upper_case[default_upper_case_commands[l]] = 1;
+
+  for (l = 0; default_code_types[l]; l++)
+    self->code_types[default_code_types[l]] = 1;
+
+  for (l = 0; default_pre_class_types[l].type; l++)
+    {
+      const PRE_CLASS_TYPE_INFO *pre_class_type = &default_pre_class_types[l];
+      self->pre_class_types[pre_class_type->type]
+        = strdup (pre_class_type->pre_class);
+    }
+
+  for (l = 0; xml_accent_text_entities[l].cmd; l++)
+    {
+      enum command_id cmd = xml_accent_text_entities[l].cmd;
+      const ACCENT_ENTITY_INFO *xml_accent_info
+        = &xml_accent_text_entities[l].accent_entity_info;
+      ACCENT_ENTITY_INFO *accent_info = &self->accent_entities[cmd];
+
+      if (xml_accent_info->entity)
+        accent_info->entity = strdup (xml_accent_info->entity);
+      if (xml_accent_info->characters)
+        accent_info->characters = strdup (xml_accent_info->characters);
+    }
+
+  for (i = 0; i < style_formatted_cmd.number; i++)
+    {
+      enum command_id cmd = style_formatted_cmd.list[i];
+      enum conversion_context cctx;
+      for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
+        {
+          HTML_STYLE_COMMAND_CONVERSION *format_spec
+            = &self->html_style_command_conversion[cmd][cctx];
+          HTML_STYLE_COMMAND_CONVERSION *default_spec
+            = &default_style_commands_formatting[cmd][cctx];
+
+          if (default_spec->element)
+            format_spec->element = strdup (default_spec->element);
+          format_spec->quote = default_spec->quote;
+        }
+    }
+
+  /* apply customization (from Perl) */
+
+  if (self->html_customized_upper_case_commands)
+    {
+      for (l = 0; self->html_customized_upper_case_commands[l].cmd; l++)
+        {
+          COMMAND_INTEGER_INFORMATION *customized_upper
+            = &self->html_customized_upper_case_commands[l];
+          self->upper_case[customized_upper->cmd] = customized_upper->integer;
+        }
+    }
+
+  if (self->html_customized_code_types)
+    {
+      for (l = 0; self->html_customized_code_types[l].type; l++)
+        {
+          TYPE_INTEGER_INFORMATION *customized_code
+            = &self->html_customized_code_types[l];
+          self->code_types[customized_code->type] = customized_code->integer;
+        }
+    }
+
+  if (self->html_customized_pre_class_types)
+    {
+      for (l = 0; self->html_customized_pre_class_types[l].type; l++)
+        {
+          PRE_CLASS_TYPE_INFO *customized_pre_class
+            = &self->html_customized_pre_class_types[l];
+          enum element_type type = customized_pre_class->type;
+
+          free (self->pre_class_types[type]);
+          self->pre_class_types[type]
+             = strdup (customized_pre_class->pre_class);
+        }
+    }
+
+  if (self->html_customized_accent_entity_info)
+    {
+      for (l = 0; self->html_customized_accent_entity_info[l].cmd; l++)
+        {
+          enum command_id cmd = 
self->html_customized_accent_entity_info[l].cmd;
+          const ACCENT_ENTITY_INFO *custom_accent_info
+            = &self->html_customized_accent_entity_info[l].accent_entity_info;
+          ACCENT_ENTITY_INFO *accent_info = &self->accent_entities[cmd];
+
+          if (custom_accent_info->entity)
+            {
+              free (accent_info->entity);
+              if (strlen (custom_accent_info->entity))
+                accent_info->entity = strdup (custom_accent_info->entity);
+              else
+                accent_info->entity = 0;
+            }
+          if (custom_accent_info->characters)
+            {
+              free (accent_info->characters);
+              accent_info->characters = strdup 
(custom_accent_info->characters);
+            }
+        }
+    }
+
+  if (self->html_customized_style_commands)
+    {
+      for (l = 0; self->html_customized_style_commands[l].cmd; l++)
+        {
+          enum conversion_context cctx;
+          enum command_id cmd = self->html_customized_style_commands[l].cmd;
+          /* should not happen thanks to checks in perl
+          if (!(html_commands_data[cmd].flags & HF_style_command))
+            fprintf (stderr, "ERROR: %s: customized as style command\n",
+                     builtin_command_name (cmd));
+           */
+
+          for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++)
+            {
+              if (self->html_customized_style_commands[l].conversion[cctx])
+                {
+                  HTML_STYLE_COMMAND_CONVERSION *format_spec
+                    = &self->html_style_command_conversion[cmd][cctx];
+                  HTML_STYLE_COMMAND_CONVERSION *custom_spec
+                    = self->html_customized_style_commands[l].conversion[cctx];
+
+                  free (format_spec->element);
+
+                  if (custom_spec->element)
+                    format_spec->element = strdup (custom_spec->element);
+                  else
+                    format_spec->element = 0;
+                  format_spec->quote = custom_spec->quote;
+                }
+            }
+        }
+    }
+
+  /* initialization needing some information not available before.  Besides
+     customized information, mainly nr_special_units, which we
+     pretend could be customize (even though it cannot for now) */
+
+  self->direction_unit_direction_name = (const char **) malloc
+     ((nr_special_units + NON_SPECIAL_DIRECTIONS_NR +1) * sizeof (char *));
+  memcpy (self->direction_unit_direction_name, html_button_direction_names,
+          NON_SPECIAL_DIRECTIONS_NR * sizeof (char *));
+  memcpy (self->direction_unit_direction_name + NON_SPECIAL_DIRECTIONS_NR,
+          self->special_unit_info[SUI_type_direction],
+          nr_special_units * sizeof (char *));
+  self->direction_unit_direction_name[
+               nr_special_units + NON_SPECIAL_DIRECTIONS_NR] = 0;
+  /*
+  for (l = 0; self->direction_unit_direction_name[l]; l++)
+    fprintf (stderr, "DEBUG: direction unit names: %d '%s'\n", l,
+             self->direction_unit_direction_name[l]);
+   */
+
+
+
+  /* setup translated_direction_strings */
+  for (DS_type = 0; DS_type < TDS_TRANSLATED_MAX_NR; DS_type++)
+    {
+      self->translated_direction_strings[DS_type]
+        = new_directions_strings_translated_type (nr_string_directions);
+      for (l = 0; l < nr_string_directions; l++)
+        {
+          HTML_DIRECTION_STRING_TRANSLATED *dir_string_translated
+            = &self->translated_direction_strings[DS_type][l];
+          if (self->customized_translated_direction_strings[DS_type]
+              && self->customized_translated_direction_strings[DS_type][l])
+            {
+              HTML_DIRECTION_STRING_TRANSLATED *custom_dir_translated
+                = self->customized_translated_direction_strings[DS_type][l];
+              if (custom_dir_translated->to_convert)
+                dir_string_translated->to_convert
+                  = strdup (custom_dir_translated->to_convert);
+              else
+                {
+                  int j;
+
+                  for (j = 0; j < nr_dir_str_contexts; j++)
+                    if (custom_dir_translated->converted[j])
+                      dir_string_translated->converted[j]
+                        = strdup (custom_dir_translated->converted[j]);
+                }
+            }
+          else if (l < NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
+                  || !non_default_special_unit_directions[
+                       l - (NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR)])
+            {
+              const HTML_DEFAULT_DIRECTION_STRING_TRANSLATED 
*default_dir_translated
+                = &default_translated_directions_strings[DS_type][l];
+              if (default_dir_translated->to_convert)
+                dir_string_translated->to_convert
+                  = strdup (default_dir_translated->to_convert);
+              else
+                {
+                  if (default_dir_translated->converted)
+                    {
+                      int j;
+
+                      for (j = 0; j < nr_dir_str_contexts; j++)
+                          dir_string_translated->converted[j]
+                            = strdup (default_dir_translated->converted);
+                    }
+                }
+            }
+        }
+    }
+
+  free (non_default_special_unit_directions);
+
+
+  /* allocate space for translated tree types, they will be created
+     on-demand during the conversion */
+  for (l = 0; l < SUIT_type_heading+1; l++)
+    {
+      self->special_unit_info_tree[l] = (ELEMENT **)
+        malloc ((nr_special_units +1) * sizeof (ELEMENT *));
+      memset (self->special_unit_info_tree[l], 0,
+               (nr_special_units +1) * sizeof (ELEMENT *));
+    }
+
+  self->global_units_directions
+    = (const OUTPUT_UNIT **) malloc ((D_Last + nr_special_units+1)
+                               * sizeof (OUTPUT_UNIT));
+
+  /* note that we allocate the same size as no_arg_formatted_cmd
+     even though in general there are much less translated commands,
+     for simplicity */
+  if (no_arg_formatted_cmd.number)
+    {
+      self->no_arg_formatted_cmd_translated.list = (enum command_id *)
+       malloc (no_arg_formatted_cmd.number * sizeof (enum command_id));
+      memset (self->no_arg_formatted_cmd_translated.list, 0,
+              no_arg_formatted_cmd.number * sizeof (enum command_id));
+    }
+
+  for (l = 0; command_special_unit_variety[l].cmd; l++)
+    {
+      char *special_unit_variety = command_special_unit_variety[l].variety;
+      /* number is index +1 */
+      size_t number = find_string (&self->special_unit_varieties,
+                                   special_unit_variety);
+      enum command_id cmd = command_special_unit_variety[l].cmd;
+      html_commands_data[cmd].flags |= HF_special_variety;
+      self->command_special_variety_name_index[l].cmd = cmd;
+      self->command_special_variety_name_index[l].index = number - 1;
+    }
+
+  for (l = 0; ignored_types[l]; l++)
+    {
+      enum element_type type = ignored_types[l];
+      self->type_conversion_function[type].status = FRS_status_ignored;
+    }
+
+  for (l = 0; l < TXI_TREE_TYPES_NUMBER; l++)
+    {
+      int status = register_type_conversion_function (
+                                        &self->type_conversion_function[l],
+                                        l, &self->types_conversion[l]);
+      external_type_conversion_function += status;
+
+      status = register_type_open_function (&self->type_open_function[l],
+                                   l, &self->types_open[l]);
+      external_type_open_function += status;
+
+      register_type_conversion_function (
+             &self->css_string_type_conversion_function[l], l,
+             &self->css_string_types_conversion[l]);
+    }
+
+  for (l = 0; l < BUILTIN_CMD_NUMBER; l++)
+    {
+      int status = register_command_conversion_function (
+                            &self->command_conversion_function[l],
+                            l, &self->commands_conversion[l]);
+      external_command_conversion_function += status;
+
+      status = register_command_open_function (
+                            &self->command_open_function[l],
+                            l, &self->commands_open[l]);
+      external_command_open_function += status;
+
+      register_command_conversion_function (
+            &self->css_string_command_conversion_function[l], l,
+             &self->css_string_commands_conversion[l]);
+    }
+
+  for (l = 0; l < OU_special_unit+1; l++)
+    {
+      int status = register_output_unit_conversion_function
+                                  (&self->output_unit_conversion_function[l],
+                                        l, &self->output_units_conversion[l]);
+      external_output_unit_conversion_function += status;
+    }
+
+  self->special_unit_body_formatting = (SPECIAL_UNIT_BODY_FORMATTING *)
+    malloc (nr_special_units * sizeof (SPECIAL_UNIT_BODY_FORMATTING));
+  memset (self->special_unit_body_formatting, 0,
+          nr_special_units * sizeof (SPECIAL_UNIT_BODY_FORMATTING));
+
+  for (i = 0; i < nr_special_units; i++)
+    {
+      int status;
+      FORMATTING_REFERENCE *formatting_reference = 0;
+      if (self->special_unit_body)
+        {
+          formatting_reference = &self->special_unit_body[i];
+        }
+      status = register_special_unit_body_formatting_function
+                                  (&self->special_unit_body_formatting[i],
+          self->special_unit_varieties.list[i], formatting_reference);
+      external_special_unit_body_formatting_function += status;
+    }
+
+  qsort (self->htmlxref.list, self->htmlxref.number,
+         sizeof (HTMLXREF_MANUAL), compare_htmlxref_manual);
+
+  /* set to customization such that it is not replaced by C functions */
+  if (self->conf->XS_EXTERNAL_FORMATTING.o.integer > 0)
+    {
+      for (l = 0; l < FR_format_translate_message+1; l++)
+        if (self->formatting_references[l].status == FRS_status_default_set)
+          {
+            self->formatting_references[l].status
+                                   = FRS_status_customization_set;
+            external_formatting_function++;
+          }
+    }
+  else
+    { /* count the functions implemented in perl */
+      for (l = 0; l < FR_format_translate_message+1; l++)
+        if (self->formatting_references[l].status
+                  == FRS_status_customization_set)
+          external_formatting_function++;
+    }
+
+  /* remaining of the file is for the replacement of call to external
+     functions by internal functions in C.
+   */
+  if (self->conf->XS_EXTERNAL_CONVERSION.o.integer > 0)
+    goto out;
+
+  for (i = 0; types_internal_conversion_table[i].type_conversion; i++)
+    {
+      enum element_type type = types_internal_conversion_table[i].type;
+      TYPE_CONVERSION_FUNCTION *type_conversion
+         = &self->type_conversion_function[type];
+      TYPE_CONVERSION_FUNCTION *css_string_type_conversion
+         = &self->css_string_type_conversion_function[type];
+      if (type_conversion->status == FRS_status_default_set
+          || type_conversion->status == FRS_status_none)
+        {
+          type_conversion->formatting_reference = 0;
+          type_conversion->status = FRS_status_internal;
+          type_conversion->type_conversion
+              = types_internal_conversion_table[i].type_conversion;
+          if (type_conversion->status == FRS_status_default_set)
+            external_type_conversion_function--;
+        }
+      css_string_type_conversion->formatting_reference = 0;
+      css_string_type_conversion->status = FRS_status_internal;
+      if (type == ET_text)
+        css_string_type_conversion->type_conversion
+          = &html_css_string_convert_text;
+      else
+        css_string_type_conversion->type_conversion
+          = types_internal_conversion_table[i].type_conversion;
+    }
+
+  for (i = 0; types_internal_open_table[i].type_open; i++)
+    {
+      enum element_type type = types_internal_open_table[i].type;
+      TYPE_OPEN_FUNCTION *type_open = &self->type_open_function[type];
+      if (type_open->status == FRS_status_default_set
+          || type_open->status == FRS_status_none)
+        {
+          type_open->formatting_reference = 0;
+          type_open->status = FRS_status_internal;
+          type_open->type_open
+              = types_internal_open_table[i].type_open;
+          if (type_open->status == FRS_status_default_set)
+            external_type_open_function--;
+        }
+    }
+
+  for (i = 0; commands_internal_conversion_table[i].command_conversion; i++)
+    {
+      enum command_id cmd = commands_internal_conversion_table[i].cmd;
+      COMMAND_CONVERSION_FUNCTION *command_conversion
+               = &self->command_conversion_function[cmd];
+      COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
+               = &self->css_string_command_conversion_function[cmd];
+      if (command_conversion->status == FRS_status_default_set
+          || command_conversion->status == FRS_status_none)
+        {
+          command_conversion->formatting_reference = 0;
+          command_conversion->status = FRS_status_internal;
+          command_conversion->command_conversion
+              = commands_internal_conversion_table[i].command_conversion;
+          if (command_conversion->status == FRS_status_default_set)
+            external_command_conversion_function--;
+        }
+      css_string_command_conversion->formatting_reference = 0;
+      css_string_command_conversion->status = FRS_status_internal;
+      css_string_command_conversion->command_conversion
+              = commands_internal_conversion_table[i].command_conversion;
+    }
+
+  if (strcmp (self->conf->FORMAT_MENU.o.string, "menu"))
+    {
+      static enum command_id menu_cmd_list[]
+       = {CM_menu, CM_detailmenu, 0};
+      int j;
+      for (j = 0; menu_cmd_list[j]; j++)
+        {
+          enum command_id cmd = menu_cmd_list[j];
+          COMMAND_CONVERSION_FUNCTION *command_conversion
+               = &self->command_conversion_function[cmd];
+          if (command_conversion->status == FRS_status_internal)
+            command_conversion->status = FRS_status_ignored;
+        }
+    }
+  for (i = 0; i < format_raw_cmd.number; i++)
+    {
+      enum command_id cmd = format_raw_cmd.list[i];
+      COMMAND_CONVERSION_FUNCTION *command_conversion
+        = &self->command_conversion_function[cmd];
+      if (command_conversion->status == FRS_status_internal)
+        {
+          const char *format = builtin_command_name (cmd);
+          if (!format_expanded_p (self->expanded_formats, format))
+            command_conversion->status = FRS_status_ignored;
+        }
+    }
+
+  /* all the no arg formatted commands are implemented in C */
+  for (i = 0; i < no_arg_formatted_cmd.number; i++)
+    {
+      enum command_id cmd = no_arg_formatted_cmd.list[i];
+      COMMAND_CONVERSION_FUNCTION *command_conversion
+           = &self->command_conversion_function[cmd];
+      COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
+           = &self->css_string_command_conversion_function[cmd];
+      if (command_conversion->status == FRS_status_default_set
+          || command_conversion->status == FRS_status_none)
+        {
+          command_conversion->formatting_reference = 0;
+          command_conversion->status = FRS_status_internal;
+          command_conversion->command_conversion
+            = &html_convert_no_arg_command;
+          if (command_conversion->status == FRS_status_default_set)
+            external_command_conversion_function--;
+        }
+
+      css_string_command_conversion->formatting_reference = 0;
+      css_string_command_conversion->status = FRS_status_internal;
+      css_string_command_conversion->command_conversion
+        = &html_css_string_convert_no_arg_command;
+    }
+
+  /* accents commands implemented in C */
+  if (accent_cmd.number)
+    {
+      for (i = 0; i < accent_cmd.number; i++)
+        {
+          enum command_id cmd = accent_cmd.list[i];
+          COMMAND_CONVERSION_FUNCTION *command_conversion
+               = &self->command_conversion_function[cmd];
+          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
+               = &self->css_string_command_conversion_function[cmd];
+          if (command_conversion->status == FRS_status_default_set
+              || command_conversion->status == FRS_status_none)
+            {
+              command_conversion->formatting_reference = 0;
+              command_conversion->status = FRS_status_internal;
+              command_conversion->command_conversion
+                = &html_convert_accent_command;
+              if (command_conversion->status == FRS_status_default_set)
+                external_command_conversion_function--;
+            }
+          css_string_command_conversion->formatting_reference = 0;
+          css_string_command_conversion->status = FRS_status_internal;
+          css_string_command_conversion->command_conversion
+            = &html_css_string_convert_accent_command;
+        }
+    }
+
+  /* all the commands in style_formatted_cmd are implemented in C.
+     It is not only the style commands, some others too.  indicateurl
+     is not in style_formatted_cmd for now either */
+  if (style_formatted_cmd.number)
+    {
+      for (i = 0; i < style_formatted_cmd.number; i++)
+        {
+          enum command_id cmd = style_formatted_cmd.list[i];
+          COMMAND_CONVERSION_FUNCTION *command_conversion
+               = &self->command_conversion_function[cmd];
+          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
+               = &self->css_string_command_conversion_function[cmd];
+
+          if (command_conversion->status == FRS_status_default_set
+              || command_conversion->status == FRS_status_none)
+            {
+              command_conversion->formatting_reference = 0;
+              command_conversion->status = FRS_status_internal;
+              command_conversion->command_conversion
+                = &html_convert_style_command;
+              if (command_conversion->status == FRS_status_default_set)
+                external_command_conversion_function--;
+            }
+
+          css_string_command_conversion->formatting_reference = 0;
+          css_string_command_conversion->status = FRS_status_internal;
+          css_string_command_conversion->command_conversion
+            = &html_convert_style_command;
+        }
+    }
+
+  /* preformatted commands are implemented in C */
+  if (preformatted_cmd_list.top > 0)
+    {
+      for (i = 0; i < preformatted_cmd_list.top; i++)
+        {
+          enum command_id cmd = preformatted_cmd_list.stack[i];
+          COMMAND_CONVERSION_FUNCTION *command_conversion
+               = &self->command_conversion_function[cmd];
+          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
+               = &self->css_string_command_conversion_function[cmd];
+
+          if (command_conversion->status == FRS_status_default_set
+              || command_conversion->status == FRS_status_none)
+            {
+              command_conversion->formatting_reference = 0;
+              command_conversion->status = FRS_status_internal;
+              command_conversion->command_conversion
+                = &html_convert_preformatted_command;
+              if (command_conversion->status == FRS_status_default_set)
+                external_command_conversion_function--;
+            }
+
+          css_string_command_conversion->formatting_reference = 0;
+          css_string_command_conversion->status = FRS_status_internal;
+          css_string_command_conversion->command_conversion
+            = &html_convert_preformatted_command;
+        }
+    }
+  /* def commands are implemented in C */
+  if (def_cmd_list.top > 0)
+    {
+      for (i = 0; i < def_cmd_list.top; i++)
+        {
+          enum command_id cmd = def_cmd_list.stack[i];
+          COMMAND_CONVERSION_FUNCTION *command_conversion
+               = &self->command_conversion_function[cmd];
+          COMMAND_CONVERSION_FUNCTION *css_string_command_conversion
+               = &self->css_string_command_conversion_function[cmd];
+
+          if (command_conversion->status == FRS_status_default_set
+              || command_conversion->status == FRS_status_none)
+            {
+              command_conversion->formatting_reference = 0;
+              command_conversion->status = FRS_status_internal;
+              command_conversion->command_conversion
+                = &html_convert_def_command;
+              if (command_conversion->status == FRS_status_default_set)
+                external_command_conversion_function--;
+            }
+
+          css_string_command_conversion->formatting_reference = 0;
+          css_string_command_conversion->status = FRS_status_internal;
+          css_string_command_conversion->command_conversion
+            = &html_convert_def_command;
+        }
+    }
+
+  for (i = 0; commands_internal_open_table[i].command_open; i++)
+    {
+      enum command_id cmd = commands_internal_open_table[i].cmd;
+      COMMAND_OPEN_FUNCTION *command_open = &self->command_open_function[cmd];
+      if (command_open->status == FRS_status_default_set
+          || command_open->status == FRS_status_none)
+        {
+          command_open->formatting_reference = 0;
+          command_open->status = FRS_status_internal;
+          command_open->command_open
+              = commands_internal_open_table[i].command_open;
+          if (command_open->status == FRS_status_default_set)
+            external_command_open_function--;
+        }
+    }
+
+  for (i = 0;
+     output_units_internal_conversion_table[i].output_unit_conversion; i++)
+    {
+      enum output_unit_type type
+           = output_units_internal_conversion_table[i].type;
+      OUTPUT_UNIT_CONVERSION_FUNCTION *output_unit_conversion
+         = &self->output_unit_conversion_function[type];
+      if (output_unit_conversion->status == FRS_status_default_set
+          || output_unit_conversion->status == FRS_status_none)
+        {
+          output_unit_conversion->formatting_reference = 0;
+          output_unit_conversion->status = FRS_status_internal;
+          output_unit_conversion->output_unit_conversion
+           = output_units_internal_conversion_table[i].output_unit_conversion;
+          if (output_unit_conversion->status == FRS_status_default_set)
+            external_output_unit_conversion_function--;
+        }
+    }
+
+  for (i = 0;
+    special_unit_body_internal_formatting_table[i].special_unit_variety; i++)
+    {
+      const SPECIAL_UNIT_BODY_INTERNAL_CONVERSION *internal_conversion
+        = &special_unit_body_internal_formatting_table[i];
+      /* number is index +1 */
+      size_t number = find_string (&self->special_unit_varieties,
+                                   internal_conversion->special_unit_variety);
+      int j = number -1;
+      if (j >= 0)
+        {
+          SPECIAL_UNIT_BODY_FORMATTING *body_formatting
+            = &self->special_unit_body_formatting[j];
+          if (body_formatting->status == FRS_status_default_set
+              || body_formatting->status == FRS_status_none)
+            {
+              body_formatting->formatting_reference = 0;
+              body_formatting->status = FRS_status_internal;
+              body_formatting->special_unit_body_formatting
+                = internal_conversion->special_unit_body_formatting;
+              if (body_formatting->status == FRS_status_default_set)
+                external_special_unit_body_formatting_function--;
+            }
+        }
+    }
+
+ out:
+  self->external_references_number = self->conf->BIT_user_function_number
+        + self->file_id_setting_ref_number
+        + external_special_unit_body_formatting_function
+        + external_output_unit_conversion_function
+        + external_command_conversion_function
+        + external_command_open_function
+        + external_type_conversion_function
+        + external_type_open_function
+        + external_formatting_function;
+
+   /*
+  fprintf (stderr, "TOTAL: %d. conf %zu fi %d sbf %d ouc %d cc %d co %d tc %d 
to %d f %d\n",
+           self->external_references_number,
+           self->conf->BIT_user_function_number,
+           self->file_id_setting_ref_number,
+           external_special_unit_body_formatting_function,
+           external_output_unit_conversion_function,
+           external_command_conversion_function,
+           external_command_open_function,
+           external_type_conversion_function,
+           external_type_open_function,
+           external_formatting_function);
+    */
+}
+
+static void
+copy_html_no_arg_command_conversion (HTML_NO_ARG_COMMAND_CONVERSION *to,
+                                     HTML_NO_ARG_COMMAND_CONVERSION *from)
+{
+  if (from->element)
+    to->element = strdup (from->element);
+  to->unset = from->unset;
+  if (from->text)
+    to->text = strdup (from->text);
+  if (from->translated_converted)
+    to->translated_converted = strdup (from->translated_converted);
+  if (from->translated_to_convert)
+    to->translated_to_convert = strdup (from->translated_to_convert);
+}
+
+char ***
+new_directions_strings_type (int nr_string_directions,
+                             int nr_dir_str_contexts)
+{
+  int i;
+  char ***result = (char ***)
+        malloc (nr_string_directions * sizeof (char **));
+  memset (result, 0,
+          nr_string_directions * sizeof (char **));
+
+  for (i = 0; i < nr_string_directions; i++)
+    {
+      result[i] = (char **)
+         malloc (nr_dir_str_contexts * sizeof (char *));
+      memset (result[i], 0,
+              nr_dir_str_contexts * sizeof (char *));
+    }
+
+  return result;
+}
+
+/* transform <hr> to <hr/>
+   main effect is s/^(<[a-zA-Z]+[^<>]*)>$/$1\/>/ */
+static char *
+xhtml_re_close_lone_element (const char *input)
+{
+  size_t len = strlen (input);
+  size_t n;
+  const char *p;
+  char *result;
+  if (len < 3 || input[len -1] != '>' || input[0] != '<'
+      || !isascii_alpha (input[1]))
+    return strdup (input);
+
+  /* before > */
+  p = input + len - 2;
+  while (p > input +1)
+    {
+      if (*p == '/')
+        /* already a closed lone element */
+        return strdup (input);
+      if (strchr (whitespace_chars, *p))
+        p--;
+      else
+        break;
+    }
+
+  p = input + 2;
+  n = strcspn (p, "<>");
+  if (n +2 != len -1)
+    return strdup (input);
+
+  result = (char *) malloc ((len +1 +1) * sizeof (char));
+  memcpy (result, input, (len -1) * sizeof (char));
+  result[len -1] = '/';
+  result[len] = '>';
+  result[len+1] = '\0';
+
+  return result;
+}
+
+static void
+close_lone_conf_element (OPTION *option)
+{
+  const char *variable_value = option->o.string;
+  if (variable_value)
+    {
+      char *closed_lone_element = xhtml_re_close_lone_element (variable_value);
+      if (strcmp (closed_lone_element, variable_value))
+        {
+          option_force_conf (option, 0, closed_lone_element);
+        }
+      free (closed_lone_element);
+    }
+}
+
+int
+compare_index_name (const void *a, const void *b)
+{
+  const INDEX **idx_a = (const INDEX **) a;
+  const INDEX **idx_b = (const INDEX **) b;
+
+  return strcmp ((*idx_a)->name, (*idx_b)->name);
+}
+
+const enum command_id spaces_cmd[] = {CM_SPACE, CM_TAB, CM_NEWLINE, CM_tie};
+
+/* called very early in conversion functions, before updating
+   customization, before calling user-defined functions...  */
+void
+html_initialize_output_state (CONVERTER *self, const char *context)
+{
+  int i;
+  size_t j;
+  const char *output_encoding;
+  int nr_special_units = self->special_unit_varieties.number;
+  /* The corresponding direction without FirstInFile are used instead
+     of FirstInFile*, so the directions_strings are not set */
+  int nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
+                     + nr_special_units;
+  int nr_dir_str_contexts = TDS_context_string + 1;
+  enum direction_string_type DS_type;
+  const char *line_break_element;
+  int css_style_idx = 0;
+  int *non_default_special_unit_directions =
+     determine_non_default_special_unit_directions (self);
+
+  if (!self->document && self->conf->DEBUG.o.integer > 0)
+    {
+      fprintf (stderr, "REMARK: html_initialize_output_state: no document");
+    }
+
+  /* corresponds with default_no_arg_commands_formatting
+     + conf_default_no_arg_commands_formatting_normal in Perl */
+  HTML_NO_ARG_COMMAND_CONVERSION
+   output_no_arg_commands_formatting[BUILTIN_CMD_NUMBER]
+                                              [NO_ARG_COMMAND_CONTEXT_NR];
+
+  output_encoding = self->conf->OUTPUT_ENCODING_NAME.o.string;
+
+  for (i = 0; i < SC_non_breaking_space+1; i++)
+    {
+      const char *unicode_point = special_characters_formatting[i][2];
+      const char *entity = special_characters_formatting[i][0];
+      const char *encoded_string = special_characters_formatting[i][1];
+      const char *numeric_entity = special_characters_formatting[i][3];
+      const char *special_character_string;
+
+      if (self->conf->OUTPUT_CHARACTERS.o.integer > 0
+          && unicode_point_decoded_in_encoding (output_encoding,
+                                                unicode_point))
+        special_character_string = encoded_string;
+      else if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0)
+        special_character_string = numeric_entity;
+      else
+        special_character_string = entity;
+
+      self->special_character[i].string = special_character_string;
+      self->special_character[i].len = strlen (special_character_string);
+    }
+
+  if (!self->conf->OPEN_QUOTE_SYMBOL.o.string)
+    {
+      int set = option_set_conf (&self->conf->OPEN_QUOTE_SYMBOL, 0,
+                                 
self->special_character[SC_left_quote].string);
+      /* override undef set in init file/command line */
+      if (!set)
+        option_force_conf (&self->conf->OPEN_QUOTE_SYMBOL, 0, "");
+    }
+  if (!self->conf->CLOSE_QUOTE_SYMBOL.o.string)
+    {
+      int set = option_set_conf (&self->conf->CLOSE_QUOTE_SYMBOL, 0,
+                              self->special_character[SC_right_quote].string);
+      /* override undef set in init file/command line */
+      if (!set)
+        option_force_conf (&self->conf->CLOSE_QUOTE_SYMBOL, 0, "");
+    }
+  if (!self->conf->MENU_SYMBOL.o.string)
+    {
+      int set = option_set_conf (&self->conf->MENU_SYMBOL, 0,
+                                 self->special_character[SC_bullet].string);
+      /* override undef set in init file/command line */
+      if (!set)
+        option_force_conf (&self->conf->MENU_SYMBOL, 0, "");
+    }
+
+  if (self->conf->USE_XML_SYNTAX.o.integer > 0)
+    {
+      close_lone_conf_element (&self->conf->BIG_RULE);
+      close_lone_conf_element (&self->conf->DEFAULT_RULE);
+      line_break_element = "<br/>";
+    }
+  else
+    line_break_element = "<br>";
+
+  self->line_break_element.string = line_break_element;
+  self->line_break_element.len = strlen (line_break_element);
+
+  memcpy (output_no_arg_commands_formatting, 
default_no_arg_commands_formatting,
+          sizeof (default_no_arg_commands_formatting));
+
+  /* if not the textual entity */
+  if (strcmp(self->special_character[SC_non_breaking_space].string,
+             special_characters_formatting[SC_non_breaking_space][0]))
+    {
+      for (j = 0; j < sizeof (spaces_cmd) / sizeof (spaces_cmd[0]); j++)
+        {
+          enum command_id cmd = spaces_cmd[j];
+          /* cast to drop const */
+          output_no_arg_commands_formatting[cmd][HCC_type_normal].text
+            = (char *)self->special_character[SC_non_breaking_space].string;
+        }
+    }
+
+  if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0)
+    {
+      for (j = 0; j < no_arg_formatted_cmd.number; j++)
+        {
+          enum command_id cmd = no_arg_formatted_cmd.list[j];
+          if (unicode_entities[cmd])
+            output_no_arg_commands_formatting[cmd][HCC_type_normal].text
+              = unicode_entities[cmd];
+        }
+    }
+
+  /* cast to discard const */
+  output_no_arg_commands_formatting[CM_ASTERISK][HCC_type_normal].text
+    = (char *)self->line_break_element.string;
+
+  initialize_css_selector_style_list (&self->css_element_class_styles,
+                                      default_css_element_class_styles.number);
+  for (j = 0; j < default_css_element_class_styles.number; j++)
+    {
+      CSS_SELECTOR_STYLE *default_selector_style
+        = &default_css_element_class_styles.list[j];
+      if (default_selector_style->style)
+        {
+          CSS_SELECTOR_STYLE *selector_style
+            = &self->css_element_class_styles.list[css_style_idx];
+          selector_style->selector = strdup (default_selector_style->selector);
+          selector_style->style = strdup (default_selector_style->style);
+          css_style_idx++;
+        }
+      else
+        self->css_element_class_styles.number--;
+    }
+
+  for (j = 0; j < no_arg_formatted_cmd.number; j++)
+    {
+      enum command_id cmd = no_arg_formatted_cmd.list[j];
+      enum conversion_context cctx;
+      for (cctx = 0; cctx < NO_ARG_COMMAND_CONTEXT_NR; cctx++)
+        {
+          HTML_NO_ARG_COMMAND_CONVERSION *customized_no_arg_cmd
+            = self->customized_no_arg_commands_formatting[cmd][cctx];
+          HTML_NO_ARG_COMMAND_CONVERSION *result
+            = &self->html_no_arg_command_conversion[cmd][cctx];
+          if (customized_no_arg_cmd)
+            {
+              copy_html_no_arg_command_conversion (result,
+                                                   customized_no_arg_cmd);
+            }
+          else if (!output_no_arg_commands_formatting[cmd][cctx].unset)
+            {
+              const char *unicode_brace_no_arg_formatting = 0;
+              if (self->conf->OUTPUT_CHARACTERS.o.integer > 0)
+                {
+                  unicode_brace_no_arg_formatting
+                    = unicode_brace_no_arg_command (cmd,
+                         self->conf->OUTPUT_ENCODING_NAME.o.string);
+                }
+              if (unicode_brace_no_arg_formatting)
+                {
+                  memset (result, 0, sizeof (HTML_NO_ARG_COMMAND_CONVERSION));
+                  result->text
+                    = strdup (unicode_brace_no_arg_formatting);
+
+                  /* reset CSS for itemize command arguments */
+                  if (cctx == HCC_type_css_string
+                      && builtin_command_data[cmd].flags & CF_brace
+                      && cmd != CM_bullet && cmd != CM_w)
+                    {
+                      const char *special_list_mark_command
+                       = get_special_list_mark_css_string_no_arg_command (cmd);
+
+                      if (!special_list_mark_command)
+                        {
+                           char *selector;
+                           char *style;
+                           xasprintf (&selector, "ul.mark-%s",
+                                      builtin_command_name (cmd));
+                           xasprintf (&style, "list-style-type: \"%s\"",
+                                      result->text);
+                           html_css_set_selector_style (
+                                         &self->css_element_class_styles,
+                                                        selector, style);
+                           free (selector);
+                           free (style);
+                        }
+                    }
+                }
+              else
+                {
+                  copy_html_no_arg_command_conversion (result,
+                     &output_no_arg_commands_formatting[cmd][cctx]);
+                }
+            }
+          else
+            {
+              copy_html_no_arg_command_conversion (result,
+                &output_no_arg_commands_formatting[cmd][cctx]);
+            }
+        }
+    }
+
+  for (j = 0; j < no_arg_formatted_cmd.number; j++)
+    {
+      enum command_id cmd = no_arg_formatted_cmd.list[j];
+      COMMAND_CONVERSION_FUNCTION *command_conversion
+        = &self->command_conversion_function[cmd];
+      if (command_conversion->status == FRS_status_default_set
+          || command_conversion->status == FRS_status_internal)
+        {
+          html_complete_no_arg_commands_formatting (self, cmd, 0);
+        }
+    }
+
+  for (DS_type = 0; DS_type < TDS_TYPE_MAX_NR; DS_type++)
+    {
+      int i;
+      const char * const*default_converted_dir_str;
+      char ***customized_type_dir_strings;
+
+      self->directions_strings[DS_type]
+        = new_directions_strings_type (nr_string_directions,
+                                       nr_dir_str_contexts);
+
+      /* those will be determined from translatable strings */
+      if (DS_type < TDS_TRANSLATED_MAX_NR)
+        continue;
+
+      default_converted_dir_str =
+        default_converted_directions_strings[
+                                       DS_type - (TDS_TRANSLATED_MAX_NR)];
+      customized_type_dir_strings = self->customized_directions_strings[
+                                       DS_type - (TDS_TRANSLATED_MAX_NR)];
+      for (i = 0; i < nr_string_directions; i++)
+        {
+          if (customized_type_dir_strings && customized_type_dir_strings[i])
+            {
+              int j;
+              for (j = 0; j < nr_dir_str_contexts; j++)
+                {
+                  if (customized_type_dir_strings[i][j])
+                    self->directions_strings[DS_type][i][j]
+                      = html_substitute_non_breaking_space (self,
+                                     customized_type_dir_strings[i][j]);
+                }
+            }
+          else if (default_converted_dir_str[i])
+            {
+              if (i < NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR
+                  || !non_default_special_unit_directions[
+                       i - (NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR)])
+                self->directions_strings[DS_type][i][TDS_context_normal]
+                  = html_substitute_non_breaking_space (self,
+                                            default_converted_dir_str[i]);
+            }
+
+          if (self->directions_strings[DS_type][i][TDS_context_normal]
+              && !self->directions_strings[DS_type][i][TDS_context_string])
+            {
+              self->directions_strings[DS_type][i][TDS_context_string]
+                 = strdup (
+               self->directions_strings[DS_type][i][TDS_context_normal]);
+            }
+        }
+    }
+
+  free (non_default_special_unit_directions);
+
+  sort_css_element_class_styles (&self->css_element_class_styles);
+
+  /* set the htmlxref type split of the document */
+  self->document_htmlxref_split_type = htmlxref_split_type_mono;
+
+  if (self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string))
+    {
+      int i;
+      for (i = 1; i < htmlxref_split_type_chapter+1; i++)
+        {
+          if (!strcmp (self->conf->SPLIT.o.string,
+                       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));
+
+  if (self->conf->NODE_NAME_IN_INDEX.o.integer < 0)
+    option_set_conf (&self->conf->NODE_NAME_IN_INDEX,
+                     self->conf->USE_NODES.o.integer, 0);
+
+  if (self->conf->HTML_MATH.o.string
+      && self->conf->CONVERT_TO_LATEX_IN_MATH.o.integer < 0)
+    {
+      option_set_conf (&self->conf->CONVERT_TO_LATEX_IN_MATH, 1, 0);
+    }
+
+  if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0
+      && self->conf->SHOW_TITLE.o.integer < 0)
+    option_set_conf (&self->conf->SHOW_TITLE, 1, 0);
+
+
+  self->current_formatting_references = &self->formatting_references[0];
+  self->current_commands_conversion_function
+     = &self->command_conversion_function[0];
+  self->current_types_conversion_function = &self->type_conversion_function[0];
+  self->current_format_protect_text = &html_default_format_protect_text;
+
+  html_new_document_context (self, context, 0, 0);
+
+  if (self->document && self->document->indices_info.number)
+    {
+      size_t i;
+      size_t j;
+      INDEX_LIST *indices_info = &self->document->indices_info;
+      const INDEX **sorted_index_names;
+      size_t index_nr = indices_info->number;
+      size_t non_empty_index_nr = 0;
+      size_t idx_non_empty = 0;
+
+      for (i = 0; i < index_nr; i++)
+        {
+          INDEX *idx = indices_info->list[i];
+          if (idx->entries_number > 0)
+            non_empty_index_nr++;
+        }
+
+      sorted_index_names = (const INDEX **) malloc (index_nr * sizeof (INDEX 
*));
+
+      memcpy (sorted_index_names, indices_info->list,
+              index_nr * sizeof (INDEX *));
+      qsort (sorted_index_names, index_nr, sizeof (INDEX *),
+             compare_index_name);
+
+      /* store only non empty indices in sorted_index_names */
+      self->sorted_index_names.number = non_empty_index_nr;
+      self->sorted_index_names.list = (const INDEX **)
+         malloc (self->sorted_index_names.number * sizeof (INDEX *));
+      for (j = 0; j < index_nr; j++)
+        {
+          if (sorted_index_names[j]->entries_number > 0)
+            {
+              self->sorted_index_names.list[idx_non_empty]
+                  = sorted_index_names[j];
+              idx_non_empty++;
+            }
+        }
+      free (sorted_index_names);
+    }
+
+  if (self->document)
+    {
+      const LISTOFFLOATS_TYPE_LIST *listoffloats
+         = &self->document->listoffloats;
+
+      if (listoffloats->number)
+        {
+          self->shared_conversion_state.formatted_listoffloats_nr
+           = (int *) malloc (listoffloats->number * sizeof (int));
+          memset (self->shared_conversion_state.formatted_listoffloats_nr,
+              0, listoffloats->number * sizeof (int));
+        }
+    }
+}
+
+static OUTPUT_UNIT *
+register_special_unit (CONVERTER *self, char *special_unit_variety)
+{
+  ELEMENT *unit_command = new_element (ET_special_unit_element);
+  OUTPUT_UNIT *special_unit = new_output_unit (OU_special_unit);
+
+  special_unit->special_unit_variety = special_unit_variety;
+  unit_command->e.c->associated_unit = special_unit;
+  special_unit->uc.special_unit_command = unit_command;
+
+  return special_unit;
+}
+
+typedef struct SPECIAL_UNIT_ORDER {
+    const char *order;
+    const char *variety;
+} SPECIAL_UNIT_ORDER;
+
+int
+compare_special_units (const void *a, const void *b)
+{
+  const SPECIAL_UNIT_ORDER *spu_order_a = (const SPECIAL_UNIT_ORDER *) a;
+  const SPECIAL_UNIT_ORDER *spu_order_b = (const SPECIAL_UNIT_ORDER *) b;
+
+  int result = strcmp (spu_order_a->order, spu_order_b->order);
+  if (result != 0)
+    return result;
+  return strcmp (spu_order_a->variety, spu_order_b->variety);
+}
+
+void
+prepare_special_units (CONVERTER *self, size_t output_units_descriptor)
+{
+  size_t i;
+  STRING_LIST *special_unit_varieties = &self->special_unit_varieties;
+  SPECIAL_UNIT_ORDER *special_units_order;
+  OUTPUT_UNIT *previous_output_unit = 0;
+
+  size_t special_units_descriptor
+         = new_output_units_descriptor (self->document);
+  size_t associated_special_units_descriptor
+         = new_output_units_descriptor (self->document);
+
+  /* retrieve after reallocating */
+
+  OUTPUT_UNIT_LIST *special_units
+    = retrieve_output_units (self->document, special_units_descriptor);
+
+  OUTPUT_UNIT_LIST *associated_special_units
+    = retrieve_output_units (self->document,
+                             associated_special_units_descriptor);
+
+  OUTPUT_UNIT_LIST *output_units
+    = retrieve_output_units (self->document, output_units_descriptor);
+
+  /* for separate special output units */
+  STRING_LIST *do_special = new_string_list ();
+
+  self->output_units_descriptors[OUDT_special_units] = 
special_units_descriptor;
+  self->output_units_descriptors[OUDT_associated_special_units]
+     = associated_special_units_descriptor;
+
+  if (self->document->sections_list
+      && self->document->sections_list->number > 1)
+    {
+      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];
+          OPTION *contents_option_ref = get_command_option (self->conf, cmd);
+          if (contents_option_ref->o.integer > 0)
+            contents_set = 1;
+          if (contents_set)
+            {
+              int j;
+              char *special_unit_variety = 0;
+              char *contents_location
+                = self->conf->CONTENTS_OUTPUT_LOCATION.o.string;
+
+              for (j = 0; command_special_unit_variety[j].cmd; j++)
+                {
+                  if (command_special_unit_variety[j].cmd == cmd)
+                    {
+                      special_unit_variety
+                        = command_special_unit_variety[j].variety;
+                      break;
+                    }
+                }
+              if (contents_location
+                  && !strcmp (contents_location, "separate_element"))
+                add_string (special_unit_variety, do_special);
+              else
+                {
+                  OUTPUT_UNIT *special_output_unit = 0;
+                  const OUTPUT_UNIT *associated_output_unit = 0;
+                  if (contents_location
+                      && !strcmp (contents_location, "after_title"))
+                    {
+                      associated_output_unit = output_units->list[0];
+                    }
+                  else if (contents_location
+                           && !strcmp (contents_location, "after_top"))
+                    {
+                      if (self->document->global_commands.top)
+                        {/* note that top is a uniq command */
+                          const ELEMENT *section_top
+                             = self->document->global_commands.top;
+
+                          if (section_top->e.c->associated_unit)
+                            associated_output_unit
+                                 = section_top->e.c->associated_unit;
+                        }
+                      if (!associated_output_unit)
+                        continue;
+                    }
+                  else if (contents_location
+                           && !strcmp (contents_location, "inline"))
+                    {
+                      const ELEMENT_LIST *global_command
+                       = get_cmd_global_multi_command (
+                                      &self->document->global_commands, cmd);
+                      if (global_command->number > 0)
+                        {
+                          size_t i;
+                          for (i = 0; i < global_command->number; i++)
+                            {
+                              const ELEMENT *command = global_command->list[i];
+                              ROOT_AND_UNIT *root_unit
+                               = html_get_tree_root_element (self, command, 0);
+                              if (root_unit->output_unit)
+                                associated_output_unit = 
root_unit->output_unit;
+                              free (root_unit);
+                              if (associated_output_unit)
+                                break;
+                            }
+                        }
+                      else
+                        continue;
+                    }
+                  else /* should not happen */
+                    continue;
+
+                  special_output_unit
+                    = register_special_unit (self, special_unit_variety);
+                  special_output_unit->associated_document_unit
+                    = associated_output_unit;
+                  add_to_output_unit_list (associated_special_units,
+                                           special_output_unit);
+                }
+            }
+        }
+    }
+
+  if (self->document->global_commands.footnotes.number > 0
+      && self->conf->footnotestyle.o.string
+      && !strcmp (self->conf->footnotestyle.o.string, "separate")
+      && output_units->number > 1)
+    add_string ("footnotes", do_special);
+
+  if ((self->conf->DO_ABOUT.o.integer < 0
+       && output_units->number > 1
+       && ((self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string))
+           || self->conf->HEADERS.o.integer > 0))
+      || self->conf->DO_ABOUT.o.integer > 0)
+    add_string ("about", do_special);
+
+  special_units_order = (SPECIAL_UNIT_ORDER *)
+    malloc (sizeof (SPECIAL_UNIT_ORDER) * do_special->number);
+  for (i = 0; i < do_special->number; i++)
+    {
+      char *special_unit_variety = do_special->list[i];
+      special_units_order[i].order = html_special_unit_info (self,
+                                                        SUI_type_order,
+                                                        special_unit_variety);
+      special_units_order[i].variety = special_unit_variety;
+    }
+
+  qsort (special_units_order, do_special->number, sizeof (SPECIAL_UNIT_ORDER),
+         compare_special_units);
+
+  previous_output_unit = output_units->list[output_units->number-1];
+
+  for (i = 0; i < do_special->number; i++)
+    {
+      /* take the string from special_unit_varieties */
+      char *special_unit_variety;
+      /* number is index +1 */
+      size_t number = find_string (special_unit_varieties,
+                                   special_units_order[i].variety);
+      int special_unit_varieties_idx = number -1;
+      if (special_unit_varieties_idx < 0)
+        {
+          char *msg;
+          xasprintf (&msg, "special_unit_varieties not found: %s\n",
+                           special_units_order[i].variety);
+          bug (msg);
+        }
+
+      special_unit_variety
+        = special_unit_varieties->list[special_unit_varieties_idx];
+      OUTPUT_UNIT *special_output_unit
+                    = register_special_unit (self, special_unit_variety);
+      add_to_output_unit_list (special_units,
+                               special_output_unit);
+
+      if (previous_output_unit)
+        {
+          special_output_unit->tree_unit_directions[D_prev]
+             = previous_output_unit;
+          previous_output_unit->tree_unit_directions[D_next]
+             = special_output_unit;
+        }
+      previous_output_unit = special_output_unit;
+    }
+
+  free (special_units_order);
+  destroy_strings_list (do_special);
+}
+
+void
+html_prepare_conversion_units (CONVERTER *self)
+{
+  size_t output_units_descriptor;
+
+  if (self->conf->USE_NODES.o.integer > 0)
+    output_units_descriptor = split_by_node (self->document);
+  else
+    output_units_descriptor = split_by_section (self->document);
+  self->output_units_descriptors[OUDT_units] = output_units_descriptor;
+
+  /* configuration used to determine if a special element is to be done
+     (in addition to contents) */
+  set_global_document_commands (self, CL_last, conf_for_special_units);
+  /*
+    NOTE if the last value of footnotestyle is separate, all the footnotes
+    formatted text are set to the special element set in _prepare_special_units
+    as _html_get_tree_root_element uses the Footnote direction for every 
footnote.
+    Therefore if @footnotestyle separate is set late in the document the 
current
+    value may not be consistent with the link obtained for the footnote
+    formatted text.  This is not an issue, as the manual says that
+    @footnotestyle should only appear in the preamble, and it makes sense
+    to have something consistent in the whole document for footnotes position.
+   */
+  prepare_special_units (self, output_units_descriptor);
+
+  /* reset to the default */
+  set_global_document_commands (self, CL_before, conf_for_special_units);
+}
diff --git a/tp/Texinfo/XS/convert/html_prepare_converter.h 
b/tp/Texinfo/XS/convert/html_prepare_converter.h
new file mode 100644
index 0000000000..08bd005b01
--- /dev/null
+++ b/tp/Texinfo/XS/convert/html_prepare_converter.h
@@ -0,0 +1,20 @@
+/* html_prepare_converter.h - definitions for html_prepare_converter.c */
+#ifndef HTML_PREPARE_CONVERTER_H
+#define HTML_PREPARE_CONVERTER_H
+
+#include "command_ids.h"
+#include "converter_types.h"
+
+void html_format_setup (void);
+
+void html_converter_init_special_unit (CONVERTER *self);
+void html_converter_customize (CONVERTER *self);
+
+char ***new_directions_strings_type (int nr_string_directions,
+                                     int nr_dir_str_contexts);
+
+void html_initialize_output_state (CONVERTER *self, const char *context);
+
+void html_prepare_conversion_units (CONVERTER *self);
+
+#endif
diff --git a/tp/Texinfo/XS/convert/texinfo.c b/tp/Texinfo/XS/convert/texinfo.c
index a19a8097b6..9fc31b1288 100644
--- a/tp/Texinfo/XS/convert/texinfo.c
+++ b/tp/Texinfo/XS/convert/texinfo.c
@@ -39,6 +39,7 @@
 #include "transformations.h"
 #include "converter.h"
 #include "convert_html.h"
+#include "html_prepare_converter.h"
 #include "texinfo.h"
 
 /* initialization of the library. */



reply via email to

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