blob: 6cabc51e5b85963db31a0842c61873bbe4594b35 [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/>. */
/* NOTE we store in AV lists indexed as size_t. AV max size seems to be
the max of SSize_t, if it is < SIZE_MAX there could theoretically be
overflows. However, Perl documentation says sizeof(SSize_t) == sizeof(Size_t)
and "Size_t ... is usually size_t". In addition these are very big numbers.
In Perl documentation there is no description of a constant that would
give the max of SSize_t.
We build index numbers, document, output units and converter descriptors
indexed as size_t to Perl SV using newSViv ((IV)descriptor). There
could theoretically be and overflow of IV if PERL_QUAD_MAX < SIZE_MAX.
(PERL_QUAD_MAX is the max size of IV in Perl). On an Intel 64 bit
GNU Linux, PERL_QUAD_MAX is half of SIZE_MAX. However those are
big numbers, while the Texinfo command numbers are quite small and other
descriptor numbers should be very small so this should not be an issue
in practice.
We also get IV and cast to size_t when getting info from Perl, like
descriptor = (size_t) SvIV (descriptor_sv). In that case there is no
reason to overflow.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
/* Avoid namespace conflicts. */
#define context perl_context
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#undef context
#include "command_ids.h"
#include "element_types.h"
#include "types_data.h"
#include "source_mark_types.h"
#include "tree_types.h"
#include "global_commands_types.h"
#include "option_types.h"
/* for GLOBAL_INFO ERROR_MESSAGE CL_* RUD_type* ERROR_MESSAGE_LIST */
#include "document_types.h"
/* CONVERTER sv_string_type CONVERTER_INITIALIZATION_INFO
enum sv_string_type */
#include "converter_types.h"
/* non_perl_* */
#include "xs_utils.h"
/* fatal */
#include "base_utils.h"
/* for associated_info_table elt_info_names
add_to_element_list */
#include "tree.h"
/* for lookup_extra */
#include "extra.h"
/* for element_command_name */
#include "builtin_commands.h"
/* for c_hashmap_iterator_next_value */
#include "hashmap.h"
/* for xasprintf get_encoding_conversion output_conversions
direction_names expanded_formats_number output_unit_type_names
informative_command_value get_global_document_command
direction_unit_direction_name */
#include "utils.h"
/* find_option_string */
#include "customization_options.h"
/* for debugging */
#include "debug.h"
/* for clear_error_message_list */
#include "errors.h"
#include "convert_to_texinfo.h"
#include "document.h"
#include "output_unit.h"
/* for TEXT_OPTIONS */
#include "convert_to_text.h"
/* also button_function_type_string */
#include "get_perl_info.h"
#include "build_perl_info.h"
/* NOTE This file includes the Perl headers, therefore we get the Perl
redefinitions of functions, in particular for functions related to
memory allocation, such as 'free' or 'malloc'. In other files, the
C library or Gnulib redefinition of those functions are used. It is
wrong to mix functions from Perl and C library plus Gnulib. If memory
is allocated with C library malloc, and then freed with Perl defined
free (or vice versa), then an error can occur like "Free to wrong pool".
https://lists.gnu.org/archive/html/bug-texinfo/2016-01/msg00016.html
*/
/* Functions defined in files with C library plus Gnulib definition should
therefore be used to allocate or free to match with the functions
used to free or allocate in other files using C library plus Gnulib
definitions.
To be sure to use non Perl defined functions, constructors and wrappers
must be used, from xs_utils.h:
non_perl_free, non_perl_malloc, non_perl_realloc, non_perl_strdup,
non_perl_strndup, non_perl_xvasprintf.
This is often needed, when memory is allocated or free'd in
'pure' C code (code that does not include Perl headers), as it is
safer to assume that C library or Gnulib function definitions are
always used in 'pure' C code.
To be sure to use Perl defined functions in files that include
both Gnulib and Perl headers, wrappers can be used, from
build_perl_info.h:
perl_only_free, perl_only_strdup, perl_only_strndup, perl_only_malloc.
In November 2024, no file is compiled with both Perl and Gnulib
headers as it lead to many errors on MS-Windows, therefore most of
the perl_only_* wrappers are currently unused. perl_only_strndup
is used for portability because strndup does not exist in mingw
and we can't use Gnulib strndup with Perl headers.
*/
/* wrappers to be sure to use Perl defined functions */
/* NB this function does not appear to be used currently. */
void
perl_only_free (void *ptr)
{
dTHX;
free (ptr);
}
/* NB this function does not appear to be used currently. */
void *
perl_only_malloc (size_t size)
{
dTHX;
return malloc (size);
}
/* Implement as we are not sure that Perl will define a version of this
function. */
/* NB this function does not appear to be used currently. */
char *
perl_only_strdup (const char *s)
{
size_t len = strlen (s);
char *ret = perl_only_malloc (len+1);
memcpy (ret, s, len+1);
return ret;
}
/* Implement as we are not sure that Perl will define a version of this
function.
Used for portability for platforms that do not have strndup in the
C library (mingw for instance).
*/
char *
perl_only_strndup (const char *s, size_t n)
{
size_t len = strlen (s);
if (len > n)
len = n;
char *ret = perl_only_malloc (len+1);
memcpy (ret, s, len);
ret[len] = '\0';
return ret;
}
/* build generic C data to Perl used in tree and elsewhere */
AV *
build_string_list (const STRING_LIST *strings_list, enum sv_string_type type)
{
AV *av;
size_t i;
dTHX;
av = newAV ();
for (i = 0; i < strings_list->number; i++)
{
const char *value = strings_list->list[i];
if (!value)
av_push (av, newSV (0));
else if (type == svt_char)
av_push (av, newSVpv_utf8 (value, 0));
else
av_push (av, newSVpv_byte (value, 0));
}
return av;
}
/* currently unused */
AV *
build_elements_list (const CONST_ELEMENT_LIST *list)
{
AV *list_av;
SV *sv;
size_t i;
dTHX;
list_av = newAV ();
av_unshift (list_av, list->number);
for (i = 0; i < list->number; i++)
{
sv = newSVsv ((SV *) list->list[i]->sv);
av_store (list_av, i, sv);
}
return list_av;
}
/* Build Texinfo tree data and Texinfo tree to Perl */
static HV *
new_element_perl_data (ELEMENT *e)
{
HV *element_hv;
HV *hv_stash;
SV *element_sv;
dTHX;
/* this reference should be retained in C code, which means that each time
a reference is transferred it should be duplicated (or aliased) */
element_hv = newHV ();
hv_stash = gv_stashpv ("Texinfo::TreeElement", GV_ADD);
element_sv = newRV_noinc ((SV *) element_hv);
sv_bless (element_sv, hv_stash);
e->sv = element_sv;
return element_hv;
}
void element_to_perl_hash (ELEMENT *e, int avoid_recursion);
/* Return reference to Perl array built from e. If any of
the elements in E don't have 'sv' set, set it to an empty
hash table, or create it if there is no parent element, indicating the
element is not in the tree.
Note that not having 'sv' set should be rare (actually never happen),
as the contents children are processed before the extra
information where build_perl_array is called.
*/
static AV *
build_perl_array (const ELEMENT_LIST *e_l, int avoid_recursion)
{
AV *av;
size_t i;
dTHX;
av = newAV ();
for (i = 0; i < e_l->number; i++)
{
ELEMENT *element = e_l->list[i];
if (!element->sv)
{
if (type_data[element->type].flags & TF_text
|| element->e.c->parent)
{
new_element_perl_data (element);
}
else
{
/* NOTE should not be possible, all the elements in
extra_contents should be in-tree. Checked in 2023.
*/
static TEXT message;
char *debug_str = print_element_debug (element, 1);
text_init (&message);
text_printf (&message,
"BUG: build_perl_array oot %d: %s\n", i, debug_str);
non_perl_free (debug_str);
fprintf (stderr, "%s", message.text);
non_perl_free (message.text);
/* Out-of-tree element */
/* WARNING: This is possibly recursive. */
element_to_perl_hash (element, avoid_recursion);
}
}
av_store (av, (SSize_t) i, newSVsv ((SV *) element->sv));
}
return av;
}
static SV *
build_perl_const_element_array (const CONST_ELEMENT_LIST *e_l, int avoid_recursion)
{
SV *sv;
AV *av;
size_t i;
dTHX;
av = newAV ();
sv = newRV_noinc ((SV *) av);
for (i = 0; i < e_l->number; i++)
{
if (!e_l->list[i]->sv)
{
ELEMENT *f = (ELEMENT *)e_l->list[i];
if (type_data[f->type].flags & TF_text || f->e.c->parent)
{
new_element_perl_data (f);
}
else
{
/* NOTE should not be possible, all the elements in
extra_contents should be in-tree. Checked in 2023.
*/
static TEXT message;
char *debug_str = print_element_debug (f, 1);
text_init (&message);
text_printf (&message,
"BUG: build_perl_const_element_array oot %d: %s\n", i, debug_str);
non_perl_free (debug_str);
fprintf (stderr, "%s", message.text);
non_perl_free (message.text);
/* Out-of-tree element */
/* WARNING: This is possibly recursive. */
element_to_perl_hash (f, avoid_recursion);
}
}
av_store (av, (SSize_t) i, newSVsv ((SV *) e_l->list[i]->sv));
}
return sv;
}
static int hashes_ready = 0;
static U32 HSH_parent = 0;
static U32 HSH_type = 0;
static U32 HSH_cmdname = 0;
static U32 HSH_contents = 0;
static U32 HSH_text = 0;
static U32 HSH_extra = 0;
static U32 HSH_info = 0;
static U32 HSH_source_info = 0;
static U32 HSH_file_name = 0;
static U32 HSH_line_nr = 0;
static U32 HSH_macro = 0;
/* contents appears in other parts of the tree */
static void
build_perl_container (ELEMENT *e, int avoid_recursion)
{
HV *element_hv;
AV *contents_av;
dTHX;
if (!e->sv)
{
element_hv = new_element_perl_data (e);
}
else
{
element_hv = (HV *) SvRV ((SV *) e->sv);
hv_clear (element_hv);
}
contents_av = build_perl_array (&e->e.c->contents, avoid_recursion);
hv_store (element_hv, "contents", strlen ("contents"),
newRV_noinc ((SV *) contents_av), HSH_contents);
}
static SV *
build_perl_directions (const ELEMENT * const *e_l, int avoid_recursion)
{
SV *sv;
HV *hv;
size_t d;
dTHX;
hv = newHV ();
sv = newRV_noinc ((SV *) hv);
for (d = 0; d < directions_length; d++)
{
if (e_l[d])
{
const char *key = direction_names[d];
const ELEMENT *e = e_l[d];
if (!e->sv)
{
/* recast to a non const element, as we need to modify it */
ELEMENT *f = (ELEMENT *)e;
if (type_data[e->type].flags & TF_text || e->e.c->parent)
{
new_element_perl_data (f);
}
else
{
/* NOTE This should not happen, all the elements are in-tree.
*/
static TEXT message;
char *debug_str = print_element_debug (e, 1);
text_init (&message);
text_printf (&message,
"BUG: build_perl_directions oot %s: %s\n", key, debug_str);
non_perl_free (debug_str);
fprintf (stderr, "%s", message.text);
non_perl_free (message.text);
/* Out-of-tree element */
/* WARNING: This is possibly recursive. */
element_to_perl_hash (f, avoid_recursion);
}
}
hv_store (hv, key, strlen (key), newSVsv ((SV *) e->sv), 0);
}
}
return sv;
}
SV *
build_extra_index_entry (const INDEX_ENTRY_LOCATION *entry_loc)
{
AV *av;
SV *sv;
dTHX;
av = newAV ();
av_unshift (av, 2);
sv = newSVpv_utf8 (entry_loc->index_name,
strlen (entry_loc->index_name));
av_store (av, 0, sv);
sv = newSViv ((IV) entry_loc->number);
av_store (av, 1, sv);
return newRV_noinc ((SV *) av);
}
/* The SV returned holds one reference in addition to the reference kept in C
for object that have a reference kept in C */
SV *
build_key_pair_info (const KEY_PAIR *k, int avoid_recursion)
{
enum ai_key_name key;
enum extra_type k_type;
dTHX;
key = k->key;
k_type = associated_info_table[key].type;
switch (k_type)
{
case extra_element:
{
/* For references to other parts of the tree, create the hash so
we can point to it. */
/* Note that this does not happen much, as the contents
are often processed before the extra information. */
const ELEMENT *f = k->k.const_element;
if (!f->sv)
{
/* need to cast to remove const to add the Perl object reference */
ELEMENT *e = (ELEMENT *)f;
new_element_perl_data (e);
}
return newSVsv ((SV *)f->sv);
break;
}
case extra_element_oot:
{
/*
Can be used for complex subtrees or special
out of tree elements, but must always be associated to only one
element and must not refer to the tree through contents.
*/
/* f->sv should not already exist the first time the tree
is built, but can already exist if the tree is rebuilt
if (f->sv)
{
static TEXT message;
char *debug_str = print_element_debug (e, 1);
text_init (&message);
text_printf (&message,
"element_to_perl_hash oot %s double in %s %p\n",
key, debug_str, f->sv);
non_perl_free (debug_str);
fatal (message.text);
fprintf (stderr, message.text);
}
*/
ELEMENT *f = k->k.element;
if (!f->sv || !avoid_recursion)
element_to_perl_hash (f, avoid_recursion);
return newSVsv ((SV *)f->sv);
break;
}
case extra_container:
{
ELEMENT *f = k->k.element;
build_perl_container (f, avoid_recursion);
return newSVsv ((SV *)f->sv);
break;
}
case extra_contents:
{
const CONST_ELEMENT_LIST *l = k->k.const_list;
if (l && l->number)
return build_perl_const_element_array (l, avoid_recursion);
break;
}
case extra_directions:
{
return build_perl_directions (k->k.directions, avoid_recursion);
break;
}
case extra_string:
{ /* A simple string. */
return newSVpv_utf8 (k->k.string, 0);
break;
}
case extra_integer:
{ /* A simple integer. */
return newSViv (k->k.integer);
break;
}
case extra_string_list:
{
AV *av = build_string_list (k->k.strings_list, svt_char);
return newRV_noinc ((SV *)av);
break;
}
case extra_index_entry:
{
return build_extra_index_entry (k->k.index_entry);
break;
}
default:
fatal ("build_key_pair_info: unknown extra type");
break;
}
return 0;
}
static int
build_associated_info (HV *extra, const ASSOCIATED_INFO *a,
int avoid_recursion)
{
int nr_info = 0;
dTHX;
if (a->info_number > 0)
{
size_t i;
for (i = 0; i < a->info_number; i++)
{
const KEY_PAIR *k = &a->info[i];
enum ai_key_name key = k->key;
enum extra_type k_type = associated_info_table[key].type;
const char *key_name;
SV *sv;
if (k_type == extra_none)
continue;
key_name = associated_info_table[key].name;
nr_info++;
sv = build_key_pair_info (k, avoid_recursion);
/*
if (sv && SvROK((SV*) sv))
fprintf (stderr, "EXTRA %s %p %d\n", key_name, (HV *)SvRV ((SV *) sv), SvREFCNT ((SV *) (HV *)SvRV ((SV *) sv)));
*/
if (sv)
hv_store (extra, key_name, strlen (key_name), sv, 0);
}
}
return nr_info;
}
static void
store_extra_additional_info (const ELEMENT *e, const ASSOCIATED_INFO *a,
int avoid_recursion, HV **extra_hv)
{
HV *hv;
int nr_info;
dTHX;
if (*extra_hv == 0)
hv = newHV ();
else
hv = *extra_hv;
nr_info = build_associated_info (hv, a, avoid_recursion);
if (*extra_hv == 0)
{
if (nr_info > 0)
{
HV *element_hv = (HV *) SvRV ((SV*) e->sv);
*extra_hv = hv;
hv_store (element_hv, "extra", strlen ("extra"),
newRV_noinc ((SV *)hv), 0);
}
else
/* release the hash reference, nothing was store inside */
SvREFCNT_dec (hv);
}
}
static void
store_source_mark_list (const ELEMENT *e)
{
dTHX;
if (e->source_mark_list)
{
AV *av;
SV *sv;
size_t i;
HV *element_hv = (HV *) SvRV ((SV*) e->sv);
if (e->source_mark_list->number == 0)
{
fprintf (stderr, "BUG: store_source_mark_list: 0 source marks but "
"source_mark_list\n");
return;
}
av = newAV ();
sv = newRV_noinc ((SV *) av);
hv_store (element_hv, "source_marks", strlen ("source_marks"), sv, 0);
for (i = 0; i < e->source_mark_list->number; i++)
{
HV *source_mark;
SV *sv;
const SOURCE_MARK *s_mark = e->source_mark_list->list[i];
IV source_mark_position;
IV source_mark_counter;
source_mark = newHV ();
#define STORE(key, value) hv_store (source_mark, key, strlen (key), value, 0)
/* A simple integer. The intptr_t cast here prevents
a warning on MinGW ("cast from pointer to integer of
different size"). */
source_mark_counter = (IV) (intptr_t) s_mark->counter;
STORE("counter", newSViv (source_mark_counter));
if (s_mark->position > 0)
{
source_mark_position = (IV) (intptr_t) s_mark->position;
STORE("position", newSViv (source_mark_position));
}
if (s_mark->element)
{
ELEMENT *s_m_e = s_mark->element;
/* should only be referred to in one source mark */
/* but can be reused when tree is rebuilt
if (e->sv)
fatal ("element_to_perl_hash source mark elt twice");
*/
element_to_perl_hash (s_m_e, 0);
STORE("element", newSVsv ((SV *)s_m_e->sv));
}
if (s_mark->line)
{
SV *sv = newSVpv_utf8 (s_mark->line, 0);
STORE("line", sv);
}
#define SAVE_S_M_STATUS(X) \
case SM_status_ ## X: \
sv = newSVpv_utf8 (#X, 0);\
STORE("status", sv); \
break;
switch (s_mark->status)
{
SAVE_S_M_STATUS (start)
SAVE_S_M_STATUS (end)
/* for SM_status_none */
default:
break;
}
switch (s_mark->type)
{
#define sm_type(X) \
case SM_type_ ## X: \
sv = newSVpv_utf8 (#X, 0);\
STORE("sourcemark_type", sv); \
break;
SM_TYPES_LIST
#undef sm_type
/* for SM_type_none */
default:
break;
}
av_push (av, newRV_noinc ((SV *)source_mark));
#undef STORE
}
}
}
static void
setup_info_hv (ELEMENT *e, HV **info_hv)
{
dTHX;
if (*info_hv == 0)
{
HV *element_hv = (HV *) SvRV ((SV*) e->sv);
*info_hv = (HV *) newHV ();
hv_store (element_hv, "info", strlen ("info"),
newRV_noinc ((SV *)*info_hv), HSH_info);
}
}
static void
store_info_string (ELEMENT *e, const char *string,
const char *key, HV **info_hv)
{
dTHX;
if (!string)
return;
setup_info_hv (e, info_hv);
hv_store (*info_hv, key, strlen (key),
newSVpv_utf8 (string, strlen (string)), 0);
}
static void
store_info_integer (ELEMENT *e, int value,
const char *key, HV **info_hv)
{
dTHX;
setup_info_hv (e, info_hv);
hv_store (*info_hv, key, strlen (key), newSViv (value), 0);
}
static void
store_extra_flag (ELEMENT *e, const char *key, HV **extra_hv)
{
dTHX;
if (*extra_hv == 0)
{
HV *element_hv = (HV *) SvRV ((SV*) e->sv);
*extra_hv = (HV *) newHV ();
hv_store (element_hv, "extra", strlen ("extra"),
newRV_noinc ((SV *)*extra_hv), HSH_extra);
}
hv_store (*extra_hv, key, strlen (key), newSViv (1), 0);
}
void
pass_source_info_hash (const SOURCE_INFO *source_info, HV *hv)
{
#define STORE(key, sv, hsh) hv_store (hv, key, strlen (key), sv, hsh)
dTHX;
if (source_info->file_name)
{
STORE("file_name", newSVpv (source_info->file_name, 0),
HSH_file_name);
}
if (source_info->line_nr)
{
STORE("line_nr", newSViv (source_info->line_nr), HSH_line_nr);
}
if (source_info->macro)
{
STORE("macro", newSVpv_utf8 (source_info->macro, 0), HSH_macro);
}
}
static void
build_base_element (ELEMENT *e, HV *hv)
{
SV *sv;
const char *cmdname;
dTHX;
if (type_data[e->type].flags & TF_text)
{
if (e->type != ET_normal_text)
{
sv = newSVpv (type_data[e->type].name, 0);
STORE("type", sv, HSH_type);
}
sv = newSVpv_utf8 (e->e.text->text, e->e.text->end);
STORE("text", sv, HSH_text);
return;
}
/* non-text elements */
if (e->type
&& !(type_data[e->type].flags & TF_c_only))
{
sv = newSVpv (type_data[e->type].name, 0);
STORE("type", sv, HSH_type);
}
cmdname = element_command_name (e);
if (cmdname)
{
/* Note we could optimize the call to newSVpv here and
elsewhere by passing an appropriate second argument. */
sv = newSVpv (cmdname, 0);
STORE("cmdname", sv, HSH_cmdname);
}
#undef STORE
}
void
build_new_base_element (ELEMENT *element)
{
dTHX;
if (!element->sv)
{
HV *element_hv = new_element_perl_data (element);
build_base_element (element, element_hv);
}
}
/* Set E->sv and 'hv' on E's descendants. e->parent->sv is assumed
to already exist. */
/* If AVOID_RECURSION is set, recurse in children elements only if
hv is not set */
void
element_to_perl_hash (ELEMENT *e, int avoid_recursion)
{
SV *sv;
HV *info_hv = 0;
HV *extra_hv = 0;
HV *element_hv;
const char *cmdname;
dTHX;
/*
fprintf (stderr, "ETPH %p %s\n", e, print_element_debug (e, 0));
*/
/* e->sv may already exist if there was an extra value elsewhere
referring to e (if there are references to in-tree elements in extra,
which may not be the case), or if the tree is rebuilt. */
if (!e->sv)
{
element_hv = new_element_perl_data (e);
}
else
{
/* reset for the case the element already exists, it is simpler than
resetting every unset fields */
element_hv = (HV *) SvRV ((SV *) e->sv);
hv_clear (element_hv);
}
if (!hashes_ready)
{
hashes_ready = 1;
PERL_HASH(HSH_parent, "parent", strlen ("parent"));
PERL_HASH(HSH_type, "type", strlen ("type"));
PERL_HASH(HSH_cmdname, "cmdname", strlen ("cmdname"));
PERL_HASH(HSH_contents, "contents", strlen ("contents"));
PERL_HASH(HSH_text, "text", strlen ("text"));
PERL_HASH(HSH_extra, "extra", strlen ("extra"));
PERL_HASH(HSH_info, "info", strlen ("info"));
PERL_HASH(HSH_source_info, "source_info", strlen ("source_info"));
PERL_HASH(HSH_file_name, "file_name", strlen ("file_name"));
PERL_HASH(HSH_line_nr, "line_nr", strlen ("line_nr"));
PERL_HASH(HSH_macro, "macro", strlen ("macro"));
}
if (e->flags & EF_inserted)
store_info_integer (e, 1, "inserted", &info_hv);
build_base_element (e, element_hv);
store_source_mark_list (e);
if (type_data[e->type].flags & TF_text)
return;
if (e->e.c->parent)
{
if (!e->e.c->parent->sv)
{
static TEXT message;
char *debug_str = print_element_debug (e, 1);
text_init (&message);
text_printf (&message, "parent %p sv not set in %s '%s'\n",
e->e.c->parent, debug_str, convert_to_texinfo (e));
fatal (message.text);
non_perl_free (debug_str);
}
/* copy the SV instead of simply reusing it, otherwise the changes
to the corresponding reference in Perl will affect all the
references. See:
https://lists.gnu.org/archive/html/bug-texinfo/2025-06/msg00018.html
*/
sv = newSVsv ((SV *) e->e.c->parent->sv);
hv_store (element_hv, "parent", strlen ("parent"), sv, HSH_parent);
}
#define store_flag(flag) \
if (e->flags & EF_##flag) \
store_extra_flag (e, #flag, &extra_hv); \
/* node */
store_flag(isindex)
/* node, anchor, float */
store_flag(is_target)
/* def_line for block/line for @def*x */
store_flag(omit_def_name_space)
/* @def*x */
store_flag(not_after_command)
/* @*table */
store_flag(command_as_argument_kbd_code)
store_flag(invalid_syntax)
/* kbd */
store_flag(code)
/* ET_paragraph */
store_flag(indent)
/* ET_paragraph */
store_flag(noindent)
#undef store_flag
cmdname = element_command_name (e);
/* process info_string array */
if (cmdname)
{
store_info_string (e, e->e.c->string_info[sit_alias_of],
"alias_of", &info_hv);
if (e->e.c->cmd == CM_verb && e->e.c->contents.number > 0)
store_info_string (e, e->e.c->string_info[sit_delimiter],
"delimiter", &info_hv);
}
/* process elt_info array */
if (type_data[e->type].elt_info_number > 0)
{
int i;
for (i = 0; i < type_data[e->type].elt_info_number; i++)
{
ELEMENT *info_element = e->elt_info[i];
if (info_element)
{
if (!info_element->sv || !avoid_recursion)
element_to_perl_hash (info_element, avoid_recursion);
setup_info_hv (e, &info_hv);
hv_store (info_hv, elt_info_names[i],
strlen (elt_info_names[i]),
newSVsv ((SV *)info_element->sv), 0);
}
}
}
if (e->e.c->contents.number > 0)
{
AV *av;
size_t i;
av = newAV ();
sv = newRV_noinc ((SV *) av);
av_unshift (av, e->e.c->contents.number);
hv_store (element_hv, "contents", strlen ("contents"), sv, HSH_contents);
for (i = 0; i < e->e.c->contents.number; i++)
{
ELEMENT *child = e->e.c->contents.list[i];
if (!child->sv || !avoid_recursion)
element_to_perl_hash (child, avoid_recursion);
av_store (av, i, newSVsv ((SV *) child->sv));
}
}
store_extra_additional_info (e, &e->e.c->extra_info,
avoid_recursion, &extra_hv);
if (e->e.c->associated_unit)
{
/* output_unit_to_perl_hash uses the unit_contents elements hv,
so we may want to setup the tree hv before building the output
units. In that case, the output unit hv is not ready, so here
we do not error out if the hv is not set.
*/
if (e->e.c->associated_unit->hv)
{
hv_store (element_hv, "associated_unit", strlen ("associated_unit"),
newRV_inc ((SV *) e->e.c->associated_unit->hv), 0);
}
}
if (e->e.c->source_info.line_nr)
{
const SOURCE_INFO *source_info = &e->e.c->source_info;
HV *hv = newHV ();
pass_source_info_hash (source_info, hv);
hv_store (element_hv, "source_info", strlen ("source_info"),
newRV_noinc ((SV *)hv), HSH_source_info);
}
}
SV *
build_texinfo_tree (ELEMENT *root, int avoid_recursion)
{
/* should not happen because caller should make sure to call with a tree */
if (! root)
return 0;
/*
fprintf (stderr, "BTT ------------------------------------------------\n");
*/
if (!root->sv || !avoid_recursion)
element_to_perl_hash (root, avoid_recursion);
return root->sv;
}
void
build_tree_to_build (ELEMENT_LIST *tree_to_build)
{
if (tree_to_build->number > 0)
{
size_t i;
for (i = 0; i < tree_to_build->number; i++)
{
build_texinfo_tree (tree_to_build->list[i], 1);
}
tree_to_build->number = 0;
}
}
/* languages and translations */
HV *
build_lang_info (const DOCUMENT_LANG_INFO *lang_info)
{
HV *lang_info_hv;
dTHX;
#define STORE(key,sv) hv_store (lang_info_hv, #key, strlen(#key), sv, 0);
lang_info_hv = newHV ();
if (lang_info->lang)
STORE(lang, newSVpv (lang_info->lang, 0));
if (lang_info->region)
STORE(region, newSVpv (lang_info->region, 0));
if (lang_info->script)
STORE(script, newSVpv (lang_info->script, 0));
if (lang_info->variants.number > 0)
{
AV *variants_av = build_string_list (&lang_info->variants, svt_byte);
SV *sv = newRV_noinc ((SV *) variants_av);
STORE(variants, sv);
}
STORE(bcp47_locale, newSVpv (lang_info->bcp47_locale, 0));
#undef STORE
return lang_info_hv;
}
/* build nodes, sections... relations */
#define STORE_RELS_INFO_ELEMENT(keyname) \
if (relations->keyname) \
{ \
sv = newSVsv ((SV *) relations->keyname->sv); \
hv_store (relations_hv, #keyname, \
strlen (#keyname), sv, 0); \
}
#define STORE_RELS_INFO_SECTION_RELATIONS(keyname) \
if (relations->keyname) \
{ \
if (!relations->keyname->hv) \
{ \
SECTION_RELATIONS *section = (SECTION_RELATIONS *) \
relations->keyname; \
section->hv = newHV (); \
} \
sv = newRV_inc ((SV *) relations->keyname->hv); \
hv_store (relations_hv, #keyname, \
strlen (#keyname), sv, 0); \
}
#define STORE_RELS_INFO_NODE_RELATIONS(keyname) \
if (relations->keyname) \
{ \
if (!relations->keyname->hv) \
{ \
NODE_RELATIONS *node = (NODE_RELATIONS *) \
relations->keyname; \
node->hv = newHV (); \
} \
sv = newRV_inc ((SV *) relations->keyname->hv); \
hv_store (relations_hv, #keyname, \
strlen (#keyname), sv, 0); \
}
static void
build_node_relations (NODE_RELATIONS *relations)
{
HV *relations_hv;
SV *sv;
dTHX;
if (!relations->hv)
{
relations->hv = newHV ();
}
relations_hv = relations->hv;
sv = newSVsv ((SV *) relations->element->sv);
hv_store (relations_hv, "element", strlen ("element"), sv, 0);
STORE_RELS_INFO_SECTION_RELATIONS(associated_section)
STORE_RELS_INFO_ELEMENT(associated_title_command)
STORE_RELS_INFO_SECTION_RELATIONS(node_preceding_part)
STORE_RELS_INFO_ELEMENT(node_description)
STORE_RELS_INFO_ELEMENT(node_long_description)
if (relations->menus)
{
sv = build_perl_const_element_array (relations->menus, 0);
hv_store (relations_hv, "menus", strlen ("menus"), sv, 0);
}
if (relations->node_directions)
{
sv = build_perl_directions (relations->node_directions, 0);
hv_store (relations_hv, "node_directions",
strlen ("node_directions"), sv, 0);
}
}
AV *
build_node_relations_list (const NODE_RELATIONS_LIST *list)
{
AV *list_av;
size_t i;
dTHX;
list_av = newAV ();
av_unshift (list_av, list->number);
for (i = 0; i < list->number; i++)
{
NODE_RELATIONS *relations = list->list[i];
build_node_relations (relations);
/* In case the HV was just created, keep the reference created by
newHV instead of transferring it to the list_av, considering
that it is associated to the C code */
av_store (list_av, i, newRV_inc ((SV *) relations->hv));
}
return list_av;
}
static HV *
build_perl_section_directions (const SECTION_RELATIONS * const *s_d)
{
HV *hv;
size_t d;
dTHX;
hv = newHV ();
for (d = 0; d < directions_length; d++)
{
if (s_d[d])
{
const char *key = direction_names[d];
if (!s_d[d]->hv)
{
/* cast to modify */
SECTION_RELATIONS *relations = (SECTION_RELATIONS *)s_d[d];
relations->hv = newHV ();
}
hv_store (hv, key, strlen (key),
newRV_inc ((SV *) s_d[d]->hv), 0);
}
}
return hv;
}
static AV *
build_perl_section_relations_array (const SECTION_RELATIONS_LIST *list)
{
AV *av;
size_t i;
dTHX;
av = newAV ();
for (i = 0; i < list->number; i++)
{
SECTION_RELATIONS *relations = list->list[i];
if (!relations->hv)
relations->hv = newHV ();
av_store (av, (SSize_t) i, newRV_inc ((SV *) relations->hv));
}
return av;
}
static void
build_section_relations (SECTION_RELATIONS *relations)
{
HV *relations_hv;
HV *hv;
SV *sv;
dTHX;
if (!relations->hv)
{
relations->hv = newHV ();
}
relations_hv = relations->hv;
sv = newSVsv ((SV *) relations->element->sv);
hv_store (relations_hv, "element", strlen ("element"), sv, 0);
STORE_RELS_INFO_NODE_RELATIONS(associated_node)
STORE_RELS_INFO_NODE_RELATIONS(associated_anchor_command)
STORE_RELS_INFO_SECTION_RELATIONS(associated_part)
STORE_RELS_INFO_SECTION_RELATIONS(part_associated_section)
STORE_RELS_INFO_NODE_RELATIONS(part_following_node)
if (relations->section_directions)
{
hv = build_perl_section_directions (relations->section_directions);
hv_store (relations_hv, "section_directions",
strlen ("section_directions"), newRV_noinc ((SV *) hv), 0);
}
if (relations->toplevel_directions)
{
hv = build_perl_section_directions (relations->toplevel_directions);
hv_store (relations_hv, "toplevel_directions",
strlen ("toplevel_directions"), newRV_noinc ((SV *) hv), 0);
}
if (relations->section_children)
{
AV *av = build_perl_section_relations_array (relations->section_children);
hv_store (relations_hv, "section_children",
strlen ("section_children"), newRV_noinc ((SV *) av), 0);
}
}
AV *
build_section_relations_list (const SECTION_RELATIONS_LIST *list)
{
AV *list_av;
size_t i;
dTHX;
list_av = newAV ();
av_unshift (list_av, list->number);
for (i = 0; i < list->number; i++)
{
SECTION_RELATIONS *relations = list->list[i];
build_section_relations (relations);
/* In case the HV was just created, keep the reference created by
newHV instead of transferring it to the list_av, considering
that it is associated to the C code */
av_store (list_av, i, newRV_inc ((SV *) relations->hv));
}
return list_av;
}
AV *
build_heading_relations_list (const HEADING_RELATIONS_LIST *list)
{
AV *list_av;
SV *sv;
size_t i;
dTHX;
list_av = newAV ();
av_unshift (list_av, list->number);
for (i = 0; i < list->number; i++)
{
HEADING_RELATIONS *relations = list->list[i];
HV *relations_hv;
if (!relations->hv)
relations->hv = newHV ();
relations_hv = relations->hv;
sv = newSVsv ((SV *) relations->element->sv);
hv_store (relations_hv, "element", strlen ("element"), sv, 0);
STORE_RELS_INFO_NODE_RELATIONS(associated_anchor_command)
av_store (list_av, i, newRV_inc ((SV *) relations_hv));
}
return list_av;
}
#undef STORE_RELS_INFO_ELEMENT
/* currently unused */
AV *
build_integer_stack (const INTEGER_STACK *integer_stack)
{
AV *av;
size_t i;
dTHX;
av = newAV ();
for (i = 0; i < integer_stack->top; i++)
{
int value = integer_stack->stack[i];
av_push (av, newSViv (value));
}
return av;
}
/* build error messages data to Perl, for Parser, Document and Converters */
/* build perl already 'formatted' message, same as the output of
Texinfo::Report::format*message */
static SV *
convert_error (const ERROR_MESSAGE e)
{
HV *hv;
SV *msg;
SV *err_line;
dTHX;
hv = newHV ();
msg = newSVpv_utf8 (e.message, 0);
err_line = newSVpv_utf8 (e.error_line, 0);
hv_store (hv, "text", strlen ("text"), msg, 0);
hv_store (hv, "error_line", strlen ("error_line"), err_line, 0);
hv_store (hv, "type", strlen ("type"),
(e.type == MSG_error || e.type == MSG_document_error)
? newSVpv ("error", strlen ("error"))
: newSVpv ("warning", strlen ("warning")),
0);
if (e.continuation)
hv_store (hv, "continuation", strlen ("continuation"),
newSViv (e.continuation), 0);
if (e.type != MSG_document_error && e.type != MSG_document_warning)
pass_source_info_hash (&e.source_info, hv);
return newRV_noinc ((SV *) hv);
}
/* Errors */
/* ERROR_MESSAGES_LIST passed must be non-NULL
*/
void
pass_errors (const ERROR_MESSAGE_LIST *error_list, AV *av)
{
size_t i;
dTHX;
for (i = 0; i < error_list->number; i++)
{
SV *sv = convert_error (error_list->list[i]);
av_push (av, sv);
}
}
/* If KEY is NULL, "error_messages" is used.
Get the object_sv HV hash value associated to KEY if it exists, and if it
does not, create an array associated to KEY.
Return a reference to the array.
if ERROR_MESSAGES is set, add the error messages to the array before
returning its reference.
*/
SV *
pass_errors_to_hv (const ERROR_MESSAGE_LIST *error_messages, SV *object_sv,
const char *key)
{
HV *object_hv;
SV **error_messages_sv;
AV *report_av;
const char *error_messages_key = "error_messages";
dTHX;
if (key)
error_messages_key = key;
object_hv = (HV *) SvRV (object_sv);
error_messages_sv = hv_fetch (object_hv, error_messages_key,
strlen (error_messages_key), 0);
/* An 'error_messages' is systematically added to document at
initialization, so the condition should always be true for a Document
object_hv.
For a Parser, however, it is only added when needed, so it should
need to be created here.
*/
if (error_messages_sv)
report_av = (AV *) SvRV (*error_messages_sv);
else
{
report_av = newAV ();
hv_store (object_hv, error_messages_key, strlen (error_messages_key),
newRV_noinc ((SV *) report_av), 0);
}
if (error_messages)
pass_errors (error_messages, report_av);
return newRV_inc ((SV *) report_av);
}
/* Build data registered in Texinfo Document to Perl and Document */
/* Return array of target elements. build_texinfo_tree must
be called first. */
static AV *
build_target_elements_list (const LABEL_LIST *labels_list)
{
AV *target_array;
SV *sv;
size_t i;
dTHX;
target_array = newAV ();
av_unshift (target_array, labels_list->number);
for (i = 0; i < labels_list->number; i++)
{
sv = newSVsv (labels_list->list[i].element->sv);
av_store (target_array, i, sv);
}
return target_array;
}
static HV *
build_identifiers_target (const struct C_HASHMAP *identifiers_target)
{
HV* hv;
dTHX;
hv = newHV ();
if (identifiers_target)
{
struct BUCKET_ARENA_ITERATOR *hash_iterator = 0;
const char *key;
const ELEMENT *element;
while (1)
{
element = c_hashmap_iterator_next_value (identifiers_target,
&hash_iterator, &key);
if (!key)
break;
SV *sv = newSVsv (element->sv);
hv_store (hv, key, strlen (key), sv, 0);
}
}
return hv;
}
static AV *
build_internal_xref_list (const ELEMENT_LIST *internal_xref_list)
{
AV *list_av;
SV *sv;
size_t i;
dTHX;
list_av = newAV ();
av_unshift (list_av, internal_xref_list->number);
for (i = 0; i < internal_xref_list->number; i++)
{
sv = newSVsv (internal_xref_list->list[i]->sv);
av_store (list_av, i, sv);
}
return list_av;
}
/* Return hash for list of @float's that appeared in the file. */
static HV *
build_listoffloats_list (LISTOFFLOATS_TYPE_LIST *listoffloats)
{
HV *float_hash;
SV *sv;
size_t i;
dTHX;
float_hash = newHV ();
for (i = 0; i < listoffloats->number; i++)
{
size_t j;
LISTOFFLOATS_TYPE *listoffloat = &listoffloats->float_types[i];
FLOAT_INFORMATION_LIST *float_list = &listoffloat->float_list;
SV *float_type = newSVpv_utf8 (listoffloat->type, 0);
AV *av = newAV ();
hv_store_ent (float_hash, float_type,
newRV_noinc ((SV *)av), 0);
for (j = 0; j < float_list->number; j++)
{
const FLOAT_INFORMATION *float_info = &float_list->list[j];
const ELEMENT *float_elt = float_info->float_element;
const SECTION_RELATIONS *float_section = float_info->float_section;
AV *float_section_av = newAV ();
sv = newSVsv ((SV *)float_elt->sv);
av_push (float_section_av, sv);
if (float_section)
{
if (!float_section->hv)
fatal ("Need to build sections first");
sv = newRV_inc ((SV *)float_section->hv);
av_push (float_section_av, sv);
}
else
av_push (float_section_av, newSV (0));
av_push (av, newRV_noinc ((SV *) float_section_av));
}
}
return float_hash;
}
#define STORE2(key, value) hv_store (entry_hv, key, strlen (key), value, 0)
HV *
build_index_entry (const INDEX_ENTRY *index_entry)
{
HV *entry_hv;
dTHX;
entry_hv = newHV ();
STORE2("index_name", newSVpv_utf8 (index_entry->index_name, 0));
STORE2("entry_element",
newSVsv ((SV *)index_entry->entry_element->sv));
if (index_entry->entry_associated_element)
STORE2("entry_associated_element",
newSVsv ((SV *)index_entry->entry_associated_element->sv));
/* NOTE theoretical IV overflow if PERL_QUAD_MAX < SIZE_MAX */
STORE2("entry_number", newSViv ((IV) index_entry->number));
return entry_hv;
}
#undef STORE2
/* returns a hash for a single entry in $self->{'index_names'}, containing
information about a single index. */
HV *
build_single_index_data (const INDEX *index)
{
#define STORE(key, value) hv_store (hv, key, strlen (key), value, 0)
HV *hv;
AV *entries;
size_t j;
dTHX;
hv = newHV ();
STORE("name", newSVpv_utf8 (index->name, 0));
STORE("in_code", index->in_code ? newSViv (1) : newSViv (0));
if (index->merged_in)
STORE("merged_in", newSVpv_utf8 (index->merged_in->name, 0));
if (index->entries_number > 0)
{
entries = newAV ();
av_unshift (entries, index->entries_number);
STORE("index_entries", newRV_noinc ((SV *) entries));
for (j = 0; j < index->entries_number; j++)
{
HV *entry_hv = build_index_entry (&index->index_entries[j]);
av_store (entries, j, newRV_noinc ((SV *)entry_hv));
}
}
return hv;
}
/* build information from index, selecting only the information useful
when looking at the index to complement the information available
with an index entry */
HV *
build_single_index_info (const INDEX *index)
{
HV *hv;
dTHX;
hv = newHV ();
STORE("name", newSVpv_utf8 (index->name, 0));
STORE("in_code", index->in_code ? newSViv (1) : newSViv (0));
if (index->merged_in)
STORE("merged_in", newSVpv_utf8 (index->merged_in->name, 0));
return hv;
}
#undef STORE
/* Return object to be used as $self->{'index_names'} in the perl code.
build_texinfo_tree must be called before this so all the 'hv' fields
are set on the elements in the tree. */
static HV *
build_index_data (const INDEX_LIST *indices_info)
{
size_t i;
HV *hv;
dTHX;
hv = newHV ();
for (i = 0; i < indices_info->number; i++)
{
const INDEX *idx = indices_info->list[i];
HV *hv2 = build_single_index_data (idx);
hv_store (hv, idx->name, strlen (idx->name),
newRV_noinc ((SV *)hv2), 0);
}
return hv;
}
/* ALTIMP Texinfo/ParserNonXS.pm get_parser_info */
void
pass_global_info (HV *hv, const GLOBAL_INFO *global_info_ref,
const GLOBAL_COMMANDS *global_commands_ref)
{
const GLOBAL_INFO global_info = *global_info_ref;
const GLOBAL_COMMANDS global_commands = *global_commands_ref;
size_t i;
dTHX;
if (global_info.input_encoding_name)
hv_store (hv, "input_encoding_name", strlen ("input_encoding_name"),
newSVpv (global_info.input_encoding_name, 0), 0);
if (global_info.input_file_name)
hv_store (hv, "input_file_name", strlen ("input_file_name"),
newSVpv (global_info.input_file_name, 0), 0);
if (global_info.input_directory)
hv_store (hv, "input_directory", strlen ("input_directory"),
newSVpv (global_info.input_directory, 0), 0);
if (global_info.included_files.number)
{
AV *av = build_string_list (&global_info.included_files, svt_byte);
hv_store (hv, "included_files", strlen ("included_files"),
newRV_noinc ((SV *) av), 0);
}
for (i = 0; i < global_info.other_info.info_number; i++)
{
const KEY_STRING_PAIR *k = &global_info.other_info.info[i];
hv_store (hv, k->key, strlen (k->key), newSVpv_utf8 (k->string, 0), 0);
}
/* duplicate information with global_commands to avoid needing to use
global_commands and build tree elements in other codes, for
information useful for structuring and transformation codes */
if (global_commands.novalidate)
hv_store (hv, "novalidate", strlen ("novalidate"),
newSViv (1), 0);
if (global_commands.setfilename)
{
enum command_id cmd;
const char *setfilename_text
= informative_command_value (global_commands.setfilename, &cmd);
if (setfilename_text)
hv_store (hv, "setfilename", strlen ("setfilename"),
newSVpv_utf8 (setfilename_text, 0), 0);
}
if (global_info.preamble_lang_cmd.number > 0)
{
AV *preamble_lang_av = newAV ();
for (i = 0; i < global_info.preamble_lang_cmd.number; i++)
{
const PREAMBLE_LANG_CMD *preamble_lang_cmd
= &global_info.preamble_lang_cmd.list[i];
AV *preamble_lang_cmd_av = newAV ();
av_push (preamble_lang_av,
newRV_noinc ((SV *) preamble_lang_cmd_av));
const char *cmdname = builtin_command_name (preamble_lang_cmd->cmd);
av_push (preamble_lang_cmd_av, newSVpv (cmdname, 0));
if (preamble_lang_cmd->cmd == CM_documentlanguagevariant)
{
AV *av = build_string_list (
preamble_lang_cmd->plc.lang_variants, svt_byte);
av_push (preamble_lang_cmd_av, newRV_noinc ((SV *) av));
}
else
{
av_push (preamble_lang_cmd_av, newSVpv (
preamble_lang_cmd->plc.lang_string, 0));
}
}
hv_store (hv, "preamble_lang_cmd",
strlen ("preamble_lang_cmd"),
newRV_noinc ((SV *) preamble_lang_av), 0);
}
}
/* Return object to be used as 'commands_info', which holds references
to tree elements. */
static HV *
build_global_commands (const GLOBAL_COMMANDS *global_commands_ref)
{
HV *hv;
AV *av;
size_t i;
const GLOBAL_COMMANDS global_commands = *global_commands_ref;
dTHX;
hv = newHV ();
/* These should be unique elements. */
#define GLOBAL_UNIQUE_CASE(cmd) \
if (global_commands.cmd && global_commands.cmd->sv) \
{ \
hv_store (hv, #cmd, strlen (#cmd), \
newSVsv ((SV *) global_commands.cmd->sv), 0); \
}
GLOBAL_UNIQUE_CASE(setfilename);
#include "main/global_unique_commands_case.c"
#undef GLOBAL_UNIQUE_CASE
/* list of direntry and dircategory */
if (global_commands.dircategory_direntry.number > 0)
{
AV *av = newAV ();
hv_store (hv, "dircategory_direntry", strlen ("dircategory_direntry"),
newRV_noinc ((SV *) av), 0);
for (i = 0; i < global_commands.dircategory_direntry.number; i++)
{
const ELEMENT *e = global_commands.dircategory_direntry.list[i];
if (e->sv)
av_push (av, newSVsv ((SV *) e->sv));
}
}
if (global_commands.language_commands.number > 0)
{
AV *av = newAV ();
hv_store (hv, "language_commands", strlen ("language_commands"),
newRV_noinc ((SV *) av), 0);
for (i = 0; i < global_commands.language_commands.number; i++)
{
const ELEMENT *e = global_commands.language_commands.list[i];
if (e->sv)
av_push (av, newSVsv ((SV *) e->sv));
}
}
/* The following are arrays of elements. */
if (global_commands.footnotes.number > 0)
{
av = newAV ();
hv_store (hv, "footnote", strlen ("footnote"),
newRV_noinc ((SV *) av), 0);
for (i = 0; i < global_commands.footnotes.number; i++)
{
const ELEMENT *e = global_commands.footnotes.list[i];
if (e->sv)
av_push (av, newSVsv ((SV *) e->sv));
}
}
/* float is a type, it does not work there, use floats instead */
if (global_commands.floats.number > 0)
{
av = newAV ();
hv_store (hv, "float", strlen ("float"),
newRV_noinc ((SV *) av), 0);
for (i = 0; i < global_commands.floats.number; i++)
{
const ELEMENT *e = global_commands.floats.list[i];
if (e->sv)
av_push (av, newSVsv ((SV *) e->sv));
}
}
#define GLOBAL_CASE(cmd) \
if (global_commands.cmd.number > 0) \
{ \
av = newAV (); \
hv_store (hv, #cmd, strlen (#cmd), \
newRV_noinc ((SV *) av), 0); \
for (i = 0; i < global_commands.cmd.number; i++) \
{ \
const ELEMENT *e = global_commands.cmd.list[i]; \
if (e->sv) \
av_push (av, newSVsv ((SV *) e->sv)); \
} \
}
#include "global_multi_commands_case.c"
#undef GLOBAL_CASE
return hv;
}
/* build a minimal document, without tree/global commands/indices, only
with the document descriptor information, errors and information that do
not refer directly to tree elements */
SV *
build_minimal_document (DOCUMENT *document)
{
HV *hv_stash;
HV *hv;
SV *sv;
HV *hv_info;
AV *messages_list_av;
dTHX;
/* We do not attempt to reuse a pre-existing C document hv, as
build_minimal_document is only called on documents that were just
created and do not already have associated hv */
/* There is a bug message below if there is already a C document hv */
/* Reference held by the C code released at document destruction */
hv = newHV ();
if (document->tree)
{
HV *hv_tree = newHV ();
HV *hv_stash = gv_stashpv ("Texinfo::TreeElement", GV_ADD);
/* at this point there is no reference retained in C as the reference
on Perl object is not already stored in C element structure data */
SV *tree_sv = newRV_noinc ((SV *) hv_tree);
sv_bless (tree_sv, hv_stash);
hv_store (hv, "tree", strlen ("tree"), tree_sv, 0);
hv_store (hv_tree, "tree_document_descriptor",
strlen ("tree_document_descriptor"),
newSViv (document->descriptor), 0);
}
hv_info = newHV ();
pass_global_info (hv_info, &document->global_info,
&document->global_commands);
hv_store (hv, "global_info", strlen ("global_info"),
newRV_noinc ((SV *) hv_info), 0);
document->modified_information &= ~F_DOCM_global_info;
hv_store (hv, "document_descriptor", strlen ("document_descriptor"),
newSViv (document->descriptor), 0);
/* New error messages list for document to be used after parsing, for
structuring and tree modifications */
messages_list_av = newAV ();
hv_store (hv, "error_messages", strlen ("error_messages"),
newRV_noinc ((SV *) messages_list_av), 0);
if (!document->hv)
{
document->hv = (void *) hv;
/* a new reference returned to keep the reference held in C */
SvREFCNT_inc ((SV *) hv);
}
else
{
fprintf (stderr,
"BUG: build_minimal_document: %zu: already %p and new %p document hv\n",
document->descriptor, document->hv, hv);
}
hv_stash = gv_stashpv ("Texinfo::Document", GV_ADD);
sv = newRV_noinc ((SV *) hv);
sv_bless (sv, hv_stash);
return sv;
}
static HV *
build_sectioning_root (SECTIONING_ROOT *sectioning_root)
{
HV *hv = 0;
AV *av;
dTHX;
hv = newHV ();
av = build_perl_section_relations_array (
&sectioning_root->section_children);
hv_store (hv, "section_children", strlen ("section_children"),
newRV_noinc ((SV *) av), 0);
hv_store (hv, "section_root_level", strlen ("section_root_level"),
newSViv (sectioning_root->section_root_level), 0);
return hv;
}
static void
fill_document_hv (HV *hv, DOCUMENT *document)
{
SV *sv_tree = 0;
HV *hv_info;
HV *hv_commands_info;
HV *hv_index_names;
HV *hv_listoffloats_list;
HV *hv_indices_sort_strings = 0;
AV *av_internal_xref;
HV *hv_identifiers_target;
AV *av_labels_list;
AV *av_nodes_list = 0;
AV *av_sections_list = 0;
AV *av_headings_list = 0;
HV *hv_sectioning_root = 0;
dTHX;
if (document->tree)
sv_tree = build_texinfo_tree (document->tree, 0);
hv_info = newHV ();
pass_global_info (hv_info, &document->global_info,
&document->global_commands);
hv_commands_info = build_global_commands (&document->global_commands);
hv_index_names = build_index_data (&document->indices_info);
hv_listoffloats_list
= build_listoffloats_list (&document->listoffloats);
av_internal_xref = build_internal_xref_list (&document->internal_references);
hv_identifiers_target
= build_identifiers_target (&document->identifiers_target);
av_labels_list = build_target_elements_list (&document->labels_list);
av_nodes_list = build_node_relations_list (&document->nodes_list);
av_sections_list = build_section_relations_list (&document->sections_list);
av_headings_list = build_heading_relations_list (&document->headings_list);
if (document->sectioning_root)
hv_sectioning_root = build_sectioning_root (document->sectioning_root);
if (document->indices_sort_strings)
hv_indices_sort_strings = build_indices_sort_strings (
document->indices_sort_strings,
hv_index_names);
#define STORE(key, value) hv_store (hv, key, strlen (key), newRV_noinc ((SV *) value), 0)
/* must be kept in sync with Texinfo::Document register keys */
if (sv_tree)
hv_store (hv, "tree", strlen ("tree"), newSVsv ((SV *) sv_tree), 0);
document->modified_information &= ~F_DOCM_tree;
STORE("indices", hv_index_names);
document->modified_information &= ~F_DOCM_index_names;
STORE("listoffloats_list", hv_listoffloats_list);
document->modified_information &= ~F_DOCM_floats;
STORE("internal_references", av_internal_xref);
document->modified_information &= ~F_DOCM_internal_references;
STORE("commands_info", hv_commands_info);
document->modified_information &= ~F_DOCM_global_commands;
STORE("global_info", hv_info);
document->modified_information &= ~F_DOCM_global_info;
STORE("identifiers_target", hv_identifiers_target);
document->modified_information &= ~F_DOCM_identifiers_target;
STORE("labels_list", av_labels_list);
document->modified_information &= ~F_DOCM_labels_list;
STORE("nodes_list", av_nodes_list);
document->modified_information &= ~F_DOCM_nodes_list;
STORE("sections_list", av_sections_list);
document->modified_information &= ~F_DOCM_sections_list;
if (hv_sectioning_root)
{
STORE("sectioning_root", hv_sectioning_root);
document->modified_information &= ~F_DOCM_sectioning_root;
}
STORE("headings_list", av_headings_list);
document->modified_information &= ~F_DOCM_headings_list;
if (hv_indices_sort_strings)
{
STORE("index_entries_sort_strings", hv_indices_sort_strings);
document->modified_information &= ~F_DOCM_indices_sort_strings;
}
#undef STORE
}
/* Return a Texinfo::Document perl object corresponding to the
C document structure corresponding to DOCUMENT.
If NO_STORE is set, destroy the C document.
*/
SV *
build_document (DOCUMENT *document, int no_store)
{
HV *hv;
SV *sv;
HV *hv_stash;
dTHX;
if (document->hv)
hv = document->hv;
else
{
/* we go there through parse_texi_line called with no_store set as is
the case in Translations.pm */
/* reference retained in C, unless the document is not stored */
hv = newHV ();
/* error messages list for document to be used after parsing, for
structuring and tree modifications */
AV *messages_list_av = newAV ();
hv_store (hv, "error_messages", strlen ("error_messages"),
newRV_noinc ((SV *) messages_list_av), 0);
}
fill_document_hv (hv, document);
if (no_store)
{
if (document->hv)
/* This situation happens if a document is built first through
parsing Texinfo file or string (as a minimal document) and
afterwards build_tree is called with no_store set to 1. This
makes sure that the tree is not associated to C anymore, such
that modifications in Perl are not forgotten when a tree
unmodified is returned from C. Pod-Simple-Texinfo is an example
where this happens.
*/
/* take ownership of a document reference before having it
released by destroy_document, to avoid going through 0 and
also to have a reference to release to the caller.
*/
SvREFCNT_inc ((SV *) hv);
destroy_document (document);
}
else
{
hv_store (hv, "document_descriptor", strlen ("document_descriptor"),
newSViv (document->descriptor), 0);
if (document->tree && document->tree->sv)
{
HV *hv_tree = (HV *) SvRV ((SV *) document->tree->sv);
hv_store (hv_tree, "tree_document_descriptor",
strlen ("tree_document_descriptor"),
newSViv (document->descriptor), 0);
}
if (!document->hv)
/* This situation cannot happen with the current code. Indeed,
the parse_texi* functions lead either to a minimal document
being built, with an hv set, or to a Perl only tree, with
no_store set (in parse_texi_line only) */
document->hv = (void *) hv;
/* a new reference returned to keep the reference held in C */
SvREFCNT_inc ((SV *) hv);
}
hv_stash = gv_stashpv ("Texinfo::Document", GV_ADD);
sv = newRV_noinc ((SV *) hv);
sv_bless (sv, hv_stash);
return sv;
}
void
store_document_texinfo_tree (DOCUMENT *document)
{
dTHX;
if (document->modified_information & F_DOCM_tree
&& document->tree)
{
const char *key = "tree";
SV *result_sv = build_texinfo_tree (document->tree, 0);
HV *result_hv = (HV *) SvRV (result_sv);
hv_store (result_hv, "tree_document_descriptor",
strlen ("tree_document_descriptor"),
newSViv (document->descriptor), 0);
hv_store (document->hv, key, strlen (key), newSVsv (result_sv), 0);
document->modified_information &= ~F_DOCM_tree;
}
/* systematically rebuild, as section relations
can be accessed from the tree. Done in this function,
as it is supposed to be called before an access to modified
tree and sectioning structure.
*/
/* Also store, such that next call that get cached values
get the right information */
if (document->modified_information & F_DOCM_sections_list)
{
const char *key = "sections_list";
AV *av_list
= build_section_relations_list (&document->sections_list);
hv_store (document->hv, key, strlen (key),
newRV_noinc ((SV *) av_list), 0);
document->modified_information &= ~F_DOCM_sections_list;
}
if (document->modified_information & F_DOCM_nodes_list)
{
const char *key = "nodes_list";
AV *av_list
= build_node_relations_list (&document->nodes_list);
hv_store (document->hv, key, strlen (key),
newRV_noinc ((SV *) av_list), 0);
document->modified_information &= ~F_DOCM_nodes_list;
}
if (document->modified_information & F_DOCM_headings_list)
{
const char *key = "headings_list";
AV *av_list
= build_heading_relations_list (&document->headings_list);
hv_store (document->hv, key, strlen (key),
newRV_noinc ((SV *) av_list), 0);
document->modified_information &= ~F_DOCM_headings_list;
}
}
/* Build Output unit and output units lists to Perl*/
static void
output_unit_to_perl_hash (OUTPUT_UNIT *output_unit)
{
int i;
SV *sv;
HV *directions_hv;
dTHX;
/* output_unit->hv may already exist because of directions or if there was a
first_in_page referring to output_unit, or because the output units
list is being rebuilt */
if (!output_unit->hv)
/* the reference created by newHV is considered to be retained by the
C code and is released when the output unit is destroyed in C */
output_unit->hv = newHV ();
else
hv_clear (output_unit->hv);
#define STORE(key) hv_store (output_unit->hv, key, strlen (key), sv, 0)
sv = newSVpv (output_unit_type_names[output_unit->unit_type], 0);
STORE("unit_type");
if (output_unit->unit_type == OU_special_unit)
{
ELEMENT *command = output_unit->uc.special_unit_command;
if (!command->sv)
{
SV *unit_sv;
HV *element_hv;
/* a virtual out of tree element, add it to perl */
element_to_perl_hash (command, 0);
element_hv = (HV *) SvRV ((SV *) command->sv);
unit_sv = newRV_inc ((SV *) output_unit->hv);
hv_store (element_hv, "associated_unit",
strlen ("associated_unit"), unit_sv, 0);
}
sv = newSVsv ((SV *) command->sv);
STORE("unit_command");
}
else
{
const ELEMENT *command = output_unit->uc.unit_command;
if (command)
{
if (!command->sv)
{
char *msg;
char *output_unit_text = output_unit_texi (output_unit);
xasprintf (&msg, "Missing output unit unit_command sv: %s",
output_unit_text);
non_perl_free (output_unit_text);
fatal (msg);
non_perl_free (msg);
}
sv = newSVsv ((SV *) command->sv);
STORE("unit_command");
}
if (output_unit->unit_section)
{
sv = newRV_inc ((SV *) output_unit->unit_section->hv);
STORE("unit_section");
}
if (output_unit->unit_node)
{
sv = newRV_inc ((SV *) output_unit->unit_node->hv);
STORE("unit_node");
}
/* there is nothing else of use for external_node_unit, exit now */
if (output_unit->unit_type == OU_external_node_unit)
return;
}
/* NOTE theoretical IV overflow if PERL_QUAD_MAX < SIZE_MAX */
sv = newSViv ((IV) output_unit->index);
STORE("unit_index");
/* setup an hash reference in any case */
directions_hv = newHV ();
sv = newRV_noinc ((SV *) directions_hv);
STORE("directions");
for (i = 0; i < RUD_type_FirstInFileNodeBack+1; i++)
{
if (output_unit->directions[i])
{
const char *direction_name = relative_unit_direction_name[i];
/* remove const in case hv needs to be added */
OUTPUT_UNIT *direction_unit
= (OUTPUT_UNIT *) output_unit->directions[i];
SV *unit_sv;
if (!direction_unit->hv)
{
/* If it is known in advance that Perl data needs to be rebuilt, the Perl
references should exist for all the output units because they are
setup and built to Perl if needed in _prepare_conversion_units,
while directions are setup afterwards in _prepare_units_directions_files.
external_node_target are not set in _prepare_conversion_units, but
are set before rebuilding the other output units in
_prepare_units_directions_files XS code.
However, if the output units are built late because they are built
to Perl from a user function, the output units were never built
to Perl and there are already directions that will point to output
units not already built to Perl, so it is not an error.
char *msg;
xasprintf (&msg, "BUG: %s: no output unit Perl ref: %s",
direction_name,
output_unit_texi (direction_unit));
fatal (msg);
non_perl_free (msg);
*/
direction_unit->hv = newHV ();
}
unit_sv = newRV_inc ((SV *) direction_unit->hv);
hv_store (directions_hv, direction_name, strlen (direction_name),
unit_sv, 0);
}
}
if (output_unit->associated_document_unit)
{
sv = newRV_inc ((SV *) output_unit->associated_document_unit->hv);
STORE("associated_document_unit");
}
if (output_unit->unit_filename)
{
sv = newSVpv_utf8 (output_unit->unit_filename,
strlen (output_unit->unit_filename));
STORE("unit_filename");
}
if (output_unit->unit_contents.number)
{
AV *av;
size_t i;
av = newAV ();
sv = newRV_noinc ((SV *) av);
STORE("unit_contents");
for (i = 0; i < output_unit->unit_contents.number; i++)
{
const ELEMENT *element = output_unit->unit_contents.list[i];
SV *element_sv;
SV *unit_sv;
if (!element->sv)
fatal ("Missing output unit unit_contents element sv");
element_sv = newSVsv ((SV *) element->sv);
av_push (av, element_sv);
if (element->e.c->associated_unit == output_unit)
{
HV *element_hv = (HV *) SvRV ((SV *) element_sv);
unit_sv = newRV_inc ((SV *) output_unit->hv);
/* set the tree element associated_unit */
hv_store (element_hv, "associated_unit",
strlen ("associated_unit"),
unit_sv, 0);
}
}
}
if (output_unit->tree_unit_directions[0]
|| output_unit->tree_unit_directions[1])
{
size_t i;
size_t directions_nr = sizeof (output_unit->tree_unit_directions)
/ sizeof (output_unit->tree_unit_directions[0]);
HV *hv_tree_unit_directions = newHV ();
sv = newRV_noinc ((SV *) hv_tree_unit_directions);
STORE("tree_unit_directions");
for (i = 0; i < directions_nr; i++)
{
OUTPUT_UNIT *target = output_unit->tree_unit_directions[i];
if (target)
{
if (!target->hv)
target->hv = newHV ();
sv = newRV_inc ((SV *) target->hv);
hv_store (hv_tree_unit_directions, direction_names[i],
strlen (direction_names[i]), sv, 0);
}
}
}
if (output_unit->first_in_page)
{
OUTPUT_UNIT *target = output_unit->first_in_page;
if (!target->hv)
target->hv = newHV ();
sv = newRV_inc ((SV *) target->hv);
STORE("first_in_page");
}
if (output_unit->special_unit_variety)
{
sv = newSVpv_utf8 (output_unit->special_unit_variety,
strlen (output_unit->special_unit_variety));
STORE("special_unit_variety");
}
#undef STORE
}
/* build output unit hashes but do not put output units hashes in
an array. Useful for external_nodes_units, which are to be
built to Perl, but have no array in Perl, they are only referred to
in directions. */
static void
output_units_list_to_perl_hash (const DOCUMENT *document,
size_t output_units_descriptor)
{
const OUTPUT_UNIT_LIST *output_units;
size_t i;
output_units = retrieve_output_units (document, output_units_descriptor);
if (!output_units || !output_units->number)
return;
for (i = 0; i < output_units->number; i++)
{
OUTPUT_UNIT *output_unit = output_units->list[i];
output_unit_to_perl_hash (output_unit);
}
}
static int
fill_output_units_descriptor_av (const DOCUMENT *document,
AV *av_output_units,
size_t output_units_descriptor)
{
const OUTPUT_UNIT_LIST *output_units;
size_t i;
dTHX;
output_units = retrieve_output_units (document, output_units_descriptor);
if (!output_units || !output_units->number)
return 0;
for (i = 0; i < output_units->number; i++)
{
SV *sv;
OUTPUT_UNIT *output_unit = output_units->list[i];
output_unit_to_perl_hash (output_unit);
/* keep the reference owned by the C code */
sv = newRV_inc ((SV *) output_unit->hv);
av_push (av_output_units, sv);
}
/* store in the first perl output unit of the list */
/* NOTE theoretical IV overflow if PERL_QUAD_MAX < SIZE_MAX */
hv_store (output_units->list[0]->hv, "output_units_descriptor",
strlen ("output_units_descriptor"),
newSViv ((IV)output_units_descriptor), 0);
hv_store (output_units->list[0]->hv, "output_units_document_descriptor",
strlen ("output_units_document_descriptor"),
newSViv ((IV)document->descriptor), 0);
return 1;
}
SV *
build_output_units_list (const DOCUMENT *document,
size_t output_units_descriptor)
{
AV *av_output_units;
dTHX;
av_output_units = newAV ();
if (fill_output_units_descriptor_av (document, av_output_units,
output_units_descriptor))
return newRV_noinc ((SV *) av_output_units);
av_undef (av_output_units);
return newSV (0);
}
/* Can be called to rebuild output units when the converter is available.
*/
void
store_output_units_texinfo_tree (CONVERTER *converter, SV *converter_sv)
{
dTHX;
if (converter->document)
{
/* need to setup the Perl tree before rebuilding the output units as
they refer to Perl root command elements */
store_document_texinfo_tree (converter->document);
if (converter->document->modified_information & F_DOCM_output_units)
{
HV *converter_hv = (HV *) SvRV (converter_sv);
SV **document_units_sv;
/* build external_nodes_units before rebuilding the other
output units as the external_nodes_units may have never been built,
while other units could have already been built without directions
information.
*/
output_units_list_to_perl_hash (converter->document,
converter->output_units_descriptors[OUDT_external_nodes_units]);
/* reuse "document_units" array if set */
document_units_sv
= hv_fetch (converter_hv, "document_units",
strlen ("document_units"), 0);
if (document_units_sv && SvOK (*document_units_sv))
{
AV *av_output_units = (AV *) SvRV (*document_units_sv);
size_t output_units_descriptor
= converter->output_units_descriptors[OUDT_units];
av_clear (av_output_units);
if (!fill_output_units_descriptor_av (converter->document,
av_output_units,
output_units_descriptor))
{
/* the output_units_descriptor is not found. If there is
something to rebuild, this should mean that there is an output
units list in C, therefore we output an error here. It could
be redundant with errors output earlier in calling code, but it
is better to have more debug messages.
*/
fprintf (stderr,
"BUG: store_output_units_texinfo_tree: output units"
" descriptor not found: %zu\n", output_units_descriptor);
}
}
else
{
SV *output_units_sv
= build_output_units_list (converter->document,
converter->output_units_descriptors[OUDT_units]);
hv_store (converter_hv, "document_units",
strlen ("document_units"),
output_units_sv, 0);
}
output_units_list_to_perl_hash (converter->document,
converter->output_units_descriptors[OUDT_special_units]);
output_units_list_to_perl_hash (converter->document,
converter->output_units_descriptors[OUDT_associated_special_units]);
converter->document->modified_information &= ~F_DOCM_output_units;
}
}
}
/* Can be called to rebuild output units when the converter is not known.
Output units are kept in the document, but are setup and destroyed by
converters. If converters accessed concurently documents, there may
be trouble here (and in other codes too).
*/
void
store_document_tree_output_units (DOCUMENT *document)
{
dTHX;
if (document)
{
/* need to setup the Perl tree before rebuilding the output units as
they refer to Perl root command elements */
store_document_texinfo_tree (document);
/* we hope that there are not two output units lists referring to the
tree... */
if (document->modified_information & F_DOCM_output_units)
{
const OUTPUT_UNIT_LISTS *output_units_lists
= &document->output_units_lists;
size_t i;
if (output_units_lists->number > OUDT_external_nodes_units+1)
fprintf (stderr, "WARNING: %zu output units built to Perl\n",
output_units_lists->number);
for (i = 0; i < output_units_lists->number; i++)
{
output_units_list_to_perl_hash (document, i+1);
}
document->modified_information &= ~F_DOCM_output_units;
}
}
}
/* Get a reference to the document tree. Either built from C data if the
document could be found and if HANDLER_ONLY is not set, else from
a Perl document, if possible the one associated with C data, otherwise
DOCUMENT_IN.
If the C document data was not stored, the tree will be only be
in DOCUMENT_IN. */
SV *
document_tree (SV *document_in, int handler_only)
{
DOCUMENT *document;
SV **sv_reference = 0;
dTHX;
document = get_sv_document_document (document_in, 0);
if (!handler_only && document)
{
store_document_tree_output_units (document);
if (document->tree && document->tree->sv)
return newSVsv (document->tree->sv);
}
if (document && document->tree)
{
build_new_base_element (document->tree);
/* in that case, we do not reuse the "tree" reference
in document->hv. We therefore need to readd anything
relevant, in practice only "tree_document_descriptor" */
if (document->tree->sv)
{
const char *document_key = "tree_document_descriptor";
HV *element_hv;
SV **element_document_descriptor_sv;
element_hv = (HV *) SvRV ((SV *) document->tree->sv);
element_document_descriptor_sv
= hv_fetch (element_hv, document_key, strlen (document_key), 0);
if (!element_document_descriptor_sv)
{
hv_store (element_hv, document_key, strlen (document_key),
newSViv (document->descriptor), 0);
}
return newSVsv (document->tree->sv);
}
}
/* Prefer the tree of the Perl document associated to the C data */
if (document)
sv_reference = hv_fetch (document->hv, "tree", strlen ("tree"), 0);
if (!sv_reference)
{
HV *document_hv = (HV *) SvRV (document_in);
sv_reference = hv_fetch (document_hv, "tree", strlen ("tree"), 0);
}
if (sv_reference && SvOK (*sv_reference))
return newSVsv (*sv_reference);
return newSV (0);
}
/* Build Texinfo Document registered data to Perl */
/* Note that the built Perl data is cached in the same place where pure Perl
code looks for. The Perl data is returned if nothing changed in C. It
means that after the first build to Perl, pure Perl code can change the
Perl data and get the modified Perl data back even if XS is used, without
XS/C code noticing any change. In that case the C data will drift away
from the Perl data, which could lead to subtle bugs.
*/
/* there are 2 differences between BUILD_PERL_DOCUMENT_ITEM and
BUILD_PERL_DOCUMENT_LIST: in BUILD_PERL_DOCUMENT_LIST no check on existing
document->fieldname and the address of document->fieldname is passed,
not document->fieldname directly.
*/
#define BUILD_PERL_DOCUMENT_ITEM(funcname,fieldname,keyname,flagname,buildname,HVAV) \
SV * \
funcname (SV *document_in) \
{ \
DOCUMENT *document; \
\
dTHX;\
\
document = get_sv_document_document (document_in, #funcname); \
\
if (document) \
{\
const char *key = keyname; \
if (document->fieldname) \
{ \
store_document_tree_output_units (document);\
if (document->modified_information & flagname)\
{\
HVAV *result_av_hv = buildname (document->fieldname);\
SV *result_sv = newRV_noinc ((SV *) result_av_hv);\
hv_store (document->hv, key, strlen (key), result_sv, 0);\
document->modified_information &= ~flagname;\
return newSVsv (result_sv); \
}\
}\
\
SV **sv_reference = hv_fetch (document->hv, key, strlen (key), 0);\
if (sv_reference && SvOK (*sv_reference))\
return newSVsv (*sv_reference);\
}\
\
return newSV (0);\
}
/*
BUILD_PERL_DOCUMENT_ITEM(funcname,fieldname,keyname,flagname,buildname,HVAV)
*/
BUILD_PERL_DOCUMENT_ITEM(document_sectioning_root,sectioning_root,"sectioning_root",F_DOCM_sectioning_root,build_sectioning_root,HV)
#undef BUILD_PERL_DOCUMENT_ITEM
#define BUILD_PERL_DOCUMENT_LIST(funcname,fieldname,keyname,flagname,buildname,HVAV) \
SV * \
funcname (SV *document_in) \
{ \
DOCUMENT *document; \
\
dTHX;\
\
document = get_sv_document_document (document_in, #funcname); \
\
if (document)\
{\
const char *key = keyname; \
store_document_tree_output_units (document);\
if (document->modified_information & flagname)\
{\
HVAV *result_av_hv = buildname (&document->fieldname);\
SV *result_sv = newRV_noinc ((SV *) result_av_hv);\
hv_store (document->hv, key, strlen (key), result_sv, 0);\
document->modified_information &= ~flagname;\
return newSVsv (result_sv);\
}\
\
SV **sv_reference = hv_fetch (document->hv, key, strlen (key), 0);\
if (sv_reference && SvOK (*sv_reference))\
return newSVsv (*sv_reference);\
}\
\
return newSV (0);\
}
/*
BUILD_PERL_DOCUMENT_LIST(funcname,fieldname,keyname,flagname,buildname,HVAV)
*/
BUILD_PERL_DOCUMENT_LIST(document_nodes_list,nodes_list,"nodes_list",F_DOCM_nodes_list,build_node_relations_list,AV)
BUILD_PERL_DOCUMENT_LIST(document_sections_list,sections_list,"sections_list",F_DOCM_sections_list,build_section_relations_list,AV)
BUILD_PERL_DOCUMENT_LIST(document_headings_list,headings_list,"headings_list",F_DOCM_headings_list,build_heading_relations_list,AV)
BUILD_PERL_DOCUMENT_LIST(document_floats_information,listoffloats,"listoffloats_list",F_DOCM_floats,build_listoffloats_list,HV)
BUILD_PERL_DOCUMENT_LIST(document_internal_references_information,internal_references,"internal_references",F_DOCM_internal_references,build_internal_xref_list,AV)
BUILD_PERL_DOCUMENT_LIST(document_labels_list,labels_list,"labels_list",F_DOCM_labels_list,build_target_elements_list,AV)
BUILD_PERL_DOCUMENT_LIST(document_indices_information,indices_info,"indices",F_DOCM_index_names,build_index_data,HV)
BUILD_PERL_DOCUMENT_LIST(document_labels_information,identifiers_target,"identifiers_target",F_DOCM_identifiers_target,build_identifiers_target,HV)
BUILD_PERL_DOCUMENT_LIST(document_global_commands_information,global_commands,"commands_info",F_DOCM_global_commands,build_global_commands,HV)
#undef BUILD_PERL_DOCUMENT_LIST
SV *
document_global_information (SV *document_in)
{
DOCUMENT *document;
dTHX;
document = get_sv_document_document (document_in,
"document_global_information");
if (document)
{
const char *key = "global_info";
if (document->modified_information & F_DOCM_global_info)
{
/* Reuse the Perl hash associated to document if found */
HV *hv;
SV **global_info_sv
= hv_fetch (document->hv, key, strlen (key), 0);
if (global_info_sv && SvOK (*global_info_sv))
hv = (HV *) SvRV (*global_info_sv);
else
{
hv = newHV ();
hv_store (document->hv, key, strlen (key),
newRV_noinc ((SV *) hv), 0);
}
pass_global_info (hv, &document->global_info,
&document->global_commands);
document->modified_information &= ~F_DOCM_global_info;
return newRV_inc ((SV *) hv);
}
SV **sv_reference = hv_fetch (document->hv, key, strlen (key), 0);
if (sv_reference && SvOK (*sv_reference))
return newSVsv (*sv_reference);
}
return newSV (0);
}
/* Build indices information for Perl */
/* finds index name and entry number index entry SV in Perl data */
static SV *
find_idx_name_entry_number_sv (HV *indices_information_hv,
const char* index_name, int entry_number,
const char *message)
{
SV **index_info_sv;
SV *index_entry_sv = 0;
dTHX;
index_info_sv = hv_fetch (indices_information_hv, index_name,
strlen (index_name), 0);
if (!index_info_sv)
{
fprintf (stderr, "%s index %s not found\n", message, index_name);
}
else
{
HV *index_info_hv = (HV *) SvRV (*index_info_sv);
SV **index_info_index_entries_sv = hv_fetch (index_info_hv,
"index_entries", strlen ("index_entries"), 0);
if (!index_info_index_entries_sv)
{
fprintf (stderr, "%s index %s 'index_entries' not found\n",
message, index_name);
}
else
{
AV *index_info_entries_av
= (AV *) SvRV (*index_info_index_entries_sv);
SV **index_entry_info_sv = av_fetch (index_info_entries_av,
entry_number -1, 0);
if (!index_entry_info_sv)
{
fprintf (stderr, "%s: %d in %s not found\n", message,
entry_number, index_name);
}
else
index_entry_sv = *index_entry_info_sv;
}
}
return index_entry_sv;
}
HV *
build_indices_sort_strings (const INDICES_SORT_STRINGS *indices_sort_strings,
HV *indices_information_hv)
{
HV *indices_sort_strings_hv;
size_t i;
dTHX;
if (!indices_sort_strings)
return 0;
indices_sort_strings_hv = newHV ();
for (i = 0; i < indices_sort_strings->number; i++)
{
const INDEX_SORT_STRINGS *index_sort_strings
= &indices_sort_strings->indices[i];
const char *index_name = index_sort_strings->index->name;
if (index_sort_strings->entries_number > 0)
{
size_t j;
AV *sort_string_entries_av = newAV ();
hv_store (indices_sort_strings_hv, index_name, strlen (index_name),
newRV_noinc ((SV *)sort_string_entries_av), 0);
for (j = 0; j < index_sort_strings->entries_number; j++)
{
const INDEX_ENTRY_SORT_STRING *index_entry_sort_string
= &index_sort_strings->sort_string_entries[j];
const INDEX_ENTRY *entry = index_entry_sort_string->entry;
const char *entry_index_name = entry->index_name;
int entry_number = entry->number;
char *message;
SV *index_entry_sv;
HV *index_entry_sort_string_hv;
AV *sort_string_subentries_av;
size_t k;
if (index_entry_sort_string->subentries_number <= 0)
{
fprintf (stderr, "BUG: build_indices_sort_strings:"
" %s: entry %zu: no subentries", index_name, j);
continue;
}
xasprintf (&message, "BUG: build_indices_sort_strings:"
" %s: entry %zu", index_name, j);
index_entry_sv
= find_idx_name_entry_number_sv (indices_information_hv,
entry_index_name, entry_number,
message);
non_perl_free (message);
/* probably not possible, unless there is a bug */
if (!index_entry_sv)
continue;
index_entry_sort_string_hv = newHV ();
av_push (sort_string_entries_av,
newRV_noinc ((SV *) index_entry_sort_string_hv));
hv_store (index_entry_sort_string_hv, "index_name",
strlen ("index_name"),
newSVpv_utf8 (entry->index_name, 0), 0);
hv_store (index_entry_sort_string_hv, "number",
strlen ("number"), newSViv (entry->number), 0);
hv_store (index_entry_sort_string_hv, "entry",
strlen ("entry"), newSVsv (index_entry_sv), 0);
sort_string_subentries_av = newAV ();
hv_store (index_entry_sort_string_hv, "sort_strings",
strlen ("sort_strings"),
newRV_noinc ((SV *) sort_string_subentries_av), 0);
for (k = 0; k < index_entry_sort_string->subentries_number; k++)
{
const INDEX_SUBENTRY_SORT_STRING *subentry_sort_string
= &index_entry_sort_string->sort_string_subentries[k];
HV *subentry_sort_string_hv = newHV ();
av_push (sort_string_subentries_av,
newRV_noinc ((SV *) subentry_sort_string_hv));
hv_store (subentry_sort_string_hv, "sort_string",
strlen ("sort_string"),
newSVpv_utf8 (subentry_sort_string->sort_string, 0), 0);
hv_store (subentry_sort_string_hv, "alpha",
strlen ("alpha"),
newSViv (subentry_sort_string->alpha), 0);
}
}
}
}
return indices_sort_strings_hv;
}
HV *
build_sorted_indices_by_index (
const INDEX_SORTED_BY_INDEX *index_entries_by_index,
HV *indices_information_hv)
{
HV *indices_hv;
const INDEX_SORTED_BY_INDEX *idx;
dTHX;
if (!index_entries_by_index)
return 0;
indices_hv = newHV ();
for (idx = index_entries_by_index; idx->name; idx++)
{
AV *entries_av = newAV ();
size_t j;
hv_store (indices_hv, idx->name, strlen (idx->name),
newRV_noinc ((SV *)entries_av), 0);
for (j = 0; j < idx->entries_number; j++)
{
const INDEX_ENTRY *entry = idx->entries[j];
const char *index_name = entry->index_name;
int entry_number = entry->number;
char *message;
SV *index_entry_sv;
xasprintf (&message, "BUG: build_sorted_indices_by_index:"
" %s: entry %zu", idx->name, j);
index_entry_sv
= find_idx_name_entry_number_sv (indices_information_hv,
index_name, entry_number,
message);
non_perl_free (message);
if (index_entry_sv)
{
av_push (entries_av, newSVsv (index_entry_sv));
}
}
}
return indices_hv;
}
HV *
build_sorted_indices_by_letter (
const INDEX_SORTED_BY_LETTER *index_entries_by_letter,
HV *indices_information_hv)
{
HV *indices_hv;
const INDEX_SORTED_BY_LETTER *idx;
dTHX;
if (!index_entries_by_letter)
return 0;
indices_hv = newHV ();
for (idx = index_entries_by_letter; idx->name; idx++)
{
AV *sorted_letters_av;
size_t i;
if (idx->letter_number <= 0)
continue;
sorted_letters_av = newAV ();
hv_store (indices_hv, idx->name, strlen (idx->name),
newRV_noinc ((SV *)sorted_letters_av), 0);
for (i = 0; i < idx->letter_number; i++)
{
size_t j;
HV *letter_hv = newHV ();
AV *entries_av = newAV ();
const LETTER_INDEX_ENTRIES *letter = &idx->letter_entries[i];
hv_store (letter_hv, "letter", strlen ("letter"),
newSVpv_utf8 (letter->letter, 0), 0);
hv_store (letter_hv, "entries", strlen ("entries"),
newRV_noinc ((SV *)entries_av), 0);
av_push (sorted_letters_av, newRV_noinc ((SV *)letter_hv));
for (j = 0; j < letter->entries_number; j++)
{
const INDEX_ENTRY *entry = letter->entries[j];
const char *index_name = entry->index_name;
int entry_number = entry->number;
char *message;
SV *index_entry_sv;
xasprintf (&message, "BUG: build_sorted_indices_by_letter:"
" %s: %s: entry %zu", idx->name,
letter->letter, j);
index_entry_sv
= find_idx_name_entry_number_sv (indices_information_hv,
index_name, entry_number,
message);
non_perl_free (message);
if (index_entry_sv)
{
av_push (entries_av, newSVsv (index_entry_sv));
}
}
}
}
return indices_hv;
}
/* a fake output units list that only holds a descriptor allowing
to retrieve the C data */
SV *
setup_output_units_handler (const DOCUMENT *document,
size_t output_units_descriptor)
{
AV *av_output_units;
HV *dummy_output_unit;
SV *sv;
const OUTPUT_UNIT_LIST *output_units;
dTHX;
output_units = retrieve_output_units (document, output_units_descriptor);
if (!output_units || !output_units->number)
return newSV (0);
av_output_units = newAV ();
dummy_output_unit = newHV ();
hv_store (dummy_output_unit, "output_units_descriptor",
strlen ("output_units_descriptor"),
newSViv (output_units_descriptor), 0);
hv_store (dummy_output_unit, "output_units_document_descriptor",
strlen ("output_units_document_descriptor"),
newSViv ((IV)document->descriptor), 0);
sv = newRV_noinc ((SV *) dummy_output_unit);
av_push (av_output_units, sv);
return newRV_noinc ((SV *) av_output_units);
}
static HV *
build_expanded_formats (const EXPANDED_FORMAT *expanded_formats)
{
size_t i;
HV *expanded_hv;
dTHX;
expanded_hv = newHV ();
for (i = 0; i < expanded_formats_number (); i++)
{
if (expanded_formats[i].expandedp)
{
const char *format = expanded_formats[i].format;
hv_store (expanded_hv, format, strlen (format),
newSViv (1), 0);
}
}
return expanded_hv;
}
static HV *
build_translated_commands (const TRANSLATED_COMMAND_LIST *translated_commands)
{
size_t i;
HV *translated_hv;
dTHX;
translated_hv = newHV ();
for (i = 0; i < translated_commands->number; i++)
{
enum command_id cmd = translated_commands->list[i].cmd;
const char *translation = translated_commands->list[i].translation;
const char *command_name = builtin_command_name (cmd);
hv_store (translated_hv, command_name, strlen (command_name),
newSVpv_utf8 (translation, 0), 0);
}
return translated_hv;
}
SV *
build_convert_text_options (TEXT_OPTIONS *text_options)
{
HV *text_options_hv;
HV *expanded_formats_hv;
HV *translated_commands_hv;
dTHX;
text_options_hv = newHV ();
#define STORE(key, sv) hv_store (text_options_hv, key, strlen (key), sv, 0)
if (text_options->ASCII_GLYPH)
STORE("ASCII_GLYPH", newSViv (1));
if (text_options->DEBUG)
STORE("DEBUG", newSViv (1));
if (text_options->DOC_ENCODING_FOR_INPUT_FILE_NAME)
STORE("DOC_ENCODING_FOR_INPUT_FILE_NAME", newSViv (1));
if (text_options->NUMBER_SECTIONS)
STORE("NUMBER_SECTIONS", newSViv (1));
if (text_options->TEST)
STORE("TEST", newSViv (1));
if (text_options->sort_string)
STORE("sort_string", newSViv (1));
if (text_options->encoding)
STORE("enabled_encoding", newSVpv_utf8 (text_options->encoding, 0));
if (text_options->set_case)
STORE("set_case", newSViv (text_options->set_case));
if (text_options->code_state)
STORE("_code_state", newSViv (text_options->code_state));
if (text_options->INPUT_FILE_NAME_ENCODING)
STORE("INPUT_FILE_NAME_ENCODING",
newSVpv_utf8 (text_options->INPUT_FILE_NAME_ENCODING, 0));
if (text_options->LOCALE_ENCODING)
STORE("LOCALE_ENCODING",
newSVpv_utf8 (text_options->LOCALE_ENCODING, 0));
if (text_options->COMMAND_LINE_ENCODING)
STORE("COMMAND_LINE_ENCODING",
newSVpv_utf8 (text_options->COMMAND_LINE_ENCODING, 0));
expanded_formats_hv = build_expanded_formats (text_options->expanded_formats);
STORE("expanded_formats", newRV_noinc ((SV *)expanded_formats_hv));
if (text_options->include_directories.number > 0)
{
AV *av = build_string_list (&text_options->include_directories, svt_byte);
STORE("INCLUDE_DIRECTORIES", newRV_noinc ((SV *) av));
}
translated_commands_hv
= build_translated_commands (&text_options->translated_commands);
STORE("translated_commands", newRV_noinc ((SV *) translated_commands_hv));
if (text_options->converter && text_options->converter->sv)
{
STORE("converter", newSVsv (text_options->converter->sv));
}
#undef STORE
return newRV_noinc ((SV *)text_options_hv);
}
void
pass_document_sv_to_converter_sv (SV *converter_sv, SV *document_in)
{
HV *converter_hv;
dTHX;
converter_hv = (HV *)SvRV (converter_sv);
if (document_in && SvOK (document_in))
{
hv_store (converter_hv, "document", strlen ("document"),
newSVsv (document_in), 0);
}
hv_delete (converter_hv, "sorted_indices_by_letter",
strlen ("sorted_indices_by_letter"), G_DISCARD);
hv_delete (converter_hv, "sorted_indices_by_index",
strlen ("sorted_indices_by_index"), G_DISCARD);
hv_delete (converter_hv, "index_entries_sort_strings",
strlen ("index_entries_sort_strings"), G_DISCARD);
}
void
pass_converter_text_options (const CONVERTER *converter, SV *converter_sv)
{
HV *converter_hv;
dTHX;
converter_hv = (HV *)SvRV (converter_sv);
if (converter && converter->convert_text_options)
{
SV *text_options_sv
= build_convert_text_options (converter->convert_text_options);
hv_store (converter_hv,
"convert_text_options", strlen("convert_text_options"),
text_options_sv, 0);
}
}
/* build customization options to Perl */
/* build a Perl button data from pure C button structure.
This is a partial implementation.
This function can only be called for default buttons for now, so we do
not need to handle other types of buttons, which are either not
interesting to handle, or need Perl info */
static SV *
html_build_button (const CONVERTER *converter, BUTTON_SPECIFICATION *button,
int *user_function_number)
{
dTHX;
*user_function_number = 0;
switch (button->type)
{
const char *direction_name;
case BST_direction:
if (button->b.direction < 0)
direction_name = button->direction_string;
else
direction_name
= direction_unit_direction_name (button->b.direction,
converter);
if (!direction_name)
{
char *msg;
xasprintf (&msg, "No name for button direction %d",
button->b.direction);
fatal (msg);
non_perl_free (msg);
}
return newSVpv_utf8 (direction_name, 0);
break;
case BST_direction_info:
{
BUTTON_SPECIFICATION_INFO *button_spec = button->b.button_info;
AV *button_spec_info_av;
if (button_spec->direction < 0)
direction_name = button->direction_string;
else
direction_name
= direction_unit_direction_name (button_spec->direction,
converter);
if (!direction_name)
{
char *msg;
xasprintf (&msg, "No name for array button direction %d",
button_spec->direction);
fatal (msg);
non_perl_free (msg);
}
if (button_spec->type == BIT_function)
{
/* contains a leading :: */
const char *sub_name = html_button_function_type_string[
button_spec->bi.button_function.type];
if (sub_name)
{
char *sub_full_name;
CV *button_function_cv;
xasprintf (&sub_full_name, "Texinfo::Convert::HTML%s",
sub_name);
button_function_cv = get_cv (sub_full_name, 0);
if (!button_function_cv)
fprintf (stderr, "BUG: %s: not found\n", sub_full_name);
non_perl_free (sub_full_name);
button_spec_info_av = newAV ();
av_push (button_spec_info_av,
newSVpv_utf8 (direction_name, 0));
/* This is needed as tested. This probably means that get_cv
do not increase the refcount, so it is ok to do it here */
av_push (button_spec_info_av,
newRV_inc ((SV *) button_function_cv));
return newRV_noinc ((SV *) button_spec_info_av);
}
}
}
break;
default:
break;
}
return newSV (0);
}
void
html_build_buttons_specification (CONVERTER *converter,
BUTTON_SPECIFICATION_LIST *buttons)
{
AV *buttons_av;
size_t i;
dTHX;
/* we retain this reference in C, which should be removed when
the converter is destroyed, by creating one when returning
a reference on the AV */
buttons_av = newAV ();
buttons->av = buttons_av;
for (i = 0; i < buttons->number; i++)
{
int user_function_number;
BUTTON_SPECIFICATION *button = &buttons->list[i];
SV *button_sv = html_build_button (converter, button,
&user_function_number);
buttons->BIT_user_function_number += user_function_number;
if (converter)
converter->external_references_number += user_function_number;
button->sv = button_sv;
/* retain a reference in C */
av_push (buttons_av, newSVsv (button_sv));
}
}
SV *
html_build_direction_icons (const DIRECTION_ICON_LIST *direction_icons)
{
HV *icons_hv;
size_t i;
dTHX;
if (!direction_icons)
return newSV (0);
icons_hv = newHV ();
for (i = 0; i < direction_icons->number; i++)
{
DIRECTION_ICON *icon = &direction_icons->icons_list[i];
if (icon->direction_name)
{
SV *name_sv;
SV *direction_name_sv = newSVpv_utf8 (icon->direction_name, 0);
if (icon->name)
name_sv = newSVpv_utf8 (icon->name, 0);
else
name_sv = newSV (0);
hv_store_ent (icons_hv, direction_name_sv, name_sv, 0);
}
}
return newRV_noinc ((SV *)icons_hv);
}
SV *
build_sv_option (const OPTION *option, CONVERTER *converter)
{
dTHX;
switch (option->type)
{
case GOT_integer:
if (option->o.integer == -1)
return newSV (0);
return newSViv (option->o.integer);
break;
case GOT_char:
if (!option->o.string)
return newSV (0);
return newSVpv_utf8 (option->o.string, 0);
break;
case GOT_bytes:
if (!option->o.string)
return newSV (0);
return newSVpv_byte (option->o.string, 0);
break;
case GOT_bytes_string_list:
return newRV_noinc ((SV *) build_string_list(option->o.strlist,
svt_byte));
break;
case GOT_file_string_list:
return newRV_noinc ((SV *) build_string_list(option->o.strlist,
svt_dir));
break;
case GOT_char_string_list:
return newRV_noinc ((SV *) build_string_list(option->o.strlist,
svt_char));
break;
case GOT_buttons:
if (option->o.buttons)
{
if (!option->o.buttons->av)
html_build_buttons_specification (converter, option->o.buttons);
/* add an additional reference to retain one in C */
return newRV_inc ((SV *) option->o.buttons->av);
}
break;
case GOT_icons:
return html_build_direction_icons (option->o.icons);
break;
default:
break;
}
return newSV (0);
}
/* not much used, as in general the options are only stored in C and
accessed through the API and built when accessed through a converter.
This is only used when there is no converter, when a function is called with
a class name only and returns an option hash */
SV *
build_sv_options_from_options_list (const OPTIONS_LIST *options_list,
CONVERTER *converter)
{
size_t i;
HV *options_hv;
dTHX;
options_hv = newHV ();
for (i = 0; i < options_list->number; i++)
{
size_t index = options_list->list[i] -1;
const OPTION *option = options_list->sorted_options[index];
const char *key = option->name;
SV *option_sv = build_sv_option (option, converter);
/* we store all values as they appear, the later overriding earlier
values, and do not treat undef nor C option configured field
especially */
hv_store (options_hv, key, strlen (key), option_sv, 0);
}
return newRV_noinc ((SV *)options_hv);
}
/* pass generic converter information to Perl */
static HV *
build_deprecated_directories (
const DEPRECATED_DIRS_LIST *deprecated_directories)
{
size_t i;
HV *deprecated_directories_hv;
dTHX;
deprecated_directories_hv = newHV ();
for (i = 0; i < deprecated_directories->number; i++)
{
const char *reference_dir
= deprecated_directories->list[i].reference_dir;
const char *obsolete_dir
= deprecated_directories->list[i].obsolete_dir;
SV *reference_dir_sv = newSVpv_utf8 (reference_dir, 0);
SV *obsolete_dir_sv = newSVpv_utf8 (obsolete_dir, 0);
hv_store_ent (deprecated_directories_hv, obsolete_dir_sv,
reference_dir_sv, 0);
}
return deprecated_directories_hv;
}
/* Build a converter info hash reference based on CONF */
SV *
build_sv_converter_info_from_converter_initialization_info
(const CONVERTER_INITIALIZATION_INFO *conf, CONVERTER *converter)
{
SV *result;
HV *deprecated_directories_hv;
HV *translated_commands_hv;
HV *result_hv;
dTHX;
result = build_sv_options_from_options_list (&conf->conf, converter);
result_hv = (HV *) SvRV (result);
#define STORE(key, sv) hv_store (result_hv, key, strlen (key), sv, 0);
translated_commands_hv
= build_translated_commands (&conf->translated_commands);
STORE("translated_commands", newRV_noinc ((SV *) translated_commands_hv));
deprecated_directories_hv
= build_deprecated_directories (&conf->deprecated_config_directories);
STORE("deprecated_config_directories",
newRV_noinc ((SV *) deprecated_directories_hv));
#undef STORE
return result;
}
void
pass_generic_converter_to_converter_sv (SV *converter_sv,
const CONVERTER *converter)
{
HV *converter_hv;
HV *expanded_formats_hv;
HV *translated_commands_hv;
HV *deprecated_directories_hv;
HV *output_files_hv;
HV *unclosed_files_hv;
HV *opened_files_hv;
dTHX;
converter_hv = (HV *)SvRV (converter_sv);
#define STORE(key, sv) hv_store (converter_hv, key, strlen (key), sv, 0);
/* $converter->{'output_files'}
= Texinfo::Convert::Utils::output_files_initialize(); */
output_files_hv = newHV ();
STORE("output_files", newRV_noinc ((SV *) output_files_hv));
unclosed_files_hv = newHV ();
opened_files_hv = newHV ();
hv_store (output_files_hv, "unclosed_files", strlen ("unclosed_files"),
newRV_noinc ((SV *) unclosed_files_hv), 0);
hv_store (output_files_hv, "opened_files",
strlen ("opened_files"),
newRV_noinc ((SV *) opened_files_hv), 0);
expanded_formats_hv
= build_expanded_formats (converter->expanded_formats);
STORE("expanded_formats", newRV_noinc ((SV *) expanded_formats_hv));
translated_commands_hv
= build_translated_commands (&converter->translated_commands);
STORE("translated_commands", newRV_noinc ((SV *) translated_commands_hv));
deprecated_directories_hv
= build_deprecated_directories (&converter->deprecated_config_directories);
STORE("deprecated_config_directories",
newRV_noinc ((SV *) deprecated_directories_hv));
/* store converter_descriptor in perl converter */
/* NOTE unlikely IV overflow if PERL_QUAD_MAX < SIZE_MAX */
STORE("converter_descriptor", newSViv ((IV)converter->converter_descriptor));
#undef STORE
}
/* API to access output file names associated with output units */
static HV *
build_filenames (const FILE_NAME_PATH_COUNTER_LIST *output_unit_files)
{
size_t i;
HV *hv;
dTHX;
hv = newHV ();
if (output_unit_files)
{
for (i = 0; i < output_unit_files->number; i++)
{
const FILE_NAME_PATH_COUNTER *output_unit_file
= &output_unit_files->list[i];
const char *normalized_filename
= output_unit_file->normalized_filename;
SV *normalized_filename_sv = newSVpv_utf8 (normalized_filename, 0);
hv_store_ent (hv, normalized_filename_sv,
newSVpv_utf8 (output_unit_file->filename, 0), 0);
}
}
return hv;
}
static HV *
build_file_counters (const FILE_NAME_PATH_COUNTER_LIST *output_unit_files)
{
size_t i;
HV *hv;
dTHX;
hv = newHV ();
if (output_unit_files)
{
for (i = 0; i < output_unit_files->number; i++)
{
const FILE_NAME_PATH_COUNTER *output_unit_file
= &output_unit_files->list[i];
const char *filename = output_unit_file->filename;
SV *filename_sv = newSVpv_utf8 (filename, 0);
hv_store_ent (hv, filename_sv, newSViv (output_unit_file->counter), 0);
}
}
return hv;
}
HV *
build_out_filepaths (const FILE_NAME_PATH_COUNTER_LIST *output_unit_files)
{
size_t i;
HV *hv;
dTHX;
hv = newHV ();
if (output_unit_files)
{
for (i = 0; i < output_unit_files->number; i++)
{
const FILE_NAME_PATH_COUNTER *output_unit_file
= &output_unit_files->list[i];
const char *filename = output_unit_file->filename;
SV *filename_sv = newSVpv_utf8 (filename, 0);
hv_store_ent (hv, filename_sv,
newSVpv_utf8 (output_unit_file->filepath, 0), 0);
}
}
return hv;
}
/* currently unused */
/* Not needed because all the information is already in overriden functions,
setup in _prepare_units_directions_files (calling _html_set_pages_files)
and _node_redirections, and accessed through _html_convert_output
and count_elements_in_filename. */
void
pass_output_unit_files (SV *converter_sv,
const FILE_NAME_PATH_COUNTER_LIST *output_unit_files)
{
HV *filenames_hv;
HV *file_counters_hv;
HV *out_filepaths_hv;
dTHX;
HV *converter_hv = (HV *) SvRV (converter_sv);
filenames_hv = build_filenames (output_unit_files);
file_counters_hv = build_file_counters (output_unit_files);
out_filepaths_hv = build_out_filepaths (output_unit_files);
#define STORE(key) \
hv_store (converter_hv, #key, strlen (#key), newRV_noinc ((SV *) key##_hv), 0);
STORE(filenames);
STORE(file_counters);
STORE(out_filepaths);
#undef STORE
}
/* Texinfo::Convert::Utils output_files_information API */
static void
build_output_files_unclosed_files (HV *hv,
const OUTPUT_FILES_INFORMATION *output_files_information)
{
SV **unclosed_files_sv;
HV *unclosed_files_hv;
const FILE_STREAM_LIST *unclosed_files;
size_t i;
dTHX;
unclosed_files_sv = hv_fetch (hv, "unclosed_files",
strlen ("unclosed_files"), 0);
if (!unclosed_files_sv)
{
unclosed_files_hv = newHV ();
hv_store (hv, "unclosed_files", strlen ("unclosed_files"),
newRV_noinc ((SV *) unclosed_files_hv), 0);
}
else
{
unclosed_files_hv = (HV *)SvRV (*unclosed_files_sv);
}
unclosed_files = &output_files_information->unclosed_files;
if (unclosed_files->number > 0)
{
for (i = 0; i < unclosed_files->number; i++)
{
const FILE_STREAM *file_stream = &unclosed_files->list[i];
const char *file_path = file_stream->file_path;
/* It is not possible to associate the unclosed stream to a SV.
It is possible to obtain a PerlIO from a FILE, as described in
https://perldoc.perl.org/perlapio
with
PerlIO * PerlIO_importFILE (FILE *stdio, const char *mode)
However, it is not possible to create an IO * SV from the PerlIO
or associate to an already existing IO *. An IO * SV is created by
IO * newIO()
and it is possible to get the associated PerlIO, with
PerlIO *IoOFP(IO *io);
but not to set it.
However, it is possible to pass a stream through the XS
interface. Therefore here, the unclosed file name is registered,
the stream can then be passed to Perl through a call of
the XS interface
Texinfo::Convert::ConvertConverterXS::XS_get_unclosed_stream.
Register that there is an unclosed file from XS by associating
with undef; if from Perl, it would be associated with a file handle */
SV *file_path_sv = newSVpv_byte (file_path, 0);
hv_store_ent (unclosed_files_hv, file_path_sv, newSV (0), 0);
}
}
}
/* input hv should be an output_files hv, in general setup by
$converter->{'output_files'} = Texinfo::Convert::Utils::output_files_initialize(); */
static void
build_output_files_opened_files (HV *hv,
const OUTPUT_FILES_INFORMATION *output_files_information)
{
SV **opened_files_sv;
HV *opened_files_hv;
const STRING_LIST *opened_files;
size_t i;
dTHX;
opened_files_sv = hv_fetch (hv, "opened_files", strlen ("opened_files"), 0);
if (!opened_files_sv)
{
opened_files_hv = newHV ();
hv_store (hv, "opened_files", strlen ("opened_files"),
newRV_noinc ((SV *) opened_files_hv), 0);
}
else
{
opened_files_hv = (HV *)SvRV (*opened_files_sv);
}
opened_files = &output_files_information->opened_files;
if (opened_files->number > 0)
{
for (i = 0; i < opened_files->number; i++)
{
const char *file_path = opened_files->list[i];
SV *file_path_sv = newSVpv_byte (file_path, 0);
hv_store_ent (opened_files_hv, file_path_sv, newSViv (1), 0);
}
}
}
void
build_output_files_information (SV *converter_sv,
const OUTPUT_FILES_INFORMATION *output_files_information)
{
HV *hv;
SV **output_files_sv;
HV *output_files_hv;
dTHX;
hv = (HV *) SvRV (converter_sv);
output_files_sv = hv_fetch (hv, "output_files",
strlen ("output_files"), 0);
if (!output_files_sv)
{
output_files_hv = newHV ();
hv_store (hv, "output_files", strlen ("output_files"),
newRV_noinc ((SV *) output_files_hv), 0);
}
else
{
output_files_hv = (HV *)SvRV (*output_files_sv);
}
build_output_files_opened_files (output_files_hv,
output_files_information);
build_output_files_unclosed_files (output_files_hv,
output_files_information);
}
static const char *latex_math_options[] = {
"DEBUG", "OUTPUT_CHARACTERS", "OUTPUT_ENCODING_NAME", "TEST", 0
};
HV *
latex_build_options_for_convert_to_latex_math (CONVERTER *converter)
{
HV *options_latex_math_hv;
int i;
dTHX;
options_latex_math_hv = newHV ();
for (i = 0; latex_math_options[i]; i++)
{
const char *option_name = latex_math_options[i];
const OPTION *option = find_option_string (converter->sorted_options,
option_name);
/* no testing if option is NULL, we know that latex_math_options exist */
SV *option_sv = build_sv_option (option, converter);
if (SvOK (option_sv))
{
hv_store (options_latex_math_hv, option_name,
strlen (option_name), option_sv, 0);
}
}
return options_latex_math_hv;
}