use std::ptr::null_mut;
#[cfg(feature = "libxml_xptr_locs")]
use crate::xpointer::XmlLocationSet;
use crate::{
chvalid::XmlCharValid,
error::{__xml_raise_error, XmlErrorDomain, XmlErrorLevel, XmlParserErrors},
generic_error,
tree::{
NodeCommon, XML_XML_NAMESPACE, XmlAttrPtr, XmlDocPtr, XmlDtdPtr, XmlElementType,
XmlGenericNodePtr, XmlNodePtr, XmlNs, XmlNsPtr,
},
valid::xml_get_id,
xpath::{
XML_XPATH_NAN, XmlXPathError, XmlXPathObjectType,
functions::{cast_to_number, xml_xpath_number_function},
xml_xpath_cast_node_to_number, xml_xpath_cast_node_to_string,
xml_xpath_cast_number_to_boolean, xml_xpath_cast_to_number, xml_xpath_is_inf,
xml_xpath_is_nan, xml_xpath_node_set_create, xml_xpath_object_copy,
},
};
use super::{
XmlNodeSet, XmlXPathContext, XmlXPathObject, XmlXPathParserContext,
functions::xml_xpath_boolean_function, xml_xpath_new_node_set, xml_xpath_new_string,
};
const XML_XPATH_ERROR_MESSAGES: &[&str] = &[
"Ok\n",
"Number encoding\n",
"Unfinished literal\n",
"Start of literal\n",
"Expected $ for variable reference\n",
"Undefined variable\n",
"Invalid predicate\n",
"Invalid expression\n",
"Missing closing curly brace\n",
"Unregistered function\n",
"Invalid operand\n",
"Invalid type\n",
"Invalid number of arguments\n",
"Invalid context size\n",
"Invalid context position\n",
"Memory allocation error\n",
"Syntax error\n",
"Resource error\n",
"Sub resource error\n",
"Undefined namespace prefix\n",
"Encoding error\n",
"Char out of XML range\n",
"Invalid or incomplete context\n",
"Stack usage error\n",
"Forbidden variable\n",
"Operation limit exceeded\n",
"Recursion limit exceeded\n",
"?? Unknown error ??\n",
];
const MAXERRNO: i32 = XML_XPATH_ERROR_MESSAGES.len() as i32 - 1;
#[doc(alias = "xmlXPathErr", alias = "xmlXPatherror")]
pub fn xml_xpath_err(ctxt: Option<&mut XmlXPathParserContext>, mut error: i32) {
if !(0..=MAXERRNO).contains(&error) {
error = MAXERRNO;
}
let Some(ctxt) = ctxt else {
let code = error + XmlParserErrors::XmlXPathExpressionOk as i32
- XmlXPathError::XPathExpressionOK as i32;
let code = XmlParserErrors::try_from(code).unwrap();
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromXPath,
code,
XmlErrorLevel::XmlErrError,
None,
0,
None,
None,
None,
0,
0,
Some(XML_XPATH_ERROR_MESSAGES[error as usize]),
);
return;
};
if ctxt.error != 0 {
return;
}
ctxt.error = error;
ctxt.context.last_error.reset();
ctxt.context.last_error.domain = XmlErrorDomain::XmlFromXPath;
ctxt.context.last_error.code = XmlParserErrors::try_from(
error + XmlParserErrors::XmlXPathExpressionOk as i32
- XmlXPathError::XPathExpressionOK as i32,
)
.unwrap();
ctxt.context.last_error.level = XmlErrorLevel::XmlErrError;
ctxt.context.last_error.str1 = Some(ctxt.base.to_string().into());
ctxt.context.last_error.int1 = ctxt.cur as _;
ctxt.context.last_error.node = ctxt.context.debug_node;
if let Some(error) = ctxt.context.error {
error(ctxt.context.user_data.clone(), &ctxt.context.last_error);
} else {
let code = error + XmlParserErrors::XmlXPathExpressionOk as i32
- XmlXPathError::XPathExpressionOK as i32;
let code = XmlParserErrors::try_from(code).unwrap();
__xml_raise_error!(
None,
None,
None,
null_mut(),
ctxt.context.debug_node,
XmlErrorDomain::XmlFromXPath,
code,
XmlErrorLevel::XmlErrError,
None,
0,
Some(ctxt.base.to_string().into()),
None,
None,
ctxt.cur as _,
0,
Some(XML_XPATH_ERROR_MESSAGES[error as usize]),
);
}
}
#[doc(alias = "xmlXPathErrMemory")]
pub fn xml_xpath_err_memory(ctxt: Option<&mut XmlXPathContext>, extra: Option<&str>) {
if let Some(ctxt) = ctxt {
ctxt.last_error.reset();
if let Some(extra) = extra {
let buf = format!("Memory allocation failed : {extra}\n",);
ctxt.last_error.message = Some(buf.into());
} else {
ctxt.last_error.message = Some("Memory allocation failed\n".into());
}
ctxt.last_error.domain = XmlErrorDomain::XmlFromXPath;
ctxt.last_error.code = XmlParserErrors::XmlErrNoMemory;
if let Some(error) = ctxt.error {
error(ctxt.user_data.clone(), &ctxt.last_error);
}
} else if let Some(extra) = extra {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromXPath,
XmlParserErrors::XmlErrNoMemory,
XmlErrorLevel::XmlErrFatal,
None,
0,
Some(extra.to_owned().into()),
None,
None,
0,
0,
"Memory allocation failed : {}\n",
extra
);
} else {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromXPath,
XmlParserErrors::XmlErrNoMemory,
XmlErrorLevel::XmlErrFatal,
None,
0,
None,
None,
None,
0,
0,
"Memory allocation failed\n",
);
}
}
#[doc(alias = "xmlXPathPErrMemory")]
pub(super) fn xml_xpath_perr_memory(ctxt: Option<&mut XmlXPathParserContext>, extra: Option<&str>) {
if let Some(ctxt) = ctxt {
ctxt.error = XmlXPathError::XPathMemoryError as i32;
xml_xpath_err_memory(Some(&mut *ctxt.context), extra);
} else {
xml_xpath_err_memory(None, extra);
}
}
pub const XML_NODESET_DEFAULT: usize = 10;
#[doc(alias = "xmlXPathNodeSetDupNs")]
pub fn xml_xpath_node_set_dup_ns(
node: Option<XmlGenericNodePtr>,
mut ns: XmlNsPtr,
) -> Option<XmlGenericNodePtr> {
if node.is_none_or(|node| matches!(node.element_type(), XmlElementType::XmlNamespaceDecl)) {
if ns.node.is_none() {
ns.node = ns.next.map(|next| next.into());
}
return Some(ns.into());
}
let Some(mut cur) = XmlNsPtr::new(XmlNs {
typ: XmlElementType::XmlNamespaceDecl,
..Default::default()
}) else {
xml_xpath_err_memory(None, Some("duplicating namespace\n"));
return None;
};
cur.href = ns.href.clone();
cur.prefix = ns.prefix.clone();
cur.node = node;
Some(cur.into())
}
#[doc(alias = "xmlXPathRoot")]
pub fn xml_xpath_root(ctxt: &mut XmlXPathParserContext) {
ctxt.value_push(xml_xpath_new_node_set(
ctxt.context.doc.map(|doc| doc.into()),
));
}
pub(super) const XPATH_MAX_RECURSION_DEPTH: usize = 5000;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum XmlXPathAxisVal {
AxisAncestor = 1,
AxisAncestorOrSelf = 2,
AxisAttribute = 3,
AxisChild = 4,
AxisDescendant = 5,
AxisDescendantOrSelf = 6,
AxisFollowing = 7,
AxisFollowingSibling = 8,
AxisNamespace = 9,
AxisParent = 10,
AxisPreceding = 11,
AxisPrecedingSibling = 12,
AxisSelf = 13,
}
impl TryFrom<i32> for XmlXPathAxisVal {
type Error = anyhow::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value == 1 {
Ok(Self::AxisAncestor)
} else if value == 2 {
Ok(Self::AxisAncestorOrSelf)
} else if value == 3 {
Ok(Self::AxisAttribute)
} else if value == 4 {
Ok(Self::AxisChild)
} else if value == 5 {
Ok(Self::AxisDescendant)
} else if value == 6 {
Ok(Self::AxisDescendantOrSelf)
} else if value == 7 {
Ok(Self::AxisFollowing)
} else if value == 8 {
Ok(Self::AxisFollowingSibling)
} else if value == 9 {
Ok(Self::AxisNamespace)
} else if value == 10 {
Ok(Self::AxisParent)
} else if value == 11 {
Ok(Self::AxisPreceding)
} else if value == 12 {
Ok(Self::AxisPrecedingSibling)
} else if value == 13 {
Ok(Self::AxisSelf)
} else {
Err(anyhow::anyhow!(
"Invalid convert from value '{value}' to XmlXPathAxisVal"
))
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum XmlXPathTestVal {
NodeTestNone = 0,
NodeTestType = 1,
NodeTestPI = 2,
NodeTestAll = 3,
NodeTestNs = 4,
NodeTestName = 5,
}
impl TryFrom<i32> for XmlXPathTestVal {
type Error = anyhow::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value == 0 {
Ok(Self::NodeTestNone)
} else if value == 1 {
Ok(Self::NodeTestType)
} else if value == 2 {
Ok(Self::NodeTestPI)
} else if value == 3 {
Ok(Self::NodeTestAll)
} else if value == 4 {
Ok(Self::NodeTestNs)
} else if value == 5 {
Ok(Self::NodeTestName)
} else {
Err(anyhow::anyhow!(
"Invalid convert from value '{value}' to XmlXPathTestVal"
))
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum XmlXPathTypeVal {
NodeTypeNode = 0,
NodeTypeComment = XmlElementType::XmlCommentNode as isize,
NodeTypeText = XmlElementType::XmlTextNode as isize,
NodeTypePI = XmlElementType::XmlPINode as isize,
}
impl TryFrom<i32> for XmlXPathTypeVal {
type Error = anyhow::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value == 0 {
Ok(Self::NodeTypeNode)
} else if value == Self::NodeTypeComment as i32 {
Ok(Self::NodeTypeComment)
} else if value == Self::NodeTypeText as i32 {
Ok(Self::NodeTypeText)
} else if value == Self::NodeTypePI as i32 {
Ok(Self::NodeTypePI)
} else {
Err(anyhow::anyhow!(
"Invalid convert from value '{value}' to XmlXPathTypeVal"
))
}
}
}
#[doc(alias = "xmlXPathNodeSetMergeFunction")]
pub type XmlXPathNodeSetMergeFunction =
fn(Option<Box<XmlNodeSet>>, Option<&mut XmlNodeSet>) -> Option<Box<XmlNodeSet>>;
pub type XmlXPathTraversalFunction = fn(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr>;
#[doc(alias = "xmlXPathNextChildElement")]
pub(super) fn xml_xpath_next_child_element(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let Some(mut cur) = cur else {
let cur = ctxt.context.node?;
match cur.element_type() {
XmlElementType::XmlElementNode
| XmlElementType::XmlDocumentFragNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlEntityNode => {
let mut cur = cur.children()?;
if matches!(cur.element_type(), XmlElementType::XmlElementNode) {
return Some(cur);
}
while let Some(next) = cur.next().filter(|next| !matches!(next.element_type(), XmlElementType::XmlElementNode)) {
cur = next;
}
return Some(cur);
}
XmlElementType::XmlDocumentNode | XmlElementType::XmlHTMLDocumentNode => {
return XmlDocPtr::try_from(cur).unwrap().get_root_element().map(|root| root.into());
}
_ => {
return None;
}
}
};
match cur.element_type() {
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlEntityNode
| XmlElementType::XmlCDATASectionNode
| XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlXIncludeEnd => {}
_ => {
return None;
}
}
let next = cur.next()?;
if matches!(next.element_type(), XmlElementType::XmlElementNode) {
return Some(next);
}
cur = next;
while let Some(next) = cur
.next()
.filter(|next| !matches!(next.element_type(), XmlElementType::XmlElementNode))
{
cur = next;
}
Some(cur)
}
#[doc(alias = "xmlXPathNextPrecedingInternal")]
pub(super) fn xml_xpath_next_preceding_internal(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let mut cur = cur.or_else(|| {
let mut cur = ctxt.context.node?;
if matches!(cur.element_type(), XmlElementType::XmlAttributeNode) {
cur = cur.parent()?;
} else if let Ok(ns) = XmlNsPtr::try_from(cur) {
cur = ns
.node
.filter(|node| node.element_type() != XmlElementType::XmlNamespaceDecl)?;
}
ctxt.ancestor = cur.parent();
Some(cur)
})?;
if matches!(cur.element_type(), XmlElementType::XmlNamespaceDecl) {
return None;
}
if let Some(prev) = cur
.prev()
.filter(|p| matches!(p.element_type(), XmlElementType::XmlDTDNode))
{
cur = prev;
}
while cur.prev().is_none() {
cur = cur.parent()?;
if Some(cur) == ctxt.context.doc.unwrap().children {
return None;
}
if Some(cur) != ctxt.ancestor {
return Some(cur);
}
ctxt.ancestor = cur.parent();
}
cur = cur.prev()?;
while let Some(last) = cur.last() {
cur = last;
}
Some(cur)
}
#[doc(alias = "xmlXPathNodeSetFilter")]
pub(super) fn xml_xpath_node_set_filter(
ctxt: &mut XmlXPathParserContext,
set: Option<&mut XmlNodeSet>,
filter_op_index: i32,
min_pos: i32,
max_pos: i32,
has_ns_nodes: bool,
) {
let Some(set) = set.filter(|s| !s.is_empty()) else {
return;
};
if (set.node_tab.len() as i32) < min_pos {
set.clear(has_ns_nodes);
return;
}
let oldnode = ctxt.context.node;
let olddoc = ctxt.context.doc;
let oldcs: i32 = ctxt.context.context_size;
let oldpp: i32 = ctxt.context.proximity_position;
ctxt.context.context_size = set.node_tab.len() as i32;
let mut i = 0;
let mut j = 0;
let mut pos = 1;
while i < set.node_tab.len() {
let node = set.node_tab[i];
ctxt.context.node = Some(node);
ctxt.context.proximity_position = i as i32 + 1;
if !matches!(node.element_type(), XmlElementType::XmlNamespaceDecl)
&& node.document().is_some()
{
ctxt.context.doc = node.document();
}
let res = ctxt.evaluate_precompiled_operation_to_boolean(filter_op_index as usize, true);
if ctxt.error != XmlXPathError::XPathExpressionOK as i32 {
break;
}
if res < 0 {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathExprError as i32);
break;
}
if res != 0 && (pos >= min_pos && pos <= max_pos) {
if i != j {
set.node_tab[j] = node;
}
j += 1;
} else {
if let Ok(ns) = XmlNsPtr::try_from(node) {
xml_xpath_node_set_free_ns(ns);
}
}
if res != 0 {
if pos == max_pos {
i += 1;
break;
}
pos += 1;
}
i += 1;
}
if has_ns_nodes {
while i < set.node_tab.len() {
let node = set.node_tab[i];
if let Ok(ns) = XmlNsPtr::try_from(node) {
xml_xpath_node_set_free_ns(ns);
}
i += 1;
}
}
set.node_tab.truncate(j);
set.node_tab.shrink_to_fit();
ctxt.context.node = oldnode;
ctxt.context.doc = olddoc;
ctxt.context.context_size = oldcs;
ctxt.context.proximity_position = oldpp;
}
#[doc(alias = "xmlXPathNodeSetKeepLast")]
pub(super) fn xml_xpath_node_set_keep_last(set: Option<&mut XmlNodeSet>) {
let Some(set) = set.filter(|s| s.len() > 1) else {
return;
};
if set.node_tab.len() <= 1 {
return;
}
let len = set.node_tab.len();
for node in set.node_tab.drain(..len - 1) {
if let Ok(ns) = XmlNsPtr::try_from(node) {
xml_xpath_node_set_free_ns(ns);
}
}
}
#[doc(alias = "xmlXPathLocationSetFilter")]
#[cfg(feature = "libxml_xptr_locs")]
pub(super) fn xml_xpath_location_set_filter(
ctxt: &mut XmlXPathParserContext,
locset: &mut XmlLocationSet,
filter_op_index: i32,
min_pos: i32,
max_pos: i32,
) {
if locset.loc_tab.is_empty() || filter_op_index == -1 {
return;
}
let oldnode = ctxt.context.node;
let olddoc = ctxt.context.doc;
let oldcs: i32 = ctxt.context.context_size;
let oldpp: i32 = ctxt.context.proximity_position;
ctxt.context.context_size = locset.loc_tab.len() as i32;
let mut i = 0;
let mut j = 0;
let mut pos = 1;
while i < locset.loc_tab.len() {
let context_node = locset.loc_tab[i]
.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap();
ctxt.context.node = Some(context_node);
ctxt.context.proximity_position = i as i32 + 1;
if !matches!(
context_node.element_type(),
XmlElementType::XmlNamespaceDecl
) && context_node.document().is_some()
{
ctxt.context.doc = context_node.document();
}
let res: i32 =
ctxt.evaluate_precompiled_operation_to_boolean(filter_op_index as usize, true);
if ctxt.error != XmlXPathError::XPathExpressionOK as i32 {
break;
}
if res < 0 {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathExprError as i32);
break;
}
if res != 0 && (pos >= min_pos && pos <= max_pos) {
if i != j {
locset.loc_tab[j] = locset.loc_tab[i].clone();
}
j += 1;
}
if res != 0 {
if pos == max_pos {
break;
}
pos += 1;
}
i += 1;
}
locset.loc_tab.truncate(j);
locset.loc_tab.shrink_to(XML_NODESET_DEFAULT);
ctxt.context.node = oldnode;
ctxt.context.doc = olddoc;
ctxt.context.context_size = oldcs;
ctxt.context.proximity_position = oldpp;
}
pub(super) const MAX_FRAC: usize = 20;
#[doc(alias = "xmlXPathStringEvalNumber")]
pub fn xml_xpath_string_eval_number(s: Option<&str>) -> f64 {
let Some(s) = s else {
return 0.0;
};
let mut ok = false;
let mut isneg = false;
let mut exponent = 0;
let mut cur = s.trim_matches(|c: char| c.is_xml_blank_char());
if let Some(rem) = cur.strip_prefix('-') {
isneg = true;
cur = rem;
}
if !cur.starts_with(|c: char| c == '.' || c.is_ascii_digit()) {
return XML_XPATH_NAN;
}
let mut ret = 0.0;
while let Some(b) = cur.as_bytes().first().copied().filter(u8::is_ascii_digit) {
ret = ret * 10. + (b - b'0') as f64;
cur = &cur[1..];
ok = true;
}
if let Some(rem) = cur.strip_prefix('.') {
if !ok && !cur.starts_with(|c: char| c.is_ascii_digit()) {
return XML_XPATH_NAN;
}
cur = rem.trim_start_matches('0');
let mut frac = rem.len() - cur.len();
let max = frac + MAX_FRAC;
let mut fraction = 0.0;
while let Some(b) = cur
.as_bytes()
.first()
.filter(|b| b.is_ascii_digit() && frac < max)
{
fraction = fraction * 10. + (b - b'0') as f64;
frac += 1;
cur = &cur[1..];
}
fraction /= 10.0f64.powi(frac as i32);
ret += fraction;
cur = cur.trim_start_matches(|c: char| c.is_ascii_digit());
}
let mut is_exponent_negative = false;
if let Some(rem) = cur.strip_prefix(['e', 'E']) {
if let Some(rem) = rem.strip_prefix('-') {
is_exponent_negative = true;
cur = rem;
} else {
cur = rem.strip_prefix('+').unwrap_or(rem);
}
while let Some(b) = cur
.as_bytes()
.first()
.filter(|b| b.is_ascii_digit() && exponent < 1000000)
{
exponent = exponent * 10 + (b - b'0') as i32;
cur = &cur[1..];
}
cur = cur.trim_start_matches(|c: char| c.is_ascii_digit());
}
if !cur.is_empty() {
return XML_XPATH_NAN;
}
if isneg {
ret = -ret;
}
if is_exponent_negative {
exponent = -exponent;
}
ret * 10.0f64.powi(exponent)
}
#[doc(alias = "xmlXPathNodeValHash")]
fn xml_xpath_node_val_hash(node: Option<XmlGenericNodePtr>) -> u32 {
let mut len: i32 = 2;
let mut ret: u32 = 0;
let Some(mut node) = node else {
return 0;
};
if matches!(node.element_type(), XmlElementType::XmlDocumentNode) {
if let Some(tmp) = XmlDocPtr::try_from(node)
.unwrap()
.get_root_element()
.map(|root| root.into())
{
node = tmp;
} else if let Some(tmp) = node.children() {
node = tmp;
} else {
return 0;
}
}
let mut tmp = match node.element_type() {
XmlElementType::XmlCommentNode
| XmlElementType::XmlPINode
| XmlElementType::XmlCDATASectionNode
| XmlElementType::XmlTextNode => {
let node = XmlNodePtr::try_from(node).unwrap();
let Some(string) = node.content.as_deref() else {
return 0;
};
if string.is_empty() {
return 0;
}
let s0 = string.as_bytes()[0];
let s1 = *string.as_bytes().get(1).unwrap_or(&0);
return s0 as u32 + ((s1 as u32) << 8);
}
XmlElementType::XmlNamespaceDecl => {
let ns = XmlNsPtr::try_from(node).unwrap();
let Some(string) = ns.href.as_deref() else {
return 0;
};
if string.is_empty() {
return 0;
}
let s0 = string.as_bytes()[0];
let s1 = *string.as_bytes().get(1).unwrap_or(&0);
return s0 as u32 + ((s1 as u32) << 8);
}
XmlElementType::XmlAttributeNode => {
let attr = XmlAttrPtr::try_from(node).unwrap();
attr.children.map(XmlGenericNodePtr::from)
}
XmlElementType::XmlElementNode => node.children(),
_ => {
return 0;
}
};
while let Some(now) = tmp {
let string = match now.element_type() {
XmlElementType::XmlCDATASectionNode | XmlElementType::XmlTextNode => {
let node = XmlNodePtr::try_from(now).unwrap();
node.content.clone()
}
_ => None,
};
if let Some(string) = string.filter(|s| !s.is_empty()) {
let bytes = string.as_bytes();
if len == 1 {
return ret + ((bytes[0] as u32) << 8);
}
if bytes.len() == 1 {
len = 1;
ret = bytes[0] as u32;
} else {
return bytes[0] as u32 + ((bytes[1] as u32) << 8);
}
}
if let Some(children) = now.children().filter(|children| {
!matches!(now.element_type(), XmlElementType::XmlDTDNode)
&& !matches!(children.element_type(), XmlElementType::XmlEntityDecl)
}) {
tmp = Some(children);
continue;
}
if tmp == Some(node) {
break;
}
if let Some(next) = now.next() {
tmp = Some(next);
continue;
}
tmp = loop {
let Some(tmp) = now.parent() else {
break None;
};
if tmp == node {
break None;
}
if let Some(next) = tmp.next() {
break Some(next);
}
};
}
ret
}
#[doc(alias = "xmlXPathEqualNodeSets")]
fn xml_xpath_equal_node_sets(arg1: XmlXPathObject, arg2: XmlXPathObject, neq: i32) -> i32 {
let mut ret: i32 = 0;
if !matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
if !matches!(
arg2.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
let (Some(ns1), Some(ns2)) = (arg1.nodesetval.as_deref(), arg2.nodesetval.as_deref()) else {
return 0;
};
if ns1.node_tab.is_empty() || ns2.node_tab.is_empty() {
return 0;
}
if neq == 0 {
for &node1 in &ns1.node_tab {
for &node2 in &ns2.node_tab {
if node1 == node2 {
return 1;
}
}
}
}
let mut values1 = vec![None; ns1.node_tab.len()];
let mut hashs1 = vec![0; ns1.node_tab.len()];
let mut values2 = vec![None; ns2.node_tab.len()];
let mut hashs2 = vec![0; ns2.node_tab.len()];
for (i, &node1) in ns1.node_tab.iter().enumerate() {
hashs1[i] = xml_xpath_node_val_hash(Some(node1));
for (j, &node2) in ns2.node_tab.iter().enumerate() {
if i == 0 {
hashs2[j] = xml_xpath_node_val_hash(Some(node2));
}
if hashs1[i] != hashs2[j] {
if neq != 0 {
ret = 1;
break;
}
} else {
if values1[i].is_none() {
values1[i] = node1.get_content();
}
if values2[j].is_none() {
values2[j] = node2.get_content();
}
ret = (values1[i] == values2[j]) as i32 ^ neq;
if ret != 0 {
break;
}
}
}
if ret != 0 {
break;
}
}
ret
}
#[doc(alias = "xmlXPathEqualNodeSetFloat")]
fn xml_xpath_equal_node_set_float(
ctxt: &mut XmlXPathParserContext,
arg: XmlXPathObject,
f: f64,
neq: i32,
) -> i32 {
let mut ret: i32 = 0;
if !matches!(
arg.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
if let Some(ns) = arg.nodesetval.as_deref() {
for &node in &ns.node_tab {
ctxt.value_push(xml_xpath_new_string(Some(&xml_xpath_cast_node_to_string(
Some(node),
))));
xml_xpath_number_function(ctxt, 1);
if ctxt.error != XmlXPathError::XPathExpressionOK as i32 {
return 0;
};
let val = ctxt.value_pop().unwrap();
let v = val.floatval;
if !xml_xpath_is_nan(v) {
if (neq == 0 && v == f) || (neq != 0 && v != f) {
ret = 1;
break;
}
} else {
if neq != 0 {
ret = 1;
}
}
}
}
ret
}
#[doc(alias = "xmlXPathStringHash")]
fn xml_xpath_string_hash(string: &str) -> u32 {
if string.is_empty() {
return 0;
}
let string = string.as_bytes();
string[0] as u32 + ((*string.get(1).unwrap_or(&0) as u32) << 8)
}
#[doc(alias = "xmlXPathEqualNodeSetString")]
fn xml_xpath_equal_node_set_string(arg: XmlXPathObject, s: &str, neq: i32) -> i32 {
if !matches!(
arg.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
let Some(ns) = arg.nodesetval.as_deref().filter(|n| !n.is_empty()) else {
return 0;
};
let hash: u32 = xml_xpath_string_hash(s);
for &node in &ns.node_tab {
if xml_xpath_node_val_hash(Some(node)) == hash {
let str2 = node.get_content();
if (str2.is_some() && Some(s) == str2.as_deref()) || (str2.is_none() && s.is_empty()) {
if neq != 0 {
continue;
}
return 1;
} else if neq != 0 {
return 1;
}
} else if neq != 0 {
return 1;
}
}
0
}
fn xml_xpath_equal_values_common(
ctxt: &mut XmlXPathParserContext,
mut arg1: XmlXPathObject,
mut arg2: XmlXPathObject,
) -> i32 {
let mut ret: i32 = 0;
match arg1.typ {
XmlXPathObjectType::XPathUndefined => {}
XmlXPathObjectType::XPathBoolean => match arg2.typ {
XmlXPathObjectType::XPathUndefined => {}
XmlXPathObjectType::XPathBoolean => {
ret = (arg1.boolval == arg2.boolval) as i32;
}
XmlXPathObjectType::XPathNumber => {
ret = (arg1.boolval == xml_xpath_cast_number_to_boolean(arg2.floatval)) as i32;
}
XmlXPathObjectType::XPathString => {
let f = arg2.stringval.as_deref().is_some_and(|s| !s.is_empty());
ret = (arg1.boolval == f) as i32;
}
XmlXPathObjectType::XPathUsers => {
todo!()
}
#[cfg(feature = "libxml_xptr_locs")]
XmlXPathObjectType::XPathPoint
| XmlXPathObjectType::XPathRange
| XmlXPathObjectType::XPathLocationset => {
todo!()
}
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree => {}
},
XmlXPathObjectType::XPathNumber => {
match arg2.typ {
XmlXPathObjectType::XPathUndefined => {}
XmlXPathObjectType::XPathBoolean => {
ret = (arg2.boolval == xml_xpath_cast_number_to_boolean(arg1.floatval)) as i32;
}
ty @ XmlXPathObjectType::XPathString
| ty @ XmlXPathObjectType::XPathNumber => 'to_break: {
if matches!(ty, XmlXPathObjectType::XPathString) {
ctxt.value_push(arg2);
xml_xpath_number_function(&mut *ctxt, 1);
arg2 = ctxt.value_pop().unwrap();
if ctxt.error != 0 {
break 'to_break;
}
}
if xml_xpath_is_nan(arg1.floatval) || xml_xpath_is_nan(arg2.floatval) {
ret = 0;
} else if xml_xpath_is_inf(arg1.floatval) == 1 {
if xml_xpath_is_inf(arg2.floatval) == 1 {
ret = 1;
} else {
ret = 0;
}
} else if xml_xpath_is_inf(arg1.floatval) == -1 {
if xml_xpath_is_inf(arg2.floatval) == -1 {
ret = 1;
} else {
ret = 0;
}
} else if xml_xpath_is_inf(arg2.floatval) == 1 {
if xml_xpath_is_inf(arg1.floatval) == 1 {
ret = 1;
} else {
ret = 0;
}
} else if xml_xpath_is_inf(arg2.floatval) == -1 {
if xml_xpath_is_inf(arg1.floatval) == -1 {
ret = 1;
} else {
ret = 0;
}
} else {
ret = (arg1.floatval == arg2.floatval) as i32;
}
}
XmlXPathObjectType::XPathUsers => {
todo!()
}
#[cfg(feature = "libxml_xptr_locs")]
XmlXPathObjectType::XPathPoint
| XmlXPathObjectType::XPathRange
| XmlXPathObjectType::XPathLocationset => {
todo!()
}
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree => {}
}
}
XmlXPathObjectType::XPathString => {
match arg2.typ {
XmlXPathObjectType::XPathUndefined => {}
XmlXPathObjectType::XPathBoolean => {
let f = arg1.stringval.as_deref().is_some_and(|s| !s.is_empty());
ret = (arg2.boolval == f) as i32;
}
XmlXPathObjectType::XPathString => {
ret = (arg1.stringval == arg2.stringval) as i32;
}
XmlXPathObjectType::XPathNumber => {
ctxt.value_push(arg1);
xml_xpath_number_function(&mut *ctxt, 1);
arg1 = ctxt.value_pop().unwrap();
if ctxt.error != 0 {
} else {
if xml_xpath_is_nan(arg1.floatval) || xml_xpath_is_nan(arg2.floatval) {
ret = 0;
} else if xml_xpath_is_inf(arg1.floatval) == 1 {
if xml_xpath_is_inf(arg2.floatval) == 1 {
ret = 1;
} else {
ret = 0;
}
} else if xml_xpath_is_inf(arg1.floatval) == -1 {
if xml_xpath_is_inf(arg2.floatval) == -1 {
ret = 1;
} else {
ret = 0;
}
} else if xml_xpath_is_inf(arg2.floatval) == 1 {
if xml_xpath_is_inf(arg1.floatval) == 1 {
ret = 1;
} else {
ret = 0;
}
} else if xml_xpath_is_inf(arg2.floatval) == -1 {
if xml_xpath_is_inf(arg1.floatval) == -1 {
ret = 1;
} else {
ret = 0;
}
} else {
ret = (arg1.floatval == arg2.floatval) as i32;
}
}
}
XmlXPathObjectType::XPathUsers => {
todo!()
}
#[cfg(feature = "libxml_xptr_locs")]
XmlXPathObjectType::XPathPoint
| XmlXPathObjectType::XPathRange
| XmlXPathObjectType::XPathLocationset => {
todo!()
}
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree => {}
}
}
XmlXPathObjectType::XPathUsers => {
todo!()
}
#[cfg(feature = "libxml_xptr_locs")]
XmlXPathObjectType::XPathPoint
| XmlXPathObjectType::XPathRange
| XmlXPathObjectType::XPathLocationset => {
todo!()
}
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree => {}
}
ret
}
#[doc(alias = "xmlXPathEqualValues")]
pub fn xml_xpath_equal_values(ctxt: &mut XmlXPathParserContext) -> i32 {
let mut ret: i32 = 0;
let Some((mut arg2, mut arg1)) = ctxt.value_pop().zip(ctxt.value_pop()) else {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathInvalidOperand as i32);
return 0;
};
if arg1 == arg2 {
return 1;
}
if matches!(
arg2.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) || matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
if !matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
(arg1, arg2) = (arg2, arg1);
}
match arg2.typ {
XmlXPathObjectType::XPathUndefined => {}
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree => {
ret = xml_xpath_equal_node_sets(arg1, arg2, 0);
}
XmlXPathObjectType::XPathBoolean => {
let f = arg1.nodesetval.as_deref().is_some_and(|n| !n.is_empty());
ret = (f == arg2.boolval) as i32;
}
XmlXPathObjectType::XPathNumber => {
ret = xml_xpath_equal_node_set_float(ctxt, arg1, arg2.floatval, 0);
}
XmlXPathObjectType::XPathString => {
ret = xml_xpath_equal_node_set_string(
arg1,
arg2.stringval.as_deref().expect("Internal Error"),
0,
);
}
XmlXPathObjectType::XPathUsers => {
todo!()
}
#[cfg(feature = "libxml_xptr_locs")]
XmlXPathObjectType::XPathPoint
| XmlXPathObjectType::XPathRange
| XmlXPathObjectType::XPathLocationset => todo!(),
}
return ret;
}
xml_xpath_equal_values_common(ctxt, arg1, arg2)
}
#[doc(alias = "xmlXPathNotEqualValues")]
pub fn xml_xpath_not_equal_values(ctxt: &mut XmlXPathParserContext) -> i32 {
let mut ret: i32 = 0;
let Some((mut arg2, mut arg1)) = ctxt.value_pop().zip(ctxt.value_pop()) else {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathInvalidOperand as i32);
return 0;
};
if arg1 == arg2 {
return 0;
}
if matches!(
arg2.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) || matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
if !matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
(arg1, arg2) = (arg2, arg1);
}
match arg2.typ {
XmlXPathObjectType::XPathUndefined => {}
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree => {
ret = xml_xpath_equal_node_sets(arg1, arg2, 1);
}
XmlXPathObjectType::XPathBoolean => {
let f = arg1.nodesetval.as_deref().is_some_and(|n| !n.is_empty());
ret = (f != arg2.boolval) as i32;
}
XmlXPathObjectType::XPathNumber => {
ret = xml_xpath_equal_node_set_float(ctxt, arg1, arg2.floatval, 1);
}
XmlXPathObjectType::XPathString => {
ret = xml_xpath_equal_node_set_string(
arg1,
arg2.stringval.as_deref().expect("Internal Error"),
1,
);
}
XmlXPathObjectType::XPathUsers => {
todo!()
}
#[cfg(feature = "libxml_xptr_locs")]
XmlXPathObjectType::XPathPoint
| XmlXPathObjectType::XPathRange
| XmlXPathObjectType::XPathLocationset => {
todo!()
} }
return ret;
}
(xml_xpath_equal_values_common(ctxt, arg1, arg2) == 0) as i32
}
#[doc(alias = "xmlXPathCompareNodeSets")]
fn xml_xpath_compare_node_sets(
inf: i32,
strict: i32,
arg1: XmlXPathObject,
arg2: XmlXPathObject,
) -> i32 {
let mut init: i32 = 0;
let mut val1: f64;
let mut ret: i32 = 0;
if !matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
if !matches!(
arg2.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
let Some(ns1_table) = arg1
.nodesetval
.as_deref()
.filter(|set| !set.node_tab.is_empty())
.map(|n| n.node_tab.as_slice())
else {
return 0;
};
let Some(ns2_table) = arg2
.nodesetval
.as_deref()
.filter(|set| !set.node_tab.is_empty())
.map(|n| n.node_tab.as_slice())
else {
return 0;
};
let mut values2 = vec![0.0; ns2_table.len()];
for &node1 in ns1_table {
val1 = xml_xpath_cast_node_to_number(Some(node1));
if xml_xpath_is_nan(val1) {
continue;
}
for (j, &node2) in ns2_table.iter().enumerate() {
if init == 0 {
values2[j] = xml_xpath_cast_node_to_number(Some(node2));
}
if xml_xpath_is_nan(values2[j]) {
continue;
}
if inf != 0 && strict != 0 {
ret = (val1 < values2[j]) as i32;
} else if inf != 0 && strict == 0 {
ret = (val1 <= values2[j]) as i32;
} else if inf == 0 && strict != 0 {
ret = (val1 > values2[j]) as i32;
} else if inf == 0 && strict == 0 {
ret = (val1 >= values2[j]) as i32;
}
if ret != 0 {
break;
}
}
if ret != 0 {
break;
}
init = 1;
}
ret
}
#[doc(alias = "xmlXPathCompareNodeSetFloat")]
fn xml_xpath_compare_node_set_float(
ctxt: &mut XmlXPathParserContext,
inf: i32,
strict: i32,
arg: XmlXPathObject,
f: XmlXPathObject,
) -> i32 {
let mut ret: i32 = 0;
if !matches!(
arg.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
if let Some(ns) = arg.nodesetval.as_deref() {
for &node in &ns.node_tab {
ctxt.value_push(xml_xpath_new_string(Some(&xml_xpath_cast_node_to_string(
Some(node),
))));
xml_xpath_number_function(ctxt, 1);
ctxt.value_push(xml_xpath_object_copy(&f));
ret = xml_xpath_compare_values(ctxt, inf, strict);
if ret != 0 {
break;
}
}
}
ret
}
#[doc(alias = "xmlXPathCompareNodeSetString")]
fn xml_xpath_compare_node_set_string(
ctxt: &mut XmlXPathParserContext,
inf: i32,
strict: i32,
arg: XmlXPathObject,
s: XmlXPathObject,
) -> i32 {
let mut ret: i32 = 0;
if !matches!(
arg.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
if let Some(ns) = arg.nodesetval.as_deref() {
for &node in &ns.node_tab {
ctxt.value_push(xml_xpath_new_string(Some(&xml_xpath_cast_node_to_string(
Some(node),
))));
ctxt.value_push(xml_xpath_object_copy(&s));
ret = xml_xpath_compare_values(ctxt, inf, strict);
if ret != 0 {
break;
}
}
}
ret
}
#[doc(alias = "xmlXPathCompareNodeSetValue")]
fn xml_xpath_compare_node_set_value(
ctxt: &mut XmlXPathParserContext,
inf: i32,
strict: i32,
arg: XmlXPathObject,
val: XmlXPathObject,
) -> i32 {
if !matches!(
arg.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
return 0;
}
match val.typ {
XmlXPathObjectType::XPathNumber => {
xml_xpath_compare_node_set_float(ctxt, inf, strict, arg, val)
}
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree => {
xml_xpath_compare_node_sets(inf, strict, arg, val)
}
XmlXPathObjectType::XPathString => {
xml_xpath_compare_node_set_string(ctxt, inf, strict, arg, val)
}
XmlXPathObjectType::XPathBoolean => {
ctxt.value_push(arg);
xml_xpath_boolean_function(ctxt, 1);
ctxt.value_push(val);
xml_xpath_compare_values(ctxt, inf, strict)
}
_ => {
generic_error!(
"xmlXPathCompareNodeSetValue: Can't compare node set and object of type {:?}\n",
val.typ
);
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
0
}
}
}
#[doc(alias = "xmlXPathCompareValues")]
pub fn xml_xpath_compare_values(ctxt: &mut XmlXPathParserContext, inf: i32, strict: i32) -> i32 {
let mut ret: i32 = 0;
let arg1i: i32;
let arg2i: i32;
let Some((mut arg2, mut arg1)) = ctxt.value_pop().zip(ctxt.value_pop()) else {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathInvalidOperand as i32);
return 0;
};
if matches!(
arg2.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) || matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
if matches!(
arg2.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) && matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
ret = xml_xpath_compare_node_sets(inf, strict, arg1, arg2);
} else if matches!(
arg1.typ,
XmlXPathObjectType::XPathNodeset | XmlXPathObjectType::XPathXSLTTree
) {
ret = xml_xpath_compare_node_set_value(ctxt, inf, strict, arg1, arg2);
} else {
ret = xml_xpath_compare_node_set_value(ctxt, !inf, strict, arg2, arg1);
}
return ret;
}
if !matches!(arg1.typ, XmlXPathObjectType::XPathNumber) {
ctxt.value_push(arg1);
xml_xpath_number_function(&mut *ctxt, 1);
arg1 = ctxt.value_pop().unwrap();
}
if !matches!(arg2.typ, XmlXPathObjectType::XPathNumber) {
ctxt.value_push(arg2);
xml_xpath_number_function(&mut *ctxt, 1);
arg2 = ctxt.value_pop().unwrap();
}
if ctxt.error != 0 {
return ret;
}
if xml_xpath_is_nan(arg1.floatval) || xml_xpath_is_nan(arg2.floatval) {
ret = 0;
} else {
arg1i = xml_xpath_is_inf(arg1.floatval);
arg2i = xml_xpath_is_inf(arg2.floatval);
if inf != 0 && strict != 0 {
if (arg1i == -1 && arg2i != -1) || (arg2i == 1 && arg1i != 1) {
ret = 1;
} else if arg1i == 0 && arg2i == 0 {
ret = (arg1.floatval < arg2.floatval) as i32;
} else {
ret = 0;
}
} else if inf != 0 && strict == 0 {
if arg1i == -1 || arg2i == 1 {
ret = 1;
} else if arg1i == 0 && arg2i == 0 {
ret = (arg1.floatval <= arg2.floatval) as i32;
} else {
ret = 0;
}
} else if inf == 0 && strict != 0 {
if (arg1i == 1 && arg2i != 1) || (arg2i == -1 && arg1i != -1) {
ret = 1;
} else if arg1i == 0 && arg2i == 0 {
ret = (arg1.floatval > arg2.floatval) as i32;
} else {
ret = 0;
}
} else if inf == 0 && strict == 0 {
if arg1i == 1 || arg2i == -1 {
ret = 1;
} else if arg1i == 0 && arg2i == 0 {
ret = (arg1.floatval >= arg2.floatval) as i32;
} else {
ret = 0;
}
}
}
ret
}
#[doc(alias = "xmlXPathValueFlipSign")]
pub fn xml_xpath_value_flip_sign(ctxt: &mut XmlXPathParserContext) {
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;
}
#[doc(alias = "xmlXPathAddValues")]
pub fn xml_xpath_add_values(ctxt: &mut XmlXPathParserContext) {
let Some(mut arg) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
let val: f64 = xml_xpath_cast_to_number(&mut arg);
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;
};
ctxt.value_mut().unwrap().floatval += val;
}
#[doc(alias = "xmlXPathSubValues")]
pub fn xml_xpath_sub_values(ctxt: &mut XmlXPathParserContext) {
let Some(mut arg) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
let val: f64 = xml_xpath_cast_to_number(&mut arg);
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;
};
ctxt.value_mut().unwrap().floatval -= val;
}
#[doc(alias = "xmlXPathMultValues")]
pub fn xml_xpath_mult_values(ctxt: &mut XmlXPathParserContext) {
let Some(mut arg) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
let val: f64 = xml_xpath_cast_to_number(&mut arg);
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;
};
ctxt.value_mut().unwrap().floatval *= val;
}
#[doc(alias = "xmlXPathDivValues")]
pub fn xml_xpath_div_values(ctxt: &mut XmlXPathParserContext) {
let Some(mut arg) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
let val: f64 = xml_xpath_cast_to_number(&mut arg);
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;
};
ctxt.value_mut().unwrap().floatval /= val;
}
#[doc(alias = "xmlXPathModValues")]
pub fn xml_xpath_mod_values(ctxt: &mut XmlXPathParserContext) {
let Some(mut arg) = ctxt.value_pop() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidOperand as i32);
return;
};
let arg2: f64 = xml_xpath_cast_to_number(&mut arg);
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 arg1 = &mut ctxt.value_mut().unwrap().floatval;
if arg2 == 0.0 {
*arg1 = XML_XPATH_NAN;
} else {
*arg1 %= arg2;
}
}
#[doc(alias = "xmlXPathNextSelf")]
pub fn xml_xpath_next_self(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
if cur.is_none() {
return ctxt.context.node;
}
None
}
#[doc(alias = "xmlXPathNextChild")]
pub fn xml_xpath_next_child(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let Some(cur) = cur else {
let node = ctxt.context.node?;
match node.element_type() {
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlCDATASectionNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlEntityNode
| XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlNotationNode
| XmlElementType::XmlDTDNode => {
return node.children();
}
XmlElementType::XmlDocumentNode
| XmlElementType::XmlDocumentTypeNode
| XmlElementType::XmlDocumentFragNode
| XmlElementType::XmlHTMLDocumentNode => {
return node.children();
}
XmlElementType::XmlElementDecl
| XmlElementType::XmlAttributeDecl
| XmlElementType::XmlEntityDecl
| XmlElementType::XmlAttributeNode
| XmlElementType::XmlNamespaceDecl
| XmlElementType::XmlXIncludeStart
| XmlElementType::XmlXIncludeEnd => {
return None;
}
_ => unreachable!(),
}
};
if matches!(
cur.element_type(),
XmlElementType::XmlDocumentNode | XmlElementType::XmlHTMLDocumentNode
) {
return None;
}
cur.next()
}
#[doc(alias = "xmlXPathNextDescendant")]
pub fn xml_xpath_next_descendant(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let Some(mut cur) = cur else {
let node = ctxt.context.node?;
if matches!(
node.element_type(),
XmlElementType::XmlAttributeNode | XmlElementType::XmlNamespaceDecl
) {
return None;
}
if ctxt.context.node == ctxt.context.doc.map(|doc| doc.into()) {
return ctxt.context.doc.unwrap().children;
}
return node.children();
};
if matches!(cur.element_type(), XmlElementType::XmlNamespaceDecl) {
return None;
}
if let Some(children) = cur.children() {
if !matches!(children.element_type(), XmlElementType::XmlEntityDecl) {
cur = children;
if !matches!(cur.element_type(), XmlElementType::XmlDTDNode) {
return Some(cur);
}
}
}
if Some(cur) == ctxt.context.node {
return None;
}
while let Some(next) = cur.next() {
cur = next;
if !matches!(
cur.element_type(),
XmlElementType::XmlEntityDecl | XmlElementType::XmlDTDNode
) {
return Some(cur);
}
}
loop {
cur = cur.parent()?;
if Some(cur) == ctxt.context.node {
break None;
}
if let Some(next) = cur.next() {
cur = next;
break Some(cur);
}
}
}
#[doc(alias = "xmlXPathNextDescendantOrSelf")]
pub fn xml_xpath_next_descendant_or_self(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
if cur.is_none() {
return ctxt.context.node;
}
if matches!(
ctxt.context.node?.element_type(),
XmlElementType::XmlAttributeNode | XmlElementType::XmlNamespaceDecl
) {
return None;
}
xml_xpath_next_descendant(ctxt, cur)
}
#[doc(alias = "xmlXPathNextParent")]
pub fn xml_xpath_next_parent(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
if cur.is_none() {
let node = ctxt.context.node?;
match node.element_type() {
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlCDATASectionNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlEntityNode
| XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlNotationNode
| XmlElementType::XmlDTDNode
| XmlElementType::XmlElementDecl
| XmlElementType::XmlAttributeDecl
| XmlElementType::XmlXIncludeStart
| XmlElementType::XmlXIncludeEnd
| XmlElementType::XmlEntityDecl => {
let Some(parent) = node.parent() else {
return ctxt.context.doc.map(|doc| doc.into());
};
if XmlNodePtr::try_from(parent)
.ok()
.filter(|node| node.element_type() == XmlElementType::XmlElementNode)
.as_deref()
.and_then(|node| node.name())
.filter(|name| name.starts_with(' ') || name == "fake node libxslt")
.is_some()
{
return None;
}
return Some(parent);
}
XmlElementType::XmlAttributeNode => {
let att = XmlAttrPtr::try_from(node).unwrap();
return att.parent.map(XmlGenericNodePtr::from);
}
XmlElementType::XmlDocumentNode
| XmlElementType::XmlDocumentTypeNode
| XmlElementType::XmlDocumentFragNode
| XmlElementType::XmlHTMLDocumentNode => {
return None;
}
XmlElementType::XmlNamespaceDecl => {
let ns = XmlNsPtr::try_from(node).unwrap();
if let Some(next) = ns
.node
.filter(|node| node.element_type() != XmlElementType::XmlNamespaceDecl)
{
return Some(next);
}
return None;
}
_ => unreachable!(),
}
}
None
}
#[doc(alias = "xmlXPathNextAncestorOrSelf")]
pub fn xml_xpath_next_ancestor_or_self(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
if cur.is_none() {
return ctxt.context.node;
}
xml_xpath_next_ancestor(ctxt, cur)
}
#[doc(alias = "xmlXPathNextFollowingSibling")]
pub fn xml_xpath_next_following_sibling(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let node = ctxt.context.node?;
if matches!(
node.element_type(),
XmlElementType::XmlAttributeNode | XmlElementType::XmlNamespaceDecl
) {
return None;
}
if cur == ctxt.context.doc.map(|doc| doc.into()) {
return None;
}
if let Some(cur) = cur {
cur.next()
} else {
node.next()
}
}
#[doc(alias = "xmlXPathNextFollowing")]
pub fn xml_xpath_next_following(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
if let Some(children) = cur
.filter(|cur| {
!matches!(
cur.element_type(),
XmlElementType::XmlAttributeNode | XmlElementType::XmlNamespaceDecl
)
})
.and_then(|cur| cur.children())
{
return Some(children);
}
let mut cur = cur.or_else(|| {
let cur = ctxt.context.node?;
if let Ok(attr) = XmlAttrPtr::try_from(cur) {
attr.parent()
} else if let Ok(ns) = XmlNsPtr::try_from(cur) {
ns.node
.filter(|node| node.element_type() != XmlElementType::XmlNamespaceDecl)
} else {
None
}
})?;
if let Some(next) = cur.next() {
return Some(next);
}
loop {
cur = cur.parent()?;
if Some(cur) == ctxt.context.doc.map(|doc| doc.into()) {
break None;
}
if let Some(next) = cur.next() {
break Some(next);
}
}
}
thread_local! {
static XML_XPATH_XMLNAMESPACE_STRUCT: XmlNs = XmlNs {
next: None,
typ: XmlElementType::XmlNamespaceDecl,
href: Some(XML_XML_NAMESPACE.into()),
prefix: Some("xml".into()),
_private: null_mut(),
context: None,
node: None,
};
}
#[doc(alias = "xmlXPathNextNamespace")]
pub fn xml_xpath_next_namespace(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let node = ctxt.context.node?;
if !matches!(node.element_type(), XmlElementType::XmlElementNode) {
return None;
}
if cur.is_none() {
ctxt.context.tmp_ns_list = node.get_ns_list(ctxt.context.doc);
ctxt.context.tmp_ns_nr = 0;
if let Some(list) = ctxt.context.tmp_ns_list.as_deref() {
ctxt.context.tmp_ns_nr = list.len() as i32;
}
let reference = XML_XPATH_XMLNAMESPACE_STRUCT.with(|s| s as *const XmlNs);
return XmlGenericNodePtr::from_raw(reference as *mut XmlNs);
}
if ctxt.context.tmp_ns_nr > 0 {
ctxt.context.tmp_ns_nr -= 1;
Some(ctxt.context.tmp_ns_list.as_deref().unwrap()[ctxt.context.tmp_ns_nr as usize].into())
} else {
ctxt.context.tmp_ns_list = None;
None
}
}
#[doc(alias = "xmlXPathNextAttribute")]
pub fn xml_xpath_next_attribute(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let node = XmlNodePtr::try_from(ctxt.context.node?)
.ok()
.filter(|node| node.element_type() == XmlElementType::XmlElementNode)?;
if let Some(cur) = cur {
cur.next()
} else {
if ctxt.context.node == ctxt.context.doc.map(|doc| doc.into()) {
return None;
}
node.properties.map(|prop| prop.into())
}
}
#[doc(alias = "xmlXPathIsAncestor")]
fn xml_xpath_is_ancestor(
ancestor: Option<XmlGenericNodePtr>,
node: Option<XmlGenericNodePtr>,
) -> i32 {
let Some((ancestor, mut node)) = ancestor.zip(node) else {
return 0;
};
if matches!(node.element_type(), XmlElementType::XmlNamespaceDecl) {
return 0;
}
if matches!(ancestor.element_type(), XmlElementType::XmlNamespaceDecl) {
return 0;
}
if ancestor.document() != node.document() {
return 0;
}
if Some(ancestor) == node.document().map(|doc| doc.into()) {
return 1;
}
if Some(node) == ancestor.document().map(|doc| doc.into()) {
return 0;
}
while let Some(parent) = node.parent() {
if parent == ancestor {
return 1;
}
node = parent;
}
0
}
#[doc(alias = "xmlXPathNextPreceding")]
pub fn xml_xpath_next_preceding(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let mut cur = cur.or_else(|| {
let cur = ctxt.context.node?;
if matches!(cur.element_type(), XmlElementType::XmlAttributeNode) {
cur.parent()
} else if let Ok(ns) = XmlNsPtr::try_from(cur) {
ns.node
.filter(|node| node.element_type() != XmlElementType::XmlNamespaceDecl)
} else {
None
}
})?;
if matches!(cur.element_type(), XmlElementType::XmlNamespaceDecl) {
return None;
}
if let Some(prev) = cur
.prev()
.filter(|p| matches!(p.element_type(), XmlElementType::XmlDTDNode))
{
cur = prev;
}
loop {
if let Some(prev) = cur.prev() {
cur = prev;
while let Some(last) = cur.last() {
cur = last;
}
break Some(cur);
}
cur = cur.parent()?;
if Some(cur) == ctxt.context.doc.unwrap().children {
break None;
}
if xml_xpath_is_ancestor(Some(cur), ctxt.context.node) == 0 {
break Some(cur);
}
}
}
#[doc(alias = "xmlXPathNextAncestor")]
pub fn xml_xpath_next_ancestor(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let Some(cur) = cur else {
let node = ctxt.context.node?;
match node.element_type() {
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlCDATASectionNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlEntityNode
| XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlDTDNode
| XmlElementType::XmlElementDecl
| XmlElementType::XmlAttributeDecl
| XmlElementType::XmlEntityDecl
| XmlElementType::XmlNotationNode
| XmlElementType::XmlXIncludeStart
| XmlElementType::XmlXIncludeEnd => {
let Some(parent) = node.parent() else {
return ctxt.context.doc.map(|doc| doc.into());
};
if XmlNodePtr::try_from(parent)
.ok()
.filter(|node| node.element_type() == XmlElementType::XmlElementNode)
.as_deref()
.and_then(|node| node.name())
.filter(|name| name.starts_with(' ') || name == "fake node libxslt")
.is_some()
{
return None;
}
return Some(parent);
}
XmlElementType::XmlAttributeNode => {
let tmp = XmlAttrPtr::try_from(node).unwrap();
return tmp.parent();
}
XmlElementType::XmlDocumentNode
| XmlElementType::XmlDocumentTypeNode
| XmlElementType::XmlDocumentFragNode
| XmlElementType::XmlHTMLDocumentNode => {
return None;
}
XmlElementType::XmlNamespaceDecl => {
let ns = XmlNsPtr::try_from(node).unwrap();
if let Some(next) = ns
.node
.filter(|node| !matches!(node.element_type(), XmlElementType::XmlNamespaceDecl))
{
return Some(next);
}
return None;
}
_ => unreachable!(),
}
};
if Some(cur) == ctxt.context.doc.unwrap().children {
return ctxt.context.doc.map(|doc| doc.into());
}
if Some(cur) == ctxt.context.doc.map(|doc| doc.into()) {
return None;
}
match cur.element_type() {
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlCDATASectionNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlEntityNode
| XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlNotationNode
| XmlElementType::XmlDTDNode
| XmlElementType::XmlElementDecl
| XmlElementType::XmlAttributeDecl
| XmlElementType::XmlEntityDecl
| XmlElementType::XmlXIncludeStart
| XmlElementType::XmlXIncludeEnd => {
let parent = cur.parent()?;
if XmlNodePtr::try_from(parent)
.ok()
.filter(|node| node.element_type() == XmlElementType::XmlElementNode)
.as_deref()
.and_then(|node| node.name())
.filter(|name| name.starts_with(' ') || name == "fake node libxslt")
.is_some()
{
return None;
}
Some(parent)
}
XmlElementType::XmlAttributeNode => {
let att = XmlAttrPtr::try_from(cur).unwrap();
att.parent.map(XmlGenericNodePtr::from)
}
XmlElementType::XmlNamespaceDecl => {
let ns = XmlNsPtr::try_from(cur).unwrap();
if let Some(next) = ns
.node
.filter(|node| !matches!(node.element_type(), XmlElementType::XmlNamespaceDecl))
{
return Some(next);
}
None
}
XmlElementType::XmlDocumentNode
| XmlElementType::XmlDocumentTypeNode
| XmlElementType::XmlDocumentFragNode
| XmlElementType::XmlHTMLDocumentNode => None,
_ => unreachable!(),
}
}
#[doc(alias = "xmlXPathNextPrecedingSibling")]
pub fn xml_xpath_next_preceding_sibling(
ctxt: &mut XmlXPathParserContext,
cur: Option<XmlGenericNodePtr>,
) -> Option<XmlGenericNodePtr> {
let context_node = ctxt.context.node?;
if matches!(
context_node.element_type(),
XmlElementType::XmlAttributeNode | XmlElementType::XmlNamespaceDecl
) {
return None;
}
if cur == ctxt.context.doc.map(|doc| doc.into()) {
return None;
}
let Some(mut cur) = cur else {
return context_node.prev();
};
if let Some(prev) = cur.prev().and_then(|p| XmlDtdPtr::try_from(p).ok()) {
cur = prev.into();
}
cur.prev()
}
#[doc(alias = "xmlXPathGetElementsByIds")]
pub(super) fn xml_xpath_get_elements_by_ids(
doc: XmlDocPtr,
ids: Option<&str>,
) -> Option<Box<XmlNodeSet>> {
let ids = ids?;
let mut ret = xml_xpath_node_set_create(None)?;
for id in ids
.split(|c: char| c.is_xml_blank_char())
.filter(|s| !s.is_empty())
{
if let Some(attr) = xml_get_id(doc, id) {
let elem = if let Ok(attr) = attr {
attr.parent.map(XmlGenericNodePtr::from)
} else {
None
};
if let Some(elem) = elem {
ret.as_mut().add(elem);
}
}
}
Some(ret)
}
#[doc(alias = "xmlXPathNodeSetFreeNs")]
#[cfg(feature = "xpath")]
pub(crate) fn xml_xpath_node_set_free_ns(ns: XmlNsPtr) {
if !matches!(ns.typ, XmlElementType::XmlNamespaceDecl) {
return;
}
if ns
.node
.is_some_and(|node| !matches!(node.element_type(), XmlElementType::XmlNamespaceDecl))
{
unsafe {
ns.free();
}
}
}