| /* 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 <stdlib.h> |
| #include <stdio.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <sys/stat.h> |
| #include <dirent.h> |
| /* for euidaccess. Not portable, use gnulib */ |
| #include <unistd.h> |
| |
| #include "text.h" |
| #include "command_ids.h" |
| #include "element_types.h" |
| #include "tree_types.h" |
| #include "converter_types.h" |
| #include "html_conversion_data.h" |
| #include "html_css_data.h" |
| /* bug fatal isascii_alnum isascii_alpha read_var_len */ |
| #include "base_utils.h" |
| /* new_element */ |
| #include "tree.h" |
| #include "errors.h" |
| #include "builtin_commands.h" |
| #include "command_stack.h" |
| #include "utils.h" |
| /* option_force_conf option_set_conf */ |
| #include "customization_options.h" |
| #include "extra.h" |
| #include "debug.h" |
| #include "targets.h" |
| /* unicode_character_brace_no_arg_commands */ |
| #include "unicode.h" |
| #include "convert_to_texinfo.h" |
| #include "node_name_normalization.h" |
| #include "manipulate_indices.h" |
| /* nobrace_symbol_text text_brace_no_arg_commands */ |
| #include "convert_to_text.h" |
| /* free_translation_cache */ |
| #include "translations.h" |
| #include "output_unit.h" |
| #include "html_conversion_state.h" |
| /* converter_encoded_output_file_name */ |
| #include "convert_utils.h" |
| /* no_brace_command_accent_upper_case |
| xml_text_entity_no_arg_commands_formatting */ |
| #include "converter.h" |
| #include "convert_indices.h" |
| #include "call_html_perl_function.h" |
| #include "hashmap.h" |
| #include "format_html.h" |
| /* txi_paths_info */ |
| #include "document.h" |
| #include "conf.h" |
| #include "api.h" |
| /* html_complete_no_arg_commands_formatting html_run_stage_handlers |
| html_add_to_files_source_info html_find_file_source_info |
| html_reset_shared_conversion_state */ |
| #include "convert_html.h" |
| #include "html_prepare_converter.h" |
| #include "html_conversion_api.h" |
| #include "html_converter_api.h" |
| |
| typedef struct CMD_VARIETY { |
| enum command_id cmd; |
| char *variety; |
| } CMD_VARIETY; |
| |
| typedef struct COMMAND_ID_ARGS_SPECIFICATION { |
| enum command_id cmd; |
| unsigned long flags[MAX_COMMAND_ARGS_NR]; |
| } COMMAND_ID_ARGS_SPECIFICATION; |
| |
| /* in main/html_css_data.c */ |
| extern const CSS_SELECTOR_STYLE base_default_css_element_class_styles[]; |
| |
| const char *html_global_unit_direction_names[] = { |
| #define hgdt_name(name) #name, |
| HTML_GLOBAL_DIRECTIONS_LIST |
| #undef hgdt_name |
| }; |
| |
| const char *html_formatting_reference_names[] = { |
| #define html_fr_reference(name) #name, |
| HTML_FORMATTING_REFERENCES_LIST |
| #undef html_fr_reference |
| }; |
| |
| static CSS_SELECTOR_STYLE_LIST default_css_element_class_styles; |
| |
| COMMAND_ARGS_SPECIFICATION html_command_args_flags[BUILTIN_CMD_NUMBER]; |
| |
| /* should match enum htmlxref_split_type */ |
| const char *htmlxref_split_type_names[htmlxref_split_type_chapter + 1] = |
| { |
| "mono", "node", "section", "chapter" |
| }; |
| |
| static COMMAND_ID_ARGS_SPECIFICATION default_commands_args[] = { |
| {CM_anchor, {F_AFT_monospacestring}}, |
| {CM_namedanchor, {F_AFT_monospacestring, F_AFT_normal}}, |
| {CM_email, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal}}, |
| {CM_footnote, {F_AFT_none}}, /* no flag */ |
| {CM_printindex, {F_AFT_none}}, /* no flag */ |
| {CM_uref, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal, F_AFT_normal}}, |
| {CM_url, {F_AFT_url | F_AFT_monospacestring, F_AFT_normal, F_AFT_normal}}, |
| {CM_sp, {F_AFT_none}}, /* no flag */ |
| {CM_inforef, {F_AFT_monospace, F_AFT_normal, F_AFT_filenametext}}, |
| {CM_xref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, F_AFT_normal}}, |
| {CM_pxref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, F_AFT_normal}}, |
| {CM_ref, {F_AFT_monospace, F_AFT_normal, F_AFT_normal, F_AFT_filenametext, F_AFT_normal}}, |
| {CM_link, {F_AFT_monospace, F_AFT_normal, F_AFT_filenametext}}, |
| {CM_image, {F_AFT_url | F_AFT_filenametext | F_AFT_monospacestring, F_AFT_filenametext, F_AFT_filenametext, F_AFT_string | F_AFT_normal, F_AFT_filenametext}}, |
| {CM_inlinefmt, {F_AFT_monospacetext, F_AFT_normal}}, |
| {CM_inlinefmtifelse, {F_AFT_monospacetext, F_AFT_normal, F_AFT_normal}}, |
| {CM_inlineraw, {F_AFT_monospacetext, F_AFT_raw}}, |
| {CM_inlineifclear, {F_AFT_monospacetext, F_AFT_normal}}, |
| {CM_inlineifset, {F_AFT_monospacetext, F_AFT_normal}}, |
| {CM_item, {F_AFT_none}}, /* no flag */ |
| {CM_itemx, {F_AFT_none}}, /* no flag */ |
| {CM_value, {F_AFT_monospacestring}}, |
| {CM_abbr, {F_AFT_normal, F_AFT_string | F_AFT_normal}}, |
| {CM_acronym, {F_AFT_normal, F_AFT_string | F_AFT_normal}}, |
| }; |
| |
| /* types that are in code style in the default case. */ |
| static enum element_type default_code_types[] = { |
| 0, |
| }; |
| |
| const PRE_CLASS_TYPE_INFO default_pre_class_types[] = { |
| {ET_menu_comment, "menu-comment"}, |
| {0, 0} |
| }; |
| |
| static enum command_id quoted_style_commands[] = { |
| CM_samp, 0 |
| }; |
| |
| static enum command_id HTML_align_cmd[] = { |
| CM_raggedright, CM_flushleft, CM_flushright, CM_center, 0 |
| }; |
| |
| static enum command_id additional_format_context_cmd[] = { |
| CM_tab, CM_item, CM_itemx, CM_headitem, 0 |
| }; |
| |
| /* should correspond to enum html_special_character */ |
| /* HTML textual entity, UTF-8 encoded, unicode point, HTML numeric entity */ |
| const char *special_characters_formatting[SC_non_breaking_space+1][4] = { |
| {"¶", "\xC2\xB6", "00B6", "¶"}, |
| {"‘", "\xE2\x80\x98", "2018", "‘"}, |
| {"’", "\xE2\x80\x99", "2019", "’"}, |
| {"•", "\xE2\x80\xA2", "2022", "•"}, |
| {" ", "\xC2\xA0", "00A0", " "}, |
| }; |
| |
| CMD_VARIETY command_special_unit_variety[] = { |
| {CM_contents, "contents"}, |
| {CM_shortcontents, "shortcontents"}, |
| {CM_summarycontents, "shortcontents"}, |
| {CM_footnote, "footnotes"}, |
| {0, 0}, |
| }; |
| |
| static const enum command_id conf_for_special_units[] |
| = {CM_footnotestyle, 0}; |
| |
| static const enum command_id contents_elements_options[] |
| = {CM_contents, CM_shortcontents, CM_summarycontents, 0}; |
| |
| static const enum command_id informative_global_commands[] |
| = {CM_documentlanguage, CM_documentscript, CM_footnotestyle, |
| CM_xrefautomaticsectiontitle, |
| CM_deftypefnnewline, 0}; |
| |
| static enum element_type ignored_types[] = { |
| ET_ignorable_spaces_after_command, |
| ET_postamble_after_end, |
| ET_preamble_before_beginning, |
| ET_preamble_before_setfilename, |
| ET_spaces_at_end, |
| ET_spaces_before_paragraph, |
| ET_spaces_after_close_brace, |
| ET_spaces_before_argument, |
| ET_spaces_after_argument, |
| ET_arguments_line, 0 |
| }; |
| |
| HTML_COMMAND_STRUCT html_commands_data[BUILTIN_CMD_NUMBER]; |
| |
| static HTML_NO_ARG_COMMAND_CONVERSION default_no_arg_commands_formatting |
| [BUILTIN_CMD_NUMBER][NO_ARG_COMMAND_CONTEXT_NR]; |
| |
| static HTML_STYLE_COMMAND_CONVERSION default_style_commands_formatting |
| [BUILTIN_CMD_NUMBER][STYLE_COMMAND_CONTEXT_NR]; |
| |
| static COMMAND_STACK preformatted_cmd_list; |
| static COMMAND_STACK def_cmd_list; |
| |
| static char *unicode_entities[BUILTIN_CMD_NUMBER]; |
| |
| COMMAND_ID_LIST no_arg_formatted_cmd; |
| COMMAND_ID_LIST style_formatted_cmd; |
| COMMAND_ID_LIST accent_cmd; |
| COMMAND_ID_LIST format_raw_cmd; |
| |
| |
| |
| /* Initialization code called only once. Setup some base/default data */ |
| |
| static void |
| register_format_context_command (enum command_id cmd) |
| { |
| html_commands_data[cmd].flags |= HF_format_context; |
| } |
| |
| static void register_pre_class_command (enum command_id cmd, |
| enum command_id main_cmd) |
| { |
| if (main_cmd) |
| html_commands_data[cmd].pre_class_cmd = main_cmd; |
| else |
| html_commands_data[cmd].pre_class_cmd = cmd; |
| |
| html_commands_data[cmd].flags |= HF_pre_class; |
| } |
| |
| /* NOTE relatively generic */ |
| static void |
| initialize_cmd_list (COMMAND_ID_LIST *cmd_list, size_t size, size_t number) |
| { |
| cmd_list->list = (enum command_id *) malloc |
| (size * sizeof (enum command_id)); |
| cmd_list->number = number; |
| if (number) |
| { |
| memset (cmd_list->list, 0, number * sizeof (enum command_id)); |
| } |
| } |
| |
| static void |
| set_no_arg_commands_formatting (HTML_NO_ARG_COMMAND_CONVERSION *spec, |
| char *text) |
| { |
| spec->text = text; |
| spec->unset = 0; |
| } |
| |
| static char * |
| get_special_list_mark_css_string_no_arg_command (enum command_id cmd) |
| { |
| int s; |
| for (s = 0; |
| special_list_mark_css_string_no_arg_command[s].cmd > 0; |
| s++) |
| { |
| if (special_list_mark_css_string_no_arg_command[s].cmd == cmd) |
| { |
| return special_list_mark_css_string_no_arg_command[s].string; |
| break; |
| } |
| } |
| return 0; |
| } |
| |
| void |
| clear_css_selector_style_list (CSS_SELECTOR_STYLE_LIST *selector_styles) |
| { |
| size_t j; |
| |
| for (j = 0; j < selector_styles->number; j++) |
| { |
| CSS_SELECTOR_STYLE *selector_style = &selector_styles->list[j]; |
| free (selector_style->selector); |
| free (selector_style->style); |
| } |
| /* The following is not needed, as next the list is freed, or |
| set_css_selector_style_list_size is called, which sets number. |
| selector_styles->number = 0; |
| */ |
| } |
| |
| static void |
| set_css_selector_style_list_size (CSS_SELECTOR_STYLE_LIST *selector_styles, |
| size_t size) |
| { |
| if (selector_styles->space < size) |
| { |
| selector_styles->list = (CSS_SELECTOR_STYLE *) |
| realloc (selector_styles->list, size * sizeof (CSS_SELECTOR_STYLE)); |
| selector_styles->space = size; |
| } |
| selector_styles->number = size; |
| } |
| |
| /* set information that is independent of customization, only called once */ |
| /* depends on xml formatting data setup in setup_converter_generic. |
| When the libraries initialization functions are called when loading XS |
| modules, the Convert::Converter module should be loaded before |
| the Convert::HTML module. */ |
| void |
| html_format_setup (enum converter_format format) |
| { |
| int i; |
| int no_arg_formatted_cmd_nr = 0; |
| int accent_cmd_nr = 0; |
| int style_formatted_cmd_nr = 0; |
| int format_raw_cmd_nr = 0; |
| int no_arg_formatted_cmd_idx; |
| int default_commands_args_nr |
| = sizeof (default_commands_args) / sizeof (default_commands_args[0]); |
| int max_args = MAX_COMMAND_ARGS_NR; |
| CSS_SELECTOR_STYLE *display_preformatted_css_selector_style; |
| TEXT css_string_text; |
| |
| const enum command_id indented_format[] = { |
| CM_example, CM_display, CM_lisp, 0 |
| }; |
| |
| html_default_options_setup (); |
| |
| set_css_selector_style_list_size (&default_css_element_class_styles, |
| BASE_DEFAULT_CSS_ELEMENT_CLASS_STYLE_NR); |
| for (i = 0; i < BASE_DEFAULT_CSS_ELEMENT_CLASS_STYLE_NR; i++) |
| { |
| CSS_SELECTOR_STYLE *selector_style |
| = &default_css_element_class_styles.list[i]; |
| selector_style->selector |
| = base_default_css_element_class_styles[i].selector; |
| selector_style->style |
| = base_default_css_element_class_styles[i].style; |
| } |
| sort_css_element_class_styles (&default_css_element_class_styles); |
| |
| display_preformatted_css_selector_style |
| = find_css_selector_style (&default_css_element_class_styles, |
| "pre.display-preformatted"); |
| html_css_set_selector_style (&default_css_element_class_styles, |
| "pre.format-preformatted", |
| display_preformatted_css_selector_style->style); |
| |
| for (i = 0; i < default_commands_args_nr; i++) |
| { |
| /* we file the status for specified commands, to distinguish them |
| but it is not actually used in the code, as we default to |
| normal for unspecified commands too */ |
| enum command_id cmd = default_commands_args[i].cmd; |
| html_command_args_flags[cmd].status = 1; |
| memcpy (&html_command_args_flags[cmd].flags, |
| &default_commands_args[i].flags, |
| max_args * sizeof (unsigned long)); |
| } |
| |
| for (i = 0; indented_format[i]; i++) |
| { |
| char *css_selector; |
| enum command_id cmd = indented_format[i]; |
| html_commands_data[cmd].flags |= HF_indented_preformatted; |
| |
| /* div.lisp is output as div.example */ |
| if (cmd != CM_lisp) |
| { |
| xasprintf (&css_selector, "div.%s", builtin_command_name (cmd)); |
| html_css_set_selector_style (&default_css_element_class_styles, |
| css_selector, |
| "margin-left: 3.2em"); |
| free (css_selector); |
| } |
| } |
| |
| for (i = 0; small_block_associated_command[i][0]; i++) |
| { |
| enum command_id small_cmd = small_block_associated_command[i][0]; |
| enum command_id cmd = small_block_associated_command[i][1]; |
| if (builtin_command_data[cmd].flags & CF_preformatted) |
| { |
| register_pre_class_command (small_cmd, cmd); |
| } |
| html_commands_data[small_cmd].flags |= HF_small_block_command; |
| if (html_commands_data[cmd].flags & HF_indented_preformatted) |
| html_commands_data[small_cmd].flags |= HF_indented_preformatted; |
| } |
| |
| for (i = 0; no_brace_command_accent_upper_case[i][0]; i++) |
| { |
| enum command_id cmd = no_brace_command_accent_upper_case[i][0]; |
| enum command_id upper_case_cmd = no_brace_command_accent_upper_case[i][1]; |
| html_commands_data[cmd].upper_case_cmd = upper_case_cmd; |
| } |
| |
| /* set flags */ |
| /* also count the number of no_arg_formatted_cmd, style_formatted_cmd and |
| accent_cmd commands and other types of commands */ |
| for (i = 1; i < BUILTIN_CMD_NUMBER; i++) |
| { |
| if (xml_text_entity_no_arg_commands_formatting[i]) |
| no_arg_formatted_cmd_nr++; |
| |
| if (html_style_commands_element[i] |
| || (builtin_command_data[i].flags & CF_brace |
| && (builtin_command_data[i].data == BRACE_style_other |
| || builtin_command_data[i].data == BRACE_style_code |
| || builtin_command_data[i].data == BRACE_style_no_code))) |
| { |
| html_commands_data[i].flags |= HF_style_command; |
| style_formatted_cmd_nr++; |
| } |
| |
| if (builtin_command_data[i].flags & CF_brace |
| && builtin_command_data[i].data == BRACE_accent) |
| { |
| accent_cmd_nr++; |
| html_command_args_flags[i].status = 1; |
| html_command_args_flags[i].flags[0] = F_AFT_none; |
| } |
| |
| if ((builtin_command_data[i].flags & CF_block |
| && builtin_command_data[i].data != BLOCK_format_raw) |
| || builtin_command_data[i].flags & CF_root) |
| register_format_context_command (i); |
| |
| if (builtin_command_data[i].flags & CF_preformatted |
| || builtin_command_data[i].flags & CF_root) |
| html_commands_data[i].flags |= HF_composition_context; |
| |
| if (builtin_command_data[i].flags & CF_block) |
| { |
| if (builtin_command_data[i].data == BLOCK_menu) |
| html_commands_data[i].flags |= HF_composition_context; |
| else if (builtin_command_data[i].data == BLOCK_format_raw) |
| { |
| html_commands_data[i].flags |= HF_format_raw; |
| format_raw_cmd_nr++; |
| } |
| } |
| |
| if (builtin_command_data[i].flags & CF_preformatted) |
| { |
| if (!(html_commands_data[i].flags & HF_pre_class)) |
| register_pre_class_command (i, 0); |
| push_command (&preformatted_cmd_list, i); |
| } |
| |
| if (builtin_command_data[i].flags & CF_def) |
| push_command (&def_cmd_list, i); |
| } |
| register_pre_class_command (CM_verbatim, 0); |
| register_pre_class_command (CM_menu, 0); |
| for (i = 0; additional_format_context_cmd[i]; i++) |
| register_format_context_command (additional_format_context_cmd[i]); |
| |
| /* do not consider indicateurl to be a style command, it has a different |
| formatting function */ |
| html_commands_data[CM_indicateurl].flags &= ~HF_style_command; |
| |
| for (i = 0; HTML_align_cmd[i]; i++) |
| { |
| enum command_id cmd = HTML_align_cmd[i]; |
| html_commands_data[cmd].flags |= HF_HTML_align | HF_composition_context; |
| } |
| |
| html_commands_data[CM_float].flags |= HF_composition_context; |
| |
| initialize_cmd_list (&no_arg_formatted_cmd, no_arg_formatted_cmd_nr, |
| no_arg_formatted_cmd_nr); |
| initialize_cmd_list (&accent_cmd, accent_cmd_nr, 0); |
| initialize_cmd_list (&style_formatted_cmd, style_formatted_cmd_nr, 0); |
| initialize_cmd_list (&format_raw_cmd, format_raw_cmd_nr, 0); |
| |
| for (i = 0; quoted_style_commands[i]; i++) |
| { |
| enum command_id cmd = quoted_style_commands[i]; |
| enum conversion_context cctx; |
| for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++) |
| { |
| default_style_commands_formatting[cmd][cctx].quote = 1; |
| } |
| } |
| |
| no_arg_formatted_cmd_idx = 0; |
| for (i = 0; i < BUILTIN_CMD_NUMBER; i++) |
| { |
| if (builtin_command_data[i].flags & CF_brace |
| && builtin_command_data[i].data == BRACE_accent) |
| { |
| accent_cmd.list[accent_cmd.number] = i; |
| accent_cmd.number++; |
| } |
| else if (html_commands_data[i].flags & HF_format_raw) |
| { |
| format_raw_cmd.list[format_raw_cmd.number] = i; |
| format_raw_cmd.number++; |
| } |
| |
| if (html_commands_data[i].flags & HF_style_command) |
| { |
| style_formatted_cmd.list[style_formatted_cmd.number] = i; |
| style_formatted_cmd.number++; |
| |
| if (html_style_commands_element[i]) |
| { |
| enum conversion_context cctx; |
| for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++) |
| { |
| /* the default_style_commands_formatting contains const char only |
| but the type is not const, so drop the const with a cast */ |
| default_style_commands_formatting[i][cctx].element |
| = (char *) html_style_commands_element[i]; |
| } |
| } |
| } |
| |
| if (xml_text_entity_no_arg_commands_formatting[i]) |
| { |
| /* the value is never modified but the struct field type is not const |
| so need to cast to drop const */ |
| default_no_arg_commands_formatting[i][HCC_type_normal].text |
| = (char *)xml_text_entity_no_arg_commands_formatting[i]; |
| |
| no_arg_formatted_cmd.list[no_arg_formatted_cmd_idx] = i; |
| no_arg_formatted_cmd_idx++; |
| |
| /* preset unset for string and preformatted contexts */ |
| /* css_string coverage is checked when setting css string context |
| values below and unset is set explicitly if needed */ |
| default_no_arg_commands_formatting[i][HCC_type_string].unset = 1; |
| default_no_arg_commands_formatting[i][HCC_type_preformatted].unset = 1; |
| } |
| } |
| |
| default_style_commands_formatting[CM_sc][HCC_type_preformatted].element |
| = "span"; |
| |
| /* modify normal context values and add other contexts values, removing |
| unset. Should only be for commands with normal context already set. |
| */ |
| default_no_arg_commands_formatting[CM_SPACE][HCC_type_normal].text = " "; |
| default_no_arg_commands_formatting[CM_TAB][HCC_type_normal].text = " "; |
| default_no_arg_commands_formatting[CM_NEWLINE][HCC_type_normal].text = " "; |
| |
| default_no_arg_commands_formatting[CM_enddots][HCC_type_normal].text = "..."; |
| default_no_arg_commands_formatting[CM_enddots][HCC_type_normal].element |
| = "small"; |
| |
| set_no_arg_commands_formatting ( |
| &default_no_arg_commands_formatting[CM_dots][HCC_type_preformatted], "..."); |
| set_no_arg_commands_formatting ( |
| &default_no_arg_commands_formatting[CM_enddots][HCC_type_preformatted], "..."); |
| |
| default_no_arg_commands_formatting[CM_ASTERISK][HCC_type_normal].text = "<br>"; |
| set_no_arg_commands_formatting ( |
| &default_no_arg_commands_formatting[CM_ASTERISK][HCC_type_preformatted], "\n"); |
| |
| for (i = 0; i < no_arg_formatted_cmd_nr; i++) |
| { |
| enum command_id cmd = no_arg_formatted_cmd.list[i]; |
| /* prepare unicode numeric entities. Freed at exit */ |
| if (unicode_character_brace_no_arg_commands[cmd].codepoint) |
| { |
| unsigned long point_nr |
| = strtoul (unicode_character_brace_no_arg_commands[cmd].codepoint, |
| NULL, 16); |
| char *entity; |
| xasprintf (&entity, "&#%lu;", point_nr); |
| unicode_entities[cmd] = entity; |
| } |
| |
| /* css_strings */ |
| if (cmd == CM_ASTERISK) |
| default_no_arg_commands_formatting[cmd][HCC_type_css_string].text |
| = "\\A "; |
| else if (cmd == CM_error) |
| { |
| default_no_arg_commands_formatting[cmd][HCC_type_css_string].text |
| = 0; |
| default_no_arg_commands_formatting[cmd][HCC_type_css_string].unset |
| = 1; |
| } |
| else if (unicode_character_brace_no_arg_commands[cmd].css_string) |
| { |
| unsigned long point_nr |
| = strtoul (unicode_character_brace_no_arg_commands[cmd].codepoint, |
| NULL, 16); |
| if (point_nr < 128) /* 7bit ascii */ |
| default_no_arg_commands_formatting[cmd][HCC_type_css_string].text |
| = (char *)unicode_character_brace_no_arg_commands[cmd].text; |
| else |
| /* the value is never modified but the struct field type is not const |
| so need to cast to drop const */ |
| default_no_arg_commands_formatting[cmd][HCC_type_css_string].text |
| = (char *)unicode_character_brace_no_arg_commands[cmd].css_string; |
| } |
| else if (nobrace_symbol_text[cmd]) |
| default_no_arg_commands_formatting[cmd][HCC_type_css_string].text |
| /* the value is never modified but the struct field type is not const |
| so need to cast to drop const */ |
| = (char *)nobrace_symbol_text[cmd]; |
| else if (text_brace_no_arg_commands[cmd]) |
| /* the value is never modified but the struct field type is not const |
| so need to cast to drop const */ |
| default_no_arg_commands_formatting[cmd][HCC_type_css_string].text |
| = (char *)text_brace_no_arg_commands[cmd]; |
| else |
| fprintf (stderr, "BUG: %s: no css_string\n", |
| builtin_command_data[cmd].cmdname); |
| } |
| |
| /* w not in css_string, set the corresponding default_css_element_class_styles |
| especially, which also has none and not w in the class */ |
| html_css_set_selector_style (&default_css_element_class_styles, |
| "ul.mark-none", "list-style-type: none"); |
| |
| text_init (&css_string_text); |
| /* setup default_css_element_class_styles for mark commands based on |
| css strings */ |
| for (i = 0; i < no_arg_formatted_cmd_nr; i++) |
| { |
| enum command_id cmd = no_arg_formatted_cmd.list[i]; |
| if (default_no_arg_commands_formatting[cmd][HCC_type_css_string].text |
| && builtin_command_data[cmd].flags & CF_brace) |
| { |
| char *selector; |
| text_append_n (&css_string_text, "list-style-type: ", 17); |
| if (cmd == CM_bullet) |
| text_append_n (&css_string_text, "disc", 4); |
| else |
| { |
| char *css_string; |
| const char *p; |
| char *special_list_mark_command |
| = get_special_list_mark_css_string_no_arg_command (cmd); |
| if (special_list_mark_command) |
| css_string = special_list_mark_command; |
| else |
| css_string |
| = default_no_arg_commands_formatting[cmd][HCC_type_css_string].text; |
| p = html_after_escaped_characters (css_string); |
| text_append_n (&css_string_text, "\"", 1); |
| if (p && !*p) |
| { |
| text_append_n (&css_string_text, css_string, |
| p - css_string - 1); |
| } |
| else |
| text_append (&css_string_text, css_string); |
| text_append_n (&css_string_text, "\"", 1); |
| } |
| xasprintf (&selector, "ul.mark-%s", builtin_command_name (cmd)); |
| html_css_set_selector_style (&default_css_element_class_styles, |
| selector, css_string_text.text); |
| free (selector); |
| text_reset (&css_string_text); |
| } |
| } |
| free (css_string_text.text); |
| } |
| |
| /* ALTIMP Texinfo::Convert::HTML builtin_default_css_text */ |
| /* should be called after html_format_setup */ |
| char * |
| html_builtin_default_css_text (void) |
| { |
| TEXT css_text; |
| size_t i; |
| |
| text_init (&css_text); |
| text_append (&css_text, ""); |
| |
| for (i = 0; i < default_css_element_class_styles.number; i++) |
| { |
| CSS_SELECTOR_STYLE *selector_style |
| = &default_css_element_class_styles.list[i]; |
| if (strlen (selector_style->style)) |
| text_printf (&css_text, "%s {%s}\n", selector_style->selector, |
| selector_style->style); |
| } |
| |
| return css_text.text; |
| } |
| |
| |
| |
| /* converter_initialize functions. |
| |
| beginning of converter initialize, setup special units info, apply |
| defaults based on customization variables. |
| Apply specific customizations (from Perl) */ |
| |
| /* This should be constant at the end of the converter initialization */ |
| int |
| html_nr_string_directions (const CONVERTER *self) |
| { |
| /* The corresponding direction without FirstInFile are used instead |
| of FirstInFile*, so the directions_strings are not set */ |
| return NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR |
| + (int) self->special_unit_varieties.number |
| + (int) self->added_global_units_directions.number |
| + (int) self->customized_global_text_directions.number; |
| } |
| |
| /* here because it is used in files also needing html_nr_string_directions */ |
| void |
| html_free_customized_global_units_directions ( |
| DIRECTION_NODE_NAME_LIST *customized_global_units_directions) |
| { |
| size_t j; |
| for (j = 0; j < customized_global_units_directions->number; j++) |
| { |
| DIRECTION_NODE_NAME *direction_node_name |
| = &customized_global_units_directions->list[j]; |
| free (direction_node_name->direction); |
| free (direction_node_name->node_name); |
| } |
| free (customized_global_units_directions->list); |
| customized_global_units_directions->list = 0; |
| customized_global_units_directions->number = 0; |
| } |
| |
| /* only used from XS */ |
| void |
| html_set_global_direction (CONVERTER *self, |
| DIRECTION_NODE_NAME_LIST *customized_global_units_directions, |
| const char *direction, const char *node_name) |
| { |
| DIRECTION_NODE_NAME *direction_node_name = 0; |
| int main_directions_idx = html_get_direction_index (self, direction); |
| if (main_directions_idx < 0) |
| { |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "not setting an unknown direction: %s", |
| direction); |
| return; |
| } |
| |
| if (customized_global_units_directions->number > 0) |
| { |
| size_t i; |
| for (i = 0; i < customized_global_units_directions->number; i++) |
| { |
| DIRECTION_NODE_NAME *current |
| = &customized_global_units_directions->list[i]; |
| if (!strcmp (current->direction, direction)) |
| { |
| direction_node_name = current; |
| break; |
| } |
| } |
| if (!direction_node_name) |
| customized_global_units_directions->list |
| = realloc ( |
| customized_global_units_directions->list, |
| sizeof (DIRECTION_NODE_NAME) |
| * (customized_global_units_directions->number+1)); |
| } |
| else |
| { |
| customized_global_units_directions->list |
| = (DIRECTION_NODE_NAME *) |
| malloc (sizeof (DIRECTION_NODE_NAME)); |
| } |
| |
| if (!direction_node_name) |
| { |
| direction_node_name |
| = &customized_global_units_directions->list[ |
| customized_global_units_directions->number]; |
| direction_node_name->node_name = 0; |
| direction_node_name->direction = strdup (direction); |
| if (main_directions_idx > DEFAULT_GLOBAL_DIRECTION_LAST_IDX) |
| { |
| /* do not replace relative directions */ |
| direction_node_name->direction_nr = 0; |
| |
| if (node_name) |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "keep direction %s, do not set to `%s'", |
| self->main_units_direction_names[main_directions_idx], |
| node_name); |
| else |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "keep direction %s", |
| self->main_units_direction_names[main_directions_idx]); |
| } |
| else |
| direction_node_name->direction_nr = main_directions_idx+1; |
| customized_global_units_directions->number++; |
| } |
| else |
| { |
| free (direction_node_name->node_name); |
| direction_node_name->node_name = 0; |
| } |
| if (node_name) |
| direction_node_name->node_name = strdup (node_name); |
| } |
| |
| /* Also used to get htmlxref info from Perl. Initialize in C */ |
| HTMLXREF_MANUAL * |
| new_htmlxref_manual_list (size_t size) |
| { |
| HTMLXREF_MANUAL *result = (HTMLXREF_MANUAL *) |
| malloc (size * sizeof (HTMLXREF_MANUAL)); |
| memset (result, 0, size * sizeof (HTMLXREF_MANUAL)); |
| |
| return result; |
| } |
| |
| /* This variable setting/substitution is quite generic and could be used |
| in other codes, but it is only needed here for now */ |
| typedef struct STRING_VARIABLE_INFO { |
| char *name; |
| char *string; |
| } STRING_VARIABLE_INFO; |
| |
| typedef struct STRING_VARIABLES_LIST { |
| size_t number; |
| size_t space; |
| STRING_VARIABLE_INFO *list; |
| } STRING_VARIABLES_LIST; |
| |
| static void |
| set_variable_value (STRING_VARIABLES_LIST *variables, |
| const char *name, const char *value) |
| { |
| size_t i; |
| |
| for (i = 0; i < variables->number; i++) |
| { |
| STRING_VARIABLE_INFO *variable = &variables->list[i]; |
| if (!strcmp (variable->name, name)) |
| { |
| free (variable->string); |
| variable->string = strdup (value); |
| return; |
| } |
| } |
| |
| if (variables->number == variables->space) |
| { |
| variables->list = (STRING_VARIABLE_INFO *) realloc |
| (variables->list, |
| sizeof (STRING_VARIABLE_INFO) * (variables->space += 5)); |
| } |
| |
| variables->list[variables->number].name = strdup (name); |
| variables->list[variables->number].string = strdup (value); |
| |
| variables->number++; |
| } |
| |
| static void |
| clear_string_variables_list (STRING_VARIABLES_LIST *variables) |
| { |
| size_t i; |
| |
| for (i = 0; i < variables->number; i++) |
| { |
| STRING_VARIABLE_INFO *variable = &variables->list[i]; |
| free (variable->name); |
| free (variable->string); |
| } |
| variables->number = 0; |
| } |
| |
| static char * |
| substitute_variables (const char *input_text, |
| const STRING_VARIABLES_LIST *variables) |
| { |
| TEXT substituted; |
| const char *p = input_text; |
| |
| text_init (&substituted); |
| text_append (&substituted, ""); |
| |
| while (*p) |
| { |
| const char *q = strchr (p, '$'); |
| if (q) |
| { |
| int found = 0; |
| size_t var_len; |
| |
| if (q - p) |
| text_append_n (&substituted, p, q - p); |
| p = q; |
| q++; /* past $ */ |
| if (*q == '{') |
| { |
| /* past { */ |
| q++; |
| var_len = read_var_len (q); |
| if (var_len) |
| { |
| if (*(q + var_len) == '}') |
| { |
| size_t i; |
| char *flag = strndup (q, var_len); |
| |
| /* past } */ |
| q += var_len +1; |
| for (i = 0; i < variables->number; i++) |
| { |
| if (!strcmp (variables->list[i].name, flag)) |
| { |
| text_append (&substituted, |
| variables->list[i].string); |
| found = 1; |
| break; |
| } |
| } |
| free (flag); |
| } |
| } |
| } |
| |
| if (!found) |
| text_append_n (&substituted, p, q - p); |
| p = q; |
| } |
| else |
| { |
| text_append (&substituted, p); |
| break; |
| } |
| } |
| |
| return substituted.text; |
| } |
| |
| static HTMLXREF_MANUAL * |
| get_create_htmlxref_manual (HTMLXREF_MANUAL_LIST *htmlxref_list, |
| const char *manual_name) |
| { |
| size_t i; |
| HTMLXREF_MANUAL *htmlxref_manual; |
| |
| for (i = 0; i < htmlxref_list->number; i++) |
| { |
| htmlxref_manual = &htmlxref_list->list[i]; |
| if (!strcmp (htmlxref_manual->manual, manual_name)) |
| return htmlxref_manual; |
| } |
| |
| if (htmlxref_list->number == htmlxref_list->space) |
| { |
| htmlxref_list->list = (HTMLXREF_MANUAL *) realloc |
| (htmlxref_list->list, |
| sizeof (HTMLXREF_MANUAL) * (htmlxref_list->space += 5)); |
| } |
| |
| htmlxref_manual = &htmlxref_list->list[htmlxref_list->number]; |
| memset (htmlxref_manual, 0, sizeof (HTMLXREF_MANUAL)); |
| htmlxref_manual->manual = strdup (manual_name); |
| |
| htmlxref_list->number++; |
| |
| return htmlxref_manual; |
| } |
| |
| static void |
| fill_source_info_file (SOURCE_INFO *source_info, CONVERTER *self, |
| size_t line_nr, const char *file) |
| { |
| source_info->macro = 0; |
| source_info->line_nr = line_nr; |
| |
| if (self->conf->TEST.o.integer > 0) |
| { |
| char *filename_and_directory[2]; |
| /* strip directories for out-of-source builds reproducible file names */ |
| parse_file_path (file, filename_and_directory); |
| free (filename_and_directory[1]); |
| source_info->file_name = add_string (filename_and_directory[0], |
| &self->small_strings); |
| free (filename_and_directory[0]); |
| } |
| else |
| source_info->file_name = add_string (file, &self->small_strings); |
| } |
| |
| static void |
| parse_htmlxref_files (CONVERTER *self, HTMLXREF_MANUAL_LIST *htmlxref_list, |
| STRING_LIST *htmlxref_files) |
| { |
| size_t i; |
| |
| STRING_VARIABLES_LIST variables; |
| memset (&variables, 0, sizeof (STRING_VARIABLES_LIST)); |
| |
| for (i = 0; i < htmlxref_files->number; i++) |
| { |
| const char *file = htmlxref_files->list[i]; |
| FILE *stream = 0; |
| int line_nr = 0; |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| fprintf (stderr, "C|html refs config file: %s\n", file); |
| |
| stream = fopen (file, "r"); |
| if (!stream) |
| { |
| char *decoded_file; |
| const char *encoding = self->conf->COMMAND_LINE_ENCODING.o.string; |
| |
| if (encoding) |
| { |
| int status; |
| /* cast to remove const */ |
| decoded_file = decode_string ((char *)file, |
| encoding, &status, 0); |
| } |
| else |
| decoded_file = strdup (file); |
| |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "could not open html refs config file %s: %s", |
| decoded_file, strerror (errno)); |
| |
| free (decoded_file); |
| |
| continue; |
| } |
| |
| while (1) |
| { |
| const char *p; |
| size_t n; |
| char *line = 0; |
| ssize_t status = getline (&line, &n, stream); |
| size_t len; |
| char *split_or_mono = 0; |
| char *manual; |
| char *href = 0; |
| enum htmlxref_split_type htmlxref_type = htmlxref_split_type_none; |
| const char *q; |
| size_t spaces_len; |
| char *subst_href; |
| HTMLXREF_MANUAL *htmlxref_manual; |
| |
| if (status == -1) |
| { |
| free (line); |
| break; |
| } |
| line_nr++; |
| |
| /* |
| fprintf (stderr, "LLL %s:%d: %s", file, line_nr, line); |
| */ |
| |
| p = line; |
| |
| p += strspn (p, whitespace_chars); |
| if (*p == '#' || *p == '\0') |
| goto next_line; |
| |
| len = read_var_len (p); |
| if (len) |
| { |
| q = p; |
| q += len; |
| q += strspn (q, whitespace_chars); |
| if (*q == '=') |
| { |
| char *name = strndup (p, len); |
| char *definition; |
| char *end_line; |
| q++; |
| q += strspn (q, whitespace_chars); |
| definition = substitute_variables (q, &variables); |
| end_line = strchr (definition, '\n'); |
| if (end_line) |
| *end_line = '\0'; |
| set_variable_value (&variables, name, definition); |
| /* |
| fprintf (stderr, "VVV %s='%s'\n", name, definition); |
| */ |
| free (definition); |
| free (name); |
| goto next_line; |
| } |
| } |
| len = strcspn (p, whitespace_chars); |
| /* should always be true as we already handled a spaces only line */ |
| if (len) |
| { |
| q = p + len; |
| spaces_len = strspn (q, whitespace_chars); |
| |
| if (spaces_len && *q) |
| { |
| size_t spec_len; |
| |
| q += spaces_len; |
| spec_len = strcspn (q, whitespace_chars); |
| |
| if (spec_len) |
| { |
| split_or_mono = strndup (q, spec_len); |
| q += spec_len; |
| |
| enum htmlxref_split_type i; |
| |
| for (i = 0; i < htmlxref_split_type_chapter+1; i++) |
| { |
| if (!strcmp (split_or_mono, |
| htmlxref_split_type_names[i])) |
| { |
| htmlxref_type = i; |
| break; |
| } |
| } |
| } |
| } |
| } |
| /* |
| fprintf (stderr, "SOM %s %d\n", split_or_mono, htmlxref_type); |
| */ |
| if (!split_or_mono) |
| { |
| SOURCE_INFO source_info; |
| fill_source_info_file (&source_info, self, line_nr, file); |
| |
| message_list_line_error_ext (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| MSG_warning, 0, &source_info, "missing type"); |
| goto next_line; |
| } |
| else if (htmlxref_type == htmlxref_split_type_none) |
| { |
| SOURCE_INFO source_info; |
| fill_source_info_file (&source_info, self, line_nr, file); |
| |
| message_list_line_error_ext (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| MSG_warning, 0, &source_info, |
| "unrecognized type: %s", split_or_mono); |
| free (split_or_mono); |
| goto next_line; |
| } |
| |
| manual = strndup (p, len); |
| |
| spaces_len = strspn (q, whitespace_chars); |
| if (spaces_len && *q) |
| { |
| q += spaces_len; |
| size_t spec_len = strcspn (q, whitespace_chars); |
| if (spec_len) |
| { |
| href = strndup (q, spec_len); |
| } |
| } |
| |
| /* No warning for an empty URL prefix as it is the only way to |
| override an entry appearing in a file processed later on |
| if (!href) |
| { |
| SOURCE_INFO source_info; |
| fill_source_info_file (&source_info, self, line_nr, file); |
| |
| message_list_line_error_ext (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| MSG_warning, 0, &source_info, |
| "missing %s URL prefix for `%s'", split_or_mono, manual); |
| free (split_or_mono); |
| free (manual); |
| goto next_line; |
| } |
| */ |
| free (split_or_mono); |
| |
| htmlxref_manual |
| = get_create_htmlxref_manual (htmlxref_list, manual); |
| free (manual); |
| |
| /* |
| fprintf (stderr, "FFF %s '%s' %d '%s'\n", htmlxref_manual->manual, |
| href, htmlxref_type, htmlxref_manual->urlprefix[htmlxref_type]); |
| */ |
| if (htmlxref_manual->urlprefix[htmlxref_type]) |
| { |
| free (href); |
| goto next_line; |
| } |
| |
| if (href) |
| { |
| subst_href = substitute_variables (href, &variables); |
| |
| free (href); |
| |
| if (htmlxref_type != htmlxref_split_type_mono) |
| { |
| size_t j; |
| for (j = strlen (subst_href); j > 0; j--) |
| { |
| if (subst_href[j-1] == '/') |
| subst_href[j-1] = '\0'; |
| else |
| break; |
| } |
| } |
| } |
| else |
| subst_href = strdup (""); |
| |
| /* |
| fprintf (stderr, "HHH %s '%s' %d\n", htmlxref_manual->manual, |
| subst_href, htmlxref_type); |
| */ |
| |
| htmlxref_manual->urlprefix[htmlxref_type] = strdup (subst_href); |
| free (subst_href); |
| next_line: |
| free (line); |
| } |
| |
| clear_string_variables_list (&variables); |
| |
| if (fclose (stream) == EOF) |
| { |
| char *decoded_file; |
| const char *encoding = self->conf->COMMAND_LINE_ENCODING.o.string; |
| |
| if (encoding) |
| { |
| int status; |
| /* cast to remove const */ |
| decoded_file = decode_string ((char *)file, encoding, |
| &status, 0); |
| } |
| else |
| decoded_file = strdup (file); |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "error on closing html refs config file %s: %s", |
| decoded_file, strerror (errno)); |
| |
| free (decoded_file); |
| } |
| } |
| free (variables.list); |
| } |
| |
| static void |
| load_htmlxref_files (CONVERTER *self) |
| { |
| const char *htmlxref_mode = self->conf->HTMLXREF_MODE.o.string; |
| const char *htmlxref_file_name = "htmlxref.cnf"; |
| STRING_LIST htmlxref_files; |
| |
| if (htmlxref_mode && !strcmp (htmlxref_mode, "none")) |
| return; |
| |
| memset (&htmlxref_files, 0, sizeof (STRING_LIST)); |
| if (htmlxref_mode && !strcmp (htmlxref_mode, "file")) |
| { |
| char *encoded_htmlxref_file_name; |
| char *path_encoding; |
| const char *htmlxref_file = self->conf->HTMLXREF_FILE.o.string; |
| |
| if (htmlxref_file) |
| htmlxref_file_name = htmlxref_file; |
| |
| /* cast to remove const */ |
| encoded_htmlxref_file_name |
| = converter_encoded_output_file_name (self->conf, |
| &self->document->global_info, |
| (char *)htmlxref_file_name, |
| &path_encoding, 0); |
| free (path_encoding); |
| |
| if (euidaccess (encoded_htmlxref_file_name, R_OK) == 0) |
| add_string (encoded_htmlxref_file_name, &htmlxref_files); |
| else |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "could not find html refs config file %s", |
| htmlxref_file_name); |
| free (encoded_htmlxref_file_name); |
| } |
| else |
| { |
| STRING_LIST htmlxref_dirs; |
| const char *cnf_directory_name = "htmlxref.d"; |
| char *encoded_htmlxref_file_name; |
| char *path_encoding; |
| |
| memset (&htmlxref_dirs, 0, sizeof (STRING_LIST)); |
| |
| if (self->conf->TEST.o.integer > 0) |
| { |
| /* to have reproducible tests, do not use system or user |
| directories if TEST is set. */ |
| if (txi_paths_info.texinfo_uninstalled) |
| { |
| if (txi_paths_info.p.uninstalled.t2a_srcdir) |
| { |
| char *path; |
| xasprintf (&path, "%s/perl/t/input_files", |
| txi_paths_info.p.uninstalled.t2a_srcdir); |
| add_string (path, &htmlxref_dirs); |
| free (path); |
| } |
| else |
| add_string ("t/input_files", &htmlxref_dirs); |
| } |
| add_string (".texinfo", &htmlxref_dirs); |
| } |
| else |
| { |
| copy_strings (&htmlxref_dirs, |
| self->conf->TEXINFO_LANGUAGE_DIRECTORIES.o.strlist); |
| } |
| |
| if (self->conf->TEST.o.integer > 0) |
| { |
| htmlxref_file_name = self->conf->HTMLXREF_FILE.o.string; |
| cnf_directory_name = 0; |
| } |
| else if (self->conf->HTMLXREF_FILE.o.string) |
| htmlxref_file_name = self->conf->HTMLXREF_FILE.o.string; |
| |
| /* encode file name and handle specific cases for the main htmlxref file |
| without search in directories. |
| */ |
| if (htmlxref_file_name) |
| { |
| /* cast to remove const */ |
| encoded_htmlxref_file_name |
| = converter_encoded_output_file_name (self->conf, |
| &self->document->global_info, |
| (char *)htmlxref_file_name, |
| &path_encoding, 0); |
| free (path_encoding); |
| |
| if (file_name_is_absolute (encoded_htmlxref_file_name)) |
| { |
| if (euidaccess (encoded_htmlxref_file_name, R_OK) == 0) |
| add_string (encoded_htmlxref_file_name, &htmlxref_dirs); |
| |
| free (encoded_htmlxref_file_name); |
| htmlxref_file_name = 0; |
| } |
| else |
| { |
| char *file_name_and_directories[3]; |
| int file_with_directories = 0; |
| |
| splitpath (encoded_htmlxref_file_name, file_name_and_directories); |
| free (file_name_and_directories[0]); |
| free (file_name_and_directories[2]); |
| if (file_name_and_directories[1]) |
| { |
| STRING_LIST *file_directories |
| = splitdir (file_name_and_directories[1]); |
| free (file_name_and_directories[1]); |
| if (file_directories->number) |
| file_with_directories = 1; |
| |
| destroy_strings_list (file_directories); |
| } |
| |
| if (file_with_directories) |
| { |
| if (euidaccess (encoded_htmlxref_file_name, R_OK) == 0) |
| add_string (encoded_htmlxref_file_name, &htmlxref_dirs); |
| |
| free (encoded_htmlxref_file_name); |
| htmlxref_file_name = 0; |
| } |
| } |
| } |
| |
| /* now search in directories */ |
| if (htmlxref_file_name || cnf_directory_name) |
| { |
| static DEPRECATED_DIRS_LIST deprecated_dirs_used; |
| DEPRECATED_DIRS_LIST *deprecated_dirs |
| = &self->deprecated_config_directories; |
| char *encoded_cnf_directory_name; |
| size_t i; |
| |
| if (cnf_directory_name) |
| { |
| /* cast to remove const */ |
| encoded_cnf_directory_name |
| = converter_encoded_output_file_name (self->conf, |
| &self->document->global_info, |
| (char *)cnf_directory_name, |
| &path_encoding, 0); |
| free (path_encoding); |
| } |
| |
| for (i = 0; i < htmlxref_dirs.number; i++) |
| { |
| char *fullpath; |
| int deprecated_dir_set = 0; |
| |
| if (htmlxref_file_name) |
| { |
| xasprintf (&fullpath, "%s/%s", htmlxref_dirs.list[i], |
| encoded_htmlxref_file_name); |
| if (euidaccess (fullpath, R_OK) == 0) |
| { |
| if (deprecated_dirs) |
| { |
| DEPRECATED_DIR_INFO *deprecated_dir_info |
| = find_deprecated_dir_info (deprecated_dirs, |
| htmlxref_dirs.list[i]); |
| if (deprecated_dir_info) |
| add_deprecated_dir_info (&deprecated_dirs_used, |
| deprecated_dir_info); |
| deprecated_dir_set = 1; |
| } |
| add_string (fullpath, &htmlxref_files); |
| } |
| |
| free (fullpath); |
| } |
| |
| if (cnf_directory_name) |
| { |
| char *cnf_dir; |
| struct stat finfo; |
| |
| xasprintf (&cnf_dir, "%s/%s", htmlxref_dirs.list[i], |
| encoded_cnf_directory_name); |
| |
| if (stat (cnf_dir, &finfo) == 0 && S_ISDIR (finfo.st_mode)) |
| { |
| DIR *dir = opendir (cnf_dir); |
| char *possible_file; |
| if (dir) |
| { |
| int file_found = 0; |
| |
| struct dirent *entry; |
| while ((entry = readdir (dir))) |
| { |
| #ifdef _DIRENT_HAVE_DTYPE |
| if (entry->d_type != S_REG |
| && entry->d_type != S_UNKNOWN) |
| continue; |
| #endif |
| |
| size_t len = strlen (entry->d_name); |
| if (len >= 4 |
| && !memcmp (&entry->d_name[len - 4], |
| ".cnf", 4)) |
| { |
| xasprintf (&possible_file, "%s/%s", |
| cnf_dir, entry->d_name); |
| if (euidaccess (possible_file, R_OK) == 0) |
| { |
| add_string (possible_file, |
| &htmlxref_files); |
| file_found = 1; |
| } |
| free (possible_file); |
| } |
| } |
| (void) closedir (dir); |
| |
| if (!deprecated_dir_set && file_found |
| && deprecated_dirs) |
| { |
| DEPRECATED_DIR_INFO *deprecated_dir_info |
| = find_deprecated_dir_info (deprecated_dirs, |
| htmlxref_dirs.list[i]); |
| if (deprecated_dir_info) |
| add_deprecated_dir_info (&deprecated_dirs_used, |
| deprecated_dir_info); |
| deprecated_dir_set = 1; |
| } |
| } |
| } |
| free (cnf_dir); |
| } |
| } |
| |
| if (cnf_directory_name) |
| free (encoded_cnf_directory_name); |
| |
| if (deprecated_dirs_used.number) |
| { |
| size_t i; |
| const char *encoding = self->conf->COMMAND_LINE_ENCODING.o.string; |
| for (i = 0; i < deprecated_dirs_used.number; i++) |
| { |
| char *dir_name, *replacement_dir; |
| DEPRECATED_DIR_INFO *deprecated_dir_info |
| = &deprecated_dirs_used.list[i]; |
| if (encoding) |
| { |
| int status; |
| dir_name |
| = decode_string (deprecated_dir_info->obsolete_dir, |
| encoding, &status, 0); |
| replacement_dir |
| = decode_string (deprecated_dir_info->reference_dir, |
| encoding, &status, 0); |
| free (deprecated_dir_info->obsolete_dir); |
| free (deprecated_dir_info->reference_dir); |
| } |
| else |
| { |
| dir_name = deprecated_dir_info->obsolete_dir; |
| replacement_dir |
| = deprecated_dir_info->reference_dir; |
| } |
| |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, |
| "%s directory is deprecated. Use %s instead", |
| dir_name, replacement_dir); |
| |
| free (dir_name); |
| free (replacement_dir); |
| } |
| deprecated_dirs_used.number = 0; |
| } |
| if (htmlxref_file_name) |
| free (encoded_htmlxref_file_name); |
| } |
| free_strings_list (&htmlxref_dirs); |
| } |
| |
| if (htmlxref_files.number > 0) |
| { |
| parse_htmlxref_files (self, &self->htmlxref, &htmlxref_files); |
| free_strings_list (&htmlxref_files); |
| } |
| } |
| |
| static void |
| html_process_css_file (CONVERTER *self, FILE *fh, char *filename, |
| STRING_LIST *imports, STRING_LIST *rules) |
| { |
| TEXT text; |
| int in_rules = 0; |
| int in_comment = 0; |
| int in_import = 0; |
| int in_string = 0; |
| int line_nr = 0; |
| |
| /* the rule is to assume utf-8. There could also be a BOM, and |
| the Content-Type: HTTP header but it is not relevant here. |
| https://developer.mozilla.org/en-US/docs/Web/CSS/@charset |
| */ |
| const char *input_encoding = "utf-8"; |
| ENCODING_CONVERSION *conversion |
| = get_encoding_conversion (input_encoding, &input_conversions); |
| |
| text_init (&text); |
| |
| while (1) |
| { |
| size_t n; |
| char *input_line = 0; |
| char *line; |
| const char *p; |
| char in_string_string; |
| |
| ssize_t status = getline (&input_line, &n, fh); |
| if (status == -1) |
| { |
| free (input_line); |
| break; |
| } |
| if (!conversion) |
| line = strdup (input_line); |
| else |
| line = encode_with_iconv (conversion->iconv, input_line, 0, |
| ieh_error, 0); |
| |
| free (input_line); |
| line_nr ++; |
| /* |
| char *protected_line = debug_protect_eol (line); |
| fprintf (stderr, "NL(%d) '%s'\n", line_nr, protected_line); |
| free (protected_line); |
| */ |
| if (line_nr == 1) |
| { |
| int line_len = strlen (line); |
| if (line_len > 13) |
| { |
| size_t n_charset; |
| const char *q; |
| char *charset; |
| if (memcmp (line, "@charset ", 9)) |
| goto nocharset; |
| p = line + 9; |
| p += strspn (p, " "); |
| if (*p != '"') |
| goto nocharset; |
| p++; |
| q = p; |
| n_charset = strcspn (p, "\""); |
| if (!n_charset) |
| goto nocharset; |
| p += n_charset; |
| if (*p != '"') |
| goto nocharset; |
| p++; |
| p += strspn (p, " "); |
| if (*p != ';') |
| goto nocharset; |
| p++; |
| p += strspn (p, " "); |
| if (*p && !strchr ("\n\r", *p)) |
| goto nocharset; |
| charset = strndup (q, n_charset); |
| conversion |
| = get_encoding_conversion (charset, &input_conversions); |
| free (charset); |
| free (line); |
| continue; |
| } |
| nocharset: |
| ; |
| } |
| |
| if (in_rules) |
| { |
| add_string (line, rules); |
| free (line); |
| continue; |
| } |
| |
| text_reset (&text); |
| text_append (&text, ""); |
| |
| p = line; |
| while (1) |
| { |
| /* |
| char *protected_p = debug_protect_eol (p); |
| char *protected_text = debug_protect_eol (text.text); |
| fprintf (stderr, |
| "%s!in_comment %d in_rules %d in_import %d in_string %d: '%s'\n", |
| protected_text, in_comment, in_rules, |
| in_import, in_string,protected_p); |
| free (protected_p); |
| free (protected_text); |
| */ |
| |
| if (in_comment) |
| { |
| const char *q = p; |
| while (1) |
| { |
| const char *k = strchr (q, '*'); |
| if (k) |
| { |
| k++; |
| if (*k == '/') |
| { |
| k++; |
| text_append_n (&text, p, k - p); |
| p = k; |
| in_comment = 0; |
| break; |
| } |
| else if (!*k) |
| break; |
| else |
| q = k; |
| } |
| else |
| break; |
| } |
| if (in_comment) |
| { |
| text_append (&text, p); |
| add_string (text.text, imports); |
| break; |
| } |
| } |
| else if (!in_string && *p == '/') |
| { |
| p++; |
| if (*p == '*') |
| { |
| p++; |
| text_append_n (&text, "/*", 2); |
| in_comment = 1; |
| } |
| else |
| { |
| if (text.end > 0) |
| { |
| text_append_n (&text, "\n", 1); |
| add_string (text.text, imports); |
| } |
| p--; /* back on / */ |
| add_string (p, rules); |
| in_rules = 1; |
| break; |
| } |
| } |
| else if (!in_string && in_import && *p && strchr ("\"'", *p)) |
| { |
| /* strings outside of import start rules */ |
| text_append_n (&text, p, 1); |
| in_string_string = *p; |
| p++; |
| in_string = 1; |
| } |
| else if (in_string && *p == '\\' && *(p+1) == in_string_string) |
| { |
| text_append_n (&text, p, 2); |
| p += 2; |
| } |
| else if (in_string && *p == in_string_string) |
| { |
| text_append_n (&text, p, 1); |
| p++; |
| in_string = 0; |
| } |
| else |
| { |
| int matched_import = 0; |
| if (!in_string && !in_import) |
| { |
| const char *q = p; |
| if (*q == '\\') |
| q++; |
| if (strlen (q) >= 7 && !memcmp (q, "@import", 7)) |
| { |
| q += 7; |
| if (!*q || strchr (whitespace_chars, *q)) |
| { |
| /* spaces except newlines */ |
| q += strspn (q, " \t\v\f"); |
| text_append_n (&text, p, q - p); |
| in_import = 1; |
| p = q; |
| matched_import = 1; |
| } |
| } |
| } |
| if (!matched_import) |
| { |
| if (!in_string && in_import && *p == ';') |
| { |
| text_append_n (&text, ";", 1); |
| p++; |
| in_import = 0; |
| } |
| else if ((in_import || in_string) && *p && !strchr ("\n\r", *p)) |
| { |
| /* Count any UTF-8 continuation bytes. */ |
| int char_len = 1; |
| while ((p[char_len] & 0xC0) == 0x80) |
| char_len++; |
| text_append_n (&text, p, char_len); |
| p += char_len; |
| } |
| else if (!in_import && *p && !strchr (whitespace_chars, *p)) |
| { |
| if (text.end > 0) |
| { |
| text_append_n (&text, "\n", 1); |
| add_string (text.text, imports); |
| } |
| add_string (p, rules); |
| in_rules = 1; |
| break; |
| } |
| else if (*p && strchr (whitespace_chars, *p)) |
| { |
| text_append_n (&text, p, 1); |
| p++; |
| } |
| else if (!*p) |
| { |
| add_string (text.text, imports); |
| break; |
| } |
| } |
| } |
| } |
| free (line); |
| } |
| |
| free (text.text); |
| |
| if (in_string || in_string || in_import) |
| { |
| SOURCE_INFO source_info; |
| source_info.macro = 0; |
| source_info.file_name = filename; |
| source_info.line_nr = line_nr; |
| |
| if (in_string) |
| { |
| message_list_line_error_ext (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| MSG_warning, 0, |
| &source_info, |
| "string not closed in css file"); |
| } |
| if (in_comment) |
| { |
| message_list_line_error_ext (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| MSG_warning, 0, |
| &source_info, |
| "--css-include ended in comment"); |
| } |
| if (in_import && !in_comment && !in_string) |
| { |
| message_list_line_error_ext (&self->error_messages, |
| (self->conf && self->conf->DEBUG.o.integer > 0), |
| MSG_warning, 0, |
| &source_info, |
| "@import not finished in css file"); |
| } |
| } |
| } |
| |
| static void |
| html_prepare_css (CONVERTER *self) |
| { |
| const STRING_LIST *css_files; |
| STRING_LIST *css_import_lines; |
| STRING_LIST *css_rule_lines; |
| size_t i; |
| |
| if (self->conf->NO_CSS.o.integer > 0) |
| return; |
| |
| css_files = self->conf->CSS_FILES.o.strlist; |
| |
| if (!css_files || css_files->number <= 0) |
| return; |
| |
| css_import_lines = new_string_list (); |
| css_rule_lines = new_string_list (); |
| |
| for (i = 0; i < css_files->number; i++) |
| { |
| FILE *css_file_fh; |
| char *css_file_path; |
| char *css_file = css_files->list[i]; |
| if (!strcmp (css_file, "-")) |
| { |
| css_file_fh = stdin; |
| css_file_path = strdup ("-"); |
| } |
| else |
| { |
| css_file_path = locate_include_file (css_file, |
| self->conf->INCLUDE_DIRECTORIES.o.strlist); |
| if (!css_file_path) |
| { |
| char *css_input_file_name; |
| const char *encoding |
| = self->conf->COMMAND_LINE_ENCODING.o.string; |
| if (encoding) |
| { |
| int status; |
| css_input_file_name |
| = decode_string (css_file, encoding, &status, 0); |
| } |
| else |
| css_input_file_name = strdup (css_file); |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "CSS file %s not found", |
| css_input_file_name); |
| free (css_input_file_name); |
| continue; |
| } |
| |
| css_file_fh = fopen (css_file_path, "r"); |
| if (!css_file_fh) |
| { |
| char *css_file_name; |
| const char *encoding |
| = self->conf->COMMAND_LINE_ENCODING.o.string; |
| if (encoding) |
| { |
| int status; |
| css_file_name |
| = decode_string (css_file_path, encoding, &status, 0); |
| } |
| else |
| css_file_name = strdup (css_file_path); |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, |
| "could not open --include-file %s: %s", |
| css_file_name, strerror (errno)); |
| |
| free (css_file_name); |
| free (css_file_path); |
| continue; |
| } |
| } |
| |
| html_process_css_file (self, css_file_fh, css_file_path, |
| css_import_lines, css_rule_lines); |
| |
| if (fclose (css_file_fh)) |
| { |
| char *css_file_name; |
| const char *encoding |
| = self->conf->COMMAND_LINE_ENCODING.o.string; |
| if (encoding) |
| { |
| int status; |
| css_file_name |
| = decode_string (css_file_path, encoding, &status, 0); |
| } |
| else |
| css_file_name = strdup (css_file_path); |
| |
| message_list_document_error (&self->error_messages, self->conf, 0, |
| "error on closing CSS file %s: %s", |
| css_file_name, strerror (errno)); |
| |
| free (css_file_path); |
| } |
| |
| free (css_file_path); |
| } |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| if (css_import_lines->number > 0) |
| { |
| fprintf (stderr, "C|# css import lines\n"); |
| for (i = 0; i < css_import_lines->number; i++) |
| fprintf (stderr, "%s", css_import_lines->list[i]); |
| } |
| if (css_rule_lines->number > 0) |
| { |
| fprintf (stderr, "C|# css rule lines\n"); |
| for (i = 0; i < css_rule_lines->number; i++) |
| fprintf (stderr, "%s", css_rule_lines->list[i]); |
| } |
| } |
| |
| clear_strings_list (&self->files_css_import_lines); |
| clear_strings_list (&self->files_css_rule_lines); |
| copy_strings (&self->files_css_import_lines, css_import_lines); |
| copy_strings (&self->files_css_rule_lines, css_rule_lines); |
| |
| destroy_strings_list (css_import_lines); |
| destroy_strings_list (css_rule_lines); |
| } |
| |
| /* this code corresponds to the Perl converter_initialize code, only for |
| code to be called before Perl customization setup information is passed */ |
| void |
| html_converter_initialize_beginning (CONVERTER *self) |
| { |
| const char *split = self->conf->SPLIT.o.string; |
| int max_header_level = self->conf->MAX_HEADER_LEVEL.o.integer; |
| |
| if (!self->conf->FORMAT_MENU.o.string) |
| option_force_conf (&self->conf->FORMAT_MENU, 0, ""); |
| |
| /* NOTE we reset silently if the split specification is not one known. |
| The main program warns if the specific command line option value is |
| not known. We could add a warning here to catch mistakes in init |
| files. Wait for user reports. |
| */ |
| if (split && strlen (split) && strcmp (split, "chapter") |
| && strcmp (split, "section") && strcmp (split, "node")) |
| option_force_conf (&self->conf->SPLIT, 0, "node"); |
| |
| if (max_header_level < 0) |
| option_force_conf (&self->conf->MAX_HEADER_LEVEL, |
| html_default_options->options->MAX_HEADER_LEVEL.o.integer, 0); |
| else if (max_header_level < 1) |
| option_force_conf (&self->conf->MAX_HEADER_LEVEL, 1, 0); |
| |
| /* For CONTENTS_OUTPUT_LOCATION |
| should lead to contents not output, but if not, it is not an issue, |
| the way to set contents to be output or not should be through the |
| contents and shortcontents @-commands and customization options. |
| */ |
| |
| if (!self->conf->CONTENTS_OUTPUT_LOCATION.o.string) |
| option_force_conf (&self->conf->CONTENTS_OUTPUT_LOCATION, 0, ""); |
| if (!self->conf->INDEX_ENTRY_COLON.o.string) |
| option_force_conf (&self->conf->INDEX_ENTRY_COLON, 0, ""); |
| if (!self->conf->MENU_ENTRY_COLON.o.string) |
| option_force_conf (&self->conf->MENU_ENTRY_COLON, 0, ""); |
| |
| load_htmlxref_files (self); |
| html_prepare_css (self); |
| } |
| |
| /* for customized special_unit_info (coming from Perl) */ |
| SPECIAL_UNIT_INFO * |
| html_add_special_unit_info (SPECIAL_UNIT_INFO_LIST *special_unit_info_list, |
| int type, size_t variety_nr, const char *value) |
| { |
| SPECIAL_UNIT_INFO *special_unit_info; |
| |
| if (special_unit_info_list->number == special_unit_info_list->space) |
| { |
| special_unit_info_list->list = realloc (special_unit_info_list->list, |
| sizeof (SPECIAL_UNIT_INFO) * (special_unit_info_list->space += 5)); |
| } |
| special_unit_info |
| = &special_unit_info_list->list[special_unit_info_list->number]; |
| memset (special_unit_info, 0, sizeof (SPECIAL_UNIT_INFO)); |
| special_unit_info->type = type; |
| special_unit_info->variety_nr = variety_nr; |
| if (value) |
| special_unit_info->value = strdup (value); |
| |
| special_unit_info_list->number++; |
| return special_unit_info; |
| } |
| |
| static char ** |
| new_special_unit_info_type (int special_units_varieties_nr) |
| { |
| char **special_unit_info = (char **) |
| malloc ((special_units_varieties_nr +1) * sizeof (char *)); |
| memset (special_unit_info, 0, |
| (special_units_varieties_nr +1) * sizeof (char *)); |
| return special_unit_info; |
| } |
| |
| /* in main/html_conversion_data.c */ |
| extern const STRING_LIST default_special_unit_varieties; |
| |
| /* Setup special units varieties names. Separate from |
| html_converter_init_special_units_info such that customization may be set |
| after setting up special units varieties names but before calling |
| html_converter_init_special_units_info |
| */ |
| void |
| html_converter_init_special_unit_varieties (CONVERTER *self) |
| { |
| /* NOTE if the special units can be customized, then |
| self->special_unit_varieties should ba set and used directly instead. |
| Also default special units and special units indices should be |
| mapped instead of assuming that they are the same when setting |
| self->special_unit_info */ |
| copy_strings (&self->special_unit_varieties, &default_special_unit_varieties); |
| } |
| |
| /* set units info to default and also apply customization */ |
| void |
| html_converter_init_special_units_info (CONVERTER *self) |
| { |
| size_t nr_special_units; |
| |
| nr_special_units = self->special_unit_varieties.number; |
| |
| /* special units info */ |
| /* set to defaults */ |
| if (nr_special_units > 0) |
| { |
| size_t i; |
| enum special_unit_info_type j; |
| enum special_unit_info_tree l; |
| for (j = 0; j < SPECIAL_UNIT_INFO_TYPE_NR; j++) |
| { |
| size_t k; |
| |
| self->special_unit_info[j] |
| = new_special_unit_info_type (nr_special_units); |
| for (k = 0; k < nr_special_units; k++) |
| { |
| if (default_special_unit_info[j][k]) |
| self->special_unit_info[j][k] |
| = strdup (default_special_unit_info[j][k]); |
| } |
| } |
| for (l = 0; l < SPECIAL_UNIT_INFO_TREE_NR; l++) |
| { |
| size_t k; |
| |
| self->translated_special_unit_info_texinfo[l] |
| = new_special_unit_info_type (nr_special_units); |
| for (k = 0; k < nr_special_units; k++) |
| { |
| if (default_special_unit_tree_info[l][k]) |
| self->translated_special_unit_info_texinfo[l][k] |
| = strdup (default_special_unit_tree_info[l][k]); |
| } |
| } |
| /* apply customization */ |
| for (i = 0; i < self->customized_special_unit_info.number; i++) |
| { |
| int t; |
| SPECIAL_UNIT_INFO *custom_unit_info |
| = &self->customized_special_unit_info.list[i]; |
| size_t variety_idx = custom_unit_info->variety_nr -1; |
| enum special_unit_info_type type = custom_unit_info->type; |
| enum special_unit_info_tree tree_type = SUIT_type_none; |
| |
| for (t = 0; translated_special_unit_info[t].tree_type |
| != SUIT_type_none; t++) |
| { |
| enum special_unit_info_type string_type |
| = translated_special_unit_info[t].string_type; |
| if (string_type == type) |
| { |
| tree_type = translated_special_unit_info[t].tree_type; |
| |
| free (self->translated_special_unit_info_texinfo[tree_type] |
| [variety_idx]); |
| |
| if (custom_unit_info->value) |
| self->translated_special_unit_info_texinfo[tree_type] |
| [variety_idx] |
| = strdup (custom_unit_info->value); |
| else |
| self->translated_special_unit_info_texinfo[tree_type] |
| [variety_idx] = 0; |
| break; |
| } |
| } |
| |
| if (tree_type != SUIT_type_none) |
| continue; |
| |
| free (self->special_unit_info[type][variety_idx]); |
| |
| if (custom_unit_info->value) |
| self->special_unit_info[type][variety_idx] |
| = strdup (custom_unit_info->value); |
| else |
| self->special_unit_info[type][variety_idx] = 0; |
| } |
| } |
| } |
| |
| /* Used to get Perl formatting references. Initialize in C */ |
| FORMATTING_REFERENCE * |
| new_special_unit_formatting_references (int special_units_varieties_nr) |
| { |
| FORMATTING_REFERENCE *formatting_references = (FORMATTING_REFERENCE *) |
| malloc (special_units_varieties_nr * sizeof (FORMATTING_REFERENCE)); |
| memset (formatting_references, 0, |
| special_units_varieties_nr * sizeof (FORMATTING_REFERENCE)); |
| return formatting_references; |
| } |
| |
| static HTML_DIRECTION_STRING_TRANSLATED * |
| new_directions_strings_translated_type (int nr_string_directions) |
| { |
| HTML_DIRECTION_STRING_TRANSLATED *result |
| = (HTML_DIRECTION_STRING_TRANSLATED *) malloc |
| (nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED)); |
| memset (result, 0, |
| nr_string_directions * sizeof (HTML_DIRECTION_STRING_TRANSLATED)); |
| return result; |
| } |
| |
| static int * |
| determine_non_default_special_unit_directions (const CONVERTER *self) |
| { |
| size_t i; |
| size_t nr_special_units = self->special_unit_varieties.number; |
| int *non_default_special_unit_directions = 0; |
| /* determine the special units directions that are not the same as |
| the default units directions. If not the same as the defaults, |
| the default direction info should not be used as they are not for |
| the customized special unit direction */ |
| if (nr_special_units > 0) |
| { |
| non_default_special_unit_directions = (int *) |
| malloc (nr_special_units * sizeof (int)); |
| memset (non_default_special_unit_directions, 0, |
| nr_special_units * sizeof (int)); |
| |
| for (i = 0; i < nr_special_units; i++) |
| { |
| if (strcmp (self->special_unit_info[SUI_type_direction][i], |
| default_special_unit_info[SUI_type_direction][i])) |
| non_default_special_unit_directions[i] = 1; |
| } |
| } |
| return non_default_special_unit_directions; |
| } |
| |
| static int |
| register_type_conversion_function (TYPE_CONVERSION_FUNCTION *result, |
| enum element_type type, |
| FORMATTING_REFERENCE *formatting_reference) |
| { |
| if (formatting_reference->status > 0) |
| { |
| result->status = formatting_reference->status; |
| if (formatting_reference->status != FRS_status_ignored) |
| { |
| result->type_conversion = &html_type_conversion_external; |
| result->formatting_reference = formatting_reference; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| register_type_open_function (TYPE_OPEN_FUNCTION *result, |
| enum element_type type, |
| FORMATTING_REFERENCE *formatting_reference) |
| { |
| if (formatting_reference->status > 0) |
| { |
| result->status = formatting_reference->status; |
| if (formatting_reference->status != FRS_status_ignored) |
| { |
| result->type_open = &html_type_open_external; |
| result->formatting_reference = formatting_reference; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| register_command_conversion_function (COMMAND_CONVERSION_FUNCTION *result, |
| enum command_id cmd, |
| FORMATTING_REFERENCE *formatting_reference) |
| { |
| if (formatting_reference->status > 0) |
| { |
| result->status = formatting_reference->status; |
| if (formatting_reference->status != FRS_status_ignored) |
| { |
| result->command_conversion = &html_command_conversion_external; |
| result->formatting_reference = formatting_reference; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| register_command_open_function (COMMAND_OPEN_FUNCTION *result, |
| enum command_id cmd, |
| FORMATTING_REFERENCE *formatting_reference) |
| { |
| if (formatting_reference->status > 0) |
| { |
| result->status = formatting_reference->status; |
| if (formatting_reference->status != FRS_status_ignored) |
| { |
| result->command_open = &html_command_open_external; |
| result->formatting_reference = formatting_reference; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| register_output_unit_conversion_function |
| (OUTPUT_UNIT_CONVERSION_FUNCTION *result, |
| enum output_unit_type type, |
| FORMATTING_REFERENCE *formatting_reference) |
| { |
| if (formatting_reference->status > 0) |
| { |
| result->status = formatting_reference->status; |
| if (formatting_reference->status != FRS_status_ignored) |
| { |
| result->output_unit_conversion |
| = &html_output_unit_conversion_external; |
| result->formatting_reference = formatting_reference; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| register_special_unit_body_formatting_function |
| (SPECIAL_UNIT_BODY_FORMATTING *result, |
| const char *special_unit_variety, |
| FORMATTING_REFERENCE *formatting_reference) |
| { |
| if (formatting_reference && formatting_reference->status > 0) |
| { |
| result->status = formatting_reference->status; |
| if (formatting_reference->status != FRS_status_ignored) |
| { |
| result->special_unit_body_formatting |
| = &html_special_unit_body_formatting_external; |
| result->formatting_reference = formatting_reference; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| compare_htmlxref_manual (const void *a, const void *b) |
| { |
| const HTMLXREF_MANUAL *hxfm_a = (const HTMLXREF_MANUAL *) a; |
| const HTMLXREF_MANUAL *hxfm_b = (const HTMLXREF_MANUAL *) b; |
| |
| return strcmp (hxfm_a->manual, hxfm_b->manual); |
| } |
| |
| typedef struct COMMAND_INTERNAL_CONVERSION { |
| enum command_id cmd; |
| void (* command_conversion) (struct CONVERTER *self, |
| const enum command_id cmd, |
| const ELEMENT *element, |
| const HTML_ARGS_FORMATTED *args_formatted, |
| const char *content, TEXT *result); |
| } COMMAND_INTERNAL_CONVERSION; |
| |
| /* associate command to the C function implementing the conversion */ |
| static const COMMAND_INTERNAL_CONVERSION commands_internal_conversion_table[] = { |
| {CM_today, &html_convert_today_command}, |
| {CM_w, &html_convert_w_command}, |
| {CM_value, &html_convert_value_command}, |
| {CM_email, &html_convert_email_command}, |
| {CM_abbr, &html_convert_explained_command}, |
| {CM_acronym, &html_convert_explained_command}, |
| {CM_anchor, &html_convert_anchor_command}, |
| {CM_namedanchor, &html_convert_anchor_command}, |
| {CM_footnote, &html_convert_footnote_command}, |
| {CM_uref, &html_convert_uref_command}, |
| {CM_url, &html_convert_uref_command}, |
| {CM_image, &html_convert_image_command}, |
| {CM_math, &html_convert_math_command}, |
| /* note that if indicateurl had been in style_formatted_cmd this |
| would have prevented indicateurl to be associated to |
| convert_style_command */ |
| {CM_indicateurl, &html_convert_indicateurl_command}, |
| {CM_titlefont, &html_convert_titlefont_command}, |
| {CM_U, &html_convert_U_command}, |
| |
| {CM_inlineraw, &html_convert_inline_command}, |
| {CM_inlinefmt, &html_convert_inline_command}, |
| {CM_inlinefmtifelse, &html_convert_inline_command}, |
| {CM_inlineifclear, &html_convert_inline_command}, |
| {CM_inlineifset, &html_convert_inline_command}, |
| |
| {CM_inforef, &html_convert_xref_command}, |
| {CM_link, &html_convert_xref_command}, |
| {CM_xref, &html_convert_xref_command}, |
| {CM_ref, &html_convert_xref_command}, |
| {CM_pxref, &html_convert_xref_command}, |
| |
| {CM_html, &html_convert_raw_command}, |
| {CM_tex, &html_convert_raw_command}, |
| {CM_xml, &html_convert_raw_command}, |
| {CM_docbook, &html_convert_raw_command}, |
| {CM_latex, &html_convert_raw_command}, |
| |
| {CM_indentedblock, &html_convert_indented_command}, |
| {CM_smallindentedblock, &html_convert_indented_command}, |
| {CM_verbatim, &html_convert_verbatim_command}, |
| {CM_displaymath, &html_convert_displaymath_command}, |
| {CM_raggedright, &html_convert_simple_block_command}, |
| {CM_flushleft, &html_convert_simple_block_command}, |
| {CM_flushright, &html_convert_simple_block_command}, |
| {CM_group, &html_convert_simple_block_command}, |
| {CM_menu, &html_convert_menu_command}, |
| {CM_detailmenu, &html_convert_menu_command}, |
| {CM_float, &html_convert_float_command}, |
| {CM_quotation, &html_convert_quotation_command}, |
| {CM_smallquotation, &html_convert_quotation_command}, |
| {CM_cartouche, &html_convert_cartouche_command}, |
| {CM_itemize, &html_convert_itemize_command}, |
| {CM_enumerate, &html_convert_enumerate_command}, |
| {CM_multitable, &html_convert_multitable_command}, |
| {CM_table, &html_convert_xtable_command}, |
| {CM_ftable, &html_convert_xtable_command}, |
| {CM_vtable, &html_convert_xtable_command}, |
| |
| {CM_verbatiminclude, &html_convert_verbatiminclude_command}, |
| {CM_sp, &html_convert_sp_command}, |
| {CM_exdent, &html_convert_exdent_command}, |
| {CM_center, &html_convert_center_command}, |
| {CM_author, &html_convert_author_command}, |
| {CM_title, &html_convert_title_command}, |
| {CM_subtitle, &html_convert_subtitle_command}, |
| {CM_item, &html_convert_item_command}, |
| {CM_headitem, &html_convert_item_command}, |
| {CM_itemx, &html_convert_item_command}, |
| {CM_tab, &html_convert_tab_command}, |
| {CM_insertcopying, &html_convert_insertcopying_command}, |
| {CM_maketitle, &html_convert_maketitle_command}, |
| {CM_listoffloats, &html_convert_listoffloats_command}, |
| {CM_printindex, &html_convert_printindex_command}, |
| /* @informative_global_commands in perl */ |
| {CM_documentlanguage, &html_convert_informative_command}, |
| {CM_documentscript, &html_convert_informative_command}, |
| {CM_footnotestyle, &html_convert_informative_command}, |
| {CM_xrefautomaticsectiontitle, &html_convert_informative_command}, |
| {CM_deftypefnnewline, &html_convert_informative_command}, |
| |
| {CM_documentlanguagevariant, &html_convert_documentlanguagevariant_command}, |
| |
| {CM_contents, &html_convert_contents_command}, |
| {CM_shortcontents, &html_convert_contents_command}, |
| {CM_summarycontents, &html_convert_contents_command}, |
| |
| /* other @def* commands are handled together */ |
| {CM_defblock, &html_convert_def_command}, |
| |
| {CM_node, &html_convert_heading_command}, |
| {CM_top, &html_convert_heading_command}, |
| {CM_chapter, &html_convert_heading_command}, |
| {CM_unnumbered, &html_convert_heading_command}, |
| {CM_chapheading, &html_convert_heading_command}, |
| {CM_appendix, &html_convert_heading_command}, |
| {CM_section, &html_convert_heading_command}, |
| {CM_unnumberedsec, &html_convert_heading_command}, |
| {CM_heading, &html_convert_heading_command}, |
| {CM_appendixsec, &html_convert_heading_command}, |
| {CM_subsection, &html_convert_heading_command}, |
| {CM_unnumberedsubsec, &html_convert_heading_command}, |
| {CM_subheading, &html_convert_heading_command}, |
| {CM_appendixsubsec, &html_convert_heading_command}, |
| {CM_subsubsection, &html_convert_heading_command}, |
| {CM_unnumberedsubsubsec, &html_convert_heading_command}, |
| {CM_subsubheading, &html_convert_heading_command}, |
| {CM_appendixsubsubsec, &html_convert_heading_command}, |
| {CM_part, &html_convert_heading_command}, |
| {CM_appendixsection, &html_convert_heading_command}, |
| {CM_majorheading, &html_convert_heading_command}, |
| {CM_centerchap, &html_convert_heading_command}, |
| |
| {0, 0}, |
| }; |
| |
| typedef struct COMMAND_INTERNAL_OPEN { |
| enum command_id cmd; |
| void (* command_open) (CONVERTER *self, const enum command_id cmd, |
| const ELEMENT *element, TEXT *result); |
| } COMMAND_INTERNAL_OPEN; |
| |
| /* associate command to the C function implementing the opening */ |
| static const COMMAND_INTERNAL_OPEN commands_internal_open_table[] = { |
| {CM_node, &html_open_node_part_command}, |
| {CM_part, &html_open_node_part_command}, |
| {CM_quotation, &html_open_quotation_command}, |
| {CM_smallquotation, &html_open_quotation_command}, |
| {0, 0}, |
| }; |
| |
| typedef struct TYPE_INTERNAL_CONVERSION { |
| enum element_type type; |
| void (* type_conversion) (CONVERTER *self, const enum element_type type, |
| const ELEMENT *element, const char *content, |
| TEXT *result); |
| } TYPE_INTERNAL_CONVERSION; |
| |
| /* associate type to the C function implementing the conversion */ |
| static const TYPE_INTERNAL_CONVERSION types_internal_conversion_table[] = { |
| {ET_text, &html_convert_text}, |
| {ET_paragraph, &html_convert_paragraph_type}, |
| {ET_preformatted, &html_convert_preformatted_type}, |
| {ET_balanced_braces, &html_convert_balanced_braces_type}, |
| {ET_index_entry_command, &html_convert_index_entry_command_type}, |
| {ET_definfoenclose_command, &html_convert_definfoenclose_type}, |
| {ET_untranslated_def_line_arg, |
| &html_convert_untranslated_def_line_arg_type}, |
| {ET_row, &html_convert_row_type}, |
| {ET_multitable_head, &html_convert_multitable_head_type}, |
| {ET_multitable_body, &html_convert_multitable_body_type}, |
| {ET_menu_entry, &html_convert_menu_entry_type}, |
| {ET_menu_comment, &html_convert_menu_comment_type}, |
| {ET_before_item, &html_convert_before_item_type}, |
| {ET_table_term, &html_convert_table_term_type}, |
| {ET_def_line, &html_convert_def_line_type}, |
| {ET_def_item, &html_convert_def_item_type}, |
| {ET_inter_def_item, &html_convert_def_item_type}, |
| {ET_before_defline, &html_convert_def_item_type}, |
| {ET_table_definition, &html_convert_table_definition_type}, |
| {ET_inter_item, &html_convert_table_definition_type}, |
| |
| {0, 0}, |
| }; |
| |
| typedef struct TYPE_INTERNAL_OPEN { |
| enum element_type type; |
| void (* type_open) (CONVERTER *self, const enum element_type type, |
| const ELEMENT *element, TEXT *result); |
| } TYPE_INTERNAL_OPEN; |
| |
| /* associate type to the C function implementing the opening */ |
| static const TYPE_INTERNAL_OPEN types_internal_open_table[] = { |
| {ET_paragraph, &html_open_inline_container_type}, |
| {ET_preformatted, &html_open_inline_container_type}, |
| {0, 0}, |
| }; |
| |
| typedef struct OUTPUT_UNIT_INTERNAL_CONVERSION { |
| enum output_unit_type type; |
| void (* output_unit_conversion) (CONVERTER *self, |
| const enum output_unit_type unit_type, |
| const OUTPUT_UNIT *output_unit, const char *content, |
| TEXT *result); |
| } OUTPUT_UNIT_INTERNAL_CONVERSION; |
| |
| static const OUTPUT_UNIT_INTERNAL_CONVERSION output_units_internal_conversion_table[] = { |
| {OU_special_unit, &html_convert_special_unit_type}, |
| {OU_unit, &html_convert_unit_type}, |
| {0, 0}, |
| }; |
| |
| typedef struct SPECIAL_UNIT_BODY_INTERNAL_CONVERSION { |
| char *special_unit_variety; |
| void (* special_unit_body_formatting) (CONVERTER *self, |
| const size_t special_unit_number, |
| const char *special_unit_variety, |
| const OUTPUT_UNIT *output_unit, |
| TEXT *result); |
| } SPECIAL_UNIT_BODY_INTERNAL_CONVERSION; |
| |
| static const SPECIAL_UNIT_BODY_INTERNAL_CONVERSION |
| special_unit_body_internal_formatting_table[] = { |
| {"contents", &html_default_format_special_body_contents}, |
| {"shortcontents", &html_default_format_special_body_shortcontents}, |
| {"footnotes", &html_default_format_special_body_footnotes}, |
| {"about", &html_default_format_special_body_about}, |
| {0, 0}, |
| }; |
| |
| /* set SELF->main_units_direction_names array of direction names |
| and SELF->->units_direction_names_index hash associating direction names |
| to their index in SELF->main_units_direction_names. |
| Also set direction number in SELF->customized_global_units_directions |
| items. |
| |
| No exact equivalent in Perl where names only are used, more similar code |
| in converter_initialize. |
| */ |
| int |
| html_set_main_units_direction_names (CONVERTER *self) |
| { |
| size_t nr_special_units = self->special_unit_varieties.number; |
| /* The corresponding direction without FirstInFile are used instead |
| of FirstInFile*, so the directions_strings are not set */ |
| int nr_string_directions = NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR |
| + (int) nr_special_units; |
| size_t i; |
| |
| free (self->main_units_direction_names); |
| |
| self->main_units_direction_names = (const char **) malloc |
| ((nr_special_units + NON_SPECIAL_DIRECTIONS_NR +1) |
| * sizeof (char *)); |
| memcpy (self->main_units_direction_names, html_button_direction_names, |
| NON_SPECIAL_DIRECTIONS_NR * sizeof (char *)); |
| memcpy (self->main_units_direction_names + NON_SPECIAL_DIRECTIONS_NR, |
| self->special_unit_info[SUI_type_direction], |
| nr_special_units * sizeof (char *)); |
| self->main_units_direction_names[ |
| nr_special_units + NON_SPECIAL_DIRECTIONS_NR] = 0; |
| |
| /* at the same time the names are added to the main_units_direction_names |
| array, fill an hash associating the name to the index in the |
| array used to retrieve the indice based on the direction name. |
| |
| Setup now and fill with directions already set, since |
| html_get_direction_index is called just below |
| */ |
| init_c_hashmap (&self->units_direction_names_index, |
| nr_string_directions + FIRSTINFILE_NR |
| + self->customized_global_units_directions.number |
| + self->added_global_units_directions.number |
| + self->customized_global_text_directions.number); |
| for (i = 0; self->main_units_direction_names[i]; i++) |
| { |
| uintptr_t idx = i; |
| c_hashmap_register (&self->units_direction_names_index, |
| self->main_units_direction_names[i], |
| (void *)idx); |
| } |
| |
| if (self->customized_global_units_directions.number > 0) |
| { |
| for (i = 0; i < self->customized_global_units_directions.number; i++) |
| { |
| DIRECTION_NODE_NAME *direction_node_name |
| = &self->customized_global_units_directions.list[i]; |
| const char *direction = direction_node_name->direction; |
| int main_directions_idx = html_get_direction_index (self, direction); |
| if (main_directions_idx < 0) |
| { |
| /* an added global output unit direction */ |
| /* it would be unlikely for the direction to already exist, |
| since it should ultimately come from a Perl hash. In case |
| it could be set from C, it is better to check */ |
| size_t added_string_nr |
| = find_string (&self->added_global_units_directions, |
| direction); |
| if (!added_string_nr) |
| { |
| add_string (direction, &self->added_global_units_directions); |
| added_string_nr = self->added_global_units_directions.number; |
| } |
| direction_node_name->direction_nr |
| = nr_special_units + NON_SPECIAL_DIRECTIONS_NR |
| + added_string_nr; |
| } |
| else |
| { |
| if (main_directions_idx > DEFAULT_GLOBAL_DIRECTION_LAST_IDX) |
| { |
| /* do not replace relative directions */ |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, "keep direction %s, do not set to `%s'", |
| self->main_units_direction_names[main_directions_idx], |
| direction_node_name->node_name); |
| } |
| else |
| direction_node_name->direction_nr = main_directions_idx+1; |
| } |
| } |
| } |
| |
| if (self->added_global_units_directions.number) |
| { |
| self->main_units_direction_names |
| = (const char **) realloc (self->main_units_direction_names, |
| (nr_special_units + NON_SPECIAL_DIRECTIONS_NR + |
| self->added_global_units_directions.number +1) * sizeof (char *)); |
| |
| for (i = 0; i < self->added_global_units_directions.number; i++) |
| { |
| size_t added_idx = NON_SPECIAL_DIRECTIONS_NR + nr_special_units + i; |
| uintptr_t idx = added_idx; |
| |
| self->main_units_direction_names[added_idx] |
| = self->added_global_units_directions.list[i]; |
| |
| c_hashmap_register (&self->units_direction_names_index, |
| self->main_units_direction_names[added_idx], |
| (void *)idx); |
| } |
| |
| nr_string_directions += self->added_global_units_directions.number; |
| |
| self->main_units_direction_names[ |
| nr_special_units + NON_SPECIAL_DIRECTIONS_NR |
| + self->added_global_units_directions.number] = 0; |
| } |
| |
| if (self->customized_global_text_directions.number) |
| { |
| /* remove duplicate directions starting from the end to remove only |
| already seen strings */ |
| for (i = self->customized_global_text_directions.number; i > 0; i--) |
| { |
| const char *direction |
| = self->customized_global_text_directions.list[i-1]; |
| int main_directions_idx = html_get_direction_index (self, direction); |
| if (main_directions_idx >= 0) |
| { |
| remove_from_strings_list ( |
| &self->customized_global_text_directions, i-1); |
| } |
| } |
| |
| if (self->customized_global_text_directions.number) |
| /* now add to customized_global_text_directions */ |
| { |
| self->main_units_direction_names |
| = (const char **) realloc (self->main_units_direction_names, |
| (nr_special_units + NON_SPECIAL_DIRECTIONS_NR + |
| self->added_global_units_directions.number + |
| self->customized_global_text_directions.number +1) * sizeof (char *)); |
| |
| for (i = 0; i < self->customized_global_text_directions.number; i++) |
| { |
| const char *direction |
| = self->customized_global_text_directions.list[i]; |
| size_t added_idx = nr_special_units + NON_SPECIAL_DIRECTIONS_NR |
| + self->added_global_units_directions.number +i; |
| uintptr_t idx = added_idx; |
| |
| self->main_units_direction_names[added_idx] = direction; |
| |
| c_hashmap_register (&self->units_direction_names_index, |
| self->main_units_direction_names[added_idx], |
| (void *)idx); |
| } |
| self->main_units_direction_names[ |
| nr_special_units + NON_SPECIAL_DIRECTIONS_NR |
| + self->added_global_units_directions.number |
| + self->customized_global_text_directions.number] = 0; |
| } |
| |
| nr_string_directions += self->customized_global_text_directions.number; |
| } |
| |
| /* |
| for (i = 0; self->main_units_direction_names[i]; i++) |
| fprintf (stderr, "DEBUG: direction unit names: %zu '%s'\n", i, |
| self->main_units_direction_names[i]); |
| */ |
| |
| return nr_string_directions; |
| } |
| |
| |
| /* When initializing from Perl, html_converter_get_customization_sv is called |
| first to set customization information based on Perl data. Then this |
| function is called for the remainder of initialization that requires |
| customization information. |
| */ |
| void |
| html_converter_customize (CONVERTER *self) |
| { |
| size_t i; |
| int l; |
| enum direction_string_type DS_type; |
| size_t nr_special_units = self->special_unit_varieties.number; |
| int nr_string_directions = html_nr_string_directions (self); |
| int nr_dir_str_contexts = TDS_context_string +1; |
| int *non_default_special_unit_directions = |
| determine_non_default_special_unit_directions (self); |
| |
| /* counters of external formatting functions */ |
| int external_special_unit_body_formatting_function = 0; |
| int external_output_unit_conversion_function = 0; |
| int external_command_conversion_function = 0; |
| int external_command_open_function = 0; |
| int external_type_conversion_function = 0; |
| int external_type_open_function = 0; |
| int external_formatting_function = 0; |
| int added_external_references; |
| |
| /* for @sc */ |
| for (l = 0; default_upper_case_commands[l]; l++) |
| self->upper_case[default_upper_case_commands[l]] = 1; |
| |
| for (l = 0; default_code_types[l]; l++) |
| self->code_types[default_code_types[l]] = 1; |
| |
| for (l = 0; default_pre_class_types[l].type; l++) |
| { |
| const PRE_CLASS_TYPE_INFO *pre_class_type = &default_pre_class_types[l]; |
| self->pre_class_types[pre_class_type->type] |
| = strdup (pre_class_type->pre_class); |
| } |
| |
| for (l = 0; xml_accent_text_entities[l].cmd; l++) |
| { |
| enum command_id cmd = xml_accent_text_entities[l].cmd; |
| const ACCENT_ENTITY_INFO *xml_accent_info |
| = &xml_accent_text_entities[l].accent_entity_info; |
| ACCENT_ENTITY_INFO *accent_info = &self->accent_entities[cmd]; |
| |
| if (xml_accent_info->entity) |
| accent_info->entity = strdup (xml_accent_info->entity); |
| if (xml_accent_info->characters) |
| accent_info->characters = strdup (xml_accent_info->characters); |
| } |
| |
| for (i = 0; i < style_formatted_cmd.number; i++) |
| { |
| enum command_id cmd = style_formatted_cmd.list[i]; |
| enum conversion_context cctx; |
| for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++) |
| { |
| HTML_STYLE_COMMAND_CONVERSION *format_spec |
| = &self->html_style_command_conversion[cmd][cctx]; |
| HTML_STYLE_COMMAND_CONVERSION *default_spec |
| = &default_style_commands_formatting[cmd][cctx]; |
| |
| if (default_spec->element) |
| format_spec->element = strdup (default_spec->element); |
| format_spec->quote = default_spec->quote; |
| } |
| } |
| |
| /* apply customization (from Perl) */ |
| |
| if (self->html_customized_upper_case_commands) |
| { |
| for (l = 0; self->html_customized_upper_case_commands[l].cmd; l++) |
| { |
| COMMAND_INTEGER_INFORMATION *customized_upper |
| = &self->html_customized_upper_case_commands[l]; |
| self->upper_case[customized_upper->cmd] = customized_upper->integer; |
| } |
| } |
| |
| if (self->html_customized_code_types) |
| { |
| for (l = 0; self->html_customized_code_types[l].type; l++) |
| { |
| TYPE_INTEGER_INFORMATION *customized_code |
| = &self->html_customized_code_types[l]; |
| self->code_types[customized_code->type] = customized_code->integer; |
| } |
| } |
| |
| if (self->html_customized_pre_class_types) |
| { |
| for (l = 0; self->html_customized_pre_class_types[l].type; l++) |
| { |
| PRE_CLASS_TYPE_INFO *customized_pre_class |
| = &self->html_customized_pre_class_types[l]; |
| enum element_type type = customized_pre_class->type; |
| |
| free (self->pre_class_types[type]); |
| self->pre_class_types[type] |
| = strdup (customized_pre_class->pre_class); |
| } |
| } |
| |
| if (self->html_customized_accent_entity_info) |
| { |
| for (l = 0; self->html_customized_accent_entity_info[l].cmd; l++) |
| { |
| enum command_id cmd = self->html_customized_accent_entity_info[l].cmd; |
| const ACCENT_ENTITY_INFO *custom_accent_info |
| = &self->html_customized_accent_entity_info[l].accent_entity_info; |
| ACCENT_ENTITY_INFO *accent_info = &self->accent_entities[cmd]; |
| |
| if (custom_accent_info->entity) |
| { |
| free (accent_info->entity); |
| if (strlen (custom_accent_info->entity)) |
| accent_info->entity = strdup (custom_accent_info->entity); |
| else |
| accent_info->entity = 0; |
| } |
| if (custom_accent_info->characters) |
| { |
| free (accent_info->characters); |
| accent_info->characters = strdup (custom_accent_info->characters); |
| } |
| } |
| } |
| |
| if (self->html_customized_style_commands) |
| { |
| for (l = 0; self->html_customized_style_commands[l].cmd; l++) |
| { |
| enum conversion_context cctx; |
| enum command_id cmd = self->html_customized_style_commands[l].cmd; |
| /* should not happen thanks to checks in perl |
| if (!(html_commands_data[cmd].flags & HF_style_command)) |
| fprintf (stderr, "ERROR: %s: customized as style command\n", |
| builtin_command_name (cmd)); |
| */ |
| |
| for (cctx = 0; cctx < STYLE_COMMAND_CONTEXT_NR; cctx++) |
| { |
| if (self->html_customized_style_commands[l].conversion[cctx]) |
| { |
| HTML_STYLE_COMMAND_CONVERSION *format_spec |
| = &self->html_style_command_conversion[cmd][cctx]; |
| HTML_STYLE_COMMAND_CONVERSION *custom_spec |
| = self->html_customized_style_commands[l].conversion[cctx]; |
| |
| free (format_spec->element); |
| |
| if (custom_spec->element) |
| format_spec->element = strdup (custom_spec->element); |
| else |
| format_spec->element = 0; |
| format_spec->quote = custom_spec->quote; |
| } |
| } |
| } |
| } |
| |
| /* initialization needing some information not available before. Besides |
| customized information, mainly the number of special units, which we |
| pretend could be customized (even though it cannot for now) */ |
| |
| /* setup translated_direction_strings */ |
| for (DS_type = 0; DS_type < TDS_TRANSLATED_MAX_NR; DS_type++) |
| { |
| self->translated_direction_strings[DS_type] |
| = new_directions_strings_translated_type (nr_string_directions); |
| for (l = 0; l < nr_string_directions; l++) |
| { |
| HTML_DIRECTION_STRING_TRANSLATED *dir_string_translated |
| = &self->translated_direction_strings[DS_type][l]; |
| if (self->customized_translated_direction_strings[DS_type] |
| && self->customized_translated_direction_strings[DS_type][l]) |
| { |
| HTML_DIRECTION_STRING_TRANSLATED *custom_dir_translated |
| = self->customized_translated_direction_strings[DS_type][l]; |
| if (custom_dir_translated->to_convert) |
| dir_string_translated->to_convert |
| = strdup (custom_dir_translated->to_convert); |
| else |
| { |
| int j; |
| |
| for (j = 0; j < nr_dir_str_contexts; j++) |
| if (custom_dir_translated->converted[j]) |
| dir_string_translated->converted[j] |
| = strdup (custom_dir_translated->converted[j]); |
| } |
| } |
| else if (l < NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR |
| || (l < (NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR) |
| + (int) self->special_unit_varieties.number |
| && !non_default_special_unit_directions[ |
| l - (NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR)])) |
| { |
| const HTML_DEFAULT_DIRECTION_STRING_TRANSLATED *default_dir_translated |
| = &default_translated_directions_strings[DS_type][l]; |
| if (default_dir_translated->to_convert) |
| dir_string_translated->to_convert |
| = strdup (default_dir_translated->to_convert); |
| else |
| { |
| if (default_dir_translated->converted) |
| { |
| int j; |
| |
| for (j = 0; j < nr_dir_str_contexts; j++) |
| dir_string_translated->converted[j] |
| = strdup (default_dir_translated->converted); |
| } |
| } |
| } |
| } |
| } |
| |
| free (non_default_special_unit_directions); |
| |
| |
| /* allocate space for translated tree types, they will be created |
| on-demand during the conversion */ |
| for (l = 0; l < SPECIAL_UNIT_INFO_TREE_NR; l++) |
| { |
| self->translated_special_unit_info_tree[l] = (ELEMENT **) |
| malloc ((nr_special_units +1) * sizeof (ELEMENT *)); |
| memset (self->translated_special_unit_info_tree[l], 0, |
| (nr_special_units +1) * sizeof (ELEMENT *)); |
| } |
| |
| self->global_units_directions |
| = (const OUTPUT_UNIT **) malloc ((DEFAULT_GLOBAL_DIRECTION_LAST_IDX |
| + nr_special_units |
| + self->added_global_units_directions.number+1) |
| * sizeof (OUTPUT_UNIT)); |
| |
| /* note that we allocate the same size as no_arg_formatted_cmd |
| even though in general there are much less translated commands, |
| for simplicity */ |
| if (no_arg_formatted_cmd.number) |
| { |
| self->no_arg_formatted_cmd_translated.list = (enum command_id *) |
| malloc (no_arg_formatted_cmd.number * sizeof (enum command_id)); |
| memset (self->no_arg_formatted_cmd_translated.list, 0, |
| no_arg_formatted_cmd.number * sizeof (enum command_id)); |
| } |
| |
| for (l = 0; command_special_unit_variety[l].cmd; l++) |
| { |
| char *special_unit_variety = command_special_unit_variety[l].variety; |
| /* number is index +1 */ |
| size_t number = find_string (&self->special_unit_varieties, |
| special_unit_variety); |
| enum command_id cmd = command_special_unit_variety[l].cmd; |
| html_commands_data[cmd].flags |= HF_special_variety; |
| self->command_special_variety_name_index[l].cmd = cmd; |
| self->command_special_variety_name_index[l].index = number - 1; |
| } |
| |
| for (l = 0; ignored_types[l]; l++) |
| { |
| enum element_type type = ignored_types[l]; |
| self->type_conversion_function[type].status = FRS_status_ignored; |
| self->css_string_type_conversion_function[type].status |
| = FRS_status_ignored; |
| } |
| |
| for (l = 0; l < TXI_TREE_TYPES_NUMBER; l++) |
| { |
| int status = register_type_conversion_function ( |
| &self->type_conversion_function[l], |
| l, &self->types_conversion[l]); |
| external_type_conversion_function += status; |
| |
| status = register_type_open_function (&self->type_open_function[l], |
| l, &self->types_open[l]); |
| external_type_open_function += status; |
| |
| register_type_conversion_function ( |
| &self->css_string_type_conversion_function[l], l, |
| &self->css_string_types_conversion[l]); |
| } |
| |
| for (l = 0; l < BUILTIN_CMD_NUMBER; l++) |
| { |
| int status = register_command_conversion_function ( |
| &self->command_conversion_function[l], |
| l, &self->commands_conversion[l]); |
| external_command_conversion_function += status; |
| |
| status = register_command_open_function ( |
| &self->command_open_function[l], |
| l, &self->commands_open[l]); |
| external_command_open_function += status; |
| |
| register_command_conversion_function ( |
| &self->css_string_command_conversion_function[l], l, |
| &self->css_string_commands_conversion[l]); |
| } |
| |
| for (l = 0; l < OU_special_unit+1; l++) |
| { |
| int status = register_output_unit_conversion_function |
| (&self->output_unit_conversion_function[l], |
| l, &self->output_units_conversion[l]); |
| external_output_unit_conversion_function += status; |
| } |
| |
| self->special_unit_body_formatting = (SPECIAL_UNIT_BODY_FORMATTING *) |
| malloc (nr_special_units * sizeof (SPECIAL_UNIT_BODY_FORMATTING)); |
| memset (self->special_unit_body_formatting, 0, |
| nr_special_units * sizeof (SPECIAL_UNIT_BODY_FORMATTING)); |
| |
| for (i = 0; i < nr_special_units; i++) |
| { |
| int status; |
| FORMATTING_REFERENCE *formatting_reference = 0; |
| if (self->special_unit_body) |
| { |
| formatting_reference = &self->special_unit_body[i]; |
| } |
| status = register_special_unit_body_formatting_function |
| (&self->special_unit_body_formatting[i], |
| self->special_unit_varieties.list[i], formatting_reference); |
| external_special_unit_body_formatting_function += status; |
| } |
| |
| qsort (self->htmlxref.list, self->htmlxref.number, |
| sizeof (HTMLXREF_MANUAL), compare_htmlxref_manual); |
| |
| /* set to customization such that it is not replaced by C functions */ |
| if (self->conf->XS_EXTERNAL_FORMATTING.o.integer > 0) |
| { |
| for (l = 0; l < FR_format_translate_message+1; l++) |
| if (self->formatting_references[l].status == FRS_status_default_set) |
| { |
| self->formatting_references[l].status |
| = FRS_status_customization_set; |
| external_formatting_function++; |
| } |
| } |
| else |
| { /* count the functions implemented in perl */ |
| for (l = 0; l < FR_format_translate_message+1; l++) |
| if (self->formatting_references[l].status |
| == FRS_status_customization_set) |
| external_formatting_function++; |
| } |
| |
| /* remaining of the file is for the replacement of call to external |
| functions by internal functions in C. |
| */ |
| if (self->conf->XS_EXTERNAL_CONVERSION.o.integer > 0) |
| goto out; |
| |
| for (i = 0; types_internal_conversion_table[i].type_conversion; i++) |
| { |
| enum element_type type = types_internal_conversion_table[i].type; |
| TYPE_CONVERSION_FUNCTION *type_conversion |
| = &self->type_conversion_function[type]; |
| TYPE_CONVERSION_FUNCTION *css_string_type_conversion |
| = &self->css_string_type_conversion_function[type]; |
| if (type_conversion->status == FRS_status_default_set |
| || type_conversion->status == FRS_status_none) |
| { |
| if (type_conversion->status == FRS_status_default_set) |
| external_type_conversion_function--; |
| type_conversion->formatting_reference = 0; |
| type_conversion->status = FRS_status_internal; |
| type_conversion->type_conversion |
| = types_internal_conversion_table[i].type_conversion; |
| } |
| css_string_type_conversion->formatting_reference = 0; |
| css_string_type_conversion->status = FRS_status_internal; |
| if (type == ET_text) |
| css_string_type_conversion->type_conversion |
| = &html_css_string_convert_text; |
| else |
| css_string_type_conversion->type_conversion |
| = types_internal_conversion_table[i].type_conversion; |
| } |
| |
| for (i = 0; types_internal_open_table[i].type_open; i++) |
| { |
| enum element_type type = types_internal_open_table[i].type; |
| TYPE_OPEN_FUNCTION *type_open = &self->type_open_function[type]; |
| if (type_open->status == FRS_status_default_set |
| || type_open->status == FRS_status_none) |
| { |
| if (type_open->status == FRS_status_default_set) |
| external_type_open_function--; |
| type_open->formatting_reference = 0; |
| type_open->status = FRS_status_internal; |
| type_open->type_open |
| = types_internal_open_table[i].type_open; |
| } |
| } |
| |
| for (i = 0; commands_internal_conversion_table[i].command_conversion; i++) |
| { |
| enum command_id cmd = commands_internal_conversion_table[i].cmd; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| COMMAND_CONVERSION_FUNCTION *css_string_command_conversion |
| = &self->css_string_command_conversion_function[cmd]; |
| if (command_conversion->status == FRS_status_default_set |
| || command_conversion->status == FRS_status_none) |
| { |
| if (command_conversion->status == FRS_status_default_set) |
| external_command_conversion_function--; |
| command_conversion->formatting_reference = 0; |
| command_conversion->status = FRS_status_internal; |
| command_conversion->command_conversion |
| = commands_internal_conversion_table[i].command_conversion; |
| } |
| css_string_command_conversion->formatting_reference = 0; |
| css_string_command_conversion->status = FRS_status_internal; |
| css_string_command_conversion->command_conversion |
| = commands_internal_conversion_table[i].command_conversion; |
| } |
| |
| if (strcmp (self->conf->FORMAT_MENU.o.string, "menu") |
| && strcmp (self->conf->FORMAT_MENU.o.string, "menu_no_detailmenu")) |
| { |
| static enum command_id menu_cmd_list[] |
| = {CM_menu, CM_detailmenu, 0}; |
| int j; |
| for (j = 0; menu_cmd_list[j]; j++) |
| { |
| enum command_id cmd = menu_cmd_list[j]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| if (command_conversion->status == FRS_status_internal) |
| command_conversion->status = FRS_status_ignored; |
| } |
| } |
| for (i = 0; i < format_raw_cmd.number; i++) |
| { |
| enum command_id cmd = format_raw_cmd.list[i]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| if (command_conversion->status == FRS_status_internal) |
| { |
| const char *format = builtin_command_name (cmd); |
| if (!format_expanded_p (self->expanded_formats, format)) |
| command_conversion->status = FRS_status_ignored; |
| } |
| } |
| |
| /* all the no arg formatted commands are implemented in C */ |
| for (i = 0; i < no_arg_formatted_cmd.number; i++) |
| { |
| enum command_id cmd = no_arg_formatted_cmd.list[i]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| COMMAND_CONVERSION_FUNCTION *css_string_command_conversion |
| = &self->css_string_command_conversion_function[cmd]; |
| if (command_conversion->status == FRS_status_default_set |
| || command_conversion->status == FRS_status_none) |
| { |
| if (command_conversion->status == FRS_status_default_set) |
| external_command_conversion_function--; |
| command_conversion->formatting_reference = 0; |
| command_conversion->status = FRS_status_internal; |
| command_conversion->command_conversion |
| = &html_convert_no_arg_command; |
| } |
| |
| css_string_command_conversion->formatting_reference = 0; |
| css_string_command_conversion->status = FRS_status_internal; |
| css_string_command_conversion->command_conversion |
| = &html_css_string_convert_no_arg_command; |
| } |
| |
| /* all the accents commands are implemented in C */ |
| if (accent_cmd.number) |
| { |
| for (i = 0; i < accent_cmd.number; i++) |
| { |
| enum command_id cmd = accent_cmd.list[i]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| COMMAND_CONVERSION_FUNCTION *css_string_command_conversion |
| = &self->css_string_command_conversion_function[cmd]; |
| if (command_conversion->status == FRS_status_default_set |
| || command_conversion->status == FRS_status_none) |
| { |
| if (command_conversion->status == FRS_status_default_set) |
| external_command_conversion_function--; |
| command_conversion->formatting_reference = 0; |
| command_conversion->status = FRS_status_internal; |
| command_conversion->command_conversion |
| = &html_convert_accent_command; |
| } |
| css_string_command_conversion->formatting_reference = 0; |
| css_string_command_conversion->status = FRS_status_internal; |
| css_string_command_conversion->command_conversion |
| = &html_css_string_convert_accent_command; |
| } |
| } |
| |
| /* all the commands in style_formatted_cmd are implemented in C. |
| It is not only the style commands, some others too. indicateurl |
| is not in style_formatted_cmd */ |
| if (style_formatted_cmd.number) |
| { |
| for (i = 0; i < style_formatted_cmd.number; i++) |
| { |
| enum command_id cmd = style_formatted_cmd.list[i]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| COMMAND_CONVERSION_FUNCTION *css_string_command_conversion |
| = &self->css_string_command_conversion_function[cmd]; |
| |
| if (command_conversion->status == FRS_status_default_set |
| || command_conversion->status == FRS_status_none) |
| { |
| if (command_conversion->status == FRS_status_default_set) |
| external_command_conversion_function--; |
| command_conversion->formatting_reference = 0; |
| command_conversion->status = FRS_status_internal; |
| command_conversion->command_conversion |
| = &html_convert_style_command; |
| } |
| |
| css_string_command_conversion->formatting_reference = 0; |
| css_string_command_conversion->status = FRS_status_internal; |
| css_string_command_conversion->command_conversion |
| = &html_convert_style_command; |
| } |
| } |
| |
| /* preformatted commands are implemented in C */ |
| if (preformatted_cmd_list.top > 0) |
| { |
| for (i = 0; i < preformatted_cmd_list.top; i++) |
| { |
| enum command_id cmd = preformatted_cmd_list.stack[i]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| COMMAND_CONVERSION_FUNCTION *css_string_command_conversion |
| = &self->css_string_command_conversion_function[cmd]; |
| |
| if (command_conversion->status == FRS_status_default_set |
| || command_conversion->status == FRS_status_none) |
| { |
| if (command_conversion->status == FRS_status_default_set) |
| external_command_conversion_function--; |
| command_conversion->formatting_reference = 0; |
| command_conversion->status = FRS_status_internal; |
| command_conversion->command_conversion |
| = &html_convert_preformatted_command; |
| } |
| |
| css_string_command_conversion->formatting_reference = 0; |
| css_string_command_conversion->status = FRS_status_internal; |
| css_string_command_conversion->command_conversion |
| = &html_convert_preformatted_command; |
| } |
| } |
| /* def commands are implemented in C */ |
| if (def_cmd_list.top > 0) |
| { |
| for (i = 0; i < def_cmd_list.top; i++) |
| { |
| enum command_id cmd = def_cmd_list.stack[i]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| COMMAND_CONVERSION_FUNCTION *css_string_command_conversion |
| = &self->css_string_command_conversion_function[cmd]; |
| |
| if (command_conversion->status == FRS_status_default_set |
| || command_conversion->status == FRS_status_none) |
| { |
| if (command_conversion->status == FRS_status_default_set) |
| external_command_conversion_function--; |
| command_conversion->formatting_reference = 0; |
| command_conversion->status = FRS_status_internal; |
| command_conversion->command_conversion |
| = &html_convert_def_command; |
| } |
| |
| css_string_command_conversion->formatting_reference = 0; |
| css_string_command_conversion->status = FRS_status_internal; |
| css_string_command_conversion->command_conversion |
| = &html_convert_def_command; |
| } |
| } |
| |
| for (i = 0; commands_internal_open_table[i].command_open; i++) |
| { |
| enum command_id cmd = commands_internal_open_table[i].cmd; |
| COMMAND_OPEN_FUNCTION *command_open = &self->command_open_function[cmd]; |
| if (command_open->status == FRS_status_default_set |
| || command_open->status == FRS_status_none) |
| { |
| if (command_open->status == FRS_status_default_set) |
| external_command_open_function--; |
| command_open->formatting_reference = 0; |
| command_open->status = FRS_status_internal; |
| command_open->command_open |
| = commands_internal_open_table[i].command_open; |
| } |
| } |
| |
| for (i = 0; |
| output_units_internal_conversion_table[i].output_unit_conversion; i++) |
| { |
| enum output_unit_type type |
| = output_units_internal_conversion_table[i].type; |
| OUTPUT_UNIT_CONVERSION_FUNCTION *output_unit_conversion |
| = &self->output_unit_conversion_function[type]; |
| if (output_unit_conversion->status == FRS_status_default_set |
| || output_unit_conversion->status == FRS_status_none) |
| { |
| if (output_unit_conversion->status == FRS_status_default_set) |
| external_output_unit_conversion_function--; |
| output_unit_conversion->formatting_reference = 0; |
| output_unit_conversion->status = FRS_status_internal; |
| output_unit_conversion->output_unit_conversion |
| = output_units_internal_conversion_table[i].output_unit_conversion; |
| } |
| } |
| |
| for (i = 0; |
| special_unit_body_internal_formatting_table[i].special_unit_variety; i++) |
| { |
| const SPECIAL_UNIT_BODY_INTERNAL_CONVERSION *internal_conversion |
| = &special_unit_body_internal_formatting_table[i]; |
| /* number is index +1 */ |
| size_t number = find_string (&self->special_unit_varieties, |
| internal_conversion->special_unit_variety); |
| int j = number -1; |
| if (j >= 0) |
| { |
| SPECIAL_UNIT_BODY_FORMATTING *body_formatting |
| = &self->special_unit_body_formatting[j]; |
| if (body_formatting->status == FRS_status_default_set |
| || body_formatting->status == FRS_status_none) |
| { |
| if (body_formatting->status == FRS_status_default_set) |
| external_special_unit_body_formatting_function--; |
| body_formatting->formatting_reference = 0; |
| body_formatting->status = FRS_status_internal; |
| body_formatting->special_unit_body_formatting |
| = internal_conversion->special_unit_body_formatting; |
| } |
| } |
| } |
| |
| out: |
| added_external_references = self->conf->BIT_user_function_number |
| + self->file_id_setting_ref_number |
| + external_special_unit_body_formatting_function |
| + external_output_unit_conversion_function |
| + external_command_conversion_function |
| + external_command_open_function |
| + external_type_conversion_function |
| + external_type_open_function |
| + external_formatting_function; |
| |
| self->external_references_number += added_external_references; |
| |
| /* |
| fprintf (stderr, "TOTAL: %d. prev %d conf %zu fi %d sbf %d ouc %d cc %d co %d tc %d to %d f %d\n", |
| self->external_references_number, |
| self->external_references_number - added_external_references, |
| self->conf->BIT_user_function_number, |
| self->file_id_setting_ref_number, |
| external_special_unit_body_formatting_function, |
| external_output_unit_conversion_function, |
| external_command_conversion_function, |
| external_command_open_function, |
| external_type_conversion_function, |
| external_type_open_function, |
| external_formatting_function); |
| */ |
| |
| /* modified when formatting a Texinfo as a CSS string, but always |
| reset to these values afterwards */ |
| /* set it now and not right before starting to output, as the |
| current_* may be used in code called before starting the |
| conversion, mainly for index strings sorting to use a customized |
| function for translation */ |
| self->current_formatting_references = &self->formatting_references[0]; |
| self->current_commands_conversion_function |
| = &self->command_conversion_function[0]; |
| self->current_types_conversion_function = &self->type_conversion_function[0]; |
| self->current_format_protect_text = &html_default_format_protect_text; |
| } |
| |
| |
| |
| /* Initialize output state. Sequence of: |
| html_conversion_initialization |
| html_setup_output for output() or html_setup_convert for convert() |
| |
| To be followed by setting up output units |
| html_prepare_conversion_units |
| */ |
| |
| void |
| free_html_no_arg_command_conversion ( |
| HTML_NO_ARG_COMMAND_CONVERSION *format_spec) |
| { |
| free (format_spec->element); |
| free (format_spec->text); |
| free (format_spec->translated_converted); |
| } |
| |
| static void |
| copy_html_no_arg_command_conversion (HTML_NO_ARG_COMMAND_CONVERSION *to, |
| HTML_NO_ARG_COMMAND_CONVERSION *from) |
| { |
| if (from->element) |
| to->element = strdup (from->element); |
| else |
| to->element = 0; |
| to->unset = from->unset; |
| if (from->text) |
| to->text = strdup (from->text); |
| else |
| to->text = 0; |
| if (from->translated_converted) |
| to->translated_converted = strdup (from->translated_converted); |
| else |
| to->translated_converted = 0; |
| } |
| |
| char *** |
| new_directions_strings_type (int nr_string_directions, |
| int nr_dir_str_contexts) |
| { |
| int i; |
| char ***result = (char ***) |
| malloc (nr_string_directions * sizeof (char **)); |
| memset (result, 0, |
| nr_string_directions * sizeof (char **)); |
| |
| for (i = 0; i < nr_string_directions; i++) |
| { |
| result[i] = (char **) |
| malloc (nr_dir_str_contexts * sizeof (char *)); |
| memset (result[i], 0, |
| nr_dir_str_contexts * sizeof (char *)); |
| } |
| |
| return result; |
| } |
| |
| /* transform <hr> to <hr/> |
| main effect is s/^(<[a-zA-Z]+[^<>]*)>$/$1\/>/ */ |
| static char * |
| xhtml_re_close_lone_element (const char *input) |
| { |
| size_t len = strlen (input); |
| size_t n; |
| const char *p; |
| char *result; |
| if (len < 3 || input[len -1] != '>' || input[0] != '<' |
| || !isascii_alpha (input[1])) |
| return strdup (input); |
| |
| /* before > */ |
| p = input + len - 2; |
| while (p > input +1) |
| { |
| if (*p == '/') |
| /* already a closed lone element */ |
| return strdup (input); |
| if (strchr (whitespace_chars, *p)) |
| p--; |
| else |
| break; |
| } |
| |
| p = input + 2; |
| n = strcspn (p, "<>"); |
| if (n +2 != len -1) |
| return strdup (input); |
| |
| result = (char *) malloc ((len +1 +1) * sizeof (char)); |
| memcpy (result, input, (len -1) * sizeof (char)); |
| result[len -1] = '/'; |
| result[len] = '>'; |
| result[len+1] = '\0'; |
| |
| return result; |
| } |
| |
| static void |
| close_lone_conf_element (OPTION *option) |
| { |
| const char *variable_value = option->o.string; |
| if (variable_value) |
| { |
| char *closed_lone_element = xhtml_re_close_lone_element (variable_value); |
| if (strcmp (closed_lone_element, variable_value)) |
| { |
| option_force_conf (option, 0, closed_lone_element); |
| } |
| free (closed_lone_element); |
| } |
| } |
| |
| void |
| free_js_categories_list (JSLICENSE_CATEGORY_LIST *js_files_info) |
| { |
| if (js_files_info->number) |
| { |
| size_t i; |
| for (i = 0; i < js_files_info->number; i++) |
| { |
| JSLICENSE_FILE_INFO_LIST *jslicences_files_info |
| = &js_files_info->list[i]; |
| free (jslicences_files_info->category); |
| if (jslicences_files_info->number) |
| { |
| size_t j; |
| for (j = 0; j < jslicences_files_info->number; j++) |
| { |
| JSLICENSE_FILE_INFO *jslicense_file_info |
| = &jslicences_files_info->list[j]; |
| free (jslicense_file_info->filename); |
| free (jslicense_file_info->license); |
| free (jslicense_file_info->url); |
| free (jslicense_file_info->source); |
| } |
| } |
| free (jslicences_files_info->list); |
| } |
| free (js_files_info->list); |
| } |
| js_files_info->number = 0; |
| } |
| |
| static void |
| reset_html_targets_list (CONVERTER *self, HTML_TARGET_LIST *targets) |
| { |
| if (targets->number) |
| { |
| size_t i; |
| for (i = 0; i < targets->number; i++) |
| { |
| int j; |
| HTML_TARGET *html_target = &targets->list[i]; |
| /* setup before conversion */ |
| free (html_target->target); |
| free (html_target->special_unit_filename); |
| free (html_target->node_filename); |
| free (html_target->section_filename); |
| free (html_target->contents_target); |
| free (html_target->shortcontents_target); |
| |
| free_tree_added_elements (&html_target->tree); |
| free_tree_added_elements (&html_target->tree_nonumber); |
| free_tree_added_elements (&html_target->name_tree); |
| free_tree_added_elements (&html_target->name_tree_nonumber); |
| |
| for (j = 0; j < HTT_string_nonumber+1; j++) |
| free (html_target->command_text[j]); |
| |
| for (j = 0; j < HTT_string_nonumber+1; j++) |
| free (html_target->command_description[j]); |
| |
| for (j = 0; j < HTT_string_nonumber+1; j++) |
| free (html_target->command_name[j]); |
| } |
| targets->number = 0; |
| } |
| } |
| |
| void |
| reset_html_targets (CONVERTER *self) |
| { |
| size_t i; |
| |
| for (i = 0; i < self->html_target_cmds.top; i++) |
| { |
| enum command_id cmd = self->html_target_cmds.stack[i]; |
| reset_html_targets_list (self, &self->html_targets[cmd]); |
| } |
| |
| for (i = 0; i < ST_footnote_location+1; i++) |
| { |
| reset_html_targets_list (self, &self->html_special_targets[i]); |
| } |
| } |
| |
| void |
| reset_html_page_css (CONVERTER *self) |
| { |
| size_t i; |
| |
| for (i = 0; i < self->page_css.number; i++) |
| { |
| size_t j; |
| CSS_LIST *page_css_list = &self->page_css.list[i]; |
| |
| for (j = 0; j < page_css_list->number; j++) |
| free (page_css_list->list[j]); |
| free (page_css_list->list); |
| free (page_css_list->page_name); |
| } |
| self->page_css.number = 0; |
| } |
| |
| static const enum command_id spaces_cmd[] = { |
| CM_SPACE, CM_TAB, CM_NEWLINE, CM_tie |
| }; |
| |
| /* called first in convert and output */ |
| void |
| html_conversion_initialization (CONVERTER *self, const char *context, |
| DOCUMENT *document) |
| { |
| int i; |
| size_t j; |
| const char *output_encoding; |
| int nr_string_directions = html_nr_string_directions (self); |
| int nr_dir_str_contexts = TDS_context_string + 1; |
| enum direction_string_type DS_type; |
| const char *line_break_element; |
| int *non_default_special_unit_directions = |
| determine_non_default_special_unit_directions (self); |
| |
| converter_set_document (self, document); |
| |
| if (!document && self->conf->DEBUG.o.integer > 0) |
| { |
| fprintf (stderr, |
| "C|REMARK: conversion_initialization: no document\n"); |
| } |
| |
| /* corresponds with default_no_arg_commands_formatting |
| + conf_default_no_arg_commands_formatting_normal in Perl */ |
| HTML_NO_ARG_COMMAND_CONVERSION |
| output_no_arg_commands_formatting[BUILTIN_CMD_NUMBER] |
| [NO_ARG_COMMAND_CONTEXT_NR]; |
| |
| /* in Perl converter_info is reset here, in C corresponds to |
| different fields of the converter structure. |
| They are reset here when relevant */ |
| |
| free (self->title_titlepage); |
| self->title_titlepage = 0; |
| free (self->title_string); |
| self->title_string = 0; |
| free (self->documentdescription_string); |
| self->documentdescription_string = 0; |
| free (self->documentinfo_metadata); |
| self->documentinfo_metadata = 0; |
| free (self->copying_comment); |
| self->copying_comment = 0; |
| free (self->destination_directory); |
| self->destination_directory = 0; |
| free (self->document_name); |
| self->document_name = 0; |
| |
| self->simpletitle_tree = 0; |
| self->simpletitle_cmd = CM_NONE; |
| |
| if (self->added_title_tree) |
| { |
| destroy_element_and_children (self->title_tree); |
| |
| self->added_title_tree = 0; |
| } |
| self->title_tree = 0; |
| |
| set_global_document_commands (self, CL_before, informative_global_commands); |
| set_global_document_commands (self, CL_before, contents_elements_options); |
| |
| clear_strings_list (&self->css_import_lines); |
| clear_strings_list (&self->css_rule_lines); |
| copy_strings (&self->css_import_lines, &self->files_css_import_lines); |
| copy_strings (&self->css_rule_lines, &self->files_css_rule_lines); |
| |
| html_reset_shared_conversion_state (self); |
| |
| reset_html_page_css (self); |
| |
| reset_html_targets (self); |
| |
| html_clear_pending_footnotes (&self->pending_footnotes); |
| |
| self->current_node = 0; |
| self->current_root_command = 0; |
| |
| free_translation_cache (self->translation_cache); |
| self->translation_cache = 0; |
| |
| /* it actually matters only with output, not with convert, but it is |
| better to reset in any case */ |
| free_js_categories_list (&self->jslicenses); |
| |
| output_encoding = self->conf->OUTPUT_ENCODING_NAME.o.string; |
| |
| for (i = 0; i < SC_non_breaking_space+1; i++) |
| { |
| const char *unicode_point = special_characters_formatting[i][2]; |
| const char *entity = special_characters_formatting[i][0]; |
| const char *encoded_string = special_characters_formatting[i][1]; |
| const char *numeric_entity = special_characters_formatting[i][3]; |
| const char *special_character_string; |
| |
| if (self->conf->OUTPUT_CHARACTERS.o.integer > 0 |
| && unicode_point_decoded_in_encoding (output_encoding, |
| unicode_point)) |
| special_character_string = encoded_string; |
| else if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0) |
| special_character_string = numeric_entity; |
| else |
| special_character_string = entity; |
| |
| self->special_character[i].string = special_character_string; |
| self->special_character[i].len = strlen (special_character_string); |
| } |
| |
| if (!self->conf->OPEN_QUOTE_SYMBOL.o.string) |
| { |
| int set = option_set_conf (&self->conf->OPEN_QUOTE_SYMBOL, 0, |
| self->special_character[SC_left_quote].string); |
| /* override undef set in init file/command line */ |
| if (!set) |
| option_force_conf (&self->conf->OPEN_QUOTE_SYMBOL, 0, ""); |
| } |
| if (!self->conf->CLOSE_QUOTE_SYMBOL.o.string) |
| { |
| int set = option_set_conf (&self->conf->CLOSE_QUOTE_SYMBOL, 0, |
| self->special_character[SC_right_quote].string); |
| /* override undef set in init file/command line */ |
| if (!set) |
| option_force_conf (&self->conf->CLOSE_QUOTE_SYMBOL, 0, ""); |
| } |
| if (!self->conf->MENU_SYMBOL.o.string) |
| { |
| int set = option_set_conf (&self->conf->MENU_SYMBOL, 0, |
| self->special_character[SC_bullet].string); |
| /* override undef set in init file/command line */ |
| if (!set) |
| option_force_conf (&self->conf->MENU_SYMBOL, 0, ""); |
| } |
| |
| if (self->conf->USE_XML_SYNTAX.o.integer > 0) |
| { |
| close_lone_conf_element (&self->conf->BIG_RULE); |
| close_lone_conf_element (&self->conf->DEFAULT_RULE); |
| line_break_element = "<br/>"; |
| } |
| else |
| line_break_element = "<br>"; |
| |
| self->line_break_element.string = line_break_element; |
| self->line_break_element.len = strlen (line_break_element); |
| |
| memcpy (output_no_arg_commands_formatting, default_no_arg_commands_formatting, |
| sizeof (default_no_arg_commands_formatting)); |
| |
| /* if not the textual entity */ |
| if (strcmp(self->special_character[SC_non_breaking_space].string, |
| special_characters_formatting[SC_non_breaking_space][0])) |
| { |
| for (j = 0; j < sizeof (spaces_cmd) / sizeof (spaces_cmd[0]); j++) |
| { |
| enum command_id cmd = spaces_cmd[j]; |
| /* cast to drop const */ |
| output_no_arg_commands_formatting[cmd][HCC_type_normal].text |
| = (char *)self->special_character[SC_non_breaking_space].string; |
| } |
| } |
| |
| if (self->conf->USE_NUMERIC_ENTITY.o.integer > 0) |
| { |
| for (j = 0; j < no_arg_formatted_cmd.number; j++) |
| { |
| enum command_id cmd = no_arg_formatted_cmd.list[j]; |
| if (unicode_entities[cmd]) |
| output_no_arg_commands_formatting[cmd][HCC_type_normal].text |
| = unicode_entities[cmd]; |
| } |
| } |
| |
| /* cast to discard const */ |
| output_no_arg_commands_formatting[CM_ASTERISK][HCC_type_normal].text |
| = (char *)self->line_break_element.string; |
| |
| clear_css_selector_style_list (&self->css_element_class_styles); |
| set_css_selector_style_list_size (&self->css_element_class_styles, |
| default_css_element_class_styles.number); |
| for (j = 0; j < default_css_element_class_styles.number; j++) |
| { |
| CSS_SELECTOR_STYLE *default_selector_style |
| = &default_css_element_class_styles.list[j]; |
| CSS_SELECTOR_STYLE *selector_style |
| = &self->css_element_class_styles.list[j]; |
| selector_style->selector = strdup (default_selector_style->selector); |
| selector_style->style = strdup (default_selector_style->style); |
| } |
| |
| for (j = 0; j < no_arg_formatted_cmd.number; j++) |
| { |
| enum command_id cmd = no_arg_formatted_cmd.list[j]; |
| enum conversion_context cctx; |
| HTML_NO_ARG_COMMAND_CUSTOMIZATION *customized_no_arg_cmd_formatting |
| = &self->customized_no_arg_commands_formatting[cmd]; |
| HTML_NO_ARG_COMMAND_FORMATTING *result_formatting |
| = &self->html_no_arg_command_conversion[cmd]; |
| |
| free (result_formatting->translated_to_convert); |
| if (customized_no_arg_cmd_formatting->translated_to_convert) |
| result_formatting->translated_to_convert |
| = strdup ( |
| customized_no_arg_cmd_formatting->translated_to_convert); |
| else |
| result_formatting->translated_to_convert = 0; |
| |
| for (cctx = 0; cctx < NO_ARG_COMMAND_CONTEXT_NR; cctx++) |
| { |
| HTML_NO_ARG_COMMAND_CONVERSION *customized_no_arg_cmd |
| = customized_no_arg_cmd_formatting->context_formatting[cctx]; |
| HTML_NO_ARG_COMMAND_CONVERSION *result |
| = &result_formatting->context_formatting[cctx]; |
| |
| free_html_no_arg_command_conversion (result); |
| |
| if (customized_no_arg_cmd) |
| { |
| copy_html_no_arg_command_conversion (result, |
| customized_no_arg_cmd); |
| } |
| else if (!output_no_arg_commands_formatting[cmd][cctx].unset) |
| { |
| const char *unicode_brace_no_arg_formatting = 0; |
| if (self->conf->OUTPUT_CHARACTERS.o.integer > 0) |
| { |
| unicode_brace_no_arg_formatting |
| = unicode_brace_no_arg_command (cmd, |
| self->conf->OUTPUT_ENCODING_NAME.o.string); |
| } |
| if (unicode_brace_no_arg_formatting) |
| { |
| memset (result, 0, sizeof (HTML_NO_ARG_COMMAND_CONVERSION)); |
| result->text |
| = strdup (unicode_brace_no_arg_formatting); |
| |
| /* reset CSS for itemize command arguments */ |
| if (cctx == HCC_type_css_string |
| && builtin_command_data[cmd].flags & CF_brace |
| && cmd != CM_bullet && cmd != CM_w) |
| { |
| const char *special_list_mark_command |
| = get_special_list_mark_css_string_no_arg_command (cmd); |
| |
| if (!special_list_mark_command) |
| { |
| char *selector; |
| char *style; |
| xasprintf (&selector, "ul.mark-%s", |
| builtin_command_name (cmd)); |
| xasprintf (&style, "list-style-type: \"%s\"", |
| result->text); |
| html_css_set_selector_style ( |
| &self->css_element_class_styles, |
| selector, style); |
| free (selector); |
| free (style); |
| } |
| } |
| } |
| else |
| { |
| copy_html_no_arg_command_conversion (result, |
| &output_no_arg_commands_formatting[cmd][cctx]); |
| } |
| } |
| else |
| { |
| copy_html_no_arg_command_conversion (result, |
| &output_no_arg_commands_formatting[cmd][cctx]); |
| } |
| } |
| } |
| |
| for (j = 0; j < no_arg_formatted_cmd.number; j++) |
| { |
| enum command_id cmd = no_arg_formatted_cmd.list[j]; |
| COMMAND_CONVERSION_FUNCTION *command_conversion |
| = &self->command_conversion_function[cmd]; |
| if (command_conversion->status == FRS_status_default_set |
| || command_conversion->status == FRS_status_internal) |
| { |
| html_complete_no_arg_commands_formatting (self, cmd, 0); |
| } |
| } |
| |
| for (DS_type = 0; DS_type < TDS_TYPE_MAX_NR; DS_type++) |
| { |
| int i; |
| const char * const*default_converted_dir_str; |
| char ***customized_type_dir_strings; |
| |
| if (!self->directions_strings[DS_type]) |
| self->directions_strings[DS_type] |
| = new_directions_strings_type (nr_string_directions, |
| nr_dir_str_contexts); |
| else |
| html_clear_direction_string_type (self, |
| self->directions_strings[DS_type]); |
| |
| /* those will be determined from translatable strings */ |
| if (DS_type < TDS_TRANSLATED_MAX_NR) |
| continue; |
| |
| default_converted_dir_str = |
| default_converted_directions_strings[ |
| DS_type - (TDS_TRANSLATED_MAX_NR)]; |
| customized_type_dir_strings = self->customized_directions_strings[ |
| DS_type - (TDS_TRANSLATED_MAX_NR)]; |
| for (i = 0; i < nr_string_directions; i++) |
| { |
| if (customized_type_dir_strings && customized_type_dir_strings[i]) |
| { |
| int j; |
| for (j = 0; j < nr_dir_str_contexts; j++) |
| { |
| if (customized_type_dir_strings[i][j]) |
| self->directions_strings[DS_type][i][j] |
| = html_substitute_non_breaking_space (self, |
| customized_type_dir_strings[i][j]); |
| } |
| } |
| else if (default_converted_dir_str[i]) |
| { |
| if (i < NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR |
| || (i < (int) self->special_unit_varieties.number + |
| NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR |
| && !non_default_special_unit_directions[ |
| i - (NON_SPECIAL_DIRECTIONS_NR - FIRSTINFILE_NR)])) |
| self->directions_strings[DS_type][i][TDS_context_normal] |
| = html_substitute_non_breaking_space (self, |
| default_converted_dir_str[i]); |
| } |
| |
| if (self->directions_strings[DS_type][i][TDS_context_normal] |
| && !self->directions_strings[DS_type][i][TDS_context_string]) |
| { |
| self->directions_strings[DS_type][i][TDS_context_string] |
| = strdup ( |
| self->directions_strings[DS_type][i][TDS_context_normal]); |
| } |
| } |
| } |
| |
| free (non_default_special_unit_directions); |
| |
| sort_css_element_class_styles (&self->css_element_class_styles); |
| |
| /* set the htmlxref type split of the document */ |
| self->document_htmlxref_split_type = htmlxref_split_type_mono; |
| |
| if (self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string)) |
| { |
| int i; |
| for (i = 1; i < htmlxref_split_type_chapter+1; i++) |
| { |
| if (!strcmp (self->conf->SPLIT.o.string, |
| htmlxref_split_type_names[i])) |
| { |
| self->document_htmlxref_split_type = i; |
| break; |
| } |
| } |
| } |
| |
| /* directions */ |
| memset (self->global_units_directions, 0, |
| (DEFAULT_GLOBAL_DIRECTION_LAST_IDX + self->special_unit_varieties.number |
| + self->added_global_units_directions.number +1) * sizeof (OUTPUT_UNIT)); |
| |
| free (self->global_units_direction_names.list); |
| self->global_units_direction_names.list = 0; |
| self->global_units_direction_names.number = 0; |
| |
| clear_strings_list (&self->check_htmlxref_already_warned); |
| |
| if (self->conf->NODE_NAME_IN_INDEX.o.integer < 0) |
| option_set_conf (&self->conf->NODE_NAME_IN_INDEX, |
| self->conf->USE_NODES.o.integer, 0); |
| |
| if (self->conf->HTML_MATH.o.string |
| && self->conf->CONVERT_TO_LATEX_IN_MATH.o.integer < 0) |
| { |
| option_set_conf (&self->conf->CONVERT_TO_LATEX_IN_MATH, 1, 0); |
| } |
| |
| if (self->conf->NO_TOP_NODE_OUTPUT.o.integer > 0 |
| && self->conf->SHOW_TITLE.o.integer < 0) |
| option_set_conf (&self->conf->SHOW_TITLE, 1, 0); |
| |
| if (self->conf->SPLIT.o.string != 0 |
| && !strcmp (self->conf->SPLIT.o.string, "node") |
| && self->conf->USE_ACCESSKEY.o.integer < 0) |
| option_set_conf (&self->conf->USE_ACCESSKEY, 1, 0); |
| |
| html_new_document_context (self, context, 0, 0, 0); |
| self->document_global_context_counter = 0; |
| |
| if (self->document && self->document->indices_info.number) |
| { |
| size_t i; |
| size_t j; |
| INDEX_LIST *indices_info = &self->document->indices_info; |
| size_t index_nr = indices_info->number; |
| size_t non_empty_index_nr = 0; |
| size_t idx_non_empty = 0; |
| |
| const INDEX **sorted_index_names = sort_index_names (indices_info); |
| |
| for (i = 0; i < index_nr; i++) |
| { |
| INDEX *idx = indices_info->list[i]; |
| if (idx->entries_number > 0) |
| non_empty_index_nr++; |
| } |
| |
| /* store only non empty indices in sorted_index_names */ |
| self->sorted_index_names.number = non_empty_index_nr; |
| /* resize if needed */ |
| if (self->sorted_index_names.number > self->sorted_index_names.space) |
| { |
| self->sorted_index_names.space = self->sorted_index_names.number; |
| self->sorted_index_names.list = (const INDEX **) |
| realloc (self->sorted_index_names.list, |
| self->sorted_index_names.space * sizeof (INDEX *)); |
| } |
| for (j = 0; j < index_nr; j++) |
| { |
| if (sorted_index_names[j]->entries_number > 0) |
| { |
| self->sorted_index_names.list[idx_non_empty] |
| = sorted_index_names[j]; |
| idx_non_empty++; |
| } |
| } |
| free (sorted_index_names); |
| } |
| else |
| self->sorted_index_names.number = 0; |
| |
| if (self->document) |
| { |
| const LISTOFFLOATS_TYPE_LIST *listoffloats |
| = &self->document->listoffloats; |
| |
| if (listoffloats->number) |
| { |
| self->shared_conversion_state.formatted_listoffloats_nr |
| = (int *) malloc (listoffloats->number * sizeof (int)); |
| memset (self->shared_conversion_state.formatted_listoffloats_nr, |
| 0, listoffloats->number * sizeof (int)); |
| } |
| } |
| } |
| |
| static void |
| init_conversion_after_setup_handler (CONVERTER *self) |
| { |
| /* the presence of contents elements in the document is used in diverse |
| places, set it once for all here */ |
| set_global_document_commands (self, CL_last, contents_elements_options); |
| |
| if (self->conf->OUTPUT_CHARACTERS.o.integer > 0 |
| && self->conf->OUTPUT_ENCODING_NAME.o.string |
| /* not sure if strcasecmp is needed or not */ |
| && !strcasecmp (self->conf->OUTPUT_ENCODING_NAME.o.string, "utf-8")) |
| self->use_unicode_text = 1; |
| } |
| |
| static void |
| fill_jslicense_file_info (JSLICENSE_FILE_INFO *jslicense_file_info, |
| const char *filename, const char *license, |
| const char *url, const char *source) |
| { |
| jslicense_file_info->filename = strdup (filename); |
| jslicense_file_info->license = strdup (license); |
| jslicense_file_info->url = strdup (url); |
| jslicense_file_info->source = strdup (source); |
| } |
| |
| void |
| initialize_js_categories_list (JSLICENSE_CATEGORY_LIST *js_files_info, |
| size_t size) |
| { |
| js_files_info->list = (JSLICENSE_FILE_INFO_LIST *) |
| malloc (size * sizeof (JSLICENSE_FILE_INFO_LIST)); |
| memset (js_files_info->list, 0, |
| size * sizeof (JSLICENSE_FILE_INFO_LIST)); |
| js_files_info->number = size; |
| } |
| |
| void |
| initialize_jslicense_files (JSLICENSE_FILE_INFO_LIST *jslicences_files_info, |
| const char *category, size_t size) |
| { |
| jslicences_files_info->category = strdup (category); |
| jslicences_files_info->list = (JSLICENSE_FILE_INFO *) |
| malloc (size * sizeof (JSLICENSE_FILE_INFO)); |
| memset (jslicences_files_info->list, 0, |
| size * sizeof (JSLICENSE_FILE_INFO)); |
| jslicences_files_info->number = size; |
| } |
| |
| /* first function to call a stage handler */ |
| int |
| html_setup_output (CONVERTER *self, char **paths) |
| { |
| int handler_fatal_error_level; |
| int setup_handler_status; |
| int js_categories_list_nr = 0; |
| char *body_element_attributes; |
| const char *default_bcp47_locale; |
| const char *preamble_bcp47_locale; |
| |
| /* Should not actually be needed, as it is already deleted after conversion |
| and each time it is set out of the conversion. */ |
| memset (&self->current_filename, 0, sizeof (FILE_NUMBER_NAME)); |
| |
| if (self->conf->OUTFILE.o.string) |
| { |
| int i; |
| int need_unsplit = 0; |
| const char *outfile = self->conf->OUTFILE.o.string; |
| if (!strlen(outfile) || !strcmp (outfile, "-")) |
| need_unsplit = 1; |
| else |
| { |
| for (i = 0; null_device_names[i]; i++) |
| { |
| if (!strcmp (null_device_names[i], outfile)) |
| { |
| need_unsplit = 1; |
| break; |
| } |
| } |
| } |
| if (need_unsplit) |
| { |
| option_force_conf (&self->conf->SPLIT, 0, ""); |
| option_force_conf (&self->conf->MONOLITHIC, 1, 0); |
| } |
| } |
| |
| if (self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string)) |
| option_set_conf (&self->conf->NODE_FILES, 1, 0); |
| |
| option_set_conf (&self->conf->EXTERNAL_CROSSREF_SPLIT, 0, |
| self->conf->SPLIT.o.string); |
| |
| handler_fatal_error_level = self->conf->HANDLER_FATAL_ERROR_LEVEL.o.integer; |
| if (handler_fatal_error_level < 0) |
| { |
| handler_fatal_error_level |
| = txi_base_options.HANDLER_FATAL_ERROR_LEVEL.o.integer; |
| option_force_conf (&self->conf->HANDLER_FATAL_ERROR_LEVEL, |
| handler_fatal_error_level, 0); |
| } |
| |
| if (self->conf->HTML_MATH.o.string |
| && !strcmp (self->conf->HTML_MATH.o.string, "mathjax")) |
| { |
| /* |
| See https://www.gnu.org/licenses/javascript-labels.html |
| |
| The link to the source for mathjax does not strictly follow the advice |
| there: instead we link to instructions for obtaining the full source in |
| its preferred form of modification. |
| */ |
| if (!self->conf->MATHJAX_SCRIPT.o.string) |
| option_set_conf (&self->conf->MATHJAX_SCRIPT, 0, |
| "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"); |
| |
| if (!self->conf->MATHJAX_SOURCE.o.string) |
| option_set_conf (&self->conf->MATHJAX_SOURCE, 0, |
| "http://docs.mathjax.org/en/latest/web/hosting.html#getting-mathjax-via-git"); |
| } |
| |
| setup_handler_status = html_run_stage_handlers (self, HSHT_type_setup); |
| |
| if (setup_handler_status < handler_fatal_error_level |
| && setup_handler_status > -handler_fatal_error_level) |
| {} |
| else |
| return 0; |
| |
| set_commands_options_value (self->commands_init_conf, |
| self->sorted_options, 0); |
| |
| default_bcp47_locale = current_bcp47_locale (self); |
| |
| set_converter_preamble_language_commands (self); |
| |
| preamble_bcp47_locale = current_bcp47_locale (self); |
| |
| if (strcmp (preamble_bcp47_locale, "")) |
| { |
| xasprintf (&body_element_attributes, "lang=\"%s\"", |
| preamble_bcp47_locale); |
| option_set_conf (&self->conf->BODY_ELEMENT_ATTRIBUTES, |
| 0, body_element_attributes); |
| free (body_element_attributes); |
| } |
| else |
| { |
| /* Note: HTML 5 documentation specifies that the lang attribute can |
| take an empty string as its value to specify that the language |
| is unknown. However, outputting lang="" is unnecessary. */ |
| } |
| |
| if (strcmp (default_bcp47_locale, preamble_bcp47_locale)) |
| { |
| self->current_lang_translations |
| = reset_lang_translation_from_customization ( |
| &converters_translation_cache, |
| self->conf->documentlanguage.o.string, |
| self->conf->documentscript.o.string, |
| TXI_CONVERT_STRINGS_NR); |
| } |
| |
| init_conversion_after_setup_handler (self); |
| |
| if (self->conf->HTML_MATH.o.string |
| && !strcmp (self->conf->HTML_MATH.o.string, "mathjax")) |
| js_categories_list_nr++; |
| |
| if (self->conf->INFO_JS_DIR.o.string) |
| js_categories_list_nr++; |
| |
| if (js_categories_list_nr > 0) |
| { |
| int i = 0; |
| initialize_js_categories_list (&self->jslicenses, js_categories_list_nr); |
| if (self->conf->HTML_MATH.o.string |
| && !strcmp (self->conf->HTML_MATH.o.string, "mathjax")) |
| { |
| JSLICENSE_FILE_INFO_LIST *jslicences_files_info |
| = &self->jslicenses.list[i]; |
| initialize_jslicense_files (jslicences_files_info, "mathjax", 1); |
| fill_jslicense_file_info (&jslicences_files_info->list[0], |
| self->conf->MATHJAX_SCRIPT.o.string, |
| "Apache License, Version 2.0.", |
| "https://www.apache.org/licenses/LICENSE-2.0", |
| self->conf->MATHJAX_SOURCE.o.string); |
| i++; |
| } |
| if (self->conf->INFO_JS_DIR.o.string) |
| { |
| JSLICENSE_FILE_INFO_LIST *jslicences_files_info |
| = &self->jslicenses.list[i]; |
| initialize_jslicense_files (jslicences_files_info, "infojs", 2); |
| fill_jslicense_file_info (&jslicences_files_info->list[0], |
| "js/info.js", |
| "GNU General Public License 3.0 or later", |
| "https://www.gnu.org/licenses/gpl-3.0.html", |
| "js/info.js"); |
| fill_jslicense_file_info (&jslicences_files_info->list[1], |
| "js/modernizr.js", "Expat", |
| "http://www.jclark.com/xml/copying.txt", |
| "js/modernizr.js"); |
| } |
| } |
| |
| /* ($output_file, $destination_directory, $output_filename, $document_name) */ |
| determine_files_and_directory (self, |
| self->conf->TEXINFO_OUTPUT_FORMAT.o.string, paths); |
| |
| self->document_name = strdup (paths[3]); |
| self->destination_directory = strdup (paths[1]); |
| |
| return 1; |
| } |
| |
| void |
| html_setup_convert (CONVERTER *self) |
| { |
| init_conversion_after_setup_handler (self); |
| } |
| |
| static OUTPUT_UNIT * |
| register_special_unit (CONVERTER *self, char *special_unit_variety) |
| { |
| ELEMENT *unit_command = new_element (ET_special_unit_element); |
| OUTPUT_UNIT *special_unit = new_output_unit (OU_special_unit); |
| |
| special_unit->special_unit_variety = special_unit_variety; |
| unit_command->e.c->associated_unit = special_unit; |
| special_unit->uc.special_unit_command = unit_command; |
| |
| return special_unit; |
| } |
| |
| typedef struct SPECIAL_UNIT_ORDER { |
| const char *order; |
| const char *variety; |
| } SPECIAL_UNIT_ORDER; |
| |
| static int |
| compare_special_units (const void *a, const void *b) |
| { |
| const SPECIAL_UNIT_ORDER *spu_order_a = (const SPECIAL_UNIT_ORDER *) a; |
| const SPECIAL_UNIT_ORDER *spu_order_b = (const SPECIAL_UNIT_ORDER *) b; |
| |
| int result = strcmp (spu_order_a->order, spu_order_b->order); |
| if (result != 0) |
| return result; |
| return strcmp (spu_order_a->variety, spu_order_b->variety); |
| } |
| |
| static void |
| prepare_special_units (CONVERTER *self, size_t output_units_descriptor) |
| { |
| size_t i; |
| STRING_LIST *special_unit_varieties = &self->special_unit_varieties; |
| SPECIAL_UNIT_ORDER *special_units_order; |
| OUTPUT_UNIT *previous_output_unit = 0; |
| |
| size_t special_units_descriptor |
| = new_output_units_descriptor (self->document); |
| size_t associated_special_units_descriptor |
| = new_output_units_descriptor (self->document); |
| |
| /* retrieve after reallocating */ |
| |
| OUTPUT_UNIT_LIST *special_units |
| = retrieve_output_units (self->document, special_units_descriptor); |
| |
| OUTPUT_UNIT_LIST *associated_special_units |
| = retrieve_output_units (self->document, |
| associated_special_units_descriptor); |
| |
| OUTPUT_UNIT_LIST *output_units |
| = retrieve_output_units (self->document, output_units_descriptor); |
| |
| /* for separate special output units */ |
| STRING_LIST *do_special = new_string_list (); |
| |
| self->output_units_descriptors[OUDT_special_units] = special_units_descriptor; |
| self->output_units_descriptors[OUDT_associated_special_units] |
| = associated_special_units_descriptor; |
| |
| if (self->document->sections_list.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) |
| { |
| int j; |
| char *special_unit_variety = 0; |
| char *contents_location |
| = self->conf->CONTENTS_OUTPUT_LOCATION.o.string; |
| |
| for (j = 0; command_special_unit_variety[j].cmd; j++) |
| { |
| if (command_special_unit_variety[j].cmd == cmd) |
| { |
| special_unit_variety |
| = command_special_unit_variety[j].variety; |
| break; |
| } |
| } |
| if (contents_location |
| && !strcmp (contents_location, "separate_element")) |
| add_string (special_unit_variety, do_special); |
| else |
| { |
| OUTPUT_UNIT *special_output_unit = 0; |
| const OUTPUT_UNIT *associated_output_unit = 0; |
| if (contents_location |
| && !strcmp (contents_location, "after_title")) |
| { |
| associated_output_unit = output_units->list[0]; |
| } |
| else if (contents_location |
| && !strcmp (contents_location, "after_top")) |
| { |
| if (self->document->global_commands.top) |
| {/* note that top is a uniq command */ |
| const ELEMENT *section_top |
| = self->document->global_commands.top; |
| |
| if (section_top->e.c->associated_unit) |
| associated_output_unit |
| = section_top->e.c->associated_unit; |
| } |
| if (!associated_output_unit) |
| continue; |
| } |
| else if (contents_location |
| && !strcmp (contents_location, "inline")) |
| { |
| const ELEMENT_LIST *global_command |
| = get_cmd_global_multi_command ( |
| &self->document->global_commands, cmd); |
| if (global_command->number > 0) |
| { |
| size_t i; |
| for (i = 0; i < global_command->number; i++) |
| { |
| const ELEMENT *command = global_command->list[i]; |
| ROOT_AND_UNIT *root_unit |
| = html_get_tree_root_element (self, command, 0); |
| if (root_unit->output_unit) |
| associated_output_unit = root_unit->output_unit; |
| free (root_unit); |
| if (associated_output_unit) |
| break; |
| } |
| } |
| else |
| continue; |
| } |
| else /* should not happen */ |
| continue; |
| |
| special_output_unit |
| = register_special_unit (self, special_unit_variety); |
| special_output_unit->associated_document_unit |
| = associated_output_unit; |
| add_to_output_unit_list (associated_special_units, |
| special_output_unit); |
| } |
| } |
| } |
| } |
| |
| if (self->document->global_commands.footnotes.number > 0 |
| && self->conf->footnotestyle.o.string |
| && !strcmp (self->conf->footnotestyle.o.string, "separate") |
| && output_units->number > 1) |
| add_string ("footnotes", do_special); |
| |
| if ((self->conf->DO_ABOUT.o.integer < 0 |
| && output_units->number > 1 |
| && ((self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string)) |
| || self->conf->HEADERS.o.integer > 0)) |
| || self->conf->DO_ABOUT.o.integer > 0) |
| add_string ("about", do_special); |
| |
| special_units_order = (SPECIAL_UNIT_ORDER *) |
| malloc (sizeof (SPECIAL_UNIT_ORDER) * do_special->number); |
| for (i = 0; i < do_special->number; i++) |
| { |
| char *special_unit_variety = do_special->list[i]; |
| special_units_order[i].order = html_special_unit_info (self, |
| SUI_type_order, |
| special_unit_variety); |
| special_units_order[i].variety = special_unit_variety; |
| } |
| |
| qsort (special_units_order, do_special->number, sizeof (SPECIAL_UNIT_ORDER), |
| compare_special_units); |
| |
| previous_output_unit = output_units->list[output_units->number-1]; |
| |
| for (i = 0; i < do_special->number; i++) |
| { |
| /* take the string from special_unit_varieties */ |
| char *special_unit_variety; |
| /* number is index +1 */ |
| size_t number = find_string (special_unit_varieties, |
| special_units_order[i].variety); |
| int special_unit_varieties_idx = number -1; |
| if (special_unit_varieties_idx < 0) |
| { |
| char *msg; |
| xasprintf (&msg, "special_unit_varieties not found: %s\n", |
| special_units_order[i].variety); |
| bug (msg); |
| } |
| |
| special_unit_variety |
| = special_unit_varieties->list[special_unit_varieties_idx]; |
| OUTPUT_UNIT *special_output_unit |
| = register_special_unit (self, special_unit_variety); |
| add_to_output_unit_list (special_units, |
| special_output_unit); |
| |
| if (previous_output_unit) |
| { |
| special_output_unit->tree_unit_directions[D_prev] |
| = previous_output_unit; |
| previous_output_unit->tree_unit_directions[D_next] |
| = special_output_unit; |
| } |
| previous_output_unit = special_output_unit; |
| } |
| |
| free (special_units_order); |
| destroy_strings_list (do_special); |
| } |
| |
| void |
| html_prepare_conversion_units (CONVERTER *self) |
| { |
| size_t output_units_descriptor; |
| |
| if (self->conf->USE_NODES.o.integer > 0) |
| output_units_descriptor = split_by_node (self->document); |
| else |
| output_units_descriptor = split_by_section (self->document); |
| self->output_units_descriptors[OUDT_units] = output_units_descriptor; |
| |
| /* configuration used to determine if a special element is to be done |
| (in addition to contents) */ |
| set_global_document_commands (self, CL_last, conf_for_special_units); |
| /* |
| NOTE if the last value of footnotestyle is separate, all the footnotes |
| formatted text are set to the special element set in _prepare_special_units |
| as _html_get_tree_root_element uses the Footnote direction for every footnote. |
| Therefore if @footnotestyle separate is set late in the document the current |
| value may not be consistent with the link obtained for the footnote |
| formatted text. This is not an issue, as the manual says that |
| @footnotestyle should only appear in the preamble, and it makes sense |
| to have something consistent in the whole document for footnotes position. |
| */ |
| prepare_special_units (self, output_units_descriptor); |
| |
| /* reset to the default */ |
| set_global_document_commands (self, CL_before, conf_for_special_units); |
| |
| self->document->modified_information |= F_DOCM_output_units; |
| } |
| |
| |
| |
| /* prepare HTML targets for nodes, floats, sections, footnotes, heading |
| commands, index entries and special output units. Also prepare |
| normalized file names for root commands. |
| |
| Both for output() and convert(). |
| NOTE: in Perl done in prepare_conversion_units, separated here to |
| be able, in XS, to setup some Perl data between html_prepare_conversion_units |
| and html_prepare_conversion_units_targets. |
| */ |
| |
| int |
| html_id_is_registered (CONVERTER *self, const char *string) |
| { |
| return is_c_hashmap_registered (self->registered_ids_c_hashmap, string); |
| } |
| |
| void |
| html_register_id (CONVERTER *self, const char *string) |
| { |
| c_hashmap_register (self->registered_ids_c_hashmap, string, 0); |
| } |
| |
| /* used for diverse elements: tree units, indices, footnotes, special |
| elements, contents elements... */ |
| static HTML_TARGET * |
| add_element_target_to_list (HTML_TARGET_LIST *targets, |
| const ELEMENT *element, const char *target) |
| { |
| HTML_TARGET *element_target; |
| |
| if (targets->number == targets->space) |
| { |
| targets->list = realloc (targets->list, |
| sizeof (HTML_TARGET) * (targets->space += 5)); |
| } |
| element_target = &targets->list[targets->number]; |
| memset (element_target, 0, sizeof (HTML_TARGET)); |
| element_target->element = element; |
| if (target) |
| element_target->target = strdup (target); |
| |
| targets->number++; |
| return element_target; |
| } |
| |
| /* setup a list per command id. Note that elements associated to targets |
| without cmd are all associated to 0. This is the case for the special |
| units associated elements with type ET_special_unit_element and cmd 0 */ |
| static HTML_TARGET * |
| add_element_target (CONVERTER *self, const ELEMENT *element, |
| const char *target) |
| { |
| enum command_id cmd = element_builtin_cmd (element); |
| HTML_TARGET_LIST *targets = &self->html_targets[cmd]; |
| return add_element_target_to_list (targets, element, target); |
| } |
| |
| static HTML_TARGET * |
| add_special_target (CONVERTER *self, enum special_target_type type, |
| const ELEMENT *element, const char *target) |
| { |
| HTML_TARGET_LIST *targets = &self->html_special_targets[type]; |
| return add_element_target_to_list (targets, element, target); |
| } |
| |
| static char * |
| unique_target (CONVERTER *self, const char *target_base) |
| { |
| int nr = 1; |
| char *target = strdup (target_base); |
| while (1) |
| { |
| if (html_id_is_registered (self, target)) |
| { |
| free (target); |
| xasprintf (&target, "%s-%d", target_base, nr); |
| nr++; |
| if (nr == 0) |
| fatal ("overflow"); |
| } |
| else |
| return target; |
| } |
| } |
| |
| /* calls customization function requiring output units */ |
| static void |
| set_special_units_targets_files (CONVERTER *self, const char *document_name) |
| { |
| size_t i; |
| TEXT text_name; |
| const OUTPUT_UNIT_LIST *special_units = retrieve_output_units |
| (self->document, self->output_units_descriptors[OUDT_special_units]); |
| |
| const char *extension = ""; |
| if (self->conf->EXTENSION.o.string) |
| extension = self->conf->EXTENSION.o.string; |
| |
| text_init (&text_name); |
| |
| for (i = 0; i < special_units->number; i++) |
| { |
| TARGET_FILENAME *target_filename; |
| char *default_filename = 0; |
| char *filename = 0; |
| const OUTPUT_UNIT *special_unit = special_units->list[i]; |
| const char *special_unit_variety = special_unit->special_unit_variety; |
| |
| /* refers to self->special_unit_info */ |
| const char *target = html_special_unit_info (self, SUI_type_target, |
| special_unit_variety); |
| |
| if (!target) |
| continue; |
| |
| if (((self->conf->SPLIT.o.string && strlen (self->conf->SPLIT.o.string)) |
| || self->conf->MONOLITHIC.o.integer <= 0) |
| /* in general document_name not defined means called through convert */ |
| && document_name) |
| { |
| const char *special_unit_file_string |
| = html_special_unit_info (self, SUI_type_file_string, |
| special_unit_variety); |
| text_reset (&text_name); |
| if (!special_unit_file_string) |
| special_unit_file_string = ""; |
| text_append (&text_name, document_name); |
| text_append (&text_name, special_unit_file_string); |
| if (extension && strlen (extension)) |
| { |
| text_append (&text_name, "."); |
| text_append (&text_name, extension); |
| } |
| default_filename = strdup (text_name.text); |
| } |
| target_filename = call_file_id_setting_special_unit_target_file_name ( |
| self, special_unit, target, default_filename); |
| if (target_filename) |
| { |
| if (target_filename->target) |
| target = target_filename->target; |
| if (target_filename->filename) |
| { |
| filename = target_filename->filename; |
| free (default_filename); |
| } |
| else |
| filename = default_filename; |
| } |
| else |
| filename = default_filename; |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| const char *fileout = filename; |
| if (!fileout) |
| fileout = "UNDEF"; |
| fprintf (stderr, "C|Add special %s: target %s,\n filename %s\n", |
| special_unit_variety, target, fileout); |
| } |
| |
| HTML_TARGET *element_target |
| = add_element_target (self, special_unit->uc.special_unit_command, |
| target); |
| element_target->special_unit_filename = filename; |
| html_register_id (self, target); |
| |
| if (target_filename) |
| { |
| if (target_filename->target) |
| free (target_filename->target); |
| |
| free (target_filename); |
| } |
| } |
| free (text_name.text); |
| } |
| |
| /* calls customization function requiring output units */ |
| static void |
| prepare_associated_special_units_targets (CONVERTER *self) |
| { |
| const OUTPUT_UNIT_LIST *associated_special_units = retrieve_output_units |
| (self->document, |
| self->output_units_descriptors[OUDT_associated_special_units]); |
| |
| if (associated_special_units && associated_special_units->number > 0) |
| { |
| size_t i; |
| for (i = 0; i < associated_special_units->number; i++) |
| { |
| HTML_TARGET *element_target; |
| TARGET_FILENAME *target_filename; |
| char *filename = 0; |
| const OUTPUT_UNIT *special_unit = associated_special_units->list[i]; |
| const char *special_unit_variety = special_unit->special_unit_variety; |
| |
| /* it may be undef'ined in user customization code */ |
| const char *target = html_special_unit_info (self, SUI_type_target, |
| special_unit_variety); |
| target_filename |
| = call_file_id_setting_special_unit_target_file_name ( |
| self, special_unit, target, filename); |
| if (target_filename) |
| { |
| if (target_filename->target) |
| target = target_filename->target; |
| if (target_filename->filename) |
| filename = target_filename->filename; |
| } |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| const char *str_filename; |
| const char *str_target; |
| if (filename) |
| str_filename = filename; |
| else |
| str_filename = "UNDEF (default)"; |
| if (target) |
| str_target = target; |
| else |
| str_target = "UNDEF"; |
| fprintf (stderr, "C|Add content %s: target %s,\n" |
| " filename %s\n", special_unit_variety, |
| str_target, str_filename); |
| } |
| element_target |
| = add_element_target (self, special_unit->uc.special_unit_command, |
| target); |
| if (target) |
| html_register_id (self, target); |
| if (filename) |
| element_target->special_unit_filename = filename; |
| |
| if (target_filename) |
| { |
| if (target_filename->target) |
| free (target_filename->target); |
| |
| free (target_filename); |
| } |
| } |
| } |
| } |
| |
| /* calls customization function requiring elements */ |
| /* also used for xrefname, although there is no reason to use the associated |
| target, as for xrefname only the text is of interest */ |
| static void |
| new_sectioning_command_target (CONVERTER *self, const ELEMENT *command) |
| { |
| char *normalized_name; |
| char *filename; |
| char *target_base; |
| char *target; |
| char *target_contents = 0; |
| char *target_shortcontents = 0; |
| TARGET_CONTENTS_FILENAME *target_contents_filename; |
| |
| TARGET_FILENAME *target_filename |
| = normalized_sectioning_command_filename (self, command); |
| |
| /* should not be needed for a sectioning command, as it should not |
| be possible for that command to be a user-defined command, |
| but it is better to be consistent, and it may change in the future */ |
| enum command_id data_cmd = element_builtin_data_cmd (command); |
| unsigned long flags = builtin_command_data[data_cmd].flags; |
| |
| normalized_name = target_filename->target; |
| filename = target_filename->filename; |
| |
| free (target_filename); |
| |
| target_base = html_normalized_to_id (normalized_name); |
| |
| if (!strlen (target_base) && command->e.c->cmd == CM_top) |
| { |
| /* @top is allowed to be empty. In that case it gets this target name */ |
| free (target_base); |
| target_base = strdup ("SEC_Top"); |
| free (normalized_name); |
| normalized_name = strdup (target_base); |
| } |
| |
| if (strlen (target_base)) |
| target = unique_target (self, target_base); |
| else |
| target = strdup (""); |
| |
| free (target_base); |
| |
| if (strlen (target) |
| && (flags & CF_sectioning_heading)) |
| { |
| char *target_base_contents; |
| char *target_base_shortcontents; |
| xasprintf (&target_base_contents, "toc-%s", normalized_name); |
| target_contents = unique_target (self, target_base_contents); |
| free (target_base_contents); |
| |
| xasprintf (&target_base_shortcontents, "stoc-%s", normalized_name); |
| target_shortcontents = unique_target (self, target_base_shortcontents); |
| free (target_base_shortcontents); |
| } |
| |
| free (normalized_name); |
| |
| target_contents_filename |
| = call_file_id_setting_sectioning_command_target_name (self, command, |
| target, target_contents, target_shortcontents, filename); |
| if (target_contents_filename) |
| { |
| free (target); |
| target = target_contents_filename->target; |
| free (filename); |
| filename = target_contents_filename->filename; |
| free (target_contents); |
| target_contents = target_contents_filename->target_contents; |
| free (target_shortcontents); |
| target_shortcontents = target_contents_filename->target_shortcontents; |
| |
| free (target_contents_filename); |
| } |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| const char *command_name = element_command_name (command); |
| fprintf (stderr, "C|Register %s %s\n", command_name, target); |
| } |
| |
| HTML_TARGET *element_target |
| = add_element_target (self, command, target); |
| element_target->section_filename = filename; |
| html_register_id (self, target); |
| |
| free (target); |
| |
| if (target_contents) |
| { |
| element_target->contents_target = target_contents; |
| html_register_id (self, target_contents); |
| } |
| else |
| element_target->contents_target = strdup (""); |
| |
| if (target_shortcontents) |
| { |
| element_target->shortcontents_target = target_shortcontents; |
| html_register_id (self, target_shortcontents); |
| } |
| else |
| element_target->shortcontents_target = strdup (""); |
| } |
| |
| /* calls customization function requiring elements */ |
| /* |
| This set with two different codes |
| * the target information, id and normalized filename of 'identifiers_target', |
| ie everything that may be the target of a ref, @node, @float label, |
| @anchor. |
| * The target information of sectioning elements |
| @node and section commands targets are therefore both set. |
| |
| conversion to HTML is done on-demand, upon call to command_text |
| and similar functions. |
| Note that 'node_filename', which is set here for Top target information |
| too, is not used later for Top anchors or links, see the NOTE below |
| associated with setting TOP_NODE_FILE_TARGET. |
| */ |
| static void |
| set_root_commands_targets_node_files (CONVERTER *self) |
| { |
| |
| if (identifiers_target_number (&self->document->identifiers_target)) |
| { |
| const char *extension = 0; |
| |
| if (self->conf->EXTENSION.o.string) |
| extension = self->conf->EXTENSION.o.string; |
| /* 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; |
| for (i = 0; i < label_targets->number; i++) |
| { |
| int called = 0; |
| char *target; |
| char *node_filename; |
| char *user_node_filename; |
| const ELEMENT *label_element; |
| const ELEMENT *target_element; |
| const LABEL *label = &label_targets->list[i]; |
| |
| if (!label->identifier || label->reference) |
| continue; |
| |
| target_element = label->element; |
| label_element = get_label_element (target_element); |
| |
| TARGET_FILENAME *target_filename = |
| html_normalized_label_id_file (self, label->identifier, |
| label_element); |
| target = target_filename->target; |
| if (extension) |
| xasprintf (&node_filename, "%s.%s", target_filename->filename, |
| extension); |
| else |
| node_filename = strdup (target_filename->filename); |
| |
| free (target_filename->filename); |
| free (target_filename); |
| |
| /* a non defined filename is ok if called with convert, but not |
| if output in files. We reset if undef, silently unless verbose |
| in case called by convert. */ |
| |
| user_node_filename = call_file_id_setting_node_file_name (self, |
| target_element, node_filename, |
| &called); |
| if (user_node_filename) |
| { |
| free (node_filename); |
| node_filename = user_node_filename; |
| } |
| else if (called) |
| { |
| if (self->conf->VERBOSE.o.integer > 0) |
| { |
| message_list_document_warn (&self->error_messages, self->conf, |
| 0, "user-defined node file name not set for `%s'", |
| node_filename); |
| } |
| else if (self->conf->DEBUG.o.integer > 0) |
| { |
| fprintf (stderr, |
| "C|user-defined node file name undef for `%s'\n", |
| node_filename); |
| } |
| } |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| const char *command_name = element_command_name (target_element); |
| fprintf (stderr, "C|Label @%s %s, %s\n", command_name, target, |
| node_filename); |
| } |
| |
| HTML_TARGET *element_target |
| = add_element_target (self, target_element, target); |
| element_target->node_filename = node_filename; |
| html_register_id (self, target); |
| |
| free (target); |
| } |
| } |
| |
| 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 *section_element = section_relations->element; |
| new_sectioning_command_target (self, section_element); |
| } |
| } |
| } |
| |
| static void |
| prepare_index_entries_targets (CONVERTER *self) |
| { |
| if (self->document->indices_info.number > 0) |
| { |
| size_t i; |
| self->shared_conversion_state.formatted_index_entries |
| = (int **) malloc (self->sorted_index_names.number * sizeof (int *)); |
| |
| for (i = 0; i < self->sorted_index_names.number; i++) |
| { |
| size_t j; |
| const INDEX *idx = self->sorted_index_names.list[i]; |
| /* no need to test for idx->entries_number > 0 as indices without |
| entries are not kept in sorted_index_names. */ |
| self->shared_conversion_state.formatted_index_entries[i] |
| = (int *) malloc (idx->entries_number * sizeof (int)); |
| memset (self->shared_conversion_state.formatted_index_entries[i], |
| 0, idx->entries_number * sizeof (int)); |
| for (j = 0; j < idx->entries_number; j++) |
| { |
| const INDEX_ENTRY *index_entry; |
| const ELEMENT *main_entry_element; |
| const ELEMENT *seeentry; |
| const ELEMENT *seealso; |
| /* not modified, but cannot be explicit const as it is inserted |
| in an array of non-const elements */ |
| ELEMENT *entry_reference_content_element; |
| ELEMENT *normalize_index_element; |
| ELEMENT_LIST *subentries_tree; |
| const ELEMENT *target_element; |
| TEXT target_base; |
| char *normalized_index; |
| const char *region; |
| char *target; |
| |
| index_entry = &idx->index_entries[j]; |
| main_entry_element = index_entry->entry_element; |
| 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; |
| |
| region = lookup_extra_string (main_entry_element, |
| AI_key_element_region); |
| entry_reference_content_element |
| = converter_index_content_element (main_entry_element, self, 1); |
| /* construct element to convert to a normalized identifier to use as |
| hrefs target */ |
| normalize_index_element = new_element (ET_NONE); |
| add_to_contents_as_array (normalize_index_element, |
| entry_reference_content_element); |
| |
| subentries_tree |
| = comma_index_subentries_tree (main_entry_element, " "); |
| if (subentries_tree) |
| { |
| insert_list_slice_into_contents (normalize_index_element, |
| normalize_index_element->e.c->contents.number, |
| subentries_tree, 0, |
| subentries_tree->number); |
| } |
| normalized_index |
| = convert_to_identifier (normalize_index_element); |
| |
| destroy_element (normalize_index_element); |
| if (subentries_tree) |
| free_comma_index_subentries_tree (subentries_tree); |
| |
| destroy_element_and_children (entry_reference_content_element); |
| |
| text_init (&target_base); |
| text_append (&target_base, "index-"); |
| if (region) |
| { |
| text_append (&target_base, region); |
| text_append (&target_base, "-"); |
| } |
| text_append (&target_base, normalized_index); |
| free (normalized_index); |
| target = unique_target (self, target_base.text); |
| free (target_base.text); |
| if (index_entry->entry_associated_element) |
| target_element = index_entry->entry_associated_element; |
| else |
| target_element = main_entry_element; |
| |
| add_element_target (self, target_element, target); |
| html_register_id (self, target); |
| |
| free (target); |
| } |
| } |
| } |
| } |
| |
| 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); |
| } |
| |
| static const char *footid_base = "FOOT"; |
| static const char *docid_base = "DOCF"; |
| |
| static void |
| prepare_footnotes_targets (CONVERTER *self) |
| { |
| const ELEMENT_LIST *global_footnotes |
| = &self->document->global_commands.footnotes; |
| |
| self->shared_conversion_state.footnote_number = 0; |
| |
| if (global_footnotes->number > 0) |
| { |
| size_t i; |
| self->shared_conversion_state.footnote_id_numbers |
| = (FOOTNOTE_ID_NUMBER *) malloc (global_footnotes->number * |
| sizeof (FOOTNOTE_ID_NUMBER)); |
| for (i = 0; i < global_footnotes->number; i++) |
| { |
| const HTML_TARGET *element_target; |
| const ELEMENT *footnote = global_footnotes->list[i]; |
| TEXT footid; |
| TEXT docid; |
| int nr = i+1; |
| |
| text_init (&footid); |
| text_init (&docid); |
| text_printf (&footid, "%s%d", footid_base, nr); |
| text_printf (&docid, "%s%d", docid_base, nr); |
| |
| while (1) |
| { |
| if (html_id_is_registered (self, footid.text) |
| || html_id_is_registered (self, docid.text)) |
| { |
| nr++; |
| if (nr == 0) |
| fatal ("overflow footnote target nr"); |
| |
| text_init (&footid); |
| text_init (&docid); |
| text_printf (&footid, "%s%d", footid_base, nr); |
| text_printf (&docid, "%s%d", docid_base, nr); |
| } |
| else |
| break; |
| } |
| html_register_id (self, footid.text); |
| html_register_id (self, docid.text); |
| |
| element_target = add_element_target (self, footnote, footid.text); |
| add_special_target (self, ST_footnote_location, footnote, |
| docid.text); |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| char *footnote_txi = convert_to_texinfo (footnote); |
| fprintf (stderr, "C|Enter footnote: target %s, nr %d\n%s\n", |
| footid.text, nr, footnote_txi); |
| free (footnote_txi); |
| } |
| self->shared_conversion_state.footnote_id_numbers[i].footnote_id |
| = element_target->target; |
| self->shared_conversion_state.footnote_id_numbers[i].number = 0; |
| free (footid.text); |
| free (docid.text); |
| } |
| qsort (self->shared_conversion_state.footnote_id_numbers, |
| global_footnotes->number, |
| sizeof (FOOTNOTE_ID_NUMBER), compare_footnote_id); |
| } |
| } |
| |
| /* keep the heading command names sorted alphabetically followed by |
| xrefname to match order in perl */ |
| static const enum command_id heading_commands_list[] = { |
| CM_chapheading, CM_heading, CM_majorheading, CM_subheading, |
| CM_subsubheading, CM_xrefname, |
| 0, |
| }; |
| |
| /* indirectly calls customization function requiring elements */ |
| static void |
| set_heading_commands_targets (CONVERTER *self) |
| { |
| int i; |
| for (i = 0; heading_commands_list[i]; i++) |
| { |
| enum command_id cmd = heading_commands_list[i]; |
| const ELEMENT_LIST *global_command |
| = get_cmd_global_multi_command (&self->document->global_commands, cmd); |
| |
| if (global_command->number > 0) |
| { |
| size_t j; |
| for (j = 0; j < global_command->number; j++) |
| { |
| const ELEMENT *command = global_command->list[j]; |
| new_sectioning_command_target (self, command); |
| } |
| } |
| } |
| } |
| |
| /* duplicate in format_html.c */ |
| 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); |
| } |
| |
| /* For debug/check/optimization |
| used to check to what extent the targets are already ordered. |
| Return the number of elements ordered ok with respect to the |
| previous element |
| */ |
| size_t |
| check_targets_order (enum command_id cmd, HTML_TARGET_LIST *element_targets) |
| { |
| size_t i; |
| size_t result = 0; |
| if (element_targets->number <= 1) |
| return result; |
| for (i = 1; i < element_targets->number; i++) |
| { |
| if (compare_element_target (&element_targets->list[i-1], |
| &element_targets->list[i]) > 0) |
| { |
| fprintf (stderr, "no %s %zu %"PRIuPTR" %p %s %zu %"PRIuPTR" %p %s\n", |
| builtin_command_name (cmd), i-1, |
| (uintptr_t)element_targets->list[i-1].element, |
| element_targets->list[i-1].element, element_targets->list[i-1].target, |
| i, (uintptr_t)element_targets->list[i].element, |
| element_targets->list[i].element, element_targets->list[i].target); |
| } |
| else |
| result++; |
| } |
| return result; |
| } |
| |
| /* It may not be efficient to sort and find back with bsearch if there is |
| a small number of elements. However, some target elements are more |
| likely to already be ordered when they are accessed in their order of |
| appearance in the document. There is no guarantee, as it is only in the |
| same array that adresses are guaranteed to be increasing. A check done |
| in 2024 with gcc, using check_targets_order, and also looking at the |
| address of newly allocated elements shows that elements are |
| not that much allocated in order. However, overall, the addresses are |
| more in order when elements are accessed in the document order. |
| For indices, it is not really possible to get them in document order, |
| within an index they are in document order, but not across indices. |
| The other data are in document order, for nodes and similar because |
| the labels list is used instead of identifiers_target on purpose. |
| */ |
| static void |
| sort_cmd_targets (CONVERTER *self) |
| { |
| enum command_id cmd; |
| int type; |
| |
| /* holds the commands with targets that have been allocated, not only |
| those with actual targets. There is a difference only if the |
| converter is reused */ |
| self->html_target_cmds.top = 0; |
| |
| for (cmd = 0; cmd < BUILTIN_CMD_NUMBER; cmd++) |
| { |
| if (self->html_targets[cmd].number > 0) |
| { |
| HTML_TARGET_LIST *element_targets = &self->html_targets[cmd]; |
| /* to check the order |
| size_t ordered_items = check_targets_order (cmd, element_targets); |
| fprintf (stderr, "ORDER %s %zu / %zu\n", builtin_command_name (cmd), |
| ordered_items, element_targets->number -1); |
| */ |
| qsort (element_targets->list, |
| element_targets->number, |
| sizeof (HTML_TARGET), compare_element_target); |
| push_command (&self->html_target_cmds, cmd); |
| } |
| else if (self->html_targets[cmd].space > 0) |
| push_command (&self->html_target_cmds, cmd); |
| } |
| for (type = 0; type < ST_footnote_location+1; type++) |
| { |
| if (self->html_special_targets[type].number > 0) |
| { |
| HTML_TARGET_LIST *element_targets = &self->html_special_targets[type]; |
| qsort (element_targets->list, |
| element_targets->number, |
| sizeof (HTML_TARGET), compare_element_target); |
| } |
| } |
| } |
| |
| /* return the approximate number of targets for that manual */ |
| static size_t |
| ids_hashmap_predicted_values (CONVERTER *self) |
| { |
| size_t sectioning_commands_nr; |
| size_t index_entries_nr = 0; |
| size_t heading_commands_nr = 0; |
| size_t i; |
| |
| OUTPUT_UNIT_LIST *special_units = retrieve_output_units |
| (self->document, self->output_units_descriptors[OUDT_special_units]); |
| OUTPUT_UNIT_LIST *associated_special_units = retrieve_output_units |
| (self->document, |
| self->output_units_descriptors[OUDT_associated_special_units]); |
| |
| sectioning_commands_nr = self->document->sections_list.number; |
| |
| if (self->document->indices_info.number > 0) |
| { |
| size_t i; |
| for (i = 0; i < self->sorted_index_names.number; i++) |
| index_entries_nr += self->sorted_index_names.list[i]->entries_number; |
| } |
| |
| for (i = 0; heading_commands_list[i]; i++) |
| { |
| enum command_id cmd = heading_commands_list[i]; |
| const ELEMENT_LIST *global_command |
| = get_cmd_global_multi_command (&self->document->global_commands, cmd); |
| heading_commands_nr += global_command->number; |
| } |
| |
| return special_units->number + associated_special_units->number |
| + identifiers_target_number (&self->document->identifiers_target) |
| + 3 * sectioning_commands_nr |
| + heading_commands_nr |
| + index_entries_nr |
| + self->document->global_commands.footnotes.number * 2; |
| } |
| |
| /* indirectly calls all the functions calling customization function |
| requiring elements and output units except for external nodes formatting */ |
| /* for conversion units except for associated special units that require |
| files for document units to be set */ |
| /* does not modify the structures the labels are prepared for (output units, |
| command elements, index entries...) */ |
| void |
| html_prepare_conversion_units_targets (CONVERTER *self, |
| const char *document_name) |
| { |
| size_t predicted_values = ids_hashmap_predicted_values (self); |
| |
| /* Not done as in Perl in conversion initialization, to be able to |
| have correctly predicted number of id */ |
| if (self->registered_ids_c_hashmap) |
| { |
| clear_c_hashmap (self->registered_ids_c_hashmap); |
| init_c_hashmap (self->registered_ids_c_hashmap, predicted_values); |
| } |
| else |
| self->registered_ids_c_hashmap = new_c_hashmap (predicted_values); |
| |
| /* |
| Do that before the other elements, to be sure that special page ids |
| are registered before elements id are. |
| */ |
| set_special_units_targets_files (self, document_name); |
| |
| prepare_associated_special_units_targets (self); |
| |
| set_root_commands_targets_node_files (self); |
| |
| prepare_index_entries_targets (self); |
| prepare_footnotes_targets (self); |
| |
| set_heading_commands_targets (self); |
| |
| sort_cmd_targets (self); |
| } |
| |
| |
| |
| /* For output() prepare pages and associate them to files, setup unit |
| direction and file counters. In that case |
| html_prepare_output_units_global_targets is called in |
| html_prepare_units_directions_files. |
| |
| For convert() html_prepare_output_units_global_targets only is called. |
| */ |
| |
| /* Associate output units to the global targets, First, Last, Top, Index. |
| and special output units */ |
| void |
| html_prepare_output_units_global_targets (CONVERTER *self) |
| { |
| int i; |
| int all_special_units_nr = 0; |
| int s; |
| const SECTION_RELATIONS_LIST *sections_list = &self->document->sections_list; |
| |
| const OUTPUT_UNIT_LIST *output_units = retrieve_output_units |
| (self->document, self->output_units_descriptors[OUDT_units]); |
| |
| const OUTPUT_UNIT *top_output_unit = html_get_top_unit (self->document, |
| output_units); |
| |
| size_t special_output_units_lists[2] = { |
| self->output_units_descriptors[OUDT_special_units], |
| self->output_units_descriptors[OUDT_associated_special_units]}; |
| |
| self->global_units_directions[D_First] = output_units->list[0]; |
| self->global_units_directions[D_Last] |
| = output_units->list[output_units->number - 1]; |
| |
| self->global_units_directions[D_Top] = top_output_unit; |
| |
| /* |
| Associate Index with the last @printindex. According to Werner Lemberg, |
| "the most general index is normally the last one, not the first" |
| https://lists.gnu.org/archive/html/bug-texinfo/2025-01/msg00019.html |
| */ |
| /* It is always the last printindex, even if it is not output (for example |
| it is in @copying and @titlepage, which are certainly wrong constructs). |
| */ |
| if (self->document->global_commands.printindex.number > 0) |
| { |
| const ELEMENT *printindex |
| = self->document->global_commands.printindex.list[ |
| self->document->global_commands.printindex.number -1]; |
| ROOT_AND_UNIT *root_unit |
| = html_get_tree_root_element (self, printindex, 0); |
| if (root_unit->output_unit) |
| { |
| const OUTPUT_UNIT *document_unit = root_unit->output_unit; |
| const ELEMENT *root_command = root_unit->root; |
| if (root_command && root_command->e.c->cmd != CM_NONE) |
| { |
| const SECTION_RELATIONS *section_relations = 0; |
| int status; |
| if (root_command->e.c->cmd == CM_node) |
| { |
| 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) |
| section_relations = node_relations->associated_section; |
| } |
| else |
| { |
| size_t section_number |
| = lookup_extra_integer (root_command, |
| AI_key_section_number, &status); |
| section_relations |
| = sections_list->list[section_number -1]; |
| } |
| /* find the first level 1 sectioning element to associate the printindex |
| with */ |
| if (section_relations) |
| { |
| int status; |
| while (1) |
| { |
| int section_level |
| = lookup_extra_integer (section_relations->element, |
| AI_key_section_level, &status); |
| if (!status && section_level <= 1) |
| break; |
| |
| const SECTION_RELATIONS * const *up_section_directions |
| = section_relations->section_directions; |
| if (up_section_directions |
| && up_section_directions[D_up] |
| && up_section_directions[D_up] |
| ->element->e.c->associated_unit) |
| { |
| section_relations = up_section_directions[D_up]; |
| document_unit |
| = section_relations->element->e.c->associated_unit; |
| } |
| else |
| break; |
| } |
| } |
| } |
| self->global_units_directions[D_Index] = document_unit; |
| } |
| free (root_unit); |
| } |
| |
| if (self->customized_global_units_directions.number > 0) |
| { |
| size_t l; |
| for (l = 0; l < self->customized_global_units_directions.number; l++) |
| { |
| const ELEMENT *node_element = 0; |
| const DIRECTION_NODE_NAME *direction_node_name |
| = &self->customized_global_units_directions.list[l]; |
| size_t global_directions_idx; |
| DOCUMENT *label_document; |
| ELEMENT *label_tree; |
| |
| if (direction_node_name->direction_nr == 0) |
| { |
| /* |
| fprintf (stderr, "CUSTOM DIRECTION: Not setting %zu %s %s\n", l, |
| direction_node_name->direction, |
| direction_node_name->node_name); |
| */ |
| continue; |
| } |
| else if (direction_node_name->direction_nr |
| <= DEFAULT_GLOBAL_DIRECTION_LAST_IDX+1) |
| /* replace a default global direction */ |
| global_directions_idx = direction_node_name->direction_nr -1; |
| else if (direction_node_name->direction_nr |
| > NON_SPECIAL_DIRECTIONS_NR) |
| { |
| /* additional global direction, with/after special units */ |
| global_directions_idx = DEFAULT_GLOBAL_DIRECTION_LAST_IDX |
| + (direction_node_name->direction_nr - NON_SPECIAL_DIRECTIONS_NR); |
| } |
| else |
| { |
| /* Should not be possible, as direction_node_name->direction_nr |
| should have been left to 0 above or in |
| html_set_global_direction */ |
| char *message; |
| xasprintf (&message, |
| "Replacing relative direction %zu: %zu %s (%s `%s')", |
| l, direction_node_name->direction_nr, |
| self->main_units_direction_names[ |
| direction_node_name->direction_nr -1], |
| direction_node_name->direction, |
| direction_node_name->node_name); |
| fatal (message); |
| free (message); |
| } |
| |
| /* Determine the document unit corresponding to the direction |
| node name Texinfo code */ |
| |
| /* Parse the customized direction node name Texinfo code */ |
| /* same as creating a new parser in Perl */ |
| reset_parser_conf (); |
| parser_conf_set_NO_INDEX (1); |
| parser_conf_set_NO_USER_COMMANDS (1); |
| |
| label_document |
| = parse_string (direction_node_name->node_name, 1); |
| if (label_document->parser_error_messages.number > 0) |
| { |
| const ERROR_MESSAGE_LIST *error_messages |
| = &label_document->parser_error_messages; |
| size_t j; |
| fprintf (stderr, "Global %s node name parsing %zu error(s)\n", |
| direction_node_name->direction, |
| error_messages->number); |
| fprintf (stderr, "node name: %s\n", |
| direction_node_name->node_name); |
| fprintf (stderr, "Error messages: \n"); |
| for (j = 0; j < error_messages->number; j++) |
| fprintf (stderr, "%s", error_messages->list[j].error_line); |
| } |
| wipe_document_parser_errors (label_document); |
| label_tree = unregister_document_merge_with_document ( |
| label_document, self->document); |
| |
| /* convert to identifier and determine the node element target */ |
| if (label_tree) |
| { |
| char *normalized_node = convert_to_node_identifier (label_tree); |
| if (normalized_node) |
| { |
| const char *non_hyphen_char = normalized_node |
| + strspn (normalized_node, "-"); |
| if (*non_hyphen_char) |
| { |
| node_element = find_identifier_target ( |
| &self->document->identifiers_target, |
| normalized_node); |
| } |
| free (normalized_node); |
| } |
| destroy_element_and_children (label_tree); |
| } |
| if (!node_element) |
| { |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, |
| "could not find %s node `%s'", |
| direction_node_name->direction, |
| direction_node_name->node_name); |
| } |
| else |
| self->global_units_directions[global_directions_idx] |
| = node_element->e.c->associated_unit; |
| } |
| } |
| |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| int i; |
| fprintf (stderr, "C|GLOBAL DIRECTIONS:\n"); |
| for (i = 0; i < DEFAULT_GLOBAL_DIRECTION_LAST_IDX+1; i++) |
| { |
| if (self->global_units_directions[i]) |
| { |
| const OUTPUT_UNIT *global_unit = self->global_units_directions[i]; |
| char *unit_texi = output_unit_texi (global_unit); |
| fprintf (stderr, " %s: %s\n", html_global_unit_direction_names[i], |
| unit_texi); |
| free (unit_texi); |
| } |
| } |
| fprintf (stderr, "C|TEXT DIRECTIONS:\n"); |
| for (i = DEFAULT_GLOBAL_DIRECTION_LAST_IDX+1; |
| i < DEFAULT_TEXT_DIRECTION_LAST_IDX+1; i++) |
| { |
| const char *name = html_direction_string (self, i, TDS_type_button, |
| TDS_context_normal); |
| if (name) |
| fprintf (stderr, " %s {%s}\n", html_global_unit_direction_names[i], |
| name); |
| } |
| fprintf (stderr, "\n"); |
| } |
| |
| /* In addition to setting global_units_directions as in Perl, |
| determine total number of special output units and fill |
| special_units_direction_names. Used to setup the complete |
| list of global output units based on a direction name. |
| */ |
| for (i = 0; i < 2; i++) |
| { |
| size_t special_units_descriptor = special_output_units_lists[i]; |
| const OUTPUT_UNIT_LIST *units_list |
| = retrieve_output_units (self->document, special_units_descriptor); |
| if (units_list && units_list->number) |
| all_special_units_nr += units_list->number; |
| } |
| |
| self->special_units_direction_names = (SPECIAL_UNIT_DIRECTION *) |
| realloc (self->special_units_direction_names, |
| sizeof (SPECIAL_UNIT_DIRECTION) * (all_special_units_nr+1)); |
| memset (self->special_units_direction_names, 0, |
| sizeof (SPECIAL_UNIT_DIRECTION) * (all_special_units_nr+1)); |
| |
| s = 0; |
| for (i = 0; i < 2; i++) |
| { |
| size_t special_units_descriptor = special_output_units_lists[i]; |
| const OUTPUT_UNIT_LIST *units_list |
| = retrieve_output_units (self->document, special_units_descriptor); |
| if (units_list && units_list->number) |
| { |
| size_t j; |
| for (j = 0; j < units_list->number; j++) |
| { |
| const OUTPUT_UNIT *special_unit = units_list->list[j]; |
| const char *special_unit_variety |
| = special_unit->special_unit_variety; |
| int special_unit_direction_index |
| = html_special_unit_variety_direction_index (self, |
| special_unit_variety); |
| self->global_units_directions[special_unit_direction_index] |
| = special_unit; |
| |
| self->special_units_direction_names[s].output_unit = special_unit; |
| self->special_units_direction_names[s].direction |
| = html_special_unit_info (self, SUI_type_direction, |
| special_unit_variety); |
| s++; |
| } |
| } |
| } |
| } |
| |
| static int |
| compare_global_units_direction_name (const void *a, const void *b) |
| { |
| const SPECIAL_UNIT_DIRECTION *gudn_a = (const SPECIAL_UNIT_DIRECTION *) a; |
| const SPECIAL_UNIT_DIRECTION *gudn_b = (const SPECIAL_UNIT_DIRECTION *) b; |
| |
| return strcmp (gudn_a->direction, gudn_b->direction); |
| } |
| |
| /* To find more easily a global output unit based on a direction name, for an |
| XS interface, associate global output units to names and sort according |
| to names. Only called from Perl/XS, not needed when doing C only */ |
| void |
| html_setup_global_units_direction_names (CONVERTER *self) |
| { |
| SPECIAL_UNIT_DIRECTION *global_units_directions_list; |
| int i; |
| int global_directions_nr = 0; |
| int global_units_direction_idx = 0; |
| const SPECIAL_UNIT_DIRECTION *special_units_direction_names |
| = self->special_units_direction_names; |
| |
| for (i = 0; i < DEFAULT_GLOBAL_DIRECTION_LAST_IDX+1; i++) |
| if (self->global_units_directions[i]) |
| global_directions_nr++; |
| |
| for (i = 0; special_units_direction_names[i].output_unit; i++) |
| global_directions_nr++; |
| |
| if (self->customized_global_units_directions.number > 0) |
| { |
| size_t l; |
| for (l = 0; l < self->customized_global_units_directions.number; l++) |
| { |
| DIRECTION_NODE_NAME *direction_node_name |
| = &self->customized_global_units_directions.list[l]; |
| if (direction_node_name->direction_nr > NON_SPECIAL_DIRECTIONS_NR) |
| global_directions_nr++; |
| } |
| } |
| |
| global_units_directions_list = (SPECIAL_UNIT_DIRECTION *) |
| malloc (sizeof (SPECIAL_UNIT_DIRECTION) * (global_directions_nr)); |
| |
| for (i = 0; i < DEFAULT_GLOBAL_DIRECTION_LAST_IDX+1; i++) |
| { |
| if (self->global_units_directions[i]) |
| { |
| global_units_directions_list[global_units_direction_idx].direction |
| = html_global_unit_direction_names[i]; |
| global_units_directions_list[global_units_direction_idx].output_unit |
| = self->global_units_directions[i]; |
| global_units_direction_idx++; |
| } |
| } |
| |
| for (i = 0; special_units_direction_names[i].output_unit; i++) |
| { |
| global_units_directions_list[global_units_direction_idx].direction |
| = special_units_direction_names[i].direction; |
| global_units_directions_list[global_units_direction_idx].output_unit |
| = special_units_direction_names[i].output_unit; |
| global_units_direction_idx++; |
| } |
| |
| if (self->customized_global_units_directions.number > 0) |
| { |
| size_t l; |
| for (l = 0; l < self->customized_global_units_directions.number; l++) |
| { |
| DIRECTION_NODE_NAME *direction_node_name |
| = &self->customized_global_units_directions.list[l]; |
| if (direction_node_name->direction_nr > NON_SPECIAL_DIRECTIONS_NR) |
| { |
| size_t global_directions_idx = DEFAULT_GLOBAL_DIRECTION_LAST_IDX |
| + (direction_node_name->direction_nr - NON_SPECIAL_DIRECTIONS_NR); |
| global_units_directions_list[global_units_direction_idx].direction |
| = direction_node_name->direction; |
| global_units_directions_list[global_units_direction_idx].output_unit |
| = self->global_units_directions[global_directions_idx]; |
| global_units_direction_idx++; |
| } |
| } |
| } |
| |
| qsort (global_units_directions_list, |
| global_directions_nr, |
| sizeof (SPECIAL_UNIT_DIRECTION), compare_global_units_direction_name); |
| |
| self->global_units_direction_names.list = global_units_directions_list; |
| self->global_units_direction_names.number = global_directions_nr; |
| } |
| |
| /* To find more easily a global text direction index based on a direction |
| name, for an XS interface, associate global direction texts to names |
| and sort according to names. Only called from Perl/XS, not needed when |
| doing C only */ |
| void |
| html_setup_global_texts_direction_names (CONVERTER *self) |
| { |
| size_t i; |
| |
| for (i = DEFAULT_GLOBAL_DIRECTION_LAST_IDX+1; |
| i < DEFAULT_TEXT_DIRECTION_LAST_IDX+1; i++) |
| { |
| add_string (html_global_unit_direction_names[i], |
| &self->global_texts_direction_names); |
| } |
| |
| for (i = 0; i < self->customized_global_text_directions.number; i++) |
| { |
| add_string (self->customized_global_text_directions.list[i], |
| &self->global_texts_direction_names); |
| } |
| |
| sort_strings_list (&self->global_texts_direction_names); |
| } |
| |
| void |
| html_reset_files_source_info (FILE_SOURCE_INFO_LIST *files_source_info) |
| { |
| size_t i; |
| for (i = 0; i < files_source_info->number; i++) |
| { |
| free (files_source_info->list[i].filename); |
| free (files_source_info->list[i].path); |
| } |
| files_source_info->number = 0; |
| } |
| |
| static char * |
| add_to_unit_file_name_paths (char **unit_file_name_paths, |
| const char *filename, |
| const OUTPUT_UNIT *output_unit) |
| { |
| unit_file_name_paths[output_unit->index] = strdup (filename); |
| |
| return unit_file_name_paths[output_unit->index]; |
| } |
| |
| static void |
| html_initialize_pending_closes (CONVERTER *self, size_t number) |
| { |
| if (self->pending_closes.space < number) |
| { |
| self->pending_closes.list = (STRING_STACK *) |
| realloc (self->pending_closes.list, number * sizeof (STRING_STACK)); |
| /* The existing string stacks per file should already be empty, either because |
| the code is consistent for opening and closing, or because they are |
| emptied after the conversion (with an error message). |
| |
| Therefore, only the newly allocated string stacks per file are |
| initialized. |
| */ |
| memset (&self->pending_closes.list[self->pending_closes.space], |
| 0, (number - self->pending_closes.space) |
| * sizeof (STRING_STACK)); |
| self->pending_closes.space = number; |
| } |
| self->pending_closes.number = number; |
| } |
| |
| static void |
| prepare_page_css (CONVERTER *self, size_t pages_number) |
| { |
| /* 0 is for document_global_context_css, the remaining indices |
| for the output unit files or the unique page with convert */ |
| size_t css_pages_number = pages_number +1; |
| |
| if (self->page_css.space < css_pages_number) |
| { |
| self->page_css.space = css_pages_number; |
| self->page_css.list = (CSS_LIST *) |
| realloc (self->page_css.list, |
| self->page_css.space * sizeof (CSS_LIST)); |
| } |
| |
| memset (self->page_css.list, 0, |
| css_pages_number * sizeof (CSS_LIST)); |
| |
| self->page_css.number = css_pages_number; |
| } |
| |
| /* setup a page (+global context) in case there are no files, ie called |
| with convert or output with an empty string as filename. */ |
| void |
| html_setup_output_simple_page (CONVERTER *self, const char *output_filename) |
| { |
| NAME_NUMBER *page_name_number; |
| |
| free (self->output_unit_file_indices); |
| self->output_unit_file_indices = 0; |
| |
| free (self->special_unit_file_indices); |
| self->special_unit_file_indices = 0; |
| |
| prepare_page_css (self, 1); |
| |
| self->html_files_information.number = 1+1; |
| self->html_files_information.list = (FILE_ASSOCIATED_INFO *) |
| malloc (self->html_files_information.number |
| * sizeof (FILE_ASSOCIATED_INFO)); |
| memset (self->html_files_information.list, 0, |
| self->html_files_information.number * sizeof (FILE_ASSOCIATED_INFO)); |
| |
| html_initialize_pending_closes (self, 1+1); |
| |
| allocate_name_number_list (&self->page_name_number, 1); |
| |
| page_name_number = &self->page_name_number.list[0]; |
| page_name_number->number = 1; |
| page_name_number->name = output_filename; |
| } |
| |
| /* calls customization function requiring output units */ |
| static FILE_SOURCE_INFO_LIST * |
| html_set_pages_files (CONVERTER *self, const OUTPUT_UNIT_LIST *output_units, |
| const OUTPUT_UNIT_LIST *special_units, |
| const OUTPUT_UNIT_LIST *associated_special_units, |
| const char *output_file, |
| const char *destination_directory, const char *output_filename, |
| const char *document_name) |
| { |
| FILE_SOURCE_INFO_LIST *files_source_info; |
| char **unit_file_name_paths; |
| size_t i; |
| |
| files_source_info = &self->files_source_info; |
| |
| unit_file_name_paths = (char **) |
| malloc (output_units->number * sizeof (char *)); |
| memset (unit_file_name_paths, 0, |
| output_units->number * sizeof (char *)); |
| |
| if (!self->conf->SPLIT.o.string || !strlen (self->conf->SPLIT.o.string)) |
| { |
| size_t i; |
| html_add_to_files_source_info (files_source_info, output_filename, |
| "special_file", "non_split", 0, |
| output_file); |
| for (i = 0; i < output_units->number; i++) |
| { |
| add_to_unit_file_name_paths (unit_file_name_paths, |
| output_filename, |
| output_units->list[i]); |
| } |
| } |
| else |
| { |
| char *top_node_filename_str; |
| const OUTPUT_UNIT *node_top_output_unit = 0; |
| const char *extension = 0; |
| const ELEMENT *node_top = 0; |
| size_t file_nr = 0; |
| size_t i; |
| |
| /* first determine the top node file name. */ |
| if (identifiers_target_number (&self->document->identifiers_target)) |
| node_top = find_identifier_target (&self->document->identifiers_target, |
| "Top"); |
| |
| top_node_filename_str = top_node_filename (self, document_name); |
| |
| if (node_top && top_node_filename_str) |
| { |
| size_t i; |
| node_top_output_unit = node_top->e.c->associated_unit; |
| for (i = 0; i < output_units->number; i++) |
| if (output_units->list[i] == node_top_output_unit) |
| break; |
| html_add_to_files_source_info (files_source_info, |
| top_node_filename_str, |
| "special_file", "Top", 0, 0); |
| add_to_unit_file_name_paths (unit_file_name_paths, |
| top_node_filename_str, |
| node_top_output_unit); |
| } |
| |
| if (self->conf->EXTENSION.o.string |
| && strlen (self->conf->EXTENSION.o.string)) |
| extension = self->conf->EXTENSION.o.string; |
| |
| for (i = 0; i < output_units->number; i++) |
| { |
| const OUTPUT_UNIT *output_unit = output_units->list[i]; |
| const OUTPUT_UNIT *file_output_unit; |
| const char *output_unit_file_name; |
| /* For Top node. */ |
| if (node_top_output_unit && node_top_output_unit == output_unit) |
| continue; |
| |
| file_output_unit = output_unit->first_in_page; |
| output_unit_file_name |
| = unit_file_name_paths[file_output_unit->index]; |
| if (!output_unit_file_name) |
| { |
| const char *node_filename = 0; |
| size_t j; |
| for (j = 0; j < file_output_unit->unit_contents.number; j++) |
| { |
| const ELEMENT *root_command |
| = file_output_unit->unit_contents.list[j]; |
| if (root_command->e.c->cmd == CM_node) |
| { |
| const ELEMENT *node_target = 0; |
| const char *normalized = lookup_extra_string (root_command, |
| AI_key_identifier); |
| if (normalized) |
| node_target |
| = find_identifier_target ( |
| &self->document->identifiers_target, |
| normalized); |
| /* double node are not normalized, they are handled here */ |
| if (!node_target) |
| { |
| const FILE_SOURCE_INFO *file_source_info = 0; |
| |
| TEXT file_name_text; |
| text_init (&file_name_text); |
| text_append (&file_name_text, "unknown_node"); |
| if (extension) |
| { |
| text_append (&file_name_text, "."); |
| text_append (&file_name_text, extension); |
| } |
| file_source_info |
| = html_find_file_source_info (files_source_info, |
| file_name_text.text); |
| if (!file_source_info) |
| { |
| file_source_info |
| = html_add_to_files_source_info ( |
| files_source_info, |
| file_name_text.text, "node", 0, |
| root_command, 0); |
| node_filename = file_source_info->filename; |
| } |
| free (file_name_text.text); |
| } |
| else |
| { |
| /* Nodes with {'extra'}->{'is_target'} should always be in |
| 'identifiers_target', and thus in targets. It is a bug otherwise. */ |
| FILE_SOURCE_INFO *file_source_info = 0; |
| HTML_TARGET *node_target |
| = html_get_target (self, root_command); |
| node_filename = node_target->node_filename; |
| |
| file_source_info |
| = html_find_file_source_info (files_source_info, |
| node_filename); |
| if (file_source_info) |
| { |
| if (!strcmp (file_source_info->type, |
| "stand_in_file")) |
| {/* NOTE we keep the order, as in perl */ |
| html_set_file_source_info (file_source_info, |
| "node", |
| 0, root_command, 0); |
| } |
| } |
| else |
| html_add_to_files_source_info (files_source_info, |
| node_filename, "node", 0, |
| root_command, 0); |
| } |
| output_unit_file_name |
| = add_to_unit_file_name_paths (unit_file_name_paths, |
| node_filename, |
| file_output_unit); |
| break; |
| } |
| } |
| if (!node_filename) |
| { |
| /* use section to do the file name if there is no node */ |
| const SECTION_RELATIONS *command |
| = file_output_unit->unit_section; |
| if (command) |
| { |
| if (command->element->e.c->cmd == CM_top && !node_top |
| && top_node_filename_str) |
| { |
| /* existing top_node_filename can happen, see |
| html_tests.t top_file_name_and_node_name_collision */ |
| FILE_SOURCE_INFO *file_source_info |
| = html_find_file_source_info (files_source_info, |
| top_node_filename_str); |
| if (file_source_info) |
| {/* NOTE we keep the order, as in perl */ |
| html_set_file_source_info (file_source_info, |
| "special_file", "Top", |
| 0, 0); |
| } |
| else |
| { |
| html_add_to_files_source_info (files_source_info, |
| top_node_filename_str, |
| "special_file", "Top", |
| 0, 0); |
| } |
| output_unit_file_name |
| = add_to_unit_file_name_paths (unit_file_name_paths, |
| top_node_filename_str, |
| file_output_unit); |
| } |
| else |
| { |
| const HTML_TARGET *section_target |
| = html_get_target (self, command->element); |
| const char *section_filename |
| = section_target->section_filename; |
| |
| FILE_SOURCE_INFO *file_source_info |
| = html_find_file_source_info (files_source_info, |
| section_filename); |
| if (file_source_info) |
| { |
| if (!strcmp (file_source_info->type, |
| "stand_in_file")) |
| {/* NOTE we keep the order, as in perl */ |
| html_set_file_source_info (file_source_info, |
| "section", |
| 0, command->element, 0); |
| } |
| } |
| else |
| html_add_to_files_source_info (files_source_info, |
| section_filename, "section", 0, |
| command->element, 0); |
| output_unit_file_name |
| = add_to_unit_file_name_paths (unit_file_name_paths, |
| section_filename, |
| file_output_unit); |
| } |
| } |
| else |
| { |
| /* when everything else has failed */ |
| if (file_nr == 0 && !node_top |
| && top_node_filename_str) |
| { |
| const FILE_SOURCE_INFO *file_source_info |
| = html_find_file_source_info (files_source_info, |
| top_node_filename_str); |
| if (!file_source_info) |
| { |
| html_add_to_files_source_info (files_source_info, |
| top_node_filename_str, |
| "stand_in_file", "Top", |
| 0, 0); |
| } |
| output_unit_file_name |
| = add_to_unit_file_name_paths (unit_file_name_paths, |
| top_node_filename_str, |
| file_output_unit); |
| } |
| else |
| { |
| const FILE_SOURCE_INFO *file_source_info; |
| |
| TEXT file_name_text; |
| text_init (&file_name_text); |
| text_printf (&file_name_text, "%s_%zu", document_name, |
| file_nr); |
| if (extension) |
| { |
| text_append (&file_name_text, "."); |
| text_append (&file_name_text, extension); |
| } |
| file_source_info |
| = html_find_file_source_info (files_source_info, |
| file_name_text.text); |
| if (!file_source_info) |
| { |
| html_add_to_files_source_info (files_source_info, |
| file_name_text.text, |
| "stand_in_file", |
| "unknown", |
| 0, 0); |
| } |
| output_unit_file_name |
| = add_to_unit_file_name_paths (unit_file_name_paths, |
| file_name_text.text, |
| file_output_unit); |
| free (file_name_text.text); |
| } |
| file_nr++; |
| } |
| } |
| } |
| if (output_unit != file_output_unit) |
| add_to_unit_file_name_paths (unit_file_name_paths, |
| output_unit_file_name, |
| output_unit); |
| } |
| free (top_node_filename_str); |
| } |
| |
| self->output_unit_file_indices = (size_t *) |
| realloc (self->output_unit_file_indices, |
| output_units->number * sizeof (size_t)); |
| |
| for (i = 0; i < output_units->number; i++) |
| { |
| size_t output_unit_file_idx = 0; |
| const FILE_NAME_PATH_COUNTER *output_unit_file; |
| OUTPUT_UNIT *output_unit = output_units->list[i]; |
| char *filename = unit_file_name_paths[i]; |
| const FILE_SOURCE_INFO *file_source_info |
| = html_find_file_source_info (files_source_info, filename); |
| const char *filepath = file_source_info->path; |
| |
| FILE_NAME_PATH *file_name_path |
| = call_file_id_setting_unit_file_name (self, output_unit, |
| filename, filepath); |
| if (file_name_path) |
| { |
| if (file_name_path->filename) |
| { |
| FILE_SOURCE_INFO *file_source_info |
| = html_find_file_source_info (files_source_info, |
| file_name_path->filename); |
| if (file_source_info) |
| { |
| /* It is likely that setting different paths for the same file is |
| not intended, so we warn. */ |
| if (file_source_info->path && file_name_path->filepath |
| && strcmp (file_source_info->path, |
| file_name_path->filepath)) |
| { |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, |
| "resetting %s file path %s to %s", |
| file_name_path->filename, |
| file_source_info->path, |
| file_name_path->filepath); |
| } |
| else if (file_name_path->filepath |
| && !file_source_info->path) |
| { |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, |
| "resetting %s file path from a relative path to %s", |
| file_name_path->filename, |
| file_name_path->filepath); |
| } |
| else if (!file_name_path->filepath |
| && file_source_info->path) |
| { |
| message_list_document_warn (&self->error_messages, |
| self->conf, 0, |
| "resetting %s file path from %s to a relative path", |
| file_name_path->filename, |
| file_source_info->path); |
| } |
| html_set_file_source_info (file_source_info, "special_file", |
| "user_defined", 0, file_name_path->filepath); |
| } |
| else |
| html_add_to_files_source_info (files_source_info, |
| file_name_path->filename, |
| "special_file", "user_defined", |
| 0, file_name_path->filepath); |
| free (filename); |
| filename = file_name_path->filename; |
| } |
| free (file_name_path->filepath); |
| free (file_name_path); |
| } |
| output_unit_file_idx |
| = set_output_unit_file (self, output_unit, filename, 1); |
| self->output_unit_file_indices[i] = output_unit_file_idx; |
| output_unit_file = &self->output_unit_files.list[output_unit_file_idx]; |
| if (self->conf->DEBUG.o.integer > 0) |
| { |
| char *output_unit_text = output_unit_texi (output_unit); |
| fprintf (stderr, "C|Page %s: %s(%d)\n", output_unit_text, |
| output_unit->unit_filename, output_unit_file->counter); |
| free (output_unit_text); |
| } |
| free (filename); |
| } |
| |
| free (unit_file_name_paths); |
| |
| if (special_units && special_units->number) |
| { |
| size_t i; |
| self->special_unit_file_indices = (size_t *) |
| realloc (self->special_unit_file_indices, |
| special_units->number * sizeof (size_t)); |
| for (i = 0; i < special_units->number; i++) |
| { |
| size_t special_unit_file_idx = 0; |
| const FILE_NAME_PATH_COUNTER *special_unit_file; |
| OUTPUT_UNIT *special_unit = special_units->list[i]; |
| const ELEMENT *unit_command = special_unit->uc.special_unit_command; |
| const HTML_TARGET *special_unit_target |
| = html_get_target (self, unit_command); |
| const char *filename = special_unit_target->special_unit_filename; |
| |
| /* Associate the special elements that have no page with the main page. |
| This may only happen if not split. */ |
| if (!filename && special_units->number |
| && output_units->list[0]->unit_filename) |
| { |
| filename = output_units->list[0]->unit_filename; |
| } |
| |
| if (filename) |
| { |
| FILE_SOURCE_INFO *file_source_info |
| = html_find_file_source_info (files_source_info, filename); |
| if (file_source_info) |
| { |
| if (!strcmp (file_source_info->type, "stand_in_file")) |
| {/* NOTE we keep the order, as in perl */ |
| html_set_file_source_info (file_source_info, |
| "special_unit", |
| 0, unit_command, 0); |
| } |
| } |
| else |
| html_add_to_files_source_info (files_source_info, filename, |
| "special_unit", 0, |
| unit_command, 0); |
| } |
| special_unit_file_idx |
| = set_output_unit_file (self, special_unit, filename, 1); |
| self->special_unit_file_indices[i] = special_unit_file_idx; |
| special_unit_file |
| = &self->output_unit_files.list[special_unit_file_idx]; |
| if (self->conf->DEBUG.o.integer > 0) |
| fprintf (stderr, "C|Special page: %s(%d)\n", filename, |
| special_unit_file->counter); |
| } |
| } |
| else |
| { |
| free (self->special_unit_file_indices); |
| self->special_unit_file_indices = 0; |
| } |
| |
| for (i = 0; i < files_source_info->number; i++) |
| { |
| FILE_SOURCE_INFO *file_source_info = &files_source_info->list[i]; |
| set_file_path (self, file_source_info->filename, file_source_info->path, |
| destination_directory); |
| } |
| |
| /* |
| to be able to associate to the output unit file the associated |
| output units will be output into, this is done after document output |
| units got files. |
| In practice only used for contents and shortcontents. |
| */ |
| if (associated_special_units && associated_special_units->number > 0) |
| { |
| size_t i; |
| for (i = 0; i < associated_special_units->number; i++) |
| { |
| const char *filename = 0; |
| OUTPUT_UNIT *special_unit = associated_special_units->list[i]; |
| const OUTPUT_UNIT *associated_output_unit |
| = special_unit->associated_document_unit; |
| const ELEMENT *unit_command = special_unit->uc.special_unit_command; |
| HTML_TARGET *element_target |
| = html_get_target (self, unit_command); |
| |
| if (!element_target->special_unit_filename) |
| {/* set the file if not already set */ |
| char *unit_filename = 0; |
| if (associated_output_unit) |
| unit_filename = strdup (associated_output_unit->unit_filename); |
| element_target->special_unit_filename = unit_filename; |
| } |
| filename = element_target->special_unit_filename; |
| |
| /* set here the file name, but do not increment the counter as it is |
| already set for the output unit the special output unit is in. */ |
| if (filename) |
| set_output_unit_file (self, special_unit, filename, 0); |
| } |
| } |
| |
| prepare_page_css (self, self->output_unit_files.number); |
| |
| self->html_files_information.number = self->output_unit_files.number +1; |
| self->html_files_information.list = (FILE_ASSOCIATED_INFO *) |
| malloc (self->html_files_information.number * sizeof (FILE_ASSOCIATED_INFO)); |
| memset (self->html_files_information.list, 0, |
| self->html_files_information.number * sizeof (FILE_ASSOCIATED_INFO)); |
| |
| html_initialize_pending_closes (self, self->output_unit_files.number +1); |
| |
| return files_source_info; |
| } |
| |
| static void |
| prepare_special_units_directions (CONVERTER *self, |
| OUTPUT_UNIT_LIST *special_units) |
| { |
| size_t i; |
| |
| if (!special_units) |
| return; |
| |
| for (i = 0; i < special_units->number; i++) |
| { |
| OUTPUT_UNIT *special_unit = special_units->list[i]; |
| special_unit->directions[RUD_type_This] = special_unit; |
| } |
| } |
| |
| /* Return structure to be freed by the caller */ |
| FILE_SOURCE_INFO_LIST * |
| html_prepare_units_directions_files (CONVERTER *self, |
| const char *output_file, const char *destination_directory, |
| const char *output_filename, const char *document_name) |
| { |
| size_t i; |
| FILE_SOURCE_INFO_LIST *files_source_info = 0; |
| size_t external_nodes_units_descriptor |
| = new_output_units_descriptor (self->document); |
| OUTPUT_UNIT_LIST *external_node_target_units |
| = retrieve_output_units (self->document, external_nodes_units_descriptor); |
| OUTPUT_UNIT_LIST *output_units = retrieve_output_units |
| (self->document, self->output_units_descriptors[OUDT_units]); |
| OUTPUT_UNIT_LIST *special_units = retrieve_output_units |
| (self->document, self->output_units_descriptors[OUDT_special_units]); |
| OUTPUT_UNIT_LIST *associated_special_units = retrieve_output_units |
| (self->document, |
| self->output_units_descriptors[OUDT_associated_special_units]); |
| const NODE_RELATIONS_LIST *nodes_list = &self->document->nodes_list; |
| |
| self->output_units_descriptors[OUDT_external_nodes_units] |
| = external_nodes_units_descriptor; |
| |
| html_prepare_output_units_global_targets (self); |
| |
| split_pages (output_units, nodes_list, self->conf->SPLIT.o.string); |
| |
| /* needed even if !strlen (output_file), as self->output_unit_files.number |
| is checked below */ |
| /* same as calling initialize_output_units_files in Perl */ |
| clear_output_unit_files (&self->output_unit_files); |
| |
| html_reset_files_source_info (&self->files_source_info); |
| |
| if (strlen (output_file)) |
| { |
| files_source_info = |
| html_set_pages_files (self, output_units, special_units, |
| associated_special_units, output_file, |
| destination_directory, output_filename, document_name); |
| } |
| else |
| html_setup_output_simple_page (self, output_filename); |
| |
| units_directions (&self->document->identifiers_target, |
| nodes_list, output_units, |
| external_node_target_units, |
| self->conf->DEBUG.o.integer); |
| |
| prepare_special_units_directions (self, special_units); |
| |
| units_file_directions (output_units); |
| |
| self->document->modified_information |= F_DOCM_output_units; |
| |
| /* elements_in_file_count is only set in HTML, not in |
| Texinfo::Convert::Converter */ |
| if (self->output_unit_files.number) |
| { |
| /* set elements_in_file_count and prepare page_name_number |
| for sorting */ |
| allocate_name_number_list (&self->page_name_number, |
| self->output_unit_files.number); |
| |
| for (i = 0; i < self->output_unit_files.number; i++) |
| { |
| FILE_NAME_PATH_COUNTER *file_counter |
| = &self->output_unit_files.list[i]; |
| NAME_NUMBER *page_name_number = &self->page_name_number.list[i]; |
| |
| /* counter is dynamic, decreased when the element is encountered |
| elements_in_file_count is not modified afterwards */ |
| file_counter->elements_in_file_count = file_counter->counter; |
| |
| page_name_number->number = i+1; |
| page_name_number->name = file_counter->filename; |
| } |
| |
| sort_name_number_list (&self->page_name_number); |
| } |
| |
| return files_source_info; |
| } |
| |