blob: 327e00d9a5e4115cb6c080f06070321520b66351 [file]
/* Copyright 2023-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/>. */
/* ALTIMP perl/Texinfo/DocumentNonXS.pm */
#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 "options_data.h"
#include "tree_types.h"
#include "document_types.h"
/* non_perl_* */
#include "xs_utils.h"
/* for messages_and_encodings_setup */
#include "utils.h"
#include "customization_options.h"
/* for clear_error_message_list */
#include "errors.h"
#include "document.h"
#include "get_perl_info.h"
/* for get_sv_converter */
#include "get_converter_perl_info.h"
#include "build_perl_info.h"
#include "call_document_perl_functions.h"
/* for html_element_cdt_tree */
#include "html_converter_api.h"
/* See the NOTE in build_perl_info.c on use of functions related to
memory allocation */
MODULE = Texinfo::Document PACKAGE = Texinfo::Document
PROTOTYPES: ENABLE
# Called from Texinfo::XSLoader.pm.
# File paths are byte strings and can be in any encoding.
int
init (SV *texinfo_uninstalled_sv, SV *datadir_sv, SV *t2a_builddir_sv, SV *t2a_srcdir_sv, SV *mandatory_xs_sv)
PREINIT:
const char *t2a_builddir = 0;
const char *t2a_srcdir = 0;
const char *datadir = 0;
int texinfo_uninstalled = 0;
CODE:
if (!SvOK (mandatory_xs_sv) || !SvIV (mandatory_xs_sv))
{
if (SvOK (texinfo_uninstalled_sv))
texinfo_uninstalled = SvIV (texinfo_uninstalled_sv);
if (texinfo_uninstalled)
{
if (SvOK (t2a_builddir_sv))
t2a_builddir = SvPVbyte_nolen (t2a_builddir_sv);
if (SvOK (t2a_srcdir_sv))
t2a_srcdir = SvPVbyte_nolen (t2a_srcdir_sv);
}
if (SvOK (datadir_sv))
datadir = SvPVbyte_nolen (datadir_sv);
/* needed by the parser */
messages_and_encodings_setup (datadir);
setup_texinfo_main (texinfo_uninstalled, datadir,
t2a_builddir, t2a_srcdir);
set_use_perl_interpreter (txi_interpreter_use_embedded);
}
RETVAL = 1;
OUTPUT:
RETVAL
# This XS interface is rarely used, as, in general, a document is available
# and document_tree can be used instead. It may be useful for a tree from
# parse_texi_piece to force the C data to be removed, with no_store set to
# 1 to ensure that the resulting tree modified by pure Perl functions is not
# taken unmodified from C again.
SV *
build_tree (SV *tree_in, ...)
PROTOTYPE: $;$
PREINIT:
int no_store = 0;
DOCUMENT *document = 0;
SV *result_sv = 0;
CODE:
if (items > 1 && SvOK(ST(1)))
no_store = SvIV (ST(1));
document = get_sv_tree_document (tree_in, "build_tree");
if (document)
{
SV *document_sv = build_document (document, no_store);
if (!no_store)
{
if (document->tree)
result_sv = newSVsv ((SV *) document->tree->sv);
}
else
{ /* no more document->tree, get from Perl data */
HV *document_hv = (HV *) SvRV (document_sv);
SV **tree_sv = hv_fetch (document_hv, "tree",
strlen("tree"), 0);
if (tree_sv && SvOK (*tree_sv))
result_sv = newSVsv (*tree_sv);
}
}
if (result_sv)
RETVAL = result_sv;
else
RETVAL = newSV(0);
OUTPUT:
RETVAL
void
destroy_document (SV *document_in, SV *remove_references_sv=0)
PROTOTYPE: $;$
PREINIT:
DOCUMENT *document = 0;
CODE:
document = get_sv_document_document (document_in, "destroy_document");
if (document)
{
/* document->options NULL may happen if document is destroyed
before registering document options.
This does not happen in current codes, but could make sense,
so we account for this possibility.
*/
int check_counts = (document->options
&& document->options->TEST.o.integer > 1);
int remove_references = 0;
if (remove_references_sv && SvOK (remove_references_sv))
remove_references = SvIV (remove_references_sv);
/* call Perl function to remove Perl data in Texinfo::Document.
*/
call_document_remove_document_references (document,
remove_references);
ERROR_MESSAGE_LIST *error_messages = 0;
/* Not useful, as it is done by converters already
release_output_units_lists_built (&document->output_units_lists);
*/
/* register messages before being destroyed */
if (document->error_messages.number)
{
pass_errors_to_hv (&document->error_messages,
document_in, 0);
clear_error_message_list (&document->error_messages);
}
if (check_counts)
error_messages = set_check_element_interpreter_refcount ();
destroy_document (document);
if (check_counts)
{
/* register error messages obtained during destruction */
pass_errors_to_hv (error_messages, document_in, 0);
clear_error_message_list (error_messages);
unset_check_element_interpreter_refcount ();
}
}
SV *
errors (SV *document_in)
PREINIT:
DOCUMENT *document;
const ERROR_MESSAGE_LIST *error_messages = 0;
CODE:
/* If the errors are obtained after document destruction, then
the document will not be found, the messages should have already
been registered to Perl in destroy_document.
*/
document = get_sv_document_document (document_in, 0);
if (document)
error_messages = &document->error_messages;
RETVAL = pass_errors_to_hv (error_messages, document_in, 0);
if (document)
clear_error_message_list (&document->error_messages);
OUTPUT:
RETVAL
SV *
parser_errors (SV *document_in)
PREINIT:
DOCUMENT *document = 0;
AV *av;
CODE:
/* if XS is used, a document should be found. It could
also have been possible to abort if a document is not
found.
*/
document = get_sv_document_document (document_in,
"document_parser_errors");
av = newAV ();
if (document && document->parser_error_messages.number)
{
pass_errors (&document->parser_error_messages, av);
clear_error_message_list (&document->parser_error_messages);
}
RETVAL = newRV_noinc ((SV *) av);
OUTPUT:
RETVAL
void
register_document_options (SV *document_in, SV *sv_options_in)
PREINIT:
DOCUMENT *document = 0;
CODE:
document = get_sv_document_document (document_in,
"set_document_options");
if (document)
{
OPTIONS *options = new_options ();
OPTION **sorted_options = new_sorted_options (options);
get_sv_options (sv_options_in, options, sorted_options, 0, 0);
register_document_options (document, options, sorted_options);
}
SV *
get_conf (SV *document_in, option_name)
const char *option_name = (char *)SvPVbyte_nolen($arg);
PREINIT:
DOCUMENT *document = 0;
CODE:
document = get_sv_document_document (document_in,
"document_get_conf");
if (document && document->sorted_options)
{
const OPTION *option
= find_option_string (document->sorted_options, option_name);
if (option)
RETVAL = build_sv_option (option, 0);
else
RETVAL = newSV (0);
}
else
RETVAL = newSV (0);
OUTPUT:
RETVAL
void
set_document_global_info (SV *document_in, char *key, SV *value_sv)
PREINIT:
DOCUMENT *document = 0;
CODE:
document = get_sv_document_document (document_in,
"set_document_global_info");
if (document)
{
document->modified_information |= F_DOCM_global_info;
if (!strcmp (key, "input_file_name"))
{
const char *value = SvPVbyte_nolen(value_sv);
if (document->global_info.input_file_name)
{
fprintf (stderr,
"BUG: %zu: reset input_file_name '%s' -> '%s'\n",
document->descriptor,
document->global_info.input_file_name, value);
non_perl_free (document->global_info.input_file_name);
}
document->global_info.input_file_name
= non_perl_strdup (value);
}
else
{
const char *value = SvPVutf8_nolen(value_sv);
add_other_global_info_string (
&document->global_info.other_info,
key, value);
}
}
SV *
tree (SV *document_in, int handler_only=0)
CODE:
RETVAL = document_tree (document_in, handler_only);
OUTPUT:
RETVAL
SV *
global_information (SV *document_in)
CODE:
RETVAL = document_global_information (document_in);
OUTPUT:
RETVAL
SV *
indices_information (SV *document_in)
CODE:
RETVAL = document_indices_information (document_in);
OUTPUT:
RETVAL
SV *
global_commands_information (SV *document_in)
CODE:
RETVAL = document_global_commands_information (document_in);
OUTPUT:
RETVAL
SV *
labels_information (SV *document_in)
CODE:
RETVAL = document_labels_information (document_in);
OUTPUT:
RETVAL
SV *
nodes_list (SV *document_in)
CODE:
RETVAL = document_nodes_list (document_in);
OUTPUT:
RETVAL
SV *
sections_list (SV *document_in)
CODE:
RETVAL = document_sections_list (document_in);
OUTPUT:
RETVAL
SV *
sectioning_root (SV *document_in)
CODE:
RETVAL = document_sectioning_root (document_in);
OUTPUT:
RETVAL
SV *
headings_list (SV *document_in)
CODE:
RETVAL = document_headings_list (document_in);
OUTPUT:
RETVAL
SV *
floats_information (SV *document_in)
CODE:
document_sections_list (document_in);
RETVAL = document_floats_information (document_in);
OUTPUT:
RETVAL
SV *
internal_references_information (SV *document_in)
CODE:
RETVAL = document_internal_references_information (document_in);
OUTPUT:
RETVAL
SV *
labels_list (SV *document_in)
CODE:
RETVAL = document_labels_list (document_in);
OUTPUT:
RETVAL
void
setup_indices_sort_strings (SV *document_in, SV *converter_in)
PREINIT:
DOCUMENT *document = 0;
CODE:
document = get_sv_document_document (document_in,
"setup_indices_sort_strings");
if (document)
{
/* CONVERTER *self = 0;
if (converter_in && SvOK (converter_in))
self = get_sv_converter (converter_in, 0);
*/
document_indices_sort_strings (document,
&document->error_messages,
document->options);
}
SV *
document_indices_sort_strings (SV *document_in)
PREINIT:
DOCUMENT *document = 0;
const INDICES_SORT_STRINGS *indices_sort_strings = 0;
SV *result_sv = 0;
const char *key = "index_entries_sort_strings";
CODE:
document = get_sv_document_document (document_in,
"indices_sort_strings");
if (document)
{
/*
CONVERTER *self = 0;
if (converter_in && SvOK (converter_in))
self = get_sv_converter (converter_in, 0); */
indices_sort_strings
= document_indices_sort_strings (document,
&document->error_messages,
document->options);
}
if (indices_sort_strings)
{
HV *document_hv = (HV *) SvRV (document_in);
/* build Perl data only if needed and cache the built Perl
data in the same hash as done in overriden Perl code */
if (document->modified_information & F_DOCM_indices_sort_strings)
{
SV *indices_information_sv
= document_indices_information (document_in);
if (indices_information_sv)
{
HV *indices_information_hv
= (HV *) SvRV (indices_information_sv);
HV *indices_sort_strings_hv
= build_indices_sort_strings (indices_sort_strings,
indices_information_hv);
hv_store (document_hv, key, strlen (key),
newRV_noinc ((SV *) indices_sort_strings_hv), 0);
result_sv = newRV_inc ((SV *) indices_sort_strings_hv);
document->modified_information
&= ~F_DOCM_indices_sort_strings;
}
/* warn if not found? */
}
else
{ /* retrieve previously stored result */
SV **sv_stored = hv_fetch (document_hv, key, strlen (key), 0);
if (sv_stored && SvOK (*sv_stored))
result_sv = newSVsv (*sv_stored);
/* error out if not found? Or rebuild? */
}
}
if (result_sv)
RETVAL = result_sv;
else
RETVAL = newSV (0);
OUTPUT:
RETVAL
SV *
print_document_indices_information (SV *document_in)
PREINIT:
DOCUMENT *document = 0;
CODE:
document = get_sv_document_document (document_in,
"print_document_indices_information");
if (document)
{
char *indices_information_str
= print_document_indices_information (document);
if (indices_information_str)
{
RETVAL = newSVpv_utf8 (indices_information_str, 0);
free (indices_information_str);
}
else
RETVAL = newSV (0);
}
else
RETVAL = newSV (0);
OUTPUT:
RETVAL
SV *
print_document_indices_sort_strings (SV *document_in, SV *converter_in=0)
PREINIT:
DOCUMENT *document = 0;
CODE:
document = get_sv_document_document (document_in,
"print_document_indices_sort_strings");
if (document)
{
CONVERTER *self = 0;
if (converter_in && SvOK (converter_in))
self = get_sv_converter (converter_in, 0);
char *indices_sort_strings_str;
indices_sort_strings_str
= print_document_indices_sort_strings (document, self);
if (indices_sort_strings_str)
{
RETVAL = newSVpv_utf8 (indices_sort_strings_str, 0);
free (indices_sort_strings_str);
}
else
RETVAL = newSV (0);
}
else
RETVAL = newSV (0);
OUTPUT:
RETVAL