blob: b4f6b56d4ac8016207bcc74e49ec749b74504784 [file]
/* 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/>. */
/* abstractions useful to setup interfaces, in practice the SWIG interface
in util/swig/texinfo.i. The functions defined here should not be used
in texi2any.c nor in Perl XS interfaces used in texi2any. Functions
directly useful should not be in this file, but, for example in tree.c
or utils.c. Therefore, if a function here become used in texi2any it
should be moved elsewhere.
All the functions defined here and in swig_* files should have a
txi_ext_ prefix to avoid clashing with Texinfo C libraries names
(including future names).
*/
#include <config.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "types_data.h"
#include "source_mark_types.h"
#include "tree_types.h"
#include "document_types.h"
#include "swig_error_messages_types.h"
#include "converter_types.h"
#include "text.h"
#include "tree.h"
#include "extra.h"
/* get_cmd_global_uniq_command lookup_index_entry */
#include "utils.h"
#include "builtin_commands.h"
#include "errors.h"
#include "document.h"
#include "convert_to_text.h"
#include "swig_element_data.h"
#include "swig_text_options.h"
#include "swig_interface.h"
#define cm_flag(name) \
int \
txi_ext_element_command_is_##name (ELEMENT *element) \
{ \
enum command_id cmd; \
\
if (type_data[element->type].flags & TF_text) \
return 0; \
cmd = element_builtin_data_cmd (element); \
return builtin_command_data[cmd].flags & CF_##name; \
}
TXI_CM_FLAGS_LIST
#undef cm_flag
#define cm_oflag(name) \
int \
txi_ext_element_command_is_##name (ELEMENT *element) \
{ \
enum command_id cmd; \
\
if (type_data[element->type].flags & TF_text) \
return 0; \
cmd = element_builtin_data_cmd (element); \
return builtin_command_data[cmd].other_flags & CF_##name; \
}
TXI_CM_OTHER_FLAGS_LIST
#undef cm_oflag
static const char *brace_command_data_type_name[] = {
#define tcc_cmd_category(type, cat, uppercat) #type,
TXI_CMD_CATEGORY_BRACE
#undef tcc_cmd_category
};
static const char *nobrace_command_data_type_name[] = {
#define tcc_cmd_category(type, cat, uppercat) #type,
TXI_CMD_CATEGORY_NOBRACE
#undef tcc_cmd_category
};
static const char *block_command_data_type_name[] = {
#define tcc_cmd_category(type, cat, uppercat) #type,
TXI_CMD_CATEGORY_BLOCK
#undef tcc_cmd_category
};
static const char *line_command_data_type_name[] = {
#define tcc_cmd_category(type, cat, uppercat) #type,
TXI_CMD_CATEGORY_LINE
#undef tcc_cmd_category
};
#define ty_flag(name) \
int \
txi_ext_element_type_is_##name (ELEMENT *element) \
{ \
return type_data[element->type].flags & TF_##name; \
}
TXI_TY_FLAGS_LIST
#undef ty_flag
const char *
txi_ext_element_command_data_type (ELEMENT *element)
{
if (type_data[element->type].flags & TF_text)
return 0;
enum command_id cmd = element_builtin_data_cmd (element);
if (cmd)
{
int cmd_data_type = builtin_command_data[cmd].data;
if (builtin_command_data[cmd].flags & CF_brace)
return brace_command_data_type_name[cmd_data_type];
else if (builtin_command_data[cmd].flags & CF_line)
return line_command_data_type_name[cmd_data_type];
else if (builtin_command_data[cmd].flags & CF_nobrace)
return nobrace_command_data_type_name[cmd_data_type];
else if (builtin_command_data[cmd].flags & CF_block)
return block_command_data_type_name[cmd_data_type];
}
return 0;
}
const char *
txi_ext_element_type (ELEMENT *element)
{
if (element->type)
{
if (type_data[element->type].flags & TF_text)
{
if (element->type != ET_normal_text)
return type_data[element->type].name;
}
else if (!(type_data[element->type].flags & TF_c_only))
return type_data[element->type].name;
}
return "";
}
const char *
txi_ext_element_text (ELEMENT *element)
{
if (type_data[element->type].flags & TF_text)
return element->e.text->text;
return 0;
}
const char *
txi_ext_element_cmdname (ELEMENT *element)
{
if (!(type_data[element->type].flags & TF_text))
return element_command_name (element);
return 0;
}
ELEMENT *
txi_ext_element_list_element_by_index (ELEMENT_LIST *element_list, int index)
{
if (!element_list || element_list->number == 0)
return 0;
if (index < 0)
index = (int) element_list->number - index;
if (index < 0 || (size_t) index >= element_list->number)
return 0;
return element_list->list[index];
}
int
txi_ext_element_list_elements_number (ELEMENT_LIST *element_list)
{
return element_list->number;
}
const ELEMENT *
txi_ext_const_element_list_element_by_index (CONST_ELEMENT_LIST *element_list,
int index)
{
if (!element_list || element_list->number == 0)
return 0;
if (index < 0)
index = (int) element_list->number - index;
if (index < 0 || (size_t) index >= element_list->number)
return 0;
return element_list->list[index];
}
int
txi_ext_const_element_list_elements_number (CONST_ELEMENT_LIST *element_list)
{
return element_list->number;
}
char *
txi_ext_string_list_string_by_index (STRING_LIST *string_list, int index)
{
if (!string_list || string_list->number == 0)
return 0;
if (index < 0)
index = (int) string_list->number - index;
if (index < 0 || (size_t) index >= string_list->number)
return 0;
return string_list->list[index];
}
int
txi_ext_string_list_strings_number (STRING_LIST *string_list)
{
return string_list->number;
}
int
txi_ext_element_children_number (ELEMENT *element)
{
if (type_data[element->type].flags & TF_text)
return 0;
return element->e.c->contents.number;
}
ELEMENT *
txi_ext_element_get_child (ELEMENT *element, int index)
{
if (type_data[element->type].flags & TF_text)
return 0;
return txi_ext_element_list_element_by_index (&element->e.c->contents,
index);
}
int
txi_ext_element_source_marks_number (ELEMENT *element)
{
if (element->source_mark_list)
return element->source_mark_list->number;
return 0;
}
SOURCE_MARK *
txi_ext_element_get_source_mark (ELEMENT *element, int index)
{
SOURCE_MARK_LIST *source_mark_list = element->source_mark_list;
if (source_mark_list && source_mark_list->number == 0)
return 0;
if (index < 0)
index = (int) source_mark_list->number - index;
if (index < 0 || (size_t) index >= source_mark_list->number)
return 0;
return source_mark_list->list[index];
}
ELEMENT *
txi_ext_element_parent (ELEMENT *element)
{
if (type_data[element->type].flags & TF_text)
return 0;
return element->e.c->parent;
}
SOURCE_INFO *
txi_ext_element_source_info (ELEMENT *element)
{
if (type_data[element->type].flags & TF_text)
return 0;
return &element->e.c->source_info;
}
int
txi_ext_element_attribute_integer (const ELEMENT *element,
const char *attribute,
int *ret)
{
enum ai_key_name key = find_associated_info_key (attribute);
if (key)
{
enum extra_type k_type = associated_info_table[key].type;
switch (k_type)
{
case extra_integer:
{
const KEY_PAIR *k = lookup_extra (element, key);
if (k)
{
*ret = 0;
return k->k.integer;
}
break;
}
case extra_flag:
{
if (element->flags & associated_info_table[key].data)
{
*ret = 0;
return 1;
}
break;
}
default:
break;
}
}
*ret = -1;
return 0;
}
const char *
txi_ext_element_attribute_string (const ELEMENT *element,
const char *attribute)
{
enum ai_key_name key = find_associated_info_key (attribute);
if (key)
{
enum extra_type k_type = associated_info_table[key].type;
switch (k_type)
{
case extra_string:
{
const KEY_PAIR *k = lookup_extra (element, key);
if (k)
return k->k.string;
break;
}
case extra_string_info:
{
int idx = associated_info_table[key].data;
switch (key)
{
case AI_key_delimiter:
if (element->e.c->cmd == CM_verb
&& element->e.c->string_info[idx])
return element->e.c->string_info[idx];
break;
case AI_key_alias_of:
if ((element->e.c->cmd
|| type_data[element->type].flags & TF_with_command)
&& element->e.c->string_info[idx])
return element->e.c->string_info[idx];
break;
default:
break;
}
break;
}
default:
break;
}
}
return 0;
}
ELEMENT *
txi_ext_element_attribute_element (const ELEMENT *element,
const char *attribute)
{
enum ai_key_name key = find_associated_info_key (attribute);
if (key)
{
enum extra_type k_type = associated_info_table[key].type;
switch (k_type)
{
case extra_element_oot:
case extra_container:
{
const KEY_PAIR *k = lookup_extra (element, key);
if (k)
return k->k.element;
break;
}
case extra_element_info:
{
int idx = associated_info_table[key].data;
if (idx < type_data[element->type].elt_info_number
&& element->elt_info[idx])
{
ELEMENT *info_element = element->elt_info[idx];
return info_element;
}
break;
}
default:
break;
}
}
return 0;
}
INDEX_ENTRY *
txi_ext_element_index_entry (DOCUMENT *document, ELEMENT *element)
{
const INDEX_ENTRY_LOCATION *index_entry_info
= lookup_extra_index_entry (element,
AI_key_index_entry);
if (index_entry_info)
{
INDEX_ENTRY *result = 0;
INDEX_ENTRY_AND_INDEX *idx_info
= lookup_index_entry (index_entry_info,
&document->indices_info);
if (idx_info->index_entry)
result = idx_info->index_entry;
free (idx_info);
return result;
}
return 0;
}
INDEX *
txi_ext_index_entry_index_info (DOCUMENT *document, INDEX_ENTRY *index_entry)
{
const char *entry_index_name = index_entry->index_name;
return indices_info_index_by_name (&document->indices_info,
entry_index_name);
}
const STRING_LIST *
txi_ext_element_misc_args (ELEMENT *element)
{
return lookup_extra_string_list (element, AI_key_misc_args);
}
const STRING_LIST *
txi_ext_element_formal_args (ELEMENT *element)
{
return lookup_extra_string_list (element, AI_key_formal_args);
}
/* New element and element modification */
ELEMENT *
txi_ext_store_new_element (DOCUMENT *document, const char *type_name,
const char *command_name, int is_text_element)
{
ELEMENT *e = new_element_from_names (type_name, command_name,
is_text_element);
if (e)
add_to_element_list (&document->additional_elements, e);
return e;
}
void
txi_ext_element_reset_text (ELEMENT *element)
{
if (type_data[element->type].flags & TF_text)
{
text_reset (element->e.text);
text_append (element->e.text, "");
}
}
void
txi_ext_element_append_text (ELEMENT *element, const char *text)
{
if (type_data[element->type].flags & TF_text)
text_append (element->e.text, text);
}
int
txi_ext_set_element_attribute_integer (ELEMENT *element,
const char *attribute, int value)
{
enum ai_key_name key = find_associated_info_key (attribute);
if (key)
{
enum extra_type k_type = associated_info_table[key].type;
switch (k_type)
{
case extra_integer:
{
add_extra_integer (element, key, value);
return 1;
break;
}
case extra_flag:
{
if (value == 1)
element->flags |= associated_info_table[key].data;
else if (value == 0)
element->flags &= ~associated_info_table[key].data;
return 1;
break;
}
default:
break;
}
}
return 0;
}
int
txi_ext_set_element_attribute_string (ELEMENT *element, const char *attribute,
const char *value)
{
enum ai_key_name key = find_associated_info_key (attribute);
if (key)
{
enum extra_type k_type = associated_info_table[key].type;
switch (k_type)
{
case extra_string:
{
add_extra_string_dup (element, key, value);
return 1;
break;
}
case extra_string_info:
{
int idx = associated_info_table[key].data;
switch (key)
{
case AI_key_delimiter:
if (element->e.c->cmd == CM_verb)
{
free (element->e.c->string_info[idx]);
if (value)
element->e.c->string_info[idx] = strdup (value);
else
element->e.c->string_info[idx] = 0;
return 1;
}
break;
case AI_key_alias_of:
if (element->e.c->cmd
|| type_data[element->type].flags & TF_with_command)
{
free (element->e.c->string_info[idx]);
if (value)
element->e.c->string_info[idx] = strdup (value);
else
element->e.c->string_info[idx] = 0;
return 1;
}
break;
default:
break;
}
break;
}
default:
break;
}
}
return 0;
}
int
txi_ext_set_element_attribute_element (ELEMENT *element, const char *attribute,
ELEMENT *value)
{
enum ai_key_name key = find_associated_info_key (attribute);
if (key)
{
enum extra_type k_type = associated_info_table[key].type;
switch (k_type)
{
case extra_element_oot:
{
add_extra_element_oot (element, key, value);
return 1;
break;
}
case extra_container:
{
add_extra_container (element, key, value);
return 1;
break;
}
case extra_element_info:
{
int idx = associated_info_table[key].data;
if (idx < type_data[element->type].elt_info_number)
{
free (element->elt_info[idx]);
element->elt_info[idx] = value;
return 1;
}
break;
}
default:
break;
}
}
return 0;
}
SECTION_RELATIONS *
txi_ext_section_relation_list_section_relation_by_index (
SECTION_RELATIONS_LIST *section_relation_list, int index)
{
if (!section_relation_list || section_relation_list->number == 0)
return 0;
if (index < 0)
index = (int) section_relation_list->number - index;
if (index < 0 || (size_t) index >= section_relation_list->number)
return 0;
return section_relation_list->list[index];
}
int txi_ext_section_relation_list_section_relations_number (
SECTION_RELATIONS_LIST *section_relation_list)
{
return section_relation_list->number;
}
NODE_RELATIONS *
txi_ext_get_node_relations (ELEMENT *element, DOCUMENT *document)
{
int status;
int node_number;
const NODE_RELATIONS_LIST *nodes_list = &document->nodes_list;
if (type_data[element->type].flags & TF_text)
return 0;
if (element->e.c->cmd != CM_node)
return 0;
node_number = lookup_extra_integer (element, AI_key_node_number, &status);
if (node_number > 0 && (size_t) node_number <= nodes_list->number)
return nodes_list->list[node_number -1];
return 0;
}
SECTION_RELATIONS *
txi_ext_get_section_relations (ELEMENT *element, DOCUMENT *document)
{
int status;
int section_number;
const SECTION_RELATIONS_LIST *sections_list = &document->sections_list;
if (type_data[element->type].flags & TF_text)
return 0;
enum command_id data_cmd = element_builtin_data_cmd (element);
if (!(builtin_command_data[data_cmd].flags & CF_root))
return 0;
section_number
= lookup_extra_integer (element, AI_key_section_number, &status);
if (section_number > 0 && (size_t) section_number <= sections_list->number)
return sections_list->list[section_number -1];
return 0;
}
HEADING_RELATIONS *
txi_ext_get_heading_relations (ELEMENT *element, DOCUMENT *document)
{
int status;
int heading_number;
const HEADING_RELATIONS_LIST *headings_list = &document->headings_list;
if (type_data[element->type].flags & TF_text)
return 0;
if (!element->e.c->cmd)
return 0;
heading_number
= lookup_extra_integer (element, AI_key_heading_number, &status);
if (heading_number > 0 && (size_t) heading_number <= headings_list->number)
return headings_list->list[heading_number -1];
return 0;
}
static size_t
txi_ext_direction_name_number (const char *direction)
{
size_t d;
if (!direction)
return 0;
for (d = 0; d < directions_length; d++)
{
if (!strcmp (direction, direction_names[d]))
return d +1;
}
return 0;
}
const ELEMENT *
txi_ext_node_relation_node_direction (NODE_RELATIONS *node,
const char *direction)
{
size_t d_number;
if (!node->node_directions)
return 0;
d_number = txi_ext_direction_name_number (direction);
if (!d_number)
return 0;
return node->node_directions[d_number -1];
}
const SECTION_RELATIONS *
txi_ext_section_relation_section_direction (SECTION_RELATIONS *section,
const char *direction)
{
size_t d_number;
if (!section->section_directions)
return 0;
d_number = txi_ext_direction_name_number (direction);
if (!d_number)
return 0;
return section->section_directions[d_number -1];
}
const SECTION_RELATIONS *
txi_ext_section_relation_toplevel_direction (SECTION_RELATIONS *section,
const char *direction)
{
size_t d_number;
if (!section->toplevel_directions)
return 0;
d_number = txi_ext_direction_name_number (direction);
if (!d_number)
return 0;
return section->toplevel_directions[d_number -1];
}
ELEMENT *
txi_ext_document_global_unique_command (DOCUMENT *document,
const char *cmdname)
{
ELEMENT *element = 0;
enum command_id cmd = lookup_builtin_command (cmdname);
if (cmd)
{
element = (ELEMENT *)get_cmd_global_uniq_command (
&document->global_commands, cmd);
}
return element;
}
const ELEMENT_LIST *
txi_ext_document_global_command_list (DOCUMENT *document,
const char *cmdname)
{
enum command_id cmd = lookup_builtin_command (cmdname);
if (document && cmd)
{
const ELEMENT_LIST *command_list
= get_cmd_global_multi_command (&document->global_commands, cmd);
if (command_list && command_list->number)
{
return command_list;
}
}
return 0;
}
INDEX_ENTRY *
txi_ext_sorted_index_entries_by_index (
const INDEX_SORTED_BY_INDEX *index_sorted,
int index)
{
if (!index_sorted || index_sorted->entries_number == 0)
return 0;
if (index < 0 || (size_t) index >= index_sorted->entries_number)
return 0;
return index_sorted->entries[index];
}
int
txi_ext_sorted_index_entries_number (const INDEX_SORTED_BY_INDEX *index_sorted)
{
return index_sorted->entries_number;
}
const INDEX_SORTED_BY_INDEX *
txi_ext_get_index_sorted_by_index (DOCUMENT *document, const char *index_name,
int use_unicode_collation,
const char *collation_language,
const char *collation_locale)
{
const INDEX_SORTED_BY_INDEX *idx;
const INDEX_SORTED_BY_INDEX *index_sorted = 0;
COLLATION_INDICES_SORTED_BY_INDEX *collation_sorted_indices
= document_sorted_indices_by_index (document, &document->error_messages,
document->options, 0, use_unicode_collation,
collation_language, collation_locale);
INDEX_SORTED_BY_INDEX *indices_sorted_by_index
= collation_sorted_indices->sorted_indices;
if (!indices_sorted_by_index)
return 0;
for (idx = indices_sorted_by_index; idx->name; idx++)
{
if (!strcmp (idx->name, index_name))
{
index_sorted = idx;
break;
}
}
if (!index_sorted || !index_sorted->entries_number)
return 0;
return index_sorted;
}
FLOAT_INFORMATION *
txi_ext_float_list_float_by_index (FLOAT_INFORMATION_LIST *float_list,
int index)
{
if (!float_list || float_list->number == 0)
return 0;
if (index < 0)
index = (int) float_list->number - index;
if (index < 0 || (size_t) index >= float_list->number)
return 0;
return &float_list->list[index];
}
int
txi_ext_float_list_floats_number (FLOAT_INFORMATION_LIST *float_list)
{
return float_list->number;
}
FLOAT_INFORMATION_LIST *
txi_ext_get_float_type_floats_information (DOCUMENT *document,
const char *float_type)
{
size_t i;
const LISTOFFLOATS_TYPE_LIST *listoffloats = &document->listoffloats;
if (!listoffloats->number)
return 0;
for (i = 0; i < listoffloats->number; i++)
{
LISTOFFLOATS_TYPE *float_types = &listoffloats->float_types[i];
if (!strcmp (float_types->type, float_type))
{
if (float_types->float_list.number > 0)
return &float_types->float_list;
else
return 0;
}
}
return 0;
}
GLOBAL_INFO *
txi_ext_document_global_information (DOCUMENT *document)
{
return &document->global_info;
}
TEXT_OPTIONS *
txi_ext_document_text_options (DOCUMENT *document)
{
TEXT_OPTIONS *result;
if (document->options)
result = copy_options_for_convert_text (document->options,
document);
else
result = new_text_options ();
return result;
}
#define tico_option_name(name) \
void \
txi_ext_text_options_set_##name (TEXT_OPTIONS *text_options, int i) \
{ \
text_options->name = i; \
}
TEXT_INDICATOR_CONVERTER_OPTIONS
#undef tico_option_name
void
txi_ext_text_options_clear_expanded_formats (TEXT_OPTIONS *text_options)
{
clear_expanded_formats (text_options->expanded_formats);
}
void
txi_ext_text_options_add_expanded_format (TEXT_OPTIONS *text_options,
const char *format)
{
add_expanded_format (text_options->expanded_formats, format);
}
void
txi_ext_text_options_set_encoding (TEXT_OPTIONS *text_options,
const char *encoding)
{
free (text_options->encoding);
text_options->encoding = strdup (encoding);
}
FORMATTED_ERROR_MESSAGE_LIST *
txi_ext_get_error_messages_list_messages (ERROR_MESSAGE_LIST *error_messages,
const char *message_encoding,
int no_warn, int use_filename,
int *count)
{
ENCODING_CONVERSION *conversion = 0;
size_t error_nrs = count_errors (error_messages);
FORMATTED_ERROR_MESSAGE_LIST *result = 0;
size_t i;
size_t error_messages_nr = error_messages->number;
TEXT text;
size_t msg_idx = 0;
*count = (int)error_nrs;
if (error_messages_nr == 0)
return 0;
if (message_encoding)
conversion = get_encoding_conversion (message_encoding,
&output_conversions);
result = (FORMATTED_ERROR_MESSAGE_LIST *) malloc
(sizeof (FORMATTED_ERROR_MESSAGE_LIST));
result->list = (FORMATTED_ERROR_MESSAGE *)
malloc (error_messages_nr * sizeof (FORMATTED_ERROR_MESSAGE));
text_init (&text);
for (i = 0; i < error_messages_nr; i++)
{
const ERROR_MESSAGE *error_msg = &error_messages->list[i];
FORMATTED_ERROR_MESSAGE *result_msg;
if (error_msg->type == MSG_warning && no_warn > 0)
continue;
result_msg = &result->list[msg_idx];
result_msg->message = strdup (error_msg->message);
if (error_msg->error_line)
result_msg->error_line = strdup (error_msg->error_line);
result_msg->source_info = error_msg->source_info;
result_msg->continuation = error_msg->continuation;
result_msg->type = error_msg->type;
text_reset (&text);
error_message_text (error_msg, use_filename, conversion, &text);
result_msg->formatted = strdup (text.text);
msg_idx++;
}
free (text.text);
result->number = msg_idx;
clear_error_message_list (error_messages);
return result;
}
FORMATTED_ERROR_MESSAGE_LIST *
txi_ext_get_parser_error_messages (DOCUMENT *document,
const char *message_encoding,
int no_warn, int use_filename,
int *count)
{
return txi_ext_get_error_messages_list_messages (
&document->parser_error_messages,
message_encoding,
no_warn, use_filename, count);
}
FORMATTED_ERROR_MESSAGE_LIST *
txi_ext_get_document_error_messages (DOCUMENT *document,
const char *message_encoding,
int no_warn, int use_filename,
int *count)
{
return txi_ext_get_error_messages_list_messages (&document->error_messages,
message_encoding,
no_warn, use_filename, count);
}
FORMATTED_ERROR_MESSAGE *
txi_ext_messages_list_message_by_index (
FORMATTED_ERROR_MESSAGE_LIST *messages_list, int index)
{
if (!messages_list || messages_list->number == 0)
return 0;
if (index < 0 || (size_t) index >= messages_list->number)
return 0;
return &messages_list->list[index];
}
int
txi_ext_messages_list_messages_number (
FORMATTED_ERROR_MESSAGE_LIST *messages_list)
{
if (messages_list)
return messages_list->number;
return 0;
}
void
txi_ext_destroy_error_messages_list (
FORMATTED_ERROR_MESSAGE_LIST *error_messages)
{
size_t j;
if (error_messages)
{
for (j = 0; j < error_messages->number; j++)
{
free (error_messages->list[j].message);
free (error_messages->list[j].error_line);
free (error_messages->list[j].formatted);
}
free (error_messages->list);
free (error_messages);
}
}