use std::iter::repeat;
use crate::{
chvalid::XmlCharValid,
parser::build_qname,
tree::{NodeCommon, XmlAttrPtr, XmlElementType, XmlNodePtr, XmlNsPtr},
xpath::XmlXPathObjectType,
};
use super::{
XmlXPathError, XmlXPathParserContext, xml_xpath_cast_node_to_number,
xml_xpath_cast_node_to_string, xml_xpath_convert_boolean, xml_xpath_convert_number,
xml_xpath_convert_string, xml_xpath_err, xml_xpath_free_node_set,
xml_xpath_get_elements_by_ids, xml_xpath_new_boolean, xml_xpath_new_float,
xml_xpath_new_node_set, xml_xpath_new_string, xml_xpath_node_set_create,
xml_xpath_node_set_merge, xml_xpath_string_eval_number, xml_xpath_wrap_node_set,
xml_xpath_wrap_string,
};
#[doc(alias = "CHECK_ARITY")]
pub(crate) fn check_arity(
ctxt: &mut XmlXPathParserContext,
nargs: usize,
x: usize,
) -> Result<(), XmlXPathError> {
if nargs != x {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidArity as i32);
return Err(XmlXPathError::XPathInvalidArity);
}
if (ctxt.value_tab.len() as i32) < ctxt.value_frame + x as i32 {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathStackError as i32);
return Err(XmlXPathError::XPathInvalidArity);
}
Ok(())
}
#[doc(alias = "CAST_TO_NUMBER")]
pub(super) fn cast_to_number(ctxt: &mut XmlXPathParserContext) {
if ctxt
.value()
.is_some_and(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_number_function(ctxt, 1);
}
}
#[doc(alias = "CAST_TO_STRING")]
pub(super) fn cast_to_string(ctxt: &mut XmlXPathParserContext) {
if ctxt
.value()
.is_some_and(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_string_function(ctxt, 1);
}
}
#[doc(alias = "CAST_TO_BOOLEAN")]
pub(super) fn cast_to_boolean(ctxt: &mut XmlXPathParserContext) {
if ctxt
.value()
.is_some_and(|value| value.typ != XmlXPathObjectType::XPathBoolean)
{
xml_xpath_boolean_function(ctxt, 1);
}
}
#[doc(alias = "xmlXPathBooleanFunction")]
pub fn xml_xpath_boolean_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
let Some(cur) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
let cur = xml_xpath_convert_boolean(Some(cur));
ctxt.value_push(cur);
}
#[doc(alias = "xmlXPathCeilingFunction")]
pub fn xml_xpath_ceiling_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
cast_to_number(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let val = &mut ctxt.value_mut().unwrap().floatval;
*val = val.ceil();
}
#[doc(alias = "xmlXPathCountFunction")]
pub fn xml_xpath_count_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
if ctxt.value().is_none_or(|value| {
!matches!(
value.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
if let Some(cur) = ctxt.value_pop() {
if let Some(nodeset) = cur.nodesetval.as_deref() {
ctxt.value_push(xml_xpath_new_float(nodeset.len() as f64));
} else {
ctxt.value_push(xml_xpath_new_float(0.0));
}
} else {
ctxt.value_push(xml_xpath_new_float(0.0));
}
}
#[doc(alias = "xmlXPathConcatFunction")]
pub fn xml_xpath_concat_function(ctxt: &mut XmlXPathParserContext, mut nargs: usize) {
if nargs < 2 && check_arity(ctxt, nargs, 2).is_err() {
return;
}
cast_to_string(ctxt);
let Some(mut cur) = ctxt
.value_pop()
.filter(|cur| cur.typ == XmlXPathObjectType::XPathString)
else {
return;
};
nargs -= 1;
while nargs > 0 {
cast_to_string(ctxt);
let Some(mut newobj) = ctxt
.value_pop()
.filter(|cur| cur.typ == XmlXPathObjectType::XPathString)
else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let mut tmp = newobj.stringval.take();
if let Some(curstr) = cur.stringval.take() {
tmp.get_or_insert_with(String::new).push_str(&curstr);
newobj.stringval = Some(curstr);
}
cur.stringval = tmp;
nargs -= 1;
}
ctxt.value_push(cur);
}
#[doc(alias = "xmlXPathContainsFunction")]
pub fn xml_xpath_contains_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 2).is_err() {
return;
}
cast_to_string(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let needle = ctxt.value_pop().unwrap();
cast_to_string(ctxt);
let hay = ctxt.value_pop();
let Some(hay) = hay.filter(|hay| hay.typ == XmlXPathObjectType::XPathString) else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
if needle
.stringval
.as_deref()
.filter(|&s| {
hay.stringval
.as_deref()
.expect("Internal Error")
.contains(s)
})
.is_some()
{
ctxt.value_push(xml_xpath_new_boolean(true));
} else {
ctxt.value_push(xml_xpath_new_boolean(false));
}
}
#[doc(alias = "xmlXPathIdFunction")]
pub fn xml_xpath_id_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
let Some(obj) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
if matches!(
obj.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
let mut ret = xml_xpath_node_set_create(None);
if let Some(nodeset) = obj.nodesetval.as_deref() {
for &node in &nodeset.node_tab {
let tokens = xml_xpath_cast_node_to_string(Some(node));
let ns = xml_xpath_get_elements_by_ids(ctxt.context.doc.unwrap(), Some(&tokens));
ret = xml_xpath_node_set_merge(ret, ns.as_deref());
xml_xpath_free_node_set(ns);
}
}
ctxt.value_push(xml_xpath_wrap_node_set(ret));
return;
}
let obj = xml_xpath_convert_string(Some(obj));
let strval = obj.stringval.as_deref();
let ret = xml_xpath_get_elements_by_ids(ctxt.context.doc.unwrap(), strval);
ctxt.value_push(xml_xpath_wrap_node_set(ret));
}
#[doc(alias = "xmlXPathFalseFunction")]
pub fn xml_xpath_false_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 0).is_err() {
return;
}
ctxt.value_push(xml_xpath_new_boolean(false));
}
#[doc(alias = "xmlXPathFloorFunction")]
pub fn xml_xpath_floor_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
cast_to_number(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let val = &mut ctxt.value_mut().unwrap().floatval;
*val = val.floor();
}
#[doc(alias = "xmlXPathLastFunction")]
pub fn xml_xpath_last_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 0).is_err() {
return;
}
if ctxt.context.context_size >= 0 {
ctxt.value_push(xml_xpath_new_float(ctxt.context.context_size as f64));
} else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidCtxtSize as i32);
}
}
#[doc(alias = "xmlXPathLangFunction")]
pub fn xml_xpath_lang_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
let mut ret: i32 = 0;
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
cast_to_string(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let val = ctxt.value_pop().unwrap();
let lang = val.stringval.as_deref();
let the_lang = ctxt.context.node.unwrap().get_lang();
'not_equal: {
if let (Some(the_lang), Some(lang)) = (the_lang, lang) {
let the_lang = the_lang.as_bytes();
let lang = lang.as_bytes();
let mut i = 0;
while i < lang.len() {
if !lang[i].eq_ignore_ascii_case(the_lang.get(i).unwrap_or(&0)) {
break 'not_equal;
}
i += 1;
}
if the_lang.get(i).unwrap_or(&0) == &0 || the_lang[i] == b'-' {
ret = 1;
}
}
}
ctxt.value_push(xml_xpath_new_boolean(ret != 0));
}
#[doc(alias = "xmlXPathLocalNameFunction")]
pub fn xml_xpath_local_name_function(ctxt: &mut XmlXPathParserContext, mut nargs: usize) {
if nargs == 0 {
ctxt.value_push(xml_xpath_new_node_set(ctxt.context.node));
nargs = 1;
}
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
if ctxt.value().is_none_or(|value| {
!matches!(
value.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let cur = ctxt.value_pop().unwrap();
if let Some(nodeset) = cur.nodesetval.as_deref() {
if !nodeset.node_tab.is_empty() {
let table = &nodeset.node_tab;
let i = 0;
match table[i].element_type() {
XmlElementType::XmlElementNode
| XmlElementType::XmlAttributeNode
| XmlElementType::XmlPINode => {
if table[i].name().is_some_and(|name| name.starts_with(' ')) {
ctxt.value_push(xml_xpath_new_string(Some("")));
} else {
ctxt.value_push(xml_xpath_new_string((*table[i]).name().as_deref()));
}
}
XmlElementType::XmlNamespaceDecl => {
let ns = XmlNsPtr::try_from(table[i]).unwrap();
let prefix = ns.prefix();
let value = xml_xpath_new_string(prefix.as_deref());
ctxt.value_push(value);
}
_ => {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
}
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
}
#[doc(alias = "xmlXPathNotFunction")]
pub fn xml_xpath_not_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
cast_to_boolean(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathBoolean)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let val = &mut ctxt.value_mut().unwrap().boolval;
*val = !*val;
}
#[doc(alias = "xmlXPathNameFunction")]
pub(super) fn xml_xpath_name_function(ctxt: &mut XmlXPathParserContext, mut nargs: usize) {
if nargs == 0 {
ctxt.value_push(xml_xpath_new_node_set(ctxt.context.node));
nargs = 1;
}
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
if ctxt.value().is_none_or(|value| {
!matches!(
value.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let cur = ctxt.value_pop().unwrap();
if let Some(nodeset) = cur.nodesetval.as_deref() {
if !nodeset.node_tab.is_empty() {
let table = &nodeset.node_tab;
let i = 0;
match table[i].element_type() {
XmlElementType::XmlElementNode => {
let node = XmlNodePtr::try_from(table[i]).unwrap();
if node.name.starts_with(' ') {
ctxt.value_push(xml_xpath_new_string(Some("")));
} else if let Some(prefix) =
node.ns.as_deref().and_then(|ns| ns.prefix.as_deref())
{
let nodename = node.name().unwrap();
let fullname = build_qname(&nodename, Some(prefix));
ctxt.value_push(xml_xpath_wrap_string(Some(&fullname)));
} else {
ctxt.value_push(xml_xpath_new_string(node.name().as_deref()));
}
}
XmlElementType::XmlAttributeNode => {
let attr = XmlAttrPtr::try_from(table[i]).unwrap();
if attr.name.starts_with(' ') {
ctxt.value_push(xml_xpath_new_string(Some("")));
} else if let Some(prefix) =
attr.ns.as_deref().and_then(|ns| ns.prefix.as_deref())
{
let attrname = attr.name().unwrap();
let fullname = build_qname(&attrname, Some(prefix));
ctxt.value_push(xml_xpath_wrap_string(Some(&fullname)));
} else {
ctxt.value_push(xml_xpath_new_string(attr.name().as_deref()));
}
}
_ => {
ctxt.value_push(xml_xpath_new_node_set(Some(table[i])));
xml_xpath_local_name_function(ctxt, 1);
}
}
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
}
#[doc(alias = "xmlXPathNamespaceURIFunction")]
pub fn xml_xpath_namespace_uri_function(ctxt: &mut XmlXPathParserContext, mut nargs: usize) {
if nargs == 0 {
ctxt.value_push(xml_xpath_new_node_set(ctxt.context.node));
nargs = 1;
}
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
if ctxt.value().is_none_or(|value| {
!matches!(
value.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let cur = ctxt.value_pop().unwrap();
if let Some(nodeset) = cur.nodesetval.as_deref() {
if !nodeset.node_tab.is_empty() {
let table = &nodeset.node_tab;
let i = 0;
match table[i].element_type() {
XmlElementType::XmlElementNode | XmlElementType::XmlAttributeNode => {
if let Ok(Some(ns)) = XmlNodePtr::try_from(table[i])
.map(|node| node.ns)
.or_else(|_| XmlAttrPtr::try_from(table[i]).map(|attr| attr.ns))
{
ctxt.value_push(xml_xpath_new_string(ns.href.as_deref()));
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
}
_ => {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
}
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
}
#[doc(alias = "xmlXPathNormalizeFunction")]
pub fn xml_xpath_normalize_function(ctxt: &mut XmlXPathParserContext, mut nargs: usize) {
if nargs == 0 {
let val = xml_xpath_cast_node_to_string(ctxt.context.node);
ctxt.value_push(xml_xpath_wrap_string(Some(&val)));
nargs = 1;
}
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
cast_to_string(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let Some(source) = ctxt.value_mut().unwrap().stringval.as_deref_mut() else {
return;
};
let oldlen = source.len();
let Some(start) = source.find(|c: char| !c.is_xml_blank_char()) else {
ctxt.value_mut()
.unwrap()
.stringval
.as_mut()
.unwrap()
.clear();
return;
};
let target = unsafe { source.as_bytes_mut() };
let mut written = 0;
let mut blank = false;
for i in start..oldlen {
let c = target[i];
if c.is_xml_blank_char() {
blank = true;
} else {
if blank {
target[written] = 0x20;
written += 1;
blank = false;
}
target[written] = c;
written += 1;
}
}
ctxt.value_mut()
.unwrap()
.stringval
.as_mut()
.unwrap()
.truncate(written);
}
#[doc(alias = "xmlXPathNumberFunction")]
pub fn xml_xpath_number_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if nargs == 0 {
if let Some(context_node) = ctxt.context.node {
let content = context_node.get_content();
let res = xml_xpath_string_eval_number(content.as_deref());
ctxt.value_push(xml_xpath_new_float(res));
} else {
ctxt.value_push(xml_xpath_new_float(0.0));
}
return;
}
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
let cur = ctxt.value_pop();
ctxt.value_push(xml_xpath_convert_number(cur));
}
#[doc(alias = "xmlXPathPositionFunction")]
pub fn xml_xpath_position_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 0).is_err() {
return;
}
if ctxt.context.proximity_position >= 0 {
ctxt.value_push(xml_xpath_new_float(ctxt.context.proximity_position as f64));
} else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidCtxtPosition as i32);
}
}
#[doc(alias = "xmlXPathRoundFunction")]
pub fn xml_xpath_round_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
cast_to_number(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let f: f64 = ctxt.value().unwrap().floatval;
if (-0.5..0.5).contains(&f) {
ctxt.value_mut().unwrap().floatval *= 0.0;
} else {
let mut rounded: f64 = f.floor();
if f - rounded >= 0.5 {
rounded += 1.0;
}
ctxt.value_mut().unwrap().floatval = rounded;
}
}
#[doc(alias = "xmlXPathStringFunction")]
pub fn xml_xpath_string_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if nargs == 0 {
let val = xml_xpath_cast_node_to_string(ctxt.context.node);
ctxt.value_push(xml_xpath_wrap_string(Some(&val)));
return;
}
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
let Some(cur) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
ctxt.value_push(xml_xpath_convert_string(Some(cur)));
}
#[doc(alias = "xmlXPathStringLengthFunction")]
pub fn xml_xpath_string_length_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if nargs == 0 {
if let Some(context_node) = ctxt.context.node {
let content = xml_xpath_cast_node_to_string(Some(context_node));
ctxt.value_push(xml_xpath_new_float(content.chars().count() as f64));
} else {
ctxt.value_push(xml_xpath_new_float(0.0));
}
return;
}
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
cast_to_string(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let cur = ctxt.value_pop().unwrap();
ctxt.value_push(xml_xpath_new_float(
cur.stringval.as_deref().map_or(0, |s| s.chars().count()) as _,
));
}
#[doc(alias = "xmlXPathStartsWithFunction")]
pub fn xml_xpath_starts_with_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 2).is_err() {
return;
}
cast_to_string(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let needle = ctxt.value_pop().unwrap();
cast_to_string(ctxt);
let hay = ctxt.value_pop();
let Some(hay) = hay.filter(|hay| hay.typ == XmlXPathObjectType::XPathString) else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let m = hay.stringval.as_deref().map_or(0, |s| s.len());
let n = needle.stringval.as_deref().map_or(0, |s| s.len());
if &hay.stringval.as_deref().unwrap()[..n.min(m)]
!= needle.stringval.as_deref().expect("Internal Error")
{
ctxt.value_push(xml_xpath_new_boolean(false));
} else {
ctxt.value_push(xml_xpath_new_boolean(true));
}
}
#[doc(alias = "xmlXPathSubstringFunction")]
pub fn xml_xpath_substring_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
let mut le: f64 = 0.0;
let mut i: i32 = 1;
let mut j: i32 = i32::MAX;
if nargs < 2 && check_arity(ctxt, nargs, 2).is_err() {
return;
}
if nargs > 3 && check_arity(ctxt, nargs, 3).is_err() {
return;
}
if nargs == 3 {
cast_to_number(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let len = ctxt.value_pop().unwrap();
le = len.floatval;
}
cast_to_number(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let start = ctxt.value_pop().unwrap();
let input: f64 = start.floatval;
cast_to_string(ctxt);
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
};
let str = ctxt.value_pop().unwrap();
if !matches!(
input.partial_cmp(&(i32::MAX as f64)),
Some(std::cmp::Ordering::Less)
) {
i = i32::MAX;
} else if input >= 1.0 {
i = input as _;
if input - input.floor() >= 0.5 {
i += 1;
}
}
if nargs == 3 {
let mut rin: f64;
let mut rle: f64;
rin = input.floor();
if input - rin >= 0.5 {
rin += 1.0;
}
rle = le.floor();
if le - rle >= 0.5 {
rle += 1.0;
}
let end: f64 = rin + rle;
if !matches!(
end.partial_cmp(&1.0),
Some(std::cmp::Ordering::Equal) | Some(std::cmp::Ordering::Greater)
) {
j = 1;
} else if end < i32::MAX as f64 {
j = end as _;
}
}
if i < j {
let ret = str
.stringval
.as_deref()
.expect("Internal Error")
.chars()
.skip(i as usize - 1)
.take((j - i) as usize)
.collect::<String>();
ctxt.value_push(xml_xpath_new_string(Some(&ret)));
} else {
ctxt.value_push(xml_xpath_new_string(Some("")));
}
}
#[doc(alias = "xmlXPathSubstringBeforeFunction")]
pub fn xml_xpath_substring_before_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 2).is_err() {
return;
}
cast_to_string(ctxt);
let find = ctxt.value_pop().unwrap();
cast_to_string(ctxt);
let str = ctxt.value_pop().unwrap();
let ss = str.stringval.as_deref().unwrap();
let fs = find.stringval.as_deref().unwrap();
let target = ss.find(fs).map(|pos| ss[..pos].to_owned());
ctxt.value_push(xml_xpath_new_string(target.as_deref()));
}
#[doc(alias = "xmlXPathSubstringAfterFunction")]
pub fn xml_xpath_substring_after_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 2).is_err() {
return;
}
cast_to_string(ctxt);
let find = ctxt.value_pop().unwrap();
cast_to_string(ctxt);
let str = ctxt.value_pop().unwrap();
let ss = str.stringval.as_deref().unwrap();
let fs = find.stringval.as_deref().unwrap();
let target = ss.find(fs).map(|pos| ss[pos..].to_owned());
ctxt.value_push(xml_xpath_new_string(target.as_deref()));
}
#[doc(alias = "xmlXPathSumFunction")]
pub fn xml_xpath_sum_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
let mut res: f64 = 0.0;
if check_arity(ctxt, nargs, 1).is_err() {
return;
}
if ctxt.value().is_none_or(|value| {
!matches!(
value.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let cur = ctxt.value_pop().unwrap();
if let Some(nodeset) = cur.nodesetval.as_deref().filter(|n| !n.is_empty()) {
if !nodeset.node_tab.is_empty() {
for &node in &nodeset.node_tab {
res += xml_xpath_cast_node_to_number(Some(node));
}
}
}
ctxt.value_push(xml_xpath_new_float(res));
}
#[doc(alias = "xmlXPathTrueFunction")]
pub fn xml_xpath_true_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 0).is_err() {
return;
}
ctxt.value_push(xml_xpath_new_boolean(true));
}
#[doc(alias = "xmlXPathTranslateFunction")]
pub fn xml_xpath_translate_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 3).is_err() {
return;
}
cast_to_string(ctxt);
let to = ctxt.value_pop().unwrap();
cast_to_string(ctxt);
let from = ctxt.value_pop().unwrap();
cast_to_string(ctxt);
let str = ctxt.value_pop().unwrap();
let to_str = to.stringval.as_deref().unwrap();
let from_str = from.stringval.as_deref().unwrap();
let arg = str.stringval.as_deref().unwrap();
let mut target = String::with_capacity(arg.len());
for c in arg.chars() {
if let Some((_, replace)) = from_str
.chars()
.zip(to_str.chars().map(Ok).chain(repeat(Err(()))))
.find(|e| e.0 == c)
{
if let Ok(c) = replace {
target.push(c);
}
} else {
target.push(c);
}
}
ctxt.value_push(xml_xpath_new_string(Some(&target)));
}
#[doc(alias = "xmlXPathEscapeUriFunction")]
pub(super) fn xml_xpath_escape_uri_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
let mut escape: [u8; 4] = [0; 4];
if check_arity(ctxt, nargs, 2).is_err() {
return;
}
let escape_reserved = ctxt.pop_boolean();
cast_to_string(ctxt);
let str = ctxt.value_pop().unwrap();
escape[0] = b'%';
escape[3] = 0;
let mut target = String::new();
let cptr = str.stringval.as_deref().expect("Internal Error").as_bytes();
for (i, &c) in cptr.iter().enumerate() {
if c.is_ascii_alphanumeric()
|| c == b'-'
|| c == b'_'
|| c == b'.'
|| c == b'!'
|| c == b'~'
|| c == b'*'
|| c == b'\''
|| c == b'('
|| c == b')'
|| (c == b'%'
&& (i + 1 < cptr.len() && cptr[i + 1].is_ascii_hexdigit())
&& (i + 2 < cptr.len() && cptr[i + 2].is_ascii_hexdigit()))
|| (!escape_reserved
&& (c == b';'
|| c == b'/'
|| c == b'?'
|| c == b':'
|| c == b'@'
|| c == b'&'
|| c == b'='
|| c == b'+'
|| c == b'$'
|| c == b','))
{
target.push(c as char);
} else {
target.push('%');
let hi = if c >> 4 < 10 {
b'0' + (c >> 4)
} else {
b'A' - 10 + (c >> 4)
};
target.push(hi as char);
let lo = if c & 0xF < 10 {
b'0' + (c & 0xF)
} else {
b'A' - 10 + (c & 0xF)
};
target.push(lo as char);
}
}
ctxt.value_push(xml_xpath_new_string(Some(&target)));
}