| /* Copyright 2010-2026 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 <https://www.gnu.org/licenses/>. */ |
| |
| #include <config.h> |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <stdarg.h> |
| #include <iconv.h> |
| #include <unistr.h> |
| #include <unictype.h> |
| |
| #include "html_conversion_data.h" |
| #include "text.h" |
| #include "element_types.h" |
| #include "tree_types.h" |
| #include "option_types.h" |
| #include "options_data.h" |
| #include "document_types.h" |
| #include "converter_types.h" |
| #include "option_types.h" |
| #include "types_data.h" |
| #include "html_converter_types.h" |
| /* fatal isascii_alnum isascii_alpha isascii_digit isascii_lower */ |
| #include "base_utils.h" |
| #include "tree.h" |
| #include "builtin_commands.h" |
| #include "command_stack.h" |
| #include "errors.h" |
| #include "utils.h" |
| #include "customization_options.h" |
| #include "extra.h" |
| #include "targets.h" |
| #include "debug.h" |
| #include "output_unit.h" |
| #include "node_name_normalization.h" |
| #include "manipulate_indices.h" |
| #include "convert_to_texinfo.h" |
| /* for OTXI_UNICODE_TEXT_CASES utf8_from_string string_from_utf8 */ |
| #include "unicode.h" |
| /* convert_to_text */ |
| #include "convert_to_text.h" |
| /* comma_index_subentries_tree ... */ |
| #include "convert_utils.h" |
| #include "call_html_perl_function.h" |
| /* for unregister_document_merge_with_document |
| #include "document.h" |
| */ |
| #include "converter.h" |
| #include "manipulate_tree.h" |
| /* for new_complete_menu_master_menu */ |
| #include "structuring.h" |
| #include "api_to_perl.h" |
| #include "html_conversion_state.h" |
| #include "convert_html.h" |
| #include "format_html.h" |
| |
| const char *direction_string_type_names[] = |
| { |
| #define tds_type(name) #name, |
| TDS_TRANSLATED_TYPES_LIST |
| TDS_NON_TRANSLATED_TYPES_LIST |
| #undef tds_type |
| }; |
| |
| const char *direction_string_context_names[] = |
| { |
| "normal", "string" |
| }; |
| |
| const enum htmlxref_split_type htmlxref_entries[htmlxref_split_type_chapter + 1][htmlxref_split_type_chapter + 1] = { |
| { htmlxref_split_type_mono, htmlxref_split_type_chapter, htmlxref_split_type_section, htmlxref_split_type_node }, |
| { htmlxref_split_type_node, htmlxref_split_type_section, htmlxref_split_type_chapter, htmlxref_split_type_mono }, |
| { htmlxref_split_type_section, htmlxref_split_type_chapter, htmlxref_split_type_node, htmlxref_split_type_mono }, |
| { htmlxref_split_type_chapter, htmlxref_split_type_section, htmlxref_split_type_node, htmlxref_split_type_mono }, |
| }; |
| |
| |
| |
| /* string functions */ |
| |
| void |
| html_close_lone_element (const CONVERTER *self, TEXT *result) |
| { |
| if (self->conf->USE_XML_SYNTAX.o.integer > 0) |
| text_append_n (result, "/>", 2); |
| else |
| text_append_n (result, ">", 1); |
| } |
| |
| /* 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; |
| } |
| |
| /* |
| static const char *xml_named_entity_nbsp = " "; |
| */ |
| static const char *html_default_entity_nbsp = " "; |
| |
| char * |
| html_substitute_non_breaking_space (CONVERTER *self, const char *text) |
| { |
| TEXT result; |
| text_init (&result); |
| text_append (&result, ""); |
| |
| const char *p = text; |
| |
| while (*p) |
| { |
| const char *q = strstr (p, html_default_entity_nbsp); |
| if (q) |
| { |
| if (q - p) |
| { |
| text_append_n (&result, p, q - p); |
| } |
| text_append_n (&result, |
| self->special_character[SC_non_breaking_space].string, |
| self->special_character[SC_non_breaking_space].len); |
| p = q + 6; /* 6: length of html_default_entity_nbsp */ |
| } |
| else |
| { |
| text_append (&result, p); |
| break; |
| } |
| } |
| return result.text; |
| } |
| |
| /* to be inlined in text parsing codes */ |
| #define OTXI_PROTECT_XML_FORM_FEED_CASES(var) \ |
| OTXI_PROTECT_XML_CASES(var) \ |
| case '\f': \ |
| text_append_n (result, "", 5); var++; \ |
| break; |
| |
| #define OTXI_ISO_ENTITY_TEXT_CASES(var) \ |
| case '-': \ |
| if (*(var+1) && !memcmp (var, "---", 3)) \ |
| { \ |
| text_append_n (result, "—", 7); \ |
| var += 3; \ |
| } \ |
| else if (!memcmp (var, "--", 2)) \ |
| { \ |
| text_append_n (result, "–", 7); \ |
| var += 2; \ |
| } \ |
| else \ |
| { \ |
| text_append_n (result, "-", 1); \ |
| var++; \ |
| } \ |
| break; \ |
| case '`': \ |
| if (!memcmp (var, "``", 2)) \ |
| { \ |
| text_append_n (result, "“", 7); \ |
| var += 2; \ |
| } \ |
| else \ |
| { \ |
| text_append_n (result, "‘", 7); \ |
| var++; \ |
| } \ |
| break; \ |
| case '\'': \ |
| if (!memcmp (var, "''", 2)) \ |
| { \ |
| text_append_n (result, "”", 7); \ |
| var += 2; \ |
| } \ |
| else \ |
| { \ |
| text_append_n (result, "’", 7); \ |
| var++; \ |
| } \ |
| break; |
| |
| #define OTXI_NO_ISO_ENTITY_TEXT_CASES(var) \ |
| case '-': \ |
| if (*(var+1) && !memcmp (var, "---", 3)) \ |
| { \ |
| text_append_n (result, "--", 2); \ |
| var += 3; \ |
| } \ |
| else \ |
| { \ |
| text_append_n (result, "-", 1); \ |
| if (!memcmp (var, "--", 2)) \ |
| var += 2; \ |
| else \ |
| var++; \ |
| } \ |
| break; \ |
| case '`': \ |
| if (!memcmp (var, "``", 2)) \ |
| { \ |
| text_append_n (result, """, 6); \ |
| var += 2; \ |
| } \ |
| else \ |
| { \ |
| text_append_n (result, var, 1); \ |
| var++; \ |
| } \ |
| break; \ |
| case '\'': \ |
| if (!memcmp (var, "''", 2)) \ |
| { \ |
| text_append_n (result, """, 6); \ |
| var += 2; \ |
| } \ |
| else \ |
| { \ |
| text_append_n (result, var, 1); \ |
| var++; \ |
| } \ |
| break; |
| |
| #define OTXI_NO_BREAK_CASES(var) \ |
| case ' ': \ |
| case '\n': \ |
| text_append_n (result, \ |
| self->special_character[SC_non_breaking_space].string, \ |
| self->special_character[SC_non_breaking_space].len); \ |
| var += strspn (var, "\n "); \ |
| break; |
| |
| #define OTXI_SPACE_PROTECTION_CASES(var) \ |
| case ' ': \ |
| text_append_n (result, \ |
| self->special_character[SC_non_breaking_space].string, \ |
| self->special_character[SC_non_breaking_space].len); \ |
| var++; \ |
| break; \ |
| case '\n': \ |
| text_append_n (result, self->line_break_element.string, \ |
| self->line_break_element.len); \ |
| var++; \ |
| break; |
| |
| /* text conversion loop, with the protection of XML special |
| characters and the possibility to add more delimiters and |
| more cases to handle those delimiters */ |
| #define OTXI_CONVERT_TEXT(delimiters,other_cases) \ |
| { \ |
| while (*p) \ |
| { \ |
| int before_sep_nr = strcspn (p, "<>&\"\f" delimiters); \ |
| if (before_sep_nr) \ |
| { \ |
| text_append_n (result, p, before_sep_nr); \ |
| p += before_sep_nr; \ |
| } \ |
| if (!*p) \ |
| break; \ |
| switch (*p) \ |
| { \ |
| OTXI_PROTECT_XML_FORM_FEED_CASES(p) \ |
| other_cases \ |
| } \ |
| } \ |
| } |
| |
| /* conversion of text for all the possibilities regarding -- --- '' |
| conversion, with the possibility to add more for spaces protection */ |
| #define OTXI_ALL_CONVERT_TEXT(additional_delim,other_cases) \ |
| const char *p = content_used; \ |
| if (html_in_code (self) || html_in_math (self)) \ |
| OTXI_CONVERT_TEXT(additional_delim, \ |
| other_cases) \ |
| else if (self->use_unicode_text) \ |
| OTXI_CONVERT_TEXT("-`'" additional_delim, \ |
| OTXI_UNICODE_TEXT_CASES(p) \ |
| other_cases) \ |
| else if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0) \ |
| OTXI_CONVERT_TEXT("-`'" additional_delim, \ |
| OTXI_NUMERIC_ENTITY_TEXT_CASES(p) \ |
| other_cases) \ |
| else if (self->conf->USE_ISO.o.integer > 0) \ |
| OTXI_CONVERT_TEXT("-`'" additional_delim, \ |
| OTXI_ISO_ENTITY_TEXT_CASES(p) \ |
| other_cases) \ |
| else \ |
| OTXI_CONVERT_TEXT("-`'" additional_delim, \ |
| OTXI_NO_ISO_ENTITY_TEXT_CASES(p) \ |
| other_cases) |
| |
| void |
| html_default_format_protect_text (const char *text, TEXT *result) |
| { |
| const char *p = text; |
| |
| OTXI_CONVERT_TEXT ( , ) |
| } |
| |
| void |
| format_protect_text (CONVERTER *self, const char *text, TEXT *result) |
| { |
| FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_protect_text]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| (*self->current_format_protect_text) (text, result); |
| } |
| else |
| { |
| char *protected_text |
| = call_formatting_function_format_protect_text (self, |
| formatting_reference, text); |
| text_append (result, protected_text); |
| free (protected_text); |
| } |
| } |
| |
| void |
| html_default_css_string_format_protect_text (const char *text, TEXT *result) |
| { |
| const char *p = text; |
| |
| 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 '\\': |
| text_append_n (result, "\\\\", 2); |
| break; |
| case '\'': |
| text_append_n (result, "\\'", 2); |
| break; |
| } |
| p++; |
| } |
| } |
| |
| static const char *reserved_unreserved_percent = "-_.!~*'()$&+,/:;=?@[]#%"; |
| |
| /* NOTE the input string should be UTF-8 encoded */ |
| static char * |
| url_protect_url_text (CONVERTER *self, const char *input_string) |
| { |
| TEXT text; |
| TEXT result; |
| text_init (&text); |
| const char *p = input_string; |
| |
| text_append (&text, ""); |
| |
| /* protect 'ligntly', do not protect unreserved and reserved characters |
| + the % itself */ |
| while (*p) |
| { |
| if (isascii_alnum (*p) || isascii_alpha (*p) || isascii_digit (*p)) |
| { |
| text_append_n (&text, p, 1); |
| p++; |
| } |
| else |
| { |
| int n = strspn (p, reserved_unreserved_percent); |
| if (n) |
| { |
| text_append_n (&text, p, n); |
| p += n; |
| } |
| else |
| { |
| int n = strspn (p, "\r\n"); |
| if (n) |
| { |
| text_printf (&text, "%%%02x", (unsigned char)' '); |
| p += n; |
| } |
| else |
| { |
| int i; |
| int char_len = 1; |
| if (!isascii (*p)) |
| { |
| /* Protect UTF-8 with continuation bytes. */ |
| while ((p[char_len] & 0xC0) == 0x80) |
| char_len++; |
| } |
| for (i = 0; i < char_len; i++) |
| { |
| /* the reason for forcing (unsigned char) is that the %x modifier |
| expects an unsigned int parameter and a char will usually be |
| promoted to an int when passed to a varargs function */ |
| text_printf (&text, "%%%02x", (unsigned char)*p); |
| p += 1; |
| } |
| } |
| } |
| } |
| } |
| text_init (&result); |
| format_protect_text (self, text.text, &result); |
| free (text.text); |
| return (result.text); |
| } |
| |
| static const char *file_path_punct = "-_.~/:"; |
| |
| /* |
| protect a file path used in an url. Characters appearing in file paths |
| are not protected. All the other characters that can be percent |
| protected are protected, including characters with specific meaning in url. |
| */ |
| static char * |
| url_protect_file_text (CONVERTER *self, const char *input_string) |
| { |
| TEXT text; |
| TEXT result; |
| text_init (&text); |
| const char *p = input_string; |
| |
| text_append (&text, ""); |
| |
| while (*p) |
| { |
| if (isascii_alnum (*p) || isascii_alpha (*p) || isascii_digit (*p)) |
| { |
| text_append_n (&text, p, 1); |
| p++; |
| } |
| else |
| { |
| int n = strspn (p, file_path_punct); |
| if (n) |
| { |
| text_append_n (&text, p, n); |
| p += n; |
| } |
| else |
| { |
| int n = strspn (p, "\r\n"); |
| if (n) |
| { |
| text_printf (&text, "%%%02x", (unsigned char)' '); |
| p += n; |
| } |
| else |
| { |
| int i; |
| int char_len = 1; |
| if (!isascii (*p)) |
| { |
| /* Protect UTF-8 with continuation bytes. */ |
| while ((p[char_len] & 0xC0) == 0x80) |
| char_len++; |
| } |
| for (i = 0; i < char_len; i++) |
| { |
| /* the reason for forcing (unsigned char) is that the %x modifier |
| expects an unsigned int parameter and a char will usually be |
| promoted to an int when passed to a varargs function */ |
| text_printf (&text, "%%%02x", (unsigned char)*p); |
| p += 1; |
| } |
| } |
| } |
| } |
| } |
| |
| text_init (&result); |
| format_protect_text (self, text.text, &result); |
| free (text.text); |
| return (result.text); |
| } |
| |
| |
| |
| /* target, links, href and root command text formatting, with caching */ |
| |
| static int |
| compare_element_target (const void *a, const void *b) |
| { |
| const HTML_TARGET *ete_a = (const HTML_TARGET *) a; |
| const HTML_TARGET *ete_b = (const HTML_TARGET *) b; |
| /* we cast to uintptr_t because comparison of pointers from different |
| objects is undefined behaviour in C. In practice it is probably |
| not an issue */ |
| uintptr_t a_element_addr = (uintptr_t)ete_a->element; |
| uintptr_t b_element_addr = (uintptr_t)ete_b->element; |
| |
| return (a_element_addr > b_element_addr) - (a_element_addr < b_element_addr); |
| } |
| |
| HTML_TARGET * |
| find_element_target_search (const HTML_TARGET_LIST *targets, |
| const ELEMENT *element) |
| { |
| HTML_TARGET *result; |
| static HTML_TARGET searched_element; |
| |
| if (targets->number == 0) |
| return 0; |
| |
| searched_element.element = element; |
| result = (HTML_TARGET *) bsearch (&searched_element, |
| targets->list, targets->number, sizeof (HTML_TARGET), |
| compare_element_target); |
| return result; |
| } |
| |
| /* note that the returned pointer may be invalidated if the targets list |
| is reallocated. Callers should make sure that the html target is |
| used before a reallocation is possible */ |
| HTML_TARGET * |
| html_get_target (const CONVERTER *self, const ELEMENT *element) |
| { |
| enum command_id cmd = element_builtin_cmd (element); |
| return find_element_target_search (&self->html_targets[cmd], element); |
| } |
| |
| /* the target may not be known already, so the caller may fill the |
| HTML_TARGET in some cases */ |
| HTML_TARGET * |
| find_element_special_target (const HTML_TARGET_LIST *targets, |
| const ELEMENT *element) |
| { |
| return find_element_target_search (targets, element); |
| } |
| |
| const char * |
| html_command_id (const CONVERTER *self, const ELEMENT *command) |
| { |
| const HTML_TARGET *target_info = html_get_target (self, command); |
| if (target_info) |
| return target_info->target; |
| else |
| 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); |
| } |
| |
| HTMLXREF_MANUAL * |
| find_htmlxref_manual |
| (const HTMLXREF_MANUAL_LIST *htmlxref_manuals, const char *manual) |
| { |
| HTMLXREF_MANUAL *result = 0; |
| static HTMLXREF_MANUAL searched_manual; |
| /* remove const with a cast, it is more efficient than duplicating */ |
| searched_manual.manual = (char *) manual; |
| |
| result = (HTMLXREF_MANUAL *) bsearch (&searched_manual, |
| htmlxref_manuals->list, |
| htmlxref_manuals->number, sizeof (HTMLXREF_MANUAL), |
| compare_htmlxref_manual); |
| |
| return result; |
| } |
| |
| char * |
| html_normalized_to_id (const char *id) |
| { |
| if (isascii_digit (id[0]) || id[0] == '_') |
| { |
| char *result; |
| xasprintf (&result, "%s%s", "g_t", id); |
| return result; |
| } |
| return strdup (id); |
| } |
| |
| /* calls customization function requiring elements */ |
| TARGET_FILENAME * |
| html_normalized_label_id_file (CONVERTER *self, const char *normalized, |
| const ELEMENT* label_element) |
| { |
| int called; |
| char *target = 0; |
| char *target_customized; |
| char *normalized_label = 0; |
| TARGET_FILENAME *target_filename = new_target_filename (); |
| |
| if (normalized) |
| { |
| normalized_label = strdup (normalized); |
| target = html_normalized_to_id (normalized); |
| } |
| else if (label_element) |
| { |
| normalized_label |
| = convert_contents_to_node_identifier (label_element); |
| if (normalized_label) |
| target = html_normalized_to_id (normalized_label); |
| } |
| |
| if (!target) |
| target = strdup (""); |
| |
| /* to find out the Top node, one could check $normalized */ |
| target_customized = call_file_id_setting_label_target_name (self, |
| normalized_label, label_element, target, |
| &called); |
| |
| if (target_customized) |
| { |
| free (target); |
| target = target_customized; |
| } |
| |
| target_filename->target = target; |
| target_filename->filename |
| = node_information_filename (self, normalized_label, label_element); |
| |
| free (normalized_label); |
| |
| return target_filename; |
| } |
| |
| /* avoid including html_conversion_api.h */ |
| extern OPTIONS_LIST *html_default_options; |
| |
| TARGET_FILENAME * |
| html_standard_label_id_file (CONVERTER *self, const char *normalized, |
| const ELEMENT *label_element, |
| const char *crossref_extension, |
| const char *extension) |
| { |
| TARGET_FILENAME *target_filename = new_target_filename (); |
| int called; |
| char *normalized_label = 0; |
| char *target = 0; |
| char *filename = 0; |
| const char *file_extension = 0; |
| char *target_customized; |
| const char *external_extension = 0; |
| |
| if (normalized) |
| { |
| normalized_label = strdup (normalized); |
| } |
| else if (label_element) |
| { |
| normalized_label |
| = convert_contents_to_node_identifier (label_element); |
| } |
| |
| if (normalized_label) |
| { |
| /* use default, not user-defined value */ |
| int basefilename_length |
| = txi_base_options.BASEFILENAME_LENGTH.o.integer; |
| target = html_normalized_to_id (normalized_label); |
| filename = strdup (normalized_label); |
| if (strlen (filename) > (size_t) basefilename_length) |
| { |
| filename[basefilename_length] = '\0'; |
| } |
| } |
| else |
| { |
| target = strdup (""); |
| filename = strdup (""); |
| } |
| |
| /* to find out the Top node, one could check $normalized */ |
| target_customized = call_file_id_setting_label_target_name (self, |
| normalized_label, label_element, target, |
| &called); |
| |
| free (normalized_label); |
| |
| if (target_customized) |
| { |
| free (target); |
| target = target_customized; |
| } |
| |
| target_filename->target = target; |
| target_filename->filename = filename; |
| |
| if (crossref_extension) |
| external_extension = crossref_extension; |
| else if (extension) |
| external_extension = extension; |
| |
| if (external_extension && strlen (external_extension)) |
| file_extension = external_extension; |
| |
| target_filename->extension = file_extension; |
| |
| return target_filename; |
| } |
| |
| char * |
| external_node_href (CONVERTER *self, const ELEMENT *external_node, |
| const ELEMENT *source_command) /* for messages only */ |
| { |
| TEXT result; |
| char *target; |
| char *target_filebase; |
| /* used if !target_split */ |
| char *file = 0; |
| /* used if target_split */ |
| char *directory = 0; |
| const char *extension; |
| int target_split = 0; |
| char *normalized = lookup_extra_string (external_node, AI_key_normalized); |
| const ELEMENT *node_contents = lookup_extra_container (external_node, |
| AI_key_node_content); |
| const ELEMENT *manual_content = lookup_extra_container (external_node, |
| AI_key_manual_content); |
| |
| TARGET_FILENAME *target_filename = |
| html_standard_label_id_file (self, normalized, node_contents, |
| self->conf->EXTERNAL_CROSSREF_EXTENSION.o.string, |
| html_default_options->options->EXTENSION.o.string); |
| |
| extension = target_filename->extension; |
| /* to be freed before return */ |
| target = target_filename->target; |
| target_filebase = target_filename->filename; |
| free (target_filename); |
| |
| /* always undef if conversion is called through convert() */ |
| if (self->conf->EXTERNAL_CROSSREF_SPLIT.o.string |
| && strlen (self->conf->EXTERNAL_CROSSREF_SPLIT.o.string)) |
| /* initialize to EXTERNAL_CROSSREF_SPLIT */ |
| target_split = 1; |
| |
| if (manual_content) |
| { |
| char *manual_name; |
| size_t len; |
| char *manual_base = 0; |
| char *p; |
| char *htmlxref_href = 0; |
| enum htmlxref_split_type split_found = htmlxref_split_type_none; |
| HTMLXREF_MANUAL *htmlxref_manual; |
| |
| self->convert_text_options->code_state++; |
| manual_name = convert_to_text (manual_content, |
| self->convert_text_options); |
| self->convert_text_options->code_state--; |
| |
| if (self->conf->IGNORE_REF_TO_TOP_NODE_UP.o.integer > 0 |
| && !strlen (target)) |
| { |
| char *top_node_up = self->conf->TOP_NODE_UP.o.string; |
| if (top_node_up) |
| { |
| char *parentheses_manual_name; |
| xasprintf (&parentheses_manual_name, "(%s)", manual_name); |
| if (!strcmp (top_node_up, parentheses_manual_name)) |
| { |
| free (parentheses_manual_name); |
| free (manual_name); |
| free (target); |
| free (target_filebase); |
| return strdup (""); |
| } |
| free (parentheses_manual_name); |
| } |
| } |
| p = strrchr (manual_name, '/'); |
| if (!p) |
| p = manual_name; |
| else |
| p++; |
| manual_base = strdup (p); |
| |
| p = 0; |
| len = strlen (manual_base); |
| if (len >= 4) |
| { |
| p = manual_base + len - 4; |
| if (strcmp (p, ".inf")) |
| p = 0; |
| } |
| if (!p && len >= 5) |
| { |
| p = manual_base + len - 5; |
| if (strcmp (p, ".info")) |
| p = 0; |
| } |
| |
| if (p) |
| { |
| message_list_command_warn (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| source_command, 0, |
| "do not set %s suffix in reference for manual `%s'", |
| p, manual_name); |
| *p = '\0'; |
| } |
| |
| if (!self->conf->HTMLXREF_MODE.o.string |
| || strcmp (self->conf->HTMLXREF_MODE.o.string, "none")) |
| { |
| htmlxref_manual = find_htmlxref_manual (&self->htmlxref, manual_base); |
| |
| if (htmlxref_manual) |
| { |
| const enum htmlxref_split_type *ordered_split_types |
| = htmlxref_entries[self->document_htmlxref_split_type]; |
| int i; |
| for (i = 0; i < htmlxref_split_type_chapter +1; i++) |
| { |
| const enum htmlxref_split_type split_ordered |
| = ordered_split_types[i]; |
| if (htmlxref_manual->urlprefix[split_ordered]) |
| { |
| split_found = split_ordered; |
| if (strlen (htmlxref_manual->urlprefix[split_ordered])) |
| htmlxref_href = url_protect_url_text (self, |
| htmlxref_manual->urlprefix[split_ordered]); |
| break; |
| } |
| } |
| } |
| if (split_found != htmlxref_split_type_none) |
| { |
| if (split_found == htmlxref_split_type_mono) |
| target_split = 0; |
| else |
| target_split = 1; |
| } |
| else |
| { /* nothing specified for that manual, use default */ |
| if (self->conf->CHECK_HTMLXREF.o.integer > 0) |
| { |
| if ((source_command != 0) && |
| (source_command->e.c->source_info.line_nr != 0)) |
| { /* check if already set and set if not */ |
| if (!html_check_htmlxref_already_warned (self, |
| manual_name, &source_command->e.c->source_info)) |
| { |
| message_list_command_warn (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| source_command, 0, |
| "no HTML cross-references entry found for `%s'", |
| manual_name); |
| } |
| } |
| else |
| { |
| if (!html_check_htmlxref_already_warned (self, |
| manual_name, 0)) |
| { |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, |
| "no HTML cross-references entry found for `%s'", |
| manual_name); |
| } |
| } |
| } |
| } |
| free (manual_name); |
| } |
| |
| if (target_split) |
| { |
| char *directory_part; |
| if (htmlxref_href) |
| { |
| directory_part = htmlxref_href; |
| } |
| else |
| { |
| TEXT dir_path; |
| char *url_encoded_path; |
| text_init (&dir_path); |
| const char *output_format |
| = self->conf->TEXINFO_OUTPUT_FORMAT.o.string; |
| |
| if (self->conf->EXTERNAL_DIR.o.string) |
| { |
| text_printf (&dir_path, "%s/%s", |
| self->conf->EXTERNAL_DIR.o.string, manual_base); |
| } |
| else if (self->conf->SPLIT.o.string |
| && strlen (self->conf->SPLIT.o.string)) |
| { |
| text_append_n (&dir_path, "../", 3); |
| text_append (&dir_path, manual_base); |
| } |
| if (output_format && strlen (output_format)) |
| { |
| text_append_n (&dir_path, "_", 1); |
| text_append (&dir_path, output_format); |
| } |
| url_encoded_path = url_protect_file_text (self, dir_path.text); |
| free (dir_path.text); |
| directory_part = url_encoded_path; |
| } |
| xasprintf (&directory, "%s/", directory_part); |
| free (directory_part); |
| } |
| else |
| { /* target not split */ |
| if (htmlxref_href) |
| { |
| file = htmlxref_href; |
| } |
| else |
| { |
| TEXT file_path; |
| text_init (&file_path); |
| |
| if (self->conf->EXTERNAL_DIR.o.string) |
| { |
| text_printf (&file_path, "%s/%s", |
| self->conf->EXTERNAL_DIR.o.string, manual_base); |
| } |
| else if (self->conf->SPLIT.o.string |
| && strlen (self->conf->SPLIT.o.string)) |
| { |
| text_append_n (&file_path, "../", 3); |
| text_append (&file_path, manual_base); |
| } |
| else |
| text_append (&file_path, manual_base); |
| if (extension) |
| text_printf (&file_path, ".%s", extension); |
| |
| |
| file = url_protect_file_text (self, file_path.text); |
| free (file_path.text); |
| } |
| } |
| free (manual_base); |
| } |
| |
| text_init (&result); |
| |
| if (target_split) |
| { |
| char *file_name = 0; |
| TARGET_DIRECTORY_FILENAME *target_dir_filename; |
| |
| if ((!strcmp (target, "Top") || !strlen (target)) |
| && self->conf->TOP_NODE_FILE_TARGET.o.string) |
| { |
| file_name = strdup (self->conf->TOP_NODE_FILE_TARGET.o.string); |
| } |
| else |
| { |
| if (extension) |
| xasprintf (&file_name, "%s.%s", target_filebase, extension); |
| else |
| file_name = strdup (target_filebase); |
| } |
| target_dir_filename |
| = call_file_id_setting_external_target_split_name (self, |
| normalized, external_node, target, directory, file_name); |
| if (target_dir_filename) |
| { |
| free (directory); |
| directory = target_dir_filename->directory; |
| free (file_name); |
| file_name = target_dir_filename->filename; |
| free (target); |
| target = target_dir_filename->target; |
| free (target_dir_filename); |
| } |
| text_append (&result, directory); |
| text_append (&result, file_name); |
| if (strlen (target)) |
| { |
| text_append_n (&result, "#", 1); |
| text_append (&result, target); |
| } |
| |
| free (file_name); |
| free (directory); |
| } |
| else |
| { |
| TARGET_FILENAME *target_filename; |
| |
| if (!strlen (target)) |
| { |
| free (target); |
| target = strdup ("Top"); |
| } |
| |
| target_filename |
| = call_file_id_setting_external_target_non_split_name (self, |
| normalized, external_node, target, file); |
| |
| if (target_filename) |
| { |
| free (file); |
| file = target_filename->filename; |
| free (target); |
| target = target_filename->target; |
| free (target_filename); |
| } |
| text_append (&result, file); |
| if (strlen (target)) |
| { |
| text_append_n (&result, "#", 1); |
| text_append (&result, target); |
| } |
| |
| free (file); |
| } |
| |
| free (target); |
| free (target_filebase); |
| return result.text; |
| } |
| |
| /* index within the global (including special units) directions */ |
| int |
| html_special_unit_variety_direction_index (const CONVERTER *self, |
| const char *special_unit_variety) |
| { |
| /* number is index +1 */ |
| size_t number = find_string (&self->special_unit_varieties, |
| special_unit_variety); |
| int i = number -1; |
| if (i >= 0) |
| return D_Last +1 +i; |
| return -1; |
| } |
| |
| /* |
| If FIND_CONTAINER is set, the element that holds the command output |
| is found, otherwise the element that holds the command is found. This is |
| mostly relevant for footnote only. |
| If no known root element type is found, the returned root element is undef, |
| and not set to the element at the tree root |
| */ |
| /* NOTE should not be called with a text element */ |
| ROOT_AND_UNIT * |
| html_get_tree_root_element (CONVERTER *self, const ELEMENT *command, |
| int find_container) |
| { |
| const ELEMENT *current = command; |
| const OUTPUT_UNIT *output_unit = 0; |
| const ELEMENT *root_command = 0; |
| |
| while (1) |
| { |
| enum command_id data_cmd = element_builtin_data_cmd (current); |
| unsigned long flags = builtin_command_data[data_cmd].flags; |
| |
| if (current->type == ET_special_unit_element) |
| { |
| ROOT_AND_UNIT *result = malloc (sizeof (ROOT_AND_UNIT)); |
| result->output_unit = current->e.c->associated_unit; |
| result->root = current; |
| return result; |
| } |
| |
| if (data_cmd && (flags & CF_root)) |
| root_command = current; |
| else if (data_cmd && (flags & CF_block) |
| && builtin_command_data[data_cmd].data == BLOCK_region) |
| { |
| const OUTPUT_UNIT_LIST *output_units |
| = retrieve_output_units (self->document, |
| self->output_units_descriptors[OUDT_units]); |
| if (data_cmd == CM_copying |
| && self->document->global_commands.insertcopying.number > 0) |
| { |
| const ELEMENT_LIST global_insertcopying |
| = self->document->global_commands.insertcopying; |
| size_t i; |
| for (i = 0; i < global_insertcopying.number; i++) |
| { |
| const ELEMENT *insertcopying |
| = global_insertcopying.list[i]; |
| ROOT_AND_UNIT *cur_result = html_get_tree_root_element (self, |
| insertcopying, find_container); |
| if (cur_result->output_unit || cur_result->root) |
| return cur_result; |
| free (cur_result); |
| } |
| } |
| else if (data_cmd == CM_titlepage |
| && self->conf->USE_TITLEPAGE_FOR_TITLE.o.integer > 0 |
| && self->conf->SHOW_TITLE.o.integer > 0) |
| { |
| ROOT_AND_UNIT *result = malloc (sizeof (ROOT_AND_UNIT)); |
| result->output_unit = output_units->list[0]; |
| result->root = output_units->list[0]->uc.unit_command; |
| return result; |
| } |
| if (output_unit || root_command) |
| fatal ("Problem output_unit, root_command"); |
| ROOT_AND_UNIT *result = malloc (sizeof (ROOT_AND_UNIT)); |
| memset (result, 0, sizeof (ROOT_AND_UNIT)); |
| return result; |
| } |
| else if (find_container |
| && html_commands_data[data_cmd].flags & HF_special_variety) |
| { |
| int j; |
| for (j = 0; self->command_special_variety_name_index[j].cmd; j++) |
| { |
| /* @footnote and possibly @*contents when a separate element is set */ |
| COMMAND_ID_INDEX cmd_variety_index |
| = self->command_special_variety_name_index[j]; |
| if (cmd_variety_index.cmd == data_cmd) |
| { |
| char *special_unit_variety |
| = self->special_unit_varieties.list[cmd_variety_index.index]; |
| int special_unit_direction_index |
| = html_special_unit_variety_direction_index (self, |
| special_unit_variety); |
| const OUTPUT_UNIT *special_unit |
| = self->global_units_directions[special_unit_direction_index]; |
| if (special_unit) |
| { |
| ROOT_AND_UNIT *result = malloc (sizeof (ROOT_AND_UNIT)); |
| result->output_unit = special_unit; |
| result->root = 0; |
| return result; |
| } |
| break; |
| } |
| } |
| } |
| |
| if (current->e.c->associated_unit) |
| { |
| ROOT_AND_UNIT *result = malloc (sizeof (ROOT_AND_UNIT)); |
| result->output_unit = current->e.c->associated_unit; |
| result->root = current; |
| return result; |
| } |
| else if (current->e.c->parent) |
| { |
| current = current->e.c->parent; |
| } |
| else |
| { |
| ROOT_AND_UNIT *result = malloc (sizeof (ROOT_AND_UNIT)); |
| result->output_unit = 0; |
| result->root = root_command; |
| return result; |
| } |
| } |
| } |
| |
| const FILE_NUMBER_NAME * |
| html_command_filename (CONVERTER *self, const ELEMENT *command) |
| { |
| HTML_TARGET *target_info; |
| |
| target_info = html_get_target (self, command); |
| |
| if (target_info) |
| { |
| ROOT_AND_UNIT *root_unit; |
| |
| if (target_info->filename_set) |
| return &target_info->file_number_name; |
| |
| /* this finds a special element for footnote command if such an element |
| exists. This is best, the special element filename is the footnote |
| filename. */ |
| |
| root_unit |
| = html_get_tree_root_element (self, command, 1); |
| |
| if (root_unit && root_unit->output_unit |
| && root_unit->output_unit->unit_filename) |
| { |
| target_info->file_number_name.filename |
| = root_unit->output_unit->unit_filename; |
| if (root_unit->output_unit->unit_type == OU_unit) |
| { |
| size_t file_index |
| = self->output_unit_file_indices[root_unit->output_unit->index]; |
| target_info->file_number_name.file_number = file_index +1; |
| } |
| } |
| target_info->filename_set = 1; |
| |
| free (root_unit); |
| |
| return &target_info->file_number_name; |
| } |
| |
| return 0; |
| } |
| |
| const ELEMENT * |
| html_command_root_element_command (CONVERTER *self, const ELEMENT *command) |
| { |
| HTML_TARGET *target_info; |
| |
| target_info = html_get_target (self, command); |
| if (target_info) |
| { |
| if (!target_info->root_element_command_set) |
| { |
| /* in contrast with command_filename() we find the root element through |
| the location holding the @footnote command. It is better, as the |
| footnote special element is not associated with a root command, |
| it is better to stay in the document to find a root element. */ |
| ROOT_AND_UNIT *root_unit |
| = html_get_tree_root_element (self, command, 0); |
| |
| if (root_unit && root_unit->output_unit |
| && root_unit->output_unit->unit_type == OU_unit) |
| { |
| target_info->root_element_command |
| = root_unit->output_unit->uc.unit_command; |
| } |
| else |
| target_info->root_element_command = 0; |
| |
| target_info->root_element_command_set = 1; |
| |
| free (root_unit); |
| |
| } |
| return target_info->root_element_command; |
| } |
| return 0; |
| } |
| |
| const ELEMENT * |
| html_command_node (CONVERTER *self, const ELEMENT *command) |
| { |
| HTML_TARGET *target_info; |
| |
| target_info = html_get_target (self, command); |
| if (target_info) |
| { |
| if (!target_info->node_command_set) |
| { |
| /* this finds a special element for footnote command if |
| such an element exists */ |
| ROOT_AND_UNIT *root_unit |
| = html_get_tree_root_element (self, command, 1); |
| if (root_unit) |
| { |
| if (root_unit->root) |
| { |
| const ELEMENT *root_command = root_unit->root; |
| if (root_command && root_command->e.c->cmd == CM_node) |
| target_info->node_command = root_command; |
| else if (self->document && root_command |
| && builtin_command_data[root_command->e.c->cmd].flags & CF_root) |
| { |
| int status; |
| const SECTION_RELATIONS_LIST *sections_list |
| = &self->document->sections_list; |
| size_t section_number |
| = lookup_extra_integer (root_command, |
| AI_key_section_number, &status); |
| |
| const SECTION_RELATIONS *section_relations |
| = sections_list->list[section_number -1]; |
| if (section_relations->associated_node) |
| target_info->node_command |
| = section_relations->associated_node->element; |
| } |
| } |
| free (root_unit); |
| } |
| target_info->node_command_set = 1; |
| } |
| return target_info->node_command; |
| } |
| return 0; |
| } |
| |
| /* return value to be freed */ |
| /* SPECIFIED_TARGET can be used to specify explicitly the target. |
| */ |
| char * |
| html_internal_command_href (CONVERTER *self, const ELEMENT *command, |
| const char *source_filename, |
| const char *specified_target) |
| { |
| const HTML_TARGET *target_info; |
| TEXT href; |
| const char *filename_from; |
| const char *target = 0; |
| const FILE_NUMBER_NAME *target_filename; |
| FILE_NUMBER_NAME *set_target_filename = 0; |
| |
| if (source_filename) |
| filename_from = source_filename; |
| else |
| filename_from = self->current_filename.filename; |
| |
| |
| if (specified_target) |
| target = specified_target; |
| else |
| { |
| const ELEMENT *target_command = command; |
| /* for sectioning command prefer the associated node. If there is no |
| associated node, use the associated_anchor_command. This order |
| is important for sectioning commands, it means that in the following |
| case the @chapter href will be given by the @node, even if there is |
| an @xrefname in-between. |
| |
| @node my node |
| @xrefname name for my node |
| |
| @chapter Chapter without directly associated node |
| */ |
| if (self->document) |
| { |
| int status; |
| size_t section_number |
| = lookup_extra_integer (command, AI_key_section_number, &status); |
| |
| if (section_number) |
| { |
| const SECTION_RELATIONS_LIST *sections_list |
| = &self->document->sections_list; |
| const SECTION_RELATIONS *section_relations |
| = sections_list->list[section_number -1]; |
| |
| if (section_relations->associated_node) |
| target_command = section_relations->associated_node->element; |
| else if (section_relations->associated_anchor_command) |
| target_command |
| = section_relations->associated_anchor_command->element; |
| } |
| else |
| { |
| size_t heading_number |
| = lookup_extra_integer (command, |
| AI_key_heading_number, &status); |
| |
| if (heading_number) |
| { |
| const HEADING_RELATIONS_LIST *headings_list |
| = &self->document->headings_list; |
| const HEADING_RELATIONS *heading_relations |
| = headings_list->list[heading_number -1]; |
| |
| if (heading_relations->associated_anchor_command) |
| target_command |
| = heading_relations->associated_anchor_command->element; |
| } |
| } |
| } |
| |
| target_info = html_get_target (self, target_command); |
| if (target_info) |
| target = target_info->target; |
| } |
| if (!target) |
| return 0; |
| |
| text_init (&href); |
| |
| target_filename = html_command_filename (self, command); |
| if (!target_filename || !target_filename->filename) |
| { |
| /* Happens if there are no pages, for example if OUTPUT is set to '' |
| as in the test cases. Also for things in @titlepage when |
| titlepage is not output. */ |
| const OUTPUT_UNIT_LIST *output_units |
| = retrieve_output_units (self->document, |
| self->output_units_descriptors[OUDT_units]); |
| if (output_units->list[0]->unit_filename) |
| { /* In that case use the first page. */ |
| set_target_filename = (FILE_NUMBER_NAME *) |
| malloc (sizeof (FILE_NUMBER_NAME)); |
| set_target_filename->filename = output_units->list[0]->unit_filename; |
| set_target_filename->file_number |
| = self->output_unit_file_indices[0] +1; |
| } |
| target_filename = set_target_filename; |
| } |
| |
| if (target_filename && target_filename->filename) |
| { |
| if (!filename_from |
| || strcmp (target_filename->filename, filename_from)) |
| { |
| const ELEMENT *command_root_element |
| = html_command_root_element_command (self, command); |
| char *protected_filename |
| = url_protect_file_text (self, target_filename->filename); |
| |
| text_append (&href, protected_filename); |
| free (protected_filename); |
| |
| /* omit target if the command is an element command, there is only |
| one element in file and there is a file in the href */ |
| if (filename_from && command_root_element) |
| { |
| int possible_empty_target = 0; |
| |
| if (command_root_element == command) |
| possible_empty_target = 1; |
| else if (command_root_element->e.c->cmd == CM_node |
| && self->document) |
| { |
| int status; |
| size_t node_number |
| = lookup_extra_integer (command_root_element, |
| AI_key_node_number, &status); |
| |
| if (node_number) |
| { |
| const NODE_RELATIONS *node_relations |
| = self->document->nodes_list.list[node_number -1]; |
| |
| if (node_relations->associated_section |
| && node_relations->associated_section->element == command) |
| possible_empty_target = 1; |
| } |
| } |
| |
| if (possible_empty_target) |
| { |
| if (target_filename->file_number > 0) |
| { |
| size_t count_in_file |
| = count_elements_in_file_number (self, CEFT_total, |
| target_filename->file_number); |
| if (count_in_file == 1) |
| target = ""; |
| } |
| } |
| } |
| } |
| } |
| |
| if (strlen (target)) |
| { |
| text_append_n (&href, "#", 1); |
| text_append (&href, target); |
| } |
| |
| if (set_target_filename) |
| free (set_target_filename); |
| |
| if (href.end <= 0) |
| { |
| free (href.text); |
| return 0; |
| } |
| |
| return href.text; |
| } |
| |
| /* return value to be freed by caller */ |
| char * |
| html_command_description (CONVERTER *self, const ELEMENT *command, |
| const enum html_text_type type) |
| { |
| HTML_TARGET *target_info; |
| |
| const ELEMENT *manual_content = lookup_extra_container (command, |
| AI_key_manual_content); |
| if (manual_content) |
| return 0; |
| |
| target_info = html_get_target (self, command); |
| |
| if (target_info) |
| { |
| if (target_info->command_description[type]) |
| return strdup (target_info->command_description[type]); |
| else |
| { |
| const ELEMENT *node = 0; |
| char *explanation; |
| char *context_name; |
| const ELEMENT *node_description = 0; |
| int long_description = 0; |
| int formatted_nodedescription_nr = 0; |
| HTML_TARGET *node_target_info; |
| char *multiple_formatted = 0; |
| ELEMENT *description_element; |
| const char *command_name; |
| enum command_id cmd; |
| unsigned long context_type = 0; |
| |
| if (command->type == ET_special_unit_element) |
| return 0; |
| |
| cmd = element_builtin_cmd (command); |
| |
| if (cmd == CM_float || cmd == CM_anchor || cmd == CM_namedanchor) |
| return 0; |
| |
| if (cmd == CM_node) |
| node = command; |
| else if (self->document) |
| { |
| int status; |
| size_t section_number |
| = lookup_extra_integer (command, |
| AI_key_section_number, &status); |
| const SECTION_RELATIONS *section_relations |
| = self->document->sections_list.list[section_number -1]; |
| if (section_relations->associated_node) |
| node = section_relations->associated_node->element; |
| } |
| |
| if (!node) |
| return 0; |
| |
| if (self->document) |
| { |
| int status; |
| size_t node_number |
| = lookup_extra_integer (node, |
| AI_key_node_number, &status); |
| const NODE_RELATIONS *node_relations |
| = self->document->nodes_list.list[node_number -1]; |
| |
| if (node_relations->node_description) |
| node_description = node_relations->node_description; |
| else if (node_relations->node_long_description) |
| { |
| node_description = node_relations->node_long_description; |
| long_description = 1; |
| } |
| } |
| |
| if (!node_description) |
| return 0; |
| |
| node_target_info = html_get_target (self, node); |
| node_target_info->formatted_nodedescription_nr++; |
| formatted_nodedescription_nr |
| = node_target_info->formatted_nodedescription_nr; |
| |
| if (formatted_nodedescription_nr > 1) |
| { |
| xasprintf (&multiple_formatted, |
| "node-description-%d", |
| formatted_nodedescription_nr); |
| } |
| |
| if (!long_description) |
| description_element = node_description->e.c->contents.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); |
| } |
| |
| command_name = element_command_name (command); |
| xasprintf (&context_name, "%s description", command_name); |
| xasprintf (&explanation, "command_description:%s @%s", |
| html_command_text_type_name[type], |
| command_name); |
| |
| if (type == HTT_string) |
| context_type |= CTXF_string; |
| |
| target_info->command_description[type] |
| = html_convert_tree_new_formatting_context (self, |
| description_element, context_name, context_type, |
| multiple_formatted, explanation, 0); |
| free (context_name); |
| free (explanation); |
| |
| if (formatted_nodedescription_nr > 1) |
| free (multiple_formatted); |
| |
| if (long_description) |
| { |
| remove_tree_to_build (self, description_element); |
| description_element->e.c->contents.list = 0; |
| destroy_element (description_element); |
| } |
| return strdup (target_info->command_description[type]); |
| } |
| } |
| /* |
| Can happen |
| * if USE_NODES is 0 and there are no sectioning commands. |
| * if a special element target was set to undef in user defined code. |
| * for @*ref with missing targets (maybe @novalidate needed in that case). |
| * for @node header if the node consist only in spaces (example in sectioning |
| in_menu_only_special_ascii_spaces_node). |
| * for multiple targets with the same name, eg both @node and @anchor |
| * with @inforef with node argument only, without manual argument. |
| */ |
| return 0; |
| } |
| |
| /* return value to be freed */ |
| /* Return string for linking to $COMMAND with <a href>. |
| SOURCE_COMMAND is for messages only. |
| SPECIFIED_TARGET can be set to specify explicitly the target |
| */ |
| char * |
| html_command_href (CONVERTER *self, const ELEMENT *command, |
| const char *source_filename, |
| const ELEMENT *source_command, |
| const char *specified_target) |
| { |
| const ELEMENT *manual_content = lookup_extra_container (command, |
| AI_key_manual_content); |
| if (manual_content) |
| { |
| return external_node_href (self, command, source_command); |
| } |
| |
| return html_internal_command_href (self, command, source_filename, |
| specified_target); |
| } |
| |
| const char * |
| html_command_contents_target (CONVERTER *self, const ELEMENT *command, |
| enum command_id contents_or_shortcontents) |
| { |
| const HTML_TARGET *target_info; |
| |
| if (contents_or_shortcontents == CM_summarycontents) |
| contents_or_shortcontents = CM_shortcontents; |
| |
| target_info = html_get_target (self, command); |
| if (target_info) |
| { |
| if (contents_or_shortcontents == CM_shortcontents) |
| return target_info->shortcontents_target; |
| else if (contents_or_shortcontents == CM_contents) |
| return target_info->contents_target; |
| } |
| return 0; |
| } |
| |
| static HTML_TARGET * |
| get_footnote_location_target (const CONVERTER *self, const ELEMENT *command) |
| { |
| HTML_TARGET *result |
| = find_element_special_target |
| (&self->html_special_targets[ST_footnote_location], |
| command); |
| return result; |
| } |
| |
| const char * |
| html_footnote_location_target (const CONVERTER *self, const ELEMENT *command) |
| { |
| const HTML_TARGET *footnote_location_special_target_info |
| = get_footnote_location_target (self, command); |
| if (footnote_location_special_target_info) |
| return footnote_location_special_target_info->target; |
| return 0; |
| } |
| |
| /* Return string for linking to CONTENTS_OR_SHORTCONTENTS associated |
| element from $COMMAND with <a href> */ |
| char * |
| html_command_contents_href (CONVERTER *self, const ELEMENT *command, |
| enum command_id contents_or_shortcontents, |
| const char *source_filename) |
| { |
| int j; |
| const char *filename_from; |
| const char *target = html_command_contents_target (self, command, |
| contents_or_shortcontents); |
| |
| if (source_filename) |
| filename_from = source_filename; |
| else |
| filename_from = self->current_filename.filename; |
| |
| for (j = 0; self->command_special_variety_name_index[j].cmd; j++) |
| { |
| const COMMAND_ID_INDEX cmd_variety_index |
| = self->command_special_variety_name_index[j]; |
| if (cmd_variety_index.cmd == contents_or_shortcontents) |
| { |
| TEXT href; |
| const FILE_NUMBER_NAME *target_filename = 0; |
| const char *special_unit_variety |
| = self->special_unit_varieties.list[cmd_variety_index.index]; |
| int special_unit_direction_index |
| = html_special_unit_variety_direction_index (self, |
| special_unit_variety); |
| const OUTPUT_UNIT *special_unit |
| = self->global_units_directions[special_unit_direction_index]; |
| if (special_unit) |
| { |
| target_filename = html_command_filename (self, |
| special_unit->uc.special_unit_command); |
| } |
| |
| text_init (&href); |
| |
| if (target_filename && target_filename->filename |
| && (!filename_from |
| || strcmp (target_filename->filename, filename_from))) |
| text_append (&href, target_filename->filename); |
| |
| if (target && strlen (target)) |
| { |
| text_append_n (&href, "#", 1); |
| text_append (&href, target); |
| } |
| |
| if (href.end <= 0) |
| { |
| free (href.text); |
| return 0; |
| } |
| return href.text; |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| SPECIFIED_TARGET can be set to specify explicitly the target. |
| TARGET_FILENAME_IN can be set to specify explicitly the file. |
| Returned string to be freed by the caller. |
| */ |
| char * |
| html_footnote_location_href (CONVERTER *self, const ELEMENT *command, |
| const char *source_filename, |
| const char *specified_target, |
| const char *target_filename_in) |
| { |
| TEXT href; |
| const char *filename_from; |
| HTML_TARGET *footnote_location_target_info; |
| const char *target = 0; |
| const char *target_filename = target_filename_in; |
| |
| if (source_filename) |
| filename_from = source_filename; |
| else |
| filename_from = self->current_filename.filename; |
| |
| footnote_location_target_info = get_footnote_location_target (self, command); |
| |
| if (specified_target) |
| target = specified_target; |
| else |
| target = footnote_location_target_info->target; |
| |
| /* In the default footnote formatting functions, which calls |
| footnote_location_href, the target file is always known as the |
| footnote in the document appears before the footnote text formatting. |
| $target_filename is therefore always defined. It is a good thing |
| for the case of @footnote being formatted more than once (in multiple |
| @insertcopying for instance) as the file found just below may not be the |
| correct one in such a case. |
| */ |
| if (!target_filename) |
| { |
| if (footnote_location_target_info |
| && footnote_location_target_info->file_number_name.filename) |
| { |
| target_filename |
| = footnote_location_target_info->file_number_name.filename; |
| } |
| else |
| { |
| /* in contrast with command_filename() we find the location holding |
| the @footnote command, not the footnote element with footnotes */ |
| ROOT_AND_UNIT *root_unit |
| = html_get_tree_root_element (self, command, 0); |
| |
| if (root_unit && root_unit->output_unit |
| && root_unit->output_unit->unit_filename) |
| { |
| size_t file_index |
| = self->output_unit_file_indices[root_unit->output_unit->index]; |
| footnote_location_target_info->file_number_name.file_number |
| = file_index +1; |
| footnote_location_target_info->file_number_name.filename |
| = root_unit->output_unit->unit_filename; |
| } |
| footnote_location_target_info->filename_set = 1; |
| |
| free (root_unit); |
| target_filename |
| = footnote_location_target_info->file_number_name.filename; |
| } |
| } |
| |
| text_init (&href); |
| text_append (&href, ""); |
| if (target_filename |
| && (!filename_from || strcmp (target_filename, filename_from))) |
| { |
| text_append (&href, target_filename); |
| } |
| if (target && strlen (target)) |
| { |
| text_append_n (&href, "#", 1); |
| text_append (&href, target); |
| } |
| return href.text; |
| } |
| |
| static ELEMENT * |
| special_unit_info_tree (CONVERTER *self, |
| const enum special_unit_info_tree type, |
| const char *special_unit_variety) |
| { |
| /* number is index +1 */ |
| size_t number = find_string (&self->special_unit_varieties, |
| special_unit_variety); |
| int i = number -1; |
| const char *special_unit_info_string; |
| char *translation_context; |
| |
| if (self->translated_special_unit_info_tree[type][i]) |
| return self->translated_special_unit_info_tree[type][i]; |
| |
| special_unit_info_string |
| = self->translated_special_unit_info_texinfo[type][i]; |
| |
| /* if set to undef in user customization. To be forbidden? */ |
| if (!special_unit_info_string) |
| return 0; |
| |
| xasprintf (&translation_context, "%s section %s", |
| special_unit_variety, special_unit_info_tree_names[type]); |
| self->translated_special_unit_info_tree[type][i] |
| = html_pcdt_tree (translation_context, special_unit_info_string, |
| self, 0); |
| free (translation_context); |
| return self->translated_special_unit_info_tree[type][i]; |
| } |
| |
| char * |
| html_special_unit_info_text (CONVERTER *self, |
| const enum special_unit_info_tree type, |
| const char *special_unit_variety, |
| enum conversion_context context_type) |
| { |
| ELEMENT *unit_info_tree; |
| char *explanation; |
| char *result; |
| |
| unit_info_tree = special_unit_info_tree (self, |
| type, special_unit_variety); |
| if (!unit_info_tree) |
| return strdup (""); |
| |
| xasprintf (&explanation, "convert %s %s/%s", |
| special_unit_variety, |
| special_unit_info_tree_names[type], |
| html_command_text_type_name[context_type]); |
| |
| add_tree_to_build (self, unit_info_tree); |
| |
| if (context_type == HCC_type_string) |
| result = html_convert_tree_new_formatting_context (self, |
| unit_info_tree, explanation, |
| CTXF_string, 0, 0, 0); |
| else |
| result = html_convert_tree_explanation (self, unit_info_tree, explanation); |
| |
| remove_tree_to_build (self, unit_info_tree); |
| |
| free (explanation); |
| |
| return result; |
| } |
| |
| |
| /* the returned TREE_ADDED_ELEMENTS may not be NULL but have a NULL tree |
| field, for instance in the case of an empty sectioning element |
| */ |
| static TREE_ADDED_ELEMENTS * |
| html_internal_command_tree (CONVERTER *self, const ELEMENT *command, |
| int no_number) |
| { |
| TREE_ADDED_ELEMENTS *tree; |
| HTML_TARGET *target_info; |
| |
| target_info = html_get_target (self, command); |
| if (target_info) |
| { |
| if (!target_info->tree.status) |
| { |
| tree = &target_info->tree; |
| tree->status = tree_added_status_reused_tree; |
| if (command->type == ET_special_unit_element) |
| { |
| const char *special_unit_variety |
| = command->e.c->associated_unit->special_unit_variety; |
| ELEMENT *heading_tree = special_unit_info_tree (self, |
| SUIT_type_heading, special_unit_variety); |
| tree->tree = heading_tree; |
| } |
| else if (command->e.c->cmd == CM_node |
| || command->e.c->cmd == CM_anchor |
| || command->e.c->cmd == CM_namedanchor) |
| { |
| ELEMENT *label_element; |
| if (command->e.c->cmd == CM_anchor |
| || command->e.c->cmd == CM_namedanchor) |
| label_element = command->e.c->contents.list[0]; |
| else |
| { |
| /* arguments_line type element */ |
| const ELEMENT *arguments_line |
| = command->e.c->contents.list[0]; |
| label_element = arguments_line->e.c->contents.list[0]; |
| } |
| tree->tree = label_element; |
| tree->in_code = 1; |
| } |
| else if (command->e.c->cmd == CM_float) |
| { |
| tree->tree = float_type_number (self, command); |
| tree->status = tree_added_status_new_tree; |
| } |
| else |
| { |
| ELEMENT *line_arg; |
| |
| if (builtin_command_data[command->e.c->cmd].flags & CF_root) |
| { |
| /* arguments_line type element */ |
| const ELEMENT *arguments_line |
| = command->e.c->contents.list[0]; |
| line_arg = arguments_line->e.c->contents.list[0]; |
| } |
| else |
| /* @heading* commands */ |
| line_arg = command->e.c->contents.list[0]; |
| |
| if (line_arg->e.c->contents.number > 0) |
| { |
| const char *section_number; |
| section_number |
| = lookup_extra_string (command, |
| AI_key_section_heading_number); |
| |
| if (section_number |
| && self->conf->NUMBER_SECTIONS.o.integer != 0) |
| { |
| NAMED_STRING_ELEMENT_LIST *replaced_substrings |
| = new_named_string_element_list (); |
| ELEMENT *e_number = new_text_element (ET_normal_text); |
| ELEMENT *section_title_copy = copy_tree (line_arg, 0); |
| |
| add_element_to_named_string_element_list ( |
| replaced_substrings, "section_title", |
| section_title_copy); |
| text_append (e_number->e.text, section_number); |
| add_element_to_named_string_element_list ( |
| replaced_substrings, "number", e_number); |
| |
| if (command->e.c->cmd == CM_appendix) |
| { |
| int status; |
| int section_level = lookup_extra_integer (command, |
| AI_key_section_level, &status); |
| if (section_level == 1) |
| { |
| tree->tree |
| = html_cdt_tree ( |
| "Appendix {number} {section_title}", |
| self, replaced_substrings, 0); |
| } |
| } |
| if (!tree->tree) |
| /* TRANSLATORS: numbered section title */ |
| tree->tree = html_cdt_tree ("{number} {section_title}", |
| self, replaced_substrings, 0); |
| |
| destroy_named_string_element_list (replaced_substrings); |
| tree->status = tree_added_status_new_tree; |
| } |
| else |
| { |
| tree->tree = line_arg; |
| } |
| } |
| |
| target_info->tree_nonumber.tree = line_arg; |
| target_info->tree_nonumber.status = tree_added_status_reused_tree; |
| } |
| } |
| |
| if (no_number && target_info->tree_nonumber.tree) |
| return &target_info->tree_nonumber; |
| else |
| return &target_info->tree; |
| } |
| |
| return 0; |
| } |
| |
| static char * |
| html_convert_command_tree (CONVERTER *self, const ELEMENT *command, |
| const enum html_text_type type, |
| ELEMENT *selected_tree, |
| int in_code, |
| const char *command_info) |
| { |
| char *explanation = 0; |
| const char *context_name; |
| unsigned long context_type = 0; |
| char *result; |
| |
| if (command->e.c->cmd) |
| { |
| const char *command_name = element_command_name (command); |
| context_name = command_name; |
| xasprintf (&explanation, "%s:%s @%s", command_info, |
| html_command_text_type_name[type], |
| command_name); |
| } |
| else |
| { |
| context_name = type_data[command->type].name; |
| if (command->type == ET_special_unit_element) |
| { |
| char *special_unit_variety |
| = command->e.c->associated_unit->special_unit_variety; |
| xasprintf (&explanation, "%s %s", command_info, |
| special_unit_variety); |
| } |
| } |
| |
| if (type == HTT_string || type == HTT_string_nonumber) |
| context_type |= CTXF_string; |
| if (in_code) |
| context_type |= CTXF_code; |
| |
| html_new_document_context (self, context_name, context_type, |
| explanation, 0); |
| |
| html_set_multiple_conversions (self, 0); |
| push_element_reference_stack_element (&self->referred_command_stack, |
| command, get_sv_hv (command->sv)); |
| |
| add_tree_to_build (self, selected_tree); |
| |
| result |
| = html_convert_tree_explanation (self, selected_tree, explanation); |
| free (explanation); |
| |
| remove_tree_to_build (self, selected_tree); |
| |
| pop_element_reference_stack (&self->referred_command_stack); |
| |
| html_unset_multiple_conversions (self); |
| |
| html_pop_document_context (self); |
| |
| return result; |
| } |
| |
| /* return value to be freed by caller */ |
| char * |
| html_internal_command_text (CONVERTER *self, const ELEMENT *command, |
| const enum html_text_type type) |
| { |
| HTML_TARGET *target_info = html_get_target (self, command); |
| |
| if (target_info) |
| { |
| if (target_info->command_text[type]) |
| return strdup (target_info->command_text[type]); |
| else |
| { |
| ELEMENT *selected_tree; |
| TREE_ADDED_ELEMENTS *command_tree |
| = html_internal_command_tree (self, command, 0); |
| |
| if (!command_tree->tree) |
| return strdup (""); |
| |
| if ((type == HTT_text_nonumber || type == HTT_string_nonumber |
| || type == HTT_section_nonumber) |
| && target_info->tree_nonumber.tree) |
| selected_tree = target_info->tree_nonumber.tree; |
| else |
| selected_tree = command_tree->tree; |
| |
| target_info->command_text[type] |
| = html_convert_command_tree (self, command, type, selected_tree, |
| command_tree->in_code, |
| "command_text"); |
| return strdup (target_info->command_text[type]); |
| } |
| } |
| |
| /* |
| Can happen |
| * if USE_NODES is 0 and there are no sectioning commands. |
| * if a special element target was set to undef in user defined code. |
| * for @*ref with missing targets (maybe @novalidate needed in that case). |
| * for @node header if the node consist only in spaces (example in sectioning |
| in_menu_only_special_ascii_spaces_node). |
| * for multiple targets with the same name, eg both @node and @anchor |
| * with @inforef with node argument only, without manual argument. |
| */ |
| |
| return 0; |
| } |
| |
| /* return value to be freed by caller */ |
| char * |
| html_command_text (CONVERTER *self, const ELEMENT *command, |
| const enum html_text_type type) |
| { |
| char *result; |
| ELEMENT *manual_content = lookup_extra_container (command, |
| AI_key_manual_content); |
| if (manual_content) |
| { /* external node */ |
| unsigned long context_type = CTXF_code; |
| char *context_str; |
| ELEMENT *command_tree = new_element (ET_NONE); |
| ELEMENT *open_p = new_text_element (ET_normal_text); |
| ELEMENT *close_p = new_text_element (ET_normal_text); |
| |
| ELEMENT *node_content = lookup_extra_container (command, |
| AI_key_node_content); |
| |
| text_append_n (open_p->e.text, "(", 1); |
| text_append_n (close_p->e.text, ")", 1); |
| |
| add_to_contents_as_array (command_tree, open_p); |
| add_to_contents_as_array (command_tree, manual_content); |
| add_to_contents_as_array (command_tree, close_p); |
| |
| if (node_content) |
| add_to_contents_as_array (command_tree, node_content); |
| |
| if (command->e.c->cmd) |
| /* this never happens, as the external node label tree |
| element is never directly an @-command. It can be an @-command |
| argument, in a menu, or a reconstituted tree. */ |
| xasprintf (&context_str, "command_text %s @%s", |
| html_command_text_type_name[type], |
| element_command_name (command)); |
| else if (command->type) |
| xasprintf (&context_str, "command_text %s %s", |
| html_command_text_type_name[type], |
| type_data[command->type].name); |
| else |
| xasprintf (&context_str, "command_text %s ", |
| html_command_text_type_name[type]); |
| |
| if (type == HTT_string || type == HTT_string_nonumber) |
| context_type |= CTXF_string; |
| |
| add_tree_to_build (self, command_tree); |
| result = html_convert_tree_new_formatting_context (self, |
| command_tree, |
| context_str, context_type, |
| "command_text-manual_content", 0, 0); |
| |
| free (context_str); |
| remove_tree_to_build (self, command_tree); |
| |
| destroy_element (open_p); |
| destroy_element (close_p); |
| destroy_element (command_tree); |
| return result; |
| } |
| |
| return html_internal_command_text (self, command, type); |
| } |
| |
| TREE_ADDED_ELEMENTS * |
| html_internal_command_name_tree (CONVERTER *self, const ELEMENT *command, |
| int no_number) |
| { |
| TREE_ADDED_ELEMENTS *tree; |
| HTML_TARGET *target_info; |
| |
| target_info = html_get_target (self, command); |
| if (target_info) |
| { |
| if (!target_info->name_tree.status) |
| { |
| tree = &target_info->name_tree; |
| if (command->e.c->cmd == CM_namedanchor |
| && command->e.c->contents.number > 1 |
| && command->e.c->contents.list[1]->e.c->contents.number > 0) |
| { |
| tree->status = tree_added_status_reused_tree; |
| tree->tree = command->e.c->contents.list[1]; |
| } |
| } |
| |
| /* cannot happen currently */ |
| if (no_number && target_info->name_tree_nonumber.tree) |
| return &target_info->name_tree_nonumber; |
| else |
| return &target_info->name_tree; |
| } |
| |
| return 0; |
| } |
| |
| |
| /* return value to be freed by caller */ |
| char * |
| html_internal_command_name (CONVERTER *self, const ELEMENT *command, |
| const enum html_text_type type) |
| { |
| HTML_TARGET *target_info = html_get_target (self, command); |
| |
| if (target_info) |
| { |
| if (target_info->command_name[type]) |
| return strdup (target_info->command_name[type]); |
| else |
| { |
| ELEMENT *selected_tree; |
| TREE_ADDED_ELEMENTS *command_name_tree |
| = html_internal_command_name_tree (self, command, 0); |
| |
| if (!command_name_tree->tree) |
| command_name_tree = html_internal_command_tree (self, command, 0); |
| |
| if (!command_name_tree->tree) |
| return strdup (""); |
| |
| if ((type == HTT_text_nonumber || type == HTT_string_nonumber |
| || type == HTT_section_nonumber) |
| && target_info->name_tree_nonumber.tree) |
| selected_tree = target_info->name_tree_nonumber.tree; |
| else |
| selected_tree = command_name_tree->tree; |
| |
| target_info->command_name[type] |
| = html_convert_command_tree (self, command, type, selected_tree, |
| command_name_tree->in_code, |
| "command_name"); |
| return strdup (target_info->command_name[type]); |
| } |
| } |
| return 0; |
| } |
| |
| char * |
| html_command_name (CONVERTER *self, const ELEMENT *command, |
| const enum html_text_type type) |
| { |
| ELEMENT *manual_content = lookup_extra_container (command, |
| AI_key_manual_content); |
| if (manual_content) |
| return html_command_text (self, command, type); |
| |
| return html_internal_command_name (self, command, type); |
| } |
| |
| /* |
| if OUTPUT_UNITS is defined, the first output unit is used if a proper |
| top output unit is not found. |
| */ |
| OUTPUT_UNIT * |
| html_get_top_unit (DOCUMENT *document, const OUTPUT_UNIT_LIST *output_units) |
| { |
| const ELEMENT *node_top = find_identifier_target |
| (&document->identifiers_target, "Top"); |
| const ELEMENT *section_top = document->global_commands.top; |
| |
| if (section_top) |
| return section_top->e.c->associated_unit; |
| else if (node_top) |
| return node_top->e.c->associated_unit; |
| else if (output_units) |
| return output_units->list[0]; |
| |
| return 0; |
| } |
| |
| static int |
| unit_is_top_output_unit (CONVERTER *self, const OUTPUT_UNIT *output_unit) |
| { |
| OUTPUT_UNIT *top_output_unit = html_get_top_unit (self->document, 0); |
| return (top_output_unit && top_output_unit == output_unit); |
| } |
| |
| /* return 1 if the direction is a global direction text, |
| not associated to an output unit (such as Space) */ |
| static int |
| html_global_direction_text (CONVERTER *self, int direction) |
| { |
| if ((direction > D_direction_Last && direction < D_direction_This) |
| || (direction > NON_SPECIAL_DIRECTIONS_NR |
| + (int) self->special_unit_varieties.number |
| + (int) self->added_global_units_directions.number -1)) |
| { |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* return value to be freed by caller */ |
| char * |
| from_element_direction (CONVERTER *self, int direction, |
| enum html_text_type type, |
| const OUTPUT_UNIT *source_unit, |
| const char *source_filename, |
| const ELEMENT *source_command) |
| { |
| const char *filename_from; |
| const OUTPUT_UNIT *target_unit = 0; |
| const ELEMENT *command = 0; |
| |
| /* this means that the direction given in Perl was not found in C */ |
| if (direction < 0) |
| return 0; |
| |
| if (!source_unit) |
| source_unit = self->current_output_unit; |
| |
| if (source_filename) |
| filename_from = source_filename; |
| else |
| filename_from = self->current_filename.filename; |
| |
| /* To debug: |
| fprintf (stderr, "FED: %d %s %s\n", direction, |
| html_command_text_type_name[type], |
| self->main_units_direction_names[direction]); |
| */ |
| |
| if (direction < D_direction_Last+1) |
| target_unit = self->global_units_directions[direction]; |
| else if (direction > NON_SPECIAL_DIRECTIONS_NR - 1) |
| { |
| /* special units (global) directions and added global directions */ |
| target_unit |
| = self->global_units_directions |
| [D_direction_Last + direction - NON_SPECIAL_DIRECTIONS_NR +1]; |
| } |
| else if ((!source_unit || unit_is_top_output_unit (self, source_unit)) |
| && self->conf->TOP_NODE_UP_URL.o.string |
| && (direction == D_direction_Up || direction == D_direction_NodeUp)) |
| { |
| if (type == HTT_href) |
| return strdup (self->conf->TOP_NODE_UP_URL.o.string); |
| else if (type == HTT_text || type == HTT_node |
| || type == HTT_string || type == HTT_section |
| || type == HTT_section_nonumber || type == HTT_string_nonumber) |
| { |
| if (self->conf->TOP_NODE_UP.o.string) |
| return strdup (self->conf->TOP_NODE_UP.o.string); |
| else |
| return 0; |
| } |
| else |
| { |
| char *msg; |
| xasprintf (&msg, "type %d not available for TOP_NODE_UP\n", type); |
| fatal (msg); |
| free (msg); |
| } |
| } |
| else if (!target_unit && source_unit |
| && source_unit->directions[direction - (D_direction_Space +1)]) |
| { |
| target_unit = source_unit->directions[direction - (D_direction_Space +1)]; |
| } |
| |
| if (target_unit) |
| { |
| if (target_unit->unit_type == OU_external_node_unit) |
| { |
| const ELEMENT *external_node_element |
| = target_unit->uc.unit_command; |
| if (type == HTT_href) |
| return external_node_href (self, external_node_element, |
| source_command); |
| else if (type == HTT_text || type == HTT_node) |
| return html_command_text (self, external_node_element, 0); |
| else if (type == HTT_string) |
| return html_command_text (self, external_node_element, |
| HTT_string); |
| } |
| else if (type == HTT_node) |
| { |
| if (target_unit->unit_node) |
| command = target_unit->unit_node->element; |
| |
| type = HTT_text; |
| } |
| else if (type == HTT_section || type == HTT_section_nonumber) |
| { |
| if (target_unit->unit_section) |
| command = target_unit->unit_section->element; |
| |
| if (type == HTT_section_nonumber) |
| type = HTT_text_nonumber; |
| else |
| type = HTT_text; |
| } |
| else |
| { |
| if (target_unit->unit_type == OU_special_unit) |
| command = target_unit->uc.special_unit_command; |
| else |
| command = target_unit->uc.unit_command; |
| if (type == HTT_href) |
| { |
| if (command) |
| return html_command_href (self, command, |
| filename_from, 0, 0); |
| else |
| return 0; |
| } |
| } |
| } |
| else |
| return 0; |
| |
| if (command) |
| return html_command_text (self, command, type); |
| |
| /* |
| We end up here if there is a target element, but not of the expected |
| 'type'. For example, if type is section but there is no section associated |
| to the target element node. |
| */ |
| return 0; |
| } |
| |
| |
| |
| const char * |
| 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, |
| special_unit_variety); |
| int i = number -1; |
| |
| return self->special_unit_info[type][i]; |
| } |
| |
| |
| |
| /* html_attribute_class */ |
| |
| static void |
| add_new_css_page (PAGES_CSS_LIST *css_pages, const char *page_name) |
| { |
| CSS_LIST *page_css_list; |
| if (css_pages->space <= css_pages->number) |
| { |
| css_pages->list = realloc (css_pages->list, |
| (css_pages->space += 10) * sizeof (CSS_LIST)); |
| } |
| |
| page_css_list = &css_pages->list[css_pages->number]; |
| memset (page_css_list, 0, sizeof (CSS_LIST)); |
| page_css_list->page_name = strdup (page_name); |
| |
| css_pages->number++; |
| } |
| |
| static void |
| collect_css_element_class (CONVERTER *self, const char *selector) |
| { |
| const CSS_SELECTOR_STYLE *selector_style |
| = find_css_selector_style (&self->css_element_class_styles, selector); |
| if (selector_style) |
| { |
| size_t i; |
| size_t css_files_index; |
| CSS_LIST *page_css_list; |
| if (self->document_global_context_counter) |
| { |
| css_files_index = 0; |
| } |
| else |
| { |
| css_files_index = self->current_filename.file_number; |
| /* files not associated to output units. Only try the |
| last one, as the files should be processed sequentially */ |
| if (css_files_index == 0) |
| { |
| if (self->current_filename.filename) |
| { |
| if (self->page_css.number > 1) |
| { |
| CSS_LIST *last_css_page |
| = &self->page_css.list[self->page_css.number -1]; |
| if (last_css_page->page_name |
| && !strcmp (self->current_filename.filename, |
| last_css_page->page_name)) |
| { |
| css_files_index = self->page_css.number -1; |
| } |
| } |
| if (css_files_index == 0) |
| { |
| add_new_css_page (&self->page_css, |
| self->current_filename.filename); |
| css_files_index = self->page_css.number -1; |
| } |
| } |
| } |
| if (css_files_index == 0) |
| { |
| fprintf (stderr, "BUG: %s: CSS no current file\n", selector); |
| return; |
| } |
| } |
| page_css_list = &self->page_css.list[css_files_index]; |
| for (i = 0; i < page_css_list->number; i++) |
| { |
| if (!strcmp (page_css_list->list[i], selector)) |
| return; |
| } |
| if (page_css_list->number == page_css_list->space) |
| { |
| page_css_list->list |
| = realloc (page_css_list->list, |
| (page_css_list->space += 5) * sizeof (char *)); |
| } |
| page_css_list->list[page_css_list->number] = strdup (selector); |
| page_css_list->number++; |
| } |
| } |
| |
| static char * |
| protect_class_name (const char *class_name) |
| { |
| TEXT result; |
| TEXT space_protected; |
| text_init (&result); |
| text_init (&space_protected); |
| const char *p = class_name; |
| while (*p) |
| { |
| int n = strcspn (p, " "); |
| if (n) |
| { |
| text_append_n (&space_protected, p, n); |
| p += n; |
| } |
| if (*p) |
| { |
| int n = strspn (p, " "); |
| if (n) |
| { |
| int i; |
| for (i = 0; i < n; i++) |
| text_append_n (&space_protected, "-", 1); |
| p += n; |
| } |
| } |
| } |
| |
| /* do not use the customization API as in perl */ |
| html_default_format_protect_text (space_protected.text, &result); |
| free (space_protected.text); |
| return result.text; |
| } |
| |
| char * |
| html_attribute_class (CONVERTER *self, const char *element, |
| const STRING_LIST *classes) |
| { |
| TEXT result; |
| char *style = 0; |
| size_t i; |
| int class_nr = 0; |
| if (!classes || classes->number <= 0 |
| || self->conf->NO_CSS.o.integer > 0) |
| { |
| if (!strcmp (element, "span")) |
| return strdup (""); |
| else |
| { |
| char *result; |
| xasprintf (&result, "<%s", element); |
| return result; |
| } |
| } |
| |
| if (self->conf->INLINE_CSS_STYLE.o.integer > 0) |
| { |
| size_t i; |
| TEXT inline_styles; |
| text_init (&inline_styles); |
| int style_nr = 0; |
| for (i = 0; i < classes->number; i++) |
| { |
| const char *style_class = classes->list[i]; |
| char *selector; |
| const CSS_SELECTOR_STYLE *selector_style; |
| |
| xasprintf (&selector, "%s.%s", element, style_class); |
| selector_style |
| = find_css_selector_style (&self->css_element_class_styles, |
| selector); |
| free (selector); |
| if (selector_style && selector_style->style) |
| { |
| if (style_nr) |
| text_printf (&inline_styles, ";%s", selector_style->style); |
| else |
| text_append (&inline_styles, selector_style->style); |
| style_nr++; |
| } |
| } |
| if (inline_styles.end) |
| { |
| xasprintf (&style, " style=\"%s\"", inline_styles.text); |
| } |
| free (inline_styles.text); |
| } |
| else |
| { |
| size_t i; |
| for (i = 0; i < classes->number; i++) |
| { |
| const char *style_class = classes->list[i]; |
| char *selector; |
| |
| xasprintf (&selector, "%s.%s", element, style_class); |
| collect_css_element_class (self, selector); |
| free (selector); |
| } |
| } |
| text_init (&result); |
| text_printf (&result, "<%s class=\"", element); |
| for (i = 0; i < classes->number; i++) |
| { |
| const char *class_name = classes->list[i]; |
| char *protected_class = protect_class_name (class_name); |
| if (class_nr) |
| text_printf (&result, " %s", protected_class); |
| else |
| text_append (&result, protected_class); |
| free (protected_class); |
| class_nr++; |
| } |
| text_append_n (&result, "\"", 1); |
| if (style) |
| { |
| text_append (&result, style); |
| free (style); |
| } |
| return result.text; |
| } |
| |
| |
| |
| /* formatting functions */ |
| |
| char * |
| html_default_format_comment (CONVERTER *self, const char *text) |
| { |
| char *with_space; |
| char *result; |
| |
| xasprintf (&with_space, " %s", text); |
| result = xml_comment (self, with_space); |
| free (with_space); |
| return result; |
| } |
| |
| char * |
| html_format_comment (CONVERTER *self, const char *text) |
| { |
| FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_comment]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| return html_default_format_comment (self, text); |
| } |
| else |
| { |
| return call_formatting_function_format_comment (self, |
| formatting_reference, |
| text); |
| } |
| } |
| |
| void |
| html_default_format_separate_anchor (CONVERTER *self, const char *id, |
| const char *class, TEXT *result) |
| { |
| char *attribute_class; |
| |
| STRING_LIST *classes = new_string_list (); |
| add_string (class, classes); |
| |
| /* html_attribute_class would not work with span, so if span is |
| used, html_attribute_class should not be used */ |
| attribute_class = html_attribute_class (self, "a", classes); |
| text_append (result, attribute_class); |
| text_printf (result, " id=\"%s\"></a>", id); |
| free (attribute_class); |
| destroy_strings_list (classes); |
| } |
| |
| void |
| format_separate_anchor (CONVERTER *self, const char *id, |
| const char *class, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_separate_anchor]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_separate_anchor (self, id, class, result); |
| } |
| else |
| { |
| char *separate_anchor |
| = call_formatting_function_format_separate_anchor (self, |
| formatting_reference, id, class); |
| text_append (result, separate_anchor); |
| free (separate_anchor); |
| } |
| } |
| |
| const char * |
| direction_string (CONVERTER *self, int direction, |
| enum direction_string_type string_type, |
| enum direction_string_context context) |
| { |
| int direction_unit_direction_idx = direction; |
| |
| /* Perl direction not found in C */ |
| if (direction < 0) |
| return 0; |
| |
| /* To debug: |
| fprintf (stderr, "DS: %d %s %s %s\n", direction, |
| direction_string_type_names[string_type], |
| direction_string_context_names[context], |
| self->main_units_direction_names[direction]); |
| */ |
| if (direction >= FIRSTINFILE_MIN_IDX && direction <= FIRSTINFILE_MAX_IDX) |
| { |
| /* in general the offset is negative */ |
| direction += FIRSTINFILE_OFFSET; |
| direction_unit_direction_idx = direction; |
| } |
| else if (direction > NON_SPECIAL_DIRECTIONS_NR - 1) |
| direction -= FIRSTINFILE_NR; |
| |
| if (!self->directions_strings[string_type][direction][context] |
| && string_type < TDS_TRANSLATED_MAX_NR) |
| { |
| HTML_DIRECTION_STRING_TRANSLATED *dir_translated |
| = &self->translated_direction_strings[string_type][direction]; |
| if (dir_translated->to_convert) |
| { |
| char *result_string; |
| TEXT translation_context; |
| char *context_str; |
| ELEMENT *translated_tree; |
| unsigned long context_type = 0; |
| const char *direction_name; |
| |
| text_init (&translation_context); |
| direction_name |
| = self->main_units_direction_names[direction_unit_direction_idx]; |
| text_append (&translation_context, direction_name); |
| |
| if (direction == RUD_type_This) |
| text_append_n (&translation_context, " (current section)", 18); |
| text_append_n (&translation_context, " direction ", 11); |
| text_append (&translation_context, |
| direction_type_translation_context[string_type]); |
| translated_tree = html_pcdt_tree (translation_context.text, |
| dir_translated->to_convert, |
| self, 0); |
| free (translation_context.text); |
| |
| xasprintf (&context_str, "DIRECTION %s (%s/%s)", direction_name, |
| direction_string_type_names[string_type], |
| direction_string_context_names[context]); |
| |
| if (context == TDS_context_string) |
| context_type |= CTXF_string; |
| |
| add_tree_to_build (self, translated_tree); |
| result_string |
| = html_convert_tree_new_formatting_context (self, translated_tree, |
| context_str, context_type, |
| 0, context_str, 0); |
| |
| remove_tree_to_build (self, translated_tree); |
| free (context_str); |
| |
| destroy_element_and_children (translated_tree); |
| self->directions_strings[string_type][direction][context] |
| = result_string; |
| } |
| else |
| { |
| const char *context_converted_string = 0; |
| if (dir_translated->converted[context]) |
| context_converted_string = dir_translated->converted[context]; |
| else if (context == TDS_context_string) |
| context_converted_string |
| = dir_translated->converted[TDS_context_normal]; |
| if (context_converted_string) |
| { |
| char *translated_string |
| = html_cdt_string (context_converted_string, self, 0, 0); |
| char *result_string |
| = html_substitute_non_breaking_space (self, translated_string); |
| self->directions_strings[string_type][direction][context] |
| = result_string; |
| free (translated_string); |
| } |
| } |
| } |
| return self->directions_strings[string_type][direction][context]; |
| } |
| |
| static void |
| direction_href_attributes (CONVERTER *self, int direction, TEXT *result) |
| { |
| if (self->conf->USE_ACCESSKEY.o.integer > 0) |
| { |
| const char *accesskey |
| = direction_string (self, direction, TDS_type_accesskey, |
| TDS_context_string); |
| if (accesskey && strlen (accesskey)) |
| text_printf (result, " accesskey=\"%s\"", accesskey); |
| } |
| |
| const char *button_rel |
| = direction_string (self, direction, TDS_type_rel, |
| TDS_context_string); |
| if (button_rel && strlen (button_rel)) |
| text_printf (result, " rel=\"%s\"", button_rel); |
| } |
| |
| static char * |
| direction_a (CONVERTER *self, int direction, const char *href, |
| const char *text, int omit_rel) |
| { |
| TEXT result; |
| text_init (&result); |
| text_printf (&result, "<a href=\"%s\"", href); |
| if (!omit_rel) |
| direction_href_attributes (self, direction, &result); |
| text_append_n (&result, ">", 1); |
| text_append (&result, text); |
| text_append_n (&result, "</a>", 4); |
| return result.text; |
| } |
| |
| static char *copiable_link_array[] = {"copiable-link"}; |
| static const STRING_LIST copiable_link_classes = {copiable_link_array, 1, 1}; |
| |
| static char * |
| get_copiable_anchor (CONVERTER *self, const char *id) |
| { |
| if (id && strlen (id) && self->conf->COPIABLE_LINKS.o.integer > 0) |
| { |
| TEXT result; |
| char *attribute_class = html_attribute_class (self, "a", |
| &copiable_link_classes); |
| text_init (&result); |
| text_append (&result, attribute_class); |
| free (attribute_class); |
| text_printf (&result, " href=\"#%s\"> %s</a>", |
| id, self->special_character[SC_paragraph_symbol].string); |
| return result.text; |
| } |
| return 0; |
| } |
| |
| void |
| html_default_format_heading_text (CONVERTER *self, const enum command_id cmd, |
| const STRING_LIST *classes, const char *text, |
| int level, const char *id, const ELEMENT *element, |
| const char *target, TEXT *result) |
| { |
| int heading_level = level; |
| char *heading_html_element; |
| const char *heading_target; |
| char *copiable_anchor; |
| |
| if (!id && text[strspn (text, whitespace_chars)] == '\0') |
| return; |
| |
| /* This happens with titlefont in title for instance */ |
| if (html_in_string (self)) |
| { |
| text_append (result, text); |
| if (cmd != CM_titlefont) |
| text_append_n (result, "\n", 1); |
| return; |
| } |
| |
| if (level < 1) |
| heading_level = 1; |
| else if (level > self->conf->MAX_HEADER_LEVEL.o.integer) |
| heading_level = self->conf->MAX_HEADER_LEVEL.o.integer; |
| xasprintf (&heading_html_element, "h%d", heading_level); |
| |
| char *attribute_class |
| = html_attribute_class (self, heading_html_element, classes); |
| text_append (result, attribute_class); |
| free (heading_html_element); |
| free (attribute_class); |
| |
| if (id) |
| { |
| text_printf (result, " id=\"%s\"", id); |
| /* The ID of this heading is likely the point the user would prefer being |
| linked to over the $target, since that's where they would be seeing a |
| copiable anchor. */ |
| heading_target = id; |
| } |
| else |
| { |
| heading_target = target; |
| } |
| |
| text_append_n (result, ">", 1); |
| |
| copiable_anchor = get_copiable_anchor (self, heading_target); |
| |
| if (copiable_anchor) |
| text_append_n (result, "<span>", 6); |
| |
| text_append (result, text); |
| |
| if (copiable_anchor) |
| { |
| text_append (result, copiable_anchor); |
| free (copiable_anchor); |
| text_append_n (result, "</span>", 7); |
| } |
| |
| text_printf (result, "</h%d>", heading_level); |
| if (cmd != CM_titlefont) |
| text_append_n (result, "\n", 1); |
| if (cmd == CM_part && self->conf->DEFAULT_RULE.o.string |
| && strlen (self->conf->DEFAULT_RULE.o.string)) |
| { |
| text_append (result, self->conf->DEFAULT_RULE.o.string); |
| text_append_n (result, "\n", 1); |
| } |
| } |
| |
| static char *toc_numbered_mark_array[] = {"toc-numbered-mark"}; |
| static const STRING_LIST toc_numbered_mark_classes |
| = {toc_numbered_mark_array, 1, 1}; |
| |
| char * |
| html_default_format_contents (CONVERTER *self, const enum command_id cmd, |
| const ELEMENT *element, const char *source_filename) |
| { |
| const char *filename_from; |
| int is_contents = (cmd == CM_contents); |
| TEXT result; |
| const SECTION_RELATIONS_LIST *root_children; |
| int min_root_level; |
| int max_root_level; |
| int status; |
| int has_toplevel_contents = 0; |
| size_t i; |
| int link_to_toc = 0; |
| const STRING_LIST *toc_ul_classes = 0; |
| |
| if (source_filename) |
| filename_from = source_filename; |
| else |
| filename_from = self->current_filename.filename; |
| |
| text_init (&result); |
| text_append (&result, ""); |
| |
| if (self->document->sections_list.number == 0 |
| || !self->document->sectioning_root) |
| { |
| /* this should not happen with $sections_list as set from Structuring |
| sectioning_structure, but could happen with another source. |
| We consider that if sectioning_root is set as usual, all the |
| fields are set consistently with what sectioning_structure would |
| have set. */ |
| return result.text; |
| } |
| |
| root_children = &self->document->sectioning_root->section_children; |
| min_root_level = lookup_extra_integer (root_children->list[0]->element, |
| AI_key_section_level, |
| &status); |
| max_root_level = min_root_level; |
| |
| for (i = 0; i < root_children->number; i++) |
| { |
| const ELEMENT *top_section = root_children->list[i]->element; |
| int section_level |
| = lookup_extra_integer (top_section, AI_key_section_level, &status); |
| if (section_level < min_root_level) |
| min_root_level = section_level; |
| if (section_level > max_root_level) |
| max_root_level = section_level; |
| } |
| /* chapter level elements are considered top-level here. */ |
| if (max_root_level < 1) |
| max_root_level = 1; |
| /* |
| fprintf (stderr, "ROOT_LEVEL Max: %d, Min: %d\n", max_root_level, |
| min_root_level); |
| */ |
| |
| if ((is_contents && !self->conf->BEFORE_TOC_LINES.o.string) |
| || (!is_contents && !self->conf->BEFORE_SHORT_TOC_LINES.o.string)) |
| { |
| 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); |
| free (attribute_class); |
| destroy_strings_list (classes); |
| text_append_n (&result, ">\n", 2); |
| } |
| else if (is_contents) |
| text_append (&result, self->conf->BEFORE_TOC_LINES.o.string); |
| else |
| text_append (&result, self->conf->BEFORE_SHORT_TOC_LINES.o.string); |
| |
| if (self->conf->NUMBER_SECTIONS.o.integer > 0) |
| toc_ul_classes = &toc_numbered_mark_classes; |
| if (root_children->number > 1) |
| { |
| char *attribute_class; |
| attribute_class = html_attribute_class (self, "ul", toc_ul_classes); |
| text_append (&result, attribute_class); |
| free (attribute_class); |
| text_append_n (&result, ">\n", 2); |
| has_toplevel_contents = 1; |
| } |
| |
| link_to_toc = (!is_contents && self->conf->SHORT_TOC_LINK_TO_TOC.o.integer > 0 |
| && self->conf->contents.o.integer > 0 |
| && (!self->conf->CONTENTS_OUTPUT_LOCATION.o.string |
| || strcmp (self->conf->CONTENTS_OUTPUT_LOCATION.o.string, |
| "inline") |
| || self->document->global_commands.contents.number > 0 |
| || self->document->global_commands.shortcontents.number > 0)); |
| |
| for (i = 0; i < root_children->number; i++) |
| { |
| const SECTION_RELATIONS *top_relations = root_children->list[i]; |
| const SECTION_RELATIONS *section_relations = top_relations; |
| while (section_relations) |
| { |
| const ELEMENT *section = section_relations->element; |
| int section_level = lookup_extra_integer (section, AI_key_section_level, |
| &status); |
| const SECTION_RELATIONS_LIST *section_children |
| = section_relations->section_children; |
| if (section->e.c->cmd != CM_top) |
| { |
| char *text; |
| char *href; |
| const char *toc_id = html_command_contents_target (self, section, cmd); |
| |
| text = html_command_text (self, section, 0); |
| |
| if (link_to_toc) |
| href = html_command_contents_href (self, section, CM_contents, |
| filename_from); |
| else |
| href = html_command_href (self, section, filename_from, 0, 0); |
| if (text && strlen (text)) |
| { |
| /* no indenting for shortcontents */ |
| if (is_contents) |
| { |
| int i; |
| for (i = 0; i < 2 * (section_level - min_root_level); i++) |
| text_append_n (&result, " ", 1); |
| } |
| text_append_n (&result, "<li>", 4); |
| if ((toc_id && strlen (toc_id)) || href) |
| { |
| text_append_n (&result, "<a", 2); |
| if (toc_id && strlen (toc_id)) |
| text_printf (&result, " id=\"%s\"", toc_id); |
| if (href) |
| text_printf (&result, " href=\"%s\"", href); |
| if (section_relations->associated_node) |
| { |
| int is_index |
| = (section_relations->associated_node->element->flags & EF_isindex); |
| if (is_index) |
| text_append_n (&result, " rel=\"index\"", 12); |
| } |
| text_append_n (&result, ">", 1); |
| text_append (&result, text); |
| text_append_n (&result, "</a>", 4); |
| } |
| else |
| { |
| text_append (&result, text); |
| } |
| } |
| free (text); |
| free (href); |
| } |
| else |
| { |
| if (section_children && section_children->number > 0 |
| && has_toplevel_contents) |
| text_append_n (&result, "<li>", 4); |
| } |
| if (section_children |
| && (is_contents || section_level < max_root_level)) |
| { |
| char *attribute_class; |
| /* no indenting for shortcontents */ |
| if (is_contents) |
| { |
| int i; |
| text_append_n (&result, "\n", 1); |
| for (i = 0; i < 2 * (section_level - min_root_level); i++) |
| text_append_n (&result, " ", 1); |
| } |
| attribute_class |
| = html_attribute_class (self, "ul", toc_ul_classes); |
| text_append (&result, attribute_class); |
| free (attribute_class); |
| text_append_n (&result, ">\n", 2); |
| section_relations = section_children->list[0]; |
| } |
| else |
| { |
| if (section_relations->section_directions |
| && section_relations->section_directions[D_next] |
| && section->e.c->cmd != CM_top) |
| { |
| text_append_n (&result, "</li>\n", 6); |
| if (section_relations == top_relations) |
| break; |
| section_relations |
| = section_relations->section_directions[D_next]; |
| } |
| else |
| { |
| int is_top_section = 0; |
| if (section_relations == top_relations) |
| { |
| if (section->e.c->cmd != CM_top) |
| text_append_n (&result, "</li>\n", 6); |
| break; |
| } |
| while (1) |
| { |
| int section_level; |
| int i; |
| |
| if (!section_relations->section_directions |
| || !section_relations->section_directions[D_up]) |
| break; |
| |
| section_relations |
| = section_relations->section_directions[D_up]; |
| section = section_relations->element; |
| |
| section_level = lookup_extra_integer (section, |
| AI_key_section_level, &status); |
| text_append_n (&result, "</li>\n", 6); |
| |
| for (i = 0; i < 2 * (section_level - min_root_level); i++) |
| text_append_n (&result, " ", 1); |
| text_append_n (&result, "</ul>", 5); |
| if (section_relations == top_relations) |
| { |
| if (has_toplevel_contents) |
| text_append_n (&result, "</li>\n", 6); |
| is_top_section = 1; |
| break; |
| } |
| |
| if (section_relations->section_directions |
| && section_relations->section_directions[D_next]) |
| { |
| text_append_n (&result, "</li>\n", 6); |
| section_relations |
| = section_relations->section_directions[D_next]; |
| break; |
| } |
| } |
| if (is_top_section) |
| break; |
| } |
| } |
| } |
| } |
| |
| |
| if (root_children->number > 1) |
| text_append_n (&result, "\n</ul>", 6); |
| |
| if ((is_contents && !self->conf->AFTER_TOC_LINES.o.string) |
| || (!is_contents && !self->conf->AFTER_SHORT_TOC_LINES.o.string)) |
| text_append_n (&result, "\n</div>\n", 8); |
| else if (is_contents) |
| text_append (&result, self->conf->AFTER_TOC_LINES.o.string); |
| else |
| text_append (&result, self->conf->AFTER_SHORT_TOC_LINES.o.string); |
| |
| return result.text; |
| } |
| |
| char * |
| format_contents (CONVERTER *self, |
| const enum command_id cmd, const ELEMENT *element, |
| const char *filename) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_contents]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| return html_default_format_contents (self, cmd, element, filename); |
| } |
| else |
| { |
| return call_formatting_function_format_contents (self, |
| formatting_reference, |
| builtin_command_name (cmd), element, filename); |
| } |
| } |
| |
| void |
| format_heading_text (CONVERTER *self, const enum command_id cmd, |
| const STRING_LIST *classes, const char *text, |
| int level, const char *id, const ELEMENT *element, |
| const char *target, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_heading_text]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_heading_text (self, cmd, classes, text, |
| level, id, element, target, result); |
| } |
| else |
| { |
| char *heading_text |
| = call_formatting_function_format_heading_text (self, |
| formatting_reference, |
| builtin_command_name (cmd), |
| classes, text, |
| level, id, element, target); |
| text_append (result, heading_text); |
| free (heading_text); |
| } |
| } |
| |
| static char *foot_body_heading_array[] = {"footnote-body-heading"}; |
| static const STRING_LIST foot_body_heading_classes |
| = {foot_body_heading_array, 1, 1}; |
| |
| void |
| html_default_format_single_footnote (CONVERTER *self, const ELEMENT *element, |
| const char *footid, int number_in_doc, |
| const char *footnote_location_href, const char *mark, |
| TEXT *result) |
| { |
| char *attribute_class; |
| size_t footnote_text_len; |
| char *footnote_text; |
| char *context_str; |
| char *footnote_text_with_eol; |
| |
| xasprintf (&context_str, "%s %d %s", element_command_name (element), |
| number_in_doc, footid); |
| footnote_text |
| = html_convert_tree_new_formatting_context (self, |
| element->e.c->contents.list[0], |
| context_str, 0, 0, 0, 0); |
| free (context_str); |
| |
| footnote_text_len = strlen (footnote_text); |
| if (footnote_text_len <= 0 |
| || footnote_text[footnote_text_len -1] != '\n') |
| { |
| xasprintf (&footnote_text_with_eol, "%s\n", footnote_text); |
| free (footnote_text); |
| } |
| else |
| footnote_text_with_eol = footnote_text; |
| |
| attribute_class = html_attribute_class (self, "h5", |
| &foot_body_heading_classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| text_printf (result, "><a id=\"%s\" href=\"%s\">(%s)</a></h5>\n", |
| footid, footnote_location_href, mark); |
| |
| text_append (result, footnote_text_with_eol); |
| free (footnote_text_with_eol); |
| } |
| |
| void |
| format_single_footnote (CONVERTER *self, const ELEMENT *element, |
| const char *footid, int number_in_doc, |
| const char *footnote_location_href, const char *mark, |
| TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_single_footnote]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_single_footnote (self, element, footid, |
| number_in_doc, footnote_location_href, |
| mark, result); |
| } |
| else |
| { |
| char *footnote |
| = call_formatting_function_format_single_footnote (self, |
| formatting_reference, element, footid, |
| number_in_doc, footnote_location_href, |
| mark); |
| |
| text_append (result, footnote); |
| free (footnote); |
| } |
| } |
| |
| void |
| html_default_format_footnotes_sequence (CONVERTER *self, TEXT *result) |
| { |
| size_t i; |
| HTML_PENDING_FOOTNOTE_STACK *pending_footnotes |
| = &self->pending_footnotes; |
| |
| if (pending_footnotes->top > 0) |
| { |
| for (i = 0; i < pending_footnotes->top; i++) |
| { |
| const HTML_PENDING_FOOTNOTE *pending_footnote_info |
| = pending_footnotes->stack[i]; |
| const ELEMENT *command = pending_footnote_info->command; |
| const char *footid = pending_footnote_info->footid; |
| int number_in_doc = pending_footnote_info->number_in_doc; |
| char *footnote_mark; |
| char *footnote_location_href |
| = html_footnote_location_href (self, command, 0, |
| pending_footnote_info->docid, |
| pending_footnote_info->footnote_location_filename); |
| /* |
| NOTE the @-commands in @footnote that are formatted differently depending |
| on multi_expanded_region($self) cannot know that the original context |
| of the @footnote in the main document was $multi_expanded_region. |
| We do not want to set multi_expanded in customizable code. However, it |
| could be possible to set a shared_conversion_state based on $multi_expanded_region |
| and have all the conversion functions calling multi_expanded_region($self) |
| also check the shared_conversion_state. The special situations |
| with those @-commands in @footnote in multi expanded |
| region do not justify this additional code and complexity. The consequences |
| should only be redundant anchors HTML elements. |
| */ |
| if (self->conf->NUMBER_FOOTNOTES.o.integer > 0) |
| xasprintf (&footnote_mark, "%d", number_in_doc); |
| else if (self->conf->NO_NUMBER_FOOTNOTE_SYMBOL.o.string) |
| footnote_mark |
| = strdup (self->conf->NO_NUMBER_FOOTNOTE_SYMBOL.o.string); |
| else |
| footnote_mark = strdup (""); |
| |
| format_single_footnote (self, command, footid, number_in_doc, |
| footnote_location_href, footnote_mark, |
| result); |
| |
| free (footnote_mark); |
| free (footnote_location_href); |
| |
| html_free_pending_footnote (pending_footnotes->stack[i]); |
| } |
| pending_footnotes->top = 0; |
| } |
| } |
| |
| void |
| format_footnotes_sequence (CONVERTER *self, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_footnotes_sequence]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_footnotes_sequence (self, result); |
| } |
| else |
| { |
| char *footnotes_sequence |
| = call_formatting_function_format_footnotes_sequence (self, |
| formatting_reference); |
| text_append (result, footnotes_sequence); |
| free (footnotes_sequence); |
| } |
| } |
| |
| void |
| default_format_footnotes_segment (CONVERTER *self, TEXT *result) |
| { |
| const char *class_base; |
| char *attribute_class; |
| char *class; |
| STRING_LIST *classes; |
| char *footnote_heading; |
| int level; |
| TEXT foot_lines; |
| |
| text_init (&foot_lines); |
| format_footnotes_sequence (self, &foot_lines); |
| |
| if (foot_lines.end <= 0) |
| { |
| free (foot_lines.text); |
| return; |
| } |
| |
| classes = new_string_list (); |
| |
| class_base = html_special_unit_info (self, SUI_type_class, |
| "footnotes"); |
| xasprintf (&class, "%s-segment", class_base); |
| |
| add_string (class, classes); |
| free (class); |
| attribute_class = html_attribute_class (self, "div", classes); |
| clear_strings_list (classes); |
| |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| text_append_n (result, ">\n", 2); |
| |
| if (self->conf->DEFAULT_RULE.o.string |
| && strlen (self->conf->DEFAULT_RULE.o.string)) |
| { |
| text_append (result, self->conf->DEFAULT_RULE.o.string); |
| text_append_n (result, "\n", 1); |
| } |
| |
| footnote_heading = html_special_unit_info_text (self, SUIT_type_heading, |
| "footnotes", 0); |
| |
| level = self->conf->FOOTNOTE_END_HEADER_LEVEL.o.integer; |
| |
| xasprintf (&class, "%s-heading", class_base); |
| |
| add_string (class, classes); |
| free (class); |
| |
| format_heading_text (self, 0, classes, footnote_heading, level, 0, 0, 0, |
| result); |
| destroy_strings_list (classes); |
| text_append_n (result, "\n", 1); |
| |
| free (footnote_heading); |
| |
| text_append (result, foot_lines.text); |
| free (foot_lines.text); |
| text_append (result, "</div>\n"); |
| } |
| |
| void |
| format_footnotes_segment (CONVERTER *self, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_footnotes_segment]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| default_format_footnotes_segment (self, result); |
| } |
| else |
| { |
| char *footnotes_segment |
| = call_formatting_function_format_footnotes_segment (self, |
| formatting_reference); |
| if (footnotes_segment) |
| { |
| text_append (result, footnotes_segment); |
| free (footnotes_segment); |
| } |
| } |
| } |
| |
| void |
| html_default_format_program_string (CONVERTER *self, TEXT *result) |
| { |
| ELEMENT *tree; |
| const char *explanation; |
| if (self->conf->PROGRAM.o.string && strlen (self->conf->PROGRAM.o.string) |
| && self->conf->PACKAGE_URL.o.string) |
| { |
| ELEMENT *program_homepage = new_text_element (ET_normal_text); |
| ELEMENT *program = new_text_element (ET_normal_text); |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| |
| text_append (program_homepage->e.text, self->conf->PACKAGE_URL.o.string); |
| text_append (program->e.text, self->conf->PROGRAM.o.string); |
| |
| add_element_to_named_string_element_list (substrings, |
| "program_homepage", program_homepage); |
| add_element_to_named_string_element_list (substrings, |
| "program", program); |
| |
| tree = html_cdt_tree ("This document was generated on @emph{@today{}} " |
| "using @uref{{program_homepage}, @emph{{program}}}.", |
| self, substrings, 0); |
| destroy_named_string_element_list (substrings); |
| /* program and program_homepage are destroyed with the tree */ |
| explanation = "Tr program string program"; |
| } |
| else |
| { |
| tree = html_cdt_tree ("This document was generated on @emph{@today{}}.", |
| self, 0, 0); |
| explanation = "Tr program string date"; |
| } |
| add_tree_to_build (self, tree); |
| html_convert_tree_append (self, tree, result, explanation); |
| remove_tree_to_build (self, tree); |
| destroy_element_and_children (tree); |
| } |
| |
| void |
| format_program_string (CONVERTER *self, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_program_string]; |
| |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_program_string (self, result); |
| } |
| else |
| { |
| char *program_string |
| = call_formatting_function_format_program_string (self, |
| formatting_reference); |
| text_append (result, program_string); |
| free (program_string); |
| } |
| } |
| |
| static char *program_in_footer_array[] = {"program-in-footer"}; |
| static const STRING_LIST program_in_footer_classes |
| = {program_in_footer_array, 1, 1}; |
| |
| char * |
| html_default_format_end_file (CONVERTER *self, const char *filename, |
| const OUTPUT_UNIT *output_unit) |
| { |
| TEXT result; |
| |
| text_init (&result); |
| text_append (&result, ""); |
| |
| if (self->conf->PROGRAM_NAME_IN_FOOTER.o.integer > 0) |
| { |
| char *open; |
| size_t open_len; |
| text_append_n (&result, "<p>\n ", 6); |
| open = html_attribute_class (self, "span", &program_in_footer_classes); |
| open_len = strlen (open); |
| if (open_len > 0) |
| { |
| text_append (&result, open); |
| text_append_n (&result, ">", 1); |
| } |
| free (open); |
| |
| format_program_string (self, &result); |
| |
| if (open_len > 0) |
| text_append_n (&result, "</span>", 7); |
| text_append_n (&result, "\n</p>", 5); |
| } |
| text_append_n (&result, "\n\n", 2); |
| |
| if (self->conf->PRE_BODY_CLOSE.o.string) |
| text_append (&result, self->conf->PRE_BODY_CLOSE.o.string); |
| |
| if (self->jslicenses.number) |
| { |
| int infojs_jslicenses_file_nr = 0; |
| int mathjax_jslicenses_file_nr = 0; |
| size_t i; |
| int status; |
| for (i = 0; i < self->jslicenses.number; i++) |
| { |
| const JSLICENSE_FILE_INFO_LIST *jslicences_files_info |
| = &self->jslicenses.list[i]; |
| if (!strcmp (jslicences_files_info->category, "infojs")) |
| infojs_jslicenses_file_nr = jslicences_files_info->number; |
| else if (!strcmp (jslicences_files_info->category, "mathjax")) |
| mathjax_jslicenses_file_nr = jslicences_files_info->number; |
| } |
| if (infojs_jslicenses_file_nr > 0 |
| || ((html_get_file_information (self, "mathjax", |
| filename, &status) > 0 |
| || (!self->conf->SPLIT.o.string |
| || !strlen (self->conf->SPLIT.o.string))) |
| && mathjax_jslicenses_file_nr > 0)) |
| { |
| if (self->conf->JS_WEBLABELS_FILE.o.string |
| && self->conf->JS_WEBLABELS.o.string |
| && (!strcmp (self->conf->JS_WEBLABELS.o.string, "generate") |
| || !strcmp (self->conf->JS_WEBLABELS.o.string, "reference"))) |
| { |
| ELEMENT *tree; |
| char *js_path = url_protect_url_text (self, |
| self->conf->JS_WEBLABELS_FILE.o.string); |
| text_append_n (&result, "<a href=\"", 9); |
| text_append (&result, js_path); |
| free (js_path); |
| text_append_n (&result, "\" rel=\"jslicense\"><small>", 25); |
| |
| tree = html_cdt_tree ("JavaScript license information", |
| self, 0, 0); |
| add_tree_to_build (self, tree); |
| html_convert_tree_append (self, tree, &result, |
| "Tr JS license header"); |
| remove_tree_to_build (self, tree); |
| |
| destroy_element_and_children (tree); |
| |
| text_append_n (&result, "</small></a>", 12); |
| } |
| } |
| } |
| text_append_n (&result, "\n</body>\n</html>\n", 17); |
| return result.text; |
| } |
| |
| char * |
| html_format_end_file (CONVERTER *self, const char *filename, |
| const OUTPUT_UNIT *output_unit) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_end_file]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| return html_default_format_end_file (self, filename, output_unit); |
| } |
| else |
| { |
| return call_formatting_function_format_end_file (self, |
| formatting_reference, |
| filename, output_unit); |
| } |
| } |
| |
| typedef struct BEGIN_FILE_INFORMATION { |
| char *title; |
| char *description; |
| char *keywords; |
| char *encoding; |
| char *css_lines; |
| char *root_html_element_attributes; |
| char *body_attributes; |
| char *generator; |
| char *extra_head; |
| } BEGIN_FILE_INFORMATION; |
| |
| void |
| destroy_begin_file_information (BEGIN_FILE_INFORMATION *begin_info) |
| { |
| free (begin_info->title); |
| free (begin_info->description); |
| free (begin_info->encoding); |
| free (begin_info->keywords); |
| free (begin_info->css_lines); |
| free (begin_info->root_html_element_attributes); |
| free (begin_info->body_attributes); |
| free (begin_info->generator); |
| free (begin_info->extra_head); |
| |
| free (begin_info); |
| } |
| |
| void |
| html_default_format_css_lines (CONVERTER *self, const char *filename, |
| TEXT *result) |
| { |
| const STRING_LIST *css_refs; |
| const STRING_LIST *css_import_lines; |
| const STRING_LIST *css_rule_lines; |
| STRING_LIST *css_element_classes; |
| size_t i; |
| |
| if (self->conf->NO_CSS.o.integer > 0) |
| return; |
| |
| css_refs = self->conf->CSS_REFS.o.strlist; |
| css_element_classes = html_get_css_elements_classes (self, filename); |
| css_import_lines = html_css_get_info (self, CI_css_info_imports); |
| css_rule_lines = html_css_get_info (self, CI_css_info_rules); |
| |
| if (css_import_lines->number <= 0 |
| && (!css_element_classes || css_element_classes->number <= 0) |
| && css_rule_lines->number <= 0 |
| && (!css_refs || css_refs->number <= 0)) |
| { |
| if (css_element_classes) |
| destroy_strings_list (css_element_classes); |
| return; |
| } |
| |
| text_append (result, "<style type=\"text/css\">\n"); |
| |
| if (css_import_lines->number > 0) |
| { |
| for (i = 0; i < css_import_lines->number; i++) |
| text_append (result, css_import_lines->list[i]); |
| text_append_n (result, "\n", 1); |
| } |
| |
| if (css_element_classes) |
| { |
| if (css_element_classes->number > 0) |
| { |
| for (i = 0; i < css_element_classes->number; i++) |
| { |
| const char *selector = css_element_classes->list[i]; |
| const CSS_SELECTOR_STYLE *selector_style |
| = find_css_selector_style (&self->css_element_class_styles, |
| selector); |
| if (selector_style->style) |
| text_printf (result, "%s {%s}\n", selector, |
| selector_style->style); |
| } |
| } |
| destroy_strings_list (css_element_classes); |
| } |
| |
| if (css_rule_lines->number > 0) |
| { |
| for (i = 0; i < css_rule_lines->number; i++) |
| text_append (result, css_rule_lines->list[i]); |
| text_append_n (result, "\n", 1); |
| } |
| |
| text_append (result, "</style>\n"); |
| |
| if (css_refs) |
| { |
| for (i = 0; i < css_refs->number; i++) |
| { |
| char *protected_ref = url_protect_url_text (self, |
| css_refs->list[i]); |
| text_printf (result, |
| "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\"", |
| protected_ref); |
| free (protected_ref); |
| |
| html_close_lone_element (self, result); |
| text_append_n (result, "\n", 1); |
| } |
| } |
| } |
| |
| void |
| format_css_lines (CONVERTER *self, const char *filename, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_css_lines]; |
| |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_css_lines (self, filename, result); |
| } |
| else |
| { |
| char *css_lines |
| = call_formatting_function_format_css_lines (self, |
| formatting_reference, filename); |
| text_append (result, css_lines); |
| free (css_lines); |
| } |
| } |
| |
| /* return string to be freed by the caller */ |
| static |
| char *root_html_element_attributes_string (CONVERTER *self) |
| { |
| if (self->conf->HTML_ROOT_ELEMENT_ATTRIBUTES.o.string |
| && strlen (self->conf->HTML_ROOT_ELEMENT_ATTRIBUTES.o.string)) |
| { |
| char *result; |
| xasprintf (&result, " %s", |
| self->conf->HTML_ROOT_ELEMENT_ATTRIBUTES.o.string); |
| return result; |
| } |
| return 0; |
| } |
| |
| /* This is used for normal output files and other files, like |
| redirection file headers. $COMMAND is the tree element for |
| a @node that is being output in the file. |
| |
| Returned structure to be freed by the caller. |
| */ |
| static BEGIN_FILE_INFORMATION * |
| file_header_information (CONVERTER *self, const ELEMENT *command, |
| const char *filename) |
| { |
| BEGIN_FILE_INFORMATION *begin_info = (BEGIN_FILE_INFORMATION *) |
| malloc (sizeof (BEGIN_FILE_INFORMATION)); |
| const char *description = self->documentdescription_string; |
| char *command_description = 0; |
| int status; |
| TEXT text; |
| char *root_html_element_attributes |
| = root_html_element_attributes_string (self); |
| |
| text_init (&text); |
| |
| memset (begin_info, 0, sizeof (BEGIN_FILE_INFORMATION)); |
| |
| if (command) |
| { |
| char *command_string = 0; |
| if (self->conf->SECTION_NAME_IN_TITLE.o.integer > 0 |
| && command->e.c->cmd == CM_node && self->document) |
| { |
| int status; |
| size_t node_number |
| = lookup_extra_integer (command, AI_key_node_number, &status); |
| const NODE_RELATIONS *node_relations |
| = self->document->nodes_list.list[node_number -1]; |
| const ELEMENT *associated_title_command = associated_title_command |
| = node_relations->associated_title_command; |
| |
| if (associated_title_command) |
| { |
| command_string = html_command_text (self, |
| associated_title_command, HTT_string); |
| } |
| } |
| if (!command_string || !strlen (command_string)) |
| { |
| /* happens for @node and special_unit_element. |
| Also for @anchor and @namedanchor for redirection files. |
| */ |
| command_string = html_command_text (self, command, HTT_string); |
| } |
| |
| |
| if (command_string && strlen (command_string) |
| && strcmp (command_string, self->title_string)) |
| { |
| char *context_str; |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| ELEMENT *title_tree; |
| ELEMENT *element_text = new_text_element (ET__converted); |
| ELEMENT *title_element = new_text_element (ET__converted); |
| |
| text_append (element_text->e.text, command_string); |
| text_append (title_element->e.text, self->title_string); |
| |
| add_element_to_named_string_element_list (substrings, "title", |
| title_element); |
| add_element_to_named_string_element_list (substrings, "element_text", |
| element_text); |
| |
| /* TRANSLATORS: sectioning element title for the page header */ |
| title_tree |
| = html_cdt_tree ("{element_text} ({title})", |
| self, substrings, 0); |
| |
| destroy_named_string_element_list (substrings); |
| |
| add_tree_to_build (self, title_tree); |
| |
| if (command->e.c->cmd) |
| xasprintf (&context_str, "file_header_title-element-@%s", |
| element_command_name (command)); |
| else if (command->type) |
| xasprintf (&context_str, "file_header_title-element-%s", |
| type_data[command->type].name); |
| else |
| context_str = strdup ("file_header_title-element-"); |
| |
| begin_info->title |
| = html_convert_tree_new_formatting_context (self, |
| title_tree, context_str, CTXF_string, |
| "element_title", 0, 0); |
| |
| free (context_str); |
| remove_tree_to_build (self, title_tree); |
| destroy_element_and_children (title_tree); |
| } |
| free (command_string); |
| |
| command_description = html_command_description (self, command, |
| HTT_string); |
| } |
| if (!begin_info->title) |
| begin_info->title = strdup (self->title_string); |
| |
| if (command_description && strlen (command_description)) |
| begin_info->keywords = strdup (command_description); |
| else if (begin_info->title) |
| begin_info->keywords = strdup (begin_info->title); |
| |
| if (!description || !strlen (description)) |
| { |
| if (command_description && strlen (command_description)) |
| description = command_description; |
| else |
| description = begin_info->title; |
| } |
| |
| if (description && strlen (description)) |
| { |
| text_printf (&text, |
| "<meta name=\"description\" content=\"%s\"", description); |
| html_close_lone_element (self, &text); |
| begin_info->description = strdup (text.text); |
| } |
| |
| free (command_description); |
| |
| text_reset (&text); |
| if (self->conf->OUTPUT_ENCODING_NAME.o.string |
| && strlen (self->conf->OUTPUT_ENCODING_NAME.o.string)) |
| { |
| text_printf (&text, |
| "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"", |
| self->conf->OUTPUT_ENCODING_NAME.o.string); |
| html_close_lone_element (self, &text); |
| begin_info->encoding = strdup (text.text); |
| } |
| |
| text_reset (&text); |
| text_append (&text, ""); |
| format_css_lines (self, filename, &text); |
| begin_info->css_lines = strdup (text.text); |
| |
| if (root_html_element_attributes) |
| { |
| begin_info->root_html_element_attributes |
| = root_html_element_attributes; |
| } |
| else |
| begin_info->root_html_element_attributes = strdup (""); |
| |
| text_reset (&text); |
| if (self->conf->BODY_ELEMENT_ATTRIBUTES.o.string) |
| { |
| char *attributes = self->conf->BODY_ELEMENT_ATTRIBUTES.o.string; |
| if (attributes[0] != '\0' && attributes[0] != ' ') |
| text_append_n (&text, " ", 1); |
| text_append (&text, attributes); |
| } |
| if (self->conf->HTML_MATH.o.string |
| && !strcmp (self->conf->HTML_MATH.o.string, "mathjax") |
| && html_get_file_information (self, "mathjax", filename, &status) > 0) |
| { |
| text_append_n (&text, " class=\"tex2jax_ignore\"", 23); |
| } |
| |
| begin_info->body_attributes = strdup (text.text); |
| |
| text_reset (&text); |
| if (self->conf->PROGRAM.o.string && strlen (self->conf->PROGRAM.o.string)) |
| { |
| text_printf (&text, "<meta name=\"Generator\" content=\"%s\"", |
| self->conf->PROGRAM.o.string); |
| html_close_lone_element (self, &text); |
| text_append_n (&text, "\n", 1); |
| begin_info->generator = strdup (text.text); |
| text_reset (&text); |
| } |
| |
| if (self->conf->EXTRA_HEAD.o.string) |
| text_append (&text, self->conf->EXTRA_HEAD.o.string); |
| if (self->conf->INFO_JS_DIR.o.string) |
| { |
| if (!self->conf->SPLIT.o.string || !strlen (self->conf->SPLIT.o.string)) |
| { |
| message_list_document_error (&self->error_messages, self->conf, 0, |
| "%s not meaningful for non-split output", "INFO_JS_DIR"); |
| } |
| else |
| { |
| char *jsdir; |
| char *protected_jsdir; |
| if (!strcmp (self->conf->INFO_JS_DIR.o.string, ".")) |
| jsdir = strdup (""); |
| else |
| { |
| size_t len; |
| jsdir = strdup (self->conf->INFO_JS_DIR.o.string); |
| len = strlen (jsdir); |
| if (len > 0) |
| { |
| if (jsdir[len - 1] != '/') |
| { |
| char *tmp; |
| xasprintf (&tmp, "%s/", jsdir); |
| free (jsdir); |
| jsdir = tmp; |
| } |
| else if (len > 1 && jsdir[len - 2] == '/') |
| { |
| while (1) |
| { |
| len--; |
| if (len <= 1 || jsdir[len - 2] != '/') |
| break; |
| } |
| jsdir[len] = '\0'; |
| } |
| } |
| } |
| protected_jsdir = url_protect_url_text (self, jsdir); |
| free (jsdir); |
| text_printf (&text, "<link rel=\"stylesheet\" type=\"text/css\" " |
| "href=\"%sinfo.css\"", protected_jsdir); |
| html_close_lone_element (self, &text); |
| text_printf (&text, "\n<script src=\"%smodernizr.js\" " |
| "type=\"text/javascript\"></script>\n" |
| "<script src=\"%sinfo.js\" type=\"text/javascript\"></script>", |
| protected_jsdir, protected_jsdir); |
| free (protected_jsdir); |
| } |
| } |
| if (self->conf->HTML_MATH.o.string |
| && !strcmp (self->conf->HTML_MATH.o.string, "mathjax") |
| && (html_get_file_information (self, "mathjax", filename, &status) > 0)) |
| { |
| char *mathjax_script = url_protect_url_text (self, |
| self->conf->MATHJAX_SCRIPT.o.string); |
| const char *default_mathjax_configuration = |
| " options: {\n" |
| " skipHtmlTags: {'[-]': ['pre']}, // do not skip pre\n" |
| " ignoreHtmlClass: 'tex2jax_ignore',\n" |
| " processHtmlClass: 'tex2jax_process'\n" |
| " },\n" |
| " tex: {\n" |
| " processEscapes: false, // do not use \\$ to produce a literal dollar sign\n" |
| " processEnvironments: false, // do not process \\begin{xxx}...\\end{xxx} outside math mode\n" |
| " processRefs: false, // do not process \\ref{...} outside of math mode\n" |
| " displayMath: [ // start/end delimiter pairs for display math\n" |
| " ['\\\\[', '\\\\]']\n" |
| " ],\n" |
| " },"; |
| |
| text_printf (&text, "<script type='text/javascript'>\n" |
| "MathJax = {\n" |
| "%s\n" |
| "};\n", default_mathjax_configuration); |
| |
| if (self->conf->MATHJAX_CONFIGURATION.o.string) |
| { |
| text_printf (&text, |
| "var MathJax_conf = {\n" |
| "%s\n" |
| "};\n" |
| "\n" |
| "for (let component in MathJax_conf) {\n" |
| " if (!MathJax.hasOwnProperty(component)) {\n" |
| " MathJax[component] = MathJax_conf[component];\n" |
| " } else {\n" |
| " for (let field in MathJax_conf[component]) {\n" |
| " MathJax[component][field] = MathJax_conf[component][field];\n" |
| " }\n" |
| " }\n" |
| "}\n", self->conf->MATHJAX_CONFIGURATION.o.string); |
| } |
| |
| text_printf (&text, |
| "</script><script type=\"text/javascript\" id=\"MathJax-script\" async\n" |
| " src=\"%s\">\n" |
| "</script>", mathjax_script); |
| free (mathjax_script); |
| } |
| begin_info->extra_head = text.text; |
| return begin_info; |
| } |
| |
| static void |
| get_links (CONVERTER* self, const char *filename, |
| const OUTPUT_UNIT *output_unit, |
| const ELEMENT *node_command, TEXT *result) |
| { |
| if (self->conf->USE_LINKS.o.integer > 0 |
| && self->conf->LINKS_DIRECTIONS.o.buttons) |
| { |
| size_t i; |
| const BUTTON_SPECIFICATION_LIST *link_directions |
| = self->conf->LINKS_DIRECTIONS.o.buttons; |
| for (i = 0; i < link_directions->number; i++) |
| { |
| const BUTTON_SPECIFICATION *link = &link_directions->list[i]; |
| char *link_href; |
| if (link->type != BST_direction) |
| fatal ("LINKS_DIRECTIONS should only contain directions"); |
| link_href = from_element_direction (self, link->b.direction, |
| HTT_href, output_unit, |
| filename, node_command); |
| if (link_href) |
| { |
| char *link_string |
| = from_element_direction (self, link->b.direction, HTT_string, |
| output_unit, 0, 0); |
| const char *button_rel |
| = direction_string (self, link->b.direction, TDS_type_rel, |
| TDS_context_string); |
| text_printf (result, "<link href=\"%s\"", link_href); |
| if (button_rel) |
| { |
| text_printf (result, " rel=\"%s\"", button_rel); |
| } |
| if (link_string) |
| { |
| text_printf (result, " title=\"%s\"", link_string); |
| free (link_string); |
| } |
| html_close_lone_element (self, result); |
| text_append_n (result, "\n", 1); |
| } |
| free (link_href); |
| } |
| } |
| } |
| |
| char * |
| html_default_format_begin_file (CONVERTER *self, const char *filename, |
| const OUTPUT_UNIT *output_unit) |
| { |
| const ELEMENT *node_command = 0; |
| const ELEMENT *command_for_title = 0; |
| BEGIN_FILE_INFORMATION *begin_info; |
| TEXT result; |
| const char *package_and_version; |
| const char *package_url; |
| |
| if (output_unit) |
| { |
| const ELEMENT *element_command = 0; |
| |
| if (output_unit->unit_node) |
| node_command = output_unit->unit_node->element; |
| |
| if (output_unit->unit_type == OU_special_unit) |
| element_command = output_unit->uc.special_unit_command; |
| else |
| element_command = output_unit->uc.unit_command; |
| |
| if (self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string) |
| && element_command) |
| command_for_title = element_command; |
| } |
| |
| begin_info = file_header_information (self, command_for_title, filename); |
| |
| text_init (&result); |
| |
| if (self->conf->DOCTYPE.o.string) |
| text_append (&result, self->conf->DOCTYPE.o.string); |
| text_append_n (&result, "\n", 1); |
| text_printf (&result, "<html%s>\n", begin_info->root_html_element_attributes); |
| if (self->conf->PACKAGE_AND_VERSION.o.string) |
| package_and_version = self->conf->PACKAGE_AND_VERSION.o.string; |
| else |
| package_and_version = ""; |
| if (self->conf->PACKAGE_URL.o.string) |
| package_url = self->conf->PACKAGE_URL.o.string; |
| else |
| package_url = ""; |
| text_printf (&result, "<!-- Created by %s, %s -->\n<head>\n", |
| package_and_version, package_url); |
| if (begin_info->encoding) |
| text_append (&result, begin_info->encoding); |
| text_append_n (&result, "\n", 1); |
| if (self->copying_comment) |
| text_append (&result, self->copying_comment); |
| text_printf (&result, "<title>%s</title>\n\n", begin_info->title); |
| if (begin_info->description) |
| text_append (&result, begin_info->description); |
| text_append_n (&result, "\n", 1); |
| if (begin_info->keywords) |
| { |
| text_printf (&result, "<meta name=\"keywords\" content=\"%s\"", |
| begin_info->keywords); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| } |
| text_append (&result, "<meta name=\"resource-type\" content=\"document\""); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| text_append (&result, "<meta name=\"distribution\" content=\"global\""); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| if (begin_info->generator) |
| text_append (&result, begin_info->generator); |
| if (self->date_in_header) |
| text_append (&result, self->date_in_header); |
| text_append (&result, |
| "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\""); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n\n", 2); |
| |
| get_links (self, filename, output_unit, node_command, &result); |
| |
| text_append (&result, begin_info->css_lines); |
| text_append_n (&result, "\n", 1); |
| if (begin_info->extra_head) |
| text_append (&result, begin_info->extra_head); |
| text_append_n (&result, "\n</head>\n\n", 10); |
| |
| text_printf (&result, "<body%s>\n", begin_info->body_attributes); |
| |
| if (self->conf->AFTER_BODY_OPEN.o.string) |
| text_append (&result, self->conf->AFTER_BODY_OPEN.o.string); |
| |
| destroy_begin_file_information (begin_info); |
| |
| return result.text; |
| } |
| |
| /* return string to be freed by the caller */ |
| char * |
| html_format_begin_file (CONVERTER *self, const char *filename, |
| const OUTPUT_UNIT *output_unit) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_begin_file]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| return html_default_format_begin_file (self, filename, output_unit); |
| } |
| else |
| { |
| return call_formatting_function_format_begin_file (self, |
| formatting_reference, |
| filename, output_unit); |
| } |
| } |
| |
| static char *nav_icon_array[] = {"nav-icon"}; |
| static const STRING_LIST nav_icon_classes = {nav_icon_array, 1, 1}; |
| |
| /* return string to be freed by the caller */ |
| char * |
| html_default_format_button_icon_img (CONVERTER *self, |
| const char *button_name, |
| const char *icon, const char *name) |
| { |
| TEXT result; |
| char *icon_protected; |
| char *attribute_class; |
| |
| if (!icon) |
| return strdup (""); |
| |
| text_init (&result); |
| |
| attribute_class = html_attribute_class (self, "img", |
| &nav_icon_classes); |
| text_append (&result, attribute_class); |
| free (attribute_class); |
| |
| text_append_n (&result, " src=\"", 6); |
| icon_protected = url_protect_url_text (self, icon); |
| text_append (&result, icon_protected); |
| free (icon_protected); |
| |
| text_append_n (&result, "\" alt=\"", 7); |
| if (name) |
| { |
| if (button_name) |
| text_printf (&result, "%s: %s", button_name, name); |
| else |
| text_append (&result, name); |
| } |
| else if (button_name) |
| text_append (&result, button_name); |
| |
| text_append_n (&result, "\"", 1); |
| html_close_lone_element (self, &result); |
| |
| return result.text; |
| } |
| |
| /* return string to be freed by the caller */ |
| char * |
| format_button_icon_img (CONVERTER *self, |
| const char *button_name, |
| const char *icon, const char *name) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_button_icon_img]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| return html_default_format_button_icon_img (self, button_name, |
| icon, name); |
| } |
| else |
| { |
| return call_formatting_function_format_button_icon_img (self, |
| formatting_reference, |
| button_name, |
| icon, name); |
| } |
| } |
| |
| static char *nav_label_array[] = {"nav-label"}; |
| static const STRING_LIST nav_label_classes = {nav_label_array, 1, 1}; |
| |
| static char *nav_link_array[] = {"nav-link"}; |
| static const STRING_LIST nav_link_classes = {nav_link_array, 1, 1}; |
| |
| static FORMATTED_BUTTON_INFO * |
| default_panel_button_dynamic_direction_internal (CONVERTER *self, |
| int direction, const ELEMENT *element, |
| int omit_rel, |
| int use_first_element_in_file_directions) |
| { |
| char *href; |
| char *node = 0; |
| FORMATTED_BUTTON_INFO *formatted_button; |
| |
| formatted_button = (FORMATTED_BUTTON_INFO *) |
| malloc (sizeof (FORMATTED_BUTTON_INFO)); |
| memset (formatted_button, 0, sizeof (FORMATTED_BUTTON_INFO)); |
| |
| formatted_button->need_delimiter = 1; |
| |
| if (self->conf->USE_NODE_DIRECTIONS.o.integer > 0 |
| || (self->conf->USE_NODE_DIRECTIONS.o.integer < 0 |
| && self->conf->USE_NODES.o.integer > 0)) |
| direction += NODE_DIRECTIONS_OFFSET; |
| |
| if (use_first_element_in_file_directions) |
| direction -= FIRSTINFILE_OFFSET; |
| |
| href = from_element_direction (self, direction, HTT_href, 0, 0, element); |
| |
| if (self->conf->xrefautomaticsectiontitle.o.string |
| && !strcmp (self->conf->xrefautomaticsectiontitle.o.string, "on")) |
| node = from_element_direction (self, direction, |
| HTT_section_nonumber, 0, 0, 0); |
| |
| if (!node) |
| node = from_element_direction (self, direction, HTT_node, 0, 0, 0); |
| |
| if (node && node[strspn (node, whitespace_chars)] != '\0') |
| { |
| char *open_label; |
| char *open_link; |
| const char *close_label_span = ""; |
| const char *close_link_span = ""; |
| const char *close_label = ""; |
| const char *close_link = ""; |
| size_t open_len; |
| |
| const char *text = direction_string (self, direction, TDS_type_text, 0); |
| if (!text) |
| text = ""; |
| |
| open_label = html_attribute_class (self, "span", &nav_label_classes); |
| open_len = strlen (open_label); |
| |
| if (open_len > 0) |
| { |
| close_label_span = ">"; |
| close_label = "</span>"; |
| } |
| |
| open_link = html_attribute_class (self, "span", &nav_link_classes); |
| open_len = strlen (open_link); |
| |
| if (open_len > 0) |
| { |
| close_link_span = ">"; |
| close_link = "</span>"; |
| } |
| |
| if (href && strlen (href)) |
| { |
| char *hyperlink |
| = direction_a (self, direction, href, node, omit_rel); |
| xasprintf (&formatted_button->active, "%s%s%s: %s%s%s%s%s", |
| open_label, close_label_span, text, close_label, |
| open_link, close_link_span, hyperlink, close_link); |
| free (hyperlink); |
| } |
| else |
| xasprintf (&formatted_button->active, "%s%s%s: %s%s%s%s%s", |
| open_label, close_label_span, text, close_label, |
| open_link, close_link_span, node, close_link); |
| |
| free (open_label); |
| free (open_link); |
| } |
| |
| free (href); |
| free (node); |
| return formatted_button; |
| } |
| |
| static FORMATTED_BUTTON_INFO * |
| default_panel_button_dynamic_direction (CONVERTER *self, |
| int direction, const ELEMENT *element) |
| { |
| return default_panel_button_dynamic_direction_internal (self, |
| direction, element, 0, 0); |
| } |
| |
| static FORMATTED_BUTTON_INFO * |
| default_panel_button_dynamic_direction_node_footer (CONVERTER *self, |
| int direction, const ELEMENT *element) |
| { |
| return default_panel_button_dynamic_direction_internal (self, |
| direction, element, 1, 0); |
| } |
| |
| static FORMATTED_BUTTON_INFO * |
| default_panel_button_dynamic_direction_section_footer (CONVERTER *self, |
| int direction, const ELEMENT *element) |
| { |
| return default_panel_button_dynamic_direction_internal (self, |
| direction, element, 0, 1); |
| } |
| |
| /* the order corresponds to enum button_function_type */ |
| FORMATTED_BUTTON_INFO * (*html_format_button_function[]) (CONVERTER *self, |
| int direction, const ELEMENT *element) = { |
| 0, |
| &default_panel_button_dynamic_direction_section_footer, |
| &default_panel_button_dynamic_direction_node_footer, |
| &default_panel_button_dynamic_direction, |
| 0 |
| }; |
| |
| FORMATTED_BUTTON_INFO * |
| button_direction_function (CONVERTER *self, BUTTON_FUNCTION *button_function, |
| int direction, const ELEMENT *element) |
| { |
| if (html_format_button_function[button_function->type]) |
| return (*html_format_button_function[button_function->type]) |
| (self, direction, element); |
| else |
| return call_button_direction_function (self, button_function->sv_reference, |
| direction, element); |
| } |
| |
| FORMATTED_BUTTON_INFO * |
| html_default_format_button (CONVERTER *self, |
| const BUTTON_SPECIFICATION *button, |
| const ELEMENT *element) |
| { |
| if (button->type == BST_function) |
| { |
| return call_button_simple_function (self, button->b.sv_reference); |
| } |
| else if (button->type == BST_direction_info |
| && button->b.button_info->type == BIT_function) |
| { |
| return button_direction_function (self, |
| &button->b.button_info->bi.button_function, |
| button->b.button_info->direction, element); |
| } |
| else |
| { |
| FORMATTED_BUTTON_INFO *formatted_button; |
| formatted_button = (FORMATTED_BUTTON_INFO *) |
| malloc (sizeof (FORMATTED_BUTTON_INFO)); |
| memset (formatted_button, 0, sizeof (FORMATTED_BUTTON_INFO)); |
| |
| if (button->type == BST_direction_info) |
| { |
| int direction = button->b.button_info->direction; |
| |
| if (button->b.button_info->type |
| == BIT_selected_direction_information_type) |
| { |
| /* this case is mostly for tests, to test the direction type |
| in direction_information_type with the direction direction */ |
| if (button->b.button_info->bi.direction_information_type >= 0) |
| formatted_button->active |
| = from_element_direction (self, direction, |
| button->b.button_info->bi.direction_information_type, |
| 0, 0, element); |
| } |
| formatted_button->need_delimiter = 1; |
| } |
| /* for the next cases, button->type == BST_direction */ |
| else if (html_global_direction_text (self, button->b.direction) > 0) |
| { |
| /* handle space button */ |
| if (self->html_active_icons |
| && self->html_active_icons[button->b.direction]) |
| { |
| const char *button_name_string = direction_string (self, |
| button->b.direction, TDS_type_button, |
| TDS_context_string); |
| formatted_button->active |
| = format_button_icon_img (self, button_name_string, |
| self->html_active_icons[button->b.direction], 0); |
| } |
| else |
| { |
| const char *button_text = direction_string (self, |
| button->b.direction, TDS_type_text, 0); |
| if (button_text) |
| formatted_button->active = strdup (button_text); |
| } |
| formatted_button->need_delimiter = 0; |
| } |
| else |
| { |
| char *href = from_element_direction (self, button->b.direction, |
| HTT_href, 0, 0, element); |
| if (href) |
| { |
| /* button is active */ |
| TEXT active_text; |
| const char *active_icon = 0; |
| const char *description |
| = direction_string (self, button->b.direction, |
| TDS_type_description, TDS_context_string); |
| |
| if (self->html_active_icons |
| && self->html_active_icons[button->b.direction]) |
| { |
| active_icon = self->html_active_icons[button->b.direction]; |
| } |
| |
| text_init (&active_text); |
| if (!active_icon) |
| text_append_n (&active_text, "[", 1); |
| text_printf (&active_text, "<a href=\"%s\"", href); |
| if (description) |
| text_printf (&active_text, " title=\"%s\"", description); |
| if (self->conf->USE_ACCESSKEY.o.integer > 0) |
| { |
| const char *accesskey |
| = direction_string (self, button->b.direction, |
| TDS_type_accesskey, TDS_context_string); |
| if (accesskey && strlen (accesskey)) |
| text_printf (&active_text, " accesskey=\"%s\"", accesskey); |
| } |
| const char *button_rel |
| = direction_string (self, button->b.direction, |
| TDS_type_rel, TDS_context_string); |
| if (button_rel && strlen (button_rel)) |
| text_printf (&active_text, " rel=\"%s\"", button_rel); |
| text_append_n (&active_text, ">", 1); |
| if (active_icon) |
| { |
| const char *button_name_string = direction_string (self, |
| button->b.direction, TDS_type_button, |
| TDS_context_string); |
| char *icon_name = from_element_direction (self, |
| button->b.direction, |
| HTT_string, |
| 0, 0, 0); |
| char *icon_img |
| = format_button_icon_img (self, button_name_string, |
| active_icon, icon_name); |
| free (icon_name); |
| |
| text_append (&active_text, icon_img); |
| free (icon_img); |
| } |
| else |
| { |
| const char *button_text_string = direction_string (self, |
| button->b.direction, TDS_type_text, 0); |
| if (button_text_string) |
| text_append (&active_text, button_text_string); |
| } |
| |
| text_append_n (&active_text, "</a>", 4); |
| |
| if (!active_icon) |
| text_append_n (&active_text, "]", 1); |
| |
| formatted_button->active = active_text.text; |
| free (href); |
| } |
| else |
| { |
| TEXT passive_text; |
| const char *passive_icon = 0; |
| |
| text_init (&passive_text); |
| |
| if (self->html_passive_icons |
| && self->html_passive_icons[button->b.direction]) |
| { |
| passive_icon |
| = self->html_passive_icons[button->b.direction]; |
| } |
| if (passive_icon) |
| { |
| const char *button_name_string |
| = direction_string (self, button->b.direction, |
| TDS_type_button, TDS_context_string); |
| char *icon_name = from_element_direction (self, |
| button->b.direction, |
| HTT_string, |
| 0, 0, 0); |
| char *icon_img |
| = format_button_icon_img (self, button_name_string, |
| passive_icon, icon_name); |
| free (icon_name); |
| |
| text_append (&passive_text, icon_img); |
| free (icon_img); |
| } |
| else |
| { |
| const char *button_text_string |
| = direction_string (self, button->b.direction, |
| TDS_type_text, 0); |
| text_append_n (&passive_text, "[", 1); |
| if (button_text_string) |
| text_append (&passive_text, button_text_string); |
| text_append_n (&passive_text, "]", 1); |
| } |
| formatted_button->passive = passive_text.text; |
| } |
| formatted_button->need_delimiter = 0; |
| } |
| return formatted_button; |
| } |
| return 0; |
| } |
| |
| FORMATTED_BUTTON_INFO * |
| format_button (CONVERTER *self, |
| const BUTTON_SPECIFICATION *button, |
| const ELEMENT *element) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_button]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| return html_default_format_button (self, button, element); |
| } |
| else |
| { |
| return call_formatting_function_format_button (self, |
| formatting_reference, |
| button, element); |
| } |
| } |
| |
| static void |
| open_element_with_class (CONVERTER *self, const char *element_name, |
| const STRING_LIST *classes, TEXT *result) |
| { |
| char *attribute_class = html_attribute_class (self, element_name, |
| classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">", 1); |
| } |
| |
| static char *nav_button_array[] = {"nav-button"}; |
| static const STRING_LIST nav_button_classes = {nav_button_array, 1, 1}; |
| |
| static char *nav_panel_array[] = {"nav-panel"}; |
| static const STRING_LIST nav_panel_classes = {nav_panel_array, 1, 1}; |
| |
| void |
| html_default_format_navigation_panel (CONVERTER *self, |
| const BUTTON_SPECIFICATION_LIST *buttons, |
| const char *cmdname, const ELEMENT *element, |
| int vertical, int in_header, TEXT *result) |
| { |
| size_t i; |
| size_t nr_of_buttons_shown = 0; |
| TEXT result_buttons; |
| |
| if (!buttons) |
| return; |
| |
| /* do the buttons first in case they are formatted as an empty string */ |
| text_init (&result_buttons); |
| text_append (&result_buttons, ""); |
| |
| for (i = 0; i < buttons->number; i++) |
| { |
| const BUTTON_SPECIFICATION *button = &buttons->list[i]; |
| FORMATTED_BUTTON_INFO *button_info; |
| char *active = 0; |
| char *passive = 0; |
| int need_delimiter = 0; |
| int direction = -1; |
| if (button->type == BST_direction_info) |
| direction = button->b.button_info->direction; |
| else if (button->type == BST_direction) |
| direction = button->b.direction; |
| |
| if (direction >= 0 && direction == D_direction_Space |
| && nr_of_buttons_shown == 0) |
| continue; |
| |
| button_info = format_button (self, button, element); |
| |
| if (button_info) |
| { |
| need_delimiter = button_info->need_delimiter; |
| active = button_info->active; |
| passive = button_info->passive; |
| free (button_info); |
| } |
| |
| if (self->conf->HEADER_IN_TABLE.o.integer > 0) |
| { |
| if (vertical) |
| text_append_n (&result_buttons, "<tr>\n", 5); |
| open_element_with_class (self, "td", &nav_button_classes, |
| &result_buttons); |
| |
| if (active) |
| text_append (&result_buttons, active); |
| else if (passive) |
| text_append (&result_buttons, passive); |
| |
| text_append_n (&result_buttons, "</td>\n", 6); |
| if (vertical) |
| text_append_n (&result_buttons, "</tr>\n", 6); |
| |
| nr_of_buttons_shown++; |
| } |
| else if (active) |
| { /* only active buttons are print out when not in table */ |
| if (need_delimiter && nr_of_buttons_shown > 0) |
| text_append_n (&result_buttons, ", ", 2); |
| |
| char *open; |
| size_t open_len; |
| |
| open = html_attribute_class (self, "span", &nav_button_classes); |
| open_len = strlen (open); |
| |
| if (open_len > 0) |
| { |
| text_append (&result_buttons, open); |
| text_append_n (&result_buttons, ">", 1); |
| } |
| text_append (&result_buttons, active); |
| |
| free (open); |
| if (open_len > 0) |
| text_append_n (&result_buttons, "</span>", 7); |
| |
| nr_of_buttons_shown++; |
| } |
| |
| free (active); |
| free (passive); |
| } |
| |
| if (result_buttons.end <= 0) |
| { |
| free (result_buttons.text); |
| return; |
| } |
| |
| if (self->conf->HEADER_IN_TABLE.o.integer > 0) |
| { |
| open_element_with_class (self, "table", &nav_panel_classes, result); |
| text_append_n (result, "\n", 1); |
| |
| if (!vertical) |
| text_append_n (result, "<tr>", 4); |
| } |
| else |
| { |
| open_element_with_class (self, "p", &nav_panel_classes, result); |
| text_append_n (result, "\n", 1); |
| } |
| |
| text_append (result, result_buttons.text); |
| |
| if (self->conf->HEADER_IN_TABLE.o.integer > 0) |
| { |
| if (!vertical) |
| text_append_n (result, "</tr>", 5); |
| text_append_n (result, "</table>\n", 9); |
| } |
| else |
| { |
| text_append_n (result, "</p>\n", 5); |
| } |
| free (result_buttons.text); |
| } |
| |
| void |
| format_navigation_panel (CONVERTER *self, |
| BUTTON_SPECIFICATION_LIST *buttons, |
| const char *cmdname, const ELEMENT *element, |
| int vertical, int in_header, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_navigation_panel]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_navigation_panel (self, buttons, cmdname, |
| element, vertical, in_header, result); |
| } |
| else |
| { |
| char *navigation_panel |
| = call_formatting_function_format_navigation_panel (self, |
| formatting_reference, buttons, cmdname, |
| element, vertical, in_header); |
| text_append (result, navigation_panel); |
| free (navigation_panel); |
| } |
| } |
| |
| static char *vertical_navigation_array[] = {"vertical-navigation"}; |
| static const STRING_LIST vertical_navigation_classes |
| = {vertical_navigation_array, 1, 1}; |
| |
| void |
| html_default_format_navigation_header (CONVERTER *self, |
| BUTTON_SPECIFICATION_LIST *buttons, |
| const char *cmdname, |
| const ELEMENT *element, TEXT *result) |
| { |
| int vertical = 0; |
| size_t result_text_index; |
| if (self->conf->VERTICAL_HEAD_NAVIGATION.o.integer > 0) |
| vertical = 1; |
| if (vertical) |
| { |
| open_element_with_class (self, "table", |
| &vertical_navigation_classes, result); |
| text_append_n (result, "\n", 1); |
| text_append (result, "<tr>\n"); |
| open_element_with_class (self, "td", |
| &vertical_navigation_classes, result); |
| text_append_n (result, "\n", 1); |
| } |
| |
| /* keep the current index in result to be able to determine if text was |
| added by format_navigation_panel */ |
| result_text_index = result->end; |
| format_navigation_panel (self, buttons, cmdname, element, |
| vertical, 1, result); |
| |
| if (vertical) |
| text_append (result, "</td>\n<td>\n"); |
| else if (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "node") |
| && self->conf->DEFAULT_RULE.o.string |
| && result->end > result_text_index) |
| { |
| text_append (result, self->conf->DEFAULT_RULE.o.string); |
| text_append_n (result, "\n", 1); |
| } |
| } |
| |
| void |
| format_navigation_header (CONVERTER *self, |
| BUTTON_SPECIFICATION_LIST *buttons, |
| const char *cmdname, |
| const ELEMENT *element, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_navigation_header]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_navigation_header (self, buttons, cmdname, |
| element, result); |
| } |
| else |
| { |
| char *navigation_header |
| = call_formatting_function_format_navigation_header (self, |
| formatting_reference, |
| buttons, cmdname, element); |
| text_append (result, navigation_header); |
| free (navigation_header); |
| } |
| } |
| |
| void |
| html_default_format_element_header (CONVERTER *self, |
| const char *cmdname, const ELEMENT *command, |
| const OUTPUT_UNIT *output_unit, TEXT *result) |
| { |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| size_t i; |
| TEXT debug_txt; |
| char *output_unit_text; |
| text_init (&debug_txt); |
| text_append (&debug_txt, "C|FORMAT elt header ("); |
| for (i = 0; i < output_unit->unit_contents.number; i++) |
| { |
| char *elt_str |
| = print_element_debug (output_unit->unit_contents.list[i], 0); |
| if (i > 0) |
| text_append_n (&debug_txt, "|", 1); |
| text_append (&debug_txt, elt_str); |
| free (elt_str); |
| } |
| output_unit_text = output_unit_texi (output_unit); |
| text_printf (&debug_txt, ") %s\n", output_unit_text); |
| free (output_unit_text); |
| fprintf (stderr, "%s", debug_txt.text); |
| free (debug_txt.text); |
| } |
| |
| /* Do the heading if the command is the first command in the element */ |
| if ((output_unit->unit_contents.list[0] == command |
| || (!output_unit->unit_contents.list[0]->e.c->cmd |
| && output_unit->unit_contents.list[1] == command)) |
| /* and there is more than one element */ |
| && (output_unit->tree_unit_directions[D_next] |
| || output_unit->tree_unit_directions[D_prev])) |
| { |
| int is_top = unit_is_top_output_unit (self, output_unit); |
| size_t file_index; |
| size_t count_in_file; |
| int first_in_page = 0; |
| int previous_is_top = 0; |
| if (output_unit->unit_filename) |
| { |
| file_index = self->output_unit_file_indices[output_unit->index]; |
| count_in_file |
| = count_elements_in_file_number (self, CEFT_current, |
| file_index +1); |
| if (count_in_file == 1) |
| first_in_page = 1; |
| } |
| |
| if (output_unit->tree_unit_directions[D_prev] |
| && unit_is_top_output_unit (self, |
| output_unit->tree_unit_directions[D_prev])) |
| previous_is_top = 1; |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| char *root_heading_texi = root_heading_command_to_texinfo (command); |
| fprintf (stderr, "Header (%d, %d, %d): %s\n", previous_is_top, |
| is_top, first_in_page, root_heading_texi); |
| free (root_heading_texi); |
| } |
| |
| if (is_top) |
| /* use TOP_BUTTONS for top. */ |
| { |
| if ((self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string)) |
| || self->conf->HEADERS.o.integer > 0) |
| format_navigation_header (self, self->conf->TOP_BUTTONS.o.buttons, |
| cmdname, command, result); |
| } |
| else |
| { |
| if (first_in_page && self->conf->HEADERS.o.integer <= 0) |
| { |
| if (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "chapter")) |
| { |
| format_navigation_header (self, |
| self->conf->CHAPTER_BUTTONS.o.buttons, cmdname, command, |
| result); |
| if (self->conf->DEFAULT_RULE.o.string |
| && self->conf->VERTICAL_HEAD_NAVIGATION.o.integer <= 0) |
| { |
| text_append (result, self->conf->DEFAULT_RULE.o.string); |
| text_append_n (result, "\n", 1); |
| } |
| } |
| else if (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "section")) |
| { |
| format_navigation_header (self, |
| self->conf->SECTION_BUTTONS.o.buttons, cmdname, command, |
| result); |
| } |
| } |
| if ((first_in_page || previous_is_top) |
| && self->conf->HEADERS.o.integer > 0) |
| { |
| format_navigation_header (self, |
| self->conf->SECTION_BUTTONS.o.buttons, cmdname, command, |
| result); |
| } |
| else if (self->conf->HEADERS.o.integer > 0 |
| || (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "node"))) |
| { |
| /* got to do this here, as it isn't done otherwise since |
| navigation_header is not called */ |
| format_navigation_panel (self, |
| self->conf->SECTION_BUTTONS.o.buttons, |
| cmdname, command, 0, 1, result); |
| } |
| } |
| } |
| } |
| |
| void |
| format_element_header (CONVERTER *self, |
| const char *cmdname, const ELEMENT *element, |
| const OUTPUT_UNIT *output_unit, TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_element_header]; |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_element_header (self, cmdname, element, |
| output_unit, result); |
| } |
| else |
| { |
| char *element_header |
| = call_formatting_function_format_element_header (self, |
| formatting_reference, |
| cmdname, element, output_unit); |
| text_append (result, element_header); |
| free (element_header); |
| } |
| } |
| |
| static int |
| word_number_more_than_level (const char *text, int level, int *count) |
| { |
| const char *p = text; |
| |
| p += strspn (p, whitespace_chars); |
| |
| if (*p) |
| *count = 1; |
| |
| while (*p) |
| { |
| size_t n = strspn (p, whitespace_chars); |
| if (n) |
| { |
| p += n; |
| /* if not followed by anything, ie at the end of the string, |
| do not count the space */ |
| if (*p) |
| { |
| (*count)++; |
| if (*count >= level) |
| return 1; |
| } |
| else |
| return 0; |
| } |
| /* skip a character */ |
| int char_len = 1; |
| while ((p[char_len] & 0xC0) == 0x80) |
| char_len++; |
| p += char_len; |
| } |
| return 0; |
| } |
| |
| void |
| html_default_format_element_footer (CONVERTER *self, |
| const enum output_unit_type unit_type, |
| const OUTPUT_UNIT *output_unit, |
| const char *content, const ELEMENT *element, |
| TEXT *result) |
| { |
| int is_top = unit_is_top_output_unit (self, output_unit); |
| int next_is_top = 0; |
| int next_is_special = 0; |
| int end_page = 0; |
| BUTTON_SPECIFICATION_LIST *buttons = 0; |
| |
| if (output_unit->tree_unit_directions[D_next] |
| && unit_is_top_output_unit (self, |
| output_unit->tree_unit_directions[D_next])) |
| next_is_top = 1; |
| |
| if (output_unit->tree_unit_directions[D_next] |
| && output_unit->tree_unit_directions[D_next]->unit_type |
| == OU_special_unit) |
| next_is_special = 1; |
| |
| if (!output_unit->tree_unit_directions[D_next]) |
| end_page = 1; |
| else if (output_unit->unit_filename |
| && strcmp (output_unit->unit_filename, |
| output_unit->tree_unit_directions[D_next]->unit_filename)) |
| { |
| size_t file_index; |
| size_t count_in_file; |
| |
| if (unit_type == OU_special_unit) |
| file_index = self->special_unit_file_indices[output_unit->index]; |
| else |
| file_index = self->output_unit_file_indices[output_unit->index]; |
| count_in_file |
| = count_elements_in_file_number (self, CEFT_remaining, file_index +1); |
| |
| if (count_in_file == 1) |
| end_page = 1; |
| } |
| |
| if ((end_page || next_is_top || next_is_special || is_top) |
| && self->conf->VERTICAL_HEAD_NAVIGATION.o.integer > 0 |
| && (!self->conf->SPLIT.o.string || !strlen (self->conf->SPLIT.o.string) |
| || strcmp (self->conf->SPLIT.o.string, "node") |
| || self->conf->HEADERS.o.integer > 0 || unit_type == OU_special_unit |
| || is_top)) |
| { |
| text_append_n (result, "</td>\n</tr>\n</table>\n", 21); |
| } |
| |
| if (end_page) |
| { |
| STRING_LIST *closed_strings; |
| closed_strings = html_close_registered_sections_level (self, |
| self->current_filename.file_number, 0); |
| |
| 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); |
| |
| /* setup buttons for navigation footer */ |
| if ((is_top || unit_type == OU_special_unit) |
| && ((self->conf->SPLIT.o.string |
| && strcmp (self->conf->SPLIT.o.string, "")) |
| || self->conf->MONOLITHIC.o.integer <= 0) |
| && (self->conf->HEADERS.o.integer > 0 |
| || (self->conf->SPLIT.o.string |
| && strcmp (self->conf->SPLIT.o.string, "") |
| && strcmp (self->conf->SPLIT.o.string, "node")))) |
| { |
| if (is_top) |
| buttons = self->conf->TOP_FOOTER_BUTTONS.o.buttons; |
| else |
| buttons = self->conf->MISC_BUTTONS.o.buttons; |
| } |
| else if (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "section")) |
| buttons = self->conf->SECTION_FOOTER_BUTTONS.o.buttons; |
| else if (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "chapter")) |
| buttons = self->conf->CHAPTER_FOOTER_BUTTONS.o.buttons; |
| else if (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "node")) |
| { |
| if (self->conf->HEADERS.o.integer > 0) |
| { |
| if (self->conf->WORDS_IN_PAGE.o.integer > 0) |
| { |
| int count; |
| int more_than_level = word_number_more_than_level (content, |
| self->conf->WORDS_IN_PAGE.o.integer, &count); |
| if (content && more_than_level) |
| { |
| buttons = self->conf->NODE_FOOTER_BUTTONS.o.buttons; |
| } |
| } |
| else |
| buttons = self->conf->NODE_FOOTER_BUTTONS.o.buttons; |
| } |
| } |
| } |
| |
| if ((!output_unit->tree_unit_directions[D_next] |
| || (output_unit->unit_filename |
| && strcmp (output_unit->unit_filename, |
| output_unit->tree_unit_directions[D_next]->unit_filename))) |
| && (!self->conf->footnotestyle.o.string |
| || strcmp (self->conf->footnotestyle.o.string, "separate"))) |
| { |
| format_footnotes_segment (self, result); |
| } |
| |
| if (buttons || !end_page || self->conf->PROGRAM_NAME_IN_FOOTER.o.integer > 0) |
| { |
| const char *rule = 0; |
| |
| if (!end_page && (is_top || next_is_top || (next_is_special |
| && unit_type != OU_special_unit))) |
| { |
| rule = self->conf->BIG_RULE.o.string; |
| } |
| else if (!buttons || is_top || unit_type == OU_special_unit |
| || (end_page && self->conf->SPLIT.o.string |
| && (!strcmp (self->conf->SPLIT.o.string, "section") |
| || !strcmp (self->conf->SPLIT.o.string, "chapter"))) |
| || (self->conf->SPLIT.o.string |
| && !strcmp (self->conf->SPLIT.o.string, "node") |
| && self->conf->HEADERS.o.integer > 0)) |
| { |
| rule = self->conf->DEFAULT_RULE.o.string; |
| } |
| if (rule && strlen (rule)) |
| { |
| text_append (result, rule); |
| text_append_n (result, "\n", 1); |
| } |
| } |
| |
| if (buttons) |
| { |
| const char *cmdname = 0; |
| if (element) |
| cmdname = element_command_name (element); |
| |
| format_navigation_panel (self, buttons, cmdname, element, 0, 0, result); |
| } |
| } |
| |
| void |
| format_element_footer (CONVERTER *self, |
| const enum output_unit_type unit_type, |
| const OUTPUT_UNIT *output_unit, |
| const char *content, const ELEMENT *element, |
| TEXT *result) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_element_footer]; |
| |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| html_default_format_element_footer (self, unit_type, output_unit, |
| content, element, result); |
| } |
| else |
| { |
| char *formatted_footer |
| = call_formatting_function_format_element_footer (self, |
| formatting_reference, |
| unit_type, |
| output_unit, content, element); |
| text_append (result, formatted_footer); |
| free (formatted_footer); |
| } |
| } |
| |
| void |
| html_default_format_date_in_header (CONVERTER *self, TEXT *result) |
| { |
| ELEMENT *today_element = new_command_element (ET_brace_command, |
| CM_today); |
| char *today; |
| |
| add_tree_to_build (self, today_element); |
| today = html_convert_tree_new_formatting_context (self, today_element, |
| "DATE_IN_HEADER", |
| 0, 0, 0, 0); |
| remove_tree_to_build (self, today_element); |
| destroy_element (today_element); |
| |
| text_printf (result, |
| "<meta name=\"date\" content=\"%s\"", today); |
| free (today); |
| html_close_lone_element (self, result); |
| text_append_n (result, "\n", 1); |
| } |
| |
| /* return string to be freed by the caller */ |
| char * |
| html_default_format_jslicense_file (CONVERTER *self, |
| const JSLICENSE_CATEGORY_LIST *jslicenses) |
| { |
| TEXT result; |
| size_t i; |
| char *root_html_element_attributes; |
| |
| text_init (&result); |
| |
| if (self->conf->DOCTYPE.o.string) |
| text_append (&result, self->conf->DOCTYPE.o.string); |
| text_append_n (&result, "\n", 1); |
| root_html_element_attributes |
| = root_html_element_attributes_string (self); |
| if (!root_html_element_attributes) |
| root_html_element_attributes = strdup (""); |
| text_printf (&result, "<html%s>", root_html_element_attributes); |
| free (root_html_element_attributes); |
| text_append (&result, "<head><title>jslicense labels</title></head>\n" |
| "<body>\n" |
| "<table id=\"jslicense-labels1\">\n"); |
| |
| for (i = 0; i < jslicenses->number; i++) |
| { |
| size_t j; |
| JSLICENSE_FILE_INFO_LIST *jlicense_file_info_list |
| = &jslicenses->list[i]; |
| |
| for (j = 0; j < jlicense_file_info_list->number; j++) |
| { |
| JSLICENSE_FILE_INFO *jlicense_file_info |
| = &jlicense_file_info_list->list[j]; |
| char *p_file |
| = url_protect_url_text (self, jlicense_file_info->filename); |
| char *p_url |
| = url_protect_url_text (self, jlicense_file_info->url); |
| char *p_source |
| = url_protect_url_text (self, jlicense_file_info->source); |
| text_append_n (&result, "<tr>\n", 5); |
| text_append_n (&result, "<td><a href=\"", 13); |
| text_append (&result, p_file); |
| text_append_n (&result, "\">", 2); |
| text_append (&result, jlicense_file_info->filename); |
| text_append_n (&result, "</a></td>\n", 10); |
| text_append_n (&result, "<td><a href=\"", 13); |
| text_append (&result, p_url); |
| text_append_n (&result, "\">", 2); |
| text_append (&result, jlicense_file_info->license); |
| text_append_n (&result, "</a></td>\n", 10); |
| text_append_n (&result, "<td><a href=\"", 13); |
| text_append (&result, p_source); |
| text_append_n (&result, "\">", 2); |
| text_append (&result, jlicense_file_info->source); |
| text_append_n (&result, "</a></td>\n", 10); |
| text_append_n (&result, "</tr>\n", 6); |
| free (p_file); |
| free (p_url); |
| free (p_source); |
| } |
| } |
| text_append_n (&result, "</table>\n</body></html>\n", 24); |
| |
| return result.text; |
| } |
| |
| /* return string to be freed by the caller */ |
| char * |
| html_default_format_node_redirection_page (CONVERTER *self, |
| const ELEMENT *element, |
| const char *filename) |
| { |
| TEXT result; |
| TEXT body; |
| BEGIN_FILE_INFORMATION *begin_info; |
| const char *package_and_version; |
| const char *package_url; |
| char *href = html_command_href (self, element, filename, 0, 0); |
| char *name = html_command_text (self, element, 0); |
| ELEMENT *direction_element = new_text_element (ET__converted); |
| NAMED_STRING_ELEMENT_LIST *substrings = new_named_string_element_list (); |
| |
| text_printf (direction_element->e.text, "<a href=\"%s\">%s</a>", href, name); |
| free (name); |
| |
| add_element_to_named_string_element_list (substrings, |
| "href", direction_element); |
| |
| /* do it before in case there is CSS */ |
| |
| text_init (&body); |
| html_translate_convert_tree_append ( |
| "The node you are looking for is at {href}.", |
| self, substrings, 0, &body, "Tr redirection sentence"); |
| destroy_named_string_element_list (substrings); |
| |
| begin_info = file_header_information (self, element, filename); |
| |
| text_init (&result); |
| |
| if (self->conf->DOCTYPE.o.string) |
| text_append (&result, self->conf->DOCTYPE.o.string); |
| text_append_n (&result, "\n", 1); |
| text_printf (&result, "<html%s>\n", begin_info->root_html_element_attributes); |
| if (self->conf->PACKAGE_AND_VERSION.o.string) |
| package_and_version = self->conf->PACKAGE_AND_VERSION.o.string; |
| else |
| package_and_version = ""; |
| if (self->conf->PACKAGE_URL.o.string) |
| package_url = self->conf->PACKAGE_URL.o.string; |
| else |
| package_url = ""; |
| text_printf (&result, "<!-- Created by %s, %s -->\n" |
| "<!-- This file redirects to the location of a node or anchor -->\n" |
| "<head>\n", |
| package_and_version, package_url); |
| if (begin_info->encoding) |
| text_append (&result, begin_info->encoding); |
| text_append_n (&result, "\n", 1); |
| if (self->copying_comment) |
| text_append (&result, self->copying_comment); |
| text_printf (&result, "<title>%s</title>\n\n", begin_info->title); |
| if (begin_info->description) |
| text_append (&result, begin_info->description); |
| text_append_n (&result, "\n", 1); |
| if (begin_info->keywords) |
| { |
| text_printf (&result, "<meta name=\"keywords\" content=\"%s\"", |
| begin_info->keywords); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| } |
| text_append (&result, "<meta name=\"resource-type\" content=\"document\""); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| text_append (&result, "<meta name=\"distribution\" content=\"global\""); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| if (begin_info->generator) |
| text_append (&result, begin_info->generator); |
| if (self->date_in_header) |
| text_append (&result, self->date_in_header); |
| text_append (&result, begin_info->css_lines); |
| text_append_n (&result, "\n", 1); |
| text_printf (&result, "<meta http-equiv=\"Refresh\" content=\"0; url=%s\"", |
| href); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| text_append (&result, |
| "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\""); |
| html_close_lone_element (self, &result); |
| text_append_n (&result, "\n", 1); |
| |
| free (href); |
| |
| if (begin_info->extra_head) |
| text_append (&result, begin_info->extra_head); |
| text_append_n (&result, "\n</head>\n\n", 10); |
| text_printf (&result, "<body%s>\n", begin_info->body_attributes); |
| if (self->conf->AFTER_BODY_OPEN.o.string) |
| text_append (&result, self->conf->AFTER_BODY_OPEN.o.string); |
| |
| text_append_n (&result, "\n<p>", 4); |
| text_append (&result, body.text); |
| free (body.text); |
| text_append_n (&result, "</p>\n</body>\n", 13); |
| |
| destroy_begin_file_information (begin_info); |
| |
| return result.text; |
| } |
| |
| /* return string to be freed by the caller */ |
| char * |
| html_format_node_redirection_page (CONVERTER *self, const ELEMENT *element, |
| const char *filename) |
| { |
| const FORMATTING_REFERENCE *formatting_reference |
| = &self->current_formatting_references[FR_format_node_redirection_page]; |
| |
| if (formatting_reference->status == FRS_status_default_set |
| || formatting_reference->status == FRS_status_none) |
| { |
| return html_default_format_node_redirection_page (self, element, |
| filename); |
| } |
| else |
| { |
| return call_formatting_function_format_node_redirection_page (self, |
| formatting_reference, |
| element, filename); |
| } |
| } |
| |
| static void |
| format_simpletitle (CONVERTER *self, 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); |
| |
| xasprintf (&context_str, "%s simpletitle", |
| builtin_command_name (cmd)); |
| title_text |
| = html_convert_tree_new_formatting_context (self, |
| self->simpletitle_tree, context_str, 0, 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); |
| } |
| |
| /* command is NULL unless called from @-command formatting function */ |
| static char * |
| contents_inline_element (CONVERTER *self, const enum command_id cmd, |
| const ELEMENT *element) |
| { |
| char *table_of_contents; |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| fprintf (stderr, "CONTENTS_INLINE %s\n", builtin_command_name (cmd)); |
| |
| table_of_contents = format_contents (self, cmd, element, 0); |
| if (table_of_contents && strlen (table_of_contents)) |
| { |
| int j; |
| for (j = 0; self->command_special_variety_name_index[j].cmd; j++) |
| { |
| COMMAND_ID_INDEX cmd_variety_index |
| = self->command_special_variety_name_index[j]; |
| if (cmd_variety_index.cmd == cmd) |
| { |
| const char *id; |
| char *heading = 0; |
| TEXT result; |
| STRING_LIST *classes; |
| const char *class_base; |
| char *class; |
| char *attribute_class; |
| |
| const char *special_unit_variety |
| = self->special_unit_varieties.list[cmd_variety_index.index]; |
| int special_unit_direction_index |
| = html_special_unit_variety_direction_index (self, |
| special_unit_variety); |
| const OUTPUT_UNIT *special_unit |
| = self->global_units_directions[special_unit_direction_index]; |
| const ELEMENT *unit_command |
| = special_unit->uc.special_unit_command; |
| |
| text_init (&result); |
| |
| classes = new_string_list (); |
| class_base = html_special_unit_info (self, SUI_type_class, |
| special_unit_variety); |
| xasprintf (&class, "region-%s", class_base); |
| |
| add_string (class, classes); |
| free (class); |
| attribute_class = html_attribute_class (self, "div", classes); |
| clear_strings_list (classes); |
| |
| text_append (&result, attribute_class); |
| free (attribute_class); |
| |
| id = html_command_id (self, unit_command); |
| if (id && strlen (id)) |
| text_printf (&result, " id=\"%s\"", id); |
| heading = html_command_text (self, unit_command, 0); |
| |
| text_append_n (&result, ">\n", 2); |
| |
| xasprintf (&class, "%s-heading", class_base); |
| |
| add_string (class, classes); |
| free (class); |
| |
| if (!heading) |
| heading = strdup (""); |
| format_heading_text (self, 0, classes, heading, |
| self->conf->CHAPTER_HEADER_LEVEL.o.integer, |
| 0, 0, 0, &result); |
| destroy_strings_list (classes); |
| |
| free (heading); |
| |
| text_append_n (&result, "\n", 1); |
| |
| text_append (&result, table_of_contents); |
| text_append_n (&result, "</div>\n", 7); |
| |
| free (table_of_contents); |
| return result.text; |
| } |
| } |
| } |
| free (table_of_contents); |
| return 0; |
| } |
| |
| static void |
| contents_shortcontents_in_title (CONVERTER *self, TEXT *result) |
| { |
| if (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 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]; |
| const OPTION *contents_option_ref |
| = get_converter_command_option (self->sorted_options, 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); |
| } |
| } |
| } |
| } |
| } |
| |
| void |
| open_quotation_titlepage_stack (CONVERTER *self, int do_authors_list) |
| { |
| ELEMENT_REFERENCE_STACK_STACK *stack |
| = &self->shared_conversion_state.elements_authors; |
| |
| if (stack->top >= stack->space) |
| { |
| stack->stack |
| = realloc (stack->stack, |
| (stack->space += 5) * sizeof (ELEMENT_REFERENCE_STACK *)); |
| } |
| |
| if (do_authors_list) |
| stack->stack[stack->top] = new_element_reference_stack (); |
| else |
| stack->stack[stack->top] = 0; |
| |
| stack->top++; |
| } |
| |
| static char *maketitle_titlepage_array[] = {"maketitle-titlepage"}; |
| static const STRING_LIST maketitle_titlepage_classes |
| = {maketitle_titlepage_array, 1, 1}; |
| |
| void |
| format_maketitle (CONVERTER *self, TEXT *result) |
| { |
| DOCUMENT_INFO *document_info = get_document_documentinfo (self->document); |
| |
| if (document_info) |
| { |
| ELEMENT *e = new_element (ET_NONE); |
| char *make_title_attribute_class = html_attribute_class (self, "div", |
| &maketitle_titlepage_classes); |
| text_append (result, make_title_attribute_class); |
| free (make_title_attribute_class); |
| text_append_n (result, ">\n", 2); |
| |
| if (document_info->title.number) |
| insert_list_slice_into_contents (e, 0, &document_info->title, |
| 0, document_info->title.number); |
| if (document_info->subtitle.number) |
| insert_list_slice_into_contents (e, e->e.c->contents.number, |
| &document_info->subtitle, |
| 0, document_info->subtitle.number); |
| if (document_info->author.number) |
| insert_list_slice_into_contents (e, e->e.c->contents.number, |
| &document_info->author, |
| 0, document_info->author.number); |
| |
| open_quotation_titlepage_stack (self, 0); |
| html_convert_tree_append (self, e, result, "format maketitle"); |
| destroy_element (e); |
| self->shared_conversion_state.elements_authors.top--; |
| |
| text_append_n (result, "</div>\n", 7); |
| |
| destroy_document_info (document_info); |
| } |
| } |
| /* Convert @titlepage. Falls back to simpletitle. */ |
| static char * |
| html_default_format_titlepage (CONVERTER *self) |
| { |
| int titlepage_text = 0; |
| TEXT result; |
| text_init (&result); |
| text_append (&result, ""); |
| if (self->document->global_commands.titlepage) |
| { |
| open_quotation_titlepage_stack (self, 0); |
| ELEMENT *tmp = new_element (ET_NONE); |
| tmp->e.c->contents = self->document->global_commands.titlepage->e.c->contents; |
| html_convert_tree_append (self, tmp, &result, "convert titlepage"); |
| tmp->e.c->contents.list = 0; |
| destroy_element (tmp); |
| titlepage_text = 1; |
| self->shared_conversion_state.elements_authors.top--; |
| } |
| else if (self->document->global_commands.maketitle) |
| { |
| format_maketitle (self, &result); |
| titlepage_text = 1; |
| } |
| else if (self->simpletitle_tree) |
| { |
| format_simpletitle (self, &result); |
| titlepage_text = 1; |
| } |
| 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 char * |
| format_titlepage (CONVERTER *self) |
| { |
| 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) |
| { |
| return html_default_format_titlepage (self); |
| } |
| else |
| { |
| return call_formatting_function_format_titlepage (self, |
| formatting_reference); |
| } |
| } |
| |
| char * |
| html_default_format_title_titlepage (CONVERTER *self) |
| { |
| if (self->conf->SHOW_TITLE.o.integer > 0) |
| { |
| if (self->conf->USE_TITLEPAGE_FOR_TITLE.o.integer) |
| { |
| 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 strdup (""); |
| } |
| |
| char * |
| html_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); |
| } |
| } |
| |
| |
| |
| /* @-command elements conversion and open functions */ |
| |
| /* this function allows to call a conversion function associated to |
| a COMMAND_CONVERSION different from the ELEMENT and CMD arguments |
| associated command conversion */ |
| static void |
| conversion_function_cmd_conversion (CONVERTER *self, |
| COMMAND_CONVERSION_FUNCTION *command_conversion, |
| const enum command_id cmd, const ELEMENT *element, |
| const HTML_ARGS_FORMATTED *args_formatted, |
| const char *content, TEXT *result) |
| { |
| if (command_conversion->status == FRS_status_internal) |
| { |
| (command_conversion->command_conversion) |
| (self, cmd, element, args_formatted, |
| content, result); |
| } |
| else |
| { |
| FORMATTING_REFERENCE *formatting_reference |
| = command_conversion->formatting_reference; |
| if (formatting_reference->status > 0) |
| call_commands_conversion (self, cmd, formatting_reference, |
| element, args_formatted, content, |
| result); |
| |
| } |
| } |
| |
| static void |
| text_element_conversion (CONVERTER *self, |
| const HTML_NO_ARG_COMMAND_CONVERSION *specification, |
| const enum command_id cmd, |
| TEXT *result) |
| { |
| if (specification->element) |
| { |
| char *attribute_class; |
| |
| STRING_LIST *classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class |
| = html_attribute_class (self, specification->element, classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| text_append_n (result, ">", 1); |
| if (specification->text) |
| text_append (result, specification->text); |
| text_append_n (result, "</", 2); |
| text_append (result, specification->element); |
| text_append_n (result, ">", 1); |
| } |
| else if (specification->text) |
| text_append (result, specification->text); |
| } |
| |
| 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) |
| { |
| /* C 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; |
| const HTML_NO_ARG_COMMAND_CONVERSION *specification; |
| |
| if (html_in_preformatted_context (self) || html_in_math (self)) |
| context = HCC_type_preformatted; |
| else if (html_in_string (self)) |
| context = HCC_type_string; |
| else |
| context = HCC_type_normal; |
| |
| if (html_in_upper_case (self) |
| && html_commands_data[formatted_cmd].upper_case_cmd) |
| { |
| formatted_cmd = html_commands_data[formatted_cmd].upper_case_cmd; |
| } |
| |
| specification |
| = &self->html_no_arg_command_conversion[formatted_cmd] |
| .context_formatting[context]; |
| |
| text_element_conversion (self, specification, formatted_cmd, result); |
| } |
| |
| void |
| 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 (html_in_upper_case (self) |
| && html_commands_data[formatted_cmd].upper_case_cmd) |
| { |
| formatted_cmd = html_commands_data[formatted_cmd].upper_case_cmd; |
| } |
| |
| text_append (result, |
| self->html_no_arg_command_conversion[formatted_cmd] |
| .context_formatting[HCC_type_css_string].text); |
| } |
| |
| void |
| 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 = converter_expand_today (self, &html_cdt_tree); |
| |
| add_tree_to_build (self, today_element); |
| |
| html_convert_tree_append (self, today_element, result, "convert today"); |
| |
| remove_tree_to_build (self, today_element); |
| destroy_element_and_children (today_element); |
| } |
| |
| void |
| 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; |
| |
| /* happens with bogus @-commands without argument, like @strong something */ |
| if (!args_formatted || args_formatted->number <= 0 |
| || !args_formatted->args[0].formatted[AFT_type_normal]) |
| return; |
| |
| if (html_in_string (self)) |
| { |
| text_append (result, args_formatted->args[0].formatted[AFT_type_normal]); |
| return; |
| } |
| |
| if (cmd == CM_kbd) |
| { |
| int code = (element->flags & EF_code); |
| if (code > 0) |
| style_cmd = CM_code; |
| } |
| |
| if (html_in_preformatted_context (self)) |
| formatting_spec |
| = &self->html_style_command_conversion[style_cmd][HCC_type_preformatted]; |
| else |
| formatting_spec |
| = &self->html_style_command_conversion[style_cmd][HCC_type_normal]; |
| |
| if (formatting_spec->element) |
| { |
| char *open; |
| size_t open_len; |
| |
| STRING_LIST *classes = new_string_list (); |
| add_string (builtin_command_name (style_cmd), classes); |
| |
| if (style_cmd != cmd) |
| { |
| char *style_as_cmd; |
| xasprintf (&style_as_cmd, "as-%s-%s", |
| builtin_command_name (style_cmd), |
| builtin_command_name (cmd)); |
| add_string (style_as_cmd, classes); |
| free (style_as_cmd); |
| } |
| |
| if (formatting_spec->quote && self->conf->OPEN_QUOTE_SYMBOL.o.string) |
| text_append (result, self->conf->OPEN_QUOTE_SYMBOL.o.string); |
| |
| open |
| = html_attribute_class (self, formatting_spec->element, classes); |
| open_len = strlen (open); |
| destroy_strings_list (classes); |
| |
| if (open_len > 0) |
| { |
| text_append (result, open); |
| text_append_n (result, ">", 1); |
| } |
| |
| if (open) |
| free (open); |
| |
| text_append (result, args_formatted->args[0].formatted[AFT_type_normal]); |
| |
| if (open_len > 0) |
| { |
| text_append_n (result, "</", 2); |
| text_append (result, formatting_spec->element); |
| text_append_n (result, ">", 1); |
| } |
| |
| if (formatting_spec->quote && self->conf->CLOSE_QUOTE_SYMBOL.o.string) |
| text_append (result, self->conf->CLOSE_QUOTE_SYMBOL.o.string); |
| } |
| else |
| text_append (result, args_formatted->args[0].formatted[AFT_type_normal]); |
| } |
| |
| void |
| 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) |
| { |
| if (args_formatted->args[0].formatted[AFT_type_normal]) |
| text_append (result, |
| args_formatted->args[0].formatted[AFT_type_normal]); |
| } |
| if (!html_in_string (self)) |
| { |
| text_append (result, "<!-- /@w -->"); |
| } |
| } |
| |
| void |
| 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); |
| NAMED_STRING_ELEMENT_LIST *substrings = new_named_string_element_list (); |
| |
| text_append (value_text->e.text, |
| args_formatted->args[0].formatted[AFT_type_monospacestring]); |
| add_element_to_named_string_element_list (substrings, |
| "value", value_text); |
| |
| tree = html_cdt_tree ("@{No value for `{value}'@}", |
| self, substrings, 0); |
| |
| add_tree_to_build (self, tree); |
| html_convert_tree_append (self, tree, result, "Tr missing value"); |
| remove_tree_to_build (self, tree); |
| |
| destroy_element_and_children (tree); |
| |
| destroy_named_string_element_list (substrings); |
| } |
| |
| void |
| 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; |
| const char *text = 0; |
| |
| if (args_formatted) |
| { |
| if (args_formatted->number > 0) |
| { |
| mail = args_formatted->args[0].formatted[AFT_type_url]; |
| mail_string |
| = args_formatted->args[0].formatted[AFT_type_monospacestring]; |
| } |
| |
| if (args_formatted->number > 1 |
| && args_formatted->args[1].formatted[AFT_type_normal]) |
| { |
| text = args_formatted->args[1].formatted[AFT_type_normal]; |
| } |
| } |
| |
| if (!text || !strlen (text)) |
| { |
| text = mail_string; |
| } |
| |
| /* FIXME in Perl unicode spaces are also matched */ |
| if (!mail || mail[strspn (mail, whitespace_chars)] == '\0') |
| { |
| if (text) |
| text_append (result, text); |
| return; |
| } |
| |
| if (html_in_string (self)) |
| { |
| text_printf (result, "%s (%s)", mail_string, text); |
| } |
| else |
| { |
| char *attribute_class; |
| char *protected_mailto; |
| char *mailto; |
| |
| STRING_LIST *classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "a", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| xasprintf (&mailto, "mailto:%s", mail); |
| protected_mailto = url_protect_url_text (self, mailto); |
| free (mailto); |
| |
| text_printf (result, " href=\"%s\">%s</a>", protected_mailto, text); |
| free (protected_mailto); |
| } |
| } |
| |
| void |
| 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; |
| const char *explained_arg = 0; |
| char *normalized_type = 0; |
| const char *explanation_string = 0; |
| const char *explanation_result = 0; |
| EXPLAINED_COMMAND_TYPE_LIST *type_explanations |
| = &self->shared_conversion_state.explained_commands; |
| |
| if (element->e.c->contents.number > 0 |
| && element->e.c->contents.list[0]->e.c->contents.number > 0) |
| { |
| normalized_type = convert_to_identifier (element->e.c->contents.list[0]); |
| } |
| else |
| normalized_type = strdup (""); |
| |
| if (args_formatted && args_formatted->number > 1) |
| { |
| if (args_formatted->args[1].formatted[AFT_type_string]) |
| { |
| explanation_string |
| = args_formatted->args[1].formatted[AFT_type_string]; |
| |
| if (explanation_string[strspn |
| (explanation_string, whitespace_chars)] != '\0') |
| { |
| register_explained_command_string (type_explanations, |
| cmd, normalized_type, explanation_string); |
| } |
| else |
| explanation_string = 0; |
| } |
| if (args_formatted->args[1].formatted[AFT_type_normal]) |
| explanation_result = args_formatted->args[1].formatted[AFT_type_normal]; |
| } |
| |
| if (!explanation_string) |
| { |
| EXPLAINED_COMMAND_TYPE *type_explanation |
| = find_explained_command_string (type_explanations, |
| cmd, normalized_type); |
| if (type_explanation) |
| explanation_string = type_explanation->explanation; |
| } |
| |
| free (normalized_type); |
| |
| if (explanation_result) |
| { |
| text_init (&explained_string); |
| text_result = &explained_string; |
| } |
| else |
| text_result = result; |
| |
| if (args_formatted && args_formatted->number > 0 && |
| args_formatted->args[0].formatted[AFT_type_normal]) |
| explained_arg = args_formatted->args[0].formatted[AFT_type_normal]; |
| else |
| explained_arg = ""; |
| |
| if (!html_in_string (self)) |
| { |
| char *attribute_class; |
| |
| STRING_LIST *classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "abbr", classes); |
| destroy_strings_list (classes); |
| text_append (text_result, attribute_class); |
| free (attribute_class); |
| |
| if (explanation_string) |
| text_printf (text_result, " title=\"%s\"", explanation_string); |
| text_append_n (text_result, ">", 1); |
| text_append (text_result, explained_arg); |
| text_append_n (text_result, "</abbr>", 7); |
| } |
| else |
| text_append (text_result, explained_arg); |
| |
| if (explanation_result) |
| { |
| char *context_str; |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| ELEMENT *explained_string_element = new_text_element (ET__converted); |
| ELEMENT *explanation_result_element = new_text_element (ET__converted); |
| ELEMENT *tree; |
| |
| text_append (explained_string_element->e.text, text_result->text); |
| free (text_result->text); |
| text_append (explanation_result_element->e.text, explanation_result); |
| |
| add_element_to_named_string_element_list (substrings, |
| "explained_string", explained_string_element); |
| add_element_to_named_string_element_list (substrings, |
| "explanation", explanation_result_element); |
| tree = html_cdt_tree ("{explained_string} ({explanation})", |
| self, substrings, 0); |
| destroy_named_string_element_list (substrings); |
| |
| xasprintf (&context_str, "convert explained %s", |
| builtin_command_name (cmd)); |
| add_tree_to_build (self, tree); |
| html_convert_tree_append (self, tree, result, context_str); |
| remove_tree_to_build (self, tree); |
| free (context_str); |
| /* should destroy explained_*_element */ |
| destroy_element_and_children (tree); |
| } |
| } |
| |
| void |
| 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_multi_expanded_region (self) && !html_in_string (self)) |
| { |
| const char *id = html_command_id (self, element); |
| if (id && strlen (id)) |
| { |
| format_separate_anchor (self, id, builtin_command_name (cmd), |
| result); |
| } |
| } |
| } |
| |
| static int |
| compare_footnote_id (const void *a, const void *b) |
| { |
| const FOOTNOTE_ID_NUMBER *fid_a = (const FOOTNOTE_ID_NUMBER *) a; |
| const FOOTNOTE_ID_NUMBER *fid_b = (const FOOTNOTE_ID_NUMBER *) b; |
| |
| return strcmp (fid_a->footnote_id, fid_b->footnote_id); |
| } |
| |
| FOOTNOTE_ID_NUMBER * |
| find_footnote_id_number (const CONVERTER *self, const char *footnote_id) |
| { |
| const ELEMENT_LIST *global_footnotes |
| = &self->document->global_commands.footnotes; |
| |
| FOOTNOTE_ID_NUMBER *result = 0; |
| static FOOTNOTE_ID_NUMBER searched_footnote_id; |
| searched_footnote_id.footnote_id = footnote_id; |
| if (global_footnotes->number == 0) |
| { |
| char *msg; |
| xasprintf (&msg, "no footnotes, searching for '%s'\n", footnote_id); |
| fatal (msg); |
| free (msg); |
| } |
| |
| result = (FOOTNOTE_ID_NUMBER *) bsearch (&searched_footnote_id, |
| self->shared_conversion_state.footnote_id_numbers, |
| global_footnotes->number, sizeof (FOOTNOTE_ID_NUMBER), |
| compare_footnote_id); |
| return result; |
| } |
| |
| void |
| 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; |
| const char *footnote_id; |
| const char *footnote_docid; |
| char *footid; |
| char *docid; |
| int multiple_expanded_footnote = 0; |
| const char *multi_expanded_region; |
| int foot_num; |
| char *footnote_href; |
| char *attribute_class; |
| STRING_LIST *classes; |
| |
| self->shared_conversion_state.footnote_number++; |
| foot_num = self->shared_conversion_state.footnote_number; |
| |
| if (self->conf->NUMBER_FOOTNOTES.o.integer > 0) |
| xasprintf (&footnote_mark, "%d", foot_num); |
| else if (self->conf->NO_NUMBER_FOOTNOTE_SYMBOL.o.string) |
| footnote_mark = strdup (self->conf->NO_NUMBER_FOOTNOTE_SYMBOL.o.string); |
| else |
| footnote_mark = strdup (""); |
| |
| if (html_in_string (self)) |
| { |
| text_printf (result, "(%s)", footnote_mark); |
| free (footnote_mark); |
| return; |
| } |
| |
| footnote_id = html_command_id (self, element); |
| |
| /* happens for bogus footnotes */ |
| if (!footnote_id) |
| { |
| free (footnote_mark); |
| return; |
| } |
| |
| /* ID for linking back to the main text from the footnote. */ |
| footnote_docid = html_footnote_location_target (self, element); |
| |
| multi_expanded_region = html_multi_expanded_region (self); |
| if (multi_expanded_region) |
| { |
| /* to avoid duplicate names, use a prefix that cannot happen in anchors */ |
| xasprintf (&footid, "%s%s_%s_%d", target_prefix, multi_expanded_region, |
| footnote_id, foot_num); |
| xasprintf (&docid, "%s%s_%s_%d", target_prefix, multi_expanded_region, |
| footnote_docid, foot_num); |
| } |
| else |
| { |
| FOOTNOTE_ID_NUMBER *footnote_id_number |
| = find_footnote_id_number (self, footnote_id); |
| if (!footnote_id_number) |
| fatal ("footnote_id not found"); |
| if (!footnote_id_number->number) |
| { |
| footid = strdup (footnote_id); |
| docid = strdup (footnote_docid); |
| } |
| else |
| { |
| /* This should rarely happen, except for @footnote in @copying and |
| multiple @insertcopying... |
| Here it is not checked that there is no clash with another anchor. |
| However, unless there are more than 1000 footnotes this should not |
| happen at all, and even in that case it is very unlikely. |
| */ |
| xasprintf (&footid, "%s_%d", footnote_id, foot_num); |
| xasprintf (&docid, "%s_%d", footnote_docid, foot_num); |
| multiple_expanded_footnote = 1; |
| } |
| footnote_id_number->number++; |
| } |
| |
| if ((!self->conf->footnotestyle.o.string |
| || strcmp (self->conf->footnotestyle.o.string, "separate")) |
| && (multi_expanded_region || multiple_expanded_footnote)) |
| { |
| /* if the footnote appears multiple times, command_href() will select |
| one, but it may not be the one expanded at the location currently |
| formatted (in general the first one, but it depends if it is in a |
| tree element or not, for instance in @titlepage). |
| With footnotestyle end, considering that the footnote is in the same file |
| has a better chance of being correct. |
| */ |
| xasprintf (&footnote_href, "#%s", footid); |
| } |
| else |
| footnote_href = html_command_href (self, element, 0, 0, footid); |
| |
| html_register_footnote (self, element, footid, docid, foot_num, |
| self->current_filename.filename, |
| multi_expanded_region); |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "a", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| text_printf (result, " id=\"%s\" href=\"%s\">", docid, footnote_href); |
| |
| if (html_in_preformatted_context (self)) |
| text_printf (result, "(%s)", footnote_mark); |
| else |
| text_printf (result, "<sup>%s</sup>", footnote_mark); |
| |
| text_append_n (result, "</a>", 4); |
| |
| free (footnote_href); |
| free (footnote_mark); |
| free (footid); |
| free (docid); |
| } |
| |
| void |
| 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; |
| const char *text = 0; |
| const char *replacement; |
| char *protected_url; |
| char *attribute_class; |
| STRING_LIST *classes; |
| |
| if (!args_formatted) |
| return; |
| |
| if (args_formatted->number > 0 |
| && args_formatted->args[0].formatted[AFT_type_url] |
| && args_formatted->args[0].formatted[AFT_type_monospacestring]) |
| { |
| url = args_formatted->args[0].formatted[AFT_type_url]; |
| url_string = args_formatted->args[0].formatted[AFT_type_monospacestring]; |
| } |
| if (args_formatted->number > 1 |
| && args_formatted->args[1].formatted[AFT_type_normal]) |
| { |
| text = args_formatted->args[1].formatted[AFT_type_normal]; |
| } |
| if (args_formatted->number > 2 |
| && args_formatted->args[2].formatted[AFT_type_normal]) |
| { |
| replacement |
| = args_formatted->args[2].formatted[AFT_type_normal]; |
| if (strlen (replacement)) |
| text = replacement; |
| } |
| if ((!text || !strlen (text)) && url_string) |
| text = url_string; |
| |
| if (!url || !strlen (url)) |
| { |
| if (text) |
| text_append (result, text); |
| return; |
| } |
| |
| if (html_in_string (self)) |
| { |
| text_printf (result, "%s (%s)", text, url_string); |
| return; |
| } |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "a", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| protected_url = url_protect_url_text (self, url); |
| text_printf (result, " href=\"%s\">%s</a>", protected_url, text); |
| free (protected_url); |
| } |
| |
| static const char *image_files_extensions[] = { |
| ".png", ".jpg", ".jpeg", ".gif", 0 |
| }; |
| |
| /* return, IMAGE_PATH and IMAGE_PATH_ENCODING to be freed by caller */ |
| static char * |
| find_image_extension_file (CONVERTER *self, const ELEMENT *element, |
| const char *image_basefile, |
| const char *extension, |
| char **image_path, |
| char **image_path_encoding) |
| { |
| char *image_file; |
| char *input_file_encoding; |
| char *file_name; |
| char *located_image_path; |
| |
| xasprintf (&image_file, "%s%s", image_basefile, extension); |
| file_name = converter_encoded_input_file_name (self->conf, |
| &self->document->global_info, |
| image_file, 0, &input_file_encoding, |
| &element->e.c->source_info); |
| |
| located_image_path = locate_include_file (file_name, |
| self->conf->INCLUDE_DIRECTORIES.o.strlist); |
| free (file_name); |
| |
| if (located_image_path) |
| { |
| *image_path_encoding = input_file_encoding; |
| *image_path = located_image_path; |
| return image_file; |
| } |
| |
| free (image_file); |
| free (input_file_encoding); |
| return 0; |
| } |
| |
| typedef struct IMAGE_FILE_LOCATION_INFO { |
| char *image_file; |
| char *image_extension; |
| char *image_path; |
| char *image_path_encoding; |
| } IMAGE_FILE_LOCATION_INFO; |
| |
| void |
| free_image_file_location_info (IMAGE_FILE_LOCATION_INFO *location_info) |
| { |
| free (location_info->image_file); |
| free (location_info->image_extension); |
| free (location_info->image_path); |
| free (location_info->image_path_encoding); |
| } |
| |
| IMAGE_FILE_LOCATION_INFO * |
| html_image_file_location_name (CONVERTER *self, const enum command_id cmd, |
| const ELEMENT *element, const char *image_basefile, |
| const HTML_ARGS_FORMATTED *args_formatted) |
| { |
| char *image_file = 0; |
| const char *extension = 0; |
| |
| IMAGE_FILE_LOCATION_INFO *result = (IMAGE_FILE_LOCATION_INFO *) |
| malloc (sizeof (IMAGE_FILE_LOCATION_INFO)); |
| |
| if (args_formatted->number > 4 |
| && args_formatted->args[4].formatted[AFT_type_filenametext]) |
| { |
| extension |
| = args_formatted->args[4].formatted[AFT_type_filenametext]; |
| image_file |
| = find_image_extension_file (self, element, image_basefile, |
| extension, &result->image_path, |
| &result->image_path_encoding); |
| if (!image_file) |
| { |
| char *dot_ext; |
| xasprintf (&dot_ext, ".%s", extension); |
| image_file |
| = find_image_extension_file (self, element, image_basefile, |
| dot_ext, &result->image_path, |
| &result->image_path_encoding); |
| if (image_file) |
| result->image_extension = dot_ext; |
| else |
| free (dot_ext); |
| } |
| else |
| result->image_extension = strdup (extension); |
| } |
| |
| if (!image_file) |
| { |
| int i; |
| for (i = 0; image_files_extensions[i]; i++) |
| { |
| image_file |
| = find_image_extension_file (self, element, image_basefile, |
| image_files_extensions[i], &result->image_path, |
| &result->image_path_encoding); |
| if (image_file) |
| { |
| result->image_extension = strdup (image_files_extensions[i]); |
| break; |
| } |
| } |
| } |
| |
| if (!image_file) |
| { |
| result->image_path = 0; |
| result->image_path_encoding = 0; |
| if (extension) |
| { |
| xasprintf (&result->image_file, "%s%s", image_basefile, |
| extension); |
| result->image_extension = strdup (extension); |
| } |
| else |
| { |
| xasprintf (&result->image_file, "%s.jpg", image_basefile); |
| result->image_extension = strdup (".jpg"); |
| } |
| } |
| else |
| result->image_file = image_file; |
| |
| return result; |
| } |
| |
| void |
| 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 |
| && args_formatted->args[0].formatted[AFT_type_filenametext] |
| && strlen (args_formatted->args[0].formatted[AFT_type_filenametext])) |
| { |
| IMAGE_FILE_LOCATION_INFO *image_path_info; |
| const char *image_basefile |
| = args_formatted->args[0].formatted[AFT_type_filenametext]; |
| const char *basefile_string = 0; |
| char *image_file; |
| char *attribute_class; |
| char *protected_image_file; |
| STRING_LIST *classes; |
| const char *alt_string; |
| |
| if (args_formatted->args[0].formatted[AFT_type_monospacestring]) |
| basefile_string |
| = args_formatted->args[0].formatted[AFT_type_monospacestring]; |
| |
| if (html_in_string (self)) |
| { |
| if (basefile_string) |
| text_append (result, basefile_string); |
| return; |
| } |
| |
| image_path_info = html_image_file_location_name (self, cmd, element, |
| image_basefile, |
| args_formatted); |
| image_file = image_path_info->image_file; |
| image_path_info->image_file = 0; |
| |
| if (!image_path_info->image_path) |
| { |
| /* it would have been relevant to output the message only if |
| if not ($self->in_multiple_conversions()) |
| However, @image formatted in multiple conversions context should be |
| rare out of test suites (and probably always incorrect), so we avoid |
| complexity and slowdown. We still check that source_info is set, if |
| not it should be a copy, therefore there is no need for error |
| output, especially without line information. */ |
| if (element->e.c->source_info.line_nr) |
| { |
| message_list_command_warn (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| element, 0, "@image file `%s' (for HTML) not found, using `%s'", |
| image_basefile, image_file); |
| } |
| } |
| free_image_file_location_info (image_path_info); |
| free (image_path_info); |
| |
| if (self->conf->IMAGE_LINK_PREFIX.o.string) |
| { |
| char *tmp; |
| xasprintf (&tmp, "%s%s", self->conf->IMAGE_LINK_PREFIX.o.string, |
| image_file); |
| free (image_file); |
| image_file = tmp; |
| } |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "img", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| protected_image_file = url_protect_file_text (self, image_file); |
| free (image_file); |
| |
| if (args_formatted->number > 3 |
| && args_formatted->args[3].formatted[AFT_type_string] |
| && strlen (args_formatted->args[3].formatted[AFT_type_string])) |
| alt_string = args_formatted->args[3].formatted[AFT_type_string]; |
| else if (basefile_string) |
| alt_string = basefile_string; |
| else |
| alt_string = ""; |
| |
| text_printf (result, " src=\"%s\" alt=\"%s\"", protected_image_file, |
| alt_string); |
| |
| free (protected_image_file); |
| html_close_lone_element (self, result); |
| } |
| } |
| |
| void |
| 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; |
| const char *arg; |
| |
| if (!args_formatted || args_formatted->number <= 0 |
| || !args_formatted->args[0].formatted[AFT_type_normal]) |
| return; |
| |
| arg = args_formatted->args[0].formatted[AFT_type_normal]; |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| if (self->conf->HTML_MATH.o.string |
| && !strcmp (self->conf->HTML_MATH.o.string, "mathjax")) |
| { |
| html_register_file_information (self, "mathjax", 1); |
| add_string ("tex2jax_process", classes); |
| attribute_class = html_attribute_class (self, "em", classes); |
| text_append (result, attribute_class); |
| text_printf (result, ">\\(%s\\)</em>", arg); |
| goto out; |
| } |
| |
| attribute_class = html_attribute_class (self, "em", classes); |
| text_append (result, attribute_class); |
| text_printf (result, ">%s</em>", arg); |
| |
| out: |
| destroy_strings_list (classes); |
| free (attribute_class); |
| } |
| |
| /* return string to be freed by caller */ |
| static char * |
| set_case_if_only_word_characters (const char *text, int set_case) |
| { |
| char *text_set; |
| |
| if (set_case) |
| { |
| int str_len = strlen (text); |
| if (str_len != 1 || !isascii_alnum (*text)) |
| { |
| int w_len = word_bytes_len_multibyte (text); |
| /* not only alphanumeric characters, do not change case */ |
| if (w_len != str_len) |
| set_case = 0; |
| } |
| } |
| |
| if (set_case) |
| text_set = to_upper_or_lower_multibyte (text, set_case); |
| else |
| text_set = strdup (text); |
| |
| return text_set; |
| } |
| |
| char * |
| html_accent_entities_html_accent_internal (CONVERTER *self, const char *text, |
| const ELEMENT *element, int set_case, |
| int use_numeric_entities) |
| { |
| char *text_set = set_case_if_only_word_characters (text, set_case); |
| |
| /* do not return a dotless i or j as such if it is further composed |
| with an accented letter, return the letter as is */ |
| if (element->e.c->cmd == CM_dotless |
| /* corresponds to perl exists unicode_accented_letters{accent}->{text} */ |
| && (!strcmp (text_set, "i") || !strcmp (text_set, "j"))) |
| { |
| if (element->e.c->parent && element->e.c->parent->e.c->parent |
| && element->e.c->parent->e.c->parent->e.c->cmd) |
| { |
| enum command_id p_cmd |
| = element_builtin_cmd (element->e.c->parent->e.c->parent); |
| if (builtin_command_data[p_cmd].flags & CF_accent |
| && p_cmd != CM_tieaccent) |
| { |
| return text_set; |
| } |
| } |
| } |
| |
| if (use_numeric_entities) |
| { |
| char *formatted_accent |
| = xml_numeric_entity_accent (element->e.c->cmd, text_set); |
| if (formatted_accent) |
| { |
| free (text_set); |
| return formatted_accent; |
| } |
| } |
| else |
| { |
| char *formatted_accent; |
| if (strlen (text_set) == 1 && isascii_alpha (*text_set) |
| && self->accent_entities[element->e.c->cmd].entity |
| && self->accent_entities[element->e.c->cmd].characters |
| && strlen (self->accent_entities[element->e.c->cmd].characters) |
| && strrchr (self->accent_entities[element->e.c->cmd].characters, |
| *text_set)) |
| { |
| xasprintf (&formatted_accent, "&%s%s;", text_set, |
| self->accent_entities[element->e.c->cmd].entity); |
| free (text_set); |
| return formatted_accent; |
| } |
| formatted_accent = xml_numeric_entity_accent (element->e.c->cmd, |
| text_set); |
| if (formatted_accent) |
| { |
| free (text_set); |
| return formatted_accent; |
| } |
| } |
| /* should only be the case of @dotless, as other commands have a diacritic |
| associated, and only if the argument is not i nor j. */ |
| return text_set; |
| } |
| |
| char * |
| html_accent_entities_html_accent (CONVERTER *self, const char *text, |
| const ELEMENT *element, int index_in_stack, |
| const ELEMENT_STACK *stack, int set_case) |
| { |
| return html_accent_entities_html_accent_internal (self, text, |
| element, set_case, 0); |
| } |
| |
| char * |
| html_accent_entities_numeric_entities_accent (CONVERTER *self, |
| const char *text, const ELEMENT *element, int index_in_stack, |
| const ELEMENT_STACK *stack, int set_case) |
| { |
| return html_accent_entities_html_accent_internal (self, text, |
| element, set_case, 1); |
| } |
| |
| void |
| 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, |
| const ELEMENT *element, int index_in_stack, |
| const ELEMENT_STACK *stack, int set_case); |
| |
| int output_encoded_characters = (self->conf->OUTPUT_CHARACTERS.o.integer > 0); |
| |
| if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0) |
| format_accents = &html_accent_entities_numeric_entities_accent; |
| else |
| format_accents = &html_accent_entities_html_accent; |
| |
| accent_text = convert_accents (self, element, &html_convert_tree, |
| format_accents, output_encoded_characters, |
| html_in_upper_case (self)); |
| |
| text_append (result, accent_text); |
| free (accent_text); |
| } |
| |
| char * |
| css_string_accent (CONVERTER *self, const char *text, |
| const ELEMENT *element, int index_in_stack, |
| const ELEMENT_STACK *stack, int set_case) |
| { |
| char *text_set = set_case_if_only_word_characters (text, set_case); |
| |
| if (element->e.c->cmd == CM_dotless) |
| { |
| /* corresponds in perl, and for dotless, to |
| Texinfo::UnicodeData::unicode_accented_letters{$accent}->{$text} */ |
| if (!strcmp (text_set, "i")) |
| { |
| free (text_set); |
| return strdup ("\\" "0131" " "); |
| } |
| else if (!strcmp (text_set, "j")) |
| { |
| free (text_set); |
| return strdup ("\\" "0237" " "); |
| } |
| } |
| |
| if (unicode_diacritics[element->e.c->cmd].text) |
| { |
| char *accent_and_diacritic; |
| char *normalized_accent_text; |
| static TEXT accented_text; |
| text_init (&accented_text); |
| if (element->e.c->cmd == CM_tieaccent) |
| { |
| /* tieaccent diacritic is naturally and correctly composed |
| between two characters */ |
| /* 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 = html_after_escaped_characters (text_set); |
| char *next_text = 0; |
| ucs4_t first_char; |
| const uint8_t *next = 0; |
| uint8_t *encoded_u8 = 0; |
| |
| if (!p) |
| { |
| /* check if a character matches */ |
| encoded_u8 = utf8_from_string (text); |
| next = u8_next (&first_char, encoded_u8); |
| if (next && (uc_is_general_category (first_char, UC_CATEGORY_L) |
| /* ASCII digits */ |
| || (first_char >= 0x0030 && first_char <= 0x0039))) |
| { |
| next_text = string_from_utf8 (next); |
| } |
| } |
| else |
| { |
| next_text = p; |
| } |
| |
| if (next_text) |
| { |
| ucs4_t second_char; |
| const char *q = html_after_escaped_characters (next_text); |
| |
| if (!q) |
| { |
| const uint8_t *remaining; |
| if (!next) |
| {/* next_text should be equal to p */ |
| encoded_u8 = utf8_from_string (p); |
| next = encoded_u8; |
| } |
| remaining = u8_next (&second_char, next); |
| if (remaining |
| && (uc_is_general_category (second_char, UC_CATEGORY_L) |
| /* ASCII digits */ |
| || (second_char >= 0x0030 && second_char <= 0x0039))) |
| { |
| /* next_text remains as the text to add after |
| the diacritic */ |
| } |
| else |
| { |
| if (!p) |
| free (next_text); |
| next_text = 0; |
| } |
| } |
| |
| if (next_text) |
| { |
| /* add the first character or escaped text */ |
| if (!p) |
| { |
| char *first_char_text; |
| uint8_t *first_char_u8 = malloc (7 * sizeof (uint8_t)); |
| int first_char_len |
| = u8_uctomb (first_char_u8, first_char, 6); |
| if (first_char_len < 0) |
| fatal ("u8_uctomb returns negative value"); |
| first_char_u8[first_char_len] = 0; |
| first_char_text = string_from_utf8 (first_char_u8); |
| free (first_char_u8); |
| text_append (&accented_text, first_char_text); |
| free (first_char_text); |
| } |
| else |
| text_append_n (&accented_text, text_set, p - text_set); |
| |
| /* add the tie accent */ |
| text_printf (&accented_text, "\\%s ", |
| unicode_diacritics[element->e.c->cmd].hex_codepoint); |
| /* add the remaining, second character or escaped text |
| and everything else after (which is in general invalid |
| but we do not care) */ |
| text_append (&accented_text, next_text); |
| } |
| } |
| free (encoded_u8); |
| if (!p) |
| free (next_text); |
| if (accented_text.end > 0) |
| { |
| free (text_set); |
| return accented_text.text; |
| } |
| } |
| |
| /* case of text and diacritic (including fallback for invalid tie |
| accent) */ |
| /* check if the normalization leads to merging text and diacritic, |
| if yes use the merged character, if not output text and diacitic |
| to be set up for composition */ |
| xasprintf (&accent_and_diacritic, "%s%s", |
| text, unicode_diacritics[element->e.c->cmd].text); |
| normalized_accent_text = normalize_NFC (accent_and_diacritic); |
| free (accent_and_diacritic); |
| /* check if the normalization led to merging text and diacritic |
| as one character. If not, the leading text remains, this |
| is what the comparison checks */ |
| if (!strncmp (normalized_accent_text, text, strlen (text))) |
| { |
| /* no normalization as one character, output text and diacritic |
| such that they could be composed */ |
| text_append (&accented_text, text); |
| text_printf (&accented_text, "\\%s ", |
| unicode_diacritics[element->e.c->cmd].hex_codepoint); |
| } |
| else |
| { |
| /* determine the hexadecimal unicode point of the normalized |
| character to output in the format expected in CSS strings */ |
| char *next_text; |
| uint8_t *encoded_u8 = utf8_from_string (normalized_accent_text); |
| ucs4_t first_char; |
| const uint8_t *next = u8_next (&first_char, encoded_u8); |
| text_printf (&accented_text, "\\%04lX ", first_char); |
| next_text = string_from_utf8 (next); |
| free (encoded_u8); |
| text_append (&accented_text, next_text); |
| free (next_text); |
| } |
| free (normalized_accent_text); |
| free (text_set); |
| return accented_text.text; |
| } |
| |
| /* There are diacritics for every accent command except for dotless. |
| We should only get there with dotless if the argument is not recognized. |
| */ |
| return text_set; |
| } |
| |
| void |
| 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, |
| const ELEMENT *element, int index_in_stack, |
| const ELEMENT_STACK *stack, int set_case); |
| |
| int output_encoded_characters = (self->conf->OUTPUT_CHARACTERS.o.integer > 0); |
| |
| format_accents = &css_string_accent; |
| |
| accent_text = convert_accents (self, element, &html_convert_tree, |
| format_accents, output_encoded_characters, |
| html_in_upper_case (self)); |
| |
| text_append (result, accent_text); |
| free (accent_text); |
| } |
| |
| void |
| 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 |
| || !args_formatted->args[0].formatted[AFT_type_normal]) |
| return; |
| |
| if (self->conf->OPEN_QUOTE_SYMBOL.o.string) |
| text_append (result, self->conf->OPEN_QUOTE_SYMBOL.o.string); |
| |
| if (!html_in_string (self)) |
| { |
| char *attribute_class; |
| |
| STRING_LIST *classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "code", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| text_append_n (result, ">", 1); |
| |
| text_append (result, args_formatted->args[0].formatted[AFT_type_normal]); |
| text_append_n (result, "</code>", 7); |
| } |
| else |
| text_append (result, args_formatted->args[0].formatted[AFT_type_normal]); |
| |
| if (self->conf->CLOSE_QUOTE_SYMBOL.o.string) |
| text_append (result, self->conf->CLOSE_QUOTE_SYMBOL.o.string); |
| } |
| |
| void |
| 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] |
| && strlen (args_formatted->args[0].formatted[AFT_type_normal])) |
| { |
| STRING_LIST *classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| format_heading_text (self, cmd, classes, |
| args_formatted->args[0].formatted[AFT_type_normal], |
| 0, 0, 0, 0, result); |
| destroy_strings_list (classes); |
| } |
| } |
| |
| void |
| 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] |
| && strlen (args_formatted->args[0].formatted[AFT_type_normal])) |
| { |
| text_printf (result, "&#x%s;", |
| args_formatted->args[0].formatted[AFT_type_normal]); |
| } |
| } |
| |
| static char *mini_toc_array[] = {"mini-toc"}; |
| static const STRING_LIST mini_toc_classes = {mini_toc_array, 1, 1}; |
| |
| /* Output a list of the nodes immediately below this one */ |
| void |
| mini_toc_internal (CONVERTER *self, const SECTION_RELATIONS *section_relations, |
| TEXT *result) |
| { |
| int entry_index = 0; |
| const SECTION_RELATIONS_LIST *section_children = 0; |
| |
| if (section_relations) |
| section_children = section_relations->section_children; |
| |
| if (section_children && section_children->number > 0) |
| { |
| char *attribute_class; |
| size_t i; |
| |
| attribute_class = html_attribute_class (self, "ul", &mini_toc_classes); |
| |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">\n", 2); |
| |
| for (i = 0; i < section_children->number; i++) |
| { |
| const ELEMENT *section = section_children->list[i]->element; |
| char *text = html_command_text (self, section, HTT_text_nonumber); |
| char *accesskey; |
| char *href; |
| |
| /* could happen with empty sectioning command */ |
| if (!text || !strlen(text)) |
| continue; |
| |
| href = html_command_href (self, section, 0, 0, 0); |
| |
| entry_index++; |
| |
| if (self->conf->USE_ACCESSKEY.o.integer > 0 && entry_index < 10) |
| { |
| xasprintf (&accesskey, " accesskey=\"%d\"", entry_index); |
| } |
| else |
| accesskey = strdup (""); |
| |
| if (href) |
| { |
| text_printf (result, "<li><a href=\"%s\"%s>%s</a>", |
| href, accesskey, text); |
| } |
| else |
| text_printf (result, "<li>%s", text); |
| |
| text_append_n (result, "</li>\n", 6); |
| |
| free (text); |
| free (href); |
| free (accesskey); |
| } |
| text_append_n (result, "</ul>\n", 6); |
| } |
| } |
| |
| void |
| 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; |
| TEXT element_header; |
| TEXT toc_or_mini_toc_or_auto_menu; |
| enum command_id level_corrected_cmd; |
| int status; |
| char *heading; |
| int heading_level = -1; |
| int do_heading; |
| const char *heading_id = 0; |
| char *level_set_class = 0; |
| const NODE_RELATIONS *node_relations = 0; |
| const SECTION_RELATIONS *section_relations = 0; |
| |
| const ELEMENT *opening_section = 0; |
| enum command_id level_corrected_opening_section_cmd = 0; |
| |
| enum command_id data_cmd = element_builtin_data_cmd (element); |
| unsigned long flags = builtin_command_data[data_cmd].flags; |
| |
| /* No situation where this could happen */ |
| if (html_in_string (self)) |
| { |
| if (element->e.c->cmd != CM_node) |
| { |
| char *heading = html_command_text (self, element, HTT_string); |
| text_append (result, heading); |
| text_append_n (result, "\n", 1); |
| free (heading); |
| } |
| if (content) |
| text_append (result, content); |
| return; |
| } |
| |
| element_id = html_command_id (self, element); |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| char *root_heading_texi = root_heading_command_to_texinfo (element); |
| fprintf (stderr, "CONVERT elt heading %s\n", root_heading_texi); |
| free (root_heading_texi); |
| } |
| |
| if (flags & CF_root) |
| { |
| int status; |
| if (cmd == CM_node) |
| { |
| size_t node_number |
| = lookup_extra_integer (element, AI_key_node_number, &status); |
| if (node_number && self->document) |
| { |
| node_relations |
| = self->document->nodes_list.list[node_number -1]; |
| } |
| } |
| else if (self->document) |
| { |
| size_t section_number |
| = lookup_extra_integer (element, |
| AI_key_section_number, &status); |
| section_relations |
| = self->document->sections_list.list[section_number -1]; |
| } |
| |
| /* All the root commands are associated to an output unit, the condition |
| on associated_unit is always true. */ |
| if (element->e.c->associated_unit) |
| output_unit = element->e.c->associated_unit; |
| } |
| |
| text_init (&element_header); |
| text_append (&element_header, ""); |
| if (output_unit) |
| format_element_header (self, element_command_name (element), element, |
| output_unit, &element_header); |
| |
| text_init (&toc_or_mini_toc_or_auto_menu); |
| text_append (&toc_or_mini_toc_or_auto_menu, ""); |
| if (element->e.c->cmd == CM_top |
| && self->conf->CONTENTS_OUTPUT_LOCATION.o.string |
| && !strcmp (self->conf->CONTENTS_OUTPUT_LOCATION.o.string, "after_top") |
| && 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]; |
| const OPTION *contents_option_ref |
| = get_converter_command_option (self->sorted_options, 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 (&toc_or_mini_toc_or_auto_menu, contents_text); |
| free (contents_text); |
| } |
| } |
| } |
| } |
| |
| if (toc_or_mini_toc_or_auto_menu.end <= 0 |
| && self->conf->FORMAT_MENU.o.string |
| && section_relations) |
| { |
| if (!strcmp (self->conf->FORMAT_MENU.o.string, "sectiontoc")) |
| { |
| mini_toc_internal (self, section_relations, |
| &toc_or_mini_toc_or_auto_menu); |
| } |
| else |
| { |
| int format_menu = 0; |
| if (!strcmp (self->conf->FORMAT_MENU.o.string, "menu")) |
| format_menu = 1; |
| else if (!strcmp (self->conf->FORMAT_MENU.o.string, |
| "menu_no_detailmenu")) |
| format_menu = 2; |
| if (format_menu) |
| { |
| if (section_relations->associated_node) |
| { |
| const NODE_RELATIONS *associated_node_relations |
| = section_relations->associated_node; |
| const NODE_RELATIONS_LIST *nodes_list |
| = &self->document->nodes_list; |
| /* arguments_line type element */ |
| const ELEMENT *arguments_line |
| = associated_node_relations->element->e.c->contents.list[0]; |
| int automatic_directions |
| = (arguments_line->e.c->contents.number <= 1); |
| |
| const CONST_ELEMENT_LIST *menus |
| = associated_node_relations->menus; |
| if (!menus && automatic_directions) |
| { |
| ELEMENT *menu_node; |
| |
| if (format_menu == 1) |
| menu_node |
| = new_complete_menu_master_menu (&self->error_messages, |
| self->conf, |
| self->current_lang_translations, |
| &self->document->identifiers_target, |
| nodes_list, associated_node_relations); |
| else |
| /* menu_no_detailmenu */ |
| menu_node |
| = new_complete_node_menu (associated_node_relations, |
| self->document, |
| self->current_lang_translations, |
| self->conf->DEBUG.o.integer, 0); |
| if (menu_node) |
| { |
| add_tree_to_build (self, menu_node); |
| html_convert_tree_append (self, menu_node, |
| &toc_or_mini_toc_or_auto_menu, |
| "master menu"); |
| remove_tree_to_build (self, menu_node); |
| /* there are only new or copied elements in the menu */ |
| destroy_element_and_children (menu_node); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0 |
| && builtin_command_data[cmd].flags & CF_root) |
| { |
| int in_skipped_node_top |
| = self->shared_conversion_state.in_skipped_node_top; |
| |
| if (in_skipped_node_top == 1) |
| { |
| format_separate_anchor (self, element_id, |
| builtin_command_name (cmd), result); |
| text_append (result, element_header.text); |
| free (element_header.text); |
| text_append (result, toc_or_mini_toc_or_auto_menu.text); |
| free (toc_or_mini_toc_or_auto_menu.text); |
| return; |
| } |
| } |
| |
| lookup_extra_integer (element, AI_key_section_level, &status); |
| level_corrected_cmd = cmd; |
| if (status >= 0) |
| { |
| /* if the level was changed, use a consistent command name */ |
| level_corrected_cmd = section_level_adjusted_command_name (element); |
| if (level_corrected_cmd != cmd) |
| { |
| xasprintf (&level_set_class, "%s-level-set-%s", |
| builtin_command_name (cmd), |
| builtin_command_name (level_corrected_cmd)); |
| } |
| } |
| |
| /* find the section starting here, can be through the associated node |
| preceding the section, or the section itself */ |
| |
| if (node_relations && node_relations->associated_section) |
| { |
| opening_section |
| = node_relations->associated_section->element; |
| level_corrected_opening_section_cmd |
| = section_level_adjusted_command_name (opening_section); |
| } |
| else if (section_relations && !section_relations->associated_node) |
| { |
| opening_section = element; |
| level_corrected_opening_section_cmd = level_corrected_cmd; |
| } |
| |
| /* |
| could use empty args information also, to avoid calling command_text |
| my $empty_heading = (!scalar(@$args) or !defined($args->[0])); |
| */ |
| |
| |
| /* heading not defined may happen if the command is a @node, for example |
| if there is an error in the node. */ |
| heading = html_command_text (self, element, 0); |
| |
| /* node is used as heading if there is nothing else. */ |
| if (node_relations) |
| { |
| const ELEMENT *associated_title_command |
| = node_relations->associated_title_command; |
| const char *normalized = lookup_extra_string (element, AI_key_normalized); |
| if (output_unit && output_unit->unit_node |
| && output_unit->unit_node == node_relations |
| && !associated_title_command) |
| { |
| if (!strcmp (normalized, "Top")) |
| heading_level = 0; |
| else |
| { |
| /* use node */ |
| heading_level = 3; |
| } |
| } |
| } |
| else |
| { |
| int status; |
| int level = lookup_extra_integer (element, AI_key_section_level, &status); |
| if (status >= 0) |
| { |
| heading_level = level; |
| } |
| else |
| { |
| heading_level = section_level (element); |
| } |
| } |
| do_heading = (heading && strlen (heading) && heading_level >= 0); |
| |
| /* if set, the id is associated to the heading text */ |
| if (opening_section) |
| { |
| char *class; |
| STRING_LIST *classes; |
| char *attribute_class; |
| int status; |
| int level |
| = lookup_extra_integer (opening_section, AI_key_section_level, &status); |
| STRING_LIST *closed_strings; |
| |
| /* if Structuring sectioning_structure was not called on the |
| document (cannot happen in main program or test_utils.pl tests) */ |
| /* if (status < 0) */ |
| if (status != 0) |
| level = section_level (opening_section); |
| |
| closed_strings = html_close_registered_sections_level (self, |
| self->current_filename.file_number, level); |
| |
| 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); |
| |
| html_register_opened_section_level (self, |
| self->current_filename.file_number, level, "</div>\n"); |
| |
| /* use a specific class name to mark that this is the start of |
| the section extent. It is not necessary where the section is. */ |
| |
| classes = new_string_list (); |
| |
| xasprintf (&class, "%s-level-extent", |
| builtin_command_name (level_corrected_opening_section_cmd)); |
| |
| add_string (class, classes); |
| free (class); |
| attribute_class = html_attribute_class (self, "div", classes); |
| destroy_strings_list (classes); |
| |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| if (element_id && strlen (element_id)) |
| text_printf (result, " id=\"%s\"", element_id); |
| text_append (result, ">\n"); |
| } |
| else if (element_id && strlen (element_id)) |
| { |
| if (element_header.end > 0) |
| { |
| /* case of a @node without sectioning command and with a header. |
| put the node element anchor before the header. |
| Set the class name to the command name if there is no heading, |
| else the class will be with the heading element. */ |
| |
| char *id_class = 0; |
| if (do_heading) |
| { |
| xasprintf (&id_class, "%s-id", builtin_command_name (cmd)); |
| } |
| else |
| id_class = strdup (builtin_command_name (cmd)); |
| |
| format_separate_anchor (self, element_id, id_class, result); |
| |
| free (id_class); |
| } |
| else |
| heading_id = element_id; |
| } |
| |
| text_append (result, element_header.text); |
| free (element_header.text); |
| |
| if (do_heading) |
| { |
| STRING_LIST *heading_classes; |
| if (self->conf->TOC_LINKS.o.integer > 0 |
| && (builtin_command_data[cmd].flags & CF_root) |
| && (builtin_command_data[cmd].flags & CF_sectioning_heading)) |
| { |
| char *content_href = html_command_contents_href (self, element, |
| CM_contents, 0); |
| if (content_href) |
| { |
| char *heading_tmp = strdup (heading); |
| free (heading); |
| xasprintf (&heading, "<a href=\"%s\">%s</a>", |
| content_href, heading_tmp); |
| free (heading_tmp); |
| free (content_href); |
| } |
| } |
| |
| heading_classes = new_string_list (); |
| add_string (builtin_command_name (level_corrected_cmd), heading_classes); |
| if (level_set_class) |
| add_string (level_set_class, heading_classes); |
| if (html_in_preformatted_context (self)) |
| { |
| char *attribute_class; |
| char *id_str = 0; |
| if (heading_id) |
| { |
| xasprintf (&id_str, " id=\"%s\"", heading_id); |
| } |
| else |
| id_str = strdup (""); |
| |
| attribute_class = html_attribute_class (self, "strong", |
| heading_classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_printf (result, "%s>%s</strong>\n", id_str, heading); |
| |
| free (id_str); |
| } |
| else |
| { |
| format_heading_text (self, level_corrected_cmd, |
| heading_classes, heading, |
| heading_level |
| + self->conf->CHAPTER_HEADER_LEVEL.o.integer -1, |
| heading_id, element, element_id, result); |
| } |
| destroy_strings_list (heading_classes); |
| } |
| else if (heading_id) |
| { |
| /* case of a lone node and no header, and case of an empty @top */ |
| format_separate_anchor (self, heading_id, builtin_command_name (cmd), |
| result); |
| } |
| |
| free (heading); |
| free (level_set_class); |
| |
| if (content) |
| text_append (result, content); |
| |
| text_append (result, toc_or_mini_toc_or_auto_menu.text); |
| free (toc_or_mini_toc_or_auto_menu.text); |
| } |
| |
| void |
| 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; |
| |
| if (args_formatted && args_formatted->number > 0 |
| && args_formatted->args[0].formatted[AFT_type_monospacetext] |
| && strlen (args_formatted->args[0].formatted[AFT_type_monospacetext])) |
| format = args_formatted->args[0].formatted[AFT_type_monospacetext]; |
| else |
| return; |
| |
| if (command_other_flags (element) & CF_inline_format) |
| { |
| if (cmd == CM_inlinefmtifelse |
| && !format_expanded_p (self->expanded_formats, format)) |
| arg_index = 2; |
| else if (format_expanded_p (self->expanded_formats, format)) |
| arg_index = 1; |
| } |
| else |
| { |
| int status; |
| int expand_index = lookup_extra_integer (element, AI_key_expand_index, |
| &status); |
| if (expand_index > 0) |
| arg_index = 1; |
| } |
| if (arg_index > 0 && arg_index < args_formatted->number) |
| { |
| if (args_formatted->args[arg_index].formatted[AFT_type_normal]) |
| { |
| text_append (result, |
| args_formatted->args[arg_index].formatted[AFT_type_normal]); |
| } |
| else if (args_formatted->args[arg_index].formatted[AFT_type_raw]) |
| text_append (result, |
| args_formatted->args[arg_index].formatted[AFT_type_raw]); |
| } |
| } |
| |
| void |
| 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) |
| { |
| 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])) |
| { |
| name = strdup (args_formatted->args[1].formatted[AFT_type_normal]); |
| } |
| |
| 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]; |
| |
| if (file_arg && file_arg->formatted[AFT_type_filenametext] |
| && strlen (file_arg->formatted[AFT_type_filenametext])) |
| { |
| file = strdup (file_arg->formatted[AFT_type_filenametext]); |
| } |
| |
| 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 (element->e.c->contents.number > 0) |
| arg_node = element->e.c->contents.list[0]; |
| |
| /* 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); |
| } |
| } |
| |
| /* 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 SECTION_RELATIONS *associated_section_relations = 0; |
| const ELEMENT *associated_title_command = 0; |
| |
| if (self->document && target_node->e.c->cmd == CM_node) |
| { |
| int status; |
| size_t node_number |
| = lookup_extra_integer (target_node, |
| AI_key_node_number, &status); |
| const NODE_RELATIONS *node_relations |
| = self->document->nodes_list.list[node_number -1]; |
| |
| associated_section_relations = node_relations->associated_section; |
| associated_title_command = node_relations->associated_title_command; |
| } |
| |
| reference_element = new_text_element (ET__converted); |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| |
| if (!associated_section_relations |
| || associated_section_relations->element != target_root) |
| target_root = target_node; |
| |
| if (!html_in_string (self)) |
| href = html_command_href (self, target_root, 0, element, 0); |
| |
| if (!name) |
| { |
| if (self->conf->xrefautomaticsectiontitle.o.string |
| && !strcmp (self->conf->xrefautomaticsectiontitle.o.string, "on") |
| && associated_title_command |
| /* 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_title_command, 0)) |
| { |
| if (html_in_string (self)) |
| name = html_command_text (self, associated_title_command, |
| HTT_string); |
| else |
| name = html_command_text (self, associated_title_command, |
| 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 (self->conf->xrefautomaticsectiontitle.o.string |
| && !strcmp (self->conf->xrefautomaticsectiontitle.o.string, "on")) |
| { |
| if (html_in_string (self)) |
| name = html_command_name (self, target_root, HTT_string); |
| else |
| name |
| = html_command_name (self, target_root, HTT_text_nonumber); |
| } |
| else 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; |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), 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); |
| |
| 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 |
| { |
| /* external reference */ |
| char *href = 0; |
| char *reference = 0; |
| char *book_reference = 0; |
| |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| |
| /* We setup a label_element based on the node argument and not directly the |
| node argument to be able to use the $file argument */ |
| |
| ELEMENT *label_element = 0; |
| ELEMENT *manual_content = 0; |
| ELEMENT *node_content = 0; |
| |
| 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); |
| } |
| } |
| |
| /* 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); |
| } |
| |
| if (manual_content) |
| { |
| if (!label_element) |
| label_element = new_element (ET_NONE); |
| |
| add_extra_container (label_element, AI_key_manual_content, |
| copy_container_contents (manual_content)); |
| |
| /* convert the manual part to file string */ |
| html_set_code_context (self, 1); |
| file = html_convert_tree_explanation (self, manual_content, |
| "node file in ref"); |
| html_pop_code_context (self); |
| } |
| |
| if (!name) |
| { |
| if (book) |
| { |
| if (node_content) |
| { |
| char *node_name; |
| html_set_code_context (self, 1); |
| node_name |
| = html_convert_tree_explanation (self, node_content, |
| "node in ref"); |
| html_pop_code_context (self); |
| |
| 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 (label_element) |
| { |
| if (!html_in_string (self)) |
| href = html_command_href (self, label_element, 0, element, 0); |
| destroy_element (label_element); |
| } |
| |
| 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) |
| { |
| xasprintf (&book_reference, "<a %shref=\"%s\">%s</a>", |
| manual_name_attribute.text, href, book); |
| } |
| free (manual_name_attribute.text); |
| free (href); |
| } |
| |
| |
| 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); |
| |
| 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); |
| |
| 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); |
| |
| 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) |
| { |
| char *context_str; |
| xasprintf (&context_str, "convert xref %s", builtin_command_name (cmd)); |
| add_tree_to_build (self, tree); |
| html_convert_tree_append (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; |
| } |
| |
| if (!html_in_multiple_conversions (self)) |
| { |
| message_list_command_warn (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| 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); |
| |
| 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 |
| 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) |
| { |
| STRING_LIST *additional_classes; |
| enum command_id main_cmd = 0; |
| |
| if (!content || !strlen (content)) |
| return; |
| |
| if (html_in_string (self)) |
| { |
| 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 (cmd == CM_example) |
| { |
| /* arguments_line type element */ |
| const ELEMENT *arguments_line = element->e.c->contents.list[0]; |
| size_t i; |
| for (i = 0; i < arguments_line->e.c->contents.number; i++) |
| { |
| const ELEMENT *example_arg = arguments_line->e.c->contents.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, 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); |
| |
| 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); |
| |
| 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 |
| 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) |
| { |
| 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 |
| 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) |
| { |
| char *attribute_class; |
| STRING_LIST *classes; |
| |
| const char *id; |
| char *prepended_text = 0; |
| char *caption_text = 0; |
| char *caption_command_name = 0; |
| |
| const ELEMENT *caption_element; |
| ELEMENT *prepended; |
| FLOAT_CAPTION_PREPENDED_ELEMENT *caption_prepended |
| = float_name_caption (self, element); |
| |
| caption_element = caption_prepended->caption; |
| prepended = caption_prepended->prepended; |
| |
| free (caption_prepended); |
| |
| if (html_in_string (self)) |
| { |
| if (prepended) |
| { |
| char *prepended_text; |
| add_tree_to_build (self, prepended); |
| prepended_text |
| = html_convert_tree_new_formatting_context (self, prepended, |
| "float prepended", 0, 0, 0, 0); |
| |
| remove_tree_to_build (self, prepended); |
| destroy_element_and_children (prepended); |
| if (prepended_text) |
| { |
| text_append (result, prepended_text); |
| free (prepended_text); |
| } |
| } |
| if (content) |
| text_append (result, content); |
| |
| if (caption_element && caption_element->e.c->contents.number > 0 |
| && caption_element->e.c->contents.list[0]->e.c->contents.number > 0) |
| { |
| char *caption_text |
| = html_convert_tree_new_formatting_context (self, |
| caption_element->e.c->contents.list[0], |
| "float caption", 0, 0, 0, 0); |
| if (caption_text) |
| { |
| text_append (result, caption_text); |
| free (caption_text); |
| } |
| } |
| return; |
| } |
| |
| if (caption_element) |
| caption_command_name = builtin_command_name (caption_element->e.c->cmd); |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "div", classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| clear_strings_list (classes); |
| |
| id = html_command_id (self, element); |
| if (id && strlen (id)) |
| text_printf (result, " id=\"%s\"", id); |
| |
| text_append_n (result, ">\n", 2); |
| text_append (result, content); |
| |
| if (prepended) |
| { |
| ELEMENT *args = new_element (ET_brace_container); |
| ELEMENT *strong_element |
| = new_command_element (ET_brace_command, CM_strong); |
| |
| add_to_element_contents (strong_element, args); |
| add_to_element_contents (args, prepended); |
| |
| add_tree_to_build (self, strong_element); |
| prepended_text = html_convert_tree_new_formatting_context (self, |
| strong_element, "float number type", 0, 0, 0, 0); |
| remove_tree_to_build (self, strong_element); |
| |
| destroy_element_and_children (strong_element); |
| |
| if (caption_element) |
| { |
| char *cancelled_prepended; |
| /* register the converted prepended tree to be prepended to |
| the first paragraph in caption formatting */ |
| if (prepended_text) |
| html_register_pending_formatted_inline_content (self, |
| caption_command_name, prepended_text); |
| caption_text = html_convert_tree_new_formatting_context (self, |
| caption_element->e.c->contents.list[0], |
| "float caption", 0, 0, 0, 0); |
| if (prepended_text) |
| { |
| cancelled_prepended |
| = html_cancel_pending_formatted_inline_content (self, |
| caption_command_name); |
| /* unset if prepended text is in caption, i.e. is not cancelled */ |
| if (!cancelled_prepended) |
| { |
| free (prepended_text); |
| prepended_text = 0; |
| } |
| else |
| free (cancelled_prepended); |
| } |
| } |
| if (prepended_text && strlen (prepended_text)) |
| { |
| /* prepended text is not empty and did not find its way in caption */ |
| char *tmp; |
| xasprintf (&tmp, "<p>%s</p>", prepended_text); |
| free (prepended_text); |
| prepended_text = tmp; |
| } |
| } |
| else if (caption_element) |
| { |
| caption_text = html_convert_tree_new_formatting_context (self, |
| caption_element->e.c->contents.list[0], |
| "float caption", 0, 0, 0, 0); |
| } |
| |
| if (caption_text && strlen (caption_text)) |
| { |
| add_string (caption_command_name, classes); |
| attribute_class = html_attribute_class (self, "div", classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| text_append_n (result, ">", 1); |
| text_append (result, caption_text); |
| text_append_n (result, "</div>", 6); |
| } |
| else if (prepended_text && strlen (prepended_text)) |
| { |
| attribute_class = html_attribute_class (self, "div", |
| &type_number_float_classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">", 1); |
| text_append (result, prepended_text); |
| text_append_n (result, "</div>", 6); |
| } |
| |
| free (caption_text); |
| free (prepended_text); |
| |
| text_append_n (result, "</div>", 6); |
| |
| destroy_strings_list (classes); |
| } |
| |
| void |
| 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) |
| { |
| ELEMENT_REFERENCE_STACK_STACK *elements_authors |
| = &self->shared_conversion_state.elements_authors; |
| |
| char *cancelled = html_cancel_pending_formatted_inline_content (self, |
| builtin_command_name (cmd)); |
| if (cancelled) |
| free (cancelled); |
| |
| if (!html_in_string (self)) |
| { |
| char *attribute_class; |
| |
| STRING_LIST *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) |
| { |
| enum command_id main_cmd = small_block_associated_command[i][1]; |
| add_string (builtin_command_name (main_cmd), classes); |
| break; |
| } |
| } |
| } |
| add_string (builtin_command_name (cmd), classes); |
| attribute_class = html_attribute_class (self, "blockquote", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">\n", 2); |
| if (content) |
| text_append (result, content); |
| text_append_n (result, "</blockquote>\n", 14); |
| } |
| else |
| { |
| if (content) |
| text_append (result, content); |
| } |
| |
| if (elements_authors->top > 0) |
| { |
| size_t i; |
| ELEMENT_REFERENCE_STACK *authors |
| = elements_authors->stack[elements_authors->top -1]; |
| |
| if (!authors) |
| { |
| fprintf (stderr, "BUG: unexpected unset elements_authors " |
| "in convert_quotation_command\n"); |
| } |
| else |
| { |
| for (i = 0; i < authors->top; i++) |
| { |
| const ELEMENT *author = authors->stack[i].element; |
| if (author->e.c->contents.list[0]->e.c->contents.number > 0) |
| { |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| ELEMENT *author_arg_copy |
| = copy_tree (author->e.c->contents.list[0], 0); |
| add_element_to_named_string_element_list (substrings, |
| "author", author_arg_copy); |
| |
| /* TRANSLATORS: quotation author */ |
| html_translate_convert_tree_append ( |
| "@center --- @emph{{author}}", |
| self, substrings, 0, result, |
| "convert quotation author"); |
| destroy_named_string_element_list (substrings); |
| } |
| } |
| destroy_element_reference_stack (authors); |
| elements_authors->stack[elements_authors->top -1] = 0; |
| elements_authors->top--; |
| } |
| } |
| else |
| { |
| fprintf (stderr, "BUG: unexpected unset quotation_titlepage_stack" |
| "in convert_quotation_command\n"); |
| } |
| } |
| |
| void |
| 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; |
| int do_title; |
| int do_content; |
| |
| if (html_in_string (self)) |
| { |
| if (content) |
| text_append (result, content); |
| return; |
| } |
| |
| do_title = (args_formatted->number > 0 |
| && args_formatted->args[0].formatted[AFT_type_normal] |
| && strlen (args_formatted->args[0].formatted[AFT_type_normal])); |
| do_content = (content |
| && content[strspn (content, whitespace_chars)] != '\0'); |
| |
| if (!do_title && !do_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 (do_title) |
| { |
| text_append_n (result, "<tr><th>\n", 9); |
| text_append (result, |
| args_formatted->args[0].formatted[AFT_type_normal]); |
| text_append_n (result, "</th></tr>", 10); |
| } |
| if (do_content) |
| { |
| text_append_n (result, "<tr><td>\n", 9); |
| text_append (result, content); |
| text_append_n (result, "</td></tr>", 10); |
| } |
| text_append_n (result, "</table>\n", 9); |
| |
| free (attribute_class); |
| destroy_strings_list (classes); |
| } |
| |
| SPECIAL_LIST_MARK_CSS_NO_ARGS_CMD |
| special_list_mark_css_string_no_arg_command[] = { |
| {CM_minus, "\\2212 ", 0}, |
| {0, 0, 0}, |
| }; |
| |
| char * |
| html_convert_css_string_for_list_mark (CONVERTER *self, const ELEMENT *element, |
| const char *explanation) |
| { |
| char *result; |
| int i; |
| for (i = 0; special_list_mark_css_string_no_arg_command[i].cmd > 0; i++) |
| { |
| enum command_id cmd = special_list_mark_css_string_no_arg_command[i].cmd; |
| special_list_mark_css_string_no_arg_command[i].saved |
| = self->html_no_arg_command_conversion[cmd] |
| .context_formatting[HCC_type_css_string].text; |
| self->html_no_arg_command_conversion[cmd] |
| .context_formatting[HCC_type_css_string].text |
| = special_list_mark_css_string_no_arg_command[i].string; |
| } |
| |
| result = html_convert_css_string (self, element, explanation); |
| |
| for (i = 0; special_list_mark_css_string_no_arg_command[i].cmd > 0; i++) |
| { |
| enum command_id cmd = special_list_mark_css_string_no_arg_command[i].cmd; |
| self->html_no_arg_command_conversion[cmd] |
| .context_formatting[HCC_type_css_string].text |
| = special_list_mark_css_string_no_arg_command[i].saved; |
| special_list_mark_css_string_no_arg_command[i].saved = 0; |
| } |
| |
| return result; |
| } |
| |
| void |
| 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 char *mark_class_name = 0; |
| STRING_LIST *classes; |
| char *attribute_class; |
| const CSS_SELECTOR_STYLE *selector_style = 0; |
| const ELEMENT *arguments_line; |
| const ELEMENT *block_line_arg; |
| const ELEMENT *prepended_element; |
| |
| if (html_in_string (self)) |
| { |
| if (content) |
| text_append (result, content); |
| return; |
| } |
| |
| /* arguments_line type element */ |
| arguments_line = element->e.c->contents.list[0]; |
| block_line_arg = arguments_line->e.c->contents.list[0]; |
| |
| prepended_element |
| = itemize_line_prepended_element (block_line_arg); |
| |
| if (prepended_element) |
| { |
| if (!(type_data[prepended_element->type].flags & TF_text)) |
| { |
| if (prepended_element->e.c->cmd == CM_w) |
| mark_class_name = "none"; |
| else |
| mark_class_name = element_command_name (prepended_element); |
| } |
| } |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| if (mark_class_name) |
| { |
| char *mark_class; |
| char *ul_mark_selector; |
| xasprintf (&mark_class, "mark-%s", mark_class_name); |
| xasprintf (&ul_mark_selector, "ul.%s", mark_class); |
| |
| selector_style = find_css_selector_style (&self->css_element_class_styles, |
| ul_mark_selector); |
| free (ul_mark_selector); |
| if (selector_style && selector_style->style) |
| { |
| add_string (mark_class, classes); |
| } |
| free (mark_class); |
| } |
| |
| attribute_class = html_attribute_class (self, "ul", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| if (!selector_style && self->conf->NO_CSS.o.integer <= 0) |
| { |
| char *css_string |
| = html_convert_css_string_for_list_mark (self, block_line_arg, |
| "itemize arg"); |
| if (css_string && strlen (css_string)) |
| { |
| text_append (result, " style=\"list-style-type: '"); |
| format_protect_text (self, css_string, result); |
| text_append_n (result, "'\"", 2); |
| } |
| free (css_string); |
| } |
| |
| text_append_n (result, ">\n", 2); |
| if (content) |
| text_append (result, content); |
| text_append_n (result, "</ul>\n", 6); |
| } |
| |
| void |
| 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; |
| const ELEMENT *arguments_line; |
| const ELEMENT *block_line_arg; |
| |
| if (!content || !strlen (content)) |
| 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, "ol", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| |
| arguments_line = element->e.c->contents.list[0]; |
| block_line_arg = arguments_line->e.c->contents.list[0]; |
| |
| if (block_line_arg->e.c->contents.number |
| && type_data[block_line_arg->e.c->contents.list[0]->type].flags & TF_text) |
| { |
| const char *specification |
| = block_line_arg->e.c->contents.list[0]->e.text->text; |
| int use_start = 1; |
| unsigned int start = 0; |
| const char *type = 0; |
| size_t specification_len = strlen (specification); |
| if (specification_len == 1 && isascii_alpha (*specification)) |
| { |
| if (isascii_lower (*specification)) |
| { |
| start = 1 + (*specification - 'a'); |
| type = "a"; |
| } |
| else |
| { |
| start = 1 + (*specification - 'A'); |
| type = "A"; |
| } |
| } |
| else |
| { |
| use_start = 0; |
| if (specification_len > 0) |
| { |
| const char *p = specification; |
| int only_digits = 1; |
| while (*p) |
| { |
| if (!isascii_digit (*p)) |
| { |
| only_digits = 0; |
| break; |
| } |
| p++; |
| } |
| if (only_digits) |
| { |
| unsigned int spec_number = strtoul (specification, NULL, 10); |
| if (spec_number != 1) |
| { |
| use_start = 1; |
| start = spec_number; |
| } |
| } |
| } |
| } |
| if (type) |
| text_printf (result, " type=\"%s\"", type); |
| if (use_start) |
| text_printf (result, " start=\"%u\"", start); |
| } |
| |
| text_append_n (result, ">\n", 2); |
| text_append (result, content); |
| text_append_n (result, "</ol>\n", 6); |
| } |
| |
| void |
| 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; |
| |
| if (!content || !strlen (content)) |
| return; |
| |
| if (html_in_string (self)) |
| { |
| text_append (result, content); |
| } |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "table", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">\n", 2); |
| text_append (result, content); |
| text_append_n (result, "</table>\n", 9); |
| } |
| |
| void |
| 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; |
| |
| if (!content || !strlen (content)) |
| return; |
| |
| if (html_in_string (self)) |
| { |
| text_append (result, content); |
| } |
| |
| classes = new_string_list (); |
| add_string (builtin_command_name (cmd), classes); |
| |
| attribute_class = html_attribute_class (self, "dl", classes); |
| destroy_strings_list (classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">\n", 2); |
| text_append (result, content); |
| text_append_n (result, "</dl>\n", 6); |
| } |
| |
| 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 |
| = converter_expand_verbatiminclude (element, |
| &self->error_messages, self->conf, |
| &self->document->global_info); |
| |
| if (verbatim_include_verbatim) |
| { |
| add_tree_to_build (self, verbatim_include_verbatim); |
| html_convert_tree_append (self, verbatim_include_verbatim, |
| result, "convert verbatiminclude"); |
| remove_tree_to_build (self, verbatim_include_verbatim); |
| destroy_element_and_children (verbatim_include_verbatim); |
| } |
| } |
| |
| void |
| 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) |
| { |
| 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) |
| { |
| const char *sp_number_string = misc_args->list[0]; |
| sp_nr = strtoul (sp_number_string, NULL, 10); |
| } |
| |
| 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++) |
| { |
| text_append_n (result, self->line_break_element.string, |
| self->line_break_element.len); |
| text_append_n (result, "\n", 1); |
| } |
| } |
| } |
| |
| 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; |
| ELEMENT_REFERENCE_STACK *authors_list; |
| |
| ELEMENT_REFERENCE_STACK_STACK *elements_authors |
| = &self->shared_conversion_state.elements_authors; |
| |
| if (elements_authors->top == 0) |
| return; |
| |
| authors_list = elements_authors->stack[elements_authors->top -1]; |
| |
| if (!authors_list) |
| {/* in titlepage */ |
| 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); |
| } |
| else |
| {/* in quotation */ |
| push_element_reference_stack_element (authors_list, element, |
| get_sv_hv (element->sv)); |
| } |
| } |
| |
| 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->e.c->parent |
| && element_builtin_cmd (element->e.c->parent) == CM_itemize) |
| { |
| if (content |
| && content[strspn (content, whitespace_chars)] != '\0') |
| { |
| text_printf (result, "<li>%s</li>", content); |
| } |
| } |
| else if (element->e.c->parent |
| && element_builtin_cmd (element->e.c->parent) == CM_enumerate) |
| { |
| if (content |
| && content[strspn (content, whitespace_chars)] != '\0') |
| { |
| text_printf (result, "<li> %s</li>", content); |
| } |
| } |
| else if (element->e.c->contents.number > 0 |
| && element->e.c->contents.list[0]->type == ET_line_arg) |
| { |
| if (element->e.c->contents.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); |
| |
| if (index_entry_id) |
| { |
| text_printf (result, "<a id=\"%s\"></a>", index_entry_id); |
| anchor = get_copiable_anchor (self, index_entry_id); |
| if (anchor) |
| text_append_n (result, "<span>", 6); |
| } |
| |
| if (html_in_preformatted_context (self)) |
| { |
| 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 (cmd_or_type->variety == CTV_type_command) |
| { |
| enum command_id pre_class_cmd = cmd_or_type->ct.cmd; |
| if (builtin_command_data[pre_class_cmd].flags |
| & CF_preformatted_code) |
| { |
| char *attribute_class |
| = html_attribute_class (self, "code", |
| &table_term_preformatted_code_classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">", 1); |
| |
| pre_class_close = "</code>"; |
| break; |
| } |
| } |
| } |
| } |
| |
| tree = table_item_content_tree (self, element); |
| if (tree) |
| { |
| add_tree_to_build (self, tree->tree); |
| converted_e = tree->tree; |
| } |
| else |
| converted_e = element->e.c->contents.list[0]; |
| |
| html_convert_tree_append (self, converted_e, result, |
| "convert table_item_tree"); |
| |
| if (pre_class_close) |
| text_append (result, pre_class_close); |
| |
| if (anchor) |
| { |
| text_append (result, anchor); |
| text_append_n (result, "</span>", 7); |
| free (anchor); |
| } |
| |
| text_append_n (result, "</dt>\n", 6); |
| |
| if (tree) |
| { |
| remove_tree_to_build (self, tree->tree); |
| destroy_tree_added_elements (tree); |
| } |
| } |
| } |
| else if (element->e.c->parent->type == ET_row) |
| { |
| conversion_function_cmd_conversion (self, |
| &self->current_commands_conversion_function[CM_tab], |
| cmd, element, args_formatted, |
| content, result); |
| } |
| } |
| |
| static char * |
| trim_trailing_content (const char *content) |
| { |
| char *trimmed_content = strdup (content); |
| size_t str_len = strlen (trimmed_content); |
| if (str_len > 0) |
| { |
| char *q = trimmed_content + str_len - 1; |
| while (q > trimmed_content) |
| { |
| if (!strchr (whitespace_chars, *q)) |
| { |
| break; |
| } |
| q--; |
| } |
| *(q +1) = '\0'; |
| } |
| return trimmed_content; |
| } |
| |
| void |
| 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; |
| int status; |
| const ELEMENT *row; |
| const ELEMENT *multitable; |
| const ELEMENT *columnfractions; |
| enum command_id first_row_cmd; |
| const char *html_element = "td"; |
| |
| if (content) |
| { |
| const char *p = content; |
| p += strspn (p, whitespace_chars); |
| trimmed_content = trim_trailing_content (p); |
| } |
| else |
| trimmed_content = strdup (""); |
| |
| if (html_in_string (self)) |
| { |
| text_append (result, trimmed_content); |
| free (trimmed_content); |
| return; |
| } |
| |
| row = element->e.c->parent; |
| first_row_cmd = element_builtin_cmd (row->e.c->contents.list[0]); |
| if (first_row_cmd == CM_headitem) |
| html_element = "th"; |
| |
| text_append_n (result, "<", 1); |
| text_append_n (result, html_element, 2); |
| |
| cell_nr = (size_t) lookup_extra_integer (element, AI_key_cell_number, &status); |
| multitable = row->e.c->parent->e.c->parent; |
| |
| columnfractions = multitable_columnfractions (multitable); |
| |
| if (columnfractions) |
| { |
| const STRING_LIST *cf_misc_args |
| = lookup_extra_misc_args (columnfractions, AI_key_misc_args); |
| if (cf_misc_args && cf_misc_args->number >= cell_nr) |
| { |
| const char *fraction_str |
| = cf_misc_args->list[cell_nr -1]; |
| double fraction = strtod (fraction_str, NULL); |
| if (self->conf->_INLINE_STYLE_WIDTH.o.integer > 0) |
| text_printf (result, " style=\"width: %0.f%%\"", 100 * fraction); |
| else |
| text_printf (result, " width=\"%0.f%%\"", 100 * fraction); |
| } |
| } |
| text_append_n (result, ">", 1); |
| text_append (result, trimmed_content); |
| free (trimmed_content); |
| text_append_n (result, "</", 2); |
| text_append_n (result, html_element, 2); |
| text_append_n (result, ">", 1); |
| } |
| |
| void |
| 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) |
| { |
| if (self->document->global_commands.copying) |
| { |
| ELEMENT *tmp = new_element (ET_NONE); |
| tmp->e.c->contents = self->document->global_commands.copying->e.c->contents; |
| html_convert_tree_append (self, tmp, result, "convert insertcopying"); |
| tmp->e.c->contents.list = 0; |
| destroy_element (tmp); |
| } |
| } |
| |
| void |
| html_convert_maketitle_command (CONVERTER *self, const enum command_id cmd, |
| const ELEMENT *element, |
| const HTML_ARGS_FORMATTED *args_formatted, |
| const char *content, TEXT *result) |
| { |
| text_append (result, self->title_titlepage); |
| } |
| |
| 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 |
| 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 (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++) |
| { |
| const FLOAT_INFORMATION *float_info |
| = &float_types->float_list.list[j]; |
| char *caption_attribute_class; |
| const ELEMENT *caption_element; |
| const ELEMENT *caption_shortcaption[2]; |
| const STRING_LIST *caption_classes = 0; |
| const ELEMENT *float_elt = float_info->float_element; |
| 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); |
| |
| find_float_caption_shortcaption(float_elt, caption_shortcaption); |
| caption_element = caption_shortcaption[1]; |
| if (caption_element) |
| caption_classes = &shortcaption_in_listoffloats_classes; |
| else |
| { |
| caption_element = caption_shortcaption[0]; |
| 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 |
| = html_convert_tree_new_formatting_context (self, |
| caption_element->e.c->contents.list[0], |
| builtin_command_name (cmd), 0, |
| 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); |
| } |
| } |
| } |
| |
| #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; |
| } |
| } |
| |
| 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 *generic_cmd_class; |
| char *attribute_class; |
| |
| xasprintf (&generic_cmd_class, "index-letters-%s-%s", |
| head_or_foot, builtin_command_name (cmd)); |
| xasprintf (&index_name_cmd_class, "%s-letters-%s-%s", |
| index_name, head_or_foot, builtin_command_name (cmd)); |
| add_string (generic_cmd_class, entry_classes); |
| add_string (index_name_cmd_class, entry_classes); |
| free (index_name_cmd_class); |
| free (generic_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 */ |
| html_translate_convert_tree_append ("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); |
| } |
| |
| static ROOT_AND_UNIT * |
| get_element_root_command_element (CONVERTER *self, const ELEMENT *command) |
| { |
| ROOT_AND_UNIT *root_unit = html_get_tree_root_element (self, command, 0); |
| |
| if (root_unit && root_unit->root) |
| { |
| const ELEMENT *root_command = root_unit->root; |
| if (self->conf->USE_NODES.o.integer > 0) |
| { |
| if (root_command->e.c->cmd != CM_node |
| && builtin_command_data[root_command->e.c->cmd].flags & CF_root |
| && self->document) |
| { |
| int status; |
| size_t section_number |
| = lookup_extra_integer (root_command, |
| AI_key_section_number, &status); |
| const SECTION_RELATIONS *section_relations |
| = self->document->sections_list.list[section_number -1]; |
| |
| if (section_relations->associated_node) |
| root_unit->root |
| = section_relations->associated_node->element; |
| } |
| } |
| else if (root_command->e.c->cmd == CM_node |
| && self->document) |
| { |
| int status; |
| size_t node_number |
| = lookup_extra_integer (root_command, |
| AI_key_node_number, &status); |
| const NODE_RELATIONS *node_relations |
| = self->document->nodes_list.list[node_number -1]; |
| |
| if (node_relations->associated_section) |
| root_unit->root = node_relations->associated_section->element; |
| } |
| } |
| return root_unit; |
| } |
| |
| 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_ELEMENT_LIST subentries_list; |
| 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 *generic_cmd_class; |
| char *generic_letter_header_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); |
| int in_test; |
| |
| if (!index_entries_by_letter) |
| return; |
| |
| if (html_in_string (self)) |
| return; |
| |
| 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)) |
| { |
| index_sorted = idx; |
| break; |
| } |
| } |
| if (!index_sorted || !index_sorted->letter_number) |
| return; |
| |
| memset (&subentries_list, 0, sizeof (CONST_ELEMENT_LIST)); |
| |
| 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 *)); |
| |
| /* used both to select an 'external' Perl call and pass in_test */ |
| in_test = (self->conf->TEST.o.integer > 0); |
| |
| 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, |
| in_test, in_test, |
| (self->conf->USE_UNIDECODE.o.integer == 0)); |
| destroy_element (letter_text); |
| |
| 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; |
| } |
| |
| xasprintf (&letter_id[i], "%s_%s_letter-%s", index_element_id, |
| index_name, normalized_letter); |
| free (normalized_letter); |
| } |
| } |
| |
| html_new_document_context (self, builtin_command_name (cmd), 0, 0, 0); |
| |
| STRING_LIST *entry_classes = new_string_list (); |
| STRING_LIST *section_classes = new_string_list (); |
| |
| xasprintf (&entry_class_seeentry, "%s-index-see-entry", |
| builtin_command_name (cmd)); |
| xasprintf (§ion_class_seeentry, "%s-index-see-entry-section", |
| builtin_command_name (cmd)); |
| xasprintf (&cmd_index_entry_class, "%s-index-entry", |
| builtin_command_name (cmd)); |
| xasprintf (§ion_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)); |
| xasprintf (&generic_letter_header_class, "index-letter-header-%s", |
| builtin_command_name (cmd)); |
| |
| text_init (&entries_text); |
| text_init (&result_index_entries); |
| |
| /* 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; |
| |
| 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); |
| |
| for (j = 0; j < letter_entry->entries_number; j++) |
| { |
| size_t l; |
| 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 *referred_entry; |
| const ELEMENT *seeentry; |
| 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; |
| 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; |
| unsigned long formatting_context = 0; |
| entry_nr++; |
| |
| if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0) |
| { |
| const char *element_node |
| = lookup_extra_string (main_entry_element, |
| AI_key_element_node); |
| if (element_node && !strcmp (element_node, "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 |
| html_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; |
| |
| 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 |
| = index_entry_referred_entry (main_entry_element, CM_seeentry); |
| if (seeentry) |
| referred_entry = seeentry; |
| else |
| referred_entry |
| = index_entry_referred_entry (main_entry_element, CM_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; |
| collect_subentries (index_entry_ref->entry_element, &subentries_list); |
| |
| for (l = 0; l < subentries_list.number; l++) |
| { |
| const ELEMENT *subentry = subentries_list.list[l]; |
| ELEMENT *subentry_tree = 0; |
| const ELEMENT *line_arg; |
| |
| line_arg = subentry->e.c->contents.list[0]; |
| |
| if (line_arg->e.c->contents.number > 0) |
| { |
| size_t k; |
| subentry_tree = new_element (ET_NONE); |
| |
| for (k = 0; k < line_arg->e.c->contents.number; k++) |
| { |
| ELEMENT *content = line_arg->e.c->contents.list[k]; |
| if ((type_data[content->type].flags & TF_text) |
| || content->e.c->cmd != CM_subentry) |
| { |
| add_to_contents_as_array (subentry_tree, content); |
| } |
| } |
| } |
| |
| 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) |
| { |
| 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++; |
| if (subentry_level > SUBENTRIES_MAX_LEVEL) |
| break; |
| } |
| subentries_list.number = 0; |
| /* level/index of the last entry */ |
| last_entry_level = subentry_level - 1; |
| |
| if (in_code) |
| formatting_context |= CTXF_code; |
| |
| /* 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]); |
| if (*formatted_index_entry_nr > 1) |
| { |
| /* call with multiple_pass argument */ |
| entry = html_convert_tree_new_formatting_context (self, |
| entry_trees[level], convert_info, |
| formatting_context, |
| multiple_pass_str, 0, 0); |
| } |
| else |
| { |
| if (in_code) |
| html_set_code_context (self, 1); |
| entry = html_convert_tree_explanation (self, |
| entry_trees[level], convert_info); |
| if (in_code) |
| html_pop_code_context (self); |
| } |
| if (level > 0) |
| { |
| remove_tree_to_build (self, entry_trees[level]); |
| destroy_element (entry_trees[level]); |
| } |
| free (convert_info); |
| |
| if (level == 0) |
| add_string (cmd_index_entry_class, entry_classes); |
| else if (level > 0) |
| { |
| /* indent */ |
| char *index_entry_level; |
| xasprintf (&index_entry_level, |
| "%s-index-subentry-level-%d", |
| builtin_command_name (cmd), level); |
| add_string (index_entry_level, entry_classes); |
| free (index_entry_level); |
| } |
| text_append_n (&entries_text, "<tr>", 4); |
| 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 (referred_entry) |
| { |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| char *entry; |
| char *reference = 0; |
| |
| ELEMENT *referred_copy |
| = copy_tree (referred_entry->e.c->contents.list[0], 0); |
| |
| if (seeentry) |
| { |
| char *convert_info; |
| ELEMENT *result_tree; |
| ELEMENT *entry_tree_copy = copy_tree (entry_tree, 0); |
| add_element_to_named_string_element_list (substrings, |
| "main_index_entry", entry_tree_copy); |
| add_element_to_named_string_element_list (substrings, |
| "seeentry", referred_copy); |
| 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 = html_convert_tree_new_formatting_context (self, |
| result_tree, convert_info, |
| formatting_context, |
| multiple_pass_str, 0, 0); |
| } |
| else |
| { |
| if (in_code) |
| html_set_code_context (self, 1); |
| entry = html_convert_tree_explanation (self, result_tree, |
| convert_info); |
| if (in_code) |
| html_pop_code_context (self); |
| } |
| remove_tree_to_build (self, result_tree); |
| destroy_element_and_children (result_tree); |
| free (convert_info); |
| |
| add_string (section_class_seeentry, section_classes); |
| } |
| else |
| { |
| ELEMENT *reference_tree; |
| char *conv_str_entry; |
| char *conv_str_reference; |
| |
| add_element_to_named_string_element_list (substrings, |
| "see_also_entry", referred_copy); |
| |
| if (in_code) |
| { |
| /* TRANSLATORS: refer to another index entry */ |
| reference_tree = html_cdt_tree ( |
| "@emph{See also} @code{{see_also_entry}}", |
| self, substrings, 0); |
| } |
| else |
| { |
| /* TRANSLATORS: refer to another index entry */ |
| 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 = html_convert_tree_new_formatting_context (self, |
| entry_tree, conv_str_entry, |
| formatting_context, |
| multiple_pass_str, 0, 0); |
| reference |
| = html_convert_tree_new_formatting_context (self, |
| reference_tree, conv_str_reference, |
| 0, multiple_pass_str, 0, 0); |
| } |
| else |
| { |
| if (in_code) |
| html_set_code_context (self, 1); |
| entry = html_convert_tree_explanation (self, entry_tree, |
| conv_str_entry); |
| if (in_code) |
| html_pop_code_context (self); |
| reference = html_convert_tree_explanation (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 (section_class_seealso, section_classes); |
| } |
| |
| if (seeentry) |
| add_string (entry_class_seeentry, entry_classes); |
| if (last_entry_level == 0) |
| add_string (cmd_index_entry_class, entry_classes); |
| else if (last_entry_level > 0) |
| { |
| char *index_entry_level; |
| xasprintf (&index_entry_level, |
| "%s-index-subentry-level-%d", |
| builtin_command_name (cmd), last_entry_level); |
| add_string (index_entry_level, entry_classes); |
| free (index_entry_level); |
| } |
| destroy_named_string_element_list (substrings); |
| text_append_n (&entries_text, "<tr>", 4); |
| 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 = html_convert_tree_new_formatting_context (self, |
| entry_tree, convert_info, |
| formatting_context, |
| multiple_pass_str, 0, 0); |
| } |
| else |
| { |
| if (in_code) |
| html_set_code_context (self, 1); |
| entry = html_convert_tree_explanation (self, entry_tree, |
| convert_info); |
| if (in_code) |
| html_pop_code_context (self); |
| } |
| 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); |
| |
| if (last_entry_level == 0) |
| add_string (cmd_index_entry_class, entry_classes); |
| else if (last_entry_level > 0) |
| { |
| char *index_entry_level; |
| xasprintf (&index_entry_level, |
| "%s-index-subentry-level-%d", |
| builtin_command_name (cmd), last_entry_level); |
| add_string (index_entry_level, entry_classes); |
| free (index_entry_level); |
| } |
| text_append_n (&entries_text, "<tr>", 4); |
| 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) |
| { |
| const char *associated_command_id |
| = lookup_extra_string (main_entry_element, |
| AI_key_element_node); |
| |
| if (associated_command_id) |
| associated_command |
| = find_identifier_target ( |
| &self->document->identifiers_target, |
| associated_command_id); |
| |
| 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 && self->conf->DEBUG.o.integer > 0), |
| 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 && self->conf->DEBUG.o.integer > 0), |
| 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; |
| char *index_name_letter_header_class; |
| 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_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_explanation (self, formatted_command, |
| explanation); |
| remove_tree_to_build (self, formatted_command); |
| destroy_element (formatted_command); |
| } |
| else |
| formatted_letter |
| = html_convert_tree_explanation (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); |
| xasprintf (&index_name_letter_header_class, "%s-letter-header-%s", |
| index_name, builtin_command_name (cmd)); |
| add_string (generic_letter_header_class, entry_classes); |
| add_string (index_name_letter_header_class, entry_classes); |
| free (index_name_letter_header_class); |
| attribute_class = html_attribute_class (self, "th", entry_classes); |
| text_append (&result_index_entries, attribute_class); |
| clear_strings_list (entry_classes); |
| free (attribute_class); |
| text_printf (&result_index_entries, " colspan=\"2\" 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=\"2\">", 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; |
| } |
| } |
| free (subentries_list.list); |
| |
| 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); |
| free (generic_letter_header_class); |
| |
| 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); |
| |
| xasprintf (&generic_cmd_class, "index-entries-%s", |
| builtin_command_name (cmd)); |
| /* now format the index entries */ |
| xasprintf (&index_name_cmd_class, "%s-entries-%s", |
| index_name, builtin_command_name (cmd)); |
| add_string (generic_cmd_class, entry_classes); |
| add_string (index_name_cmd_class, entry_classes); |
| free (index_name_cmd_class); |
| free (generic_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", 2); |
| |
| text_append_n (result, "<tr><td colspan=\"2\">", 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 |
| 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) |
| { |
| if (html_in_string (self)) |
| return; |
| |
| set_informative_command_value (self->sorted_options, 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)) |
| return; |
| |
| enum command_id used_cmd; |
| |
| if (cmd == CM_summarycontents) |
| used_cmd = CM_shortcontents; |
| else |
| used_cmd = cmd; |
| |
| set_informative_command_value (self->sorted_options, 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.number > 1) |
| { |
| char *contents = contents_inline_element (self, used_cmd, element); |
| if (contents) |
| { |
| text_append (result, contents); |
| free (contents); |
| } |
| } |
| } |
| |
| void |
| html_convert_def_line_type (CONVERTER *self, const enum element_type type, |
| const ELEMENT *element, const char *content, |
| TEXT *result); |
| |
| void |
| 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) |
| { |
| STRING_LIST *classes; |
| enum command_id original_cmd = cmd; |
| char *class; |
| |
| if (builtin_command_data[cmd].flags & CF_line) |
| { |
| html_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); |
| } |
| |
| |
| 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); |
| } |
| |
| 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 (cmd == CM_node) |
| node_element = element; |
| else if (cmd == CM_part && self->document) |
| { |
| int status; |
| size_t section_number |
| = lookup_extra_integer (element, |
| AI_key_section_number, &status); |
| const SECTION_RELATIONS *part_relations |
| = self->document->sections_list.list[section_number -1]; |
| |
| if (part_relations->part_following_node) |
| node_element = part_relations->part_following_node->element; |
| } |
| 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 |
| 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; |
| /* arguments_line type element */ |
| const ELEMENT *arguments_line = element->e.c->contents.list[0]; |
| ELEMENT *block_line_args = arguments_line->e.c->contents.list[0]; |
| |
| if (block_line_args->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 (block_line_args, 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_explanation (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); |
| |
| open_quotation_titlepage_stack (self, 1); |
| } |
| |
| |
| |
| /* element types conversion and open functions */ |
| |
| 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); |
| } |
| |
| 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; |
| |
| 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); |
| } |
| |
| #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 (html_in_code (self) || html_in_math (self)) |
| { |
| html_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 |
| |
| 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); |
| |
| 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 |
| html_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; |
| 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->e.c->parent |
| && element->e.c->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); |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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 (html_in_string (self) || html_in_multiple_conversions (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 |
| 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); |
| |
| if (begin) |
| format_protect_text (self, begin, result); |
| if (content) |
| text_append (result, content); |
| if (end) |
| format_protect_text (self, end, result); |
| } |
| |
| 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); |
| |
| add_tree_to_build (self, translated); |
| |
| html_convert_tree_append (self, translated, result, |
| "translated TEXT"); |
| |
| remove_tree_to_build (self, translated); |
| destroy_element_and_children (translated); |
| } |
| |
| void |
| html_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 |
| html_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 |
| html_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 |
| 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; |
| int long_description = 0; |
| const ELEMENT *associated_title_command = 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++) |
| { |
| 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 |
| { |
| /* 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) |
| { |
| const NODE_RELATIONS *node_relations = 0; |
| if (node->e.c->cmd == CM_node && self->document) |
| { |
| int status; |
| size_t node_number |
| = lookup_extra_integer (node, AI_key_node_number, &status); |
| node_relations |
| = self->document->nodes_list.list[node_number -1]; |
| |
| if (node_relations->node_description) |
| node_description = node_relations->node_description; |
| else if (node_relations->node_long_description) |
| { |
| node_description = node_relations->node_long_description; |
| long_description = 1; |
| } |
| } |
| |
| /* if !NODE_NAME_IN_MENU, we pick the associated title |
| command element |
| */ |
| if (self->conf->NODE_NAME_IN_MENU.o.integer <= 0 |
| && node_relations) |
| { |
| associated_title_command |
| = node_relations->associated_title_command; |
| } |
| if (associated_title_command) |
| href = html_command_href (self, associated_title_command, |
| 0, element, 0); |
| else |
| 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 |
| = html_get_target (self, 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; |
| |
| 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); |
| } |
| |
| /* 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); |
| |
| if (name_entry) |
| { |
| html_convert_tree_append (self, name_entry, result, |
| "menu_arg menu_entry_name preformatted"); |
| html_convert_tree_append (self, |
| menu_entry_separators[entry_separators_idx], |
| result, "menu_arg name separator preformatted"); |
| entry_separators_idx++; |
| } |
| |
| if (menu_entry_node) |
| { |
| if (!in_string && href) |
| { |
| menu_entry_a (self, href, isindex, html_menu_entry_index, |
| result); |
| } |
| |
| html_set_code_context (self, 1); |
| |
| html_convert_tree_append (self, menu_entry_node, result, |
| "menu_arg menu_entry_node preformatted"); |
| |
| html_pop_code_context (self); |
| |
| if (!in_string && href) |
| text_append_n (result, "</a>", 4); |
| } |
| |
| if (entry_separators_idx < entry_separators_nr) |
| { |
| html_convert_tree_append (self, |
| menu_entry_separators[entry_separators_idx], |
| result, "menu_arg node separator preformatted"); |
| entry_separators_idx++; |
| } |
| |
| if (!in_string) |
| text_append_n (result, "</pre>", 6); |
| |
| 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 (!long_description) |
| description_element = node_description->e.c->contents.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 |
| = html_convert_tree_new_formatting_context (self, |
| description_element, |
| "menu_arg node description preformatted", 0, |
| multiple_formatted, 0, CM_menu); |
| |
| if (description) |
| { |
| text_append (result, description); |
| free (description); |
| } |
| |
| if (formatted_nodedescription_nr > 1) |
| free (multiple_formatted); |
| |
| if (long_description) |
| { |
| remove_tree_to_build (self, description_element); |
| description_element->e.c->contents.list = 0; |
| destroy_element (description_element); |
| } |
| } |
| else if (menu_description) |
| { |
| html_convert_tree_append (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 (associated_title_command && href) |
| { |
| char *name = html_command_text (self, associated_title_command, 0); |
| if (name && strlen (name)) |
| { |
| name_no_number = html_command_text (self, |
| associated_title_command, |
| 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_explanation (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) |
| { |
| html_set_code_context (self, 1); |
| |
| name = html_convert_tree_explanation (self, node_content, |
| "menu_arg name"); |
| html_pop_code_context (self); |
| } |
| } |
| |
| if (self->conf->MENU_SYMBOL.o.string) |
| text_append (result, self->conf->MENU_SYMBOL.o.string); |
| text_append_n (result, " ", 1); |
| |
| if (href) |
| { |
| menu_entry_a (self, href, isindex, html_menu_entry_index, |
| result); |
| } |
| |
| if (name) |
| { |
| 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); |
| } |
| |
| if (href) |
| text_append_n (result, "</a>", 4); |
| } |
| |
| if (self->conf->MENU_ENTRY_COLON.o.string) |
| text_append (result, self->conf->MENU_ENTRY_COLON.o.string); |
| text_append_n (result, "</td>", 5); |
| |
| open_element_with_class (self, "td", |
| &menu_entry_description_classes, result); |
| |
| 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); |
| } |
| |
| if (!long_description) |
| description_element = node_description->e.c->contents.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 |
| = html_convert_tree_new_formatting_context (self, |
| description_element, |
| "menu_arg node description", 0, multiple_formatted, |
| 0, CM_menu); |
| |
| if (formatted_nodedescription_nr > 1) |
| free (multiple_formatted); |
| |
| if (long_description) |
| { |
| 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_explanation (self, menu_description, |
| "menu_arg description"); |
| } |
| |
| if (description) |
| { |
| text_append (result, description); |
| free (description); |
| } |
| |
| free (name_no_number); |
| text_append_n (result, "</td></tr>\n", 11); |
| } |
| |
| free (href); |
| } |
| |
| static char *menu_comment_array[] = {"menu-comment"}; |
| static const STRING_LIST menu_comment_classes = {menu_comment_array, 1, 1}; |
| |
| void |
| html_convert_menu_comment_type (CONVERTER *self, const enum element_type type, |
| const ELEMENT *element, const char *content, |
| TEXT *result) |
| { |
| char *attribute_class; |
| |
| if (html_inside_preformatted (self) || html_in_string (self)) |
| { |
| if (content) |
| text_append (result, content); |
| return; |
| } |
| |
| 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); |
| |
| text_append_n (result, "</th></tr>", 10); |
| } |
| |
| void |
| html_convert_before_item_type (CONVERTER *self, const enum element_type type, |
| const ELEMENT *element, const char *content, |
| TEXT *result) |
| { |
| enum command_id in_format_cmd; |
| |
| if (!content || content[strspn (content, whitespace_chars)] == '\0') |
| return; |
| |
| 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); |
| } |
| } |
| |
| void |
| html_convert_table_term_type (CONVERTER *self, const enum element_type type, |
| const ELEMENT *element, const char *content, |
| TEXT *result) |
| { |
| if (content) |
| { |
| text_append (result, "<dt>"); |
| text_append (result, content); |
| } |
| } |
| |
| #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) |
| { |
| 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)) |
| { |
| /* 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->e.c->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++) |
| { |
| 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; |
| |
| /* 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->e.c->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; |
| |
| 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)); |
| 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; |
| char *explanation; |
| |
| xasprintf (&explanation, "DEF_TYPE %s", builtin_command_name (def_cmd)); |
| |
| html_set_code_context (self, 1); |
| type_text = html_convert_tree_explanation (self, parsed_def->type, |
| explanation); |
| html_pop_code_context (self); |
| |
| 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); |
| } |
| |
| 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)); |
| |
| text_append (&def_call, attribute_class); |
| free (attribute_class); |
| text_append_n (&def_call, ">", 1); |
| |
| html_set_code_context (self, 1); |
| html_convert_tree_append (self, parsed_def->name, |
| &def_call, explanation); |
| html_pop_code_context (self); |
| |
| text_append_n (&def_call, "</strong>", 9); |
| free (explanation); |
| } |
| |
| if (parsed_def->args) |
| { |
| 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)) |
| { |
| /* this is a newly created element */ |
| add_tree_to_build (self, parsed_def->args); |
| |
| html_set_code_context (self, 1); |
| args_formatted = html_convert_tree_explanation (self, |
| parsed_def->args, |
| explanation); |
| html_pop_code_context (self); |
| |
| remove_tree_to_build (self, parsed_def->args); |
| |
| 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 |
| { |
| html_set_code_context (self, 0); |
| args_formatted = html_convert_tree_explanation (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); |
| } |
| |
| if (self->conf->DEF_TABLE.o.integer > 0) |
| { |
| ELEMENT *def_category_tree |
| = definition_category_tree (element, |
| self->current_lang_translations, |
| self->conf->COMMAND_LINE_ENCODING.o.string, |
| self->conf->DEBUG.o.integer, self, |
| &html_cdt_tree); |
| |
| 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_multiple_conversions (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); |
| |
| attribute_class = html_attribute_class (self, "td", |
| &category_def_classes); |
| text_append (result, attribute_class); |
| free (attribute_class); |
| text_append_n (result, ">[", 2); |
| |
| if (def_category_tree) |
| { |
| add_tree_to_build (self, def_category_tree); |
| html_convert_tree_append (self, def_category_tree, result, 0); |
| remove_tree_to_build (self, def_category_tree); |
| destroy_element_and_children (def_category_tree); |
| } |
| text_append_n (result, "]</td></tr>\n", 12); |
| |
| destroy_parsed_def (parsed_def); |
| 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_multiple_conversions (self)) |
| text_printf (result, " id=\"%s\"", index_id); |
| text_append_n (result, ">", 1); |
| |
| if (parsed_def->category) |
| { |
| ELEMENT *e_category_tree = 0; |
| NAMED_STRING_ELEMENT_LIST *substrings |
| = new_named_string_element_list (); |
| ELEMENT *category_copy = copy_tree (parsed_def->category, 0); |
| |
| add_element_to_named_string_element_list (substrings, |
| "category", category_copy); |
| if (parsed_def->class) |
| { |
| ELEMENT *class_copy = copy_tree (parsed_def->class, 0); |
| 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")) |
| { |
| e_category_tree |
| = html_cdt_tree ("{category} on @code{{class}}:@* ", |
| self, substrings, 0); |
| } |
| else if (base_cmd == CM_defop || base_cmd == CM_deftypeop) |
| { |
| e_category_tree |
| = html_cdt_tree ("{category} on @code{{class}}: ", |
| self, substrings, 0); |
| } |
| else if (base_cmd == CM_defcv || base_cmd == CM_deftypecv) |
| { |
| e_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")) |
| { |
| e_category_tree |
| = html_cdt_tree ("{category}:@* ", |
| self, substrings, 0); |
| } |
| else |
| { |
| e_category_tree |
| = html_cdt_tree ("{category}: ", |
| self, substrings, 0); |
| } |
| } |
| destroy_named_string_element_list (substrings); |
| |
| if (e_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) |
| { |
| text_append_n (result, attribute_open, open_len); |
| text_append_n (result, ">", 1); |
| } |
| free (attribute_open); |
| add_tree_to_build (self, e_category_tree); |
| html_convert_tree_append (self, e_category_tree, result, explanation); |
| remove_tree_to_build (self, e_category_tree); |
| destroy_element_and_children (e_category_tree); |
| if (open_len) |
| text_append_n (result, "</span>", 7); |
| free (explanation); |
| } |
| } |
| |
| destroy_parsed_def (parsed_def); |
| |
| anchor = get_copiable_anchor (self, index_id); |
| |
| 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); |
| } |
| |
| text_append_n (result, "</dt>\n", 6); |
| |
| free (anchor); |
| } |
| |
| void |
| html_convert_def_item_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); |
| |
| 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 |
| html_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); |
| |
| if (content[strspn (content, whitespace_chars)] == '\0') |
| return; |
| |
| text_append_n (result, "<dd>", 4); |
| text_append (result, content); |
| text_append_n (result, "</dd>\n", 6); |
| } |
| |
| |
| 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); |
| } |
| |
| void |
| html_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); |
| } |
| } |
| |
| |
| |
| /* output units conversion functions */ |
| |
| 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) |
| { |
| if (self->output_units_conversion[unit_type].status > 0) |
| call_output_units_conversion (self, unit_type, output_unit, content, |
| result); |
| } |
| |
| void |
| html_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; |
| |
| if (html_in_string (self)) |
| return; |
| |
| if (!output_unit->tree_unit_directions[D_prev]) |
| { |
| if (!self->document->global_commands.maketitle) |
| 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); |
| } |
| |
| /* 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); |
| |
| 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; |
| } |
| } |
| |
| 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); |
| } |
| |
| 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; |
| |
| const char *special_unit_variety; |
| STRING_LIST *closed_strings; |
| size_t count_in_file = 0; |
| int level; |
| |
| if (html_in_string (self)) |
| return; |
| |
| special_unit_variety = output_unit->special_unit_variety; |
| number = find_string (&self->special_unit_varieties, |
| special_unit_variety); |
| |
| closed_strings = html_close_registered_sections_level (self, |
| self->current_filename.file_number, 0); |
| |
| 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); |
| |
| 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); |
| |
| /* This may happen with footnotes in regions that are not expanded, |
| like @copying or @titlepage */ |
| if (special_unit_body.end == 0) |
| { |
| free (special_unit_body.text); |
| return; |
| } |
| |
| classes = new_string_list (); |
| |
| 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); |
| |
| 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"); |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| |
| add_string (class, classes); |
| free (class); |
| |
| format_heading_text (self, 0, classes, heading, level, 0, 0, 0, result); |
| free (heading); |
| destroy_strings_list (classes); |
| text_append_n (result, "\n", 1); |
| |
| text_append (result, special_unit_body.text); |
| free (special_unit_body.text); |
| text_append (result, "</div>"); |
| |
| format_element_footer (self, unit_type, output_unit, content, unit_command, |
| result); |
| } |
| |
| |
| |
| /* special unit body formatting functions */ |
| |
| 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) |
| { |
| 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); |
| } |
| |
| 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) |
| { |
| char *table_of_contents = format_contents (self, CM_contents, 0, 0); |
| text_append (result, table_of_contents); |
| free (table_of_contents); |
| } |
| |
| 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 |
| 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) |
| { |
| 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 |
| 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) |
| { |
| size_t i; |
| const BUTTON_SPECIFICATION_LIST *buttons |
| = self->conf->SECTION_BUTTONS.o.buttons; |
| |
| 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); |
| } |
| |
| text_append_n (result, "<p>\n", 4); |
| |
| if (!buttons) |
| { |
| html_translate_convert_tree_append ( |
| "There are no buttons for this document.", self, 0, 0, |
| result, "ABOUT"); |
| text_append_n (result, "\n</p>\n", 6); |
| return; |
| } |
| |
| html_translate_convert_tree_append ( |
| " The buttons in the navigation panels have the following meaning:", |
| self, 0, 0, result, "ABOUT"); |
| 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); |
| html_translate_convert_tree_append ("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); |
| html_translate_convert_tree_append ("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); |
| html_translate_convert_tree_append ("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); |
| html_translate_convert_tree_append ("From 1.2.3 go to", self, 0, 0, |
| result, "ABOUT"); |
| text_append (result, "</th>\n </tr>\n"); |
| |
| 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 (button->type == BST_direction_info) |
| direction = button->b.button_info->direction; |
| else if (button->type == BST_direction) |
| direction = button->b.direction; |
| |
| if (direction < 0 || html_global_direction_text (self, direction)) |
| continue; |
| |
| text_append_n (result, " <tr>\n ", 11); |
| open_element_with_class (self, "td", &button_direction_about_classes, |
| result); |
| |
| /* 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 (button->type == BST_direction) |
| { |
| if (self->html_active_icons |
| && self->html_active_icons[direction]) |
| { |
| 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->html_active_icons[direction], 0); |
| text_append (result, button); |
| free (button); |
| } |
| 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); |
| } |
| } |
| 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); |
| |
| html_translate_convert_tree_append ( |
| " 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. "); |
| html_translate_convert_tree_append ("Section One", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "\n <ul>\n <li>1.1 "); |
| html_translate_convert_tree_append ("Subsection One-One", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "\n <ul>\n <li>...</li>\n" |
| " </ul>\n </li>\n <li>1.2 "); |
| html_translate_convert_tree_append ("Subsection One-Two", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "\n <ul>\n <li>1.2.1 "); |
| html_translate_convert_tree_append ("Subsubsection One-Two-One", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "</li>\n <li>1.2.2 "); |
| html_translate_convert_tree_append ("Subsubsection One-Two-Two", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "</li>\n <li>1.2.3 "); |
| html_translate_convert_tree_append ("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><== "); |
| html_translate_convert_tree_append ("Current Position", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, " </strong></li>\n <li>1.2.4 "); |
| html_translate_convert_tree_append ("Subsubsection One-Two-Four", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "</li>\n </ul>\n </li>\n <li>1.3 "); |
| html_translate_convert_tree_append ("Subsection One-Three", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "\n <ul>\n <li>...</li>\n" |
| " </ul>\n </li>\n <li>1.4 "); |
| html_translate_convert_tree_append ("Subsection One-Four", |
| self, 0, 0, result, "ABOUT"); |
| text_append (result, "</li>\n </ul>\n </li>\n</ul>\n"); |
| } |
| |
| |
| |
| /* order should be the same as in Texinfo::Convert::HTML output_internal_links |
| list */ |
| static const enum command_id label_cmd_types_ids[] = { |
| CM_node, CM_anchor, CM_namedanchor, CM_float |
| }; |
| |
| #define LABEL_CMD_TYPES_NR (sizeof (label_cmd_types_ids) \ |
| / sizeof (label_cmd_types_ids[0])) |
| |
| /* This is called from the main program on the converter. */ |
| char * |
| html_output_internal_links (CONVERTER *self) |
| { |
| TEXT out_string; |
| size_t document_units_descriptor |
| = self->output_units_descriptors[OUDT_units]; |
| const OUTPUT_UNIT_LIST *output_units; |
| char *language; |
| INDEX_SORTED_BY_LETTER *index_entries_by_letter |
| = get_converter_indices_sorted_by_letter (self, &language); |
| |
| text_init (&out_string); |
| |
| output_units = retrieve_output_units (self->document, |
| document_units_descriptor); |
| |
| if (output_units) |
| { |
| size_t i; |
| for (i = 0; i < output_units->number; i++) |
| { |
| const OUTPUT_UNIT *output_unit = output_units->list[i]; |
| const ELEMENT *command = output_unit->uc.unit_command; |
| |
| if (command) |
| { /* Use "" for filename, to force a filename in href. */ |
| char *href = html_command_href (self, command, "", 0, 0); |
| char *text = 0; |
| TREE_ADDED_ELEMENTS *command_tree |
| = html_internal_command_tree (self, command, 0); |
| |
| if (command_tree->tree) |
| { |
| text = convert_to_text (command_tree->tree, |
| self->convert_text_options); |
| } |
| |
| if (href || text) |
| { |
| if (href) |
| { |
| text_append (&out_string, href); |
| free (href); |
| } |
| text_append_n (&out_string, "\tunit\t", 6); |
| if (text) |
| { |
| text_append (&out_string, text); |
| free (text); |
| } |
| text_append_n (&out_string, "\n", 1); |
| } |
| } |
| } |
| } |
| |
| if (self->document) |
| { |
| if (self->document->sections_list.number > 0) |
| { |
| size_t i; |
| for (i = 0; i < self->document->sections_list.number; i++) |
| { |
| const SECTION_RELATIONS *section_relations |
| = self->document->sections_list.list[i]; |
| const ELEMENT *command = section_relations->element; |
| char *href = html_command_href (self, command, "", 0, 0); |
| char *text = 0; |
| TREE_ADDED_ELEMENTS *command_tree |
| = html_internal_command_tree (self, command, 0); |
| enum command_id level_corrected_cmd |
| = section_level_adjusted_command_name (command); |
| const char *command_name |
| = builtin_command_name (level_corrected_cmd); |
| |
| if (command_tree->tree) |
| { |
| text = convert_to_text (command_tree->tree, |
| self->convert_text_options); |
| } |
| |
| if (href || text) |
| { |
| if (href) |
| { |
| text_append (&out_string, href); |
| free (href); |
| } |
| text_append_n (&out_string, "\tsection\t", 9); |
| text_append (&out_string, command_name); |
| text_append_n (&out_string, " ", 1); |
| if (text) |
| { |
| text_append (&out_string, text); |
| free (text); |
| } |
| text_append_n (&out_string, "\n", 1); |
| } |
| } |
| } |
| |
| if (identifiers_target_number (&self->document->identifiers_target)) |
| { |
| /* use labels_list and not identifiers_target to process in the |
| document order */ |
| const LABEL_LIST *label_targets = &self->document->labels_list; |
| size_t i; |
| const ELEMENT *target_element; |
| static CONST_ELEMENT_LIST commands_lists[LABEL_CMD_TYPES_NR]; |
| size_t j; |
| |
| for (i = 0; i < label_targets->number; i++) |
| { |
| LABEL *label = &label_targets->list[i]; |
| |
| if (!label->identifier || label->reference) |
| continue; |
| |
| target_element = label->element; |
| for (j = 0; j < LABEL_CMD_TYPES_NR; j++) |
| { |
| if (target_element->e.c->cmd == label_cmd_types_ids[j]) |
| { |
| add_to_const_element_list(&commands_lists[j], |
| target_element); |
| break; |
| } |
| } |
| } |
| |
| for (j = 0; j < LABEL_CMD_TYPES_NR; j++) |
| { |
| if (commands_lists[j].number > 0) |
| { |
| const char *cmdname |
| = builtin_command_name (label_cmd_types_ids[j]); |
| for (i = 0; i < commands_lists[j].number; i++) |
| { |
| target_element = commands_lists[j].list[i]; |
| const ELEMENT *label_element |
| = get_label_element (target_element); |
| char *text = 0; |
| char *href = html_command_href (self, target_element, |
| "", 0, 0); |
| if (label_element) |
| { |
| text = convert_to_text (label_element, |
| self->convert_text_options); |
| } |
| |
| if (href || text) |
| { |
| if (href) |
| { |
| text_append (&out_string, href); |
| free (href); |
| } |
| text_printf (&out_string, "\t%s\t", cmdname); |
| if (text) |
| { |
| text_append (&out_string, text); |
| free (text); |
| } |
| text_append_n (&out_string, "\n", 1); |
| } |
| } |
| |
| /* reset the list */ |
| commands_lists[i].number = 0; |
| } |
| } |
| } |
| } |
| #undef LABEL_CMD_TYPES_NR |
| |
| if (index_entries_by_letter) |
| { |
| size_t i; |
| for (i = 0; i < self->sorted_index_names.number; i++) |
| { |
| const INDEX *current_index = self->sorted_index_names.list[i]; |
| const INDEX_SORTED_BY_LETTER *index_sorted; |
| int found = 0; |
| size_t l; |
| |
| if (current_index->merged_in) |
| continue; |
| |
| for (index_sorted = index_entries_by_letter; index_sorted->name; |
| index_sorted++) |
| { |
| if (!strcmp (current_index->name, index_sorted->name)) |
| { |
| found = 1; |
| break; |
| } |
| } |
| if (!found) |
| { |
| fprintf (stderr, "BUG: index %s not found in sorted indices\n", |
| current_index->name); |
| continue; |
| } |
| |
| for (l = 0; l < index_sorted->letter_number; l++) |
| { |
| const LETTER_INDEX_ENTRIES *letter_entry |
| = &index_sorted->letter_entries[l]; |
| size_t j; |
| for (j = 0; j < letter_entry->entries_number; j++) |
| { |
| const INDEX_ENTRY *index_entry_ref = letter_entry->entries[j]; |
| const ELEMENT *main_entry_element |
| = index_entry_ref->entry_element; |
| const ELEMENT *seeentry; |
| const ELEMENT *seealso; |
| char *href; |
| char *index_term; |
| int in_code; |
| size_t entry_index_nr; |
| const INDEX *entry_index; |
| ELEMENT *entry_content_element; |
| ELEMENT *entry_ref_tree; |
| ELEMENT_LIST *subentries_tree; |
| |
| seeentry = index_entry_referred_entry (main_entry_element, |
| CM_seeentry); |
| if (seeentry) |
| continue; |
| seealso = index_entry_referred_entry (main_entry_element, |
| CM_seealso); |
| if (seealso) |
| continue; |
| |
| href = html_command_href (self, main_entry_element, "", 0, 0); |
| |
| /* Obtain term by converting to text */ |
| 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]; |
| in_code = entry_index->in_code; |
| if (in_code) |
| self->convert_text_options->code_state++; |
| |
| entry_ref_tree = new_element (ET_NONE); |
| |
| add_to_contents_as_array (entry_ref_tree, |
| entry_content_element); |
| subentries_tree |
| = comma_index_subentries_tree (main_entry_element, 0); |
| if (subentries_tree) |
| { |
| insert_list_slice_into_contents (entry_ref_tree, |
| entry_ref_tree->e.c->contents.number, |
| subentries_tree, 0, |
| subentries_tree->number); |
| } |
| |
| index_term = convert_to_text (entry_ref_tree, |
| self->convert_text_options); |
| |
| if (in_code) |
| self->convert_text_options->code_state--; |
| |
| if (subentries_tree) |
| free_comma_index_subentries_tree (subentries_tree); |
| |
| destroy_element (entry_ref_tree); |
| |
| if (index_term |
| && index_term[strspn (index_term, whitespace_chars)] != '\0') |
| { |
| if (href) |
| text_append (&out_string, href); |
| text_printf (&out_string, "\t%s\t", index_sorted->name); |
| text_append (&out_string, index_term); |
| text_append_n (&out_string, "\n", 1); |
| } |
| |
| free (href); |
| free (index_term); |
| } |
| } |
| } |
| } |
| |
| if (out_string.end == 0) |
| { |
| free (out_string.text); |
| return 0; |
| } |
| else |
| return out_string.text; |
| } |
| |