use std::borrow::Cow;
use std::{mem::replace, ptr::null_mut, rc::Rc};
use crate::xpath::XmlXPathContext;
use crate::xpath::functions::xml_xpath_id_function;
#[cfg(feature = "libxml_xptr_locs")]
use crate::xpath::{XmlNodeSet, XmlXPathParserContext, functions::check_arity};
#[cfg(feature = "libxml_xptr_locs")]
use crate::xpath::{XmlXPathObject, internals::xml_xpath_err, xml_xpath_cmp_nodes};
use crate::{
error::{__xml_raise_error, XmlErrorDomain, XmlErrorLevel, XmlParserErrors},
parser::xml_init_parser,
tree::{NodeCommon, XmlDocPtr, XmlElementType, XmlGenericNodePtr},
xpath::{
XmlXPathError, XmlXPathObjectType, internals::xml_xpath_root, xml_xpath_new_node_set,
xml_xpath_new_string,
},
};
#[cfg(feature = "libxml_xptr_locs")]
#[repr(C)]
#[derive(Clone, PartialEq)]
pub struct XmlLocationSet {
pub(crate) loc_tab: Vec<Rc<XmlXPathObject>>,
}
#[cfg(feature = "libxml_xptr_locs")]
impl XmlLocationSet {
#[doc(alias = "xmlXPtrLocationSetCreate")]
pub(crate) fn new(val: Option<Rc<XmlXPathObject>>) -> XmlLocationSet {
let mut ret = XmlLocationSet {
loc_tab: Vec::with_capacity(XML_RANGESET_DEFAULT),
};
if let Some(val) = val {
ret.loc_tab.push(val);
}
ret
}
#[doc(alias = "xmlXPtrLocationSetAdd")]
pub(crate) fn push(&mut self, val: Rc<XmlXPathObject>) {
for loc in &self.loc_tab {
if xml_xptr_ranges_equal(loc, &val) != 0 {
return;
}
}
self.loc_tab.push(val);
}
}
#[cfg(feature = "libxml_xptr_locs")]
const XML_RANGESET_DEFAULT: usize = 10;
#[cfg(feature = "libxml_xptr_locs")]
macro_rules! STRANGE {
() => {
$crate::generic_error!("Internal error at {}:{}\n", file!(), line!());
};
}
#[doc(alias = "xmlXPtrNewLocationSetNodes")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_new_location_set_nodes(
start: XmlGenericNodePtr,
end: Option<XmlGenericNodePtr>,
) -> XmlXPathObject {
use crate::xpath::XmlXPathObjectUserData;
let mut ret = XmlXPathObject::default();
ret.typ = XmlXPathObjectType::XPathLocationset;
let loc = if let Some(end) = end {
XmlLocationSet::new(xml_xptr_new_range_nodes(start, end).map(Rc::new))
} else {
XmlLocationSet::new(xml_xptr_new_collapsed_range(start).map(Rc::new))
};
ret.user = Some(XmlXPathObjectUserData::LocationSet(loc));
ret
}
#[doc(alias = "xmlXPtrNewRangeInternal")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_new_range_internal(
start: Option<XmlGenericNodePtr>,
startindex: i32,
end: Option<XmlGenericNodePtr>,
endindex: i32,
) -> Option<XmlXPathObject> {
use crate::xpath::XmlXPathObjectUserData;
if start.is_some_and(|start| matches!(start.element_type(), XmlElementType::XmlNamespaceDecl)) {
return None;
}
if end.is_some_and(|end| matches!(end.element_type(), XmlElementType::XmlNamespaceDecl)) {
return None;
}
let mut ret = XmlXPathObject::default();
ret.typ = XmlXPathObjectType::XPathRange;
ret.user = start.map(XmlXPathObjectUserData::Node);
ret.index = startindex;
ret.user2 = end.map(XmlXPathObjectUserData::Node);
ret.index2 = endindex;
Some(ret)
}
#[doc(alias = "xmlXPtrCmpPoints")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_cmp_points(
node1: XmlGenericNodePtr,
index1: i32,
node2: XmlGenericNodePtr,
index2: i32,
) -> i32 {
if node1 == node2 {
if index1 < index2 {
return 1;
}
if index1 > index2 {
return -1;
}
return 0;
}
xml_xpath_cmp_nodes(node1, node2)
}
#[doc(alias = "xmlXPtrRangeCheckOrder")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_range_check_order(range: Option<&mut XmlXPathObject>) {
let Some(range) = range else {
return;
};
if !matches!(range.typ, XmlXPathObjectType::XPathRange) {
return;
}
if range.user2.is_none() {
return;
}
let tmp = xml_xptr_cmp_points(
range
.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
range.index,
range
.user2
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
range.index2,
);
if tmp == -1 {
std::mem::swap(&mut range.user, &mut range.user2);
std::mem::swap(&mut range.index, &mut range.index2);
}
}
#[doc(alias = "xmlXPtrNewRange")]
#[cfg(feature = "libxml_xptr_locs")]
pub(crate) fn xml_xptr_new_range(
start: XmlGenericNodePtr,
startindex: i32,
end: XmlGenericNodePtr,
endindex: i32,
) -> Option<XmlXPathObject> {
if startindex < 0 {
return None;
}
if endindex < 0 {
return None;
}
let mut ret = xml_xptr_new_range_internal(Some(start), startindex, Some(end), endindex);
xml_xptr_range_check_order(ret.as_mut());
ret
}
#[doc(alias = "xmlXPtrNewRangePoints")]
#[cfg(feature = "libxml_xptr_locs")]
pub fn xml_xptr_new_range_points(
start: &XmlXPathObject,
end: &XmlXPathObject,
) -> Option<XmlXPathObject> {
if !matches!(start.typ, XmlXPathObjectType::XPathPoint) {
return None;
}
if !matches!(end.typ, XmlXPathObjectType::XPathPoint) {
return None;
}
let mut ret = xml_xptr_new_range_internal(
start.user.as_ref().and_then(|user| user.as_node()).copied(),
start.index,
end.user.as_ref().and_then(|user| user.as_node()).copied(),
end.index,
);
xml_xptr_range_check_order(ret.as_mut());
ret
}
#[doc(alias = "xmlXPtrNewRangeNodes")]
#[cfg(feature = "libxml_xptr_locs")]
pub(crate) fn xml_xptr_new_range_nodes(
start: XmlGenericNodePtr,
end: XmlGenericNodePtr,
) -> Option<XmlXPathObject> {
let mut ret = xml_xptr_new_range_internal(Some(start), -1, Some(end), -1);
xml_xptr_range_check_order(ret.as_mut());
ret
}
#[doc(alias = "xmlXPtrNewLocationSetNodeSet")]
#[cfg(feature = "libxml_xptr_locs")]
pub(crate) fn xml_xptr_new_location_set_node_set(set: Option<&XmlNodeSet>) -> XmlXPathObject {
use crate::xpath::XmlXPathObjectUserData;
let mut ret = XmlXPathObject::default();
ret.typ = XmlXPathObjectType::XPathLocationset;
if let Some(set) = set {
let mut newset = XmlLocationSet::new(None);
for &node in &set.node_tab {
newset.push(xml_xptr_new_collapsed_range(node).map(Rc::new).unwrap());
}
ret.user = Some(XmlXPathObjectUserData::LocationSet(newset));
}
ret
}
#[doc(alias = "xmlXPtrNewRangeNodeObject")]
#[cfg(feature = "libxml_xptr_locs")]
pub(crate) fn xml_xptr_new_range_node_object(
start: XmlGenericNodePtr,
end: &XmlXPathObject,
) -> Option<XmlXPathObject> {
let end_index: i32;
let end_node = match end.typ {
XmlXPathObjectType::XPathPoint => {
end_index = end.index;
end.user.as_ref().and_then(|user| user.as_node()).copied()
}
XmlXPathObjectType::XPathRange => {
end_index = end.index2;
end.user2.as_ref().and_then(|user| user.as_node()).copied()
}
XmlXPathObjectType::XPathNodeset => {
let nodeset = end.nodesetval.as_deref().filter(|s| !s.is_empty())?;
end_index = -1;
nodeset.node_tab.last().copied()
}
_ => {
return None;
}
};
let mut ret = xml_xptr_new_range_internal(Some(start), -1, end_node, end_index);
xml_xptr_range_check_order(ret.as_mut());
ret
}
#[doc(alias = "xmlXPtrNewCollapsedRange")]
#[cfg(feature = "libxml_xptr_locs")]
pub(crate) fn xml_xptr_new_collapsed_range(start: XmlGenericNodePtr) -> Option<XmlXPathObject> {
xml_xptr_new_range_internal(Some(start), -1, None, -1)
}
#[doc(alias = "xmlXPtrRangesEqual")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_ranges_equal(range1: &XmlXPathObject, range2: &XmlXPathObject) -> i32 {
if std::ptr::eq(range1, range2) {
return 1;
}
if range1.typ != range2.typ {
return 0;
}
if range1.typ != XmlXPathObjectType::XPathRange {
return 0;
}
if range1.user != range2.user {
return 0;
}
if range1.index != range2.index {
return 0;
}
if range1.user2 != range2.user2 {
return 0;
}
if range1.index2 != range2.index2 {
return 0;
}
1
}
#[doc(alias = "xmlXPtrWrapLocationSet")]
#[cfg(feature = "libxml_xptr_locs")]
pub(crate) fn xml_xptr_wrap_location_set(val: XmlLocationSet) -> XmlXPathObject {
use crate::xpath::XmlXPathObjectUserData;
let mut ret = XmlXPathObject::default();
ret.typ = XmlXPathObjectType::XPathLocationset;
ret.user = Some(XmlXPathObjectUserData::LocationSet(val));
ret
}
#[doc(alias = "xmlXPtrGetArity")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_get_arity(cur: XmlGenericNodePtr) -> i32 {
let mut i: i32;
if cur.element_type() == XmlElementType::XmlNamespaceDecl {
return -1;
}
let mut cur = cur.children();
i = 0;
while let Some(now) = cur {
if matches!(
now.element_type(),
XmlElementType::XmlElementNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlHTMLDocumentNode
) {
i += 1;
}
cur = now.next();
}
i
}
#[doc(alias = "xmlXPtrGetIndex")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_get_index(cur: XmlGenericNodePtr) -> i32 {
let mut i: i32;
if cur.element_type() == XmlElementType::XmlNamespaceDecl {
return -1;
}
i = 1;
let mut cur = Some(cur);
while let Some(now) = cur {
if matches!(
now.element_type(),
XmlElementType::XmlElementNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlHTMLDocumentNode
) {
i += 1;
}
cur = now.prev();
}
i
}
#[doc(alias = "xmlXPtrCoveringRange")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_covering_range(
ctxt: &mut XmlXPathParserContext,
loc: Rc<XmlXPathObject>,
) -> Option<XmlXPathObject> {
let doc = ctxt.context.doc?;
match loc.typ {
XmlXPathObjectType::XPathPoint => {
return xml_xptr_new_range(
loc.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
loc.index,
loc.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
loc.index,
);
}
XmlXPathObjectType::XPathRange => {
if loc.user2.is_some() {
return xml_xptr_new_range(
loc.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
loc.index,
loc.user2
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
loc.index2,
);
} else {
let mut node = loc
.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap();
if node == XmlGenericNodePtr::from(doc) {
return xml_xptr_new_range(node, 0, node, xml_xptr_get_arity(node));
} else {
match node.element_type() {
XmlElementType::XmlAttributeNode => {
return xml_xptr_new_range(node, 0, node, xml_xptr_get_arity(node));
}
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlCDATASectionNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlNotationNode
| XmlElementType::XmlHTMLDocumentNode => {
let indx: i32 = xml_xptr_get_index(node);
node = node.parent().unwrap();
return xml_xptr_new_range(node, indx - 1, node, indx + 1);
}
_ => return None,
}
}
}
}
_ => {
}
}
None
}
#[doc(alias = "xmlXPtrRangeFunction")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_range_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::XPathLocationset | XmlXPathObjectType::XPathNodeset
)
}) {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let mut set = ctxt.value_pop().unwrap();
if set.typ == XmlXPathObjectType::XPathNodeset {
set = xml_xptr_new_location_set_node_set(set.nodesetval.as_deref());
}
let oldset = set.user.as_ref().and_then(|user| user.as_location_set());
let mut newset = XmlLocationSet::new(None);
if let Some(oldset) = oldset {
for loc in &oldset.loc_tab {
newset.push(
xml_xptr_covering_range(ctxt, loc.clone())
.map(Rc::new)
.unwrap(),
);
}
}
ctxt.value_push(xml_xptr_wrap_location_set(newset));
}
#[doc(alias = "xmlXPtrInsideRange")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_inside_range(
ctxt: &mut XmlXPathParserContext,
loc: Rc<XmlXPathObject>,
) -> Option<XmlXPathObject> {
use crate::tree::XmlNodePtr;
ctxt.context.doc?;
match loc.typ {
XmlXPathObjectType::XPathPoint => {
let node = loc
.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap();
match node.element_type() {
XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlCDATASectionNode => {
let node = XmlNodePtr::try_from(node).unwrap();
if let Some(content) = node.content.as_deref() {
return xml_xptr_new_range(
node.into(),
0,
node.into(),
content.len() as i32,
);
} else {
return xml_xptr_new_range(node.into(), 0, node.into(), 0);
}
}
XmlElementType::XmlAttributeNode
| XmlElementType::XmlElementNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlNotationNode
| XmlElementType::XmlHTMLDocumentNode => {
return xml_xptr_new_range(node, 0, node, xml_xptr_get_arity(node));
}
_ => {}
}
return None;
}
XmlXPathObjectType::XPathRange => {
let node = loc
.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap();
if loc.user2.is_some() {
return xml_xptr_new_range(
node,
loc.index,
loc.user2
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
loc.index2,
);
} else {
match node.element_type() {
XmlElementType::XmlPINode
| XmlElementType::XmlCommentNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlCDATASectionNode => {
let node = XmlNodePtr::try_from(node).unwrap();
if let Some(content) = node.content.as_deref() {
return xml_xptr_new_range(
node.into(),
0,
node.into(),
content.len() as i32,
);
} else {
return xml_xptr_new_range(node.into(), 0, node.into(), 0);
}
}
XmlElementType::XmlAttributeNode
| XmlElementType::XmlElementNode
| XmlElementType::XmlEntityRefNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlNotationNode
| XmlElementType::XmlHTMLDocumentNode => {
return xml_xptr_new_range(node, 0, node, xml_xptr_get_arity(node));
}
_ => {}
}
return None;
}
}
_ => {
}
}
None
}
#[doc(alias = "xmlXPtrRangeInsideFunction")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_range_inside_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::XPathLocationset | XmlXPathObjectType::XPathNodeset
)
}) {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let mut set = ctxt.value_pop().unwrap();
if set.typ == XmlXPathObjectType::XPathNodeset {
set = xml_xptr_new_location_set_node_set(set.nodesetval.as_deref());
}
let mut newset = XmlLocationSet::new(None);
let oldset = set.user.as_ref().and_then(|user| user.as_location_set());
if let Some(oldset) = oldset {
for loc in &oldset.loc_tab {
newset.push(
xml_xptr_inside_range(ctxt, loc.clone())
.map(Rc::new)
.unwrap(),
);
}
}
ctxt.value_push(xml_xptr_wrap_location_set(newset));
}
#[doc(alias = "xmlXPtrGetStartPoint")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_get_start_point(
obj: &XmlXPathObject,
node: &mut Option<XmlGenericNodePtr>,
indx: &mut usize,
) -> i32 {
match obj.typ {
XmlXPathObjectType::XPathPoint => {
*node = obj.user.as_ref().and_then(|user| user.as_node()).copied();
if obj.index <= 0 {
*indx = 0;
} else {
*indx = obj.index as usize;
}
return 0;
}
XmlXPathObjectType::XPathRange => {
*node = obj.user.as_ref().and_then(|user| user.as_node()).copied();
if obj.index <= 0 {
*indx = 0;
} else {
*indx = obj.index as usize;
}
return 0;
}
_ => {}
}
-1
}
#[doc(alias = "xmlXPtrGetEndPoint")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_get_end_point(
obj: &XmlXPathObject,
node: &mut Option<XmlGenericNodePtr>,
indx: &mut usize,
) -> i32 {
match obj.typ {
XmlXPathObjectType::XPathPoint => {
*node = obj.user.as_ref().and_then(|user| user.as_node()).copied();
if obj.index <= 0 {
*indx = 0;
} else {
*indx = obj.index as usize;
}
return 0;
}
XmlXPathObjectType::XPathRange => {
*node = obj.user.as_ref().and_then(|user| user.as_node()).copied();
if obj.index <= 0 {
*indx = 0;
} else {
*indx = obj.index as usize;
}
return 0;
}
_ => {}
}
-1
}
#[doc(alias = "xmlXPtrGetNthChild")]
fn xml_xptr_get_nth_child(cur: XmlGenericNodePtr, no: usize) -> Option<XmlGenericNodePtr> {
if cur.element_type() == XmlElementType::XmlNamespaceDecl {
return Some(cur);
}
let mut cur = cur.children();
let mut i = 0;
while i <= no {
let now = cur?;
if matches!(
now.element_type(),
XmlElementType::XmlElementNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlHTMLDocumentNode
) {
i += 1;
if i == no {
break;
}
}
cur = now.next();
}
cur
}
#[doc(alias = "xmlXPtrAdvanceNode")]
#[cfg(feature = "libxml_xptr_locs")]
pub(crate) fn xml_xptr_advance_node(
mut cur: XmlGenericNodePtr,
level: &mut i32,
) -> Option<XmlGenericNodePtr> {
'next: loop {
if cur.element_type() == XmlElementType::XmlNamespaceDecl {
return None;
}
if let Some(children) = cur.children() {
cur = children;
*level += 1;
if !matches!(
cur.element_type(),
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlHTMLDocumentNode
| XmlElementType::XmlCDATASectionNode
) {
if cur.element_type() == XmlElementType::XmlEntityRefNode {
} else {
continue 'next;
}
} else {
return Some(cur);
}
}
'skip: loop {
if let Some(next) = cur.next() {
cur = next;
} else {
loop {
*level -= 1;
cur = cur.parent()?;
if let Some(next) = cur.next() {
cur = next;
break;
}
}
}
if !matches!(
cur.element_type(),
XmlElementType::XmlElementNode
| XmlElementType::XmlTextNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlHTMLDocumentNode
| XmlElementType::XmlCDATASectionNode
) {
if cur.element_type() == XmlElementType::XmlEntityRefNode {
continue 'skip;
}
continue 'next;
}
break 'next Some(cur);
}
}
}
#[doc(alias = "xmlXPtrAdvanceChar")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_advance_char(
node: &mut Option<XmlGenericNodePtr>,
indx: &mut usize,
mut bytes: i32,
) -> i32 {
use crate::tree::XmlNodePtr;
let mut cur = *node;
if cur.is_none_or(|cur| cur.element_type() == XmlElementType::XmlNamespaceDecl) {
return -1;
}
let mut pos = *indx as i32;
while bytes >= 0 {
while let Some(now) = cur.filter(|cur| {
matches!(
cur.element_type(),
XmlElementType::XmlElementNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlHTMLDocumentNode
)
}) {
if pos > 0 {
cur = xml_xptr_get_nth_child(now, pos as usize);
pos = 0;
} else {
cur = xml_xptr_advance_node(now, &mut 0);
pos = 0;
}
}
let Some(now) = cur else {
*node = None;
*indx = 0;
return -1;
};
if pos == 0 {
pos = 1;
}
if bytes == 0 {
*node = cur;
*indx = pos as usize;
return 0;
}
let mut len = 0;
if now.element_type() != XmlElementType::XmlElementNode {
let cur = XmlNodePtr::try_from(now).unwrap();
if let Some(content) = cur.content.as_deref() {
len = content.len() as i32;
}
}
if pos > len {
STRANGE!();
pos = len;
}
if pos + bytes >= len {
bytes -= len - pos;
cur = xml_xptr_advance_node(now, &mut 0);
pos = 0;
} else if pos + bytes < len {
pos += bytes;
*node = cur;
*indx = pos as usize;
return 0;
}
}
-1
}
#[doc(alias = "xmlXPtrGetLastChar")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_get_last_char(node: &mut Option<XmlGenericNodePtr>, indx: &mut usize) -> i32 {
use crate::tree::XmlNodePtr;
let mut len = 0;
if node.map_or(true, |node| {
node.element_type() == XmlElementType::XmlNamespaceDecl
}) {
return -1;
}
let mut cur = *node;
let pos = *indx;
if matches!(
cur.unwrap().element_type(),
XmlElementType::XmlElementNode
| XmlElementType::XmlDocumentNode
| XmlElementType::XmlHTMLDocumentNode
) && pos > 0
{
cur = xml_xptr_get_nth_child(cur.unwrap(), pos);
}
while let Some(now) = cur {
if let Some(last) = now.last() {
cur = Some(last);
} else if let Some(content) = XmlNodePtr::try_from(now)
.ok()
.as_deref()
.filter(|now| now.element_type() != XmlElementType::XmlElementNode)
.and_then(|now| now.content.as_deref())
{
len = content.len();
break;
} else {
return -1;
}
}
if cur.is_none() {
return -1;
}
*node = cur;
*indx = len;
0
}
#[doc(alias = "xmlXPtrMatchString")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_match_string(
mut string: &str,
start: XmlGenericNodePtr,
startindex: usize,
end: &mut Option<XmlGenericNodePtr>,
endindex: &mut usize,
) -> i32 {
use crate::tree::XmlNodePtr;
if start.element_type() == XmlElementType::XmlNamespaceDecl {
return -1;
}
if end.map_or(true, |end| {
end.element_type() == XmlElementType::XmlNamespaceDecl
}) {
return -1;
}
let mut cur = start;
let mut pos = startindex - 1;
let mut stringlen = string.len();
while stringlen > 0 {
if Some(cur) == *end && pos + stringlen > *endindex {
return 0;
}
if cur.element_type() != XmlElementType::XmlElementNode {
let cur = XmlNodePtr::try_from(cur).unwrap();
if let Some(content) = cur.content.as_deref() {
let len = content.len();
if len >= pos + stringlen {
let is_match = &content.as_bytes()[pos..pos + stringlen] == string.as_bytes();
if is_match {
*end = Some(cur.into());
*endindex = pos + stringlen;
return 1;
} else {
return 0;
}
} else {
let sub = len - pos;
let is_match = content.as_bytes()[pos..pos + sub] == string.as_bytes()[..sub];
if is_match {
string = &string[sub..];
stringlen -= sub;
} else {
return 0;
}
}
}
}
let Some(next) = xml_xptr_advance_node(cur, &mut 0) else {
return 0;
};
cur = next;
pos = 0;
}
1
}
#[doc(alias = "xmlXPtrSearchString")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_search_string(
string: &str,
start: &mut Option<XmlGenericNodePtr>,
startindex: &mut usize,
end: &mut Option<XmlGenericNodePtr>,
endindex: &mut usize,
) -> i32 {
use crate::tree::XmlNodePtr;
if start.map_or(true, |start| {
start.element_type() == XmlElementType::XmlNamespaceDecl
}) {
return -1;
}
let mut cur = *start;
let mut pos = *startindex - 1;
let first = *string.as_bytes().first().unwrap_or(&0);
while let Some(cur_node) = cur {
if cur_node.element_type() != XmlElementType::XmlElementNode {
let cur_node = XmlNodePtr::try_from(cur_node).unwrap();
if let Some(content) = cur_node.content.as_deref() {
let len = content.len();
while pos <= len {
if first != 0 {
if let Some(p) = content[pos..].find(first as char) {
pos += p;
if xml_xptr_match_string(
string,
cur_node.into(),
pos + 1,
end,
endindex,
) != 0
{
*start = cur;
*startindex = pos + 1;
return 1;
}
pos += 1;
} else {
pos = len + 1;
}
} else {
*start = cur;
*startindex = pos + 1;
*end = cur;
*endindex = pos + 1;
return 1;
}
}
}
}
if cur == *end && pos >= *endindex {
return 0;
}
cur = xml_xptr_advance_node(cur_node, &mut 0);
pos = 1;
}
0
}
#[doc(alias = "xmlXPtrStringRangeFunction")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_string_range_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
let mut startindex = 0usize;
let mut endindex = 0usize;
let mut fendindex: usize;
let mut start = None;
let mut end = None;
let mut fend;
let mut newset = None;
let mut position = None;
let mut number = None;
let mut found: i32;
let mut pos = 0;
let mut num: i32 = 0;
if !(2..=4).contains(&nargs) {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathInvalidArity as i32);
return;
}
'goto_error: {
if nargs >= 4 {
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
break 'goto_error;
}
number = ctxt.value_pop();
if let Some(number) = number.as_ref() {
num = number.floatval as i32;
}
}
if nargs >= 3 {
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNumber)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
break 'goto_error;
}
position = ctxt.value_pop();
if let Some(position) = position.as_ref() {
pos = position.floatval as i32;
}
}
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathString)
{
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
break 'goto_error;
}
let string = ctxt.value_pop().unwrap();
if ctxt.value().is_none_or(|value| {
!matches!(
value.typ,
XmlXPathObjectType::XPathLocationset | XmlXPathObjectType::XPathNodeset
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
break 'goto_error;
}
let mut set = ctxt.value_pop().unwrap();
newset = Some(XmlLocationSet::new(None));
if set.nodesetval.is_none() {
break 'goto_error;
}
if set.typ == XmlXPathObjectType::XPathNodeset {
set = xml_xptr_new_location_set_node_set(set.nodesetval.as_deref());
}
let oldset = set
.user
.as_ref()
.and_then(|user| user.as_location_set())
.unwrap();
for loc in &oldset.loc_tab {
xml_xptr_get_start_point(loc, &mut start, &mut startindex);
xml_xptr_get_end_point(loc, &mut end, &mut endindex);
xml_xptr_advance_char(&mut start, &mut startindex, 0);
xml_xptr_get_last_char(&mut end, &mut endindex);
while {
fend = end;
fendindex = endindex;
found = xml_xptr_search_string(
string.stringval.as_deref().unwrap(),
&mut start,
&mut startindex,
&mut fend,
&mut fendindex,
);
if found == 1 {
if position.is_none() {
newset.as_mut().unwrap().push(
xml_xptr_new_range(
start.unwrap(),
startindex as i32,
fend.unwrap(),
fendindex as i32,
)
.map(Rc::new)
.unwrap(),
);
} else if xml_xptr_advance_char(&mut start, &mut startindex, pos - 1) == 0 {
if number.is_some() && num > 0 {
let mut rend = start;
let mut rindx = startindex - 1;
if xml_xptr_advance_char(&mut rend, &mut rindx, num) == 0 {
newset.as_mut().unwrap().push(
xml_xptr_new_range(
start.unwrap(),
startindex as i32,
rend.unwrap(),
rindx as i32,
)
.map(Rc::new)
.unwrap(),
);
}
} else if number.is_some() && num <= 0 {
newset.as_mut().unwrap().push(
xml_xptr_new_range(
start.unwrap(),
startindex as i32,
start.unwrap(),
startindex as i32,
)
.map(Rc::new)
.unwrap(),
);
} else {
newset.as_mut().unwrap().push(
xml_xptr_new_range(
start.unwrap(),
startindex as i32,
fend.unwrap(),
fendindex as i32,
)
.map(Rc::new)
.unwrap(),
);
}
}
start = fend;
startindex = fendindex;
if string.stringval.as_deref().unwrap().is_empty() {
startindex += 1;
}
}
found == 1
} {}
}
}
if let Some(newset) = newset {
(*ctxt).value_push(xml_xptr_wrap_location_set(newset));
}
}
#[doc(alias = "xmlXPtrNewPoint")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_new_point(node: XmlGenericNodePtr, indx: i32) -> Option<XmlXPathObject> {
use crate::xpath::XmlXPathObjectUserData;
if indx < 0 {
return None;
}
let mut ret = XmlXPathObject::default();
ret.typ = XmlXPathObjectType::XPathPoint;
ret.user = Some(XmlXPathObjectUserData::Node(node));
ret.index = indx;
Some(ret)
}
#[doc(alias = "xmlXPtrStartPointFunction")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_start_point_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::XPathLocationset | XmlXPathObjectType::XPathNodeset
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let mut obj = ctxt.value_pop().unwrap();
if obj.typ == XmlXPathObjectType::XPathNodeset {
obj = xml_xptr_new_location_set_node_set(obj.nodesetval.as_deref());
}
let mut newset = XmlLocationSet::new(None);
let oldset = obj.user.as_ref().and_then(|user| user.as_location_set());
if let Some(oldset) = oldset {
for tmp in &oldset.loc_tab {
let mut point = None;
match tmp.typ {
XmlXPathObjectType::XPathPoint => {
point = xml_xptr_new_point(
tmp.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
tmp.index,
)
}
XmlXPathObjectType::XPathRange => {
let node = tmp.user.as_ref().and_then(|user| user.as_node()).copied();
if let Some(node) = node {
if matches!(
node.element_type(),
XmlElementType::XmlAttributeNode | XmlElementType::XmlNamespaceDecl
) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPtrSyntaxError as i32);
return;
}
point = xml_xptr_new_point(node, tmp.index);
}
}
_ => {
}
}
if let Some(point) = point {
newset.push(Rc::new(point));
}
}
}
ctxt.value_push(xml_xptr_wrap_location_set(newset));
}
#[doc(alias = "xmlXPtrEndPointFunction")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_end_point_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::XPathLocationset | XmlXPathObjectType::XPathNodeset
)
}) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidType as i32);
return;
}
let mut obj = ctxt.value_pop().unwrap();
if obj.typ == XmlXPathObjectType::XPathNodeset {
obj = xml_xptr_new_location_set_node_set(obj.nodesetval.as_deref());
}
let mut newset = XmlLocationSet::new(None);
let oldset = obj.user.as_ref().and_then(|user| user.as_location_set());
if let Some(oldset) = oldset {
for tmp in &oldset.loc_tab {
let mut point = None;
match tmp.typ {
XmlXPathObjectType::XPathPoint => {
point = xml_xptr_new_point(
tmp.user
.as_ref()
.and_then(|user| user.as_node())
.copied()
.unwrap(),
tmp.index,
)
}
XmlXPathObjectType::XPathRange => {
let node = tmp.user2.as_ref().and_then(|user| user.as_node()).copied();
if let Some(node) = node {
if matches!(
node.element_type(),
XmlElementType::XmlAttributeNode | XmlElementType::XmlNamespaceDecl
) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPtrSyntaxError as i32);
return;
}
point = xml_xptr_new_point(node, tmp.index2);
} else if tmp.user.is_none() {
}
}
_ => {
}
}
if let Some(point) = point {
newset.push(Rc::new(point));
}
}
}
ctxt.value_push(xml_xptr_wrap_location_set(newset));
}
#[doc(alias = "xmlXPtrHereFunction")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_here_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 0).is_err() {
return;
}
let Some(here) = ctxt.context.here else {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPtrSyntaxError as i32);
return;
};
ctxt.value_push(xml_xptr_new_location_set_nodes(here, None));
}
#[doc(alias = "xmlXPtrOriginFunction")]
#[cfg(feature = "libxml_xptr_locs")]
fn xml_xptr_origin_function(ctxt: &mut XmlXPathParserContext, nargs: usize) {
if check_arity(ctxt, nargs, 0).is_err() {
return;
}
let Some(origin) = ctxt.context.origin else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPtrSyntaxError as i32);
return;
};
ctxt.value_push(xml_xptr_new_location_set_nodes(origin, None));
}
pub fn xml_xptr_new_context(
doc: Option<XmlDocPtr>,
here: Option<XmlGenericNodePtr>,
origin: Option<XmlGenericNodePtr>,
) -> XmlXPathContext {
let mut ret = XmlXPathContext::new(doc);
#[cfg(feature = "libxml_xptr_locs")]
{
ret.xptr = 1;
ret.here = here;
ret.origin = origin;
}
ret.register_function("range".into(), Some(xml_xptr_range_function));
ret.register_function("range-inside".into(), Some(xml_xptr_range_inside_function));
ret.register_function("string-range".into(), Some(xml_xptr_string_range_function));
ret.register_function("start-point".into(), Some(xml_xptr_start_point_function));
ret.register_function("end-point".into(), Some(xml_xptr_end_point_function));
ret.register_function("here".into(), Some(xml_xptr_here_function));
ret.register_function("origin".into(), Some(xml_xptr_origin_function));
ret
}
#[doc(alias = "xmlXPtrErr")]
macro_rules! xml_xptr_err {
($ctxt:expr, $error:expr, $msg:literal) => {
xml_xptr_err!(@inner, $ctxt, $error, $msg, None);
};
($ctxt:expr, $error:expr, $msg:literal, $extra:expr) => {
let msg = format!($msg, $extra);
xml_xptr_err!(@inner, $ctxt, $error, &msg, Some($extra.to_owned().into()));
};
(@inner, $ctxt:expr, $error:expr, $msg:expr, $extra:expr) => {
let error: XmlParserErrors = $error;
$ctxt.error = error as i32;
$ctxt.context.last_error.reset();
$ctxt.context.last_error.domain = XmlErrorDomain::XmlFromXPointer;
$ctxt.context.last_error.code = error;
$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 {
__xml_raise_error!(
None,
None,
None,
null_mut(),
$ctxt.context.debug_node,
XmlErrorDomain::XmlFromXPointer,
error,
XmlErrorLevel::XmlErrError,
None,
0,
$extra,
Some($ctxt.base.to_string().into()),
None,
$ctxt.cur as _,
0,
Some($msg),
);
}
};
}
#[doc(alias = "xmlXPtrGetChildNo")]
fn xml_xptr_get_child_no(ctxt: &mut XmlXPathParserContext, indx: i32) {
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathNodeset)
{
xml_xpath_err(
Some(&mut *ctxt),
crate::xpath::XmlXPathError::XPathInvalidType as i32,
);
return;
};
let mut obj = ctxt.value_pop().unwrap();
let Some(oldset) = obj.nodesetval.as_deref_mut().filter(|_| indx > 0) else {
ctxt.value_push(xml_xpath_new_node_set(None));
return;
};
if oldset.node_tab.len() != 1 {
ctxt.value_push(xml_xpath_new_node_set(None));
return;
}
let Some(cur) = xml_xptr_get_nth_child(oldset.node_tab[0], indx as usize) else {
ctxt.value_push(xml_xpath_new_node_set(None));
return;
};
oldset.node_tab[0] = cur;
ctxt.value_push(obj);
}
#[doc(alias = "xmlXPtrEvalChildSeq")]
fn xml_xptr_eval_child_seq(ctxt: &mut XmlXPathParserContext, name: Option<&str>) {
if name.is_none() && ctxt.current_char() == Some('/') && ctxt.nth_byte(1) != Some(b'1') {
xml_xptr_err!(
ctxt,
XmlParserErrors::XmlXPtrChildseqStart,
"warning: ChildSeq not starting by /1\n"
);
}
if let Some(name) = name {
ctxt.value_push(xml_xpath_new_string(Some(name)));
xml_xpath_id_function(&mut *ctxt, 1);
if ctxt.error != XmlXPathError::XPathExpressionOK as i32 {
return;
};
}
while ctxt.current_char() == Some('/') {
let mut child: i32 = 0;
let mut overflow = false;
ctxt.next_char();
while let Some(cur) = ctxt.current_char().filter(|c| c.is_ascii_digit()) {
let (c, f) = child.overflowing_mul(10);
overflow |= f;
let (c, f) = c.overflowing_add(cur as i32 - b'0' as i32);
overflow |= f;
child = c;
ctxt.next_char();
}
if overflow {
child = 0;
}
xml_xptr_get_child_no(ctxt, child);
}
}
#[doc(alias = "xmlXPtrEvalXPtrPart")]
fn xml_xptr_eval_xptr_part(ctxt: &mut XmlXPathParserContext, name: &str) {
if ctxt.current_char() != Some('(') {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathExprError as i32);
return;
}
ctxt.next_char();
let mut level = 1;
let mut buffer = String::with_capacity(ctxt.current_str().len());
while let Some(c) = ctxt.current_char() {
if c == ')' {
level -= 1;
if level == 0 {
ctxt.next_char();
break;
}
} else if c == '(' {
level += 1;
} else if c == '^' && matches!(ctxt.nth_byte(1), Some(b')' | b'(' | b'^')) {
ctxt.next_char();
}
if let Some(c) = ctxt.next_char() {
buffer.push(c);
}
}
if level != 0 && ctxt.current_char().is_none() {
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPtrSyntaxError as i32);
return;
}
if name == "xpointer" || name == "xpath1" {
let old_base = replace(&mut ctxt.base, buffer.into_boxed_str());
let old_cur = ctxt.cur;
ctxt.cur = 0;
ctxt.context.node = ctxt.context.doc.map(|doc| doc.into());
ctxt.context.proximity_position = 1;
ctxt.context.context_size = 1;
#[cfg(feature = "libxml_xptr_locs")]
{
ctxt.xptr = (name == "xpointer") as i32;
}
ctxt.evaluate_expression();
ctxt.base = old_base;
ctxt.cur = old_cur;
} else if name == "element" {
let old_base = replace(&mut ctxt.base, buffer.into_boxed_str());
let old_cur = ctxt.cur;
ctxt.cur = 0;
if ctxt.base.starts_with('/') {
xml_xpath_root(ctxt);
xml_xptr_eval_child_seq(ctxt, None);
} else {
let Some(name2) = ctxt.parse_name() else {
ctxt.base = old_base;
ctxt.cur = old_cur;
xml_xpath_err(Some(&mut *ctxt), XmlXPathError::XPathExprError as i32);
return;
};
xml_xptr_eval_child_seq(ctxt, Some(&name2));
}
ctxt.base = old_base;
ctxt.cur = old_cur;
} else if name == "xmlns" {
let old_base = replace(&mut ctxt.base, buffer.into_boxed_str());
let old_cur = ctxt.cur;
ctxt.cur = 0;
let Some(prefix) = ctxt.parse_ncname() else {
ctxt.base = old_base;
ctxt.cur = old_cur;
xml_xpath_err(Some(ctxt), XmlXPathError::XPtrSyntaxError as i32);
return;
};
ctxt.skip_blanks();
if ctxt.current_char() != Some('=') {
ctxt.base = old_base;
ctxt.cur = old_cur;
xml_xpath_err(Some(ctxt), XmlXPathError::XPtrSyntaxError as i32);
return;
}
ctxt.next_char();
ctxt.skip_blanks();
let ns_uri = ctxt.current_str().to_owned();
ctxt.context.register_ns(&prefix, Some(&ns_uri));
ctxt.base = old_base;
ctxt.cur = old_cur;
} else {
xml_xptr_err!(
ctxt,
XmlParserErrors::XmlXPtrUnknownScheme,
"unsupported scheme '{}'\n",
name
);
}
}
#[doc(alias = "xmlXPtrEvalFullXPtr")]
fn xml_xptr_eval_full_xptr(ctxt: &mut XmlXPathParserContext, name: &str) {
let mut next = Some(Cow::Borrowed(name));
while let Some(name) = next {
ctxt.error = XmlXPathError::XPathExpressionOK as i32;
xml_xptr_eval_xptr_part(ctxt, &name);
if ctxt.error != XmlXPathError::XPathExpressionOK as i32
&& ctxt.error != XmlParserErrors::XmlXPtrUnknownScheme as i32
{
return;
}
if let Some(obj) = ctxt.value() {
match obj.typ {
#[cfg(feature = "libxml_xptr_locs")]
XmlXPathObjectType::XPathLocationset => {
let loc = obj.user.as_ref().and_then(|user| user.as_location_set());
if loc.is_some_and(|loc| !loc.loc_tab.is_empty()) {
return;
}
}
XmlXPathObjectType::XPathNodeset => {
let loc = obj.nodesetval.as_deref();
if loc.is_some_and(|l| !l.is_empty()) {
return;
}
}
_ => {}
}
while ctxt.value_pop().is_some() {}
}
ctxt.skip_blanks();
next = ctxt.parse_name().map(Cow::Owned);
}
}
#[doc(alias = "xmlXPtrEvalXPointer")]
fn xml_xptr_eval_xpointer(ctxt: &mut XmlXPathParserContext) {
ctxt.skip_blanks();
if ctxt.current_char() == Some('/') {
xml_xpath_root(ctxt);
xml_xptr_eval_child_seq(ctxt, None);
} else {
let Some(name) = ctxt.parse_name() else {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathExprError as i32);
return;
};
if ctxt.current_char() == Some('(') {
xml_xptr_eval_full_xptr(ctxt, &name);
return;
} else {
xml_xptr_eval_child_seq(ctxt, Some(&name));
}
}
ctxt.skip_blanks();
if ctxt.current_char().is_some() {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathExprError as i32);
}
}
pub fn xml_xptr_eval(xpath: &str, ctx: &mut XmlXPathContext) -> Option<XmlXPathObject> {
let mut res = None;
let mut stack: i32 = 0;
xml_init_parser();
let mut ctxt = XmlXPathParserContext::new(xpath, ctx);
xml_xptr_eval_xpointer(&mut ctxt);
#[cfg(feature = "libxml_xptr_locs")]
let f = ctxt.value().is_some_and(|value| {
!matches!(
value.typ,
XmlXPathObjectType::XPathLocationset | XmlXPathObjectType::XPathNodeset
)
});
#[cfg(not(feature = "libxml_xptr_locs"))]
let f = ctxt
.value()
.is_some_and(|value| (*value).typ != XmlXPathObjectType::XPathNodeset);
if f {
xml_xptr_err!(
&mut ctxt,
XmlParserErrors::XmlXPtrEvalFailed,
"xmlXPtrEval: evaluation failed to return a node set\n"
);
} else {
res = ctxt.value_pop();
}
while let Some(tmp) = ctxt.value_pop() {
if tmp.typ == XmlXPathObjectType::XPathNodeset {
let set = tmp.nodesetval.as_deref();
if set
.is_none_or(|s| s.len() != 1 || s.get(0) != ctxt.context.doc.map(|doc| doc.into()))
{
stack += 1;
}
} else {
stack += 1;
}
}
if stack != 0 {
xml_xptr_err!(
&mut ctxt,
XmlParserErrors::XmlXPtrExtraObjects,
"xmlXPtrEval: object(s) left on the eval stack\n"
);
}
if ctxt.error != XmlXPathError::XPathExpressionOK as i32 {
res = None;
}
res
}
#[doc(alias = "xmlXPtrRangeToFunction")]
#[cfg(feature = "libxml_xptr_locs")]
pub fn xml_xptr_range_to_function(ctxt: &mut XmlXPathParserContext, _nargs: i32) {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathExprError as i32);
}
#[doc(alias = "xmlXPtrEvalRangePredicate")]
#[cfg(feature = "libxml_xptr_locs")]
pub fn xml_xptr_eval_range_predicate(ctxt: &mut XmlXPathParserContext) {
ctxt.skip_blanks();
if ctxt.current_char() != Some('[') {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidPredicateError as i32);
return;
}
ctxt.next_char();
ctxt.skip_blanks();
if ctxt
.value()
.is_none_or(|value| value.typ != XmlXPathObjectType::XPathLocationset)
{
xml_xpath_err(
Some(ctxt),
crate::xpath::XmlXPathError::XPathInvalidType as i32,
);
return;
};
let obj = ctxt.value_pop().unwrap();
let oldset = obj.user.as_ref().and_then(|user| user.as_location_set());
ctxt.context.node = None;
if let Some(oldset) = oldset.filter(|oldset| !oldset.loc_tab.is_empty()) {
let cur = ctxt.cur;
let mut newset = XmlLocationSet::new(None);
for (i, loc) in oldset.loc_tab.iter().enumerate() {
ctxt.cur = cur;
ctxt.context.node = loc.user.as_ref().and_then(|user| user.as_node()).copied();
let tmp = xml_xpath_new_node_set(ctxt.context.node);
ctxt.value_push(tmp);
let keep_stack_len = ctxt.value_tab.len();
ctxt.context.context_size = oldset.loc_tab.len() as i32;
ctxt.context.proximity_position = i as i32 + 1;
ctxt.evaluate_expression();
if ctxt.error != crate::xpath::XmlXPathError::XPathExpressionOK as i32 {
return;
};
let res = ctxt.value_pop().unwrap();
if ctxt.evaluate_predicate_result(&res) != 0 {
newset.push(loc.clone());
}
if keep_stack_len == ctxt.value_tab.len() {
ctxt.value_pop();
}
ctxt.context.node = None;
}
ctxt.context.node = None;
ctxt.context.context_size = -1;
ctxt.context.proximity_position = -1;
ctxt.value_push(xml_xptr_wrap_location_set(newset));
} else {
ctxt.context.context_size = 0;
ctxt.context.proximity_position = 0;
ctxt.evaluate_expression();
ctxt.value_pop();
ctxt.value_push(obj);
if ctxt.error != crate::xpath::XmlXPathError::XPathExpressionOK as i32 {
return;
};
}
if ctxt.current_char() != Some(']') {
xml_xpath_err(Some(ctxt), XmlXPathError::XPathInvalidPredicateError as i32);
return;
}
ctxt.next_char();
ctxt.skip_blanks();
}