| /* Copyright 2010-2026 Free Software Foundation, Inc. |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| |
| #include <string.h> |
| #include <stdlib.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" |
| /* ppport.h suggests to remove its include, but we need it to |
| replace G_LIST by G_ARRAY if G_LIST is not defined */ |
| #include "ppport.h" |
| |
| #undef context |
| |
| #include "text.h" |
| #include "tree_types.h" |
| /* *BUTTON* */ |
| #include "option_types.h" |
| #include "converter_types.h" |
| #include "types_data.h" |
| /* non_perl_* */ |
| #include "xs_utils.h" |
| /* for element_command_name (or could be builtin_command_data) */ |
| #include "builtin_commands.h" |
| /* new_target_filename new_target_contents_filename |
| new_file_name_path new_target_directory_filename |
| new_formatted_button_info output_unit_type_names enum output_unit_type |
| */ |
| #include "utils.h" |
| #include "debug.h" |
| /* for newSVpv_utf8 build_tree_to_build */ |
| #include "build_perl_info.h" |
| /* build_html_formatting_state */ |
| #include "build_html_perl_state.h" |
| #include "call_html_perl_function.h" |
| |
| /* See the NOTE in build_perl_info.c on use of functions related to |
| memory allocation */ |
| |
| /* NOTE newSVpv_utf8 is used for file names because extensions may be supplied |
| by the user, the base file name may be ASCII, the extension may not. Also, |
| in some cases, file name could be TOP_NODE_FILE_TARGET. */ |
| |
| TARGET_FILENAME * |
| call_file_id_setting_special_unit_target_file_name (CONVERTER *self, |
| const OUTPUT_UNIT *special_unit, const char *target, |
| const char *default_filename) |
| { |
| SV *special_unit_target_file_name_sv; |
| |
| dTHX; |
| |
| special_unit_target_file_name_sv |
| = (SV *) self->file_id_setting_refs[FIS_special_unit_target_file_name]; |
| |
| if (special_unit_target_file_name_sv) |
| { |
| int count; |
| char *target_ret; |
| char *filename = 0; |
| SV *target_ret_sv; |
| SV *filename_sv; |
| STRLEN len; |
| TARGET_FILENAME *result = new_target_filename (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) special_unit->hv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (target, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (default_filename, 0))); |
| PUTBACK; |
| |
| count = call_sv (special_unit_target_file_name_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 2) |
| croak ("special_unit_target_file_name should return 2 items\n"); |
| |
| filename_sv = POPs; |
| if (SvOK (filename_sv)) |
| { |
| STRLEN len; |
| filename = SvPVutf8 (filename_sv, len); |
| result->filename = non_perl_strndup (filename, len); |
| } |
| |
| target_ret_sv = POPs; |
| target_ret = SvPVutf8 (target_ret_sv, len); |
| result->target = non_perl_strndup (target_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| return 0; |
| } |
| |
| char * |
| call_file_id_setting_label_target_name (CONVERTER *self, |
| const char *normalized, const ELEMENT *label_element, |
| const char *target, int *called) |
| { |
| SV *label_target_name_sv; |
| |
| dTHX; |
| |
| *called = 0; |
| |
| label_target_name_sv |
| = (SV *) self->file_id_setting_refs[FIS_label_target_name]; |
| |
| if (label_target_name_sv) |
| { |
| int count; |
| char *target_ret; |
| STRLEN len; |
| char *result; |
| SV *target_ret_sv; |
| SV *label_element_sv; |
| |
| if (label_element) |
| label_element_sv = newSVsv ((SV *) label_element->sv); |
| else |
| label_element_sv = newSV (0); |
| |
| *called = 1; |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (normalized, 0))); |
| PUSHs(sv_2mortal (label_element_sv)); |
| PUSHs(sv_2mortal (newSVpv (target, 0))); |
| PUTBACK; |
| |
| count = call_sv (label_target_name_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("label_target_name should return 1 item\n"); |
| |
| target_ret_sv = POPs; |
| target_ret = SvPVutf8 (target_ret_sv, len); |
| result = non_perl_strndup (target_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| return 0; |
| } |
| |
| |
| char * |
| call_file_id_setting_node_file_name (CONVERTER *self, |
| const ELEMENT *target_element, const char *node_filename, |
| int *called) |
| { |
| SV *node_file_name_sv; |
| |
| dTHX; |
| |
| *called = 0; |
| |
| node_file_name_sv |
| = (SV *) self->file_id_setting_refs[FIS_node_file_name]; |
| |
| if (node_file_name_sv) |
| { |
| int count; |
| char *result; |
| SV *node_filename_ret_sv; |
| *called = 1; |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) target_element->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (node_filename, 0))); |
| PUTBACK; |
| |
| count = call_sv (node_file_name_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("node_file_name should return 1 item\n"); |
| |
| node_filename_ret_sv = POPs; |
| /* user can return undef */ |
| if (SvOK (node_filename_ret_sv)) |
| { |
| char *node_filename_ret; |
| STRLEN len; |
| node_filename_ret = SvPVutf8 (node_filename_ret_sv, len); |
| result = non_perl_strndup (node_filename_ret, len); |
| } |
| else |
| result = 0; |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| return 0; |
| } |
| |
| TARGET_CONTENTS_FILENAME * |
| call_file_id_setting_sectioning_command_target_name (CONVERTER *self, |
| const ELEMENT *command, const char *target, |
| const char *target_contents, |
| const char *target_shortcontents, const char *filename) |
| { |
| SV *sectioning_command_target_name_sv; |
| |
| dTHX; |
| |
| sectioning_command_target_name_sv |
| = (SV *) self->file_id_setting_refs[FIS_sectioning_command_target_name]; |
| |
| if (sectioning_command_target_name_sv) |
| { |
| int count; |
| STRLEN len; |
| SV *target_ret_sv; |
| SV *target_contents_ret_sv; |
| SV *target_shortcontents_ret_sv; |
| SV *filename_ret_sv; |
| char *target_ret; |
| char *target_contents_ret; |
| char *target_shortcontents_ret; |
| char *filename_ret; |
| TARGET_CONTENTS_FILENAME *result = new_target_contents_filename (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 6); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) command->sv))); |
| PUSHs(sv_2mortal (newSVpv (target, 0))); |
| PUSHs(sv_2mortal (newSVpv (target_contents, 0))); |
| PUSHs(sv_2mortal (newSVpv (target_shortcontents, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUTBACK; |
| |
| count = call_sv (sectioning_command_target_name_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 4) |
| croak ("sectioning_command_target_name should return 4 items\n"); |
| |
| filename_ret_sv = POPs; |
| filename_ret = SvPVutf8 (filename_ret_sv, len); |
| result->filename = non_perl_strndup (filename_ret, len); |
| target_shortcontents_ret_sv = POPs; |
| target_shortcontents_ret = SvPVutf8 (target_shortcontents_ret_sv, |
| len); |
| result->target_shortcontents |
| = non_perl_strndup (target_shortcontents_ret, len); |
| target_contents_ret_sv = POPs; |
| target_contents_ret = SvPVutf8 (target_contents_ret_sv, len); |
| result->target_contents = non_perl_strndup (target_contents_ret, len); |
| target_ret_sv = POPs; |
| target_ret = SvPVutf8 (target_ret_sv, len); |
| result->target = non_perl_strndup (target_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| return 0; |
| } |
| |
| FILE_NAME_PATH * |
| call_file_id_setting_unit_file_name (CONVERTER *self, |
| const OUTPUT_UNIT *output_unit, |
| const char *filename, const char *filepath) |
| { |
| SV *unit_file_name_sv; |
| |
| dTHX; |
| |
| unit_file_name_sv |
| = (SV *) self->file_id_setting_refs[FIS_unit_file_name]; |
| |
| if (unit_file_name_sv) |
| { |
| int count; |
| SV *filepath_ret_sv; |
| SV *filename_ret_sv; |
| FILE_NAME_PATH *result = new_file_name_path (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) output_unit->hv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filepath, 0))); |
| PUTBACK; |
| |
| count = call_sv (unit_file_name_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 2) |
| croak ("unit_file_name should return 2 items\n"); |
| |
| filepath_ret_sv = POPs; |
| if (SvOK (filepath_ret_sv)) |
| { |
| STRLEN len; |
| char *filepath_ret = SvPVutf8 (filepath_ret_sv, len); |
| result->filepath = non_perl_strndup (filepath_ret, len); |
| } |
| |
| filename_ret_sv = POPs; |
| if (SvOK (filename_ret_sv)) |
| { |
| STRLEN len; |
| char *filename_ret = SvPVutf8 (filename_ret_sv, len); |
| result->filename = non_perl_strndup (filename_ret, len); |
| } |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| return 0; |
| } |
| |
| TARGET_DIRECTORY_FILENAME * |
| call_file_id_setting_external_target_split_name (CONVERTER *self, |
| const char *normalized, const ELEMENT *element, |
| const char *target, const char *directory, |
| const char *file_name) |
| { |
| SV *external_target_split_name_sv; |
| |
| dTHX; |
| |
| external_target_split_name_sv |
| = (SV *) self->file_id_setting_refs[FIS_external_target_split_name]; |
| |
| if (external_target_split_name_sv) |
| { |
| int count; |
| SV *target_sv; |
| SV *filename_sv; |
| SV *directory_sv; |
| TARGET_DIRECTORY_FILENAME *result = new_target_directory_filename (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 6); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (normalized, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUSHs(sv_2mortal (newSVpv (target, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (directory, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (file_name, 0))); |
| PUTBACK; |
| |
| count = call_sv (external_target_split_name_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 3) |
| croak ("external_target_split_name should return 3 items\n"); |
| |
| filename_sv = POPs; |
| if (SvOK (filename_sv)) |
| { |
| STRLEN len; |
| char *filename_ret = SvPVutf8 (filename_sv, len); |
| result->filename = non_perl_strndup (filename_ret, len); |
| } |
| else |
| result->filename = non_perl_strdup (""); |
| |
| directory_sv = POPs; |
| if (SvOK (directory_sv)) |
| { |
| STRLEN len; |
| char *directory_ret = SvPVutf8 (directory_sv, len); |
| result->directory = non_perl_strndup (directory_ret, len); |
| } |
| else |
| result->directory = non_perl_strdup (""); |
| |
| target_sv = POPs; |
| if (SvOK (target_sv)) |
| { |
| STRLEN len; |
| char *target_ret = SvPVutf8 (target_sv, len); |
| result->target = non_perl_strndup (target_ret, len); |
| } |
| else |
| result->target = non_perl_strdup (""); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| return 0; |
| } |
| |
| TARGET_FILENAME * |
| call_file_id_setting_external_target_non_split_name (CONVERTER *self, |
| const char *normalized, const ELEMENT *element, |
| const char *target, const char *file) |
| { |
| SV *external_target_non_split_name_sv; |
| |
| dTHX; |
| |
| external_target_non_split_name_sv |
| = (SV *) self->file_id_setting_refs[FIS_external_target_non_split_name]; |
| |
| if (external_target_non_split_name_sv) |
| { |
| int count; |
| SV *target_sv; |
| SV *file_sv; |
| TARGET_FILENAME *result = new_target_filename (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 5); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (normalized, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUSHs(sv_2mortal (newSVpv (target, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (file, 0))); |
| PUTBACK; |
| |
| count = call_sv (external_target_non_split_name_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 2) |
| croak ("external_target_non_split_name should return 2 items\n"); |
| |
| file_sv = POPs; |
| if (SvOK (file_sv)) |
| { |
| STRLEN len; |
| const char *file_ret = SvPVutf8 (file_sv, len); |
| result->filename = non_perl_strndup (file_ret, len); |
| } |
| |
| target_sv = POPs; |
| if (SvOK (target_sv)) |
| { |
| STRLEN len; |
| const char *target_ret = SvPVutf8 (target_sv, len); |
| result->target = non_perl_strndup (target_ret, len); |
| } |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| return 0; |
| } |
| |
| int |
| call_file_id_setting_redirection_file_names (CONVERTER *self, |
| const ELEMENT *element, const char *filename, |
| const char *node_redirection_filename, |
| STRING_LIST *reference_redirection_files) |
| { |
| SV *redirection_file_names_sv; |
| |
| dTHX; |
| |
| redirection_file_names_sv |
| = (SV *) self->file_id_setting_refs[FIS_redirection_file_names]; |
| |
| if (redirection_file_names_sv) |
| { |
| int count, i; |
| int defined_count = 0; |
| char **result_redirection_files = 0; |
| AV *reference_redirection_files_av; |
| |
| reference_redirection_files_av |
| = build_string_list (reference_redirection_files, svt_char); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 5); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (node_redirection_filename, 0))); |
| PUSHs(sv_2mortal (newRV_noinc ((SV *) reference_redirection_files_av))); |
| PUTBACK; |
| |
| count = call_sv (redirection_file_names_sv, G_LIST); |
| |
| SPAGAIN; |
| |
| /* no returned items is valid */ |
| /* if (count == 0) |
| croak ("redirection_file_names should return items\n"); |
| */ |
| |
| if (count > 0) |
| { |
| /* Store and count non-undef strings returned by Perl. |
| The last string returned is first in result_redirection_files */ |
| result_redirection_files = (char **) |
| malloc (count * sizeof (char *)); |
| |
| for (i = 0; i < count; i++) |
| { |
| SV *file_sv = POPs; |
| if (SvOK (file_sv)) |
| { |
| STRLEN len; |
| const char *file_ret = SvPVutf8 (file_sv, len); |
| result_redirection_files[defined_count] |
| = perl_only_strndup (file_ret, len); |
| defined_count++; |
| } |
| } |
| } |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| clear_strings_list (reference_redirection_files); |
| |
| /* fill reference_redirection_files with returned values, |
| in reverse order to end up with first being first */ |
| if (defined_count) |
| { |
| for (i = defined_count -1; i >= 0; i--) |
| { |
| add_string (result_redirection_files[i], |
| reference_redirection_files); |
| free (result_redirection_files[i]); |
| } |
| } |
| free (result_redirection_files); |
| |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| |
| |
| char * |
| call_formatting_function_format_comment (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *text) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 2); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (text, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_comment should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_program_string (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 1); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_program_string should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_titlepage (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 1); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_titlepage should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_title_titlepage (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 1); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_title_titlepage should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_protect_text (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *text) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 2); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (text, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_protect_text should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_footnotes_segment (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 1); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_footnotes_segment should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_single_footnote (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const ELEMENT *element, const char *footid, |
| int number_in_doc, |
| const char *footnote_location_href, const char *mark) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 1); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (footid, 0))); |
| PUSHs(sv_2mortal (newSViv ((IV) number_in_doc))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (footnote_location_href, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (mark, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_single_footnote should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_footnotes_sequence (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 1); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_footnotes_sequence should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_css_lines (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *filename) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 2); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_css_lines should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_end_file (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *filename, const OUTPUT_UNIT *output_unit) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| SV *output_unit_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| if (output_unit) |
| output_unit_sv = newRV_inc ((SV *) output_unit->hv); |
| else |
| output_unit_sv = newSV (0); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUSHs(sv_2mortal (output_unit_sv)); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_end_file should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_begin_file (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *filename, |
| const OUTPUT_UNIT *output_unit) |
| { |
| int count; |
| char *result; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| SV *output_unit_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| if (output_unit) |
| output_unit_sv = newRV_inc ((SV *) output_unit->hv); |
| else |
| output_unit_sv = newSV (0); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUSHs(sv_2mortal (output_unit_sv)); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_begin_file should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_translate_message (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *message, const char *lang, |
| const char *message_context) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (message, 0))); |
| PUSHs(sv_2mortal (newSVpv (lang, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (message_context, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_translate_message should return 1 item\n"); |
| |
| result_sv = POPs; |
| if (SvOK (result_sv)) |
| { |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| } |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_button_icon_img (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *button_name, |
| const char *icon, const char *name) |
| |
| { |
| int count; |
| SV *name_sv; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| if (name) |
| name_sv = newSVpv_utf8 (name, 0); |
| else |
| name_sv = newSV (0); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (button_name, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (icon, 0))); |
| PUSHs(sv_2mortal (name_sv)); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_button_icon_img should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| FORMATTED_BUTTON_INFO * |
| call_formatting_function_format_button (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const BUTTON_SPECIFICATION *button, |
| const ELEMENT *element) |
| { |
| int count; |
| SV *need_delimiter_sv; |
| SV *passive_sv; |
| SV *active_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| FORMATTED_BUTTON_INFO *result = new_formatted_button_info (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) button->sv))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 3) |
| croak ("format_button should return 3 items\n"); |
| |
| need_delimiter_sv = POPs; |
| if (SvOK (need_delimiter_sv)) |
| { |
| result->need_delimiter = SvIV (need_delimiter_sv); |
| } |
| |
| passive_sv = POPs; |
| if (SvOK (passive_sv)) |
| { |
| STRLEN len; |
| const char *passive_ret = SvPVutf8 (passive_sv, len); |
| result->passive = non_perl_strndup (passive_ret, len); |
| } |
| |
| active_sv = POPs; |
| if (SvOK (active_sv)) |
| { |
| STRLEN len; |
| const char *active_ret = SvPVutf8 (active_sv, len); |
| result->active = non_perl_strndup (active_ret, len); |
| } |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_navigation_panel (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| BUTTON_SPECIFICATION_LIST *buttons, |
| const char *cmdname, const ELEMENT *element, |
| int vertical, int in_header) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| if (!buttons->av) |
| html_build_buttons_specification (self, buttons); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 5); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) buttons->av))); |
| PUSHs(sv_2mortal (newSVpv (cmdname, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUSHs(sv_2mortal (newSViv ((IV) vertical))); |
| PUSHs(sv_2mortal (newSViv ((IV) in_header))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_navigation_panel should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_navigation_header (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| BUTTON_SPECIFICATION_LIST *buttons, |
| const char *cmdname, |
| const ELEMENT *element) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| if (!buttons->av) |
| html_build_buttons_specification (self, buttons); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) buttons->av))); |
| PUSHs(sv_2mortal (newSVpv (cmdname, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_navigation_header should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_heading_text (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *cmdname, |
| const STRING_LIST *classes, |
| const char *text, |
| int level, const char *id, |
| const ELEMENT *element, const char *target) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| SV *classes_sv; |
| SV *element_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| if (classes && classes->number) |
| { |
| size_t i; |
| AV *classes_av = newAV (); |
| for (i = 0; i < classes->number; i++) |
| { |
| char *class = classes->list[i]; |
| SV *class_sv = newSVpv_utf8 (class, 0); |
| av_push (classes_av, class_sv); |
| } |
| classes_sv = newRV_noinc ((SV *) classes_av); |
| } |
| else |
| classes_sv = newSV (0); |
| |
| if (element) |
| element_sv = newSVsv ((SV *) element->sv); |
| else |
| element_sv = newSV (0); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 7); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (cmdname, 0))); |
| PUSHs(sv_2mortal (classes_sv)); |
| PUSHs(sv_2mortal (newSVpv_utf8 (text, 0))); |
| PUSHs(sv_2mortal (newSViv ((IV) level))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (id, 0))); |
| PUSHs(sv_2mortal (element_sv)); |
| PUSHs(sv_2mortal (newSVpv_utf8 (target, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_heading_text should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_contents (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *cmdname, const ELEMENT *command, |
| const char *filename) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *command_sv; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| if (command) |
| command_sv = newSVsv ((SV *) command->sv); |
| else |
| command_sv = newSV (0); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (cmdname, 0))); |
| PUSHs(sv_2mortal (command_sv)); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_contents should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_separate_anchor (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *id, const char *class) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (id, 0))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (class, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_separate_anchor should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_element_header (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const char *cmdname, const ELEMENT *command, |
| const OUTPUT_UNIT *output_unit) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (cmdname, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) command->sv))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) output_unit->hv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_element_header should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_element_footer (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const enum output_unit_type unit_type, |
| const OUTPUT_UNIT *output_unit, |
| const char *content, const ELEMENT *command) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 5); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (output_unit_type_names[unit_type], 0))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) output_unit->hv))); |
| /* content == 0 is possible, hope that newSVpv result corresponds to |
| undef in that case, but could also need to explicitely use newSV(0) */ |
| PUSHs(sv_2mortal (newSVpv_utf8 (content, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) command->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_element_footer should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| char * |
| call_formatting_function_format_node_redirection_page (CONVERTER *self, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const ELEMENT *command, const char *filename) |
| { |
| int count; |
| char *result = 0; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) command->sv))); |
| PUSHs(sv_2mortal (newSVpv_utf8 (filename, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("format_node_redirection_page should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| |
| |
| void |
| call_types_conversion (CONVERTER *self, const enum element_type type, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const ELEMENT *element, const char *content, |
| TEXT *result) |
| { |
| int count; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| if (!element->sv) |
| { |
| char *element_str = print_element_debug (element, 0); |
| fprintf (stderr, "BUG: no sv: %p %s\n", element, element_str); |
| non_perl_free (element_str); |
| abort (); |
| } |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (type_data[type].name, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| /* content == 0 is possible, hope that newSVpv result corresponds to |
| undef in that case, but could also need to explicitely use newSV(0) */ |
| PUSHs(sv_2mortal (newSVpv_utf8 (content, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("types_conversion should return 1 item\n"); |
| |
| result_sv = POPs; |
| /* it is encoded using non strict encoding, so the UTF-8 could be invalid. |
| It could be possible to add a wrapper in perl that encode to UTF-8, |
| but probably not worth it */ |
| result_ret = SvPVutf8 (result_sv, len); |
| text_append_n (result, result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| } |
| |
| void |
| call_types_open (CONVERTER *self, const enum element_type type, |
| const ELEMENT *element, TEXT *result) |
| { |
| int count; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| formatting_reference_sv = self->types_open[type].sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (type_data[type].name, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("types_open should return 1 item\n"); |
| |
| result_sv = POPs; |
| /* it is encoded using non strict encoding, so the UTF-8 could be invalid. |
| It could be possible to add a wrapper in perl that encode to UTF-8, |
| but probably not worth it */ |
| result_ret = SvPVutf8 (result_sv, len); |
| text_append_n (result, result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| } |
| |
| static const char *html_argument_formatting_type_names[] = { |
| #define html_aft_type(name) #name, |
| HTML_ARGUMENTS_FORMATTED_FORMAT_TYPE |
| #undef html_aft_type |
| }; |
| |
| static SV * |
| build_html_command_formatted_args (const HTML_ARGS_FORMATTED *args_formatted) |
| { |
| AV *av; |
| size_t i; |
| |
| dTHX; |
| |
| if (!args_formatted) |
| return newSV (0); |
| |
| av = newAV (); |
| |
| for (i = 0; i < args_formatted->number; i++) |
| { |
| const HTML_ARG_FORMATTED *arg_formatted = &args_formatted->args[i]; |
| if (arg_formatted->arg_tree) |
| { |
| int j; |
| HV *arg_formated_hv = newHV (); |
| av_push (av, newRV_noinc ((SV *) arg_formated_hv)); |
| |
| hv_store (arg_formated_hv, "arg_tree", strlen ("arg_tree"), |
| newSVsv ((SV *) arg_formatted->arg_tree->sv), 0); |
| |
| for (j = 0; j < AFT_type_raw+1; j++) |
| { |
| if (arg_formatted->formatted[j]) |
| { |
| const char *format_name |
| = html_argument_formatting_type_names[j]; |
| hv_store (arg_formated_hv, format_name, strlen (format_name), |
| newSVpv_utf8 (arg_formatted->formatted[j], 0), 0); |
| } |
| } |
| } |
| else |
| av_push (av, newSV(0)); |
| } |
| |
| return newRV_noinc ((SV *) av); |
| } |
| |
| void |
| call_commands_conversion (CONVERTER *self, const enum command_id cmd, |
| const FORMATTING_REFERENCE *formatting_reference, |
| const ELEMENT *element, |
| const HTML_ARGS_FORMATTED *args_formatted, |
| const char *content, TEXT *result) |
| { |
| int count; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| SV *args_formatted_sv; |
| const char *command_name; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| /* could also be builtin_command_data[cmd].cmdname as cmd |
| can only be < BUILTIN_CMD_NUMBER for two reasons: |
| - cmd is element_builtin_cmd (element) in convert_to_html_internal |
| - registered cmd are < BUILTIN_CMD_NUMBER |
| */ |
| command_name = element_command_name (element); |
| |
| formatting_reference_sv = formatting_reference->sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| args_formatted_sv = build_html_command_formatted_args (args_formatted); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 5); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (command_name, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUSHs(sv_2mortal (args_formatted_sv)); |
| /* content == 0 is possible, hope that newSVpv result corresponds to |
| undef in that case, but could also need to explicitely use newSV(0) */ |
| PUSHs(sv_2mortal (newSVpv_utf8 (content, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("commands_conversion should return 1 item\n"); |
| |
| result_sv = POPs; |
| /* it is encoded using non strict encoding, so the UTF-8 could be invalid. |
| It could be possible to add a wrapper in perl that encode to UTF-8, |
| but probably not worth it */ |
| result_ret = SvPVutf8 (result_sv, len); |
| text_append_n (result, result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| } |
| |
| void |
| call_commands_open (CONVERTER *self, const enum command_id cmd, |
| const ELEMENT *element, TEXT *result) |
| { |
| int count; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| const char *command_name; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| formatting_reference_sv = self->commands_open[cmd].sv_reference; |
| |
| /* could also be builtin_command_data[cmd].cmdname as cmd |
| can only be < BUILTIN_CMD_NUMBER for two reasons: |
| - cmd is element_builtin_cmd (element) in convert_to_html_internal |
| - registered cmd are < BUILTIN_CMD_NUMBER |
| */ |
| command_name = element_command_name (element); |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (command_name, 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("commands_open should return 1 item\n"); |
| |
| result_sv = POPs; |
| /* it is encoded using non strict encoding, so the UTF-8 could be invalid. |
| It could be possible to add a wrapper in perl that encode to UTF-8, |
| but probably not worth it */ |
| result_ret = SvPVutf8 (result_sv, len); |
| text_append_n (result, result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| } |
| |
| void |
| call_output_units_conversion (CONVERTER *self, |
| const enum output_unit_type unit_type, |
| const OUTPUT_UNIT *output_unit, |
| const char *content, TEXT *result) |
| { |
| int count; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| formatting_reference_sv |
| = self->output_units_conversion[unit_type].sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (output_unit_type_names[unit_type], 0))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) output_unit->hv))); |
| /* content == 0 is possible, hope that newSVpv result corresponds to |
| undef in that case, but could also need to explicitely use newSV(0) */ |
| PUSHs(sv_2mortal (newSVpv_utf8 (content, 0))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("output_units_conversion should return 1 item\n"); |
| |
| |
| result_sv = POPs; |
| /* it is encoded using non strict encoding, so the UTF-8 could be invalid. |
| It could be possible to add a wrapper in perl that encode to UTF-8, |
| but probably not worth it */ |
| result_ret = SvPVutf8 (result_sv, len); |
| text_append_n (result, result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| } |
| |
| void |
| call_special_unit_body_formatting (CONVERTER *self, |
| const size_t special_unit_number, |
| const char *special_unit_variety, |
| const OUTPUT_UNIT *output_unit, |
| TEXT *result) |
| { |
| int count; |
| const char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV *formatting_reference_sv; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| formatting_reference_sv |
| = self->special_unit_body[special_unit_number -1].sv_reference; |
| |
| build_html_formatting_state (self); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 4); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (special_unit_variety, 0))); |
| PUSHs(sv_2mortal (newRV_inc ((SV *) output_unit->hv))); |
| PUTBACK; |
| |
| count = call_sv (formatting_reference_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("special_unit_body_formatting should return 1 item\n"); |
| |
| |
| result_sv = POPs; |
| /* it is encoded using non strict encoding, so the UTF-8 could be invalid. |
| It could be possible to add a wrapper in perl that encode to UTF-8, |
| but probably not worth it */ |
| result_ret = SvPVutf8 (result_sv, len); |
| text_append_n (result, result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| } |
| |
| |
| |
| FORMATTED_BUTTON_INFO * |
| call_button_simple_function (CONVERTER *self, |
| void *formatting_reference_sv) |
| { |
| int count; |
| FORMATTED_BUTTON_INFO *result; |
| SV *need_delimiter_sv; |
| SV *active_sv; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| build_html_formatting_state (self); |
| |
| result = new_formatted_button_info (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 1); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUTBACK; |
| |
| count = call_sv ((SV *) formatting_reference_sv, |
| G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 2) |
| croak ("button_simple_function should return 2 items\n"); |
| |
| need_delimiter_sv = POPs; |
| if (SvOK (need_delimiter_sv)) |
| { |
| result->need_delimiter = SvIV (need_delimiter_sv); |
| } |
| |
| active_sv = POPs; |
| if (SvOK (active_sv)) |
| { |
| STRLEN len; |
| char *active_ret = SvPVutf8 (active_sv, len); |
| result->active = non_perl_strndup (active_ret, len); |
| } |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| FORMATTED_BUTTON_INFO * |
| call_button_direction_function (CONVERTER *self, |
| void *formatting_reference_sv, |
| int direction, const ELEMENT *element) |
| { |
| int count; |
| FORMATTED_BUTTON_INFO *result; |
| SV *need_delimiter_sv; |
| SV *active_sv; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| build_html_formatting_state (self); |
| |
| result = new_formatted_button_info (); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (newSVpv (self->main_units_direction_names[direction], |
| 0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| |
| PUTBACK; |
| |
| count = call_sv ((SV *) formatting_reference_sv, |
| G_LIST); |
| |
| SPAGAIN; |
| |
| if (count != 2) |
| croak ("button_direction_function should return 2 items\n"); |
| |
| need_delimiter_sv = POPs; |
| if (SvOK (need_delimiter_sv)) |
| { |
| result->need_delimiter = SvIV (need_delimiter_sv); |
| } |
| |
| active_sv = POPs; |
| if (SvOK (active_sv)) |
| { |
| STRLEN len; |
| char *active_ret = SvPVutf8 (active_sv, len); |
| result->active = non_perl_strndup (active_ret, len); |
| } |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| |
| |
| /* not in a more generic code file because it depends on build_tree_to_build */ |
| |
| char * |
| call_latex_convert_to_latex_math (CONVERTER *self, const ELEMENT *element) |
| { |
| int count; |
| char *result; |
| char *result_ret; |
| STRLEN len; |
| SV *result_sv; |
| SV **options_latex_math_sv; |
| SV *options_latex_math; |
| HV *converter_hv; |
| |
| dTHX; |
| |
| build_tree_to_build (&self->tree_to_build); |
| |
| dSP; |
| |
| converter_hv = (HV *) SvRV ((SV *) self->sv); |
| |
| options_latex_math_sv = hv_fetch (converter_hv, "options_latex_math", |
| strlen ("options_latex_math"), 0); |
| |
| if (options_latex_math_sv) |
| { |
| /* increase the refcount for the call */ |
| options_latex_math = SvREFCNT_inc (*options_latex_math_sv); |
| } |
| else |
| { |
| /* NOTE this case should never happen. If it does, we could set the |
| options here dynamically */ |
| fprintf (stderr, "BUG: no options_latex_math in %p %p\n", |
| converter_hv, self->sv); |
| options_latex_math = newSV (0); |
| } |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (newSV (0))); |
| PUSHs(sv_2mortal (newSVsv ((SV *) element->sv))); |
| PUSHs(sv_2mortal (options_latex_math)); |
| PUTBACK; |
| |
| count = call_pv ( |
| "Texinfo::Convert::LaTeX::convert_to_latex_math", |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("convert_to_latex_math should return 1 item\n"); |
| |
| result_sv = POPs; |
| result_ret = SvPVutf8 (result_sv, len); |
| result = non_perl_strndup (result_ret, len); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return result; |
| } |
| |
| int |
| call_stage_handler (CONVERTER *self, void *stage_handler_sv, |
| const char *stage_name, int *error_status) |
| { |
| int count; |
| SV *document_sv = 0; |
| SV *result_sv; |
| int status; |
| |
| dTHX; |
| |
| *error_status = 0; |
| |
| if (self->document) |
| { |
| HV *converter_hv = (HV *) SvRV ((SV *) self->sv); |
| SV **document_ref_sv = hv_fetch (converter_hv, "document", |
| strlen ("document"), 0); |
| if (document_ref_sv && SvOK (*document_ref_sv)) |
| { |
| /* increase the refcount for the call */ |
| document_sv = SvREFCNT_inc (*document_ref_sv); |
| } |
| } |
| |
| if (!document_sv) |
| document_sv = newSV (0); |
| |
| dSP; |
| |
| ENTER; |
| SAVETMPS; |
| |
| PUSHMARK(SP); |
| EXTEND(SP, 3); |
| |
| PUSHs(sv_2mortal (SvREFCNT_inc ((SV *) self->sv))); |
| PUSHs(sv_2mortal (document_sv)); |
| PUSHs(sv_2mortal (newSVpv (stage_name, 0))); |
| |
| PUTBACK; |
| |
| count = call_sv ((SV *) stage_handler_sv, |
| G_SCALAR); |
| |
| SPAGAIN; |
| |
| if (count != 1) |
| croak ("call_stage_handler should return 1 item\n"); |
| |
| |
| result_sv = POPs; |
| if (!SvOK (result_sv) || SvROK (result_sv) || !looks_like_number(result_sv)) |
| { |
| status = 1; |
| *error_status = 1; |
| } |
| else |
| status = (int) SvIV (result_sv); |
| |
| PUTBACK; |
| |
| FREETMPS; |
| LEAVE; |
| |
| return status; |
| } |