#![allow(
missing_docs,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
#[cfg(test)]
use crate::xml::string::xmlstr_to_string;
use core::ffi::c_void;
use core::ptr;
use std::os::raw::{c_char, c_int, c_long, c_uint};
use crate::abi::allocator::xmlFreeImpl;
use crate::abi::callbacks::{xmlInputCloseCallback, xmlInputReadCallback};
use crate::abi::structs::{_xmlAttr, _xmlDoc, _xmlNode, _xmlParserCtxt, _xmlParserInputBuffer};
use crate::abi::types::xmlElementType::*;
use crate::abi::types::*;
use crate::xml::parser::helpers::{
create_parser_ctxt, free_parser_ctxt, input_from_file, input_from_io, input_from_memory,
input_from_memory_named, parse_document, setup_parser_input,
};
use crate::xml::parser::input::InputBuffer;
use crate::xml::string::{bytes_to_xmlstr, xml_strdup, xmlstr_to_bytes};
use crate::xml::tree;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum ReaderNodeType {
NONE = 0,
ELEMENT = 1,
ATTRIBUTE = 2,
TEXT = 3,
CDATA = 4,
ENTITY_REFERENCE = 5,
ENTITY = 6,
PROCESSING_INSTRUCTION = 7,
COMMENT = 8,
DOCUMENT = 9,
DOCUMENT_TYPE = 10,
DOCUMENT_FRAGMENT = 11,
NOTATION = 12,
WHITESPACE = 13,
SIGNIFICANT_WHITESPACE = 14,
END_ELEMENT = 15,
END_ENTITY = 16,
XML_DECLARATION = 17,
NAMESPACE = 18,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum ReadState {
NOT_INITIALIZED = 0,
INITIALIZED = 1,
READING = 2,
EOF = 3,
CLOSED = 4,
ERROR = 5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
#[allow(dead_code)]
pub(crate) enum ParserProp {
LOADDTD = 1,
DEFAULTATTRS = 2,
VALIDATE = 3,
SUBST_ENTITIES = 4,
}
#[derive(Debug, Clone)]
struct TraversalEvent {
node: *mut _xmlNode,
is_end: bool,
depth: i32,
}
#[allow(dead_code)]
unsafe fn compute_depth(node: *mut _xmlNode) -> i32 {
if node.is_null() {
return 0;
}
let mut depth: i32 = 0;
let mut cur = unsafe { (*node).parent };
while !cur.is_null() {
if unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
depth += 1;
}
cur = unsafe { (*cur).parent };
}
depth
}
const fn element_type_to_reader_type(etype: c_int) -> ReaderNodeType {
match etype {
x if x == XML_ELEMENT_NODE as c_int => ReaderNodeType::ELEMENT,
x if x == XML_ATTRIBUTE_NODE as c_int => ReaderNodeType::ATTRIBUTE,
x if x == XML_TEXT_NODE as c_int => ReaderNodeType::TEXT,
x if x == XML_CDATA_SECTION_NODE as c_int => ReaderNodeType::CDATA,
x if x == XML_ENTITY_REF_NODE as c_int => ReaderNodeType::ENTITY_REFERENCE,
x if x == XML_ENTITY_NODE as c_int => ReaderNodeType::ENTITY,
x if x == XML_PI_NODE as c_int => ReaderNodeType::PROCESSING_INSTRUCTION,
x if x == XML_COMMENT_NODE as c_int => ReaderNodeType::COMMENT,
x if x == XML_DOCUMENT_NODE as c_int => ReaderNodeType::DOCUMENT,
x if x == XML_DOCUMENT_TYPE_NODE as c_int => ReaderNodeType::DOCUMENT_TYPE,
x if x == XML_DOCUMENT_FRAG_NODE as c_int => ReaderNodeType::DOCUMENT_FRAGMENT,
x if x == XML_NOTATION_NODE as c_int => ReaderNodeType::NOTATION,
x if x == XML_DTD_NODE as c_int => ReaderNodeType::DOCUMENT_TYPE,
x if x == XML_NAMESPACE_DECL as c_int => ReaderNodeType::NONE,
_ => ReaderNodeType::NONE,
}
}
fn is_whitespace_only(text: &[u8]) -> bool {
text.iter()
.all(|&b| b == b' ' || b == b'\t' || b == b'\n' || b == b'\r')
}
#[derive(Clone, Copy)]
enum AttrTarget {
None,
Ns(*mut crate::abi::structs::_xmlNs),
Prop(*mut _xmlAttr),
}
#[derive(Debug)]
pub struct XmlTextReader {
doc: *mut _xmlDoc,
ctxt: *mut _xmlParserCtxt,
events: Vec<TraversalEvent>,
event_index: usize,
state: ReadState,
cur_node: *mut _xmlNode,
node_type: ReaderNodeType,
depth: i32,
name: *mut xmlChar,
value: *mut xmlChar,
attribute_count: i32,
cur_attribute: i32,
options: c_int,
encoding: *mut xmlChar,
URL: *mut xmlChar,
errors: Vec<String>,
parsed: bool,
error_handler: Option<xmlTextReaderErrorFunc>,
error_arg: *mut c_void,
structured_handler: Option<crate::abi::callbacks::xmlStructuredErrorFunc>,
structured_arg: *mut c_void,
last_err: crate::abi::structs::_xmlError,
max_amplification: c_int,
schema: *mut c_void,
rng: *mut c_void,
owns_doc: bool,
cur_attr_is_ns: bool,
}
impl XmlTextReader {
unsafe fn new(ctxt: *mut _xmlParserCtxt, URL: Option<&[u8]>, encoding: Option<&[u8]>) -> Self {
let url_ptr = URL
.map(|u| unsafe { bytes_to_xmlstr(u) })
.unwrap_or(ptr::null_mut());
let enc_ptr = encoding
.map(|e| unsafe { bytes_to_xmlstr(e) })
.unwrap_or(ptr::null_mut());
XmlTextReader {
doc: ptr::null_mut(),
ctxt,
events: Vec::new(),
event_index: 0,
state: ReadState::INITIALIZED,
cur_node: ptr::null_mut(),
node_type: ReaderNodeType::NONE,
depth: 0,
name: ptr::null_mut(),
value: ptr::null_mut(),
attribute_count: -1,
cur_attribute: -1,
options: 0,
encoding: enc_ptr,
URL: url_ptr,
errors: Vec::new(),
parsed: false,
error_handler: None,
error_arg: ptr::null_mut(),
structured_handler: None,
structured_arg: ptr::null_mut(),
last_err: unsafe { core::mem::zeroed() },
max_amplification: 0,
schema: ptr::null_mut(),
rng: ptr::null_mut(),
owns_doc: true,
cur_attr_is_ns: false,
}
}
unsafe fn parse_and_build_events(&mut self) -> c_int {
if self.ctxt.is_null() {
self.state = ReadState::ERROR;
self.errors.push("No parser context".to_string());
return -1;
}
unsafe {
(*self.ctxt).options = self.options;
}
let result = unsafe { parse_document(self.ctxt) };
let doc = unsafe { (*self.ctxt).myDoc };
self.doc = doc;
if !self.ctxt.is_null() {
unsafe { free_parser_ctxt(self.ctxt) };
}
self.ctxt = ptr::null_mut();
if result != 0 || doc.is_null() {
self.state = ReadState::ERROR;
self.errors.push("Failed to parse document".to_string());
return -1;
}
if self.encoding.is_null() && !doc.is_null() {
let doc_enc = unsafe { (*doc).encoding };
if !doc_enc.is_null() {
self.encoding = unsafe { xml_strdup(doc_enc as *const xmlChar) };
}
}
self.build_events();
self.parsed = true;
0
}
fn build_events(&mut self) {
self.events.clear();
if self.doc.is_null() {
return;
}
let root = unsafe { (*self.doc).children };
if root.is_null() {
return;
}
unsafe {
let mut n = root;
while !n.is_null() {
self.walk_tree(n, 0);
n = (*n).next;
}
}
}
unsafe fn walk_tree(&mut self, node: *mut _xmlNode, depth: i32) {
if node.is_null() {
return;
}
let node_type = unsafe { (*node).type_ };
if node_type == XML_ELEMENT_NODE as c_int {
self.events.push(TraversalEvent {
node,
is_end: false,
depth,
});
let mut child = unsafe { (*node).children };
while !child.is_null() {
let child_depth = depth + 1;
self.walk_tree(child, child_depth);
child = unsafe { (*child).next };
}
if !unsafe { (*node).children }.is_null() {
self.events.push(TraversalEvent {
node,
is_end: true,
depth,
});
}
} else if node_type == XML_TEXT_NODE as c_int
|| node_type == XML_CDATA_SECTION_NODE as c_int
|| node_type == XML_COMMENT_NODE as c_int
|| node_type == XML_PI_NODE as c_int
|| node_type == XML_ENTITY_REF_NODE as c_int
{
self.events.push(TraversalEvent {
node,
is_end: false,
depth,
});
} else {
self.events.push(TraversalEvent {
node,
is_end: false,
depth,
});
}
}
fn position_at(&mut self, index: usize) {
if index >= self.events.len() {
self.state = ReadState::EOF;
self.cur_node = ptr::null_mut();
self.node_type = ReaderNodeType::NONE;
self.depth = 0;
self.clear_cached_name();
self.clear_cached_value();
self.attribute_count = -1;
self.cur_attribute = -1;
return;
}
let ev_node: *mut _xmlNode;
let ev_is_end: bool;
let ev_depth: i32;
{
let event = &self.events[index];
ev_node = event.node;
ev_is_end = event.is_end;
ev_depth = event.depth;
}
self.event_index = index;
self.cur_node = ev_node;
self.depth = ev_depth;
let etype = unsafe { (*ev_node).type_ };
if ev_is_end {
self.node_type = ReaderNodeType::END_ELEMENT;
} else {
self.node_type = element_type_to_reader_type(etype);
if etype == XML_TEXT_NODE as c_int || etype == XML_CDATA_SECTION_NODE as c_int {
let content = unsafe { (*ev_node).content };
if !content.is_null() {
let len = unsafe { libc::strlen(content as *const libc::c_char) as usize };
let slice = unsafe { core::slice::from_raw_parts(content, len) };
if is_whitespace_only(slice) {
self.node_type = ReaderNodeType::SIGNIFICANT_WHITESPACE;
}
}
}
}
unsafe { self.cache_name_and_value(ev_node, ev_is_end) };
if etype == XML_ELEMENT_NODE as c_int && !ev_is_end {
self.attribute_count = unsafe { self.count_attributes(ev_node) };
} else {
self.attribute_count = -1;
}
self.cur_attribute = -1;
self.cur_attr_is_ns = false;
}
unsafe fn cache_name_and_value(&mut self, node: *mut _xmlNode, is_end: bool) {
self.clear_cached_name();
self.clear_cached_value();
if node.is_null() {
return;
}
let etype = unsafe { (*node).type_ };
let name: *mut xmlChar = if is_end {
unsafe { (*node).name as *mut xmlChar }
} else {
if etype == XML_ELEMENT_NODE as c_int
|| etype == XML_PI_NODE as c_int
|| etype == XML_ENTITY_REF_NODE as c_int
|| etype == XML_ENTITY_NODE as c_int
|| etype == XML_DOCUMENT_TYPE_NODE as c_int
|| etype == XML_NOTATION_NODE as c_int
{
unsafe { (*node).name as *mut xmlChar }
} else if etype == XML_ATTRIBUTE_NODE as c_int {
ptr::null_mut()
} else {
ptr::null_mut()
}
};
if !name.is_null() {
let qualified: *mut xmlChar = if etype == XML_ELEMENT_NODE as c_int && !node.is_null() {
let ns = unsafe { (*node).ns };
if !ns.is_null() && !unsafe { (*ns).prefix }.is_null() {
let plen =
libc::strlen(unsafe { (*ns).prefix } as *const libc::c_char) as usize;
let nlen = libc::strlen(name as *const libc::c_char) as usize;
let p =
crate::abi::allocator::xmlMallocImpl(plen + 1 + nlen + 1) as *mut xmlChar;
if !p.is_null() {
libc::memcpy(
p as *mut libc::c_void,
unsafe { (*ns).prefix } as *const libc::c_void,
plen,
);
*p.add(plen) = b':';
libc::memcpy(
p.add(plen + 1) as *mut libc::c_void,
name as *const libc::c_void,
nlen,
);
*p.add(plen + 1 + nlen) = 0;
}
p
} else {
unsafe { xml_strdup(name as *const xmlChar) }
}
} else {
unsafe { xml_strdup(name as *const xmlChar) }
};
self.name = qualified;
} else if !is_end {
let fixed: &[u8] = match etype {
x if x == XML_TEXT_NODE as c_int => b"#text\0",
x if x == XML_CDATA_SECTION_NODE as c_int => b"#cdata-section\0",
x if x == XML_COMMENT_NODE as c_int => b"#comment\0",
x if x == XML_DOCUMENT_NODE as c_int => b"#document\0",
x if x == XML_HTML_DOCUMENT_NODE as c_int => b"#document\0",
x if x == XML_DOCUMENT_FRAG_NODE as c_int => b"#document-fragment\0",
_ => b"",
};
if !fixed.is_empty() {
self.name = unsafe { xml_strdup(fixed.as_ptr() as *const xmlChar) };
}
}
let value: *mut xmlChar = if etype == XML_TEXT_NODE as c_int
|| etype == XML_CDATA_SECTION_NODE as c_int
|| etype == XML_COMMENT_NODE as c_int
{
unsafe { (*node).content }
} else if etype == XML_PI_NODE as c_int {
unsafe { (*node).content }
} else if etype == XML_ENTITY_REF_NODE as c_int {
unsafe { (*node).content }
} else {
ptr::null_mut()
};
if !value.is_null() {
self.value = unsafe { xml_strdup(value as *const xmlChar) };
}
}
unsafe fn count_attributes(&self, node: *mut _xmlNode) -> i32 {
let mut count: i32 = 0;
let mut ns = unsafe { (*node).nsDef };
while !ns.is_null() {
count += 1;
ns = unsafe { (*ns).next };
}
let mut prop = unsafe { (*node).properties };
while !prop.is_null() {
count += 1;
prop = unsafe { (*prop).next };
}
count
}
unsafe fn attr_at(&self, node: *mut _xmlNode, index: i32) -> AttrTarget {
if node.is_null() || index < 0 {
return AttrTarget::None;
}
let mut i = 0;
let mut ns = unsafe { (*node).nsDef };
while !ns.is_null() {
if i == index {
return AttrTarget::Ns(ns);
}
i += 1;
ns = unsafe { (*ns).next };
}
let mut prop = unsafe { (*node).properties };
while !prop.is_null() {
if i == index {
return AttrTarget::Prop(prop);
}
i += 1;
prop = unsafe { (*prop).next };
}
AttrTarget::None
}
unsafe fn attr_index_by_name(&self, node: *mut _xmlNode, name: *const xmlChar) -> i32 {
if node.is_null() || name.is_null() {
return -1;
}
let mut i = 0;
let mut ns = unsafe { (*node).nsDef };
while !ns.is_null() {
let n = unsafe { &*ns };
let nsname: Vec<u8> = if n.prefix.is_null() {
b"xmlns\0".to_vec()
} else {
let mut v = b"xmlns:\0".to_vec();
let plen = libc::strlen(n.prefix as *const libc::c_char) as usize;
v.extend_from_slice(core::slice::from_raw_parts(n.prefix, plen));
v.push(0);
v
};
let nlen = libc::strlen(name as *const libc::c_char) as usize;
let nbytes = core::slice::from_raw_parts(name, nlen);
if nbytes == &nsname[..nsname.len() - 1] {
return i;
}
i += 1;
ns = unsafe { (*ns).next };
}
let mut prop = unsafe { (*node).properties };
while !prop.is_null() {
let pn = unsafe { (*prop).name };
if !pn.is_null()
&& libc::strcmp(pn as *const libc::c_char, name as *const libc::c_char) == 0
{
return i;
}
i += 1;
prop = unsafe { (*prop).next };
}
-1
}
fn clear_cached_name(&mut self) {
if !self.name.is_null() {
unsafe { xmlFreeImpl(self.name as *mut c_void) };
self.name = ptr::null_mut();
}
}
fn clear_cached_value(&mut self) {
if !self.value.is_null() {
unsafe { xmlFreeImpl(self.value as *mut c_void) };
self.value = ptr::null_mut();
}
}
pub unsafe fn Read(&mut self) -> c_int {
if self.state == ReadState::ERROR || self.state == ReadState::CLOSED {
return -1;
}
if !self.parsed {
if self.parse_and_build_events() != 0 {
self.state = ReadState::ERROR;
return -1;
}
self.state = ReadState::READING;
}
if self.state == ReadState::EOF {
return 0;
}
if self.cur_attribute >= 0 {
self.cur_attribute = -1;
self.cur_attr_is_ns = false;
if !self.cur_node.is_null() {
unsafe { self.cache_name_and_value(self.cur_node, false) };
}
}
if self.events.is_empty() {
self.state = ReadState::EOF;
return 0;
}
let next_index = if self.cur_node.is_null() {
0
} else {
self.event_index + 1
};
if next_index < self.events.len() {
self.position_at(next_index);
1
} else {
self.state = ReadState::EOF;
self.cur_node = ptr::null_mut();
self.node_type = ReaderNodeType::NONE;
self.depth = 0;
self.clear_cached_name();
self.clear_cached_value();
self.attribute_count = -1;
self.cur_attribute = -1;
0
}
}
pub unsafe fn Next(&mut self) -> c_int {
if self.state != ReadState::READING || self.cur_node.is_null() {
return -1;
}
let current_depth = self.depth;
let mut i = self.event_index + 1;
while i < self.events.len() {
let event = &self.events[i];
if event.depth <= current_depth && !event.is_end {
self.position_at(i);
return 1;
}
i += 1;
}
0
}
pub unsafe fn MoveToElement(&mut self) -> c_int {
if self.cur_attribute < 0 {
return 0;
}
self.cur_attribute = -1;
self.cur_attr_is_ns = false;
if !self.cur_node.is_null() {
unsafe { self.cache_name_and_value(self.cur_node, false) };
self.node_type = ReaderNodeType::ELEMENT;
}
1
}
pub unsafe fn MoveToAttribute(&mut self, name: *const xmlChar) -> c_int {
if self.cur_node.is_null() {
return -1;
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return -1;
}
let idx = unsafe { self.attr_index_by_name(self.cur_node, name) };
if idx < 0 {
return 0;
}
self.cur_attribute = idx;
let target = unsafe { self.attr_at(self.cur_node, idx) };
unsafe { self.cache_attribute_info(target) };
1
}
pub unsafe fn MoveToAttributeNo(&mut self, index: c_int) -> c_int {
if self.cur_node.is_null() || index < 0 {
return -1;
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return -1;
}
let target = unsafe { self.attr_at(self.cur_node, index) };
match target {
AttrTarget::None => 0,
t => {
self.cur_attribute = index;
unsafe { self.cache_attribute_info(t) };
1
}
}
}
pub unsafe fn MoveToFirstAttribute(&mut self) -> c_int {
if self.cur_node.is_null() {
return -1;
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return -1;
}
let first = unsafe { self.attr_at(self.cur_node, 0) };
match first {
AttrTarget::None => 0,
t => {
self.cur_attribute = 0;
unsafe { self.cache_attribute_info(t) };
1
}
}
}
pub unsafe fn MoveToNextAttribute(&mut self) -> c_int {
if self.cur_attribute < 0 || self.cur_node.is_null() {
return -1;
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return -1;
}
let next_index = self.cur_attribute + 1;
let target = unsafe { self.attr_at(self.cur_node, next_index) };
match target {
AttrTarget::None => 0,
t => {
self.cur_attribute = next_index;
unsafe { self.cache_attribute_info(t) };
1
}
}
}
unsafe fn cache_attribute_info(&mut self, target: AttrTarget) {
self.node_type = ReaderNodeType::ATTRIBUTE;
self.cur_attr_is_ns = matches!(target, AttrTarget::Ns(_));
self.clear_cached_name();
self.clear_cached_value();
match target {
AttrTarget::Ns(ns) => {
let n = unsafe { &*ns };
if n.prefix.is_null() {
self.name = unsafe { xml_strdup(c"xmlns".as_ptr() as *const xmlChar) };
} else {
let plen = libc::strlen(n.prefix as *const libc::c_char) as usize;
let mut v = Vec::with_capacity(6 + plen);
v.extend_from_slice(b"xmlns:");
v.extend_from_slice(core::slice::from_raw_parts(n.prefix, plen));
v.push(0);
let p = crate::abi::allocator::xmlMallocImpl(v.len()) as *mut xmlChar;
if !p.is_null() {
libc::memcpy(
p as *mut libc::c_void,
v.as_ptr() as *const libc::c_void,
v.len(),
);
self.name = p;
}
}
if !n.href.is_null() {
self.value = unsafe { xml_strdup(n.href as *const xmlChar) };
}
}
AttrTarget::Prop(prop) => {
let attr = unsafe { &*prop };
if !attr.name.is_null() {
if !attr.ns.is_null() && !unsafe { (*attr.ns).prefix }.is_null() {
let plen = libc::strlen(unsafe { (*attr.ns).prefix } as *const libc::c_char)
as usize;
let nlen = libc::strlen(attr.name as *const libc::c_char) as usize;
let p = crate::abi::allocator::xmlMallocImpl(plen + 1 + nlen + 1)
as *mut xmlChar;
if !p.is_null() {
libc::memcpy(
p as *mut libc::c_void,
unsafe { (*attr.ns).prefix } as *const libc::c_void,
plen,
);
*p.add(plen) = b':';
libc::memcpy(
p.add(plen + 1) as *mut libc::c_void,
attr.name as *const libc::c_void,
nlen,
);
*p.add(plen + 1 + nlen) = 0;
self.name = p;
}
} else {
self.name = unsafe { xml_strdup(attr.name as *const xmlChar) };
}
}
if !attr.children.is_null() {
let val = unsafe { (*attr.children).content };
if !val.is_null() {
self.value = unsafe { xml_strdup(val as *const xmlChar) };
}
}
}
AttrTarget::None => {}
}
}
pub unsafe fn Prev(&mut self) -> c_int {
if self.state != ReadState::READING || self.cur_node.is_null() {
return -1;
}
let current_depth = self.depth;
let mut i = if self.event_index > 0 {
self.event_index - 1
} else {
return 0;
};
loop {
let event = &self.events[i];
if event.depth == current_depth && !event.is_end {
self.position_at(i);
return 1;
}
if i == 0 {
break;
}
i -= 1;
}
0
}
pub const fn Depth(&self) -> c_int {
self.depth
}
pub const fn NodeType(&self) -> ReaderNodeType {
self.node_type
}
pub unsafe fn Name(&self) -> *mut xmlChar {
if self.name.is_null() {
return ptr::null_mut();
}
unsafe { xml_strdup(self.name as *const xmlChar) }
}
pub unsafe fn Value(&self) -> *mut xmlChar {
if self.value.is_null() {
return ptr::null_mut();
}
unsafe { xml_strdup(self.value as *const xmlChar) }
}
pub const fn ConstName(&self) -> *const xmlChar {
self.name as *const xmlChar
}
pub const fn ConstValue(&self) -> *const xmlChar {
self.value as *const xmlChar
}
pub const fn HasValue(&self) -> c_int {
if self.value.is_null() {
0
} else {
1
}
}
pub fn HasAttributes(&self) -> c_int {
if self.cur_node.is_null() {
return 0;
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return 0;
}
let props = unsafe { (*self.cur_node).properties };
let nsdefs = unsafe { (*self.cur_node).nsDef };
if props.is_null() && nsdefs.is_null() {
0
} else {
1
}
}
pub fn IsEmptyElement(&self) -> c_int {
if self.cur_node.is_null() {
return 0;
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return 0;
}
let children = unsafe { (*self.cur_node).children };
if children.is_null() {
1
} else {
0
}
}
pub unsafe fn BaseUri(&self) -> *mut xmlChar {
if self.doc.is_null() {
return ptr::null_mut();
}
let url = unsafe { (*self.doc).URL };
if url.is_null() {
return ptr::null_mut();
}
unsafe { xml_strdup(url as *const xmlChar) }
}
pub unsafe fn LocalName(&self) -> *mut xmlChar {
if self.name.is_null() {
return ptr::null_mut();
}
let name_bytes = unsafe { xmlstr_to_bytes(self.name as *const xmlChar) };
if let Some(pos) = name_bytes.iter().position(|&b| b == b':') {
let local = &name_bytes[pos + 1..];
if local.is_empty() {
return ptr::null_mut();
}
unsafe { bytes_to_xmlstr(local) }
} else {
unsafe { xml_strdup(self.name as *const xmlChar) }
}
}
pub unsafe fn NamespaceUri(&self) -> *mut xmlChar {
if self.cur_node.is_null() {
return ptr::null_mut();
}
let ns = unsafe { (*self.cur_node).ns };
if ns.is_null() {
return ptr::null_mut();
}
let href = unsafe { (*ns).href };
if href.is_null() {
return ptr::null_mut();
}
unsafe { xml_strdup(href as *const xmlChar) }
}
pub unsafe fn Prefix(&self) -> *mut xmlChar {
if self.cur_node.is_null() {
return ptr::null_mut();
}
let ns = unsafe { (*self.cur_node).ns };
if ns.is_null() {
return ptr::null_mut();
}
let prefix = unsafe { (*ns).prefix };
if prefix.is_null() {
return ptr::null_mut();
}
unsafe { xml_strdup(prefix as *const xmlChar) }
}
pub const fn AttributeCount(&self) -> c_int {
self.attribute_count
}
pub const fn ReadState(&self) -> ReadState {
self.state
}
pub unsafe fn GetAttribute(&self, name: *const xmlChar) -> *mut xmlChar {
if self.cur_node.is_null() {
return ptr::null_mut();
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
let idx = unsafe { self.attr_index_by_name(self.cur_node, name) };
if idx < 0 {
return ptr::null_mut();
}
match unsafe { self.attr_at(self.cur_node, idx) } {
AttrTarget::Ns(ns) => {
let href = unsafe { (*ns).href };
if href.is_null() {
ptr::null_mut()
} else {
unsafe { xml_strdup(href as *const xmlChar) }
}
}
AttrTarget::Prop(prop) => {
let val = unsafe { (*prop).children };
if !val.is_null() {
let content = unsafe { (*val).content };
if !content.is_null() {
return unsafe { xml_strdup(content as *const xmlChar) };
}
}
ptr::null_mut()
}
AttrTarget::None => ptr::null_mut(),
}
}
pub unsafe fn GetAttributeNo(&self, index: c_int) -> *mut xmlChar {
if self.cur_node.is_null() || index < 0 {
return ptr::null_mut();
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
match unsafe { self.attr_at(self.cur_node, index) } {
AttrTarget::Ns(ns) => {
let href = unsafe { (*ns).href };
if href.is_null() {
ptr::null_mut()
} else {
unsafe { xml_strdup(href as *const xmlChar) }
}
}
AttrTarget::Prop(prop) => {
let val = unsafe { (*prop).children };
if !val.is_null() {
let content = unsafe { (*val).content };
if !content.is_null() {
return unsafe { xml_strdup(content as *const xmlChar) };
}
}
ptr::null_mut()
}
AttrTarget::None => ptr::null_mut(),
}
}
pub unsafe fn GetAttributeNs(
&self,
localName: *const xmlChar,
namespaceURI: *const xmlChar,
) -> *mut xmlChar {
if self.cur_node.is_null() {
return ptr::null_mut();
}
let etype = unsafe { (*self.cur_node).type_ };
if etype != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
let mut prop = unsafe { (*self.cur_node).properties };
while !prop.is_null() {
let prop_local = unsafe { (*prop).name };
let prop_ns = unsafe { (*prop).ns };
if prop_local.is_null() {
prop = unsafe { (*prop).next };
continue;
}
let name_match = unsafe {
crate::xml::string::xml_strcmp(prop_local as *const xmlChar, localName) == 0
};
if name_match {
let ns_match = if namespaceURI.is_null() {
prop_ns.is_null()
} else if prop_ns.is_null() {
false
} else {
unsafe {
crate::xml::string::xml_strcmp(
(*prop_ns).href as *const xmlChar,
namespaceURI,
) == 0
}
};
if ns_match {
let val = unsafe { (*prop).children };
if !val.is_null() {
let content = unsafe { (*val).content };
if !content.is_null() {
return unsafe { xml_strdup(content as *const xmlChar) };
}
}
return ptr::null_mut();
}
}
prop = unsafe { (*prop).next };
}
ptr::null_mut()
}
pub unsafe fn LookupNamespace(&self, prefix: *const xmlChar) -> *mut xmlChar {
if self.cur_node.is_null() {
return ptr::null_mut();
}
let mut cur = self.cur_node;
while !cur.is_null() {
let mut ns_def = unsafe { (*cur).nsDef };
while !ns_def.is_null() {
let ns_prefix = unsafe { (*ns_def).prefix };
let match_prefix = if prefix.is_null() || *prefix == 0 {
ns_prefix.is_null()
} else if ns_prefix.is_null() {
false
} else {
unsafe {
crate::xml::string::xml_strcmp(ns_prefix as *const xmlChar, prefix) == 0
}
};
if match_prefix {
let href = unsafe { (*ns_def).href };
if !href.is_null() {
return unsafe { xml_strdup(href as *const xmlChar) };
}
return ptr::null_mut();
}
ns_def = unsafe { (*ns_def).next };
}
cur = unsafe { (*cur).parent };
}
ptr::null_mut()
}
pub const fn GetParserProp(&self, prop: c_int) -> c_int {
match prop {
1 => {
if (self.options & XML_PARSE_DTDLOAD) != 0 { 1 } else { 0 }
}
2 => {
if (self.options & XML_PARSE_DTDATTR) != 0 { 1 } else { 0 }
}
3 => {
if (self.options & XML_PARSE_DTDVALID) != 0 { 1 } else { 0 }
}
4 => {
if (self.options & XML_PARSE_NOENT) != 0 { 1 } else { 0 }
}
_ => -1,
}
}
pub const fn SetParserProp(&mut self, prop: c_int, value: c_int) -> c_int {
match prop {
1 => {
if value != 0 {
self.options |= XML_PARSE_DTDLOAD;
} else {
self.options &= !XML_PARSE_DTDLOAD;
}
0
}
2 => {
if value != 0 {
self.options |= XML_PARSE_DTDATTR;
} else {
self.options &= !XML_PARSE_DTDATTR;
}
0
}
3 => {
if value != 0 {
self.options |= XML_PARSE_DTDVALID;
} else {
self.options &= !XML_PARSE_DTDVALID;
}
0
}
4 => {
if value != 0 {
self.options |= XML_PARSE_NOENT;
} else {
self.options &= !XML_PARSE_NOENT;
}
0
}
_ => -1,
}
}
pub const fn CurrentDoc(&self) -> *mut _xmlDoc {
self.doc
}
}
impl Drop for XmlTextReader {
fn drop(&mut self) {
self.clear_cached_name();
self.clear_cached_value();
if !self.last_err.message.is_null() {
unsafe { libc::free(self.last_err.message as *mut libc::c_void) };
self.last_err.message = ptr::null_mut();
}
if !self.encoding.is_null() {
unsafe { xmlFreeImpl(self.encoding as *mut c_void) };
self.encoding = ptr::null_mut();
}
if !self.URL.is_null() {
unsafe { xmlFreeImpl(self.URL as *mut c_void) };
self.URL = ptr::null_mut();
}
if !self.doc.is_null() && self.owns_doc {
unsafe { tree::free_doc(self.doc) };
self.doc = ptr::null_mut();
}
if !self.ctxt.is_null() {
unsafe { free_parser_ctxt(self.ctxt) };
self.ctxt = ptr::null_mut();
}
}
}
unsafe fn reader_from_input(
input: *mut _xmlParserInputBuffer,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> *mut XmlTextReader {
if input.is_null() {
return ptr::null_mut();
}
let ctxt = create_parser_ctxt();
if ctxt.is_null() {
return ptr::null_mut();
}
let mut data = Vec::new();
let mut tmp = [0u8; 4096];
let read_cb = unsafe { (*input).readcallback };
let ioctx = unsafe { (*input).context };
if let Some(read) = read_cb {
loop {
let n = unsafe { read(ioctx, tmp.as_mut_ptr() as *mut c_char, tmp.len() as c_int) };
if n <= 0 {
break;
}
data.extend_from_slice(&tmp[..n as usize]);
}
}
let close_cb = unsafe { (*input).closecallback };
if let Some(close) = close_cb {
unsafe { close(ioctx) };
}
let input_buf = InputBuffer::from_memory(&data, None);
setup_parser_input(ctxt, input_buf);
unsafe {
(*ctxt).options = options;
}
let url_bytes = if URL.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(URL);
Some(cstr.to_bytes().to_vec())
}
};
let enc_bytes = if encoding.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(encoding);
Some(cstr.to_bytes().to_vec())
}
};
let mut reader = XmlTextReader::new(ctxt, url_bytes.as_deref(), enc_bytes.as_deref());
reader.options = options;
Box::into_raw(Box::new(reader))
}
#[no_mangle]
pub unsafe extern "C" fn xmlNewTextReader(
input: *mut _xmlParserInputBuffer,
URI: *const c_char,
) -> *mut XmlTextReader {
unsafe { reader_from_input(input, URI, ptr::null(), 0) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderForFile(
filename: *const c_char,
encoding: *const c_char,
options: c_int,
) -> *mut XmlTextReader {
if filename.is_null() {
return ptr::null_mut();
}
let ctxt = unsafe { create_parser_ctxt() };
if ctxt.is_null() {
return ptr::null_mut();
}
let input = match unsafe { input_from_file(filename) } {
Ok(input) => input,
Err(_) => {
unsafe { free_parser_ctxt(ctxt) };
return ptr::null_mut();
}
};
unsafe { setup_parser_input(ctxt, input) };
unsafe {
(*ctxt).options = options;
}
let enc_bytes = if encoding.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(encoding);
Some(cstr.to_bytes().to_vec())
}
};
let mut reader = XmlTextReader::new(ctxt, None, enc_bytes.as_deref());
reader.options = options;
Box::into_raw(Box::new(reader))
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderForMemory(
buffer: *const c_char,
size: c_int,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> *mut XmlTextReader {
if buffer.is_null() || size <= 0 {
return ptr::null_mut();
}
let ctxt = unsafe { create_parser_ctxt() };
if ctxt.is_null() {
return ptr::null_mut();
}
let input = unsafe { input_from_memory_named(buffer, size, URL) };
unsafe { setup_parser_input(ctxt, input) };
unsafe {
(*ctxt).options = options;
}
let url_bytes = if URL.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(URL);
Some(cstr.to_bytes().to_vec())
}
};
let enc_bytes = if encoding.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(encoding);
Some(cstr.to_bytes().to_vec())
}
};
let mut reader = XmlTextReader::new(ctxt, url_bytes.as_deref(), enc_bytes.as_deref());
reader.options = options;
Box::into_raw(Box::new(reader))
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderForFd(
fd: c_int,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> *mut XmlTextReader {
let ctxt = unsafe { create_parser_ctxt() };
if ctxt.is_null() {
return ptr::null_mut();
}
let mut buf = Vec::new();
let mut tmp = [0u8; 4096];
loop {
let n = unsafe { libc::read(fd, tmp.as_mut_ptr() as *mut c_void, tmp.len()) };
if n <= 0 {
break;
}
buf.extend_from_slice(&tmp[..n as usize]);
}
let input = unsafe { input_from_memory(buf.as_ptr() as *const c_char, buf.len() as c_int) };
unsafe { setup_parser_input(ctxt, input) };
unsafe {
(*ctxt).options = options;
}
let url_bytes = if URL.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(URL);
Some(cstr.to_bytes().to_vec())
}
};
let enc_bytes = if encoding.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(encoding);
Some(cstr.to_bytes().to_vec())
}
};
let mut reader = XmlTextReader::new(ctxt, url_bytes.as_deref(), enc_bytes.as_deref());
reader.options = options;
Box::into_raw(Box::new(reader))
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderForIO(
ioread: Option<xmlInputReadCallback>,
ioclose: Option<xmlInputCloseCallback>,
ioctx: *mut c_void,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> *mut XmlTextReader {
let ctxt = unsafe { create_parser_ctxt() };
if ctxt.is_null() {
return ptr::null_mut();
}
let input = unsafe { input_from_io(ioread, ioclose, ioctx) };
unsafe { setup_parser_input(ctxt, input) };
unsafe {
(*ctxt).options = options;
}
let url_bytes = if URL.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(URL);
Some(cstr.to_bytes().to_vec())
}
};
let enc_bytes = if encoding.is_null() {
None
} else {
unsafe {
let cstr = std::ffi::CStr::from_ptr(encoding);
Some(cstr.to_bytes().to_vec())
}
};
let mut reader = XmlTextReader::new(ctxt, url_bytes.as_deref(), enc_bytes.as_deref());
reader.options = options;
Box::into_raw(Box::new(reader))
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderRead(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).Read() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderNext(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).Next() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderNextSibling(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).Next() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderPrev(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).Prev() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderMoveToElement(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).MoveToElement() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderMoveToAttribute(
reader: *mut XmlTextReader,
name: *const xmlChar,
) -> c_int {
if reader.is_null() || name.is_null() {
return -1;
}
unsafe { (*reader).MoveToAttribute(name) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderMoveToAttributeNo(
reader: *mut XmlTextReader,
index: c_int,
) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).MoveToAttributeNo(index) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderMoveToFirstAttribute(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).MoveToFirstAttribute() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderMoveToNextAttribute(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).MoveToNextAttribute() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderAttributeCount(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).AttributeCount() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderDepth(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).Depth() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderNodeType(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).NodeType() as c_int }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderName(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).Name() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderValue(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).Value() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstName(reader: *mut XmlTextReader) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
unsafe { (*reader).ConstName() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstValue(reader: *mut XmlTextReader) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
unsafe { (*reader).ConstValue() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderBaseUri(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).BaseUri() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderLocalName(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).LocalName() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderNamespaceUri(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).NamespaceUri() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderPrefix(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).Prefix() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderHasValue(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return 0;
}
unsafe { (*reader).HasValue() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderHasAttributes(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return 0;
}
unsafe { (*reader).HasAttributes() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderIsEmptyElement(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return 0;
}
unsafe { (*reader).IsEmptyElement() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderReadState(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return ReadState::ERROR as c_int;
}
unsafe { (*reader).ReadState() as c_int }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderGetAttribute(
reader: *mut XmlTextReader,
name: *const xmlChar,
) -> *mut xmlChar {
if reader.is_null() || name.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).GetAttribute(name) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderGetAttributeNo(
reader: *mut XmlTextReader,
index: c_int,
) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).GetAttributeNo(index) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderGetAttributeNs(
reader: *mut XmlTextReader,
localName: *const xmlChar,
namespaceURI: *const xmlChar,
) -> *mut xmlChar {
if reader.is_null() || localName.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).GetAttributeNs(localName, namespaceURI) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderLookupNamespace(
reader: *mut XmlTextReader,
prefix: *const xmlChar,
) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).LookupNamespace(prefix) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderGetParserProp(
reader: *mut XmlTextReader,
prop: c_int,
) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).GetParserProp(prop) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSetParserProp(
reader: *mut XmlTextReader,
prop: c_int,
value: c_int,
) -> c_int {
if reader.is_null() {
return -1;
}
unsafe { (*reader).SetParserProp(prop, value) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlFreeTextReader(reader: *mut XmlTextReader) {
if reader.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(reader);
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSetup(
reader: *mut XmlTextReader,
input: *mut _xmlParserInputBuffer,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> c_int {
if reader.is_null() {
return -1;
}
let r = unsafe { &mut *reader };
r.clear_cached_name();
r.clear_cached_value();
if !r.doc.is_null() {
unsafe { tree::free_doc(r.doc) };
r.doc = ptr::null_mut();
}
if !r.ctxt.is_null() {
unsafe { free_parser_ctxt(r.ctxt) };
r.ctxt = ptr::null_mut();
}
r.events.clear();
r.event_index = 0;
r.state = ReadState::INITIALIZED;
r.cur_node = ptr::null_mut();
r.node_type = ReaderNodeType::NONE;
r.depth = 0;
r.attribute_count = -1;
r.cur_attribute = -1;
r.options = options;
r.parsed = false;
r.errors.clear();
if !r.URL.is_null() {
unsafe { xmlFreeImpl(r.URL as *mut c_void) };
r.URL = ptr::null_mut();
}
if !URL.is_null() {
let url_str = unsafe { std::ffi::CStr::from_ptr(URL) };
r.URL = unsafe { bytes_to_xmlstr(url_str.to_bytes()) };
}
if !r.encoding.is_null() {
unsafe { xmlFreeImpl(r.encoding as *mut c_void) };
r.encoding = ptr::null_mut();
}
if !encoding.is_null() {
let enc_str = unsafe { std::ffi::CStr::from_ptr(encoding) };
r.encoding = unsafe { bytes_to_xmlstr(enc_str.to_bytes()) };
}
if !input.is_null() {
let ctxt = unsafe { create_parser_ctxt() };
if ctxt.is_null() {
return -1;
}
let mut data = Vec::new();
let mut tmp = [0u8; 4096];
let read_cb = unsafe { (*input).readcallback };
let ioctx = unsafe { (*input).context };
if let Some(read) = read_cb {
loop {
let n = unsafe { read(ioctx, tmp.as_mut_ptr() as *mut c_char, tmp.len() as c_int) };
if n <= 0 {
break;
}
data.extend_from_slice(&tmp[..n as usize]);
}
}
let close_cb = unsafe { (*input).closecallback };
if let Some(close) = close_cb {
unsafe { close(ioctx) };
}
let input_buf = InputBuffer::from_memory(&data, None);
unsafe { setup_parser_input(ctxt, input_buf) };
unsafe {
(*ctxt).options = options;
}
r.ctxt = ctxt;
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderCurrentDoc(reader: *mut XmlTextReader) -> *mut _xmlDoc {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).CurrentDoc() }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderClose(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe {
let r = &mut *reader;
r.cur_node = ptr::null_mut();
r.node_type = ReaderNodeType::NONE;
r.clear_cached_name();
r.clear_cached_value();
r.state = ReadState::CLOSED;
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderCurrentNode(reader: *mut XmlTextReader) -> *mut _xmlNode {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).cur_node }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderExpand(reader: *mut XmlTextReader) -> *mut _xmlNode {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).cur_node }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderGetParserLineNumber(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return 0;
}
unsafe {
let node = (*reader).cur_node;
if node.is_null() {
0
} else {
(*node).line as c_int
}
}
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderGetParserColumnNumber(
reader: *mut XmlTextReader,
) -> c_int {
if reader.is_null() {
return -1;
}
-1
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderIsValid(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
0
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderNormalization(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
1
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderReadAttributeValue(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe {
let r = &*reader;
if r.node_type == ReaderNodeType::ATTRIBUTE && !r.cur_node.is_null() {
1
} else {
0
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderReadString(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe {
let node = (*reader).cur_node;
if node.is_null() {
return ptr::null_mut();
}
tree::node_get_content(node)
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderReadInnerXml(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe {
let node = (*reader).cur_node;
if node.is_null() {
return ptr::null_mut();
}
let buf = crate::xml::io::buf_create(-1);
if buf.is_null() {
return ptr::null_mut();
}
let mut child = (*node).children;
while !child.is_null() {
tree::serialize_node(child, buf, 0, 0);
child = (*child).next;
}
let len = crate::xml::io::buf_length(buf) as usize;
let content = crate::xml::io::buf_content(buf);
if content.is_null() || len == 0 {
crate::xml::io::buf_free(buf);
return ptr::null_mut();
}
let out = xml_strdup(content);
crate::xml::io::buf_free(buf);
out
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderReadOuterXml(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe {
let node = (*reader).cur_node;
if node.is_null() {
return ptr::null_mut();
}
let buf = crate::xml::io::buf_create(-1);
if buf.is_null() {
return ptr::null_mut();
}
tree::serialize_node(node, buf, 0, 0);
let len = crate::xml::io::buf_length(buf) as usize;
let content = crate::xml::io::buf_content(buf);
if content.is_null() || len == 0 {
crate::xml::io::buf_free(buf);
return ptr::null_mut();
}
let out = xml_strdup(content);
crate::xml::io::buf_free(buf);
out
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderStandalone(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
unsafe {
let doc = (*reader).doc;
if doc.is_null() {
return -1;
}
(*doc).standalone
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderXmlLang(reader: *mut XmlTextReader) -> *mut xmlChar {
if reader.is_null() {
return ptr::null_mut();
}
unsafe {
let mut node = (*reader).cur_node;
while !node.is_null() {
let mut prop = (*node).properties;
while !prop.is_null() {
if !(*prop).name.is_null() {
let name = crate::xml::string::xmlstr_to_bytes((*prop).name);
if name == b"lang" && !(*prop).ns.is_null() {
let ns_href = crate::xml::string::xmlstr_to_bytes((*(*prop).ns).href);
if ns_href == b"http://www.w3.org/XML/1998/namespace" {
let v = (*prop).children;
if !v.is_null() && !(*v).content.is_null() {
return xml_strdup((*v).content);
}
}
}
}
prop = (*prop).next;
}
node = (*node).parent;
}
ptr::null_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::allocator::xmlFreeImpl;
use core::ffi::c_void;
use std::os::raw::c_char;
unsafe fn create_reader(xml: &str) -> *mut XmlTextReader {
let bytes = xml.as_bytes();
xmlReaderForMemory(
bytes.as_ptr() as *const c_char,
bytes.len() as c_int,
ptr::null(),
ptr::null(),
0,
)
}
unsafe fn free_reader(reader: *mut XmlTextReader) {
if !reader.is_null() {
xmlFreeTextReader(reader);
}
}
unsafe fn collect_nodes(reader: *mut XmlTextReader) -> Vec<(ReaderNodeType, String, i32)> {
let mut result = Vec::new();
loop {
let ret = xmlTextReaderRead(reader);
if ret <= 0 {
break;
}
let r = &*reader;
let ntype = r.NodeType();
let name = if r.name.is_null() {
String::new()
} else {
xmlstr_to_string(r.name as *const xmlChar)
};
let depth = r.Depth();
result.push((ntype, name, depth));
}
result
}
#[test]
fn test_create_reader_from_memory() {
unsafe {
let reader = create_reader("<root/>");
assert!(!reader.is_null());
assert_eq!((*reader).ReadState(), ReadState::INITIALIZED);
free_reader(reader);
}
}
#[test]
fn test_read_simple_document() {
unsafe {
let reader = create_reader("<root><child>text</child></root>");
assert!(!reader.is_null());
let nodes = collect_nodes(reader);
assert_eq!(nodes.len(), 5);
assert_eq!(nodes[0], (ReaderNodeType::ELEMENT, "root".to_string(), 0));
assert_eq!(nodes[1], (ReaderNodeType::ELEMENT, "child".to_string(), 1));
assert_eq!(nodes[2], (ReaderNodeType::TEXT, "#text".to_string(), 2));
assert_eq!(
nodes[3],
(ReaderNodeType::END_ELEMENT, "child".to_string(), 1)
);
assert_eq!(
nodes[4],
(ReaderNodeType::END_ELEMENT, "root".to_string(), 0)
);
assert_eq!((*reader).ReadState(), ReadState::EOF);
free_reader(reader);
}
}
#[test]
fn test_read_state_transitions() {
unsafe {
let reader = create_reader("<root/>");
assert!(!reader.is_null());
assert_eq!((*reader).ReadState(), ReadState::INITIALIZED);
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).ReadState(), ReadState::READING);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!((*reader).Depth(), 0);
assert_eq!(xmlTextReaderRead(reader), 0);
assert_eq!((*reader).ReadState(), ReadState::EOF);
free_reader(reader);
}
}
#[test]
fn test_null_reader_returns_error() {
unsafe {
assert_eq!(xmlTextReaderRead(ptr::null_mut()), -1);
assert_eq!(xmlTextReaderDepth(ptr::null_mut()), -1);
assert_eq!(xmlTextReaderNodeType(ptr::null_mut()), -1);
assert!(xmlTextReaderName(ptr::null_mut()).is_null());
assert!(xmlTextReaderValue(ptr::null_mut()).is_null());
assert_eq!(xmlTextReaderHasValue(ptr::null_mut()), 0);
assert_eq!(xmlTextReaderIsEmptyElement(ptr::null_mut()), 0);
assert_eq!(
xmlTextReaderReadState(ptr::null_mut()),
ReadState::ERROR as c_int
);
}
}
#[test]
fn test_xmlFreeTextReader_null() {
unsafe {
xmlFreeTextReader(ptr::null_mut());
}
}
#[test]
fn test_reader_name_and_value() {
unsafe {
let reader = create_reader("<root>hello</root>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
let name = xmlTextReaderName(reader);
assert!(!name.is_null());
assert_eq!(xmlstr_to_string(name), "root");
xmlFreeImpl(name as *mut c_void);
assert_eq!(xmlTextReaderHasValue(reader), 0);
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::TEXT);
assert_eq!((*reader).HasValue(), 1);
let val = xmlTextReaderValue(reader);
assert!(!val.is_null());
assert_eq!(xmlstr_to_string(val), "hello");
xmlFreeImpl(val as *mut c_void);
free_reader(reader);
}
}
#[test]
fn test_empty_element() {
unsafe {
let reader = create_reader("<empty/>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!((*reader).IsEmptyElement(), 1);
assert_eq!((*reader).HasAttributes(), 0);
assert_eq!((*reader).AttributeCount(), 0);
assert_eq!(xmlTextReaderRead(reader), 0);
assert_eq!((*reader).ReadState(), ReadState::EOF);
free_reader(reader);
}
}
#[test]
fn test_element_with_attributes() {
unsafe {
let reader = create_reader(r#"<root a="1" b="2"/>"#);
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!((*reader).HasAttributes(), 1);
let attrs = xmlTextReaderAttributeCount(reader);
assert_eq!(attrs, 2);
free_reader(reader);
}
}
#[test]
fn test_attribute_navigation() {
unsafe {
let reader = create_reader(r#"<root a="1" b="2"></root>"#);
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(xmlTextReaderMoveToFirstAttribute(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ATTRIBUTE);
let name = xmlTextReaderConstName(reader);
assert!(!name.is_null());
assert_eq!(xmlstr_to_bytes(name), b"a");
let val = xmlTextReaderConstValue(reader);
assert!(!val.is_null());
assert_eq!(xmlstr_to_bytes(val), b"1");
assert_eq!(xmlTextReaderMoveToNextAttribute(reader), 1);
let name = xmlTextReaderConstName(reader);
assert!(!name.is_null());
assert_eq!(xmlstr_to_bytes(name), b"b");
let val = xmlTextReaderConstValue(reader);
assert!(!val.is_null());
assert_eq!(xmlstr_to_bytes(val), b"2");
assert_eq!(xmlTextReaderMoveToNextAttribute(reader), 0);
assert_eq!(xmlTextReaderMoveToElement(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(
xmlTextReaderMoveToAttribute(reader, b"a\0" as *const u8 as *const xmlChar),
1
);
assert_eq!((*reader).NodeType(), ReaderNodeType::ATTRIBUTE);
assert_eq!(xmlTextReaderMoveToElement(reader), 1);
assert_eq!(xmlTextReaderMoveToAttributeNo(reader, 1), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ATTRIBUTE);
free_reader(reader);
}
}
#[test]
fn test_get_attribute() {
unsafe {
let reader = create_reader(r#"<root a="hello" b="world"/>"#);
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
let val = xmlTextReaderGetAttribute(reader, b"a\0" as *const u8 as *const xmlChar);
assert!(!val.is_null());
assert_eq!(xmlstr_to_bytes(val), b"hello");
xmlFreeImpl(val as *mut c_void);
let val = xmlTextReaderGetAttribute(reader, b"b\0" as *const u8 as *const xmlChar);
assert!(!val.is_null());
assert_eq!(xmlstr_to_bytes(val), b"world");
xmlFreeImpl(val as *mut c_void);
let val = xmlTextReaderGetAttribute(reader, b"c\0" as *const u8 as *const xmlChar);
assert!(val.is_null());
let val = xmlTextReaderGetAttributeNo(reader, 0);
assert!(!val.is_null());
assert_eq!(xmlstr_to_bytes(val), b"hello");
xmlFreeImpl(val as *mut c_void);
let val = xmlTextReaderGetAttributeNo(reader, 1);
assert!(!val.is_null());
assert_eq!(xmlstr_to_bytes(val), b"world");
xmlFreeImpl(val as *mut c_void);
let val = xmlTextReaderGetAttributeNo(reader, 2);
assert!(val.is_null());
free_reader(reader);
}
}
#[test]
fn test_depth_tracking() {
unsafe {
let reader = create_reader("<a><b><c/></b></a>");
assert!(!reader.is_null());
let nodes = collect_nodes(reader);
assert_eq!(nodes.len(), 5);
assert_eq!(nodes[0].2, 0); assert_eq!(nodes[1].2, 1); assert_eq!(nodes[2].2, 2); assert_eq!(nodes[3].2, 1); assert_eq!(nodes[4].2, 0);
free_reader(reader);
}
}
#[test]
fn test_multiple_siblings() {
unsafe {
let reader = create_reader("<root><a>A</a><b>B</b><c>C</c></root>");
assert!(!reader.is_null());
let nodes = collect_nodes(reader);
assert_eq!(nodes.len(), 11);
assert_eq!(nodes[1], (ReaderNodeType::ELEMENT, "a".to_string(), 1));
assert_eq!(nodes[4], (ReaderNodeType::ELEMENT, "b".to_string(), 1));
assert_eq!(nodes[7], (ReaderNodeType::ELEMENT, "c".to_string(), 1));
free_reader(reader);
}
}
#[test]
fn test_next_skip_to_sibling() {
unsafe {
let reader = create_reader("<root><a>A</a><b>B</b><c>C</c></root>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(xmlstr_to_string((*reader).name as *const xmlChar), "a");
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::TEXT);
assert_eq!(xmlTextReaderNext(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(xmlstr_to_string((*reader).name as *const xmlChar), "b");
assert_eq!(xmlTextReaderNext(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(xmlstr_to_string((*reader).name as *const xmlChar), "c");
assert_eq!(xmlTextReaderNext(reader), 0);
free_reader(reader);
}
}
#[test]
fn test_comment_and_pi_nodes() {
unsafe {
let xml = b"<?pi target?><root><!-- comment -->text</root>\0";
let reader = xmlReaderForMemory(
xml.as_ptr() as *const c_char,
(xml.len() - 1) as c_int,
ptr::null(),
ptr::null(),
0,
);
assert!(!reader.is_null());
let nodes = collect_nodes(reader);
assert!(!nodes.is_empty(), "no nodes collected: {:?}", nodes);
assert_eq!(
nodes[0].0,
ReaderNodeType::PROCESSING_INSTRUCTION,
"expected PI at nodes[0], got {:?} name={}",
nodes[0].0,
nodes[0].1
);
assert_eq!(
nodes[0].0,
ReaderNodeType::PROCESSING_INSTRUCTION,
"expected PI at nodes[0], got {:?} name={}",
nodes[0].0,
nodes[0].1
);
let root_idx = nodes
.iter()
.position(|(t, n, _)| *t == ReaderNodeType::ELEMENT && n == "root");
assert!(
root_idx.is_some(),
"no ELEMENT root found in nodes: {:?}",
nodes
.iter()
.map(|(t, n, _)| format!("{:?}:{}", t, n))
.collect::<Vec<_>>()
);
let comment_idx = nodes
.iter()
.position(|(t, _, _)| *t == ReaderNodeType::COMMENT);
assert!(comment_idx.is_some(), "no COMMENT found");
let text_idx = nodes
.iter()
.position(|(t, _, _)| *t == ReaderNodeType::TEXT);
assert!(text_idx.is_some(), "no TEXT found");
free_reader(reader);
}
}
#[test]
fn test_local_name() {
unsafe {
let reader = create_reader("<root/>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
let local = xmlTextReaderLocalName(reader);
assert!(!local.is_null());
assert_eq!(xmlstr_to_bytes(local), b"root");
xmlFreeImpl(local as *mut c_void);
free_reader(reader);
}
}
#[test]
fn test_base_uri() {
unsafe {
let reader = create_reader("<root/>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
let uri = xmlTextReaderBaseUri(reader);
assert!(uri.is_null());
free_reader(reader);
}
}
#[test]
fn test_lookup_namespace() {
unsafe {
let reader = create_reader(r#"<root xmlns:ns="http://example.com"><ns:child/></root>"#);
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(xmlTextReaderRead(reader), 1);
let uri = xmlTextReaderLookupNamespace(reader, b"ns\0" as *const u8 as *const xmlChar);
assert!(!uri.is_null());
assert_eq!(xmlstr_to_bytes(uri), b"http://example.com");
xmlFreeImpl(uri as *mut c_void);
let uri = xmlTextReaderLookupNamespace(reader, ptr::null());
assert!(uri.is_null());
let uri = xmlTextReaderLookupNamespace(
reader,
b"nonexistent\0" as *const u8 as *const xmlChar,
);
assert!(uri.is_null());
free_reader(reader);
}
}
#[test]
fn test_parser_properties() {
unsafe {
let reader = create_reader("<root/>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderGetParserProp(reader, 1), 0); assert_eq!(xmlTextReaderGetParserProp(reader, 2), 0); assert_eq!(xmlTextReaderGetParserProp(reader, 3), 0); assert_eq!(xmlTextReaderGetParserProp(reader, 4), 0);
assert_eq!(xmlTextReaderSetParserProp(reader, 1, 1), 0);
assert_eq!(xmlTextReaderGetParserProp(reader, 1), 1);
assert_eq!(xmlTextReaderSetParserProp(reader, 4, 1), 0);
assert_eq!(xmlTextReaderGetParserProp(reader, 4), 1);
assert_eq!(xmlTextReaderGetParserProp(reader, 99), -1);
assert_eq!(xmlTextReaderSetParserProp(reader, 99, 1), -1);
free_reader(reader);
}
}
#[test]
fn test_current_doc() {
unsafe {
let reader = create_reader("<root/>");
assert!(!reader.is_null());
assert!((*reader).CurrentDoc().is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
let doc = xmlTextReaderCurrentDoc(reader);
assert!(!doc.is_null());
free_reader(reader);
}
}
#[test]
fn test_free_reader_after_read() {
unsafe {
let reader = create_reader("<root><child/></root>");
assert!(!reader.is_null());
while xmlTextReaderRead(reader) > 0 {}
assert_eq!((*reader).ReadState(), ReadState::EOF);
free_reader(reader);
}
}
#[test]
fn test_reader_for_memory_null_buffer() {
unsafe {
let reader = xmlReaderForMemory(ptr::null(), 10, ptr::null(), ptr::null(), 0);
assert!(reader.is_null());
}
}
#[test]
fn test_reader_for_memory_empty_size() {
unsafe {
let data = b"<root/>";
let reader = xmlReaderForMemory(
data.as_ptr() as *const c_char,
0,
ptr::null(),
ptr::null(),
0,
);
assert!(reader.is_null());
}
}
#[test]
fn test_reader_for_file_not_found() {
unsafe {
let filename = b"/nonexistent/file.xml\0" as *const u8 as *const c_char;
let reader = xmlReaderForFile(filename, ptr::null(), 0);
assert!(reader.is_null());
}
}
#[test]
fn test_const_name_and_value() {
unsafe {
let reader = create_reader("<root>text</root>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
let cname = xmlTextReaderConstName(reader);
assert!(!cname.is_null());
assert_eq!(xmlstr_to_bytes(cname), b"root");
assert_eq!(xmlTextReaderRead(reader), 1);
let cval = xmlTextReaderConstValue(reader);
assert!(!cval.is_null());
assert_eq!(xmlstr_to_bytes(cval), b"text");
free_reader(reader);
}
}
#[test]
fn test_complex_nested_document() {
unsafe {
let xml = r#"<?xml version="1.0"?>
<library>
<book id="1">
<title>XML Fundamentals</title>
<author>John Doe</author>
</book>
<book id="2">
<title>XSLT Recipes</title>
<author>Jane Smith</author>
</book>
</library>"#;
let reader = create_reader(xml);
assert!(!reader.is_null());
let mut element_count = 0;
let mut end_element_count = 0;
let mut text_count = 0;
let mut pi_count = 0;
loop {
let ret = xmlTextReaderRead(reader);
if ret <= 0 {
break;
}
match (*reader).NodeType() {
ReaderNodeType::ELEMENT => element_count += 1,
ReaderNodeType::END_ELEMENT => end_element_count += 1,
ReaderNodeType::TEXT => text_count += 1,
ReaderNodeType::PROCESSING_INSTRUCTION => pi_count += 1,
_ => {}
}
}
assert_eq!(element_count, 7);
assert_eq!(end_element_count, 7);
assert_eq!(text_count, 4);
assert_eq!(pi_count, 0);
free_reader(reader);
}
}
#[test]
fn test_setup_reinitialize() {
unsafe {
let reader = create_reader("<root/>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).ReadState(), ReadState::READING);
assert_eq!(
xmlTextReaderSetup(reader, ptr::null_mut(), ptr::null(), ptr::null(), 0),
0
);
assert_eq!((*reader).ReadState(), ReadState::INITIALIZED);
free_reader(reader);
}
}
#[test]
fn test_has_attributes_on_non_element() {
unsafe {
let reader = create_reader("<root>text</root>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1); assert_eq!((*reader).HasAttributes(), 0); assert_eq!(xmlTextReaderRead(reader), 1); assert_eq!((*reader).HasAttributes(), 0);
free_reader(reader);
}
}
#[test]
fn test_prev_sibling() {
unsafe {
let reader = create_reader("<root><a/><b/><c/></root>");
assert!(!reader.is_null());
while xmlTextReaderRead(reader) > 0 {
}
assert_eq!(xmlTextReaderPrev(reader), -1);
free_reader(reader);
}
}
#[test]
fn test_move_to_attribute_no_not_on_element() {
unsafe {
let reader = create_reader("<root>text</root>");
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1); assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
assert_eq!(xmlTextReaderMoveToAttributeNo(reader, 0), 0);
free_reader(reader);
}
}
#[test]
fn test_get_attribute_ns() {
unsafe {
let reader = create_reader(r#"<root a="1" b="2"/>"#);
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
let val = xmlTextReaderGetAttributeNs(
reader,
b"a\0" as *const u8 as *const xmlChar,
ptr::null(),
);
assert!(!val.is_null());
assert_eq!(xmlstr_to_bytes(val), b"1");
xmlFreeImpl(val as *mut c_void);
free_reader(reader);
}
}
#[test]
fn test_mixed_content() {
unsafe {
let reader = create_reader("<root>before<child/>after</root>");
assert!(!reader.is_null());
let nodes = collect_nodes(reader);
assert_eq!(nodes.len(), 5);
assert_eq!(nodes[0], (ReaderNodeType::ELEMENT, "root".to_string(), 0));
assert_eq!(nodes[1].0, ReaderNodeType::TEXT);
assert_eq!(nodes[2], (ReaderNodeType::ELEMENT, "child".to_string(), 1));
assert_eq!(nodes[3].0, ReaderNodeType::TEXT);
free_reader(reader);
}
}
#[test]
fn test_error_handling_invalid_xml() {
unsafe {
let data = b"<root><\0" as *const u8 as *const c_char;
let reader = xmlReaderForMemory(data, 7, ptr::null(), ptr::null(), 0);
assert!(!reader.is_null());
let ret = xmlTextReaderRead(reader);
assert!(ret == -1 || ret == 0);
free_reader(reader);
}
}
#[test]
fn test_reader_with_options() {
unsafe {
let data = b"<root/>\0" as *const u8 as *const c_char;
let reader = xmlReaderForMemory(
data,
7,
ptr::null(),
ptr::null(),
XML_PARSE_NOENT | XML_PARSE_DTDLOAD,
);
assert!(!reader.is_null());
assert_eq!((*reader).options & XML_PARSE_NOENT, XML_PARSE_NOENT);
assert_eq!((*reader).options & XML_PARSE_DTDLOAD, XML_PARSE_DTDLOAD);
assert_eq!(xmlTextReaderRead(reader), 1);
free_reader(reader);
}
}
#[test]
fn test_reader_for_fd() {
unsafe {
let tmp_path = "/tmp/libxml_rs_test_reader_fd.xml";
let tmp_cstr = std::ffi::CString::new(tmp_path).unwrap();
let content = b"<root><data/></root>";
let fd = libc::open(
tmp_cstr.as_ptr(),
libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC,
0o644,
);
assert!(fd >= 0);
libc::write(fd, content.as_ptr() as *const c_void, content.len());
libc::close(fd);
let fd = libc::open(tmp_cstr.as_ptr(), libc::O_RDONLY, 0);
assert!(fd >= 0);
let reader = xmlReaderForFd(fd, ptr::null(), ptr::null(), 0);
assert!(!reader.is_null());
let nodes = collect_nodes(reader);
assert_eq!(nodes.len(), 3);
free_reader(reader);
libc::close(fd);
std::fs::remove_file(tmp_path).ok();
}
}
#[test]
fn test_reader_for_io() {
unsafe {
extern "C" fn io_read(context: *mut c_void, buffer: *mut c_char, len: c_int) -> c_int {
if context.is_null() || buffer.is_null() || len <= 0 {
return -1;
}
let ctx = unsafe { &mut *(context as *mut IoCtx) };
if ctx.pos >= ctx.data.len() {
return 0;
}
let remaining = ctx.data.len() - ctx.pos;
let to_copy = if (remaining as c_int) < len {
remaining
} else {
len as usize
};
unsafe {
std::ptr::copy_nonoverlapping(
ctx.data.as_ptr().add(ctx.pos),
buffer as *mut u8,
to_copy,
);
}
ctx.pos += to_copy;
to_copy as c_int
}
extern "C" fn io_close(_context: *mut c_void) -> c_int {
0
}
struct IoCtx {
data: &'static [u8],
pos: usize,
}
let mut ctx = IoCtx {
data: b"<root/>",
pos: 0,
};
let reader = xmlReaderForIO(
Some(io_read),
Some(io_close),
&mut ctx as *mut IoCtx as *mut c_void,
ptr::null(),
ptr::null(),
0,
);
assert!(!reader.is_null());
assert_eq!(xmlTextReaderRead(reader), 1);
assert_eq!((*reader).NodeType(), ReaderNodeType::ELEMENT);
let cname = xmlTextReaderConstName(reader);
assert!(!cname.is_null());
assert_eq!(xmlstr_to_bytes(cname), b"root");
assert_eq!(xmlTextReaderRead(reader), 0);
free_reader(reader);
}
}
}
pub const XML_PARSER_SEVERITY_VALIDITY_WARNING: c_int = 1;
pub const XML_PARSER_SEVERITY_VALIDITY_ERROR: c_int = 2;
pub const XML_PARSER_SEVERITY_WARNING: c_int = 3;
pub const XML_PARSER_SEVERITY_ERROR: c_int = 4;
#[derive(Debug)]
#[repr(C)]
pub struct XmlTextReaderLocator {
pub reader: *mut XmlTextReader,
}
pub type xmlTextReaderErrorFunc = unsafe extern "C" fn(
arg: *mut c_void,
msg: *const c_char,
severity: c_int,
locator: *mut XmlTextReaderLocator,
);
#[no_mangle]
pub unsafe extern "C" fn xmlReaderForDoc(
cur: *const xmlChar,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> *mut XmlTextReader {
if cur.is_null() {
return ptr::null_mut();
}
let len = unsafe { libc::strlen(cur as *const libc::c_char) } as c_int;
unsafe { xmlReaderForMemory(cur as *const c_char, len, URL, encoding, options) }
}
#[no_mangle]
pub unsafe extern "C" fn xmlNewTextReaderFilename(URI: *const c_char) -> *mut XmlTextReader {
unsafe { xmlReaderForFile(URI, ptr::null(), 0) }
}
unsafe fn reader_renew(reader: *mut XmlTextReader, new_reader: *mut XmlTextReader) {
debug_assert!(!reader.is_null() && !new_reader.is_null() && reader != new_reader);
unsafe {
core::ptr::drop_in_place(reader);
core::ptr::copy_nonoverlapping(new_reader, reader, 1);
let layout = std::alloc::Layout::new::<XmlTextReader>();
std::alloc::dealloc(new_reader as *mut u8, layout);
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderNewDoc(
reader: *mut XmlTextReader,
cur: *const xmlChar,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> c_int {
if reader.is_null() || cur.is_null() {
return -1;
}
let r = unsafe { xmlReaderForDoc(cur, URL, encoding, options) };
if r.is_null() {
return -1;
}
unsafe { reader_renew(reader, r) };
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderNewFile(
reader: *mut XmlTextReader,
filename: *const c_char,
encoding: *const c_char,
options: c_int,
) -> c_int {
if reader.is_null() {
return -1;
}
let r = unsafe { xmlReaderForFile(filename, encoding, options) };
if r.is_null() {
return -1;
}
unsafe { reader_renew(reader, r) };
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderNewMemory(
reader: *mut XmlTextReader,
buffer: *const c_char,
size: c_int,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> c_int {
if reader.is_null() || buffer.is_null() {
return -1;
}
let r = unsafe { xmlReaderForMemory(buffer, size, URL, encoding, options) };
if r.is_null() {
return -1;
}
unsafe { reader_renew(reader, r) };
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderNewFd(
reader: *mut XmlTextReader,
fd: c_int,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> c_int {
if reader.is_null() {
return -1;
}
let r = unsafe { xmlReaderForFd(fd, URL, encoding, options) };
if r.is_null() {
return -1;
}
unsafe { reader_renew(reader, r) };
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderNewIO(
reader: *mut XmlTextReader,
ioread: Option<xmlInputReadCallback>,
ioclose: Option<xmlInputCloseCallback>,
ioctx: *mut c_void,
URL: *const c_char,
encoding: *const c_char,
options: c_int,
) -> c_int {
if reader.is_null() || ioread.is_none() {
return -1;
}
let r = unsafe { xmlReaderForIO(ioread, ioclose, ioctx, URL, encoding, options) };
if r.is_null() {
return -1;
}
unsafe { reader_renew(reader, r) };
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderWalker(doc: *mut _xmlDoc) -> *mut XmlTextReader {
if doc.is_null() {
return ptr::null_mut();
}
let mut reader = XmlTextReader::new(ptr::null_mut(), None, None);
reader.doc = doc;
reader.parsed = true;
reader.owns_doc = false; reader.state = ReadState::READING;
reader.build_events();
Box::into_raw(Box::new(reader))
}
#[no_mangle]
pub unsafe extern "C" fn xmlReaderNewWalker(
reader: *mut XmlTextReader,
doc: *mut _xmlDoc,
) -> c_int {
if reader.is_null() || doc.is_null() {
return -1;
}
let r = unsafe { xmlReaderWalker(doc) };
if r.is_null() {
return -1;
}
unsafe { reader_renew(reader, r) };
0
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderByteConsumed(reader: *mut XmlTextReader) -> c_long {
if reader.is_null() {
return -1;
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstBaseUri(reader: *mut XmlTextReader) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
unsafe { (*reader).URL }
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstEncoding(reader: *mut XmlTextReader) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
let r = unsafe { &*reader };
if !r.encoding.is_null() {
return r.encoding;
}
if !r.doc.is_null() {
return unsafe { (*r.doc).encoding };
}
ptr::null()
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstLocalName(reader: *mut XmlTextReader) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
let r = unsafe { &*reader };
if r.cur_node.is_null() {
return ptr::null();
}
if r.cur_attribute >= 0 {
let target = unsafe { r.attr_at(r.cur_node, r.cur_attribute) };
return match target {
AttrTarget::Ns(ns) => {
if ns.is_null() {
ptr::null()
} else if unsafe { (*ns).prefix }.is_null() {
c"xmlns".as_ptr() as *const xmlChar
} else {
unsafe { (*ns).prefix }
}
}
AttrTarget::Prop(p) => {
if p.is_null() || unsafe { (*p).name }.is_null() {
ptr::null()
} else {
unsafe { (*p).name }
}
}
AttrTarget::None => ptr::null(),
};
}
let etype = unsafe { (*r.cur_node).type_ };
if etype == XML_ELEMENT_NODE as c_int || etype == XML_ATTRIBUTE_NODE as c_int {
unsafe { (*r.cur_node).name }
} else {
ptr::null()
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstNamespaceUri(
reader: *mut XmlTextReader,
) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
let r = unsafe { &*reader };
if r.cur_node.is_null() {
return ptr::null();
}
if r.cur_attribute >= 0 {
let target = unsafe { r.attr_at(r.cur_node, r.cur_attribute) };
return match target {
AttrTarget::Ns(_ns) => {
c"http://www.w3.org/2000/xmlns/".as_ptr() as *const xmlChar
}
AttrTarget::Prop(p) => {
if p.is_null() || unsafe { (*p).ns }.is_null() {
ptr::null()
} else {
unsafe { (*(*p).ns).href }
}
}
AttrTarget::None => ptr::null(),
};
}
let ns = unsafe { (*r.cur_node).ns };
if ns.is_null() || unsafe { (*ns).href }.is_null() {
ptr::null()
} else {
unsafe { (*ns).href }
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstPrefix(reader: *mut XmlTextReader) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
let r = unsafe { &*reader };
if r.cur_node.is_null() {
return ptr::null();
}
if r.cur_attribute >= 0 {
let target = unsafe { r.attr_at(r.cur_node, r.cur_attribute) };
return match target {
AttrTarget::Ns(ns) => {
if ns.is_null() || unsafe { (*ns).prefix }.is_null() {
ptr::null()
} else {
c"xmlns".as_ptr() as *const xmlChar
}
}
AttrTarget::Prop(p) => {
if p.is_null() || unsafe { (*p).ns }.is_null() {
ptr::null()
} else {
unsafe { (*(*p).ns).prefix }
}
}
AttrTarget::None => ptr::null(),
};
}
let ns = unsafe { (*r.cur_node).ns };
if ns.is_null() || unsafe { (*ns).prefix }.is_null() {
ptr::null()
} else {
unsafe { (*ns).prefix }
}
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderConstString(
_reader: *mut XmlTextReader,
str: *const xmlChar,
) -> *const xmlChar {
str
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstXmlLang(reader: *mut XmlTextReader) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
let r = unsafe { &*reader };
let mut node = r.cur_node;
while !node.is_null() {
let mut prop = unsafe { (*node).properties };
while !prop.is_null() {
let p = unsafe { &*prop };
if !p.name.is_null()
&& unsafe { *p.name } == b'x'
&& unsafe { *p.name.add(1) } == b'm'
&& unsafe { *p.name.add(2) } == b'l'
&& unsafe { *p.name.add(3) } == b':'
&& unsafe { *p.name.add(4) } == b'l'
&& unsafe { *p.name.add(5) } == b'a'
&& unsafe { *p.name.add(6) } == b'n'
&& unsafe { *p.name.add(7) } == b'g'
&& unsafe { *p.name.add(8) } == 0
{
if !p.children.is_null() {
let txt = p.children;
if unsafe { (*txt).type_ }
== crate::abi::types::xmlElementType::XML_TEXT_NODE as c_int
{
return unsafe { (*txt).content };
}
}
return ptr::null();
}
prop = p.next;
}
node = unsafe { (*node).parent };
}
ptr::null()
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderConstXmlVersion(
reader: *mut XmlTextReader,
) -> *const xmlChar {
if reader.is_null() {
return ptr::null();
}
let r = unsafe { &*reader };
if r.doc.is_null() {
return ptr::null();
}
unsafe { (*r.doc).version }
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderQuoteChar(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
b'"' as c_int
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderIsDefault(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderIsNamespaceDecl(reader: *mut XmlTextReader) -> c_int {
if reader.is_null() {
return -1;
}
let r = unsafe { &*reader };
if r.cur_node.is_null() {
return -1;
}
r.cur_attr_is_ns as c_int
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderMoveToAttributeNs(
reader: *mut XmlTextReader,
localName: *const xmlChar,
namespaceURI: *const xmlChar,
) -> c_int {
if reader.is_null() || localName.is_null() || namespaceURI.is_null() {
return -1;
}
let r = unsafe { &mut *reader };
let node = r.cur_node;
if node.is_null() {
return -1;
}
if unsafe { (*node).type_ } != XML_ELEMENT_NODE as c_int {
return 0;
}
const XMLNS_URI: &[u8] = b"http://www.w3.org/2000/xmlns/\0";
if libc::strcmp(
namespaceURI as *const libc::c_char,
XMLNS_URI.as_ptr() as *const libc::c_char,
) == 0
{
let is_default = libc::strcmp(
localName as *const libc::c_char,
c"xmlns".as_ptr() as *const libc::c_char,
) == 0;
let mut ns = unsafe { (*node).nsDef };
let mut index = 0;
while !ns.is_null() {
let n = unsafe { &*ns };
let prefix_match = if is_default {
n.prefix.is_null()
} else {
!n.prefix.is_null()
&& libc::strcmp(
n.prefix as *const libc::c_char,
localName as *const libc::c_char,
) == 0
};
if prefix_match {
r.cur_attribute = index;
r.node_type = ReaderNodeType::ATTRIBUTE;
r.cache_attribute_info(AttrTarget::Ns(ns));
return 1;
}
index += 1;
ns = unsafe { (*ns).next };
}
return 0;
}
let mut prop = unsafe { (*node).properties };
let mut index = 0;
let mut ns_count = 0;
let mut ns = unsafe { (*node).nsDef };
while !ns.is_null() {
ns_count += 1;
ns = unsafe { (*ns).next };
}
while !prop.is_null() {
let p = unsafe { &*prop };
if !p.name.is_null()
&& !p.ns.is_null()
&& !(*p.ns).href.is_null()
&& libc::strcmp(
p.name as *const libc::c_char,
localName as *const libc::c_char,
) == 0
&& libc::strcmp(
(*p.ns).href as *const libc::c_char,
namespaceURI as *const libc::c_char,
) == 0
{
r.cur_attribute = ns_count + index;
r.node_type = ReaderNodeType::ATTRIBUTE;
r.cache_attribute_info(AttrTarget::Prop(prop));
return 1;
}
index += 1;
prop = unsafe { (*prop).next };
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderPreserve(reader: *mut XmlTextReader) -> *mut _xmlNode {
if reader.is_null() {
return ptr::null_mut();
}
unsafe { (*reader).cur_node }
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderPreservePattern(
reader: *mut XmlTextReader,
_pattern: *const xmlChar,
_namespaces: *mut *const xmlChar,
) -> c_int {
if reader.is_null() {
return -1;
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSetErrorHandler(
reader: *mut XmlTextReader,
f: Option<xmlTextReaderErrorFunc>,
arg: *mut c_void,
) {
if reader.is_null() {
return;
}
unsafe {
(*reader).error_handler = f;
(*reader).error_arg = arg;
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderGetErrorHandler(
reader: *mut XmlTextReader,
f: *mut Option<xmlTextReaderErrorFunc>,
arg: *mut *mut c_void,
) {
if reader.is_null() {
return;
}
unsafe {
if !f.is_null() {
*f = (*reader).error_handler;
}
if !arg.is_null() {
*arg = (*reader).error_arg;
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSetStructuredErrorHandler(
reader: *mut XmlTextReader,
f: Option<crate::abi::callbacks::xmlStructuredErrorFunc>,
arg: *mut c_void,
) {
if reader.is_null() {
return;
}
unsafe {
(*reader).structured_handler = f;
(*reader).structured_arg = arg;
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSetResourceLoader(
reader: *mut XmlTextReader,
loader: Option<crate::abi::callbacks::xmlResourceLoader>,
data: *mut c_void,
) {
if reader.is_null() {
return;
}
unsafe {
if !(*reader).ctxt.is_null() {
crate::abi::exports_parserint::xmlCtxtSetResourceLoader((*reader).ctxt, loader, data);
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderGetLastError(
reader: *mut XmlTextReader,
) -> *const crate::abi::structs::_xmlError {
if reader.is_null() {
return ptr::null();
}
let r = unsafe { &mut *reader };
if let Some(msg) = r.errors.last() {
unsafe {
let bytes = msg.as_bytes();
let m = libc::malloc(bytes.len() + 1) as *mut xmlChar;
if !m.is_null() {
libc::memcpy(
m as *mut libc::c_void,
bytes.as_ptr() as *const libc::c_void,
bytes.len(),
);
*m.add(bytes.len()) = 0;
if !r.last_err.message.is_null() {
libc::free(r.last_err.message as *mut libc::c_void);
}
(*reader).last_err.message = m as *mut c_char;
(*reader).last_err.domain = crate::abi::types::XML_FROM_PARSER as c_int;
(*reader).last_err.level = crate::abi::types::xmlErrorLevel::XML_ERR_ERROR as c_int;
(*reader).last_err.code = crate::abi::types::XML_ERR_INTERNAL_ERROR as c_int;
}
}
}
&(*reader).last_err as *const crate::abi::structs::_xmlError
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderLocatorBaseURI(
locator: *mut XmlTextReaderLocator,
) -> *mut xmlChar {
if locator.is_null() {
return ptr::null_mut();
}
unsafe {
let r = (*locator).reader;
if r.is_null() {
return ptr::null_mut();
}
xml_strdup((*r).URL)
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderLocatorLineNumber(
locator: *mut XmlTextReaderLocator,
) -> c_int {
if locator.is_null() {
return -1;
}
unsafe {
let r = (*locator).reader;
if r.is_null() {
return -1;
}
let node = (*r).cur_node;
if node.is_null() {
return -1;
}
(*node).line as c_int
}
}
#[no_mangle]
pub const unsafe extern "C" fn xmlTextReaderGetRemainder(
_reader: *mut XmlTextReader,
) -> *mut crate::abi::structs::_xmlParserInputBuffer {
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSetMaxAmplification(
reader: *mut XmlTextReader,
maxAmpl: c_uint,
) {
if reader.is_null() {
return;
}
unsafe { (*reader).max_amplification = maxAmpl as c_int };
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSchemaValidate(
reader: *mut XmlTextReader,
xsd: *const c_char,
) -> c_int {
if reader.is_null() || xsd.is_null() {
return -1;
}
if unsafe { (*reader).doc }.is_null() && !unsafe { (*reader).parsed } {
unsafe { (*reader).Read() };
}
let ctxt = crate::xml::schemas::xmlSchemaNewParserCtxt(xsd);
if ctxt.is_null() {
return -1;
}
let schema = crate::xml::schemas::xmlSchemaParse(ctxt);
if schema.is_null() {
crate::xml::schemas::xmlSchemaFreeParserCtxt(ctxt);
return -1;
}
let vctxt = crate::xml::schemas::xmlSchemaNewValidCtxt(schema);
if vctxt.is_null() {
crate::xml::schemas::xmlSchemaFree(schema);
crate::xml::schemas::xmlSchemaFreeParserCtxt(ctxt);
return -1;
}
let ret = crate::xml::schemas::xmlSchemaValidateDoc(vctxt, unsafe { (*reader).doc });
crate::xml::schemas::xmlSchemaFreeValidCtxt(vctxt);
crate::xml::schemas::xmlSchemaFree(schema);
crate::xml::schemas::xmlSchemaFreeParserCtxt(ctxt);
ret
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSchemaValidateCtxt(
reader: *mut XmlTextReader,
ctxt: *mut c_void,
_options: c_int,
) -> c_int {
if reader.is_null() || ctxt.is_null() {
return -1;
}
if unsafe { (*reader).doc }.is_null() && !unsafe { (*reader).parsed } {
unsafe { (*reader).Read() };
}
crate::xml::schemas::xmlSchemaValidateDoc(ctxt, unsafe { (*reader).doc })
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderSetSchema(
reader: *mut XmlTextReader,
schema: *mut c_void,
) -> c_int {
if reader.is_null() {
return -1;
}
unsafe {
(*reader).schema = schema;
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderRelaxNGValidate(
reader: *mut XmlTextReader,
rng: *const c_char,
) -> c_int {
if reader.is_null() || rng.is_null() {
return -1;
}
if unsafe { (*reader).doc }.is_null() && !unsafe { (*reader).parsed } {
unsafe { (*reader).Read() };
}
let ctxt = crate::xml::relaxng::xmlRelaxNGNewParserCtxt(rng);
if ctxt.is_null() {
return -1;
}
let schema = crate::xml::relaxng::xmlRelaxNGParse(ctxt);
if schema.is_null() {
crate::xml::relaxng::xmlRelaxNGFreeParserCtxt(ctxt);
return -1;
}
let vctxt = crate::xml::relaxng::xmlRelaxNGNewValidCtxt(schema);
if vctxt.is_null() {
crate::xml::relaxng::xmlRelaxNGFree(schema);
crate::xml::relaxng::xmlRelaxNGFreeParserCtxt(ctxt);
return -1;
}
let ret = crate::xml::relaxng::xmlRelaxNGValidateDoc(vctxt, unsafe { (*reader).doc });
crate::xml::relaxng::xmlRelaxNGFreeValidCtxt(vctxt);
crate::xml::relaxng::xmlRelaxNGFree(schema);
crate::xml::relaxng::xmlRelaxNGFreeParserCtxt(ctxt);
ret
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderRelaxNGValidateCtxt(
reader: *mut XmlTextReader,
ctxt: *mut c_void,
_options: c_int,
) -> c_int {
if reader.is_null() || ctxt.is_null() {
return -1;
}
if unsafe { (*reader).doc }.is_null() && !unsafe { (*reader).parsed } {
unsafe { (*reader).Read() };
}
crate::xml::relaxng::xmlRelaxNGValidateDoc(ctxt, unsafe { (*reader).doc })
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextReaderRelaxNGSetSchema(
reader: *mut XmlTextReader,
schema: *mut c_void,
) -> c_int {
if reader.is_null() {
return -1;
}
unsafe {
(*reader).rng = schema;
}
0
}