use crate::abi::allocator::xmlFreeImpl;
use crate::abi::exports_xml2::*;
use crate::abi::structs::*;
use crate::abi::types::xmlElementType::*;
use crate::abi::types::xmlXPathObjectType;
use crate::abi::types::*;
use crate::xml::tree::*;
use std::ffi::c_void;
use std::os::raw::{c_char, c_int};
use std::ptr;
use super::compiler::{get_element_name, get_element_ns, is_xslt_element, is_xslt_namespace};
pub const XSLT_MAX_DEPTH: c_int = 3000;
pub const XSLT_MAX_INSERT_DEPTH: c_int = 50;
pub const XSLT_STATE_OK: c_int = 0;
pub const XSLT_STATE_ERROR: c_int = 1;
pub const XSLT_STATE_STOPPED: c_int = 2;
#[no_mangle]
pub static mut xsltMaxDepth: c_int = 30000;
#[no_mangle]
pub static mut xsltMaxVars: c_int = 15000;
static mut XSLT_XINCLUDE_DEFAULT: c_int = 0;
#[no_mangle]
pub unsafe extern "C" fn xsltSetXIncludeDefault(xinclude: c_int) {
unsafe { XSLT_XINCLUDE_DEFAULT = if xinclude != 0 { 1 } else { 0 } };
}
#[no_mangle]
pub unsafe extern "C" fn xsltGetXIncludeDefault() -> c_int {
unsafe { XSLT_XINCLUDE_DEFAULT }
}
#[no_mangle]
pub unsafe extern "C" fn xsltNewTransformContext(
style: *mut _xsltStylesheet,
doc: *mut _xmlDoc,
) -> *mut _xsltTransformContext {
if style.is_null() {
return ptr::null_mut();
}
let ctxt = libc::calloc(1, core::mem::size_of::<_xsltTransformContext>())
as *mut _xsltTransformContext;
if ctxt.is_null() {
return ptr::null_mut();
}
(*ctxt).style = style;
(*ctxt).state = XSLT_STATE_OK;
(*ctxt).parserOptions = 2 | 4 | 8 | 16384;
(*ctxt).maxTemplateDepth = unsafe { xsltMaxDepth };
(*ctxt).maxTemplateVars = unsafe { xsltMaxVars };
let xpath_ctxt = xmlXPathNewContext(doc);
if !xpath_ctxt.is_null() {
(*ctxt).xpathCtxt = xpath_ctxt;
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if !internal.is_null() {
(*internal).func_lookup_data = ctxt as *mut c_void;
}
crate::abi::exports_xslt_functions::xsltRegisterAllFunctions(xpath_ctxt);
}
(*ctxt).sec = crate::xslt::security::xsltGetDefaultSecurityPrefs();
if !doc.is_null() {
let docu = libc::calloc(1, core::mem::size_of::<_xsltDocument>()) as *mut _xsltDocument;
if !docu.is_null() {
(*docu).main = 1;
(*docu).doc = doc;
(*ctxt).document = docu;
(*ctxt).docList = docu;
}
(*ctxt).initialContextDoc = doc;
(*ctxt).initialContextNode = doc as *mut _xmlNode;
}
ctxt
}
#[no_mangle]
pub unsafe extern "C" fn xsltFreeTransformContext(ctxt: *mut _xsltTransformContext) {
if ctxt.is_null() {
return;
}
if !(*ctxt).xpathCtxt.is_null() {
xmlXPathFreeContext((*ctxt).xpathCtxt);
}
crate::xslt::variables::xsltFreeGlobalVariables(ctxt);
crate::xslt::keys::xsltFreeKeyTables(ctxt);
crate::xslt::documents::xsltFreeDocCache(ctxt);
crate::xslt::extensions::xsltFreeExts(ctxt);
if !(*ctxt).varsTab.is_null() {
libc::free((*ctxt).varsTab as *mut libc::c_void);
}
if !(*ctxt).templTab.is_null() {
libc::free((*ctxt).templTab as *mut libc::c_void);
}
if !(*ctxt).document.is_null() {
let docu = (*ctxt).document;
(*docu).doc = ptr::null_mut();
libc::free(docu as *mut libc::c_void);
(*ctxt).document = ptr::null_mut();
}
libc::free(ctxt as *mut libc::c_void);
}
#[no_mangle]
pub unsafe extern "C" fn xsltApplyStylesheet(
style: *mut _xsltStylesheet,
doc: *mut _xmlDoc,
params: *mut *const c_char,
) -> *mut _xmlDoc {
xsltApplyStylesheetUser(
style,
doc,
params,
ptr::null(),
ptr::null_mut(),
ptr::null_mut(),
)
}
#[no_mangle]
pub unsafe extern "C" fn xsltApplyStylesheetUser(
style: *mut _xsltStylesheet,
doc: *mut _xmlDoc,
params: *mut *const c_char,
_output: *const c_char,
_profile: *mut c_void,
userCtxt: *mut _xsltTransformContext,
) -> *mut _xmlDoc {
if style.is_null() || doc.is_null() {
return ptr::null_mut();
}
let mut ctxt = userCtxt;
let mut own_ctxt = false;
if ctxt.is_null() {
ctxt = xsltNewTransformContext(style, doc);
if ctxt.is_null() {
return ptr::null_mut();
}
own_ctxt = true;
}
if !params.is_null() {
crate::xslt::parameters::xsltParseStylesheetParams(style, params);
}
crate::xslt::variables::xsltInitGlobalVariables(ctxt);
crate::xslt::keys::xsltInitKeys(ctxt, style);
crate::xslt::whitespace::xsltApplyStripSpaces(style, doc);
let result = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
if result.is_null() {
if own_ctxt {
xsltFreeTransformContext(ctxt);
}
return ptr::null_mut();
}
(*result).type_ = XML_DOCUMENT_NODE as c_int;
(*result).version = crate::xml::string::xml_strdup(c"1.0".as_ptr() as *const xmlChar);
(*result).doc = result;
if !(*style).encoding.is_null() {
(*result).encoding = crate::xml::string::xml_strdup((*style).encoding);
}
if !(*style).version.is_null() {
let v = crate::xml::string::xml_strdup((*style).version);
if !v.is_null() {
if !(*result).version.is_null() {
libc::free((*result).version as *mut libc::c_void);
}
(*result).version = v;
}
}
(*ctxt).initialContextDoc = doc;
(*ctxt).initialContextNode = doc as *mut _xmlNode;
(*ctxt).output = result;
(*ctxt).insert = result as *mut _xmlNode;
(*ctxt).node = doc as *mut _xmlNode;
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).node = doc as *mut _xmlNode;
(*(*ctxt).xpathCtxt).doc = doc;
(*(*ctxt).xpathCtxt).contextSize = 1;
(*(*ctxt).xpathCtxt).proximityPosition = 1;
}
let result_code = apply_templates_to_node(ctxt, doc as *mut _xmlNode, ptr::null());
let _ = result_code;
let final_result = if (*ctxt).state == XSLT_STATE_OK {
result
} else {
free_doc(result);
ptr::null_mut()
};
if own_ctxt {
(*ctxt).output = ptr::null_mut();
xsltFreeTransformContext(ctxt);
}
final_result
}
#[no_mangle]
pub unsafe extern "C" fn xsltApplyStylesheetStacked(
style: *mut _xsltStylesheet,
doc: *mut _xmlDoc,
params: *mut *const c_char,
_stack: *mut c_void,
) -> *mut _xmlDoc {
xsltApplyStylesheet(style, doc, params)
}
#[no_mangle]
pub unsafe extern "C" fn xsltFreeTransformResult(result: *mut _xmlDoc) {
if !result.is_null() {
free_doc(result);
}
}
#[no_mangle]
pub unsafe extern "C" fn xsltRunStylesheetUser(
style: *mut _xsltStylesheet,
doc: *mut _xmlDoc,
params: *mut *const c_char,
output: *const c_char,
SAX: *mut crate::abi::structs::_xmlSAXHandler,
IObuf: *mut crate::abi::structs::_xmlOutputBuffer,
profile: *mut c_void,
userCtxt: *mut _xsltTransformContext,
) -> c_int {
if output.is_null() && SAX.is_null() && IObuf.is_null() {
return -1;
}
if !SAX.is_null() && !IObuf.is_null() {
return -1;
}
if !SAX.is_null() {
return -1;
}
let tmp = xsltApplyStylesheetUser(style, doc, params, output, profile, userCtxt);
if tmp.is_null() {
eprintln!("xsltRunStylesheet : run failed");
return -1;
}
let ret = if !IObuf.is_null() {
let mut txt: *mut xmlChar = ptr::null_mut();
let mut len: c_int = 0;
let r = crate::xslt::serialization::xsltSaveResultToString(&mut txt, &mut len, tmp, style);
if r != 0 || txt.is_null() {
-1
} else {
let written = crate::xml::io::output_buffer_write(IObuf, len, txt as *const c_char);
crate::abi::allocator::xmlFreeImpl(txt as *mut c_void);
written
}
} else {
crate::xslt::serialization::xsltSaveResultToFilename(output, tmp, style, 0)
};
free_doc(tmp);
ret
}
#[no_mangle]
pub unsafe extern "C" fn xsltRunStylesheet(
style: *mut _xsltStylesheet,
doc: *mut _xmlDoc,
params: *mut *const c_char,
output: *const c_char,
SAX: *mut crate::abi::structs::_xmlSAXHandler,
IObuf: *mut crate::abi::structs::_xmlOutputBuffer,
) -> c_int {
xsltRunStylesheetUser(
style,
doc,
params,
output,
SAX,
IObuf,
ptr::null_mut(),
ptr::null_mut(),
)
}
#[allow(dead_code)]
pub(crate) unsafe fn apply_root_template(
ctxt: *mut _xsltTransformContext,
doc: *mut _xmlDoc,
) -> c_int {
let style = (*ctxt).style;
if style.is_null() {
return -1;
}
let doc_node = doc as *mut _xmlNode;
(*ctxt).node = doc_node;
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).contextSize = 1;
(*(*ctxt).xpathCtxt).proximityPosition = 1;
}
let templ = crate::xslt::templates::xsltFindTemplate(style, doc_node, ptr::null());
if templ.is_null() {
return 0;
}
let mut vars_base = (*ctxt).varsNr;
let _ = &mut vars_base;
(*ctxt).templ = templ;
execute_content(ctxt, (*templ).content);
(*ctxt).templ = ptr::null_mut();
0
}
pub(crate) unsafe fn apply_templates_to_node(
ctxt: *mut _xsltTransformContext,
node: *mut _xmlNode,
mode: *const xmlChar,
) -> c_int {
let style = (*ctxt).style;
if style.is_null() {
return -1;
}
let templ = crate::xslt::templates::xsltFindTemplate(style, node, mode);
if templ.is_null() {
let typ = (*node).type_;
if typ == XML_TEXT_NODE as c_int
|| typ == XML_CDATA_SECTION_NODE as c_int
|| typ == XML_ATTRIBUTE_NODE as c_int
{
let content = node_get_content(node);
if !content.is_null() {
append_text_node(ctxt, content);
libc::free(content as *mut libc::c_void);
}
} else if typ == XML_ELEMENT_NODE as c_int
|| typ == XML_DOCUMENT_NODE as c_int
|| typ == XML_HTML_DOCUMENT_NODE as c_int
{
apply_templates_to_children(ctxt, node, mode);
}
return 0;
}
if (*ctxt).depth >= (*ctxt).maxTemplateDepth {
return -1;
}
(*ctxt).depth += 1;
(*ctxt).templ = templ;
(*ctxt).node = node;
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).contextSize = 1;
(*(*ctxt).xpathCtxt).proximityPosition = 1;
}
execute_content(ctxt, (*templ).content);
(*ctxt).depth -= 1;
(*ctxt).templ = ptr::null_mut();
0
}
pub(crate) unsafe fn apply_templates_to_children(
ctxt: *mut _xsltTransformContext,
node: *mut _xmlNode,
mode: *const xmlChar,
) -> c_int {
let mut children: Vec<*mut _xmlNode> = Vec::new();
let mut child = (*node).children;
while !child.is_null() {
children.push(child);
child = (*child).next;
}
let size = children.len();
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).contextSize = size as c_int;
}
for (i, node) in children.iter().enumerate() {
(*ctxt).node = *node;
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).proximityPosition = (i + 1) as c_int;
}
apply_templates_to_node(ctxt, *node, mode);
}
0
}
pub unsafe fn execute_content(ctxt: *mut _xsltTransformContext, content: *mut _xmlNode) -> c_int {
let mut cur = content;
while !cur.is_null() {
if (*ctxt).state == XSLT_STATE_ERROR || (*ctxt).state == XSLT_STATE_STOPPED {
return -1;
}
let next = (*cur).next;
xsltProcessInstruction(ctxt, cur);
cur = next;
}
0
}
pub unsafe fn xsltProcessInstruction(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() || inst.is_null() {
return -1;
}
let typ = (*inst).type_;
(*ctxt).inst = inst;
match typ {
t if t == XML_TEXT_NODE as c_int || t == XML_CDATA_SECTION_NODE as c_int => {
if !(*inst).content.is_null() {
append_text_node(ctxt, (*inst).content);
}
0
}
t if t == XML_COMMENT_NODE as c_int => {
if !(*inst).content.is_null() {
append_comment_node(ctxt, (*inst).content);
}
0
}
t if t == XML_PI_NODE as c_int => {
if !(*inst).name.is_null() {
let content = if (*inst).content.is_null() {
ptr::null()
} else {
(*inst).content
};
append_pi_node(ctxt, (*inst).name, content);
}
0
}
t if t == XML_ELEMENT_NODE as c_int => {
if is_xslt_namespace(inst) {
process_xslt_instruction(ctxt, inst);
} else {
process_literal_element(ctxt, inst);
}
0
}
_ => 0,
}
}
pub(crate) unsafe fn process_xslt_instruction(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
) -> c_int {
let name = get_element_name(inst);
match name.as_deref() {
Some("apply-templates") => {
process_apply_templates(ctxt, inst);
}
Some("call-template") => {
process_call_template(ctxt, inst);
}
Some("apply-imports") => {
process_apply_imports(ctxt, inst);
}
Some("for-each") => {
process_for_each(ctxt, inst);
}
Some("value-of") => {
process_value_of(ctxt, inst);
}
Some("copy-of") => {
process_copy_of(ctxt, inst);
}
Some("copy") => {
process_copy(ctxt, inst);
}
Some("element") => {
process_element(ctxt, inst);
}
Some("attribute") => {
process_attribute(ctxt, inst);
}
Some("text") => {
process_text(ctxt, inst);
}
Some("comment") => {
process_comment(ctxt, inst);
}
Some("processing-instruction") => {
process_pi(ctxt, inst);
}
Some("number") => {
process_number(ctxt, inst);
}
Some("choose") => {
process_choose(ctxt, inst);
}
Some("when") | Some("otherwise") => {
}
Some("if") => {
process_if(ctxt, inst);
}
Some("variable") => {
process_variable(ctxt, inst);
}
Some("param") => {
process_param(ctxt, inst);
}
Some("with-param") => {
}
Some("sort") => {
}
Some("message") => {
process_message(ctxt, inst);
}
Some("fallback") => {
}
Some("output")
| Some("decimal-format")
| Some("namespace-alias")
| Some("attribute-set")
| Some("key")
| Some("strip-space")
| Some("preserve-space")
| Some("import")
| Some("include")
| Some("stylesheet")
| Some("transform") => {
}
_ => {
let ns = get_element_ns(inst);
if let Some(ns_uri) = ns {
if ns_uri == crate::exslt::EXSLT_NS_COMMON
&& get_element_name(inst).as_deref() == Some("document")
{
process_exsl_document(ctxt, inst);
return 0;
}
let name_ptr = (*inst).name;
let ns_cstr = str_to_cstr(&ns_uri);
let found = crate::xslt::extensions::xsltFindExtElement(
ctxt,
name_ptr,
ns_cstr.as_ptr() as *const xmlChar,
);
if !found.is_null() {
return 0;
}
}
}
}
0
}
fn str_to_cstr(s: &str) -> Vec<u8> {
let mut v = s.as_bytes().to_vec();
v.push(0);
v
}
unsafe fn process_exsl_document(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let href = get_prop(inst, c"href".as_ptr() as *const xmlChar);
if href.is_null() {
crate::xslt::errors::xsltTransformError(
ctxt,
(*ctxt).style,
inst,
c"exsl:document: missing href attribute".as_ptr() as *const c_char,
);
return;
}
let frag = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
if frag.is_null() {
libc::free(href as *mut libc::c_void);
return;
}
(*frag).type_ = XML_DOCUMENT_NODE as c_int;
(*frag).doc = frag;
let saved_insert = (*ctxt).insert;
let saved_output = (*ctxt).output;
(*ctxt).insert = frag as *mut _xmlNode;
(*ctxt).output = frag;
execute_content(ctxt, (*inst).children);
(*ctxt).insert = saved_insert;
(*ctxt).output = saved_output;
let fname = crate::abi::versioning::c_str_to_bytes(href as *const c_char);
if let Some(name) = fname {
let path = String::from_utf8_lossy(name);
let cpath = str_to_cstr(&path);
let out = libc::fopen(
cpath.as_ptr() as *const c_char,
c"wb".as_ptr() as *const c_char,
);
if !out.is_null() {
let buf = crate::xml::io::buf_create(-1);
if !buf.is_null() {
crate::xml::tree::doc_dump(buf, frag);
let content = crate::xml::io::buf_content(buf);
let len = crate::xml::io::buf_length(buf);
if !content.is_null() && len > 0 {
libc::fwrite(content as *const libc::c_void, 1, len as usize, out);
}
crate::xml::io::buf_free(buf);
}
libc::fclose(out);
}
}
libc::free(href as *mut libc::c_void);
free_doc(frag);
}
pub(crate) unsafe fn eval_xpath(
ctxt: *mut _xsltTransformContext,
expr: *const xmlChar,
) -> *mut _xmlXPathObject {
if ctxt.is_null() || expr.is_null() {
return ptr::null_mut();
}
let xpath_ctxt = (*ctxt).xpathCtxt;
if xpath_ctxt.is_null() {
return ptr::null_mut();
}
(*xpath_ctxt).node = (*ctxt).node;
if !(*ctxt).document.is_null() {
(*xpath_ctxt).doc = (*(*ctxt).document).doc;
}
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if !internal.is_null() {
(*internal).context_node = (*ctxt).node;
if !(*ctxt).document.is_null() {
(*internal).document = (*(*ctxt).document).doc;
}
(*internal).context_size = (*xpath_ctxt).contextSize;
(*internal).context_position = (*xpath_ctxt).proximityPosition;
(*internal).proximity_position = (*xpath_ctxt).proximityPosition;
if !(*ctxt).inst.is_null() {
register_in_scope_ns(internal, (*ctxt).inst);
}
}
xmlXPathEvalExpression(expr, xpath_ctxt)
}
pub(crate) unsafe fn register_in_scope_ns(
internal: *mut crate::xml::xpath::context::XPathContext,
node: *mut _xmlNode,
) {
unsafe {
let mut cur = node;
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
while !cur.is_null() {
let mut ns = (*cur).nsDef;
while !ns.is_null() {
let prefix = if (*ns).prefix.is_null() {
String::new()
} else {
let l = libc::strlen((*ns).prefix as *const libc::c_char);
String::from_utf8_lossy(core::slice::from_raw_parts((*ns).prefix, l))
.into_owned()
};
if !seen.contains(&prefix) && !(*ns).href.is_null() {
let l = libc::strlen((*ns).href as *const libc::c_char);
let href = String::from_utf8_lossy(core::slice::from_raw_parts((*ns).href, l))
.into_owned();
(*internal).register_namespace(&prefix, &href);
seen.insert(prefix);
}
ns = (*ns).next;
}
cur = (*cur).parent;
}
}
}
pub(crate) unsafe fn report_xpath_eval_failure(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
elem_name: &str,
) {
unsafe {
let xpath_ctxt = (*ctxt).xpathCtxt;
if !xpath_ctxt.is_null() {
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if !internal.is_null() {
if let Some(msg) = &(*internal).error {
let line = format!("XPath error : {}\n", msg);
libc::write(2, line.as_ptr() as *const libc::c_void, line.len());
}
}
}
let url = if inst.is_null() || (*inst).doc.is_null() || (*(*inst).doc).URL.is_null() {
if !ctxt.is_null()
&& !(*ctxt).style.is_null()
&& !(*(*ctxt).style).doc.is_null()
&& !(*(*(*ctxt).style).doc).URL.is_null()
{
let u = (*(*(*ctxt).style).doc).URL;
let l = libc::strlen(u as *const libc::c_char);
String::from_utf8_lossy(core::slice::from_raw_parts(u, l)).into_owned()
} else {
"unknown".to_string()
}
} else {
let u = (*(*inst).doc).URL;
let l = libc::strlen(u as *const libc::c_char);
String::from_utf8_lossy(core::slice::from_raw_parts(u, l)).into_owned()
};
let line_no = if inst.is_null() { 0 } else { (*inst).line };
let line = format!(
"runtime error: file {} line {} element {}\n",
url, line_no, elem_name
);
libc::write(2, line.as_ptr() as *const libc::c_void, line.len());
let tail = "XPath evaluation returned no result.\n";
libc::write(2, tail.as_ptr() as *const libc::c_void, tail.len());
(*ctxt).state = XSLT_STATE_STOPPED;
}
}
pub(crate) unsafe fn process_apply_templates(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
) {
let mode = get_prop(inst, c"mode".as_ptr() as *const xmlChar);
let select = get_prop(inst, c"select".as_ptr() as *const xmlChar);
let params = collect_with_params(ctxt, inst);
let obj = if !select.is_null() {
eval_xpath(ctxt, select)
} else {
build_child_node_set(ctxt)
};
if !select.is_null() {
libc::free(select as *mut libc::c_void);
}
if obj.is_null() {
if !mode.is_null() {
libc::free(mode as *mut libc::c_void);
}
report_xpath_eval_failure(ctxt, inst, "apply-templates");
return;
}
let nodes = if (*obj).type_ == xmlXPathObjectType::XPATH_NODESET as c_int {
(*obj).nodesetval as *mut _xmlNodeSet
} else {
ptr::null_mut()
};
if !nodes.is_null() && (*nodes).nodeNr > 0 {
let sort = find_sort_children(ctxt, inst);
let mut node_ptrs: Vec<*mut _xmlNode> = Vec::new();
let mut i = 0;
while i < (*nodes).nodeNr {
let n = *(*nodes).nodeTab.offset(i as isize);
if !n.is_null() {
node_ptrs.push(n);
}
i += 1;
}
if !sort.is_null() {
let sorted = libc::calloc(1, core::mem::size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
if !sorted.is_null() {
(*sorted).nodeNr = node_ptrs.len() as c_int;
(*sorted).nodeMax = node_ptrs.len() as c_int;
let tab = libc::malloc(node_ptrs.len() * core::mem::size_of::<*mut _xmlNode>())
as *mut *mut _xmlNode;
(*sorted).nodeTab = tab;
for (idx, n) in node_ptrs.iter().enumerate() {
if !tab.is_null() {
*tab.add(idx) = *n;
}
}
crate::xslt::sorting::xsltSortNodeSet(ctxt, sorted, sort);
let mut k = 0;
while k < (*sorted).nodeNr {
if (*ctxt).state == XSLT_STATE_ERROR || (*ctxt).state == XSLT_STATE_STOPPED {
break;
}
let n = *(*sorted).nodeTab.offset(k as isize);
if !n.is_null() {
(*ctxt).node = n;
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).contextSize = (*sorted).nodeNr;
(*(*ctxt).xpathCtxt).proximityPosition = k + 1;
}
apply_templates_with_params(ctxt, n, mode, params);
}
k += 1;
}
libc::free((*sorted).nodeTab as *mut libc::c_void);
libc::free(sorted as *mut libc::c_void);
}
} else {
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).contextSize = node_ptrs.len() as c_int;
}
for (i, n) in node_ptrs.iter().enumerate() {
if (*ctxt).state == XSLT_STATE_ERROR || (*ctxt).state == XSLT_STATE_STOPPED {
break;
}
if !n.is_null() {
(*ctxt).node = *n;
if !(*ctxt).xpathCtxt.is_null() {
(*(*ctxt).xpathCtxt).proximityPosition = (i + 1) as c_int;
}
apply_templates_with_params(ctxt, *n, mode, params);
}
}
}
}
if !mode.is_null() {
libc::free(mode as *mut libc::c_void);
}
xmlXPathFreeObject(obj);
}
pub(crate) unsafe fn apply_templates_with_params(
ctxt: *mut _xsltTransformContext,
node: *mut _xmlNode,
mode: *const xmlChar,
params: *mut _xsltStackElem,
) {
let mut p = params;
while !p.is_null() {
crate::xslt::parameters::xsltPushParam(ctxt, p);
p = (*p).next;
}
apply_templates_to_node(ctxt, node, mode);
let mut p = params;
while !p.is_null() {
crate::xslt::parameters::xsltPopParam(ctxt);
p = (*p).next;
}
}
pub(crate) unsafe fn build_child_node_set(
ctxt: *mut _xsltTransformContext,
) -> *mut _xmlXPathObject {
let ns = xmlXPathNodeSetCreate(ptr::null_mut());
if ns.is_null() {
return ptr::null_mut();
}
let obj = xmlMalloc_zero_obj();
if obj.is_null() {
libc::free(ns as *mut libc::c_void);
return ptr::null_mut();
}
(*obj).type_ = xmlXPathObjectType::XPATH_NODESET as c_int;
(*obj).nodesetval = ns as *mut c_void;
let node = (*ctxt).node;
if !node.is_null() {
let mut child = (*node).children;
while !child.is_null() {
append_to_node_set(ns, child);
child = (*child).next;
}
}
obj
}
unsafe fn xmlMalloc_zero_obj() -> *mut _xmlXPathObject {
libc::calloc(1, core::mem::size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject
}
unsafe fn append_to_node_set(ns: *mut _xmlNodeSet, node: *mut _xmlNode) {
if ns.is_null() || node.is_null() {
return;
}
let mut i = 0;
while i < (*ns).nodeNr {
if *(*ns).nodeTab.offset(i as isize) == node {
return;
}
i += 1;
}
if (*ns).nodeNr >= (*ns).nodeMax {
let new_max = if (*ns).nodeMax == 0 {
8
} else {
(*ns).nodeMax * 2
};
let new_tab = libc::realloc(
(*ns).nodeTab as *mut libc::c_void,
(new_max as usize) * core::mem::size_of::<*mut _xmlNode>(),
) as *mut *mut _xmlNode;
if new_tab.is_null() {
return;
}
(*ns).nodeTab = new_tab;
(*ns).nodeMax = new_max;
}
*(*ns).nodeTab.offset((*ns).nodeNr as isize) = node;
(*ns).nodeNr += 1;
}
pub(crate) unsafe fn collect_with_params(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
) -> *mut _xsltStackElem {
let mut head: *mut _xsltStackElem = ptr::null_mut();
let mut tail: *mut _xsltStackElem = ptr::null_mut();
let mut child = (*inst).children;
while !child.is_null() {
let next = (*child).next;
if is_xslt_element(child, "with-param") {
let param = evaluate_with_param(ctxt, child);
if !param.is_null() {
(*param).next = ptr::null_mut();
if tail.is_null() {
head = param;
tail = param;
} else {
(*tail).next = param;
tail = param;
}
}
}
child = next;
}
head
}
pub(crate) unsafe fn evaluate_with_param(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
) -> *mut _xsltStackElem {
let name = get_prop(inst, c"name".as_ptr() as *const xmlChar);
if name.is_null() {
return ptr::null_mut();
}
let select = get_prop(inst, c"select".as_ptr() as *const xmlChar);
let param = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
if param.is_null() {
libc::free(name as *mut libc::c_void);
if !select.is_null() {
libc::free(select as *mut libc::c_void);
}
return ptr::null_mut();
}
(*param).name = name;
(*param).flags = 2 | 4; if !select.is_null() {
let obj = eval_xpath(ctxt, select);
if !obj.is_null() {
(*param).value = obj;
} else {
report_xpath_eval_failure(ctxt, inst, "with-param");
libc::free(select as *mut libc::c_void);
libc::free(param as *mut libc::c_void);
return ptr::null_mut();
}
libc::free(select as *mut libc::c_void);
} else {
let value = eval_content_fragment(ctxt, (*inst).children);
if !value.is_null() {
(*param).value = value;
}
}
param
}
pub(crate) unsafe fn eval_content_fragment(
ctxt: *mut _xsltTransformContext,
content: *mut _xmlNode,
) -> *mut _xmlXPathObject {
let frag = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
if frag.is_null() {
return ptr::null_mut();
}
(*frag).type_ = XML_DOCUMENT_NODE as c_int;
(*frag).doc = frag;
let saved_insert = (*ctxt).insert;
let saved_output = (*ctxt).output;
(*ctxt).insert = frag as *mut _xmlNode;
(*ctxt).output = frag;
execute_content(ctxt, content);
(*ctxt).insert = saved_insert;
(*ctxt).output = saved_output;
let obj = xmlMalloc_zero_obj();
if obj.is_null() {
free_doc(frag);
return ptr::null_mut();
}
(*obj).type_ = xmlXPathObjectType::XPATH_XSLT_TREE as c_int;
(*obj).nodesetval = frag as *mut c_void;
obj
}
#[allow(clippy::while_immutable_condition)]
pub(crate) unsafe fn process_call_template(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let name = get_prop(inst, c"name".as_ptr() as *const xmlChar);
if name.is_null() {
return;
}
let style = (*ctxt).style;
let templ = crate::xslt::templates::xsltLookupTemplate(style, name);
libc::free(name as *mut libc::c_void);
if templ.is_null() {
return;
}
if (*ctxt).depth >= (*ctxt).maxTemplateDepth {
return;
}
(*ctxt).depth += 1;
let old_vars_nr = (*ctxt).varsNr;
let params = collect_with_params(ctxt, inst);
let mut to_push: Vec<*mut _xsltStackElem> = Vec::new();
let mut p = params;
while !p.is_null() {
to_push.push(p);
p = (*p).next;
}
for param in to_push {
crate::xslt::parameters::xsltPushParam(ctxt, param);
}
let saved_templ = (*ctxt).templ;
(*ctxt).templ = templ;
execute_content(ctxt, (*templ).content);
(*ctxt).templ = saved_templ;
while (*ctxt).varsNr > old_vars_nr {
crate::xslt::parameters::xsltPopParam(ctxt);
}
(*ctxt).depth -= 1;
}
pub(crate) unsafe fn process_apply_imports(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let _ = inst;
let style = (*ctxt).style;
let node = (*ctxt).node;
let current_templ = (*ctxt).templ;
if style.is_null() || node.is_null() || current_templ.is_null() {
return;
}
let current_depth = (*current_templ).position;
let mode = (*current_templ).mode;
let mut best: *mut _xsltTemplate = ptr::null_mut();
let mut best_priority: f32 = f32::NEG_INFINITY;
let mut best_depth: c_int = -1;
let mut templ = (*style).templates;
while !templ.is_null() {
if (*templ).position <= current_depth {
templ = (*templ).next;
continue;
}
if !(*templ).mode.is_null() {
if mode.is_null()
|| libc::strcmp(
(*templ).mode as *const libc::c_char,
mode as *const libc::c_char,
) != 0
{
templ = (*templ).next;
continue;
}
} else if !mode.is_null() {
templ = (*templ).next;
continue;
}
let pattern_ptr = (*templ).params as *mut crate::xslt::patterns::_xsltPattern;
if pattern_ptr.is_null() {
templ = (*templ).next;
continue;
}
if crate::xslt::patterns::xsltTestPattern(ctxt, pattern_ptr, node) == 0 {
templ = (*templ).next;
continue;
}
let priority = (*templ).priority;
if priority > best_priority || (priority == best_priority && (*templ).position > best_depth)
{
best = templ;
best_priority = priority;
best_depth = (*templ).position;
}
templ = (*templ).next;
}
if !best.is_null() {
if (*ctxt).depth >= (*ctxt).maxTemplateDepth {
return;
}
(*ctxt).depth += 1;
let saved_templ = (*ctxt).templ;
(*ctxt).templ = best;
execute_content(ctxt, (*best).content);
(*ctxt).templ = saved_templ;
(*ctxt).depth -= 1;
}
}
pub(crate) unsafe fn process_for_each(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let select = get_prop(inst, c"select".as_ptr() as *const xmlChar);
if select.is_null() {
return;
}
let obj = eval_xpath(ctxt, select);
libc::free(select as *mut libc::c_void);
if obj.is_null() {
report_xpath_eval_failure(ctxt, inst, "for-each");
return;
}
if (*obj).type_ != xmlXPathObjectType::XPATH_NODESET as c_int {
xmlXPathFreeObject(obj);
return;
}
let nodes = (*obj).nodesetval as *mut _xmlNodeSet;
if nodes.is_null() || (*nodes).nodeNr == 0 {
xmlXPathFreeObject(obj);
return;
}
let sort = find_sort_children(ctxt, inst);
let saved_node = (*ctxt).node;
let xpath_ctxt = (*ctxt).xpathCtxt;
let (saved_size, saved_pos) = if xpath_ctxt.is_null() {
(0, 0)
} else {
((*xpath_ctxt).contextSize, (*xpath_ctxt).proximityPosition)
};
let mut node_ptrs: Vec<*mut _xmlNode> = Vec::new();
let mut i = 0;
while i < (*nodes).nodeNr {
let n = *(*nodes).nodeTab.offset(i as isize);
if !n.is_null() {
node_ptrs.push(n);
}
i += 1;
}
if !sort.is_null() {
let sorted = libc::calloc(1, core::mem::size_of::<_xmlNodeSet>()) as *mut _xmlNodeSet;
if !sorted.is_null() {
(*sorted).nodeNr = node_ptrs.len() as c_int;
(*sorted).nodeMax = node_ptrs.len() as c_int;
let tab = libc::malloc(node_ptrs.len() * core::mem::size_of::<*mut _xmlNode>())
as *mut *mut _xmlNode;
(*sorted).nodeTab = tab;
for (idx, n) in node_ptrs.iter().enumerate() {
if !tab.is_null() {
*tab.add(idx) = *n;
}
}
crate::xslt::sorting::xsltSortNodeSet(ctxt, sorted, sort);
if !xpath_ctxt.is_null() {
(*xpath_ctxt).contextSize = (*sorted).nodeNr;
}
let mut k = 0;
while k < (*sorted).nodeNr {
if (*ctxt).state == XSLT_STATE_ERROR || (*ctxt).state == XSLT_STATE_STOPPED {
break;
}
let n = *(*sorted).nodeTab.offset(k as isize);
if !n.is_null() {
(*ctxt).node = n;
if !xpath_ctxt.is_null() {
(*xpath_ctxt).proximityPosition = k + 1;
}
execute_content(ctxt, (*inst).children);
}
k += 1;
}
libc::free((*sorted).nodeTab as *mut libc::c_void);
libc::free(sorted as *mut libc::c_void);
}
} else {
if !xpath_ctxt.is_null() {
(*xpath_ctxt).contextSize = node_ptrs.len() as c_int;
}
for (i, n) in node_ptrs.iter().enumerate() {
if (*ctxt).state == XSLT_STATE_ERROR || (*ctxt).state == XSLT_STATE_STOPPED {
break;
}
if !n.is_null() {
(*ctxt).node = *n;
if !xpath_ctxt.is_null() {
(*xpath_ctxt).proximityPosition = (i + 1) as c_int;
}
execute_content(ctxt, (*inst).children);
}
}
}
(*ctxt).node = saved_node;
if !xpath_ctxt.is_null() {
(*xpath_ctxt).contextSize = saved_size;
(*xpath_ctxt).proximityPosition = saved_pos;
}
xmlXPathFreeObject(obj);
}
unsafe fn find_sort_children(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
) -> *mut _xsltSort {
if ctxt.is_null() || inst.is_null() {
return ptr::null_mut();
}
let mut child = (*inst).children;
while !child.is_null() {
if is_xslt_element(child, "sort") {
let style = (*ctxt).style;
let sort = crate::xslt::sorting::xsltCompileSort(style, child);
return sort;
}
child = (*child).next;
}
ptr::null_mut()
}
pub(crate) unsafe fn process_value_of(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let select = get_prop(inst, c"select".as_ptr() as *const xmlChar);
if select.is_null() {
return;
}
let obj = eval_xpath(ctxt, select);
libc::free(select as *mut libc::c_void);
if obj.is_null() {
report_xpath_eval_failure(ctxt, inst, "value-of");
return;
}
let strv = xmlXPathCastToString(obj);
xmlXPathFreeObject(obj);
if !strv.is_null() {
if *strv != 0 {
append_text_node(ctxt, strv);
}
libc::free(strv as *mut libc::c_void);
}
}
pub(crate) unsafe fn process_copy_of(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let select = get_prop(inst, c"select".as_ptr() as *const xmlChar);
if select.is_null() {
return;
}
let obj = eval_xpath(ctxt, select);
libc::free(select as *mut libc::c_void);
if obj.is_null() {
report_xpath_eval_failure(ctxt, inst, "copy-of");
return;
}
if (*obj).type_ == xmlXPathObjectType::XPATH_NODESET as c_int {
let nodes = (*obj).nodesetval as *mut _xmlNodeSet;
if !nodes.is_null() {
let mut i = 0;
while i < (*nodes).nodeNr {
let n = *(*nodes).nodeTab.offset(i as isize);
if !n.is_null() {
copy_node_deep(ctxt, n);
}
i += 1;
}
}
} else if (*obj).type_ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
let frag = (*obj).nodesetval as *mut _xmlDoc;
if !frag.is_null() {
let mut child = (*frag).children;
while !child.is_null() {
let next = (*child).next;
copy_node_deep(ctxt, child);
child = next;
}
}
} else {
let strv = xmlXPathCastToString(obj);
if !strv.is_null() {
if *strv != 0 {
append_text_node(ctxt, strv);
}
libc::free(strv as *mut libc::c_void);
}
}
xmlXPathFreeObject(obj);
}
pub(crate) unsafe fn copy_node_deep(ctxt: *mut _xsltTransformContext, node: *mut _xmlNode) {
if node.is_null() {
return;
}
let typ = (*node).type_;
if typ == XML_TEXT_NODE as c_int || typ == XML_CDATA_SECTION_NODE as c_int {
if !(*node).content.is_null() {
append_text_node(ctxt, (*node).content);
}
} else if typ == XML_COMMENT_NODE as c_int {
if !(*node).content.is_null() {
append_comment_node(ctxt, (*node).content);
}
} else if typ == XML_PI_NODE as c_int {
if !(*node).name.is_null() {
append_pi_node(ctxt, (*node).name, (*node).content);
}
} else if typ == XML_ELEMENT_NODE as c_int {
let name = (*node).name;
let new_elem = new_element_node(ctxt, name, (*node).ns);
if new_elem.is_null() {
return;
}
let mut prop = (*node).properties;
while !prop.is_null() {
let attr_name = (*prop).name;
let attr_val = node_get_content((*prop).children);
if !attr_name.is_null() && !attr_val.is_null() {
set_prop(new_elem, attr_name, attr_val);
libc::free(attr_val as *mut libc::c_void);
}
prop = (*prop).next;
}
let saved_insert = (*ctxt).insert;
(*ctxt).insert = new_elem;
let mut child = (*node).children;
while !child.is_null() {
let next = (*child).next;
copy_node_deep(ctxt, child);
child = next;
}
(*ctxt).insert = saved_insert;
}
}
unsafe fn new_element_node(
ctxt: *mut _xsltTransformContext,
name: *const xmlChar,
ns: *mut _xmlNs,
) -> *mut _xmlNode {
let elem = new_node(ns, name);
if elem.is_null() {
return ptr::null_mut();
}
append_to_result(ctxt, elem);
elem
}
pub(crate) unsafe fn append_to_result(ctxt: *mut _xsltTransformContext, node: *mut _xmlNode) {
let insert = (*ctxt).insert;
if insert.is_null() {
return;
}
add_child(insert, node);
let doc = if (*insert).type_ == XML_DOCUMENT_NODE as c_int {
insert as *mut _xmlDoc
} else {
(*insert).doc
};
if !doc.is_null() {
set_node_doc(node, doc);
}
}
unsafe fn set_node_doc(node: *mut _xmlNode, doc: *mut _xmlDoc) {
if node.is_null() {
return;
}
(*node).doc = doc;
let mut prop = (*node).properties;
while !prop.is_null() {
(*prop).doc = doc;
prop = (*prop).next;
}
let mut child = (*node).children;
while !child.is_null() {
set_node_doc(child, doc);
child = (*child).next;
}
}
pub(crate) unsafe fn append_text_node(ctxt: *mut _xsltTransformContext, content: *const xmlChar) {
let insert = (*ctxt).insert;
if insert.is_null() || content.is_null() {
return;
}
let text = new_text(content);
if text.is_null() {
return;
}
append_to_result(ctxt, text);
}
pub(crate) unsafe fn append_comment_node(
ctxt: *mut _xsltTransformContext,
content: *const xmlChar,
) {
let insert = (*ctxt).insert;
if insert.is_null() || content.is_null() {
return;
}
let comment = new_comment(content);
if comment.is_null() {
return;
}
append_to_result(ctxt, comment);
}
pub(crate) unsafe fn append_pi_node(
ctxt: *mut _xsltTransformContext,
name: *const xmlChar,
content: *const xmlChar,
) {
let insert = (*ctxt).insert;
if insert.is_null() || name.is_null() {
return;
}
let pi = new_pi(name, content);
if pi.is_null() {
return;
}
append_to_result(ctxt, pi);
}
pub(crate) unsafe fn process_copy(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let node = (*ctxt).node;
if node.is_null() {
return;
}
let typ = (*node).type_;
let saved_insert = (*ctxt).insert;
if typ == XML_ELEMENT_NODE as c_int {
let new_elem = new_element_node(ctxt, (*node).name, (*node).ns);
if new_elem.is_null() {
return;
}
(*ctxt).insert = new_elem;
} else if typ == XML_TEXT_NODE as c_int || typ == XML_CDATA_SECTION_NODE as c_int {
if !(*node).content.is_null() {
append_text_node(ctxt, (*node).content);
}
} else if typ == XML_COMMENT_NODE as c_int {
if !(*node).content.is_null() {
append_comment_node(ctxt, (*node).content);
}
} else if typ == XML_PI_NODE as c_int {
if !(*node).name.is_null() {
append_pi_node(ctxt, (*node).name, (*node).content);
}
} else if typ == XML_ATTRIBUTE_NODE as c_int && !(*node).children.is_null() {
let val = node_get_content((*node).children);
if !val.is_null() {
append_text_node(ctxt, val);
libc::free(val as *mut libc::c_void);
}
}
execute_content(ctxt, (*inst).children);
(*ctxt).insert = saved_insert;
}
pub(crate) unsafe fn process_element(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let name_attr = get_prop(inst, c"name".as_ptr() as *const xmlChar);
if name_attr.is_null() {
return;
}
let name_str = eval_avt(ctxt, name_attr);
libc::free(name_attr as *mut libc::c_void);
if name_str.is_null() {
return;
}
let ns_attr = get_prop(inst, c"namespace".as_ptr() as *const xmlChar);
let ns_str = if !ns_attr.is_null() {
let v = eval_avt(ctxt, ns_attr);
libc::free(ns_attr as *mut libc::c_void);
v
} else {
ptr::null_mut()
};
let ns = if !ns_str.is_null() && *ns_str != 0 {
let n = new_ns(ptr::null_mut(), ns_str, ptr::null());
libc::free(ns_str as *mut libc::c_void);
n
} else {
if !ns_str.is_null() {
libc::free(ns_str as *mut libc::c_void);
}
ptr::null_mut()
};
let elem = new_node(ns, name_str);
libc::free(name_str as *mut libc::c_void);
if elem.is_null() {
return;
}
append_to_result(ctxt, elem);
let saved_insert = (*ctxt).insert;
(*ctxt).insert = elem;
execute_content(ctxt, (*inst).children);
(*ctxt).insert = saved_insert;
}
pub(crate) unsafe fn process_attribute(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let name_attr = get_prop(inst, c"name".as_ptr() as *const xmlChar);
if name_attr.is_null() {
return;
}
let name_str = eval_avt(ctxt, name_attr);
libc::free(name_attr as *mut libc::c_void);
if name_str.is_null() {
return;
}
let insert = (*ctxt).insert;
if insert.is_null() {
xmlFreeImpl(name_str as *mut c_void);
return;
}
let saved_insert = (*ctxt).insert;
let buf = libc::calloc(1, core::mem::size_of::<_xmlBuffer>()) as *mut _xmlBuffer;
if buf.is_null() {
xmlFreeImpl(name_str as *mut c_void);
return;
}
(*buf).content = libc::calloc(1, 64) as *mut xmlChar;
(*buf).size = 64;
(*buf).use_ = 0;
let frag_doc = libc::calloc(1, core::mem::size_of::<_xmlDoc>()) as *mut _xmlDoc;
if frag_doc.is_null() {
libc::free(buf as *mut libc::c_void);
xmlFreeImpl(name_str as *mut c_void);
return;
}
(*frag_doc).type_ = XML_DOCUMENT_NODE as c_int;
(*frag_doc).doc = frag_doc;
(*ctxt).insert = frag_doc as *mut _xmlNode;
execute_content(ctxt, (*inst).children);
let mut value: Vec<u8> = Vec::new();
let mut child = (*frag_doc).children;
while !child.is_null() {
if (*child).type_ == XML_TEXT_NODE as c_int && !(*child).content.is_null() {
let len = libc::strlen((*child).content as *const libc::c_char) as usize;
value.extend_from_slice(core::slice::from_raw_parts((*child).content, len));
}
child = (*child).next;
}
free_doc(frag_doc);
(*ctxt).insert = saved_insert;
let mut cvalue = value.clone();
cvalue.push(0);
set_prop(insert, name_str, cvalue.as_ptr() as *const xmlChar);
xmlFreeImpl(name_str as *mut c_void);
}
pub(crate) unsafe fn process_text(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let doe = get_prop(inst, c"disable-output-escaping".as_ptr() as *const xmlChar);
if !doe.is_null() {
libc::free(doe as *mut libc::c_void);
}
let mut child = (*inst).children;
while !child.is_null() {
if (*child).type_ == XML_TEXT_NODE as c_int && !(*child).content.is_null() {
append_text_node(ctxt, (*child).content);
}
child = (*child).next;
}
}
pub(crate) unsafe fn process_comment(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let content = node_get_content((*inst).children);
if !content.is_null() {
append_comment_node(ctxt, content);
libc::free(content as *mut libc::c_void);
}
}
pub(crate) unsafe fn process_pi(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let name_attr = get_prop(inst, c"name".as_ptr() as *const xmlChar);
if name_attr.is_null() {
return;
}
let name_str = eval_avt(ctxt, name_attr);
libc::free(name_attr as *mut libc::c_void);
if name_str.is_null() {
return;
}
let content = node_get_content((*inst).children);
append_pi_node(ctxt, name_str, content);
if !content.is_null() {
libc::free(content as *mut libc::c_void);
}
xmlFreeImpl(name_str as *mut c_void);
}
pub(crate) unsafe fn process_number(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let value_attr = get_prop(inst, c"value".as_ptr() as *const xmlChar);
let mut number: f64 = f64::NAN;
if !value_attr.is_null() {
let obj = eval_xpath(ctxt, value_attr);
libc::free(value_attr as *mut libc::c_void);
if !obj.is_null() {
number = (*obj).floatval;
xmlXPathFreeObject(obj);
} else {
report_xpath_eval_failure(ctxt, inst, "number");
return;
}
} else {
number = 1.0;
let node = (*ctxt).node;
if !node.is_null() {
let mut sib = (*node).prev;
while !sib.is_null() {
if (*sib).type_ == XML_ELEMENT_NODE as c_int {
number += 1.0;
}
sib = (*sib).prev;
}
}
}
let format = get_prop(inst, c"format".as_ptr() as *const xmlChar);
let formatted = crate::xslt::numbering::xsltFormatNumber(number, format);
if !format.is_null() {
libc::free(format as *mut libc::c_void);
}
if !formatted.is_null() {
append_text_node(ctxt, formatted);
libc::free(formatted as *mut libc::c_void);
}
}
unsafe fn xpath_obj_boolean(obj: *mut _xmlXPathObject) -> bool {
if obj.is_null() {
return false;
}
let typ = (*obj).type_;
if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
return (*obj).boolval != 0;
}
if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
let n = (*obj).floatval;
return n != 0.0 && !n.is_nan();
}
if typ == xmlXPathObjectType::XPATH_STRING as c_int {
return !(*obj).stringval.is_null() && *(*obj).stringval != 0;
}
if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
let ns = (*obj).nodesetval as *mut _xmlNodeSet;
return !ns.is_null() && (*ns).nodeNr > 0;
}
false
}
pub(crate) unsafe fn process_choose(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let mut child = (*inst).children;
let mut executed = false;
while !child.is_null() {
let next = (*child).next;
if is_xslt_element(child, "when") {
let test = get_prop(child, c"test".as_ptr() as *const xmlChar);
if !test.is_null() {
let obj = eval_xpath(ctxt, test);
libc::free(test as *mut libc::c_void);
if obj.is_null() {
report_xpath_eval_failure(ctxt, child, "when");
return;
}
let truthy = xpath_obj_boolean(obj);
xmlXPathFreeObject(obj);
if truthy {
execute_content(ctxt, (*child).children);
executed = true;
break;
}
}
} else if is_xslt_element(child, "otherwise") && !executed {
execute_content(ctxt, (*child).children);
executed = true;
}
child = next;
}
}
pub(crate) unsafe fn process_if(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let test = get_prop(inst, c"test".as_ptr() as *const xmlChar);
if test.is_null() {
return;
}
let obj = eval_xpath(ctxt, test);
libc::free(test as *mut libc::c_void);
if obj.is_null() {
report_xpath_eval_failure(ctxt, inst, "if");
return;
}
let truthy = xpath_obj_boolean(obj);
xmlXPathFreeObject(obj);
if truthy {
execute_content(ctxt, (*inst).children);
}
}
pub(crate) unsafe fn process_variable(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let name = get_prop(inst, c"name".as_ptr() as *const xmlChar);
if name.is_null() {
return;
}
let select = get_prop(inst, c"select".as_ptr() as *const xmlChar);
let var = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
if var.is_null() {
libc::free(name as *mut libc::c_void);
if !select.is_null() {
libc::free(select as *mut libc::c_void);
}
return;
}
(*var).name = name;
(*var).flags = 4; if !select.is_null() {
let obj = eval_xpath(ctxt, select);
if !obj.is_null() {
(*var).value = obj;
} else {
report_xpath_eval_failure(ctxt, inst, "variable");
libc::free(select as *mut libc::c_void);
libc::free(var as *mut libc::c_void);
return;
}
libc::free(select as *mut libc::c_void);
} else {
let value = eval_content_fragment(ctxt, (*inst).children);
if !value.is_null() {
(*var).value = value;
}
}
crate::xslt::variables::xsltPushVariable(ctxt, var);
}
pub(crate) unsafe fn process_param(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let name = get_prop(inst, c"name".as_ptr() as *const xmlChar);
if name.is_null() {
return;
}
let already_bound = {
let xpath_ctxt = (*ctxt).xpathCtxt;
if xpath_ctxt.is_null() {
false
} else {
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if internal.is_null() {
false
} else {
let name_len = libc::strlen(name as *const libc::c_char);
let name_bytes = core::slice::from_raw_parts(name, name_len);
let name_owned = String::from_utf8_lossy(name_bytes).into_owned();
(*internal).variables.contains_key(&name_owned)
}
}
};
if already_bound {
libc::free(name as *mut libc::c_void);
return;
}
let select = get_prop(inst, c"select".as_ptr() as *const xmlChar);
let var = libc::calloc(1, core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
if var.is_null() {
libc::free(name as *mut libc::c_void);
if !select.is_null() {
libc::free(select as *mut libc::c_void);
}
return;
}
(*var).name = name;
(*var).flags = 2 | 4; if !select.is_null() {
let obj = eval_xpath(ctxt, select);
if !obj.is_null() {
(*var).value = obj;
} else {
report_xpath_eval_failure(ctxt, inst, "param");
libc::free(select as *mut libc::c_void);
libc::free(var as *mut libc::c_void);
return;
}
libc::free(select as *mut libc::c_void);
} else {
let value = eval_content_fragment(ctxt, (*inst).children);
if !value.is_null() {
(*var).value = value;
}
}
crate::xslt::variables::xsltPushVariable(ctxt, var);
}
pub(crate) unsafe fn process_message(ctxt: *mut _xsltTransformContext, inst: *mut _xmlNode) {
let content = node_get_content((*inst).children);
if !content.is_null() {
let len = libc::strlen(content as *const libc::c_char) as usize;
let _ = libc::write(2, content as *const libc::c_void, len);
let terminate = get_prop(inst, c"terminate".as_ptr() as *const xmlChar);
if !terminate.is_null() {
if libc::strcmp(
terminate as *const libc::c_char,
c"yes".as_ptr() as *const libc::c_char,
) == 0
{
(*ctxt).state = XSLT_STATE_ERROR;
}
libc::free(terminate as *mut libc::c_void);
}
libc::free(content as *mut libc::c_void);
}
}
pub(crate) unsafe fn eval_avt(
ctxt: *mut _xsltTransformContext,
value: *const xmlChar,
) -> *mut xmlChar {
if value.is_null() {
return ptr::null_mut();
}
let len = libc::strlen(value as *const libc::c_char);
let bytes = core::slice::from_raw_parts(value, len);
let mut out: Vec<u8> = Vec::new();
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if b == b'{' {
if i + 1 < bytes.len() && bytes[i + 1] == b'{' {
out.push(b'{');
i += 2;
continue;
}
if let Some(rel) = bytes[i + 1..].iter().position(|c| *c == b'}') {
let close = i + 1 + rel;
let expr_bytes = &bytes[i + 1..close];
let expr_c = crate::xml::string::bytes_to_xmlstr(expr_bytes);
if !expr_c.is_null() {
let obj = eval_xpath(ctxt, expr_c);
xmlFreeImpl(expr_c as *mut c_void);
if !obj.is_null() {
let strv = xmlXPathCastToString(obj);
xmlXPathFreeObject(obj);
if !strv.is_null() {
let slen = libc::strlen(strv as *const libc::c_char);
out.extend_from_slice(core::slice::from_raw_parts(strv, slen));
xmlFreeImpl(strv as *mut c_void);
}
} else {
report_xpath_eval_failure(ctxt, (*ctxt).inst, "attribute-value-template");
return ptr::null_mut();
}
}
i = close + 1;
continue;
}
out.push(b'{');
i += 1;
continue;
}
if b == b'}' {
if i + 1 < bytes.len() && bytes[i + 1] == b'}' {
out.push(b'}');
i += 2;
continue;
}
out.push(b'}');
i += 1;
continue;
}
out.push(b);
i += 1;
}
crate::xml::string::bytes_to_xmlstr(&out)
}
pub(crate) unsafe fn process_literal_element(
ctxt: *mut _xsltTransformContext,
inst: *mut _xmlNode,
) {
let elem = new_node((*inst).ns, (*inst).name);
if elem.is_null() {
return;
}
append_to_result(ctxt, elem);
let mut prop = (*inst).properties;
while !prop.is_null() {
let attr_name = (*prop).name;
if !attr_name.is_null() {
let name_bytes = core::slice::from_raw_parts(
attr_name,
libc::strlen(attr_name as *const libc::c_char) as usize,
);
if name_bytes != b"xmlns" {
let attr_val = node_get_content((*prop).children);
if !attr_val.is_null() {
let avt_val = eval_avt(ctxt, attr_val);
libc::free(attr_val as *mut libc::c_void);
if !avt_val.is_null() {
set_prop(elem, attr_name, avt_val);
xmlFreeImpl(avt_val as *mut c_void);
}
}
}
}
prop = (*prop).next;
}
let saved_insert = (*ctxt).insert;
(*ctxt).insert = elem;
execute_content(ctxt, (*inst).children);
(*ctxt).insert = saved_insert;
}
unsafe fn emit_transform_error_with_ctxt(tctxt: *mut _xsltTransformContext, msg: &[u8]) {
{
let mut buf = msg.to_vec();
buf.push(0);
crate::xslt::errors::xsltTransformError(
tctxt,
ptr::null_mut(),
ptr::null_mut(),
buf.as_ptr() as *const std::os::raw::c_char,
);
}
}
pub(crate) unsafe fn register_xslt_functions(ctxt: *mut _xsltTransformContext) {
let xpath_ctxt = (*ctxt).xpathCtxt;
if xpath_ctxt.is_null() {
return;
}
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if internal.is_null() {
return;
}
let internal = &mut *internal;
use crate::xml::xpath::context::XPathContext;
use crate::xml::xpath::types::{NodeSet, XPathValue};
let core_funcs = crate::xml::xpath::functions::core_functions();
for (name, func) in core_funcs {
internal.register_function(&name, func);
}
internal.register_function("document", |ctx, args| {
let value = match args.first() {
Some(v) => v.as_string(),
None => return Err("document() requires an argument".to_string()),
};
let uri = value;
let _ = ctx;
let _ = uri;
Ok(XPathValue::NodeSet(NodeSet::new()))
});
internal.register_function("key", |ctx, args| {
let tctxt = ctx.func_lookup_data as *mut _xsltTransformContext;
if tctxt.is_null() {
return Ok(XPathValue::NodeSet(NodeSet::new()));
}
let name_str = match args.first() {
Some(v) => v.as_string(),
None => return Err("key() requires a name argument".to_string()),
};
let value_str = match args.get(1) {
Some(XPathValue::NodeSet(ns)) => match ns.first() {
Some(n) => crate::xml::xpath::types::node_string_value(n),
None => return Ok(XPathValue::NodeSet(NodeSet::new())),
},
Some(v) => v.as_string(),
None => return Err("key() requires a value argument".to_string()),
};
let name_c = crate::xml::string::bytes_to_xmlstr(name_str.as_bytes());
let value_c = crate::xml::string::bytes_to_xmlstr(value_str.as_bytes());
if name_c.is_null() || value_c.is_null() {
if !name_c.is_null() {
crate::abi::allocator::xmlFreeImpl(name_c as *mut c_void);
}
if !value_c.is_null() {
crate::abi::allocator::xmlFreeImpl(value_c as *mut c_void);
}
return Ok(XPathValue::NodeSet(NodeSet::new()));
}
let ns = unsafe { crate::xslt::keys::xsltEvalKeyFunction(tctxt, name_c, value_c) };
crate::abi::allocator::xmlFreeImpl(name_c as *mut c_void);
crate::abi::allocator::xmlFreeImpl(value_c as *mut c_void);
if ns.is_null() {
return Ok(XPathValue::NodeSet(NodeSet::new()));
}
let mut out = NodeSet::new();
unsafe {
let node_nr = (*ns).nodeNr;
let node_tab = (*ns).nodeTab;
if !node_tab.is_null() {
for i in 0..node_nr as isize {
let n = *node_tab.add(i as usize);
if !n.is_null() {
out.push(n);
}
}
}
crate::abi::exports_xml2::xmlXPathFreeNodeSet(ns);
}
Ok(XPathValue::NodeSet(out))
});
internal.register_function("generate-id", |ctx, args| {
let node = match args.first() {
Some(XPathValue::NodeSet(ns)) => ns.first().unwrap_or(ctx.context_node),
_ => ctx.context_node,
};
if node.is_null() {
return Ok(XPathValue::String(String::new()));
}
let id = { format!("id{:p}", node) };
Ok(XPathValue::String(id))
});
internal.register_function("format-number", |ctx, args| {
if args.len() < 2 || args.len() > 3 {
return Err("format-number() requires 2 or 3 arguments".to_string());
}
let number = args[0].as_number();
let picture = args[1].as_string();
let tctxt = ctx.func_lookup_data as *mut _xsltTransformContext;
let mut fmt: *mut _xsltDecimalFormat = if tctxt.is_null() {
ptr::null_mut()
} else {
unsafe { (*(*tctxt).style).decimalFormat }
};
if args.len() == 3 {
let qname = args[2].as_string();
let (prefix, local) = match qname.split_once(':') {
Some((p, l)) => (Some(p), l),
None => (None, qname.as_str()),
};
let mut ns_uri: *const xmlChar = ptr::null();
let mut ncname_ok = true;
if let Some(p) = prefix {
let inst = unsafe { (*tctxt).inst };
if inst.is_null() {
ncname_ok = false;
} else {
let prefix_c = crate::xml::string::bytes_to_xmlstr(p.as_bytes());
let ns = unsafe {
crate::abi::exports_xml2::xmlSearchNs((*inst).doc, inst, prefix_c)
};
if !prefix_c.is_null() {
crate::abi::allocator::xmlFreeImpl(prefix_c as *mut c_void);
}
if ns.is_null() {
let msg = format!(
"format-number : No namespace found for QName '{}:{}'\n",
p, local
);
emit_transform_error_with_ctxt(tctxt, msg.as_bytes());
unsafe {
(*(*tctxt).style).errors += 1;
}
ncname_ok = false;
} else {
ns_uri = unsafe { (*ns).href };
}
}
}
if ncname_ok {
let local_c = crate::xml::string::bytes_to_xmlstr(local.as_bytes());
let style = unsafe { (*tctxt).style };
fmt = crate::xslt::numbering::decimal_format_by_qname(style, ns_uri, local_c);
if !local_c.is_null() {
crate::abi::allocator::xmlFreeImpl(local_c as *mut c_void);
}
if fmt.is_null() {
let msg = format!("format-number() : undeclared decimal format '{}'\n", qname);
emit_transform_error_with_ctxt(tctxt, msg.as_bytes());
return Err("Stack usage error".to_string());
}
}
}
let picture_c = crate::xml::string::bytes_to_xmlstr(picture.as_bytes());
let mut result: *mut xmlChar = ptr::null_mut();
let status = unsafe {
crate::xslt::numbering::xslt_format_number_conversion(
fmt,
picture_c,
number,
&mut result,
)
};
if !picture_c.is_null() {
crate::abi::allocator::xmlFreeImpl(picture_c as *mut c_void);
}
let _ = status;
let s = if result.is_null() {
String::new()
} else {
unsafe {
std::ffi::CStr::from_ptr(result as *const std::os::raw::c_char)
.to_string_lossy()
.into_owned()
}
};
if !result.is_null() {
crate::abi::allocator::xmlFreeImpl(result as *mut c_void);
}
Ok(XPathValue::String(s))
});
internal.register_function("system-property", |_ctx, args| {
let name = match args.first() {
Some(v) => v.as_string(),
None => return Err("system-property() requires an argument".to_string()),
};
let value = match name.as_str() {
"xsl:version" => "1.0",
"xsl:vendor" => "libxslt",
"xsl:vendor-url" => "http://xmlsoft.org/XSLT/",
_ => "",
};
Ok(XPathValue::String(value.to_string()))
});
internal.register_function("element-available", |_ctx, args| {
let name = match args.first() {
Some(v) => v.as_string(),
None => return Err("element-available() requires an argument".to_string()),
};
let exslt_elements = [
"exsl:document",
"exsl:node-set",
"exsl:object-type",
"func:function",
"func:result",
"func:script",
"dyn:element",
"dyn:attribute",
"dyn:call",
"dyn:evaluate",
];
let available = name.starts_with("xsl:")
|| exslt_elements.contains(&name.as_str())
|| matches!(
name.as_str(),
"apply-templates"
| "call-template"
| "apply-imports"
| "for-each"
| "value-of"
| "copy-of"
| "copy"
| "element"
| "attribute"
| "text"
| "comment"
| "processing-instruction"
| "number"
| "choose"
| "if"
| "variable"
| "param"
| "sort"
| "message"
| "fallback"
| "output"
| "decimal-format"
| "namespace-alias"
| "attribute-set"
| "key"
| "strip-space"
| "preserve-space"
| "import"
| "include"
| "stylesheet"
| "transform"
);
Ok(XPathValue::Boolean(available))
});
internal.register_function("function-available", |_ctx, args| {
let name = match args.first() {
Some(v) => v.as_string(),
None => return Err("function-available() requires an argument".to_string()),
};
let core = [
"last",
"position",
"count",
"id",
"local-name",
"namespace-uri",
"name",
"string",
"concat",
"starts-with",
"contains",
"substring-before",
"substring-after",
"substring",
"string-length",
"normalize-space",
"translate",
"boolean",
"not",
"true",
"false",
"lang",
"number",
"sum",
"floor",
"ceiling",
"round",
];
let xslt_fn = [
"document",
"key",
"generate-id",
"system-property",
"element-available",
"function-available",
"current",
"unparsed-entity-uri",
];
let exslt_available = crate::exslt::lookup(&name).is_some();
let local = name.rsplit(':').next().unwrap_or(&name);
Ok(XPathValue::Boolean(
core.contains(&local) || xslt_fn.contains(&local) || exslt_available,
))
});
internal.register_function("current", |ctx, _args| {
let node = ctx.context_node;
if node.is_null() {
return Ok(XPathValue::NodeSet(NodeSet::new()));
}
let mut ns = NodeSet::new();
ns.push(node);
Ok(XPathValue::NodeSet(ns))
});
for (name, f) in crate::exslt::iter_functions() {
internal.register_function(&name, f);
}
crate::exslt::functions::register_stylesheet_functions(
internal,
(*ctxt)
.style
.as_ref()
.map_or(std::ptr::null_mut(), |s| s.doc),
);
internal.function_lookup = Some(Box::new(|ctx: &XPathContext, name: &str| {
let (prefix, local) = name.split_once(':')?;
let tctxt = ctx.func_lookup_data as *mut _xsltTransformContext;
if tctxt.is_null() {
return None;
}
let style = unsafe { (*tctxt).style.as_ref() }?;
let style_doc = style.doc;
let prefix_c = crate::xml::string::bytes_to_xmlstr(prefix.as_bytes());
let root = unsafe { (*style_doc).children };
let ns = unsafe { crate::xml::tree::search_ns(style_doc, root, prefix_c) };
if !prefix_c.is_null() {
crate::abi::allocator::xmlFreeImpl(prefix_c as *mut c_void);
}
if ns.is_null() {
return None;
}
let href = unsafe { (*ns).href };
if href.is_null() {
return None;
}
let href_str = unsafe {
std::ffi::CStr::from_ptr(href as *const std::os::raw::c_char)
.to_string_lossy()
.into_owned()
};
let local_c = crate::xml::string::bytes_to_xmlstr(local.as_bytes());
let href_c = crate::xml::string::bytes_to_xmlstr(href_str.as_bytes());
let fnptr = unsafe { crate::xslt::extensions::xsltFindExtFunction(tctxt, local_c, href_c) };
if !local_c.is_null() {
crate::abi::allocator::xmlFreeImpl(local_c as *mut c_void);
}
if !href_c.is_null() {
crate::abi::allocator::xmlFreeImpl(href_c as *mut c_void);
}
if fnptr.is_null() {
return None;
}
let f: Option<unsafe extern "C" fn(*mut c_void, c_int)> =
unsafe { std::mem::transmute(fnptr) };
let tctxt_addr = tctxt as usize;
Some(Box::new(
move |_ctx: &mut XPathContext, args: &[XPathValue]| {
let t = tctxt_addr as *mut _xsltTransformContext;
let xpath_ctxt = unsafe { (*t).xpathCtxt };
if xpath_ctxt.is_null() {
return Err("XSLT: null XPath context".to_string());
}
unsafe { crate::abi::exports_xml2::call_c_xpath_function(f, xpath_ctxt, args) }
},
))
}));
}
pub const fn exslt_element_names() -> &'static [&'static str] {
&[
"exsl:document",
"exsl:node-set",
"exsl:object-type",
"func:function",
"func:result",
"func:script",
"dyn:element",
"dyn:attribute",
"dyn:call",
"dyn:evaluate",
]
}
pub fn exslt_function_names() -> Vec<String> {
crate::exslt::iter_functions()
.into_iter()
.map(|(n, _)| n)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use core::ptr;
#[test]
fn test_new_context_null_style() {
unsafe {
assert!(xsltNewTransformContext(ptr::null_mut(), ptr::null_mut()).is_null());
}
}
#[test]
fn test_free_null() {
unsafe {
xsltFreeTransformContext(ptr::null_mut());
xsltFreeTransformResult(ptr::null_mut());
}
}
#[test]
fn test_apply_stylesheet_null() {
unsafe {
assert!(
xsltApplyStylesheet(ptr::null_mut(), ptr::null_mut(), ptr::null_mut()).is_null()
);
}
}
#[test]
fn test_end_to_end_simplified_stylesheet() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?><html xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"><body><p>Hello</p></body></html>\0";
let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
xsl.as_ptr() as *const c_char,
(xsl.len() - 1) as c_int,
ptr::null(),
);
assert!(!style.is_null(), "stylesheett parse failed");
let src = b"<?xml version=\"1.0\"?><root><item>world</item></root>\0";
let doc = crate::abi::exports_xml2::xmlReadMemory(
src.as_ptr() as *const c_char,
(src.len() - 1) as c_int,
ptr::null(),
ptr::null(),
0,
);
assert!(!doc.is_null());
let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
assert!(!result.is_null(), "apply failed");
let mut txt: *mut xmlChar = ptr::null_mut();
let mut len: c_int = 0;
let ret = crate::xslt::serialization::xsltSaveResultToString(
&mut txt, &mut len, result, style,
);
assert_eq!(ret, 0);
assert!(!txt.is_null());
let out = String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize));
assert!(
out.contains("Hello"),
"result should contain the literal text, got: {}",
out
);
libc::free(txt as *mut libc::c_void);
crate::xml::tree::free_doc(result);
crate::xml::tree::free_doc(doc);
crate::xslt::stylesheet::xsltFreeStylesheet(style);
}
}
#[test]
fn test_end_to_end_template_transform() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\n\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\
<xsl:template match=\"/\">\n\
<out><xsl:value-of select=\"/root/item\"/></out>\n\
</xsl:template>\n\
</xsl:stylesheet>\0";
let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
xsl.as_ptr() as *const c_char,
(xsl.len() - 1) as c_int,
ptr::null(),
);
assert!(!style.is_null(), "stylesheet parse failed");
let src = b"<?xml version=\"1.0\"?><root><item>world</item></root>\0";
let doc = crate::abi::exports_xml2::xmlReadMemory(
src.as_ptr() as *const c_char,
(src.len() - 1) as c_int,
ptr::null(),
ptr::null(),
0,
);
assert!(!doc.is_null());
let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
assert!(!result.is_null(), "apply failed");
let mut txt: *mut xmlChar = ptr::null_mut();
let mut len: c_int = 0;
let ret = crate::xslt::serialization::xsltSaveResultToString(
&mut txt, &mut len, result, style,
);
assert_eq!(ret, 0);
let out = String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize));
assert!(
out.contains("world"),
"result should contain the selected value, got: {}",
out
);
libc::free(txt as *mut libc::c_void);
crate::xml::tree::free_doc(result);
crate::xml::tree::free_doc(doc);
crate::xslt::stylesheet::xsltFreeStylesheet(style);
}
}
unsafe fn run_transform(xsl: &[u8], src: &[u8]) -> String {
let style = crate::xslt::stylesheet::xsltParseStylesheetMemory(
xsl.as_ptr() as *const c_char,
(xsl.len() - 1) as c_int,
ptr::null(),
);
assert!(!style.is_null(), "stylesheet parse failed");
let doc = crate::abi::exports_xml2::xmlReadMemory(
src.as_ptr() as *const c_char,
(src.len() - 1) as c_int,
ptr::null(),
ptr::null(),
0,
);
assert!(!doc.is_null());
let result = xsltApplyStylesheet(style, doc, ptr::null_mut());
assert!(!result.is_null(), "apply failed");
let mut txt: *mut xmlChar = ptr::null_mut();
let mut len: c_int = 0;
let ret =
crate::xslt::serialization::xsltSaveResultToString(&mut txt, &mut len, result, style);
assert_eq!(ret, 0);
let out =
String::from_utf8_lossy(core::slice::from_raw_parts(txt, len as usize)).into_owned();
libc::free(txt as *mut libc::c_void);
crate::xml::tree::free_doc(result);
crate::xml::tree::free_doc(doc);
crate::xslt::stylesheet::xsltFreeStylesheet(style);
out
}
#[test]
fn test_xslt_for_each() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<list><xsl:for-each select=\"/root/item\"><i><xsl:value-of select=\".\"/></i></xsl:for-each></list>\
</xsl:template>\
</xsl:stylesheet>\0";
let src =
b"<?xml version=\"1.0\"?><root><item>a</item><item>b</item><item>c</item></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<i>a</i>"), "got: {}", out);
assert!(out.contains("<i>b</i>"), "got: {}", out);
assert!(out.contains("<i>c</i>"), "got: {}", out);
}
}
#[test]
fn test_xslt_core_functions_in_value_of() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out>\
<cnt><xsl:value-of select=\"count(library/book)\"/></cnt>\
<sub><xsl:value-of select=\"substring('hello',1,2)\"/></sub>\
<str><xsl:value-of select=\"string(library/book[1]/title)\"/></str>\
</out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?>\
<library>\
<book><title>Rust</title></book>\
<book><title>XML</title></book>\
</library>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<cnt>2</cnt>"), "count() wrong: {}", out);
assert!(out.contains("<sub>he</sub>"), "substring() wrong: {}", out);
assert!(out.contains("<str>Rust</str>"), "string() wrong: {}", out);
}
}
#[test]
fn test_xslt_avt_in_literal_attribute() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out>\
<xsl:for-each select=\"library/book\">\
<book id=\"{@id}\" label=\"{{literal}}\"/>\
</xsl:for-each>\
</out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?>\
<library>\
<book id=\"b1\"/>\
<book id=\"b2\"/>\
</library>\0";
let out = run_transform(xsl, src);
assert!(
out.contains("<book id=\"b1\" label=\"{literal}\""),
"AVT not evaluated: {}",
out
);
assert!(
out.contains("<book id=\"b2\" label=\"{literal}\""),
"AVT not evaluated: {}",
out
);
}
}
#[test]
fn test_xslt_avt_in_xsl_element_name() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out>\
<xsl:element name=\"el-{library/book/@id}\">text</xsl:element>\
</out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?>\
<library><book id=\"b1\"/></library>\0";
let out = run_transform(xsl, src);
assert!(
out.contains("<el-b1>"),
"xsl:element AVT not evaluated: {}",
out
);
}
}
#[test]
fn test_xslt_variable_inline_content_rtf() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:variable name=\"rtf\"><nums><n>3</n><n>7</n></nums></xsl:variable>\
<xsl:template match=\"/\">\
<out><v><xsl:value-of select=\"$rtf\"/></v></out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root/>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<v>37</v>"), "RTF string-value wrong: {}", out);
}
}
#[test]
fn test_xslt_exsl_node_set_on_rtf() {
unsafe {
crate::exslt::register_all();
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:exsl=\"http://exslt.org/common\" xmlns:math=\"http://exslt.org/math\" extension-element-prefixes=\"exsl math\">\
<xsl:variable name=\"rtf\"><nums><n>3</n><n>7</n><n>1</n><n>9</n></nums></xsl:variable>\
<xsl:template match=\"/\">\
<out>\
<max><xsl:value-of select=\"math:max(exsl:node-set($rtf)/nums/n)\"/></max>\
<cnt><xsl:value-of select=\"count(exsl:node-set($rtf)/nums/n)\"/></cnt>\
</out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root/>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<max>9</max>"), "math:max wrong: {}", out);
assert!(out.contains("<cnt>4</cnt>"), "count wrong: {}", out);
}
}
#[test]
fn test_xslt_if_node_set_test() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out>\
<xsl:for-each select=\"library/book\">\
<b><xsl:if test=\"author\">A</xsl:if><xsl:if test=\"missing\">M</xsl:if></b>\
</xsl:for-each>\
</out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?>\
<library><book><author>x</author></book><book/></library>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<b>A</b>"), "node-set test false: {}", out);
assert!(out.contains("<b/>"), "missing-node test true: {}", out);
}
}
#[test]
fn test_xslt_attribute_string_value() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out>\
<x><xsl:value-of select=\"string(library/book[1]/@id)\"/></x>\
<y><xsl:value-of select=\"count(library/book[@id='b2'])\"/></y>\
</out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?>\
<library><book id=\"b1\"/><book id=\"b2\"/></library>\0";
let out = run_transform(xsl, src);
assert!(
out.contains("<x>b1</x>"),
"attr string-value wrong: {}",
out
);
assert!(out.contains("<y>1</y>"), "attr predicate wrong: {}", out);
}
}
#[test]
fn test_xslt_sort_descending() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out>\
<xsl:for-each select=\"library/book\">\
<xsl:sort select=\"title\" order=\"descending\"/>\
<i><xsl:value-of select=\"title\"/></i>\
</xsl:for-each>\
</out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?>\
<library><book><title>Alpha</title></book><book><title>Gamma</title></book><book><title>Beta</title></book></library>\0";
let out = run_transform(xsl, src);
let gamma = out.find("<i>Gamma</i>").unwrap();
let beta = out.find("<i>Beta</i>").unwrap();
let alpha = out.find("<i>Alpha</i>").unwrap();
assert!(gamma < beta && beta < alpha, "not descending: {}", out);
}
}
#[test]
fn test_xslt_key_function() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:key name=\"byAuthor\" match=\"book\" use=\"author\"/>\
<xsl:template match=\"/\">\
<out><k><xsl:value-of select=\"key('byAuthor', 'Smith')/title\"/></k></out>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?>\
<library><book><title>A</title><author>Smith</author></book><book><title>B</title><author>Jones</author></book></library>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<k>A</k>"), "key() wrong: {}", out);
}
}
#[test]
fn test_xslt_call_template_with_params() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out>\
<xsl:call-template name=\"greet\">\
<xsl:with-param name=\"who\" select=\"'World'\"/>\
</xsl:call-template>\
</out>\
</xsl:template>\
<xsl:template name=\"greet\">\
<xsl:param name=\"who\" select=\"'nobody'\"/>\
<g>Hello <xsl:value-of select=\"$who\"/>!</g>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root/>\0";
let out = run_transform(xsl, src);
assert!(out.contains("Hello World"), "with-param lost: {}", out);
}
}
#[test]
fn test_xslt_html_method_meta_charset() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:output method=\"html\" indent=\"yes\"/>\
<xsl:template match=\"/\">\
<html><head><title>T</title></head><body><p>x</p></body></html>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root/>\0";
let out = run_transform(xsl, src);
assert!(
out.contains("<meta charset=\"UTF-8\">"),
"meta charset missing: {}",
out
);
assert!(!out.contains(" <head>"), "unexpected indent: {}", out);
}
}
#[test]
fn test_xslt_if() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<xsl:if test=\"/root/item = 'yes'\"><yes/></xsl:if>\
<xsl:if test=\"/root/item = 'no'\"><no/></xsl:if>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root><item>yes</item></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<yes/>"), "got: {}", out);
assert!(!out.contains("<no/>"), "got: {}", out);
}
}
#[test]
fn test_xslt_choose() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<xsl:choose>\
<xsl:when test=\"/root/item = 'a'\"><chosen>a</chosen></xsl:when>\
<xsl:when test=\"/root/item = 'b'\"><chosen>b</chosen></xsl:when>\
<xsl:otherwise><chosen>other</chosen></xsl:otherwise>\
</xsl:choose>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root><item>b</item></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<chosen>b</chosen>"), "got: {}", out);
}
}
#[test]
fn test_xslt_variable_and_call_template() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:variable name=\"greeting\" select=\"'Hello'\"/>\
<xsl:template match=\"/\">\
<xsl:call-template name=\"say\"/>\
</xsl:template>\
<xsl:template name=\"say\">\
<msg><xsl:value-of select=\"$greeting\"/> <xsl:value-of select=\"/root/name\"/></msg>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root><name>World</name></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("HelloWorld"), "got: {}", out);
}
}
#[test]
fn test_xslt_text_preserves_whitespace() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<msg><xsl:value-of select=\"'Hello'\"/><xsl:text> </xsl:text><xsl:value-of select=\"/root/name\"/></msg>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root><name>World</name></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("Hello World"), "got: {}", out);
}
}
#[test]
fn test_xslt_element_and_attribute() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<xsl:element name=\"custom\">\
<xsl:attribute name=\"attr\">value</xsl:attribute>\
<xsl:value-of select=\"/root/item\"/>\
</xsl:element>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root><item>data</item></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("custom"), "got: {}", out);
assert!(out.contains("attr=\"value\""), "got: {}", out);
assert!(out.contains("data"), "got: {}", out);
}
}
#[test]
fn test_xslt_apply_templates_with_select() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<out><xsl:apply-templates select=\"/root/item\"/></out>\
</xsl:template>\
<xsl:template match=\"item\"><item><xsl:value-of select=\".\"/></item></xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root><item>alpha</item><item>beta</item></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<item>alpha</item>"), "got: {}", out);
assert!(out.contains("<item>beta</item>"), "got: {}", out);
}
}
#[test]
fn test_xslt_text_and_comment_and_pi() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<xsl:text>plain</xsl:text>\
<xsl:comment>a comment</xsl:comment>\
<xsl:processing-instruction name=\"target\">pi-data</xsl:processing-instruction>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root/>\0";
let out = run_transform(xsl, src);
assert!(out.contains("plain"), "got: {}", out);
assert!(out.contains("<!--a comment-->"), "got: {}", out);
assert!(out.contains("<?target pi-data?>"), "got: {}", out);
}
}
#[test]
fn test_xslt_copy_and_copy_of() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<xsl:copy-of select=\"/root/item\"/>\
<xsl:copy-of select=\"'literal'\"/>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root><item>copied</item></root>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<item>copied</item>"), "got: {}", out);
assert!(out.contains("literal"), "got: {}", out);
}
}
#[test]
fn test_xslt_number() {
unsafe {
let xsl = b"<?xml version=\"1.0\"?>\
<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\
<xsl:template match=\"/\">\
<n><xsl:number value=\"42\"/></n>\
<r><xsl:number value=\"9\" format=\"I\"/></r>\
</xsl:template>\
</xsl:stylesheet>\0";
let src = b"<?xml version=\"1.0\"?><root/>\0";
let out = run_transform(xsl, src);
assert!(out.contains("<n>42</n>"), "got: {}", out);
assert!(out.contains("<r>IX</r>"), "got: {}", out);
}
}
}