use crate::abi::allocator::xmlFree;
use crate::abi::exports_xml2::{xmlXPathFreeObject, xmlXPathObjectCopy};
use crate::abi::structs::*;
use crate::abi::types::*;
use std::ffi::c_void;
use std::os::raw::c_int;
use std::ptr;
pub const XSLT_VAR_GLOBAL: c_int = 1 << 0;
pub const XSLT_VAR_PARAM: c_int = 1 << 1;
pub const XSLT_VAR_INTERNAL: c_int = 1 << 2;
pub unsafe fn xsltPushVariable(
ctxt: *mut _xsltTransformContext,
var: *mut _xsltStackElem,
) -> c_int {
if ctxt.is_null() || var.is_null() {
return -1;
}
let ctx = &mut *ctxt;
let new_vars_nr = ctx.varsNr + 1;
if new_vars_nr > ctx.varsMax {
let new_max = if ctx.varsMax == 0 {
16
} else {
ctx.varsMax * 2
};
let new_tab = libc::realloc(
ctx.varsTab as *mut libc::c_void,
(new_max as usize) * core::mem::size_of::<*mut _xsltStackElem>(),
) as *mut *mut _xsltStackElem;
if new_tab.is_null() {
return -1;
}
ctx.varsTab = new_tab as *mut c_void;
ctx.varsMax = new_max;
}
(*var).next = if ctx.varsNr > 0 {
*((ctx.varsTab as *mut *mut _xsltStackElem).offset((ctx.varsNr - 1) as isize))
} else {
ptr::null_mut()
};
*((ctx.varsTab as *mut *mut _xsltStackElem).offset(ctx.varsNr as isize)) = var;
ctx.varsNr = new_vars_nr;
let xpath_ctxt = ctx.xpathCtxt;
if !xpath_ctxt.is_null() && !(*var).name.is_null() {
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if !internal.is_null() {
let name_len = libc::strlen((*var).name as *const libc::c_char);
let name_bytes = core::slice::from_raw_parts((*var).name, name_len);
let name = String::from_utf8_lossy(name_bytes).into_owned();
let value = if (*var).value.is_null() {
crate::xml::xpath::types::XPathValue::String(String::new())
} else {
crate::abi::exports_xml2::object_to_xpathvalue_pub((*var).value)
};
(*internal).register_variable(&name, value);
}
}
0
}
pub unsafe fn xsltPopVariable(ctxt: *mut _xsltTransformContext) -> *mut _xsltStackElem {
if ctxt.is_null() {
return ptr::null_mut();
}
let ctx = &mut *ctxt;
if ctx.varsNr == 0 {
return ptr::null_mut();
}
ctx.varsNr -= 1;
let var = *((ctx.varsTab as *mut *mut _xsltStackElem).offset(ctx.varsNr as isize));
if ctx.varsNr > 0 {
let prev = *((ctx.varsTab as *mut *mut _xsltStackElem).offset((ctx.varsNr - 1) as isize));
(*prev).next = ptr::null_mut();
}
(*var).next = ptr::null_mut();
if !(*var).name.is_null() {
let xpath_ctxt = ctx.xpathCtxt;
if !xpath_ctxt.is_null() {
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if !internal.is_null() {
let name_len = libc::strlen((*var).name as *const libc::c_char);
let name_bytes = core::slice::from_raw_parts((*var).name, name_len);
let name = String::from_utf8_lossy(name_bytes).into_owned();
(*internal).unregister_variable(&name);
}
}
}
var
}
pub unsafe fn xsltLookupVariable(
ctxt: *mut _xsltTransformContext,
name: *const xmlChar,
ns_uri: *const xmlChar,
) -> *mut _xsltStackElem {
if ctxt.is_null() || name.is_null() {
return ptr::null_mut();
}
let ctx = &mut *ctxt;
let mut i = ctx.varsNr;
while i > 0 {
i -= 1;
let var = *((ctx.varsTab as *mut *mut _xsltStackElem).offset(i as isize));
if var.is_null() {
continue;
}
let vname = (*var).name;
if vname.is_null() {
continue;
}
if libc::strcmp(vname as *const libc::c_char, name as *const libc::c_char) != 0 {
continue;
}
if !ns_uri.is_null() && !(*var).nameURI.is_null() {
if libc::strcmp(
(*var).nameURI as *const libc::c_char,
ns_uri as *const libc::c_char,
) != 0
{
continue;
}
}
return var;
}
ptr::null_mut()
}
pub unsafe fn xsltEvalVariable(
ctxt: *mut _xsltTransformContext,
var: *mut _xsltStackElem,
) -> *mut _xmlXPathObject {
if ctxt.is_null() || var.is_null() {
return ptr::null_mut();
}
let v = &mut *var;
if !v.value.is_null() {
return xmlXPathObjectCopy(v.value);
}
ptr::null_mut()
}
pub unsafe fn xsltFreeStackElem(var: *mut _xsltStackElem) {
if var.is_null() {
return;
}
let v = &mut *var;
if !v.comp.is_null() {
xmlXPathFreeObject(v.comp);
v.comp = ptr::null_mut();
}
if !v.value.is_null() {
xmlXPathFreeObject(v.value);
v.value = ptr::null_mut();
}
if (v.flags & XSLT_VAR_INTERNAL) != 0 {
if !v.name.is_null() {
libc::free(v.name as *mut libc::c_void);
}
if !v.nameURI.is_null() {
libc::free(v.nameURI as *mut libc::c_void);
}
if !v.select.is_null() {
libc::free(v.select as *mut libc::c_void);
}
}
v.next = ptr::null_mut();
xmlFree(var as *mut libc::c_void);
}
pub unsafe fn xsltFreeGlobalVariables(ctxt: *mut _xsltTransformContext) {
if ctxt.is_null() {
return;
}
let ctx = &mut *ctxt;
let mut i = 0;
while i < ctx.varsNr {
let var = *((ctx.varsTab as *mut *mut _xsltStackElem).offset(i as isize));
if !var.is_null() {
xsltFreeStackElem(var);
*((ctx.varsTab as *mut *mut _xsltStackElem).offset(i as isize)) = ptr::null_mut();
}
i += 1;
}
ctx.varsNr = 0;
if !ctx.varsTab.is_null() {
libc::free(ctx.varsTab as *mut libc::c_void);
ctx.varsTab = ptr::null_mut();
ctx.varsMax = 0;
}
}
pub unsafe fn xsltInitGlobalVariables(ctxt: *mut _xsltTransformContext) {
if ctxt.is_null() {
return;
}
let ctx = &mut *ctxt;
let style = ctx.style;
if style.is_null() {
return;
}
let mut param = (*style).params as *mut _xsltStackElem;
while !param.is_null() {
register_global_value(ctxt, param, false);
param = (*param).next;
}
let mut var = (*style).variables as *mut _xsltStackElem;
while !var.is_null() {
register_global_value(ctxt, var, true);
var = (*var).next;
}
}
unsafe fn register_global_value(
ctxt: *mut _xsltTransformContext,
var: *mut _xsltStackElem,
skip_if_bound: bool,
) {
if var.is_null() || (*var).name.is_null() {
return;
}
let name = crate::abi::versioning::c_str_to_bytes((*var).name as *const std::os::raw::c_char);
let name = match name {
Some(n) => String::from_utf8_lossy(n).into_owned(),
None => return,
};
if skip_if_bound {
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() && (*internal).variables.contains_key(&name) {
return;
}
}
}
let value: Option<crate::xml::xpath::types::XPathValue> = if !(*var).value.is_null() {
let v = (*var).value;
let typ = (*v).type_;
if typ == xmlXPathObjectType::XPATH_STRING as c_int {
let s = (*v).stringval;
let s = if s.is_null() {
String::new()
} else {
crate::abi::versioning::c_str_to_bytes(s as *const std::os::raw::c_char)
.map(|b| String::from_utf8_lossy(b).into_owned())
.unwrap_or_default()
};
Some(crate::xml::xpath::types::XPathValue::String(s))
} else {
None
}
} else if !(*var).select.is_null() {
let xpath_ctxt = (*ctxt).xpathCtxt;
if xpath_ctxt.is_null() {
return;
}
let saved_node = (*xpath_ctxt).node;
(*xpath_ctxt).node = (*ctxt).document as *mut _xmlNode;
let internal = (*xpath_ctxt).extra as *mut crate::xml::xpath::context::XPathContext;
if !internal.is_null() {
(*internal).context_node = (*ctxt).document as *mut _xmlNode;
(*internal).document = (*ctxt).document;
}
let obj = crate::abi::exports_xml2::xmlXPathEvalExpression((*var).select, xpath_ctxt);
(*xpath_ctxt).node = saved_node;
let result = if !obj.is_null() {
let v = crate::abi::exports_xml2::object_to_xpathvalue_pub(obj);
crate::abi::exports_xml2::xmlXPathFreeObject(obj);
Some(v)
} else {
let xerr = {
let internal = (*ctxt).xpathCtxt;
if internal.is_null() {
"Invalid expression".to_string()
} else {
let x = (*internal).extra as *mut crate::xml::xpath::context::XPathContext;
if x.is_null() {
"Invalid expression".to_string()
} else {
(*x).error
.clone()
.unwrap_or_else(|| "Invalid expression".to_string())
}
}
};
let nm =
crate::abi::versioning::c_str_to_bytes((*var).name as *const std::os::raw::c_char)
.unwrap_or(b"")
.to_vec();
eprintln!("XPath error : {}", xerr);
eprintln!("runtime error");
eprintln!(
"Evaluating user parameter {} failed",
String::from_utf8_lossy(&nm)
);
(*ctxt).state = crate::xslt::transform::XSLT_STATE_STOPPED;
None
};
result
} else if !(*var).tree.is_null() {
let doc = crate::xml::tree::new_doc(ptr::null());
if doc.is_null() {
return;
}
let mut child = (*var).tree;
let mut last: *mut _xmlNode = ptr::null_mut();
while !child.is_null() {
let copy = crate::xml::tree::copy_node(child, 1);
if !copy.is_null() {
if last.is_null() {
(*doc).children = copy;
} else {
(*last).next = copy;
}
(*copy).prev = last;
(*copy).parent = doc as *mut _xmlNode;
(*copy).doc = doc;
last = copy;
}
child = (*child).next;
}
if !last.is_null() {
(*doc).last = last;
}
crate::xslt::documents::xsltRegisterRVT(ctxt, doc);
let mut ns = crate::xml::xpath::types::NodeSet::new();
ns.push(doc as *mut _xmlNode);
Some(crate::xml::xpath::types::XPathValue::NodeSet(ns))
} else {
Some(crate::xml::xpath::types::XPathValue::String(String::new()))
};
if let Some(v) = value {
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() {
(*internal).register_variable(&name, v);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::allocator::{xmlFree, xmlMalloc};
use crate::abi::structs::*;
use core::ptr;
fn make_stack_elem(name: &[u8]) -> *mut _xsltStackElem {
unsafe {
let v = xmlMalloc(core::mem::size_of::<_xsltStackElem>()) as *mut _xsltStackElem;
if v.is_null() {
return ptr::null_mut();
}
core::ptr::write(v, {
let cname = libc::malloc(name.len() + 1) as *mut xmlChar;
if !cname.is_null() {
libc::memcpy(
cname as *mut libc::c_void,
name.as_ptr() as *const libc::c_void,
name.len(),
);
*cname.add(name.len()) = 0;
}
_xsltStackElem {
next: ptr::null_mut(),
style: ptr::null_mut(),
inst: ptr::null_mut(),
name: cname,
nameURI: ptr::null(),
select: ptr::null(),
comp: ptr::null_mut(),
tree: ptr::null_mut(),
value: ptr::null_mut(),
flags: XSLT_VAR_INTERNAL,
level: 0,
compCtxt: 0,
}
});
v
}
}
fn make_ctxt() -> *mut _xsltTransformContext {
unsafe {
let c = xmlMalloc(core::mem::size_of::<_xsltTransformContext>())
as *mut _xsltTransformContext;
if c.is_null() {
return ptr::null_mut();
}
core::ptr::write(
c,
_xsltTransformContext {
style: ptr::null_mut(),
templ: ptr::null_mut(),
document: ptr::null_mut(),
node: ptr::null_mut(),
nodeList: ptr::null_mut(),
contextSize: 0,
proximityPosition: 0,
xpathCtxt: ptr::null_mut(),
xpathReturn: ptr::null_mut(),
xpathReturnNr: 0,
xpathReturnMax: 0,
xpathReturnTab: ptr::null_mut(),
templNr: 0,
templMax: 0,
templTab: ptr::null_mut(),
depth: 0,
maxDepth: 0,
varsNr: 0,
varsMax: 0,
varsBase: 0,
varsTab: ptr::null_mut(),
paramsNr: 0,
paramsMax: 0,
paramsBase: 0,
paramsTab: ptr::null_mut(),
keyTables: ptr::null_mut(),
hasSort: 0,
output: ptr::null_mut(),
errFunc: ptr::null_mut(),
errCtxt: ptr::null_mut(),
extInfos: ptr::null_mut(),
extrasNr: 0,
extrasMax: 0,
extrasTab: ptr::null_mut(),
state: 0,
priority: 0,
parserCtxt: ptr::null_mut(),
save: 0,
opts: 0,
secPrefs: ptr::null_mut(),
docCache: ptr::null_mut(),
globalVars: ptr::null_mut(),
profile: ptr::null_mut(),
prof: 0,
extFunctionsNr: 0,
extFunctionsMax: 0,
extFunctionsTab: ptr::null_mut(),
userData: ptr::null_mut(),
frame: 0,
frameNr: 0,
frameMax: 0,
frameTab: ptr::null_mut(),
insert: ptr::null_mut(),
resultDoc: ptr::null_mut(),
nsNr: 0,
nsMax: 0,
nsTab: ptr::null_mut(),
nsNrOriginal: 0,
nsMaxOriginal: 0,
nsTabOriginal: ptr::null_mut(),
currentTemplate: ptr::null_mut(),
templHash: ptr::null_mut(),
varsNrOriginal: 0,
varsMaxOriginal: 0,
varsTabOriginal: ptr::null_mut(),
insertDepth: 0,
maxInsertDepth: 0,
},
);
c
}
}
#[test]
fn test_push_pop_variable() {
unsafe {
let ctxt = make_ctxt();
assert!(!ctxt.is_null());
let v1 = make_stack_elem(b"var1\0");
let v2 = make_stack_elem(b"var2\0");
assert_eq!(xsltPushVariable(ctxt, v1), 0);
assert_eq!(xsltPushVariable(ctxt, v2), 0);
assert_eq!((*ctxt).varsNr, 2);
let popped = xsltPopVariable(ctxt);
assert_eq!(popped, v2);
let popped2 = xsltPopVariable(ctxt);
assert_eq!(popped2, v1);
assert!(xsltPopVariable(ctxt).is_null());
xsltFreeStackElem(v1);
xsltFreeStackElem(v2);
xmlFree(ctxt as *mut libc::c_void);
}
}
#[test]
fn test_lookup_variable() {
unsafe {
let ctxt = make_ctxt();
let v1 = make_stack_elem(b"foo\0");
let v2 = make_stack_elem(b"bar\0");
xsltPushVariable(ctxt, v1);
xsltPushVariable(ctxt, v2);
let found = xsltLookupVariable(ctxt, b"bar\0".as_ptr() as *const xmlChar, ptr::null());
assert_eq!(found, v2);
let not_found =
xsltLookupVariable(ctxt, b"baz\0".as_ptr() as *const xmlChar, ptr::null());
assert!(not_found.is_null());
xsltFreeStackElem(v1);
xsltFreeStackElem(v2);
xmlFree(ctxt as *mut libc::c_void);
}
}
#[test]
fn test_lookup_empty_stack() {
unsafe {
let ctxt = make_ctxt();
let found = xsltLookupVariable(ctxt, b"foo\0".as_ptr() as *const xmlChar, ptr::null());
assert!(found.is_null());
xmlFree(ctxt as *mut libc::c_void);
}
}
#[test]
fn test_push_null_returns_error() {
unsafe {
let ctxt = make_ctxt();
assert_eq!(xsltPushVariable(ptr::null_mut(), ptr::null_mut()), -1);
assert_eq!(xsltPushVariable(ctxt, ptr::null_mut()), -1);
xmlFree(ctxt as *mut libc::c_void);
}
}
}