blob: 4baac56200fec35d744f2bad68dc30cc0b9a2efd [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/>. */
/* Interface similar to the Perl modules interface for Texinfo parsing,
higher-level interface for document structure and transformations,
and interface similar to the Perl modules interface for conversion */
/* not used in code called from texi2any/Perl, meant to be used exclusively
from C code */
#include <config.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef ENABLE_NLS
#include <libintl.h>
#endif
/* to have a definition of gettext in case ENABLE_NLS
is not set */
#include "gettext.h"
#include "interpreter_use_types.h"
#include "document_types.h"
#include "option_types.h"
#include "options_defaults.h"
#include "api.h"
/* fatal */
#include "base_utils.h"
#include "conf.h"
#include "errors.h"
/* parse_file_path free_output_files_information */
#include "utils.h"
#include "customization_options.h"
#include "document.h"
#include "translations.h"
#include "structuring.h"
#include "transformations.h"
#include "convert_utils.h"
/* find_format_name_converter_format setup_converter_generic
converter_defaults destroy_converter_initialization_info converter_converter
converter_output converter_convert converter_remove_output_units
destroy_converter */
#include "converter.h"
#include "html_converter_api.h"
#include "call_perl_function.h"
#include "call_conversion_perl.h"
#include "call_embed_perl.h"
#include "call_document_perl_functions.h"
/* definition of set_use_perl_interpreter */
#include "xs_utils.h"
#include "api_to_perl.h"
#include "texinfo.h"
#define _(String) gettext (String)
/* associate transformation name to the corresponding flag */
static const TRANSFORMATION_NAME_FLAG txi_tree_transformation_table[] = {
#define tt_type(name) {#name, STTF_ ## name},
TT_TYPES_LIST
#undef tt_type
{NULL, 0}
};
/* similar to Texinfo::Common::valid_tree_transformation */
/* Also returns the flag associated to the transformation */
unsigned long
txi_find_tree_transformation (const char *transformation_name)
{
int i;
for (i = 0; txi_tree_transformation_table[i].name; i++)
{
if (!strcmp (transformation_name, txi_tree_transformation_table[i].name))
return txi_tree_transformation_table[i].flag;
}
return 0;
}
/*
Start an embedded Perl interpreter and initialize by passing the
load_txi_modules_basename script to be called from the embedded
interpreter.
Return 0 in case of success.
*/
/* TODO warn/exit if loaded twice? The problem with being loaded
twice is that the init files code may have modified previous
interpreter data */
int
txi_load_interpreter (const INTERPRETER_LOADING_INFO *loading_info)
{
const char *load_txi_modules_basename = "load_txi_modules";
char *load_modules_path;
int status;
if (txi_paths_info.texinfo_uninstalled)
xasprintf (&load_modules_path, "%s/perl/%s.pl",
txi_paths_info.p.uninstalled.t2a_srcdir,
load_txi_modules_basename);
else
xasprintf (&load_modules_path, "%s/%s",
txi_paths_info.p.installed.converter_datadir,
load_txi_modules_basename);
status = call_init_perl (loading_info->argc_ref, loading_info->argv_ref,
loading_info->env_ref, load_modules_path,
loading_info->version_checked);
/* status < 0 means replacement call_init_perl that does nothing */
if (status > 0)
{
char *message;
/* unexpected failure, no point continuing, the output needs
the interpreter and libperl will segfault */
xasprintf (&message, "call_init_perl status: %d", status);
fatal (message);
free (message);
}
else if (status < 0)
{
fprintf (stderr, "WARNING: no interpreter embedding code built\n");
/*
no need to call set_use_perl_interpreter
txi_interpreter_use_no_interpreter, it is the default in
that case */
}
else
set_use_perl_interpreter (txi_interpreter_use_embedded);
free (load_modules_path);
return status;
}
/* assume that there is already a Perl interpreter loaded, but the
texi2any Perl modules are not loaded and load some modules.
Currently unused */
int
txi_use_interpreter_load_modules (int texinfo_uninstalled, const char *datadir,
const char *converter_datadir,
const char *converter_libdir,
const char *t2a_builddir,
const char *t2a_srcdir, int updirs)
{
int loaded
= call_eval_load_texinfo_modules (texinfo_uninstalled, t2a_builddir,
updirs, converter_datadir,
converter_libdir, datadir);
/* In general, loaded <= 0 cannot happen, because failure to call Perl
if embedded or to load modules should lead to dying/croaking
earlier, notably in XSLoader */
if (loaded < 0)
{
/* Unexpected failure loading Perl modules, consider that there is no
Perl interpreter
*/
set_use_perl_interpreter (0);
return 1;
}
else
{
set_use_perl_interpreter (txi_interpreter_use_interpreter);
return 0;
}
}
/* Start an embedded interpreter or initialize an existing interpreter.
To be called before loading init files.
*/
/* TODO remove the use_interpreter unused possibilities */
void
txi_setup_load_interpreter (enum interpreter_use use_interpreter,
int *argc_ref, char ***argv_ref, char ***env_ref,
const char *version_checked,
INTERPRETER_LOADING_INFO *loading_info)
{
if (use_interpreter == txi_interpreter_use_embedded
|| use_interpreter == txi_interpreter_want_embedded)
{
loading_info->argc_ref = argc_ref;
loading_info->argv_ref = argv_ref;
loading_info->env_ref = env_ref;
loading_info->version_checked = version_checked;
if (use_interpreter == txi_interpreter_use_embedded)
txi_load_interpreter (loading_info);
}
else
{
set_use_perl_interpreter (txi_interpreter_use_no_interpreter);
}
}
static void
err_add_option_value (OPTIONS_LIST *options_list, const char *option_name,
int int_value, const char *char_value)
{
if (!add_option_value (options_list, option_name,
int_value, char_value))
fprintf (stderr, "BUG: error setting %s\n", option_name);
}
/* ALTIMP texi2any.pl and load_txi_modules.pl */
/* Customization variables independent of conversion format are set similarly
in texi2any.pl */
void
txi_set_base_default_options (OPTIONS_LIST *main_program_set_options,
const char *locale_encoding,
const char *console_output_encoding,
const char *program_file)
{
const char *configured_version = PACKAGE_VERSION_CONFIG;
const char *configured_package = PACKAGE_CONFIG;
const char *configured_name = PACKAGE_NAME_CONFIG;
const char *configured_url = PACKAGE_URL_CONFIG;
const char *configured_name_version
= PACKAGE_NAME_CONFIG " " PACKAGE_VERSION_CONFIG;
initialize_options_list (main_program_set_options);
/* similar to options coming from texi2any */
err_add_option_value (main_program_set_options, "PROGRAM", 0, program_file);
#define set_configured_information(varname,varvalue) \
err_add_option_value (main_program_set_options, #varname, 0, varvalue);
set_configured_information(PACKAGE_VERSION, configured_version)
set_configured_information(PACKAGE, configured_package)
set_configured_information(PACKAGE_NAME, configured_name)
set_configured_information(PACKAGE_AND_VERSION, configured_name_version)
set_configured_information(PACKAGE_URL, configured_url)
#undef set_configured_information
err_add_option_value (main_program_set_options, "COMMAND_LINE_ENCODING", 0,
locale_encoding);
err_add_option_value (main_program_set_options, "MESSAGE_ENCODING", 0,
console_output_encoding);
err_add_option_value (main_program_set_options, "LOCALE_ENCODING", 0,
locale_encoding);
/* same as Texinfo::Common::default_main_program_customization_options */
/* in general transmitted to converters as default */
add_program_cmdline_options_defaults (main_program_set_options);
add_program_customization_options_defaults (main_program_set_options);
}
/* ALTIMP texi2any.pl and load_txi_modules.pl */
/* initialization of the library for output strings translations for
parsing and conversion (generic), to be called once */
void
txi_general_output_strings_setup (void)
{
char *locales_dir;
/* code in texinfo.pl */
if (txi_paths_info.texinfo_uninstalled)
{
struct stat finfo;
int not_found = 1;
if (txi_paths_info.p.uninstalled.t2a_builddir)
{
xasprintf (&locales_dir, "%s/LocaleData",
txi_paths_info.p.uninstalled.t2a_builddir);
if (stat (locales_dir, &finfo) == 0 && S_ISDIR (finfo.st_mode))
{
not_found = 0;
setup_output_strings_translations (locales_dir, 0);
}
free (locales_dir);
}
if (not_found)
fprintf (stderr, "Locales dir for document strings not found\n");
}
else if (txi_paths_info.p.installed.converter_datadir)
{
xasprintf (&locales_dir, "%s/locale",
txi_paths_info.p.installed.converter_datadir);
setup_output_strings_translations (locales_dir, 0);
free (locales_dir);
}
}
int
txi_load_init_file (const char *file,
const INTERPRETER_LOADING_INFO *loading_info,
enum interpreter_use *embedded_interpreter)
{
int status = 0;
if (*embedded_interpreter == txi_interpreter_want_embedded)
{
int interpreter_status = txi_load_interpreter (loading_info);
if (!interpreter_status)
*embedded_interpreter = txi_interpreter_use_embedded;
}
if (*embedded_interpreter == txi_interpreter_use_embedded)
status = call_config_GNUT_load_init_file (file);
return status;
}
void
txi_stop_interpreter (enum interpreter_use embedded_interpreter)
{
if (embedded_interpreter == txi_interpreter_use_embedded)
call_finish_perl ();
}
/* hide call_eval_use_module from the main program */
void
txi_converter_output_format_setup (const char *converted_format,
const char *external_module)
{
if (external_module)
/* in that case the functions below are called from Perl */
call_eval_use_module (external_module);
}
/* ALTIMP Texinfo::Convert::XXXX */
/* This function should be used to get information on an output format
defaults, taking into account CUSTOMIZATIONS. It is not needed
for converter initialization, as similar code is already called.
Similar to Texinfo::Convert::XXXX->converter_defaults($options)
*/
CONVERTER_INITIALIZATION_INFO *
txi_converter_format_defaults (const char *converted_format,
const char *external_module,
OPTIONS_LIST *customizations)
{
enum converter_format converter_format;
CONVERTER_INITIALIZATION_INFO *conf;
CONVERTER_INITIALIZATION_INFO *format_defaults;
if (external_module)
{
format_defaults = call_module_converter_defaults (external_module,
customizations);
if (format_defaults)
return format_defaults;
}
converter_format
= find_format_name_converter_format (converted_format);
conf = new_converter_initialization_info ();
if (customizations)
copy_options_list (&conf->conf, customizations);
format_defaults = converter_defaults (converter_format, conf);
destroy_converter_initialization_info (conf);
return format_defaults;
}
/* ALTIMP Texinfo/ParserXS.pm */
/* parser initialization, similar to calling Texinfo::Parser::parser in Perl.
The implementation is different from Texinfo::ParserNonXS because here
we need to convert options list to parser configuration function calls,
as is done in ParserXS.pm with XS. This is not needed in ParserNonXS.pm.
The implementation is similar to ParserXS.pm on purpose.
Also sets INCLUDE_DIRECTORIES minimally if not specified in options,
FILE_PATH is only used in that case.
*/
void
txi_parser (const char *file_path, const VALUE_LIST *values,
OPTIONS_LIST *options_list)
{
char *input_file_name_and_directory[2];
char *input_directory;
size_t i;
int debug = 0;
int includes_set = 0;
/* special case, we need to know if debug is set before calling
reset_parser */
if (options_list)
{
if (options_list->options->DEBUG.o.integer >= 0)
debug = 1;
}
reset_parser (debug);
parser_conf_set_DEBUG (debug);
if (values)
{
parser_conf_reset_values ();
for (i = 0; i < values->number; i++)
{
parser_conf_add_value (values->list[i].name,
values->list[i].value);
}
}
if (options_list)
{
for (i = 0; i < options_list->number; i++)
{
size_t index = options_list->list[i] -1;
OPTION *option = options_list->sorted_options[index];
if (!strcmp (option->name, "INCLUDE_DIRECTORIES"))
{
includes_set = 1;
parser_conf_clear_INCLUDE_DIRECTORIES ();
if (option->o.strlist)
{
size_t j;
STRING_LIST *directories = option->o.strlist;
for (j = 0; j < directories->number; j++)
if (directories->list[j])
parser_conf_add_include_directory (directories->list[j]);
}
}
else if (!strcmp (option->name, "EXPANDED_FORMATS"))
{
parser_conf_clear_expanded_formats ();
if (option->o.strlist)
{
size_t j;
STRING_LIST *expanded_formats = option->o.strlist;
for (j = 0; j < expanded_formats->number; j++)
if (expanded_formats->list[j])
parser_conf_add_expanded_format
(expanded_formats->list[j]);
}
}
else if (!strcmp (option->name, "documentlanguage"))
{
if (option->o.string)
parser_conf_set_documentlanguage (option->o.string);
}
else if (!strcmp (option->name, "documentscript"))
{
if (option->o.string)
parser_conf_set_documentscript (option->o.string);
}
else if (!strcmp (option->name, "FORMAT_MENU"))
{
if (option->o.string
&& (!strcmp (option->o.string, "menu")
|| !strcmp (option->o.string, "menu_no_detailmenu")))
parser_conf_set_show_menu (1);
else
parser_conf_set_show_menu (0);
}
else if (!strcmp (option->name,
"IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME"))
parser_conf_set_IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME
(option->o.integer);
else if (!strcmp (option->name, "CPP_LINE_DIRECTIVES"))
parser_conf_set_CPP_LINE_DIRECTIVES (option->o.integer);
else if (!strcmp (option->name, "MAX_MACRO_CALL_NESTING"))
parser_conf_set_MAX_MACRO_CALL_NESTING (option->o.integer);
else if (!strcmp (option->name, "NO_INDEX"))
parser_conf_set_NO_INDEX (option->o.integer);
else if (!strcmp (option->name, "NO_USER_COMMANDS"))
parser_conf_set_NO_USER_COMMANDS (option->o.integer);
else if (!strcmp (option->name, "DOC_ENCODING_FOR_INPUT_FILE_NAME"))
parser_conf_set_DOC_ENCODING_FOR_INPUT_FILE_NAME
(option->o.integer);
else if (!strcmp (option->name, "INPUT_FILE_NAME_ENCODING"))
{
if (option->o.string)
parser_conf_set_INPUT_FILE_NAME_ENCODING (option->o.string);
}
else if (!strcmp (option->name, "LOCALE_ENCODING"))
{
if (option->o.string)
parser_conf_set_LOCALE_ENCODING (option->o.string);
}
else if (!strcmp (option->name, "COMMAND_LINE_ENCODING"))
{
if (option->o.string)
parser_conf_set_COMMAND_LINE_ENCODING (option->o.string);
}
else if (!strcmp (option->name, "accept_internalvalue"))
{
/* called from gdt, no need to store the parser configuration */
if (option->o.integer > 0)
parser_conf_set_accept_internalvalue (1);
/* $store_conf = 0; */
}
else if (strcmp (option->name, "DEBUG"))
{
fprintf (stderr, "ignoring parser configuration value \"%s\"\n",
option->name);
}
}
}
if (!includes_set)
{
if (file_path)
{
parse_file_path (file_path, input_file_name_and_directory);
input_directory = input_file_name_and_directory[1];
free (input_file_name_and_directory[0]);
if (input_directory && strcmp (input_directory, "."))
{
parser_conf_clear_INCLUDE_DIRECTORIES ();
parser_conf_add_include_directory (".");
parser_conf_add_include_directory (input_directory);
}
free (input_directory);
}
}
}
/* ALTIMP in texi2any.pl */
/* call all the structuring/transformations typically done for a document.
FLAGS select the structure/transformations called. If FORMAT_MENU is set
the structure functions related to menus are called.
No implementation in Perl as a function, as the modules are loaded on
demand, which makes it impossible to implement as a function.
*/
void
txi_complete_document (DOCUMENT *document, unsigned long flags,
int format_menu, CONVERTER *converter,
ELEMENT * (*element_cdt_tree_fn) (const char *string, const ELEMENT *element,
CONVERTER *self,
NAMED_STRING_ELEMENT_LIST *replaced_substrings,
const char *translation_context)
)
{
int use_sections = (flags & STTF_complete_menus_use_sections);
if (flags & STTF_fill_gaps_in_sectioning)
fill_gaps_in_sectioning_in_document (document, 0);
if (flags & STTF_relate_index_entries_to_table_items)
relate_index_entries_to_table_items_in_document (document);
if (flags & STTF_move_index_entries_after_items)
move_index_entries_after_items_in_document (document);
if (flags & STTF_insert_nodes_for_sectioning_commands)
insert_nodes_for_sectioning_commands (document);
associate_internal_references (document);
sectioning_structure (document);
if (!(flags & STTF_no_warn_non_empty_parts))
warn_non_empty_parts (document);
if (flags & STTF_complete_tree_nodes_menus)
complete_tree_nodes_menus_in_document (document, use_sections);
if (flags & STTF_complete_tree_nodes_missing_menu)
complete_tree_nodes_missing_menu (document, use_sections);
if (flags & STTF_regenerate_master_menu)
regenerate_master_menu (document, use_sections);
if (flags & STTF_nodes_tree)
{
construct_nodes_tree (document);
if (format_menu)
{
check_node_tree_menu_structure (document);
complete_node_tree_with_menus (document);
check_nodes_are_referenced (document);
}
}
if (flags & STTF_floats)
number_floats (document);
/* FIXME remove that, now that index sorting is mainly done by converters
and call the function explicitely instead if needed (for tests
probably)
if (flags & STTF_setup_index_entries_sort_strings)
document_indices_sort_strings (document, &document->error_messages,
document->options, converter);
*/
}
/* In texi2any.pl, not in a separate function */
/* setup CONF initialization data. */
void
txi_converter_initialization_setup (CONVERTER_INITIALIZATION_INFO *conf,
const DEPRECATED_DIRS_LIST *deprecated_dirs,
const OPTIONS_LIST *customizations)
{
if (deprecated_dirs)
copy_deprecated_dirs (&conf->deprecated_config_directories,
deprecated_dirs);
if (customizations)
copy_options_list (&conf->conf, customizations);
}
/* In texi2any.pl, not in a separate function */
/* converter setup. Similar to a converter initialization in texi2any.pl */
CONVERTER *
txi_converter_setup (const char *external_module,
const char *converted_format,
const CONVERTER_INITIALIZATION_INFO *converter_init_info)
{
enum converter_format converter_format;
CONVERTER *self;
if (external_module)
{
self = call_module_converter (external_module, converter_init_info);
if (!self)
{
char *message;
xasprintf (&message, "no interpreter or NULL return for module: %s",
external_module);
fatal (message);
free (message);
}
else
return self;
}
converter_format = find_format_name_converter_format (converted_format);
self = converter_converter (converter_format, converter_init_info);
return self;
}
/* high level interface, possibly hiding some details of the data */
DOCUMENT *
txi_parse_texi_file (const char *input_file_path, int *status)
{
return parse_file (input_file_path, status);
}
/* similar to Texinfo::Convert::XXX->output */
char *
txi_converter_output (CONVERTER *converter, DOCUMENT *document,
const char *external_module)
{
if (external_module)
{
size_t i;
OUTPUT_TEXT_FILES_INFO *output_text_files_info
= call_converter_output (converter, document);
OUTPUT_FILES_INFORMATION *output_files_information
= output_text_files_info->output_files_information;
FILE_STREAM_LIST *unclosed_files
= &output_files_information->unclosed_files;
char *text_result = 0;
if (output_text_files_info->text)
{
text_result = strdup (output_text_files_info->text);
free (output_text_files_info->text);
}
if (output_files_information)
{
copy_strings (&converter->output_files_information.opened_files,
&output_files_information->opened_files);
/* copy unclosed files */
for (i = 0; i < unclosed_files->number; i++)
{
register_unclosed_file (
&converter->output_files_information,
unclosed_files->list[i].file_path,
unclosed_files->list[i].stream,
unclosed_files->list[i].io);
}
free_output_files_information (output_files_information);
free (output_files_information);
}
free (output_text_files_info);
return text_result;
}
return converter_output (converter, document);
}
/* corresponds to, in texi2any.pl:
- load elements count module
- initialize converter
- call the converter specific method
*/
CONVERTER_TEXT_INFO *
txi_sort_element_counts (const char *external_module,
const OPTIONS_LIST *customizations,
DOCUMENT *document, int use_sections,
int count_words)
{
if (external_module)
{
CONVERTER_INITIALIZATION_INFO *converter_init_info
= new_converter_initialization_info ();
CONVERTER_TEXT_INFO *result = (CONVERTER_TEXT_INFO *)
malloc (sizeof (CONVERTER_TEXT_INFO));
txi_converter_initialization_setup (converter_init_info,
0, customizations);
call_eval_use_module (external_module);
result->converter
= call_module_converter (external_module, converter_init_info);
if (!result->converter)
{ /* the caller should make sure an interpreter is loaded */
char *message;
xasprintf (&message,
"no interpreter or NULL return for sort element count module: %s",
external_module);
fatal (message);
free (message);
return 0;
}
result->text = call_sort_element_counts (result->converter, document,
use_sections, count_words);
destroy_converter_initialization_info (converter_init_info);
return result;
}
/* cannot happen right now since the argument is set in the only
caller */
else
{
fatal ("no external module for sort element count");
return 0;
}
}
/* similar to Texinfo::Convert::XXX->convert */
char *
txi_converter_convert (CONVERTER *converter, DOCUMENT *document)
{
return converter_convert (converter, document);
}
/* ALTIMP Texinfo::Document::destroy_document */
void
txi_destroy_document (DOCUMENT *document, const char *external_module,
int remove_references)
{
ERROR_MESSAGE_LIST *error_messages = 0;
int check_counts = (document->options->TEST.o.integer > 1);
if (check_counts)
{
/* Call Perl function to remove Perl references when Perl code is used */
if (external_module)
call_document_remove_document_references (document,
remove_references);
error_messages = set_check_element_interpreter_refcount ();
}
destroy_document (document);
if (check_counts)
{
/* the messages are discarded. The same information has already
been printed on STDERR */
clear_error_message_list (error_messages);
unset_check_element_interpreter_refcount ();
}
}
/* ALTIMP Texinfo::Convert::Converter */
void
txi_converter_remove_output_units (CONVERTER *converter,
const char *external_module)
{
if (external_module)
release_converter_output_units_remove_perl_output_units (converter);
converter_remove_output_units (converter);
}
/* ALTIMP Texinfo::Convert::Converter */
void
txi_destroy_converter (CONVERTER *converter, const char *external_module)
{
if (external_module)
call_object_converter_perl_release (converter);
destroy_converter (converter);
}
/* defined here to hide call_close_perl_io PerlIO closing function call */
int
txi_close_file_stream (const char *program_file, const FILE_STREAM *file_stream)
{
int error_nrs = 0;
if (file_stream->stream)
{
if (fclose (file_stream->stream))
{
fprintf (stderr, _("%s: error on closing %s: %s"),
program_file, file_stream->file_path,
strerror (errno));
fprintf (stderr, "%s", "\n");
error_nrs++;
}
}
/* having io field set can not happen when C output_files_open_out
is called, only when getting conversion results from Perl.
*/
if (file_stream->io)
{
char *errno_message = call_close_perl_io (file_stream->io);
if (errno_message)
{
fprintf (stderr, _("%s: error on closing io %s: %s"),
program_file, file_stream->file_path,
errno_message);
fprintf (stderr, "%s", "\n");
free (errno_message);
error_nrs++;
}
}
return error_nrs;
}
size_t
txi_output_parser_error_messages (DOCUMENT *document,
const char *message_encoding,
int no_warn, int use_filename)
{
return output_error_messages (&document->parser_error_messages,
message_encoding, no_warn,
use_filename);
}
size_t
txi_output_document_error_messages (DOCUMENT *document,
const char *message_encoding,
int no_warn, int use_filename)
{
return output_error_messages (&document->error_messages, message_encoding,
no_warn, use_filename);
}
size_t
txi_output_converter_error_messages (CONVERTER *converter,
const char *message_encoding,
int no_warn, int use_filename)
{
return output_error_messages (&converter->error_messages, message_encoding,
no_warn, use_filename);
}
/* used to show the built-in CSS rules */
char *
txi_builtin_default_css_text (void)
{
setup_converter_format (COF_html);
return html_builtin_default_css_text ();
}