use core::ffi::c_void;
use core::ptr;
use std::os::raw::{c_char, c_int, c_long, c_ulong};
use crate::abi::allocator;
use crate::abi::constants::*;
use crate::abi::structs::*;
use crate::abi::types::xmlCharEncoding::XML_CHAR_ENCODING_UTF8;
use crate::abi::types::xmlDocProperties::XML_DOC_USERBUILT;
use crate::abi::types::xmlElementType::*;
use crate::abi::types::*;
use crate::xml::io;
unsafe fn dup_xml_str(str: *const xmlChar) -> *mut xmlChar {
if str.is_null() {
return ptr::null_mut();
}
let len = unsafe { crate::abi::exports_xml2::xmlStrlen(str) as usize };
if len == 0 {
let buf = unsafe { allocator::xmlMallocImpl(1) as *mut xmlChar };
if !buf.is_null() {
unsafe { *buf = 0 };
}
return buf;
}
let buf = unsafe { allocator::xmlMallocImpl(len + 1) as *mut xmlChar };
if !buf.is_null() {
unsafe {
ptr::copy_nonoverlapping(str, buf, len + 1);
}
}
buf
}
#[allow(dead_code)]
unsafe fn copy_xml_str_content(dest: *mut xmlChar, src: *const xmlChar, max_len: usize) -> bool {
if src.is_null() || dest.is_null() || max_len == 0 {
return false;
}
let len = unsafe { crate::abi::exports_xml2::xmlStrlen(src) as usize };
if len >= max_len {
return false;
}
unsafe {
ptr::copy_nonoverlapping(src, dest, len);
*dest.add(len) = 0;
}
true
}
pub const unsafe fn xml_strlen(str: *const xmlChar) -> c_int {
if str.is_null() {
return 0;
}
let mut len: c_int = 0;
while unsafe { *str.add(len as usize) != 0 } {
len += 1;
}
len
}
pub unsafe fn new_doc(version: *const xmlChar) -> *mut _xmlDoc {
let doc = allocator::xmlMallocZero(size_of::<_xmlDoc>() as usize) as *mut _xmlDoc;
if doc.is_null() {
return ptr::null_mut();
}
unsafe {
(*doc).type_ = XML_DOCUMENT_NODE as c_int;
(*doc).standalone = -1; (*doc).doc = doc; (*doc).properties = XML_DOC_USERBUILT as c_int;
(*doc).compression = -1; (*doc).charset = XML_CHAR_ENCODING_UTF8 as c_int;
let ver = if version.is_null() {
XML_DEFAULT_VERSION.as_ptr() as *const xmlChar
} else {
version
};
(*doc).version = dup_xml_str(ver);
}
crate::abi::data_globals::register_node_hook(doc as *mut _xmlNode);
doc
}
pub unsafe fn free_doc(doc: *mut _xmlDoc) {
if doc.is_null() {
return;
}
crate::abi::data_globals::deregister_node_hook(doc as *mut _xmlNode);
let d = unsafe { &mut *doc };
if !d.ids.is_null() {
crate::xml::validation::free_id_table(d.ids as *mut crate::xml::hash::HashTable);
d.ids = ptr::null_mut();
}
let dict = d.dict;
let mut ext_subset = d.extSubset;
let int_subset = d.intSubset;
if !ext_subset.is_null() && ext_subset == int_subset {
ext_subset = ptr::null_mut();
}
if !ext_subset.is_null() {
unlink_node_internal(ext_subset as *mut _xmlNode, d as *mut _xmlDoc);
d.extSubset = ptr::null_mut();
free_dtd(ext_subset);
}
if !int_subset.is_null() {
unlink_node_internal(int_subset as *mut _xmlNode, d as *mut _xmlDoc);
d.intSubset = ptr::null_mut();
free_dtd(int_subset);
}
if !d.children.is_null() {
free_node_list(d.children);
}
if !d.oldNs.is_null() {
free_ns_list(d.oldNs);
}
if !d.version.is_null() {
allocator::xmlFreeImpl(d.version as *mut c_void);
}
if !d.encoding.is_null() {
allocator::xmlFreeImpl(d.encoding as *mut c_void);
}
if !d.URL.is_null() {
allocator::xmlFreeImpl(d.URL as *mut c_void);
}
allocator::xmlFreeImpl(doc as *mut c_void);
if !dict.is_null() {
crate::abi::exports_xml2::xmlDictFree(dict);
}
}
unsafe fn rebind_copied_ns(new_doc: *mut _xmlDoc, top: *mut _xmlNode, el: *mut _xmlNode) {
unsafe {
let bind = |n: *mut _xmlNode| {
let ns = (*n).ns;
if ns.is_null() {
return;
}
let prefix = (*ns).prefix;
let mut found = search_ns(new_doc, el, prefix);
if found.is_null() {
found = new_ns(top, (*ns).href, prefix);
}
if !found.is_null() {
(*n).ns = found;
}
};
if (*el).type_ == XML_ELEMENT_NODE as c_int {
bind(el);
let mut a = (*el).properties;
while !a.is_null() {
if !(*a).ns.is_null() && a as *mut _xmlNode != el {
let prefix = (*(*a).ns).prefix;
let mut found = search_ns(new_doc, el, prefix);
if found.is_null() {
found = new_ns(top, (*(*a).ns).href, prefix);
}
if !found.is_null() {
(*a).ns = found;
}
}
a = (*a).next;
}
}
}
}
pub(crate) unsafe fn reconcile_copied_tree_ns(new_doc: *mut _xmlDoc) {
if new_doc.is_null() {
return;
}
unsafe {
let mut top: *mut _xmlNode = ptr::null_mut();
let mut c = (*new_doc).children;
while !c.is_null() {
if (*c).type_ == XML_ELEMENT_NODE as c_int {
top = c;
break;
}
c = (*c).next;
}
if top.is_null() {
return;
}
let mut stack: Vec<*mut _xmlNode> = vec![top];
while let Some(el) = stack.pop() {
rebind_copied_ns(new_doc, top, el);
let mut kids: Vec<*mut _xmlNode> = Vec::new();
let mut ch = (*el).children;
while !ch.is_null() {
if (*ch).type_ == XML_ELEMENT_NODE as c_int {
kids.push(ch);
}
ch = (*ch).next;
}
for k in kids.into_iter().rev() {
stack.push(k);
}
}
}
}
unsafe fn unlink_node_internal(node: *mut _xmlNode, _doc: *mut _xmlDoc) {
if node.is_null() {
return;
}
let parent = (*node).parent;
if parent.is_null() {
return;
}
if (*node).prev.is_null() {
(*parent).children = (*node).next;
} else {
(*(*node).prev).next = (*node).next;
}
if (*node).next.is_null() {
(*parent).last = (*node).prev;
} else {
(*(*node).next).prev = (*node).prev;
}
(*node).next = ptr::null_mut();
(*node).prev = ptr::null_mut();
(*node).parent = ptr::null_mut();
}
pub unsafe fn copy_doc(doc: *const _xmlDoc, recursive: c_int) -> *mut _xmlDoc {
if doc.is_null() {
return ptr::null_mut();
}
let d = unsafe { &*doc };
let new_doc = new_doc(d.version);
if new_doc.is_null() {
return ptr::null_mut();
}
unsafe {
(*new_doc).type_ = d.type_;
(*new_doc).standalone = d.standalone;
(*new_doc).encoding = dup_xml_str(d.encoding);
(*new_doc).URL = dup_xml_str(d.URL);
(*new_doc).charset = d.charset;
(*new_doc).properties = d.properties;
if recursive != 0 {
if !d.intSubset.is_null() {
let dtd_copy = crate::xml::dtd::copy_dtd(d.intSubset);
if !dtd_copy.is_null() {
(*new_doc).intSubset = dtd_copy;
}
}
}
if recursive != 0 && !d.children.is_null() {
let mut head: *mut _xmlNode = ptr::null_mut();
let mut tail: *mut _xmlNode = ptr::null_mut();
let mut cur = d.children;
while !cur.is_null() {
let ct = unsafe { (*cur).type_ };
let copy: *mut _xmlNode = if ct == XML_DTD_NODE as c_int {
unsafe {
if (*new_doc).intSubset.is_null() {
let dc = crate::xml::dtd::copy_dtd(
cur as *const crate::abi::structs::_xmlDtd,
);
if dc.is_null() {
break;
}
(*new_doc).intSubset = dc;
dc as *mut _xmlNode
} else {
(*new_doc).intSubset as *mut _xmlNode
}
}
} else {
copy_node(cur, recursive)
};
if copy.is_null() {
break;
}
unsafe {
if tail.is_null() {
head = copy;
} else {
(*tail).next = copy;
(*copy).prev = tail;
}
tail = copy;
}
cur = unsafe { (*cur).next };
}
(*new_doc).children = head;
(*new_doc).last = tail;
if !head.is_null() {
let mut child = head;
while !child.is_null() {
(*child).parent = new_doc as *mut _xmlNode;
(*child).doc = new_doc;
child = (*child).next;
}
propagate_doc(head, new_doc);
reconcile_copied_tree_ns(new_doc);
}
}
}
new_doc
}
pub unsafe fn doc_set_root_element(doc: *mut _xmlDoc, root: *mut _xmlNode) -> *mut _xmlNode {
if doc.is_null() || root.is_null() || unsafe { (*root).type_ } == XML_NAMESPACE_DECL as c_int {
return ptr::null_mut();
}
let old_root = doc_get_root_element(doc);
if old_root == root {
return old_root;
}
unsafe {
if !(*root).parent.is_null() {
unlink_node(root);
}
if (*root).doc != doc {
propagate_doc(root, doc);
}
(*root).parent = doc as *mut _xmlNode;
(*root).doc = doc;
if old_root.is_null() {
if (*doc).children.is_null() {
(*doc).children = root;
(*doc).last = root;
(*root).prev = ptr::null_mut();
(*root).next = ptr::null_mut();
} else {
add_sibling((*doc).last, root);
}
} else {
if (*old_root).prev.is_null() {
(*doc).children = root;
(*root).prev = ptr::null_mut();
} else {
(*(*old_root).prev).next = root;
(*root).prev = (*old_root).prev;
}
if (*old_root).next.is_null() {
(*doc).last = root;
(*root).next = ptr::null_mut();
} else {
(*(*old_root).next).prev = root;
(*root).next = (*old_root).next;
}
(*old_root).parent = ptr::null_mut();
(*old_root).prev = ptr::null_mut();
(*old_root).next = ptr::null_mut();
}
}
old_root
}
pub fn doc_get_root_element(doc: *mut _xmlDoc) -> *mut _xmlNode {
if doc.is_null() {
return ptr::null_mut();
}
let d = unsafe { &*doc };
let mut cur = d.children;
while !cur.is_null() {
let node = unsafe { &*cur };
if node.type_ == XML_ELEMENT_NODE as c_int {
return cur;
}
cur = node.next;
}
ptr::null_mut()
}
pub fn get_line_no(node: *const _xmlNode) -> c_long {
unsafe { get_line_no_internal(node, 0) }
}
unsafe fn get_line_no_internal(node: *const _xmlNode, depth: c_int) -> c_long {
if depth >= 5 {
return -1;
}
if node.is_null() {
return -1;
}
let n = unsafe { &*node };
if n.type_ == XML_ELEMENT_NODE as c_int
|| n.type_ == XML_TEXT_NODE as c_int
|| n.type_ == XML_COMMENT_NODE as c_int
|| n.type_ == XML_PI_NODE as c_int
{
if n.line == 65535 {
if n.type_ == XML_TEXT_NODE as c_int && !n.psvi.is_null() {
return n.psvi as usize as c_long;
}
if n.type_ == XML_ELEMENT_NODE as c_int && !n.children.is_null() {
let r = unsafe { get_line_no_internal(n.children, depth + 1) };
if r != -1 {
return r;
}
}
if !n.next.is_null() {
let r = unsafe { get_line_no_internal(n.next, depth + 1) };
if r != -1 {
return r;
}
}
if !n.prev.is_null() {
let r = unsafe { get_line_no_internal(n.prev, depth + 1) };
if r != -1 {
return r;
}
}
}
n.line as c_long
} else if !n.prev.is_null()
&& (unsafe { (*n.prev).type_ } == XML_ELEMENT_NODE as c_int
|| unsafe { (*n.prev).type_ } == XML_TEXT_NODE as c_int
|| unsafe { (*n.prev).type_ } == XML_COMMENT_NODE as c_int
|| unsafe { (*n.prev).type_ } == XML_PI_NODE as c_int)
{
unsafe { get_line_no_internal(n.prev, depth + 1) }
} else if !n.parent.is_null() && unsafe { (*n.parent).type_ } == XML_ELEMENT_NODE as c_int {
unsafe { get_line_no_internal(n.parent, depth + 1) }
} else {
-1
}
}
pub unsafe fn node_get_content(node: *mut _xmlNode) -> *mut xmlChar {
if node.is_null() {
return ptr::null_mut();
}
let typ = (*node).type_;
let mut result: Vec<u8> = Vec::new();
match typ {
t if t == XML_TEXT_NODE as c_int
|| t == XML_CDATA_SECTION_NODE as c_int
|| t == XML_COMMENT_NODE as c_int
|| t == XML_PI_NODE as c_int =>
{
if !(*node).content.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen((*node).content);
result
.extend_from_slice(core::slice::from_raw_parts((*node).content, len as usize));
} else if t == XML_TEXT_NODE as c_int && !(*node).children.is_null() {
let mut child = (*node).children;
while !child.is_null() {
if !(*child).content.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen((*child).content);
result.extend_from_slice(core::slice::from_raw_parts(
(*child).content,
len as usize,
));
}
child = (*child).next;
}
}
}
t if t == XML_ATTRIBUTE_NODE as c_int => {
let mut child = (*node).children;
while !child.is_null() {
let ctype = (*child).type_;
if ctype == XML_TEXT_NODE as c_int || ctype == XML_CDATA_SECTION_NODE as c_int {
if !(*child).content.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen((*child).content);
result.extend_from_slice(core::slice::from_raw_parts(
(*child).content,
len as usize,
));
}
} else if ctype == XML_ENTITY_REF_NODE as c_int
|| ctype == XML_ELEMENT_NODE as c_int
{
let sub = node_get_content(child);
if !sub.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen(sub);
result.extend_from_slice(core::slice::from_raw_parts(sub, len as usize));
allocator::xmlFreeImpl(sub as *mut c_void);
}
}
child = (*child).next;
}
}
t if t == XML_ENTITY_REF_NODE as c_int => {
let mut ent = if (*node).children.is_null() {
ptr::null_mut()
} else {
(*node).children as *mut _xmlEntity
};
if ent.is_null() {
let name = (*node).name;
if !name.is_null() {
ent = crate::xml::tree::get_doc_entity((*node).doc, name);
}
}
if !ent.is_null() {
let is_predef = (*ent).etype
== crate::abi::types::xmlEntityType::XML_INTERNAL_PREDEFINED_ENTITY as c_int;
if is_predef {
if !(*ent).content.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen((*ent).content);
result.extend_from_slice(core::slice::from_raw_parts(
(*ent).content,
len as usize,
));
}
} else if (*ent).flags & (1 << 3) == 0 {
(*ent).flags |= 1 << 3;
let mut child = (*ent).children;
while !child.is_null() {
let sub = node_get_content(child);
if !sub.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen(sub);
result
.extend_from_slice(core::slice::from_raw_parts(sub, len as usize));
allocator::xmlFreeImpl(sub as *mut c_void);
}
child = (*child).next;
}
(*ent).flags &= !(1 << 3);
}
}
}
t if t == XML_DOCUMENT_NODE as c_int || t == XML_HTML_DOCUMENT_NODE as c_int => {
let root = doc_get_root_element(node as *mut _xmlDoc);
if !root.is_null() {
let sub = node_get_content(root);
if !sub.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen(sub);
result.extend_from_slice(core::slice::from_raw_parts(sub, len as usize));
allocator::xmlFreeImpl(sub as *mut c_void);
}
}
}
t if t == XML_NAMESPACE_DECL as c_int => {
let ns = node as *mut crate::abi::structs::_xmlNs;
if !(*ns).href.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen((*ns).href);
result.extend_from_slice(core::slice::from_raw_parts((*ns).href, len as usize));
}
}
_ => {
let mut child = (*node).children;
while !child.is_null() {
let ctype = (*child).type_;
if ctype == XML_TEXT_NODE as c_int || ctype == XML_CDATA_SECTION_NODE as c_int {
if !(*child).content.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen((*child).content);
result.extend_from_slice(core::slice::from_raw_parts(
(*child).content,
len as usize,
));
}
} else if ctype == XML_ENTITY_REF_NODE as c_int
|| ctype == XML_ELEMENT_NODE as c_int
{
let sub = node_get_content(child);
if !sub.is_null() {
let len = crate::abi::exports_xml2::xmlStrlen(sub);
result.extend_from_slice(core::slice::from_raw_parts(sub, len as usize));
allocator::xmlFreeImpl(sub as *mut c_void);
}
}
child = (*child).next;
}
}
}
let buf = allocator::xmlMallocImpl(result.len() + 1) as *mut xmlChar;
if buf.is_null() {
return ptr::null_mut();
}
if !result.is_empty() {
ptr::copy_nonoverlapping(result.as_ptr(), buf, result.len());
}
*buf.add(result.len()) = 0;
buf
}
pub unsafe fn new_node(ns: *mut _xmlNs, name: *const xmlChar) -> *mut _xmlNode {
if name.is_null() {
return ptr::null_mut();
}
let node = allocator::xmlMallocZero(size_of::<_xmlNode>() as usize) as *mut _xmlNode;
if node.is_null() {
return ptr::null_mut();
}
unsafe {
(*node).type_ = XML_ELEMENT_NODE as c_int;
(*node).name = dup_xml_str(name);
(*node).ns = ns;
(*node).line = 0;
(*node).extra = 0;
if !ns.is_null() {
(*ns).context = node as *mut _xmlDoc;
}
}
crate::abi::data_globals::register_node_hook(node);
node
}
pub unsafe fn new_node_eat_name(ns: *mut _xmlNs, name: *const xmlChar) -> *mut _xmlNode {
if name.is_null() {
return ptr::null_mut();
}
let node = allocator::xmlMallocZero(size_of::<_xmlNode>() as usize) as *mut _xmlNode;
if node.is_null() {
return ptr::null_mut();
}
unsafe {
(*node).type_ = XML_ELEMENT_NODE as c_int;
(*node).name = name as *mut xmlChar;
(*node).ns = ns;
(*node).line = 0;
(*node).extra = 0;
if !ns.is_null() {
(*ns).context = node as *mut _xmlDoc;
}
}
crate::abi::data_globals::register_node_hook(node);
node
}
pub unsafe fn free_node(node: *mut _xmlNode) {
if node.is_null() {
return;
}
let n = unsafe { &mut *node };
if n.type_ == XML_DTD_NODE as c_int {
free_dtd(node as *mut _xmlDtd);
return;
} else if n.type_ == XML_NAMESPACE_DECL as c_int {
free_ns(node as *mut _xmlNs);
return;
} else if n.type_ == XML_ATTRIBUTE_NODE as c_int {
free_prop(node as *mut _xmlAttr);
return;
} else if n.type_ == XML_ELEMENT_DECL as c_int {
crate::xml::dtd::free_element(node as *mut _xmlElement);
return;
} else if n.type_ == XML_ATTRIBUTE_DECL as c_int {
crate::xml::dtd::free_attribute(node as *mut _xmlAttribute);
return;
} else if n.type_ == XML_ENTITY_DECL as c_int {
crate::xml::entities::free_entity(node as *mut _xmlEntity);
return;
}
crate::abi::data_globals::deregister_node_hook(node);
let is_element = n.type_ == XML_ELEMENT_NODE as c_int;
if is_element && !n.properties.is_null() {
free_prop_list(n.properties);
}
if is_element && !n.nsDef.is_null() {
free_ns_list(n.nsDef);
}
let dict = if n.doc.is_null() {
ptr::null_mut()
} else {
unsafe { (*n.doc).dict }
};
if !n.name.is_null() && !crate::abi::exports_hash::dict_owns_str(dict, n.name) {
let sentinels = [
crate::abi::data_globals::xmlStringText.as_ptr() as *const c_void,
crate::abi::data_globals::xmlStringTextNoenc.as_ptr() as *const c_void,
crate::abi::data_globals::xmlStringComment.as_ptr() as *const c_void,
];
let is_sentinel = sentinels.iter().any(|&p| p == n.name as *const c_void);
if !is_sentinel {
allocator::xmlFreeImpl(n.name as *mut c_void);
}
}
if !n.content.is_null() {
let node_type = n.type_;
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
{
let inline_addr = std::ptr::addr_of_mut!((*node).properties) as *const c_void;
if n.content as *const c_void != inline_addr
&& !crate::abi::exports_hash::dict_owns_str(dict, n.content)
{
allocator::xmlFreeImpl(n.content as *mut c_void);
}
}
}
allocator::xmlFreeImpl(node as *mut c_void);
}
pub unsafe fn free_node_list(mut cur: *mut _xmlNode) {
let mut frames: Vec<(*mut _xmlNode, *mut _xmlNode)> = Vec::new();
loop {
while !cur.is_null() {
let next = unsafe { (*cur).next };
let t = unsafe { (*cur).type_ };
if t == XML_DTD_NODE as c_int {
unsafe {
(*cur).prev = ptr::null_mut();
(*cur).next = ptr::null_mut();
}
cur = next;
continue;
}
if t != XML_ENTITY_REF_NODE as c_int && !unsafe { (*cur).children }.is_null() {
frames.push((cur, next));
cur = unsafe { (*cur).children };
continue;
}
free_node(cur);
cur = next;
}
let Some((parent, next)) = frames.pop() else {
break;
};
free_node(parent);
cur = next;
}
}
unsafe fn free_prop_list(prop: *mut _xmlAttr) {
let mut cur = prop;
while !cur.is_null() {
let next = unsafe { (*cur).next };
free_prop(cur);
cur = next;
}
}
unsafe fn free_prop(prop: *mut _xmlAttr) {
if prop.is_null() {
return;
}
if !unsafe { (*prop).doc }.is_null() && !unsafe { (*prop).id }.is_null() {
crate::xml::validation::remove_id(unsafe { (*prop).doc }, prop);
}
if !unsafe { (*prop).children }.is_null() {
free_node_list(unsafe { (*prop).children });
}
let dict = if unsafe { (*prop).doc }.is_null() {
ptr::null_mut()
} else {
unsafe { (*(*prop).doc).dict }
};
if !unsafe { (*prop).name }.is_null()
&& !crate::abi::exports_hash::dict_owns_str(dict, unsafe { (*prop).name })
{
allocator::xmlFreeImpl(unsafe { (*prop).name } as *mut c_void);
}
allocator::xmlFreeImpl(prop as *mut c_void);
}
unsafe fn free_ns_list(ns: *mut _xmlNs) {
let mut cur = ns;
while !cur.is_null() {
let next = unsafe { (*cur).next };
free_ns(cur);
cur = next;
}
}
unsafe fn free_ns(ns: *mut _xmlNs) {
if ns.is_null() {
return;
}
if !unsafe { (*ns).href }.is_null() {
allocator::xmlFreeImpl(unsafe { (*ns).href } as *mut c_void);
}
if !unsafe { (*ns).prefix }.is_null() {
allocator::xmlFreeImpl(unsafe { (*ns).prefix } as *mut c_void);
}
allocator::xmlFreeImpl(ns as *mut c_void);
}
pub unsafe fn copy_node(node: *const _xmlNode, recursive: c_int) -> *mut _xmlNode {
if node.is_null() {
return ptr::null_mut();
}
let n = unsafe { &*node };
let new_node = allocator::xmlMallocZero(size_of::<_xmlNode>() as usize) as *mut _xmlNode;
if new_node.is_null() {
return ptr::null_mut();
}
unsafe {
(*new_node).type_ = n.type_;
(*new_node).name = dup_xml_str(n.name);
if n.type_ == XML_ELEMENT_NODE as c_int {
(*new_node).line = n.line;
}
(*new_node).extra = n.extra;
(*new_node).psvi = n.psvi;
(*new_node).ns = n.ns;
let is_element = n.type_ == XML_ELEMENT_NODE as c_int;
if is_element && !n.nsDef.is_null() {
(*new_node).nsDef = copy_ns_list(n.nsDef);
}
let node_type = n.type_;
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)
&& !n.content.is_null()
{
(*new_node).content = dup_xml_str(n.content);
}
if is_element && !n.properties.is_null() {
(*new_node).properties = copy_prop_list(n.properties);
let mut prop = (*new_node).properties;
while !prop.is_null() {
(*prop).parent = new_node;
if !(*prop).children.is_null() {
propagate_doc((*prop).children, (*new_node).doc);
}
prop = (*prop).next;
}
}
if recursive != 0 && !n.children.is_null() {
(*new_node).children = copy_node_list(n.children, recursive);
if !(*new_node).children.is_null() {
let mut child = (*new_node).children;
let mut last_child = child;
while !child.is_null() {
(*child).parent = new_node;
(*child).doc = (*new_node).doc;
propagate_doc(child, (*new_node).doc);
if (*child).next.is_null() {
last_child = child;
}
child = (*child).next;
}
(*new_node).last = last_child;
}
}
}
new_node
}
unsafe fn copy_node_list(node: *const _xmlNode, recursive: c_int) -> *mut _xmlNode {
if node.is_null() {
return ptr::null_mut();
}
let n = unsafe { &*node };
let new_node = copy_node(node, recursive);
if new_node.is_null() {
return ptr::null_mut();
}
let mut prev = new_node;
let mut cur = n.next;
while !cur.is_null() {
let new_cur = copy_node(cur, recursive);
if new_cur.is_null() {
break;
}
unsafe {
(*prev).next = new_cur;
(*new_cur).prev = prev;
}
prev = new_cur;
cur = unsafe { (*cur).next };
}
new_node
}
unsafe fn copy_ns_list(ns: *const _xmlNs) -> *mut _xmlNs {
if ns.is_null() {
return ptr::null_mut();
}
let n = unsafe { &*ns };
let new_ns = allocator::xmlMallocZero(size_of::<_xmlNs>() as usize) as *mut _xmlNs;
if new_ns.is_null() {
return ptr::null_mut();
}
unsafe {
(*new_ns).type_ = n.type_;
(*new_ns).href = dup_xml_str(n.href);
(*new_ns).prefix = dup_xml_str(n.prefix);
}
let mut prev = new_ns;
let mut cur = n.next;
while !cur.is_null() {
let c = unsafe { &*cur };
let new_cur = allocator::xmlMallocZero(size_of::<_xmlNs>() as usize) as *mut _xmlNs;
if new_cur.is_null() {
break;
}
unsafe {
(*new_cur).type_ = c.type_;
(*new_cur).href = dup_xml_str(c.href);
(*new_cur).prefix = dup_xml_str(c.prefix);
(*prev).next = new_cur;
}
prev = new_cur;
cur = c.next;
}
new_ns
}
unsafe fn copy_prop_list(prop: *const _xmlAttr) -> *mut _xmlAttr {
if prop.is_null() {
return ptr::null_mut();
}
let p = unsafe { &*prop };
let new_prop = allocator::xmlMallocZero(size_of::<_xmlAttr>() as usize) as *mut _xmlAttr;
if new_prop.is_null() {
return ptr::null_mut();
}
unsafe {
(*new_prop).type_ = p.type_;
(*new_prop).name = dup_xml_str(p.name);
(*new_prop).ns = p.ns;
(*new_prop).atype = p.atype;
if !p.children.is_null() {
(*new_prop).children = copy_node_list(p.children, 1);
if !(*new_prop).children.is_null() {
(*(*new_prop).children).parent = new_prop as *mut _xmlNode;
}
}
}
let mut prev = new_prop;
let mut cur = p.next;
while !cur.is_null() {
let c = unsafe { &*cur };
let new_cur = allocator::xmlMallocZero(size_of::<_xmlAttr>() as usize) as *mut _xmlAttr;
if new_cur.is_null() {
break;
}
unsafe {
(*new_cur).type_ = c.type_;
(*new_cur).name = dup_xml_str(c.name);
(*new_cur).ns = c.ns;
(*new_cur).atype = c.atype;
if !c.children.is_null() {
(*new_cur).children = copy_node_list(c.children, 1);
if !(*new_cur).children.is_null() {
(*(*new_cur).children).parent = new_cur as *mut _xmlNode;
}
}
(*prev).next = new_cur;
}
prev = new_cur;
cur = c.next;
}
new_prop
}
unsafe fn propagate_doc(node: *mut _xmlNode, doc: *mut _xmlDoc) {
let mut cur = node;
while !cur.is_null() {
unsafe {
if (*cur).doc != doc {
crate::abi::exports_tree::node_set_doc_impl(cur, doc);
}
if (*cur).type_ == XML_ELEMENT_NODE as c_int {
let mut prop = (*cur).properties;
while !prop.is_null() {
if (*prop).doc != doc {
crate::abi::exports_tree::node_set_doc_impl(prop as *mut _xmlNode, doc);
}
if !(*prop).children.is_null() {
propagate_doc((*prop).children, doc);
}
prop = (*prop).next;
}
}
if !(*cur).children.is_null() {
propagate_doc((*cur).children, doc);
}
}
cur = unsafe { (*cur).next };
}
}
pub unsafe fn unlink_node(node: *mut _xmlNode) {
if node.is_null() {
return;
}
let n = unsafe { &mut *node };
let doc = n.doc;
if n.type_ == XML_DTD_NODE as c_int && !doc.is_null() {
if (*doc).intSubset == node as *mut _xmlDtd {
(*doc).intSubset = ptr::null_mut();
}
if (*doc).extSubset == node as *mut _xmlDtd {
(*doc).extSubset = ptr::null_mut();
}
}
let prev = n.prev;
let next = n.next;
if !prev.is_null() {
unsafe { (*prev).next = next };
}
if !next.is_null() {
unsafe { (*next).prev = prev };
}
let parent = n.parent;
if !parent.is_null() {
if n.type_ == XML_ATTRIBUTE_NODE as c_int {
if unsafe { (*parent).properties } == node as *mut _xmlAttr {
unsafe { (*parent).properties = next as *mut _xmlAttr };
}
} else {
if unsafe { (*parent).children } == node {
unsafe { (*parent).children = next };
}
if unsafe { (*parent).last } == node {
unsafe { (*parent).last = prev };
}
}
}
let doc = n.doc;
if !doc.is_null() && !parent.is_null() {
}
if !doc.is_null() && parent.is_null() {
if unsafe { (*doc).children } == node {
unsafe { (*doc).children = next };
}
if unsafe { (*doc).last } == node {
unsafe { (*doc).last = prev };
}
}
n.parent = ptr::null_mut();
n.prev = ptr::null_mut();
n.next = ptr::null_mut();
}
pub unsafe fn add_child(parent: *mut _xmlNode, cur: *mut _xmlNode) -> *mut _xmlNode {
if parent.is_null() || cur.is_null() {
return ptr::null_mut();
}
let p = unsafe { &mut *parent };
let c = unsafe { &mut *cur };
if !c.parent.is_null() || !c.prev.is_null() || !c.next.is_null() {
unlink_node(cur);
}
if c.type_ == XML_ATTRIBUTE_NODE as c_int {
c.parent = parent;
c.prev = ptr::null_mut();
c.next = ptr::null_mut();
if p.properties.is_null() {
p.properties = cur as *mut crate::abi::structs::_xmlAttr;
} else {
let mut last = p.properties;
while !unsafe { (*last).next }.is_null() {
last = unsafe { (*last).next };
}
unsafe { (*last).next = cur as *mut crate::abi::structs::_xmlAttr };
c.prev = last as *mut _xmlNode;
}
if !p.doc.is_null() && c.doc != p.doc {
propagate_doc(cur, p.doc);
}
return cur;
}
c.parent = parent;
if p.children.is_null() {
p.children = cur;
p.last = cur;
c.prev = ptr::null_mut();
c.next = ptr::null_mut();
} else {
c.prev = p.last;
c.next = ptr::null_mut();
if !p.last.is_null() {
unsafe { (*p.last).next = cur };
}
p.last = cur;
}
let doc = if !p.doc.is_null() {
p.doc
} else {
ptr::null_mut()
};
if !doc.is_null() && c.doc != doc {
propagate_doc(cur, doc);
}
cur
}
pub unsafe fn add_sibling(cur: *mut _xmlNode, elem: *mut _xmlNode) -> *mut _xmlNode {
if cur.is_null() || elem.is_null() {
return ptr::null_mut();
}
let c = unsafe { &mut *cur };
let e = unsafe { &mut *elem };
if !e.parent.is_null() || !e.prev.is_null() || !e.next.is_null() {
unlink_node(elem);
}
e.parent = c.parent;
e.prev = cur;
e.next = c.next;
if !c.next.is_null() {
unsafe { (*c.next).prev = elem };
}
c.next = elem;
let parent = c.parent;
if !parent.is_null() && unsafe { (*parent).last } == cur {
unsafe { (*parent).last = elem };
}
if !c.doc.is_null() && e.doc != c.doc {
propagate_doc(elem, c.doc);
}
elem
}
pub unsafe fn add_sibling_before(cur: *mut _xmlNode, elem: *mut _xmlNode) -> *mut _xmlNode {
if cur.is_null() || elem.is_null() {
return ptr::null_mut();
}
let c = unsafe { &mut *cur };
let e = unsafe { &mut *elem };
if !e.parent.is_null() || !e.prev.is_null() || !e.next.is_null() {
unlink_node(elem);
}
e.parent = c.parent;
e.prev = c.prev;
e.next = cur;
if !c.prev.is_null() {
unsafe { (*c.prev).next = elem };
}
c.prev = elem;
let parent = c.parent;
if !parent.is_null() && unsafe { (*parent).children } == cur {
unsafe { (*parent).children = elem };
}
let doc = c.doc;
if !doc.is_null() && parent.is_null() && unsafe { (*doc).children } == cur {
unsafe { (*doc).children = elem };
}
if !c.doc.is_null() && e.doc != c.doc {
propagate_doc(elem, c.doc);
}
elem
}
pub unsafe fn new_child(
parent: *mut _xmlNode,
ns: *mut _xmlNs,
name: *const xmlChar,
) -> *mut _xmlNode {
let node = new_node(ns, name);
if node.is_null() {
return ptr::null_mut();
}
if !parent.is_null() {
add_child(parent, node);
}
node
}
pub unsafe fn new_text(content: *const xmlChar) -> *mut _xmlNode {
let node = allocator::xmlMallocZero(size_of::<_xmlNode>() as usize) as *mut _xmlNode;
if node.is_null() {
return ptr::null_mut();
}
unsafe {
(*node).type_ = XML_TEXT_NODE as c_int;
(*node).name = dup_xml_str(b"text\0" as *const u8 as *const xmlChar);
(*node).content = if content.is_null() {
let empty = allocator::xmlMallocImpl(1) as *mut xmlChar;
if !empty.is_null() {
*empty = 0;
}
empty
} else {
dup_xml_str(content)
};
(*node).line = 0;
}
crate::abi::data_globals::register_node_hook(node);
node
}
pub unsafe fn new_comment(content: *const xmlChar) -> *mut _xmlNode {
let node = allocator::xmlMallocZero(size_of::<_xmlNode>() as usize) as *mut _xmlNode;
if node.is_null() {
return ptr::null_mut();
}
unsafe {
(*node).type_ = XML_COMMENT_NODE as c_int;
(*node).name = dup_xml_str(b"comment\0" as *const u8 as *const xmlChar);
(*node).content = dup_xml_str(content);
(*node).line = 0;
}
crate::abi::data_globals::register_node_hook(node);
node
}
pub unsafe fn new_pi(name: *const xmlChar, content: *const xmlChar) -> *mut _xmlNode {
if name.is_null() {
return ptr::null_mut();
}
let node = allocator::xmlMallocZero(size_of::<_xmlNode>() as usize) as *mut _xmlNode;
if node.is_null() {
return ptr::null_mut();
}
unsafe {
(*node).type_ = XML_PI_NODE as c_int;
(*node).name = dup_xml_str(name);
(*node).content = dup_xml_str(content);
(*node).line = 0;
}
crate::abi::data_globals::register_node_hook(node);
node
}
pub unsafe fn new_cdata_block(
doc: *mut _xmlDoc,
content: *const xmlChar,
len: c_int,
) -> *mut _xmlNode {
let node = allocator::xmlMallocZero(size_of::<_xmlNode>() as usize) as *mut _xmlNode;
if node.is_null() {
return ptr::null_mut();
}
unsafe {
(*node).type_ = XML_CDATA_SECTION_NODE as c_int;
(*node).doc = doc;
if !content.is_null() && len > 0 {
(*node).content = allocator::xmlMallocImpl((len + 1) as usize) as *mut xmlChar;
if !(*node).content.is_null() {
ptr::copy_nonoverlapping(content, (*node).content, len as usize);
*((*node).content.add(len as usize)) = 0;
}
} else {
let empty = allocator::xmlMallocImpl(1) as *mut xmlChar;
if !empty.is_null() {
*empty = 0;
}
(*node).content = empty;
}
(*node).line = 0;
}
node
}
pub unsafe fn new_ns(
node: *mut _xmlNode,
href: *const xmlChar,
prefix: *const xmlChar,
) -> *mut _xmlNs {
let ns = allocator::xmlMallocZero(size_of::<_xmlNs>() as usize) as *mut _xmlNs;
if ns.is_null() {
return ptr::null_mut();
}
unsafe {
(*ns).type_ = XML_LOCAL_NAMESPACE as c_int;
(*ns).href = dup_xml_str(href);
(*ns).prefix = dup_xml_str(prefix);
(*ns).context = node as *mut _xmlDoc;
if !node.is_null() {
let n = &mut *node;
let same_prefix = |a: *mut _xmlNs| {
let other = &*a;
match ((*ns).prefix, other.prefix) {
(x, y) if x == y => true,
(x, y) if x.is_null() || y.is_null() => false,
(x, y) => crate::abi::exports_xml2::xmlStrEqual(x, y) != 0,
}
};
let conflict = |a: *mut _xmlNs| -> bool {
let other = &*a;
same_prefix(a) && !other.href.is_null()
};
if n.nsDef.is_null() {
n.nsDef = ns;
} else {
let mut prev = n.nsDef;
if conflict(prev) {
free_ns(ns);
return ptr::null_mut();
}
while !(*prev).next.is_null() {
prev = (*prev).next;
if conflict(prev) {
free_ns(ns);
return ptr::null_mut();
}
}
(*prev).next = ns;
}
}
}
ns
}
pub unsafe fn set_ns(node: *mut _xmlNode, ns: *mut _xmlNs) {
if node.is_null() {
return;
}
unsafe {
(*node).ns = ns;
}
}
pub unsafe fn get_ns_list(_doc: *mut _xmlDoc, node: *mut _xmlNode) -> *mut *mut _xmlNs {
if node.is_null() {
return ptr::null_mut();
}
let mut ns_ptrs: Vec<*mut _xmlNs> = Vec::new();
let mut cur = node;
while !cur.is_null() {
let n = unsafe { &*cur };
let mut ns_def = n.nsDef;
while !ns_def.is_null() {
let ns = unsafe { &*ns_def };
let mut found = false;
for &existing in &ns_ptrs {
if existing == ns_def {
found = true;
break;
}
let e = unsafe { &*existing };
if !ns.href.is_null() && !e.href.is_null() {
let href_match =
unsafe { crate::abi::exports_xml2::xmlStrEqual(ns.href, e.href) != 0 };
if href_match {
if ns.prefix.is_null() && e.prefix.is_null() {
found = true;
break;
}
if !ns.prefix.is_null() && !e.prefix.is_null() {
let prefix_match = unsafe {
crate::abi::exports_xml2::xmlStrEqual(ns.prefix, e.prefix) != 0
};
if prefix_match {
found = true;
break;
}
}
}
}
}
if !found {
ns_ptrs.push(ns_def);
}
ns_def = unsafe { (*ns_def).next };
}
cur = n.parent;
}
if ns_ptrs.is_empty() {
return ptr::null_mut();
}
let arr = allocator::xmlMallocImpl((ns_ptrs.len() + 1) * size_of::<*mut _xmlNs>())
as *mut *mut _xmlNs;
if arr.is_null() {
return ptr::null_mut();
}
for (i, ns) in ns_ptrs.iter().enumerate() {
unsafe { *arr.add(i) = *ns };
}
unsafe { *arr.add(ns_ptrs.len()) = ptr::null_mut() };
arr
}
fn is_str_xml(s: *const xmlChar) -> bool {
if s.is_null() {
return false;
}
let b = unsafe { core::slice::from_raw_parts(s, 3) };
b[0] == b'x' && b[1] == b'm' && b[2] == b'l'
}
unsafe fn ns_in_scope(
node: *mut _xmlNode,
ancestor: *mut _xmlNode,
prefix: *const xmlChar,
) -> c_int {
let mut cur = node;
while !cur.is_null() && cur != ancestor {
let t = unsafe { (*cur).type_ };
if t == XML_ENTITY_REF_NODE as c_int || t == XML_ENTITY_DECL as c_int {
return -1;
}
if t == XML_ELEMENT_NODE as c_int {
let mut tst = unsafe { (*cur).nsDef };
while !tst.is_null() {
let ns = unsafe { &*tst };
if ns.prefix.is_null() && prefix.is_null() {
return 0;
}
if !ns.prefix.is_null()
&& !prefix.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual(ns.prefix, prefix) != 0 }
{
return 0;
}
tst = unsafe { (*tst).next };
}
}
cur = unsafe { (*cur).parent };
}
if cur != ancestor {
return -1;
}
1
}
unsafe fn ensure_doc_xml_ns(doc: *mut _xmlDoc) -> *mut _xmlNs {
if doc.is_null() {
return ptr::null_mut();
}
if !(*doc).oldNs.is_null() {
return (*doc).oldNs;
}
let ns = allocator::xmlMallocZero(size_of::<_xmlNs>()) as *mut _xmlNs;
if ns.is_null() {
return ptr::null_mut();
}
unsafe {
(*ns).type_ = XML_LOCAL_NAMESPACE as c_int;
(*ns).href = crate::xml::string::xml_strdup(
c"http://www.w3.org/XML/1998/namespace".as_ptr() as *const xmlChar,
);
(*ns).prefix = crate::xml::string::xml_strdup(c"xml".as_ptr() as *const xmlChar);
(*doc).oldNs = ns;
}
ns
}
pub unsafe fn search_ns(
doc: *mut _xmlDoc,
node: *mut _xmlNode,
name_space: *const xmlChar,
) -> *mut _xmlNs {
if node.is_null() || unsafe { (*node).type_ } == XML_NAMESPACE_DECL as c_int {
return ptr::null_mut();
}
let orig = node;
if !doc.is_null() && is_str_xml(name_space) {
return unsafe { ensure_doc_xml_ns(doc) };
}
let mut cur = node;
while unsafe { (*cur).type_ } != XML_ELEMENT_NODE as c_int {
cur = unsafe { (*cur).parent };
if cur.is_null() {
return ptr::null_mut();
}
}
let parent = cur;
while !cur.is_null() && unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
let mut ns_def = unsafe { (*cur).nsDef };
while !ns_def.is_null() {
let ns = unsafe { &*ns_def };
if unsafe { crate::abi::exports_xml2::xmlStrEqual(ns.prefix, name_space) != 0 }
&& !ns.href.is_null()
{
return ns_def;
}
ns_def = unsafe { (*ns_def).next };
}
if orig != cur {
let el_ns = unsafe { (*cur).ns };
if !el_ns.is_null()
&& unsafe {
crate::abi::exports_xml2::xmlStrEqual((*el_ns).prefix, name_space) != 0
}
&& !(*el_ns).href.is_null()
{
return el_ns;
}
}
cur = unsafe { (*cur).parent };
}
if doc.is_null() && is_str_xml(name_space) {
let ns = allocator::xmlMallocZero(size_of::<_xmlNs>()) as *mut _xmlNs;
if !ns.is_null() {
unsafe {
(*ns).type_ = XML_LOCAL_NAMESPACE as c_int;
(*ns).href = crate::xml::string::xml_strdup(
c"http://www.w3.org/XML/1998/namespace".as_ptr() as *const xmlChar,
);
(*ns).prefix = crate::xml::string::xml_strdup(c"xml".as_ptr() as *const xmlChar);
(*ns).next = (*parent).nsDef;
(*parent).nsDef = ns;
}
return ns;
}
}
ptr::null_mut()
}
pub unsafe fn search_ns_by_href(
doc: *mut _xmlDoc,
node: *mut _xmlNode,
href: *const xmlChar,
) -> *mut _xmlNs {
if node.is_null() || href.is_null() || unsafe { (*node).type_ } == XML_NAMESPACE_DECL as c_int {
return ptr::null_mut();
}
let orig = node;
let is_xml_ns_uri = unsafe {
crate::abi::exports_xml2::xmlStrEqual(
href,
c"http://www.w3.org/XML/1998/namespace".as_ptr() as *const xmlChar,
) != 0
};
if is_xml_ns_uri && !doc.is_null() {
return unsafe { ensure_doc_xml_ns(doc) };
}
let is_attr = unsafe { (*node).type_ } == XML_ATTRIBUTE_NODE as c_int;
let mut cur = node;
while unsafe { (*cur).type_ } != XML_ELEMENT_NODE as c_int {
cur = unsafe { (*cur).parent };
if cur.is_null() {
return ptr::null_mut();
}
}
let parent = cur;
while !cur.is_null() && unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
let mut ns_def = unsafe { (*cur).nsDef };
while !ns_def.is_null() {
let ns = unsafe { &*ns_def };
if !ns.href.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual(ns.href, href) != 0 }
{
let ns_prefix = ns.prefix;
if ((!is_attr) || !ns_prefix.is_null())
&& unsafe { ns_in_scope(orig, cur, ns_prefix) } == 1
{
return ns_def;
}
}
ns_def = unsafe { (*ns_def).next };
}
if orig != cur {
let el_ns = unsafe { (*cur).ns };
if !el_ns.is_null() {
let eh = (*el_ns).href;
if !eh.is_null() && unsafe { crate::abi::exports_xml2::xmlStrEqual(eh, href) != 0 }
{
let el_prefix = (*el_ns).prefix;
if ((!is_attr) || !el_prefix.is_null())
&& unsafe { ns_in_scope(orig, cur, el_prefix) } == 1
{
return el_ns;
}
}
}
}
cur = unsafe { (*cur).parent };
}
if doc.is_null() && is_xml_ns_uri {
let ns = allocator::xmlMallocZero(size_of::<_xmlNs>()) as *mut _xmlNs;
if !ns.is_null() {
unsafe {
(*ns).type_ = XML_LOCAL_NAMESPACE as c_int;
(*ns).href = crate::xml::string::xml_strdup(
c"http://www.w3.org/XML/1998/namespace".as_ptr() as *const xmlChar,
);
(*ns).prefix = crate::xml::string::xml_strdup(c"xml".as_ptr() as *const xmlChar);
(*ns).next = (*parent).nsDef;
(*parent).nsDef = ns;
}
return ns;
}
}
ptr::null_mut()
}
unsafe fn dtd_default_decl_lookup(
node: *mut _xmlNode,
name: *const xmlChar,
ns_name: *const xmlChar,
) -> *mut _xmlAttr {
let n = unsafe { &*node };
if n.doc.is_null() {
return ptr::null_mut();
}
let doc = unsafe { &*(n.doc) };
if doc.intSubset.is_null() && doc.extSubset.is_null() {
return ptr::null_mut();
}
let ns = n.ns;
let mut tmp: *mut xmlChar = ptr::null_mut();
let elem_qname: *const xmlChar = if !ns.is_null() && !unsafe { (*ns).prefix.is_null() } {
tmp = unsafe { crate::abi::exports_xml2::xmlStrdup((*ns).prefix) };
if !tmp.is_null() {
tmp = unsafe {
crate::abi::exports_xml2::xmlStrcat(tmp, b":\0" as *const u8 as *const xmlChar)
};
}
if !tmp.is_null() {
tmp = unsafe { crate::abi::exports_xml2::xmlStrcat(tmp, n.name) };
}
if tmp.is_null() {
return ptr::null_mut();
}
tmp
} else {
n.name
};
let mut attr_decl: *mut crate::abi::structs::_xmlAttribute = ptr::null_mut();
let xml_ns_uri = b"http://www.w3.org/XML/1998/namespace\0";
if ns_name.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.intSubset,
elem_qname,
name,
ptr::null(),
);
if attr_decl.is_null() && !doc.extSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.extSubset,
elem_qname,
name,
ptr::null(),
);
}
} else if unsafe {
crate::abi::exports_xml2::xmlStrEqual(ns_name, xml_ns_uri.as_ptr() as *const xmlChar) != 0
} {
let xml_prefix = b"xml\0";
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.intSubset,
elem_qname,
name,
xml_prefix.as_ptr() as *const xmlChar,
);
if attr_decl.is_null() && !doc.extSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.extSubset,
elem_qname,
name,
xml_prefix.as_ptr() as *const xmlChar,
);
}
} else {
let ns_list = unsafe { get_ns_list(n.doc, node) };
if ns_list.is_null() {
if !tmp.is_null() {
allocator::xmlFreeImpl(tmp as *mut c_void);
}
return ptr::null_mut();
}
let mut cur = ns_list;
while !unsafe { *cur }.is_null() {
let d = unsafe { *cur };
if !unsafe { (*d).href }.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual((*d).href, ns_name) != 0 }
{
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.intSubset,
elem_qname,
name,
(*d).prefix,
);
if attr_decl.is_null() && !doc.extSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_qattr_desc(
doc.extSubset,
elem_qname,
name,
(*d).prefix,
);
}
if !attr_decl.is_null() {
break;
}
}
cur = cur.add(1);
}
allocator::xmlFreeImpl(ns_list as *mut c_void);
}
if !tmp.is_null() {
allocator::xmlFreeImpl(tmp as *mut c_void);
}
if !attr_decl.is_null() && !unsafe { (*attr_decl).defaultValue.is_null() } {
return attr_decl as *mut _xmlAttr;
}
ptr::null_mut()
}
unsafe fn dtd_default_decl_lookup_plain(
node: *mut _xmlNode,
name: *const xmlChar,
) -> *mut _xmlAttr {
let n = unsafe { &*node };
if n.doc.is_null() {
return ptr::null_mut();
}
let doc = unsafe { &*(n.doc) };
let mut attr_decl: *mut crate::abi::structs::_xmlAttribute = ptr::null_mut();
if !doc.intSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_attr_desc(doc.intSubset, n.name, name);
if attr_decl.is_null() && !doc.extSubset.is_null() {
attr_decl = crate::xml::validation::get_dtd_attr_desc(doc.extSubset, n.name, name);
}
if !attr_decl.is_null() && !unsafe { (*attr_decl).defaultValue.is_null() } {
return attr_decl as *mut _xmlAttr;
}
}
ptr::null_mut()
}
pub unsafe fn set_prop(
node: *mut _xmlNode,
name: *const xmlChar,
value: *const xmlChar,
) -> *mut _xmlAttr {
if node.is_null() || name.is_null() || unsafe { (*node).type_ } != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
let mut prefix: *mut xmlChar = ptr::null_mut();
let localname = crate::xml::validation::split_qname4(name, &mut prefix);
if localname.is_null() {
if !prefix.is_null() {
allocator::xmlFreeImpl(prefix as *mut c_void);
}
return ptr::null_mut();
}
if !prefix.is_null() {
let ns = unsafe { search_ns((*node).doc, node, prefix) };
if !ns.is_null() {
allocator::xmlFreeImpl(prefix as *mut c_void);
return unsafe { set_ns_prop(node, ns, localname, value) };
}
allocator::xmlFreeImpl(prefix as *mut c_void);
return unsafe { set_ns_prop(node, ptr::null_mut(), name, value) };
}
unsafe { set_ns_prop(node, ptr::null_mut(), name, value) }
}
pub unsafe fn get_prop(node: *mut _xmlNode, name: *const xmlChar) -> *mut xmlChar {
if node.is_null() || name.is_null() || unsafe { (*node).type_ } != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
let n = unsafe { &*node };
let mut cur = n.properties;
while !cur.is_null() {
let attr = unsafe { &*cur };
if !attr.name.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual(attr.name, name) != 0 }
{
if !attr.children.is_null() {
let text = unsafe { &*attr.children };
if text.type_ == XML_TEXT_NODE as c_int && !text.content.is_null() {
return dup_xml_str(text.content);
}
}
return dup_xml_str(b"\0" as *const u8 as *const xmlChar);
}
cur = unsafe { (*cur).next };
}
let decl = unsafe { dtd_default_decl_lookup_plain(node, name) };
if !decl.is_null() {
let a = decl as *mut crate::abi::structs::_xmlAttribute;
let dv = unsafe { (*a).defaultValue };
if !dv.is_null() {
return dup_xml_str(dv);
}
}
ptr::null_mut()
}
pub unsafe fn get_ns_prop(
node: *mut _xmlNode,
name: *const xmlChar,
name_space: *const xmlChar,
) -> *mut xmlChar {
if node.is_null() || name.is_null() || unsafe { (*node).type_ } != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
let mut cur = unsafe { (*node).properties };
while !cur.is_null() {
let attr = unsafe { &*cur };
if !attr.name.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual(attr.name, name) != 0 }
{
let matches = if name_space.is_null() {
attr.ns.is_null()
} else if !attr.ns.is_null() && !(*attr.ns).href.is_null() {
unsafe { crate::abi::exports_xml2::xmlStrEqual((*attr.ns).href, name_space) != 0 }
} else {
false
};
if matches {
if !attr.children.is_null() {
let text = unsafe { &*attr.children };
if text.type_ == XML_TEXT_NODE as c_int && !text.content.is_null() {
return dup_xml_str(text.content);
}
}
return dup_xml_str(b"\0" as *const u8 as *const xmlChar);
}
}
cur = unsafe { (*cur).next };
}
let decl = unsafe { dtd_default_decl_lookup(node, name, name_space) };
if !decl.is_null() {
let a = decl as *mut crate::abi::structs::_xmlAttribute;
let dv = unsafe { (*a).defaultValue };
if !dv.is_null() {
return dup_xml_str(dv);
}
}
ptr::null_mut()
}
pub unsafe fn set_ns_prop(
node: *mut _xmlNode,
ns: *mut _xmlNs,
name: *const xmlChar,
value: *const xmlChar,
) -> *mut _xmlAttr {
if node.is_null() || name.is_null() {
return ptr::null_mut();
}
let n = unsafe { &mut *node };
let mut existing = n.properties;
while !existing.is_null() {
let attr = unsafe { &*existing };
let same_ns = if !ns.is_null() {
if unsafe { (*existing).ns }.is_null() {
false
} else {
let an = unsafe { &*(*existing).ns };
let bn = unsafe { &*ns };
(!an.href.is_null()
&& !bn.href.is_null()
&& unsafe {
crate::abi::exports_xml2::xmlStrEqual(
an.href as *const crate::abi::types::xmlChar,
bn.href as *const crate::abi::types::xmlChar,
) != 0
})
|| (an.href.is_null() && bn.href.is_null())
}
} else {
unsafe { (*existing).ns }.is_null()
};
if !attr.name.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual(attr.name, name) != 0 }
&& same_ns
{
let attr_mut = existing;
unsafe { (*attr_mut).ns = ns };
let mut was_id = false;
if !n.doc.is_null() && !attr.id.is_null() {
crate::xml::validation::remove_id(n.doc, existing);
was_id = true;
let am = existing;
unsafe {
(*am).atype = crate::abi::types::xmlAttributeType::XML_ATTRIBUTE_ID as c_int;
}
}
if !attr.children.is_null() {
free_node_list(attr.children);
let attr_mut = existing;
unsafe {
(*attr_mut).children = ptr::null_mut();
(*attr_mut).last = ptr::null_mut();
}
}
if !value.is_null() {
let text = new_text(value);
if !text.is_null() {
let attr_mut = existing;
unsafe {
(*attr_mut).children = text;
(*attr_mut).last = text;
(*text).parent = existing as *mut _xmlNode;
(*text).doc = n.doc;
}
}
if was_id {
crate::xml::validation::add_id_safe(existing, value);
}
}
return existing;
}
existing = unsafe { (*existing).next };
}
let attr = allocator::xmlMallocZero(size_of::<_xmlAttr>() as usize) as *mut _xmlAttr;
if attr.is_null() {
return ptr::null_mut();
}
unsafe {
(*attr).type_ = XML_ATTRIBUTE_NODE as c_int;
(*attr).name = dup_xml_str(name);
(*attr).ns = ns;
(*attr).parent = node;
(*attr).doc = n.doc;
}
if !value.is_null() {
let text = new_text(value);
if !text.is_null() {
unsafe {
(*attr).children = text;
(*attr).last = text;
(*text).parent = attr as *mut _xmlNode;
(*text).doc = n.doc;
}
}
if !n.doc.is_null() {
let res = crate::xml::validation::is_id(n.doc, node, attr);
if res > 0 {
crate::xml::validation::add_id_safe(attr, value);
}
}
}
unsafe {
if n.properties.is_null() {
n.properties = attr;
} else {
let mut last = n.properties;
while !(*last).next.is_null() {
last = (*last).next;
}
(*last).next = attr;
(*attr).prev = last;
}
}
attr
}
pub unsafe fn remove_prop(attr: *mut _xmlAttr) -> c_int {
if attr.is_null() {
return -1;
}
let a = unsafe { &mut *attr };
if a.parent.is_null() {
return -1;
}
let parent = a.parent;
let p = unsafe { &mut *parent };
if p.properties == attr {
p.properties = a.next;
if !a.next.is_null() {
unsafe { (*a.next).prev = ptr::null_mut() };
}
free_prop(attr);
return 0;
}
let mut tmp = p.properties;
while !tmp.is_null() {
let next = unsafe { (*tmp).next };
if next == attr {
unsafe { (*tmp).next = a.next };
if !a.next.is_null() {
unsafe { (*a.next).prev = tmp };
}
free_prop(attr);
return 0;
}
tmp = next;
}
-1
}
pub unsafe fn has_prop(node: *mut _xmlNode, name: *const xmlChar) -> *mut _xmlAttr {
if node.is_null() || name.is_null() || unsafe { (*node).type_ } != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
let mut cur = unsafe { (*node).properties };
while !cur.is_null() {
let attr = unsafe { &*cur };
if !attr.name.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual(attr.name, name) != 0 }
{
return cur;
}
cur = unsafe { (*cur).next };
}
unsafe { dtd_default_decl_lookup_plain(node, name) }
}
pub unsafe fn has_ns_prop(
node: *mut _xmlNode,
name: *const xmlChar,
name_space: *const xmlChar,
) -> *mut _xmlAttr {
if node.is_null() || name.is_null() || unsafe { (*node).type_ } != XML_ELEMENT_NODE as c_int {
return ptr::null_mut();
}
let mut cur = unsafe { (*node).properties };
while !cur.is_null() {
let attr = unsafe { &*cur };
if !attr.name.is_null()
&& unsafe { crate::abi::exports_xml2::xmlStrEqual(attr.name, name) != 0 }
{
if name_space.is_null() {
if attr.ns.is_null() {
return cur;
}
} else if !attr.ns.is_null()
&& !(*attr.ns).href.is_null()
&& unsafe {
crate::abi::exports_xml2::xmlStrEqual((*attr.ns).href, name_space) != 0
}
{
return cur;
}
}
cur = unsafe { (*cur).next };
}
unsafe { dtd_default_decl_lookup(node, name, name_space) }
}
pub unsafe fn unset_prop(node: *mut _xmlNode, name: *const xmlChar) -> c_int {
let attr = unsafe { has_prop(node, name) };
if attr.is_null() {
return -1;
}
unsafe { remove_prop(attr) }
}
pub unsafe fn unset_ns_prop(
node: *mut _xmlNode,
name: *const xmlChar,
name_space: *const xmlChar,
) -> c_int {
let attr = unsafe { has_ns_prop(node, name, name_space) };
if attr.is_null() {
return -1;
}
unsafe { remove_prop(attr) }
}
pub unsafe fn first_element_child(node: *mut _xmlNode) -> *mut _xmlNode {
if node.is_null() {
return ptr::null_mut();
}
let mut cur = unsafe { (*node).children };
while !cur.is_null() {
if unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
return cur;
}
cur = unsafe { (*cur).next };
}
ptr::null_mut()
}
pub unsafe fn last_element_child(node: *mut _xmlNode) -> *mut _xmlNode {
if node.is_null() {
return ptr::null_mut();
}
let mut cur = unsafe { (*node).last };
while !cur.is_null() {
if unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
return cur;
}
cur = unsafe { (*cur).prev };
}
ptr::null_mut()
}
pub unsafe fn next_element_sibling(node: *mut _xmlNode) -> *mut _xmlNode {
if node.is_null() {
return ptr::null_mut();
}
let mut cur = unsafe { (*node).next };
while !cur.is_null() {
if unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
return cur;
}
cur = unsafe { (*cur).next };
}
ptr::null_mut()
}
pub unsafe fn previous_element_sibling(node: *mut _xmlNode) -> *mut _xmlNode {
if node.is_null() {
return ptr::null_mut();
}
let mut cur = unsafe { (*node).prev };
while !cur.is_null() {
if unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
return cur;
}
cur = unsafe { (*cur).prev };
}
ptr::null_mut()
}
pub unsafe fn child_element_count(node: *mut _xmlNode) -> c_ulong {
if node.is_null() {
return 0;
}
let mut cur = unsafe { (*node).children };
let mut count: c_ulong = 0;
while !cur.is_null() {
if unsafe { (*cur).type_ } == XML_ELEMENT_NODE as c_int {
count += 1;
}
cur = unsafe { (*cur).next };
}
count
}
pub unsafe fn text_concat(node: *mut _xmlNode, str: *const xmlChar, num: c_int) -> c_int {
if node.is_null() || str.is_null() || num <= 0 {
return -1;
}
let cur = unsafe { &mut *node };
if cur.content.is_null() {
let p = unsafe { allocator::xmlMallocImpl(num as usize + 1) as *mut xmlChar };
if p.is_null() {
return -1;
}
unsafe {
ptr::copy_nonoverlapping(str, p, num as usize);
*p.add(num as usize) = 0;
}
cur.content = p;
return 0;
}
let old_len = unsafe { crate::xml::string::xml_strlen(cur.content) };
let p = unsafe {
allocator::xmlReallocImpl(cur.content as *mut c_void, old_len + num as usize + 1)
as *mut xmlChar
};
if p.is_null() {
return -1;
}
unsafe {
ptr::copy_nonoverlapping(str, p.add(old_len), num as usize);
*p.add(old_len + num as usize) = 0;
}
cur.content = p;
0
}
pub unsafe fn text_merge(text: *mut _xmlNode, ntext: *mut _xmlNode) -> *mut _xmlNode {
if text.is_null() || ntext.is_null() {
return ptr::null_mut();
}
if unsafe { (*ntext).content.is_null() } {
unsafe { free_node(ntext) };
return text;
}
let num = unsafe { crate::xml::string::xml_strlen((*ntext).content) };
if unsafe { text_concat(text, (*ntext).content, num as c_int) } != 0 {
return ptr::null_mut();
}
unsafe { free_node(ntext) };
text
}
pub const fn get_int_subset(doc: *const _xmlDoc) -> *mut _xmlDtd {
if doc.is_null() {
return ptr::null_mut();
}
let d = unsafe { &*doc };
d.intSubset
}
pub unsafe fn new_dtd(
doc: *mut _xmlDoc,
name: *const xmlChar,
ExternalID: *const xmlChar,
SystemID: *const xmlChar,
) -> *mut _xmlDtd {
let dtd = allocator::xmlMallocZero(size_of::<_xmlDtd>() as usize) as *mut _xmlDtd;
if dtd.is_null() {
return ptr::null_mut();
}
if !doc.is_null() && !(*doc).extSubset.is_null() {
allocator::xmlFreeImpl(dtd as *mut c_void);
return ptr::null_mut();
}
unsafe {
(*dtd).type_ = XML_DTD_NODE as c_int;
(*dtd).name = dup_xml_str(name);
(*dtd).ExternalID = dup_xml_str(ExternalID);
(*dtd).SystemID = dup_xml_str(SystemID);
(*dtd).parent = doc;
(*dtd).doc = doc;
if !doc.is_null() {
(*doc).extSubset = dtd;
}
}
dtd
}
unsafe fn free_dtd(dtd: *mut _xmlDtd) {
if dtd.is_null() {
return;
}
let _d = unsafe { &mut *dtd };
let d = &mut *dtd;
if !d.children.is_null() {
let mut c = d.children;
while !c.is_null() {
let next = (*c).next;
let t = (*c).type_;
if t != XML_ELEMENT_DECL as c_int
&& t != XML_ATTRIBUTE_DECL as c_int
&& t != XML_ENTITY_DECL as c_int
{
unlink_node_internal(c, ptr::null_mut());
free_node(c);
}
c = next;
}
}
if !d.name.is_null() {
allocator::xmlFreeImpl(d.name as *mut c_void);
}
if !d.ExternalID.is_null() {
allocator::xmlFreeImpl(d.ExternalID as *mut c_void);
}
if !d.SystemID.is_null() {
allocator::xmlFreeImpl(d.SystemID as *mut c_void);
}
unsafe extern "C" fn free_notation_wrapper(payload: *mut c_void, _name: *mut u8) {
crate::xml::dtd::free_notation(payload as *mut _xmlNotation);
}
unsafe extern "C" fn free_element_wrapper(payload: *mut c_void, _name: *mut u8) {
crate::xml::dtd::free_element(payload as *mut _xmlElement);
}
unsafe extern "C" fn free_attribute_wrapper(payload: *mut c_void, _name: *mut u8) {
crate::xml::dtd::free_attribute(payload as *mut _xmlAttribute);
}
unsafe extern "C" fn free_entity_wrapper(payload: *mut c_void, _name: *mut u8) {
crate::xml::entities::free_entity(payload as *mut _xmlEntity);
}
if !d.notations.is_null() {
crate::xml::hash::hash_free(
d.notations as *mut crate::xml::hash::HashTable,
Some(free_notation_wrapper),
);
d.notations = ptr::null_mut();
}
if !d.elements.is_null() {
crate::xml::hash::hash_free(
d.elements as *mut crate::xml::hash::HashTable,
Some(free_element_wrapper),
);
d.elements = ptr::null_mut();
}
if !d.attributes.is_null() {
crate::xml::hash::hash_free(
d.attributes as *mut crate::xml::hash::HashTable,
Some(free_attribute_wrapper),
);
d.attributes = ptr::null_mut();
}
if !d.entities.is_null() {
crate::xml::hash::hash_free(
d.entities as *mut crate::xml::hash::HashTable,
Some(free_entity_wrapper),
);
d.entities = ptr::null_mut();
}
if !d.pentities.is_null() {
crate::xml::hash::hash_free(
d.pentities as *mut crate::xml::hash::HashTable,
Some(free_entity_wrapper),
);
d.pentities = ptr::null_mut();
}
allocator::xmlFreeImpl(dtd as *mut c_void);
}
pub unsafe fn new_entity(
_doc: *mut _xmlDoc,
name: *const xmlChar,
etype: c_int,
ExternalID: *const xmlChar,
SystemID: *const xmlChar,
content: *const xmlChar,
) -> *mut _xmlEntity {
let entity = allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
if entity.is_null() {
return ptr::null_mut();
}
unsafe {
(*entity).type_ = XML_ENTITY_DECL as c_int;
(*entity).name = dup_xml_str(name);
(*entity).etype = etype;
(*entity).ExternalID = dup_xml_str(ExternalID);
(*entity).SystemID = dup_xml_str(SystemID);
(*entity).content = dup_xml_str(content);
(*entity).length = if content.is_null() {
0
} else {
crate::abi::exports_xml2::xmlStrlen(content)
};
(*entity).flags = 0;
(*entity).expandedSize = 0;
}
entity
}
pub unsafe fn get_doc_entity(doc: *const _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
crate::xml::entities::get_entity(doc as *mut _xmlDoc, name)
}
pub unsafe fn add_doc_entity(
doc: *mut _xmlDoc,
name: *const xmlChar,
etype: c_int,
ExternalID: *const xmlChar,
SystemID: *const xmlChar,
content: *const xmlChar,
) -> *mut _xmlEntity {
if doc.is_null() || name.is_null() {
return ptr::null_mut();
}
unsafe {
let mut dtd = (*doc).intSubset;
if dtd.is_null() {
dtd = new_dtd(
doc,
c"internal".as_ptr() as *const xmlChar,
ptr::null(),
ptr::null(),
);
if dtd.is_null() {
return ptr::null_mut();
}
}
crate::xml::entities::add_entity(dtd, name, etype, ExternalID, SystemID, content)
}
}
pub unsafe fn add_dtd_entity(
doc: *mut _xmlDoc,
name: *const xmlChar,
etype: c_int,
ExternalID: *const xmlChar,
SystemID: *const xmlChar,
content: *const xmlChar,
) -> *mut _xmlEntity {
if doc.is_null() || name.is_null() {
return ptr::null_mut();
}
unsafe {
let mut dtd = (*doc).extSubset;
if dtd.is_null() {
dtd = new_dtd(
doc,
c"internal".as_ptr() as *const xmlChar,
ptr::null(),
ptr::null(),
);
if dtd.is_null() {
return ptr::null_mut();
}
(*doc).extSubset = dtd;
}
crate::xml::entities::add_entity(dtd, name, etype, ExternalID, SystemID, content)
}
}
pub unsafe fn get_dtd_entity(doc: *const _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
if doc.is_null() || name.is_null() {
return ptr::null_mut();
}
unsafe {
if !(*doc).intSubset.is_null() {
let e = crate::xml::entities::get_entity_from_dtd((*doc).intSubset, name);
if !e.is_null() {
return e;
}
}
if !(*doc).extSubset.is_null() {
return crate::xml::entities::get_entity_from_dtd((*doc).extSubset, name);
}
ptr::null_mut()
}
}
pub unsafe fn get_parameter_entity(doc: *const _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
crate::xml::entities::get_parameter_entity(doc as *mut _xmlDoc, name)
}
const ENTITY_LT: &[xmlChar] = b"<";
const ENTITY_GT: &[xmlChar] = b">";
const ENTITY_AMP: &[xmlChar] = b"&";
const ENTITY_QUOT: &[xmlChar] = b""";
#[allow(dead_code)]
const ENTITY_APOS: &[xmlChar] = b"'";
const INDENT: &[xmlChar] = b" ";
const MAX_INDENT: c_int = 60;
unsafe fn write_utf8_hex_char_ref(buf: *mut _xmlBuffer, bytes: &[u8], i: usize) -> usize {
let first = bytes[i];
let (n, mut val): (usize, u32) = if first < 0x80 {
(1, first as u32)
} else if first < 0xE0 {
if i + 1 < bytes.len() {
(
2,
((first & 0x1F) as u32) << 6 | (bytes[i + 1] & 0x3F) as u32,
)
} else {
(1, 0xFFFD)
}
} else if first < 0xF0 {
if i + 2 < bytes.len() {
(
3,
((first & 0x0F) as u32) << 12
| ((bytes[i + 1] & 0x3F) as u32) << 6
| (bytes[i + 2] & 0x3F) as u32,
)
} else {
(1, 0xFFFD)
}
} else if first < 0xF8 {
if i + 3 < bytes.len() {
(
4,
((first & 0x07) as u32) << 18
| ((bytes[i + 1] & 0x3F) as u32) << 12
| ((bytes[i + 2] & 0x3F) as u32) << 6
| (bytes[i + 3] & 0x3F) as u32,
)
} else {
(1, 0xFFFD)
}
} else {
(1, 0xFFFD)
};
if val == 0xFFFE || val == 0xFFFF || val > 0x10FFFF {
val = 0xFFFD;
}
let hex = format!("&#x{:X};", val);
io::buf_add(buf, hex.as_ptr(), hex.len() as c_int);
n
}
pub(crate) unsafe fn serialize_text_flags(
buf: *mut _xmlBuffer,
content: *const xmlChar,
len: c_int,
escape_non_ascii: bool,
) {
if buf.is_null() || content.is_null() || len <= 0 {
return;
}
let bytes = core::slice::from_raw_parts(content, len as usize);
let mut i: usize = 0;
while i < bytes.len() {
let ch = bytes[i];
if ch == b']' && i + 2 < bytes.len() && bytes[i + 1] == b']' && bytes[i + 2] == b'>' {
io::buf_add(buf, b"]]" as *const u8, 2);
io::buf_add(buf, ENTITY_GT.as_ptr(), ENTITY_GT.len() as c_int);
i += 3;
continue;
}
if ch >= 0x80 && escape_non_ascii {
i += unsafe { write_utf8_hex_char_ref(buf, bytes, i) };
continue;
}
match ch {
b'<' => {
io::buf_add(buf, ENTITY_LT.as_ptr(), ENTITY_LT.len() as c_int);
}
b'&' => {
io::buf_add(buf, ENTITY_AMP.as_ptr(), ENTITY_AMP.len() as c_int);
}
b'>' => {
io::buf_add(buf, ENTITY_GT.as_ptr(), ENTITY_GT.len() as c_int);
}
b'\r' => {
io::buf_add(buf, b" " as *const u8, 5);
}
0x01..=0x08 | 0x0B | 0x0C | 0x0E..=0x1F => {
let hex = format!("&#x{:X};", ch);
io::buf_add(buf, hex.as_ptr(), hex.len() as c_int);
}
_ => {
io::buf_add(buf, &ch as *const u8, 1);
}
}
i += 1;
}
}
pub(crate) unsafe fn serialize_attr_value(buf: *mut _xmlBuffer, value: *const xmlChar) {
unsafe { serialize_attr_value_flags(buf, value, false) };
}
pub(crate) unsafe fn serialize_attr_value_flags(
buf: *mut _xmlBuffer,
value: *const xmlChar,
escape_non_ascii: bool,
) {
if buf.is_null() || value.is_null() {
return;
}
let len = xml_strlen(value);
let bytes = core::slice::from_raw_parts(value, len as usize);
let mut i: usize = 0;
while i < bytes.len() {
let ch = bytes[i];
if ch >= 0x80 && escape_non_ascii {
i += unsafe { write_utf8_hex_char_ref(buf, bytes, i) };
continue;
}
match ch {
b'\n' => {
io::buf_add(buf, b" " as *const u8, 5);
}
b'\r' => {
io::buf_add(buf, b" " as *const u8, 5);
}
b'\t' => {
io::buf_add(buf, b"	" as *const u8, 4);
}
b'<' => {
io::buf_add(buf, ENTITY_LT.as_ptr(), ENTITY_LT.len() as c_int);
}
b'&' => {
io::buf_add(buf, ENTITY_AMP.as_ptr(), ENTITY_AMP.len() as c_int);
}
b'"' => {
io::buf_add(buf, ENTITY_QUOT.as_ptr(), ENTITY_QUOT.len() as c_int);
}
b'>' => {
io::buf_add(buf, ENTITY_GT.as_ptr(), ENTITY_GT.len() as c_int);
}
_ => {
io::buf_add(buf, &ch as *const u8, 1);
}
}
i += 1;
}
}
unsafe fn write_indent(
buf: *mut _xmlBuffer,
level: c_int,
indent: *const xmlChar,
indent_len: c_int,
) {
if buf.is_null() || level <= 0 || indent.is_null() || indent_len <= 0 {
return;
}
let indent_nr = MAX_INDENT / indent_len;
let mut lvl = level;
if lvl > indent_nr {
lvl = indent_nr;
}
for _ in 0..lvl {
io::buf_add(buf, indent, indent_len);
}
}
pub(crate) fn save_escapes_non_ascii(save_encoding: *const xmlChar, doc: *mut _xmlDoc) -> bool {
if !save_encoding.is_null() {
return false;
}
if doc.is_null() || unsafe { (*doc).encoding }.is_null() {
return true;
}
let enc = unsafe { (*doc).encoding };
let name = unsafe { std::ffi::CStr::from_ptr(enc as *const c_char) }
.to_string_lossy()
.into_owned();
let lower = name.to_ascii_lowercase();
!(lower == "utf-8" || lower == "utf8" || lower == "us-ascii" || lower == "ascii")
}
unsafe fn is_noenc_text(node: *mut _xmlNode) -> bool {
if node.is_null() {
return false;
}
let n = unsafe { &*node };
if n.name.is_null() {
return false;
}
c_str_eq_bytes(n.name, b"textnoenc")
}
const unsafe fn c_str_eq_bytes(s: *const xmlChar, b: &[u8]) -> bool {
let mut i = 0usize;
while i < b.len() {
if unsafe { *s.add(i) } != b[i] {
return false;
}
i += 1;
}
unsafe { *s.add(i) == 0 }
}
#[derive(Clone, Copy)]
struct DumpState {
format: c_int,
saved: c_int,
unformatted: *mut _xmlNode,
indent: *const xmlChar,
indent_len: c_int,
no_decl: c_int,
encoding: *const xmlChar,
explicit_save: bool,
xhtml: bool,
as_html: bool,
no_empty: bool,
}
impl DumpState {
const fn new(format: c_int) -> Self {
let f = if format != 0 { 1 } else { 0 };
DumpState {
format: f,
saved: f,
unformatted: ptr::null_mut(),
indent: INDENT.as_ptr(),
indent_len: INDENT.len() as c_int,
no_decl: 0,
encoding: ptr::null(),
xhtml: false,
as_html: false,
no_empty: false,
explicit_save: false,
}
}
unsafe fn with_indent_enc(
format: c_int,
indent: *const xmlChar,
no_decl: c_int,
encoding: *const xmlChar,
) -> Self {
let f = if format != 0 { 1 } else { 0 };
let (ptr, len) = if indent.is_null() {
let g = crate::xml::globals::get_tree_indent_string();
if g.is_null() {
(INDENT.as_ptr(), INDENT.len() as c_int)
} else {
let mut n = 0i32;
while unsafe { *g.add(n as usize) } != 0 {
n += 1;
}
(g, n)
}
} else {
let mut n = 0i32;
while unsafe { *indent.add(n as usize) } != 0 {
n += 1;
}
(indent, n)
};
DumpState {
format: f,
saved: f,
unformatted: ptr::null_mut(),
indent: ptr,
indent_len: len,
no_decl,
encoding,
as_html: false,
no_empty: false,
xhtml: false,
explicit_save: false,
}
}
}
pub(crate) unsafe fn xml_is_xhtml(doc: *mut _xmlDoc) -> bool {
unsafe {
if doc.is_null() {
return false;
}
let d = &*doc;
if d.intSubset.is_null() {
return false;
}
let dtd = &*d.intSubset;
if !dtd.ExternalID.is_null()
&& (c_str_eq_bytes(dtd.ExternalID, b"-//W3C//DTD XHTML 1.0 Strict//EN")
|| c_str_eq_bytes(dtd.ExternalID, b"-//W3C//DTD XHTML 1.0 Frameset//EN")
|| c_str_eq_bytes(dtd.ExternalID, b"-//W3C//DTD XHTML 1.0 Transitional//EN"))
{
return true;
}
if !dtd.SystemID.is_null()
&& (c_str_eq_bytes(
dtd.SystemID,
b"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd",
) || c_str_eq_bytes(
dtd.SystemID,
b"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd",
) || c_str_eq_bytes(
dtd.SystemID,
b"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd",
))
{
return true;
}
false
}
}
const unsafe fn xhtml_is_empty(name: *const xmlChar) -> bool {
unsafe {
if name.is_null() {
return false;
}
c_str_eq_bytes(name, b"area")
|| c_str_eq_bytes(name, b"base")
|| c_str_eq_bytes(name, b"basefont")
|| c_str_eq_bytes(name, b"br")
|| c_str_eq_bytes(name, b"col")
|| c_str_eq_bytes(name, b"frame")
|| c_str_eq_bytes(name, b"hr")
|| c_str_eq_bytes(name, b"img")
|| c_str_eq_bytes(name, b"input")
|| c_str_eq_bytes(name, b"isindex")
|| c_str_eq_bytes(name, b"link")
|| c_str_eq_bytes(name, b"meta")
|| c_str_eq_bytes(name, b"param")
}
}
unsafe fn write_qname(buf: *mut _xmlBuffer, node: *mut _xmlNode) {
let n = unsafe { &*node };
if !n.ns.is_null() {
let ns = unsafe { &*n.ns };
if !ns.prefix.is_null() {
io::buf_cat(buf, ns.prefix);
io::buf_ccat(buf, b':');
}
}
if !n.name.is_null() {
io::buf_cat(buf, n.name);
}
}
unsafe fn ns_dump_output(buf: *mut _xmlBuffer, cur: *mut _xmlNs) {
if cur.is_null() || buf.is_null() {
return;
}
let ns = unsafe { &*cur };
if ns.type_ == XML_LOCAL_NAMESPACE as c_int && !ns.href.is_null() {
if !ns.prefix.is_null() && c_str_eq_bytes(ns.prefix, b"xml") {
return;
}
io::buf_ccat(buf, b' ');
if !ns.prefix.is_null() {
io::buf_add(buf, b"xmlns:" as *const u8, 6);
io::buf_cat(buf, ns.prefix);
} else {
io::buf_add(buf, b"xmlns" as *const u8, 5);
}
io::buf_add(buf, b"=\"" as *const u8, 2);
serialize_attr_value(buf, ns.href);
io::buf_ccat(buf, b'"');
}
}
unsafe fn attr_dump_output(buf: *mut _xmlBuffer, cur: *mut _xmlAttr, escape_non_ascii: bool) {
if cur.is_null() || buf.is_null() {
return;
}
io::buf_ccat(buf, b' ');
let a = unsafe { &*cur };
if !a.ns.is_null() {
let ans = unsafe { &*a.ns };
if !ans.prefix.is_null() {
io::buf_cat(buf, ans.prefix);
io::buf_ccat(buf, b':');
}
}
if !a.name.is_null() {
io::buf_cat(buf, a.name);
}
io::buf_add(buf, b"=\"" as *const u8, 2);
let mut child = a.children;
while !child.is_null() {
let ct = unsafe { (*child).type_ };
if ct == XML_TEXT_NODE as c_int && !unsafe { (*child).content }.is_null() {
serialize_attr_value_flags(buf, unsafe { (*child).content }, escape_non_ascii);
} else if ct == XML_ENTITY_REF_NODE as c_int && !unsafe { (*child).name }.is_null() {
io::buf_ccat(buf, b'&');
io::buf_cat(buf, unsafe { (*child).name });
io::buf_ccat(buf, b';');
}
child = unsafe { (*child).next };
}
io::buf_ccat(buf, b'"');
}
unsafe fn dump_notation_decl(buf: *mut _xmlBuffer, nota: *mut _xmlNotation) {
let n = unsafe { &*nota };
io::buf_add(buf, b"<!NOTATION " as *const u8, 11);
if !n.name.is_null() {
io::buf_cat(buf, n.name);
}
if !n.PublicID.is_null() {
io::buf_add(buf, b" PUBLIC " as *const u8, 8);
write_quoted_string(buf, n.PublicID);
if !n.SystemID.is_null() {
io::buf_ccat(buf, b' ');
write_quoted_string(buf, n.SystemID);
}
} else {
io::buf_add(buf, b" SYSTEM " as *const u8, 8);
write_quoted_string(buf, n.SystemID);
}
io::buf_add(buf, b" >\n" as *const u8, 4);
}
unsafe fn dump_element_occur(buf: *mut _xmlBuffer, ocur: c_int) {
use crate::abi::types::xmlElementContentOccur::*;
if ocur == XML_ELEMENT_CONTENT_OPT as c_int {
io::buf_ccat(buf, b'?');
} else if ocur == XML_ELEMENT_CONTENT_MULT as c_int {
io::buf_ccat(buf, b'*');
} else if ocur == XML_ELEMENT_CONTENT_PLUS as c_int {
io::buf_ccat(buf, b'+');
}
}
unsafe fn dump_element_content(buf: *mut _xmlBuffer, content: *mut _xmlElementContent) {
use crate::abi::types::xmlElementContentOccur::*;
use crate::abi::types::xmlElementContentType::*;
if content.is_null() {
return;
}
io::buf_ccat(buf, b'(');
let mut cur = content;
loop {
if cur.is_null() {
return;
}
let c = unsafe { &*cur };
match c.type_ {
t if t == XML_ELEMENT_CONTENT_PCDATA as c_int => {
io::buf_add(buf, b"#PCDATA" as *const u8, 7);
}
t if t == XML_ELEMENT_CONTENT_ELEMENT as c_int => {
if !c.prefix.is_null() {
io::buf_cat(buf, c.prefix);
io::buf_ccat(buf, b':');
}
if !c.name.is_null() {
io::buf_cat(buf, c.name);
}
}
t if t == XML_ELEMENT_CONTENT_SEQ as c_int || t == XML_ELEMENT_CONTENT_OR as c_int => {
if cur != content
&& !c.parent.is_null()
&& (c.type_ != unsafe { (*c.parent).type_ }
|| c.ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
{
io::buf_ccat(buf, b'(');
}
cur = c.c1;
continue;
}
_ => {}
}
while cur != content {
let ccur = unsafe { &*cur };
let parent = ccur.parent;
if parent.is_null() {
return;
}
let p = unsafe { &*parent };
if (ccur.type_ == XML_ELEMENT_CONTENT_OR as c_int
|| ccur.type_ == XML_ELEMENT_CONTENT_SEQ as c_int)
&& (ccur.type_ != p.type_ || ccur.ocur != XML_ELEMENT_CONTENT_ONCE as c_int)
{
io::buf_ccat(buf, b')');
}
dump_element_occur(buf, ccur.ocur);
if cur == p.c1 {
if p.type_ == XML_ELEMENT_CONTENT_SEQ as c_int {
io::buf_add(buf, b" , " as *const u8, 3);
} else if p.type_ == XML_ELEMENT_CONTENT_OR as c_int {
io::buf_add(buf, b" | " as *const u8, 3);
}
cur = p.c2;
break;
}
cur = parent;
}
if cur == content {
break;
}
}
io::buf_ccat(buf, b')');
let cc = unsafe { &*content };
dump_element_occur(buf, cc.ocur);
}
unsafe fn dump_element_decl(buf: *mut _xmlBuffer, elem: *mut _xmlElement) {
use crate::abi::types::xmlElementTypeVal::*;
let e = unsafe { &*elem };
io::buf_add(buf, b"<!ELEMENT " as *const u8, 10);
if !e.prefix.is_null() {
io::buf_cat(buf, e.prefix);
io::buf_ccat(buf, b':');
}
if !e.name.is_null() {
io::buf_cat(buf, e.name);
}
io::buf_ccat(buf, b' ');
match e.etype {
t if t == XML_ELEMENT_TYPE_EMPTY as c_int => {
io::buf_add(buf, b"EMPTY" as *const u8, 5);
}
t if t == XML_ELEMENT_TYPE_ANY as c_int => {
io::buf_add(buf, b"ANY" as *const u8, 3);
}
t if t == XML_ELEMENT_TYPE_MIXED as c_int || t == XML_ELEMENT_TYPE_ELEMENT as c_int => {
dump_element_content(buf, e.content);
}
_ => {}
}
io::buf_add(buf, b">\n" as *const u8, 2);
}
unsafe fn dump_enumeration(buf: *mut _xmlBuffer, cur: *mut _xmlEnumeration) {
let mut e = cur;
while !e.is_null() {
let en = unsafe { &*e };
if !en.name.is_null() {
io::buf_cat(buf, en.name);
}
if !en.next.is_null() {
io::buf_add(buf, b" | " as *const u8, 3);
}
e = en.next;
}
io::buf_ccat(buf, b')');
}
unsafe fn dump_attribute_decl(buf: *mut _xmlBuffer, attr: *mut _xmlAttribute) {
use crate::abi::types::xmlAttributeDefault::*;
use crate::abi::types::xmlAttributeType::*;
let a = unsafe { &*attr };
io::buf_add(buf, b"<!ATTLIST " as *const u8, 10);
if !a.elem.is_null() {
io::buf_cat(buf, a.elem);
}
io::buf_ccat(buf, b' ');
if !a.prefix.is_null() {
io::buf_cat(buf, a.prefix);
io::buf_ccat(buf, b':');
}
if !a.name.is_null() {
io::buf_cat(buf, a.name);
}
match a.atype {
t if t == XML_ATTRIBUTE_CDATA as c_int => {
io::buf_add(buf, b" CDATA" as *const u8, 6);
}
t if t == XML_ATTRIBUTE_ID as c_int => {
io::buf_add(buf, b" ID" as *const u8, 3);
}
t if t == XML_ATTRIBUTE_IDREF as c_int => {
io::buf_add(buf, b" IDREF" as *const u8, 6);
}
t if t == XML_ATTRIBUTE_IDREFS as c_int => {
io::buf_add(buf, b" IDREFS" as *const u8, 7);
}
t if t == XML_ATTRIBUTE_ENTITY as c_int => {
io::buf_add(buf, b" ENTITY" as *const u8, 7);
}
t if t == XML_ATTRIBUTE_ENTITIES as c_int => {
io::buf_add(buf, b" ENTITIES" as *const u8, 9);
}
t if t == XML_ATTRIBUTE_NMTOKEN as c_int => {
io::buf_add(buf, b" NMTOKEN" as *const u8, 8);
}
t if t == XML_ATTRIBUTE_NMTOKENS as c_int => {
io::buf_add(buf, b" NMTOKENS" as *const u8, 9);
}
t if t == XML_ATTRIBUTE_ENUMERATION as c_int => {
io::buf_add(buf, b" (" as *const u8, 2);
dump_enumeration(buf, a.tree);
}
t if t == XML_ATTRIBUTE_NOTATION as c_int => {
io::buf_add(buf, b" NOTATION (" as *const u8, 11);
dump_enumeration(buf, a.tree);
}
_ => {}
}
match a.def {
t if t == XML_ATTRIBUTE_REQUIRED as c_int => {
io::buf_add(buf, b" #REQUIRED" as *const u8, 10);
}
t if t == XML_ATTRIBUTE_IMPLIED as c_int => {
io::buf_add(buf, b" #IMPLIED" as *const u8, 9);
}
t if t == XML_ATTRIBUTE_FIXED as c_int => {
io::buf_add(buf, b" #FIXED" as *const u8, 7);
}
_ => {}
}
if !a.defaultValue.is_null() {
io::buf_add(buf, b" \"" as *const u8, 2);
serialize_attr_value(buf, a.defaultValue);
io::buf_ccat(buf, b'"');
}
io::buf_add(buf, b">\n" as *const u8, 2);
}
unsafe fn write_quoted_string(buf: *mut _xmlBuffer, str: *const xmlChar) {
if buf.is_null() {
return;
}
io::buf_ccat(buf, b'"');
if !str.is_null() {
let mut i = 0usize;
while unsafe { *str.add(i) != 0 } {
let ch = unsafe { *str.add(i) };
if ch == b'"' {
io::buf_add(buf, b""" as *const u8, 6);
} else {
io::buf_add(buf, &ch as *const u8, 1);
}
i += 1;
}
}
io::buf_ccat(buf, b'"');
}
unsafe fn dump_entity_decl(buf: *mut _xmlBuffer, ent: *mut _xmlEntity) {
use crate::abi::types::xmlEntityType::*;
let e = unsafe { &*ent };
if e.etype == XML_INTERNAL_PARAMETER_ENTITY as c_int
|| e.etype == XML_EXTERNAL_PARAMETER_ENTITY as c_int
{
io::buf_add(buf, b"<!ENTITY % " as *const u8, 11);
} else {
io::buf_add(buf, b"<!ENTITY " as *const u8, 9);
}
if !e.name.is_null() {
io::buf_cat(buf, e.name);
}
io::buf_ccat(buf, b' ');
if e.etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int
|| e.etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY as c_int
|| e.etype == XML_EXTERNAL_PARAMETER_ENTITY as c_int
{
if !e.ExternalID.is_null() {
io::buf_add(buf, b"PUBLIC " as *const u8, 7);
write_quoted_string(buf, e.ExternalID);
io::buf_ccat(buf, b' ');
} else {
io::buf_add(buf, b"SYSTEM " as *const u8, 7);
}
write_quoted_string(buf, e.SystemID);
}
if e.etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY as c_int && !e.content.is_null() {
io::buf_add(buf, b" NDATA " as *const u8, 7);
if !e.orig.is_null() {
io::buf_cat(buf, e.orig);
} else if !e.content.is_null() {
io::buf_cat(buf, e.content);
}
}
if e.etype == XML_INTERNAL_GENERAL_ENTITY as c_int
|| e.etype == XML_INTERNAL_PARAMETER_ENTITY as c_int
{
if !e.orig.is_null() {
write_quoted_string(buf, e.orig);
} else {
io::buf_ccat(buf, b'"');
if !e.content.is_null() {
let mut i = 0usize;
while unsafe { *e.content.add(i) != 0 } {
let ch = unsafe { *e.content.add(i) };
match ch {
b'"' => io::buf_add(buf, b""" as *const u8, 6),
b'%' => io::buf_add(buf, b"%" as *const u8, 6),
_ => io::buf_add(buf, &ch as *const u8, 1),
};
i += 1;
}
}
io::buf_ccat(buf, b'"');
}
}
io::buf_add(buf, b">\n" as *const u8, 2);
}
unsafe fn dtd_dump_output(
buf: *mut _xmlBuffer,
cur: *mut _xmlNode,
state: &mut DumpState,
level: &mut c_int,
) {
let dtd = cur as *mut _xmlDtd;
let d = unsafe { &*dtd };
io::buf_add(buf, b"<!DOCTYPE " as *const u8, 10);
if !d.name.is_null() {
io::buf_cat(buf, d.name);
}
if !d.ExternalID.is_null() {
io::buf_add(buf, b" PUBLIC " as *const u8, 8);
write_quoted_string(buf, d.ExternalID);
io::buf_ccat(buf, b' ');
write_quoted_string(buf, d.SystemID);
} else if !d.SystemID.is_null() {
io::buf_add(buf, b" SYSTEM " as *const u8, 8);
write_quoted_string(buf, d.SystemID);
}
if d.entities.is_null()
&& d.elements.is_null()
&& d.attributes.is_null()
&& d.notations.is_null()
&& d.pentities.is_null()
{
io::buf_ccat(buf, b'>');
return;
}
io::buf_add(buf, b" [\n" as *const u8, 3);
let format = state.format;
let lvl = *level;
state.format = 0;
*level = -1;
if !d.notations.is_null() {
crate::xml::hash::hash_scan(
d.notations as *mut crate::xml::hash::HashTable,
Some(dump_notation_decl_cb),
buf as *mut c_void,
);
}
let mut decl = d.children;
while !decl.is_null() {
let dt = unsafe { (*decl).type_ };
match dt {
t if t == XML_ELEMENT_DECL as c_int => {
dump_element_decl(buf, decl as *mut _xmlElement);
}
t if t == XML_ATTRIBUTE_DECL as c_int => {
dump_attribute_decl(buf, decl as *mut _xmlAttribute);
}
t if t == XML_ENTITY_DECL as c_int => {
dump_entity_decl(buf, decl as *mut _xmlEntity);
}
_ => {}
}
decl = unsafe { (*decl).next };
}
state.format = format;
*level = lvl;
io::buf_add(buf, b"]>" as *const u8, 2);
}
unsafe extern "C" fn dump_notation_decl_cb(
payload: *mut c_void,
data: *mut c_void,
_name: *const crate::abi::types::xmlChar,
) {
if !payload.is_null() && !data.is_null() {
dump_notation_decl(data as *mut _xmlBuffer, payload as *mut _xmlNotation);
}
}
unsafe fn doc_content_dump_output(
buf: *mut _xmlBuffer,
cur: *mut _xmlNode,
state: &mut DumpState,
level: &mut c_int,
) {
let doc = cur as *mut _xmlDoc;
let d = unsafe { &*doc };
if state.no_decl == 0 {
io::buf_add(buf, b"<?xml version=\"" as *const u8, 15);
if !d.version.is_null() {
io::buf_cat(buf, d.version);
} else {
io::buf_add(buf, b"1.0" as *const u8, 3);
}
io::buf_ccat(buf, b'"');
let enc = if state.encoding.is_null() {
d.encoding
} else {
state.encoding
};
if !enc.is_null() {
io::buf_add(buf, b" encoding=\"" as *const u8, 11);
io::buf_cat(buf, enc);
io::buf_ccat(buf, b'"');
}
match d.standalone {
0 => {
io::buf_add(buf, b" standalone=\"no\"" as *const u8, 16);
}
1 => {
io::buf_add(buf, b" standalone=\"yes\"" as *const u8, 17);
}
_ => {}
}
io::buf_add(buf, b"?>\n" as *const u8, 3);
}
if !d.children.is_null() && !d.intSubset.is_null() {
let mut in_chain = false;
let mut c = d.children;
while !c.is_null() {
if c as *mut c_void == d.intSubset as *mut c_void {
in_chain = true;
break;
}
c = unsafe { (*c).next };
}
if !in_chain {
let mut lvl = 0;
dtd_dump_output(buf, d.intSubset as *mut _xmlNode, state, &mut lvl);
io::buf_ccat(buf, b'\n');
}
}
if !d.children.is_null() {
let mut child = d.children;
while !child.is_null() {
*level = 0;
node_dump_internal(buf, child, child, cur, state, level);
let ct = unsafe { (*child).type_ };
if ct != XML_XINCLUDE_START as c_int && ct != XML_XINCLUDE_END as c_int {
io::buf_ccat(buf, b'\n');
}
child = unsafe { (*child).next };
}
}
}
unsafe fn node_dump_internal(
buf: *mut _xmlBuffer,
cur: *mut _xmlNode,
root: *mut _xmlNode,
parent: *mut _xmlNode,
state: &mut DumpState,
level: &mut c_int,
) {
if cur.is_null() || buf.is_null() {
return;
}
let n = unsafe { &*cur };
match n.type_ {
t if t == XML_ELEMENT_NODE as c_int => {
if cur != root && state.format == 1 {
write_indent(buf, *level, state.indent, state.indent_len);
}
if !n.parent.is_null() && n.parent != parent && !n.children.is_null() {
let mut sub = DumpState::new(state.format);
let mut sub_level = *level;
node_dump_internal(buf, cur, cur, n.parent, &mut sub, &mut sub_level);
return;
}
io::buf_ccat(buf, b'<');
write_qname(buf, cur);
if state.xhtml
&& !n.name.is_null()
&& c_str_eq_bytes(n.name, b"html")
&& n.ns.is_null()
&& n.nsDef.is_null()
{
io::buf_add(
buf,
b" xmlns=\"http://www.w3.org/1999/xhtml\"" as *const u8,
37,
);
}
let mut nsdef = n.nsDef;
while !nsdef.is_null() {
ns_dump_output(buf, nsdef);
nsdef = unsafe { (*nsdef).next };
}
let mut attr = n.properties;
while !attr.is_null() {
attr_dump_output(
buf,
attr,
state.explicit_save
&& !state.as_html
&& save_escapes_non_ascii(state.encoding, unsafe { (*attr).doc }),
);
attr = unsafe { (*attr).next };
}
if n.children.is_null() {
if state.as_html {
if xhtml_is_empty(n.name) {
io::buf_ccat(buf, b'>');
} else {
io::buf_ccat(buf, b'>');
io::buf_add(buf, b"</" as *const u8, 2);
write_qname(buf, cur);
io::buf_ccat(buf, b'>');
}
} else if state.no_empty {
io::buf_ccat(buf, b'>');
io::buf_add(buf, b"</" as *const u8, 2);
write_qname(buf, cur);
io::buf_ccat(buf, b'>');
} else if state.xhtml && !xhtml_is_empty(n.name) {
io::buf_ccat(buf, b'>');
io::buf_add(buf, b"</" as *const u8, 2);
write_qname(buf, cur);
io::buf_ccat(buf, b'>');
} else {
io::buf_add(buf, b"/>" as *const u8, 2);
}
} else {
if state.format == 1 {
let mut tmp = n.children;
while !tmp.is_null() {
let tt = unsafe { (*tmp).type_ };
if tt == XML_TEXT_NODE as c_int
|| tt == XML_CDATA_SECTION_NODE as c_int
|| tt == XML_ENTITY_REF_NODE as c_int
{
state.format = 0;
state.unformatted = cur;
break;
}
tmp = unsafe { (*tmp).next };
}
}
io::buf_ccat(buf, b'>');
if state.format == 1 {
io::buf_ccat(buf, b'\n');
}
if *level >= 0 {
*level += 1;
}
let mut child = n.children;
while !child.is_null() {
node_dump_internal(buf, child, root, cur, state, level);
if state.format == 1 {
let ct = unsafe { (*child).type_ };
if ct != XML_XINCLUDE_START as c_int && ct != XML_XINCLUDE_END as c_int {
io::buf_ccat(buf, b'\n');
}
}
child = unsafe { (*child).next };
}
if *level > 0 {
*level -= 1;
}
if state.format == 1 {
write_indent(buf, *level, state.indent, state.indent_len);
}
io::buf_add(buf, b"</" as *const u8, 2);
write_qname(buf, cur);
io::buf_ccat(buf, b'>');
if cur == state.unformatted {
state.format = state.saved;
state.unformatted = ptr::null_mut();
}
}
}
t if t == XML_TEXT_NODE as c_int => {
let esc = state.explicit_save
&& !state.as_html
&& save_escapes_non_ascii(state.encoding, n.doc);
if !n.content.is_null() {
if is_noenc_text(cur) {
io::buf_cat(buf, n.content);
} else {
serialize_text_flags(buf, n.content, xml_strlen(n.content), esc);
}
} else if !n.children.is_null() {
let c = node_get_content(cur);
if !c.is_null() {
if is_noenc_text(cur) {
io::buf_cat(buf, c);
} else {
serialize_text_flags(buf, c, xml_strlen(c), esc);
}
allocator::xmlFreeImpl(c as *mut c_void);
}
}
}
t if t == XML_CDATA_SECTION_NODE as c_int => {
if n.content.is_null() || unsafe { *n.content == 0 } {
io::buf_add(buf, b"<![CDATA[]]>" as *const u8, 12);
} else {
let len = xml_strlen(n.content) as usize;
let bytes = core::slice::from_raw_parts(n.content, len);
let mut i = 0usize;
let mut seg_start = 0usize;
while i < len {
if bytes[i] == b']'
&& i + 2 < len
&& bytes[i + 1] == b']'
&& bytes[i + 2] == b'>'
{
io::buf_add(buf, b"<![CDATA[" as *const u8, 9);
io::buf_add(buf, n.content.add(seg_start), (i + 2 - seg_start) as c_int);
io::buf_add(buf, b"]]>" as *const u8, 3);
seg_start = i + 2;
i += 3;
continue;
}
i += 1;
}
if seg_start < len {
io::buf_add(buf, b"<![CDATA[" as *const u8, 9);
io::buf_add(buf, n.content.add(seg_start), (len - seg_start) as c_int);
io::buf_add(buf, b"]]>" as *const u8, 3);
}
}
}
t if t == XML_COMMENT_NODE as c_int => {
if cur != root && state.format == 1 {
write_indent(buf, *level, state.indent, state.indent_len);
}
if !n.content.is_null() {
io::buf_add(buf, b"<!--" as *const u8, 4);
io::buf_cat(buf, n.content);
io::buf_add(buf, b"-->" as *const u8, 3);
}
}
t if t == XML_PI_NODE as c_int => {
if cur != root && state.format == 1 {
write_indent(buf, *level, state.indent, state.indent_len);
}
io::buf_add(buf, b"<?" as *const u8, 2);
if !n.name.is_null() {
io::buf_cat(buf, n.name);
}
if !n.content.is_null() && unsafe { *n.content != 0 } {
io::buf_ccat(buf, b' ');
io::buf_cat(buf, n.content);
}
io::buf_add(buf, b"?>" as *const u8, 2);
}
t if t == XML_ENTITY_REF_NODE as c_int => {
io::buf_ccat(buf, b'&');
if !n.name.is_null() {
io::buf_cat(buf, n.name);
}
io::buf_ccat(buf, b';');
}
t if t == XML_DOCUMENT_FRAG_NODE as c_int => {
let mut child = n.children;
while !child.is_null() {
let mut sublevel = *level;
node_dump_internal(buf, child, child, cur, state, &mut sublevel);
child = unsafe { (*child).next };
}
}
t if t == XML_DOCUMENT_NODE as c_int => {
doc_content_dump_output(buf, cur, state, level);
}
t if t == XML_HTML_DOCUMENT_NODE as c_int => {
if state.as_html {
crate::xml::html::serialize_node(cur, buf, state.format, *level);
} else {
doc_content_dump_output(buf, cur, state, level);
}
}
t if t == XML_DTD_NODE as c_int => {
dtd_dump_output(buf, cur, state, level);
}
t if t == XML_ATTRIBUTE_NODE as c_int => {
attr_dump_output(
buf,
cur as *mut _xmlAttr,
save_escapes_non_ascii(state.encoding, unsafe { (*cur).doc }),
);
}
t if t == XML_NAMESPACE_DECL as c_int => {
ns_dump_output(buf, cur as *mut _xmlNs);
}
t if t == XML_ELEMENT_DECL as c_int => {
dump_element_decl(buf, cur as *mut _xmlElement);
}
t if t == XML_ATTRIBUTE_DECL as c_int => {
dump_attribute_decl(buf, cur as *mut _xmlAttribute);
}
t if t == XML_ENTITY_DECL as c_int => {
dump_entity_decl(buf, cur as *mut _xmlEntity);
}
_ => {}
}
}
pub(crate) unsafe fn serialize_node(
node: *mut _xmlNode,
buf: *mut _xmlBuffer,
format: c_int,
level: c_int,
) {
unsafe { serialize_node_opt(node, buf, format, level, ptr::null()) };
}
pub(crate) unsafe fn serialize_node_opt(
node: *mut _xmlNode,
buf: *mut _xmlBuffer,
format: c_int,
level: c_int,
indent: *const xmlChar,
) {
unsafe { serialize_node_opts(node, buf, format, level, indent, 0) };
}
pub(crate) unsafe fn serialize_node_opts(
node: *mut _xmlNode,
buf: *mut _xmlBuffer,
format: c_int,
level: c_int,
indent: *const xmlChar,
no_decl: c_int,
) {
unsafe { serialize_node_opts_enc(node, buf, format, level, indent, no_decl, ptr::null()) };
}
pub(crate) unsafe fn serialize_node_opts_enc(
node: *mut _xmlNode,
buf: *mut _xmlBuffer,
format: c_int,
level: c_int,
indent: *const xmlChar,
no_decl: c_int,
encoding: *const xmlChar,
) {
unsafe {
serialize_node_opts_xhtml(node, buf, format, level, indent, no_decl, encoding, false)
};
}
#[allow(clippy::too_many_arguments)]
pub(crate) unsafe fn serialize_node_opts_enc_full(
node: *mut _xmlNode,
buf: *mut _xmlBuffer,
format: c_int,
level: c_int,
indent: *const xmlChar,
no_decl: c_int,
no_empty: c_int,
as_html: c_int,
encoding: *const xmlChar,
) {
unsafe {
if node.is_null() || buf.is_null() {
return;
}
let parent = (*node).parent;
let mut state = DumpState::with_indent_enc(format, indent, no_decl, encoding);
state.no_empty = no_empty != 0;
state.as_html = as_html != 0;
state.explicit_save = true;
let mut lvl = level;
node_dump_internal(buf, node, node, parent, &mut state, &mut lvl);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) unsafe fn serialize_node_opts_xhtml(
node: *mut _xmlNode,
buf: *mut _xmlBuffer,
format: c_int,
level: c_int,
indent: *const xmlChar,
no_decl: c_int,
encoding: *const xmlChar,
xhtml: bool,
) {
unsafe {
if node.is_null() || buf.is_null() {
return;
}
let parent = (*node).parent;
let mut state = DumpState::with_indent_enc(format, indent, no_decl, encoding);
state.xhtml = xhtml;
let mut lvl = level;
node_dump_internal(buf, node, node, parent, &mut state, &mut lvl);
}
}
pub(crate) unsafe fn doc_dump(buf: *mut _xmlBuffer, doc: *mut _xmlDoc) -> c_int {
if buf.is_null() || doc.is_null() {
return -1;
}
let before = io::buf_length(buf);
serialize_node(doc as *mut _xmlNode, buf, 0, 0);
let after = io::buf_length(buf);
if after < 0 || before < 0 {
return -1;
}
after - before
}
pub(crate) unsafe fn node_dump(
buf: *mut _xmlBuffer,
doc: *mut _xmlDoc,
node: *mut _xmlNode,
level: c_int,
format: c_int,
) -> c_int {
let _ = doc; if buf.is_null() || node.is_null() {
return -1;
}
let before = io::buf_length(buf);
serialize_node(node, buf, format, level);
let after = io::buf_length(buf);
if after < 0 || before < 0 {
return -1;
}
after - before
}
#[allow(dead_code)]
pub(crate) unsafe fn save_doc_to_fd(doc: *mut _xmlDoc, fd: c_int, _compression: c_int) -> c_int {
if doc.is_null() || fd < 0 {
return -1;
}
let out = io::output_buffer_create_fd(fd, ptr::null_mut());
if out.is_null() {
return -1;
}
let buf = io::buf_create(-1);
if buf.is_null() {
io::output_buffer_close(out);
return -1;
}
let ret = doc_dump(buf, doc);
if ret >= 0 {
io::output_buffer_write_string(out, io::buf_content(buf) as *const c_char);
io::output_buffer_flush(out);
}
io::buf_free(buf);
io::output_buffer_close(out);
ret
}
#[allow(dead_code)]
pub(crate) unsafe fn save_doc_to_buf(
doc: *mut _xmlDoc,
buf: *mut _xmlBuffer,
compression: c_int,
) -> c_int {
let _ = compression;
if doc.is_null() || buf.is_null() {
return -1;
}
doc_dump(buf, doc)
}
#[allow(dead_code)]
pub(crate) unsafe fn save_format_doc_to_buf(
doc: *mut _xmlDoc,
buf: *mut _xmlBuffer,
compression: c_int,
) -> c_int {
let _ = compression;
if doc.is_null() || buf.is_null() {
return -1;
}
let before = io::buf_length(buf);
serialize_node(doc as *mut _xmlNode, buf, 1, 0);
let after = io::buf_length(buf);
if after < 0 || before < 0 {
return -1;
}
after - before
}
#[allow(dead_code)]
pub(crate) unsafe fn dump_node(node: *mut _xmlNode) -> *mut xmlChar {
if node.is_null() {
return ptr::null_mut();
}
let buf = io::buf_create(-1);
if buf.is_null() {
return ptr::null_mut();
}
serialize_node(node, buf, 0, 0);
let content = io::buf_content(buf);
if content.is_null() {
io::buf_free(buf);
return ptr::null_mut();
}
let result = dup_xml_str(content);
io::buf_free(buf);
result
}
pub unsafe fn dump_doc(doc: *mut _xmlDoc) -> *mut xmlChar {
if doc.is_null() {
return ptr::null_mut();
}
let buf = io::buf_create(-1);
if buf.is_null() {
return ptr::null_mut();
}
serialize_node(doc as *mut _xmlNode, buf, 0, 0);
let content = io::buf_content(buf);
if content.is_null() {
io::buf_free(buf);
return ptr::null_mut();
}
let result = dup_xml_str(content);
io::buf_free(buf);
result
}
pub(crate) unsafe fn xmlNodeDump(
buf: *mut _xmlBuffer,
doc: *mut _xmlDoc,
node: *mut _xmlNode,
level: c_int,
format: c_int,
) -> c_int {
node_dump(buf, doc, node, level, format)
}
pub(crate) unsafe fn xmlDocDump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
if fp.is_null() || doc.is_null() {
return -1;
}
let buf = io::buf_create(-1);
if buf.is_null() {
return -1;
}
let ret = doc_dump(buf, doc);
if ret < 0 {
io::buf_free(buf);
return -1;
}
let content = io::buf_content(buf);
let len = io::buf_length(buf);
if !content.is_null() && len > 0 {
let written = libc::fwrite(
content as *const c_void,
1,
len as usize,
fp as *mut libc::FILE,
);
io::buf_free(buf);
written as c_int
} else {
io::buf_free(buf);
0
}
}
pub(crate) unsafe fn xmlDocDumpFormatMemory(
doc: *mut _xmlDoc,
mem: *mut *mut xmlChar,
size: *mut c_int,
format: c_int,
) {
if doc.is_null() || mem.is_null() {
return;
}
let buf = io::buf_create(-1);
if buf.is_null() {
unsafe {
*mem = ptr::null_mut();
if !size.is_null() {
*size = 0;
}
}
return;
}
serialize_node(doc as *mut _xmlNode, buf, format, 0);
let content = io::buf_content(buf);
let len = io::buf_length(buf);
if !content.is_null() && len > 0 {
let result = allocator::xmlMallocImpl((len + 1) as usize) as *mut xmlChar;
if !result.is_null() {
ptr::copy_nonoverlapping(content, result, len as usize);
*result.add(len as usize) = 0;
unsafe {
*mem = result;
if !size.is_null() {
*size = len;
}
}
} else {
unsafe {
*mem = ptr::null_mut();
if !size.is_null() {
*size = 0;
}
}
}
} else {
unsafe {
*mem = ptr::null_mut();
if !size.is_null() {
*size = 0;
}
}
}
io::buf_free(buf);
}
pub(crate) unsafe fn xmlDocDumpMemory(doc: *mut _xmlDoc, mem: *mut *mut xmlChar, size: *mut c_int) {
xmlDocDumpFormatMemory(doc, mem, size, 0)
}
pub(crate) unsafe fn xmlSaveFile(filename: *const c_char, cur: *mut _xmlDoc) -> c_int {
unsafe { xmlSaveFormatFileEnc(filename, cur, ptr::null(), 0) }
}
pub(crate) unsafe fn xmlSaveFileEnc(
filename: *const c_char,
cur: *mut _xmlDoc,
encoding: *const c_char,
) -> c_int {
unsafe { xmlSaveFormatFileEnc(filename, cur, encoding, 0) }
}
pub(crate) unsafe fn xmlSaveFormatFile(
filename: *const c_char,
cur: *mut _xmlDoc,
format: c_int,
) -> c_int {
unsafe { xmlSaveFormatFileEnc(filename, cur, ptr::null(), format) }
}
pub(crate) unsafe fn xmlSaveFormatFileEnc(
filename: *const c_char,
cur: *mut _xmlDoc,
encoding: *const c_char,
format: c_int,
) -> c_int {
if cur.is_null() {
return -1;
}
let options = if format != 0 {
crate::xml::save::XML_SAVE_FORMAT
} else {
0
};
let ctxt = crate::xml::save::xmlSaveToFilename(filename, encoding, options);
if ctxt.is_null() {
return -1;
}
crate::xml::save::xmlSaveDoc(ctxt, cur);
crate::xml::save::xmlSaveClose(ctxt)
}
pub(crate) unsafe fn xmlGetDocCompressMode(doc: *mut _xmlDoc) -> c_int {
if doc.is_null() {
return -1;
}
unsafe { (*doc).compression }
}
pub(crate) unsafe fn xmlSetDocCompressMode(doc: *mut _xmlDoc, mode: c_int) {
if doc.is_null() {
return;
}
unsafe {
(*doc).compression = mode;
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::ffi::c_void;
fn c_str(s: &str) -> *const xmlChar {
let bytes = s.as_bytes();
let buf = unsafe { allocator::xmlMallocImpl(bytes.len() + 1) as *mut u8 };
if !buf.is_null() {
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
*buf.add(bytes.len()) = 0;
}
}
buf as *const xmlChar
}
#[test]
fn test_new_free_doc() {
unsafe {
let doc = new_doc(ptr::null());
assert!(!doc.is_null());
assert_eq!((*doc).type_, XML_DOCUMENT_NODE as c_int);
assert_eq!((*doc).standalone, -1);
assert_eq!((*doc).doc, doc);
assert!(!(*doc).version.is_null());
free_doc(doc);
}
}
#[test]
fn test_new_doc_with_version() {
unsafe {
let ver = c_str("2.0");
let doc = new_doc(ver);
assert!(!doc.is_null());
let doc_ver = (*doc).version;
assert!(!doc_ver.is_null());
assert!(crate::abi::exports_xml2::xmlStrEqual(doc_ver, ver,) != 0);
allocator::xmlFreeImpl(ver as *mut c_void);
free_doc(doc);
}
}
#[test]
fn test_new_node() {
unsafe {
let doc = new_doc(ptr::null());
let node = new_node(ptr::null_mut(), c_str("root"));
assert!(!node.is_null());
assert_eq!((*node).type_, XML_ELEMENT_NODE as c_int);
assert!(!(*node).name.is_null());
free_node(node);
free_doc(doc);
}
}
#[test]
fn test_node_get_content_recurses_descendants() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("library"));
doc_set_root_element(doc, root);
let book = new_child(root, ptr::null_mut(), c_str("book"));
let title = new_child(book, ptr::null_mut(), c_str("title"));
let text = new_text(c_str("Rust"));
add_child(title, text);
let content = node_get_content(book);
assert!(!content.is_null());
let s = core::slice::from_raw_parts(
content,
libc::strlen(content as *const libc::c_char) as usize,
);
assert_eq!(s, b"Rust", "descendant text not concatenated");
allocator::xmlFreeImpl(content as *mut c_void);
free_doc(doc);
}
}
#[test]
fn test_doc_set_root_element() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
let old = doc_set_root_element(doc, root);
assert!(old.is_null());
assert_eq!(doc_get_root_element(doc), root);
assert_eq!((*doc).children, root);
free_doc(doc);
}
}
#[test]
fn test_add_child_and_sibling() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let child1 = new_child(root, ptr::null_mut(), c_str("child1"));
assert!(!child1.is_null());
assert_eq!((*child1).parent, root);
assert_eq!((*root).children, child1);
assert_eq!((*root).last, child1);
let child2 = new_child(root, ptr::null_mut(), c_str("child2"));
assert!(!child2.is_null());
assert_eq!((*child2).parent, root);
assert_eq!((*child1).next, child2);
assert_eq!((*child2).prev, child1);
assert_eq!((*root).last, child2);
let sibling = new_node(ptr::null_mut(), c_str("sibling"));
add_sibling(child2, sibling);
assert_eq!((*child2).next, sibling);
assert_eq!((*sibling).prev, child2);
assert_eq!((*root).last, sibling);
free_doc(doc);
}
}
#[test]
fn test_add_child_attribute_goes_to_properties() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("chapter"));
doc_set_root_element(doc, root);
let attr = crate::abi::exports_tree::xmlNewProp(
ptr::null_mut(),
c"num".as_ptr() as *const crate::abi::types::xmlChar,
c"1".as_ptr() as *const crate::abi::types::xmlChar,
);
assert!(!attr.is_null());
assert_eq!((*attr).type_, XML_ATTRIBUTE_NODE as c_int);
let ret = add_child(root, attr as *mut _xmlNode);
assert_eq!(ret, attr as *mut _xmlNode);
assert_eq!((*root).properties, attr);
assert!(
(*root).children.is_null(),
"attr must NOT become a child node"
);
assert!(!(*attr).children.is_null());
assert_eq!((*(*attr).children).parent, attr as *mut _xmlNode);
free_doc(doc);
}
}
#[test]
fn test_set_ns_prop_binds_namespace() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let ns = new_ns(root, c_str("urn:a"), c_str("a"));
let attr = set_ns_prop(root, ns, c_str("a"), c_str("urn:a"));
assert!(!attr.is_null());
assert_eq!((*root).properties, attr);
assert_eq!((*attr).ns, ns);
assert!(!(*attr).name.is_null());
free_doc(doc);
}
}
#[test]
fn test_search_ns_by_href_xml_namespace_returns_xml_prefix() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let found = search_ns_by_href(
doc,
root,
c"http://www.w3.org/XML/1998/namespace".as_ptr() as *const xmlChar,
);
assert!(!found.is_null());
assert!(!(*found).prefix.is_null());
assert!(
crate::abi::exports_xml2::xmlStrEqual(
(*found).prefix,
c"xml".as_ptr() as *const xmlChar
) != 0
);
free_doc(doc);
}
}
#[test]
fn test_free_text_node_with_static_name_sentinel() {
unsafe {
let node = allocator::xmlMallocZero(size_of::<_xmlNode>()) as *mut _xmlNode;
assert!(!node.is_null());
(*node).type_ = XML_TEXT_NODE as c_int;
(*node).name = crate::abi::data_globals::xmlStringText.as_ptr();
free_node(node); }
}
#[test]
fn test_unlink_node() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let child1 = new_child(root, ptr::null_mut(), c_str("c1"));
let child2 = new_child(root, ptr::null_mut(), c_str("c2"));
unlink_node(child1);
assert!((*child1).parent.is_null());
assert!((*child1).prev.is_null());
assert!((*child1).next.is_null());
assert_eq!((*root).children, child2);
assert_eq!((*root).last, child2);
free_node(child1);
free_doc(doc);
}
}
#[test]
fn test_text_and_comment_nodes() {
unsafe {
let text = new_text(c_str("hello world"));
assert!(!text.is_null());
assert_eq!((*text).type_, XML_TEXT_NODE as c_int);
assert!(!(*text).content.is_null());
free_node(text);
let comment = new_comment(c_str("my comment"));
assert!(!comment.is_null());
assert_eq!((*comment).type_, XML_COMMENT_NODE as c_int);
free_node(comment);
let pi = new_pi(c_str("xml"), c_str("version='1.0'"));
assert!(!pi.is_null());
assert_eq!((*pi).type_, XML_PI_NODE as c_int);
free_node(pi);
}
}
#[test]
fn test_set_and_get_prop() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let attr = set_prop(root, c_str("id"), c_str("42"));
assert!(!attr.is_null());
assert_eq!((*attr).type_, XML_ATTRIBUTE_NODE as c_int);
let value = get_prop(root, c_str("id"));
assert!(!value.is_null());
assert!(crate::abi::exports_xml2::xmlStrEqual(value, c_str("42")) != 0);
allocator::xmlFreeImpl(value as *mut c_void);
free_doc(doc);
}
}
#[test]
fn test_remove_prop() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
set_prop(root, c_str("a"), c_str("1"));
set_prop(root, c_str("b"), c_str("2"));
let value = get_prop(root, c_str("a"));
assert!(!value.is_null());
allocator::xmlFreeImpl(value as *mut c_void);
let attr = (*root).properties;
assert!(!attr.is_null());
let result = remove_prop(attr);
assert_eq!(result, 0);
let value2 = get_prop(root, c_str("a"));
assert!(value2.is_null());
free_doc(doc);
}
}
#[test]
fn test_namespace_operations() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let ns = new_ns(root, c_str("http://example.com"), c_str("ex"));
assert!(!ns.is_null());
assert!(!(*root).nsDef.is_null());
set_ns(root, ns);
assert_eq!((*root).ns, ns);
let found = search_ns(doc, root, c_str("ex"));
assert_eq!(found, ns);
let found_href = search_ns_by_href(doc, root, c_str("http://example.com"));
assert_eq!(found_href, ns);
free_doc(doc);
}
}
#[test]
fn test_new_dtd() {
unsafe {
let doc = new_doc(ptr::null());
let dtd = new_dtd(doc, c_str("root"), c_str("-//TEST//DTD"), c_str("test.dtd"));
assert!(!dtd.is_null());
assert_eq!((*dtd).type_, XML_DTD_NODE as c_int);
assert_eq!((*doc).extSubset, dtd);
free_doc(doc);
}
}
#[test]
fn test_copy_node_deep() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let _child = new_child(root, ptr::null_mut(), c_str("child"));
let copy = copy_node(root, 1);
assert!(!copy.is_null());
assert_eq!((*copy).type_, XML_ELEMENT_NODE as c_int);
assert!(!(*copy).children.is_null());
assert_eq!((*(*copy).children).type_, XML_ELEMENT_NODE as c_int);
free_node(copy);
free_doc(doc);
}
}
#[test]
fn test_new_cdata_block() {
unsafe {
let doc = new_doc(ptr::null());
let content = c_str("some <cdata> content");
let cdata = new_cdata_block(doc, content, 20);
assert!(!cdata.is_null());
assert_eq!((*cdata).type_, XML_CDATA_SECTION_NODE as c_int);
free_node(cdata);
free_doc(doc);
}
}
#[test]
fn test_free_deeply_nested_chain_is_iterative() {
unsafe {
let doc = new_doc(c"1.0".as_ptr() as *const xmlChar);
assert!(!doc.is_null());
const DEPTH: usize = 500_000;
let root = new_node(ptr::null_mut(), c_str("a"));
assert!(!root.is_null());
doc_set_root_element(doc, root);
let mut child = root;
for _ in 0..DEPTH {
let next = new_node(ptr::null_mut(), c_str("a"));
assert!(!next.is_null());
assert!(!add_child(child, next).is_null());
child = next;
}
free_doc(doc);
}
}
#[test]
fn test_null_handling() {
unsafe {
assert!(!new_doc(ptr::null()).is_null()); let doc = new_doc(ptr::null());
assert!(new_node(ptr::null_mut(), ptr::null()).is_null());
free_node(ptr::null_mut()); free_doc(ptr::null_mut()); unlink_node(ptr::null_mut()); assert!(add_child(ptr::null_mut(), ptr::null_mut()).is_null());
assert!(add_sibling(ptr::null_mut(), ptr::null_mut()).is_null());
free_doc(doc);
}
}
unsafe fn buf_eq_str(buf: *mut _xmlBuffer, expected: &str) -> bool {
let content = io::buf_content(buf);
if content.is_null() {
return expected.is_empty();
}
let len = io::buf_length(buf) as usize;
if len != expected.len() {
return false;
}
let slice = unsafe { core::slice::from_raw_parts(content, len) };
slice == expected.as_bytes()
}
#[test]
fn test_serialize_empty_document() {
unsafe {
let doc = new_doc(ptr::null());
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_element_with_text() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let text = new_text(c_str("hello world"));
add_child(root, text);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<root>hello world</root>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_element_with_attributes() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
set_prop(root, c_str("id"), c_str("42"));
set_prop(root, c_str("name"), c_str("test"));
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<root id=\"42\" name=\"test\"/>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_nested_elements() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let child = new_child(root, ptr::null_mut(), c_str("child"));
let grandchild = new_child(child, ptr::null_mut(), c_str("gc"));
let text = new_text(c_str("text"));
add_child(grandchild, text);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<root><child><gc>text</gc></child></root>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_with_formatting() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let child = new_child(root, ptr::null_mut(), c_str("child"));
let text = new_text(c_str("text"));
add_child(child, text);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
serialize_node(doc as *mut _xmlNode, buf, 1, 0);
let expected = "<?xml version=\"1.0\"?>\n<root>\n <child>text</child>\n</root>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_escape_ampersand() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let text = new_text(c_str("a & b"));
add_child(root, text);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<root>a & b</root>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_escape_angle_brackets() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let text = new_text(c_str("x < y > z"));
add_child(root, text);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<root>x < y > z</root>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_comment() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let comment = new_comment(c_str("my comment"));
add_child(root, comment);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<root><!--my comment--></root>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_pi() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let pi = new_pi(
c_str("xml-stylesheet"),
c_str("href=\"style.xsl\" type=\"text/xsl\""),
);
add_child(root, pi);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<root><?xml-stylesheet href=\"style.xsl\" type=\"text/xsl\"?></root>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_serialize_self_closing() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("empty"));
doc_set_root_element(doc, root);
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected = "<?xml version=\"1.0\"?>\n<empty/>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_dump_node_to_string() {
unsafe {
let node = new_node(ptr::null_mut(), c_str("foo"));
let text = new_text(c_str("bar"));
add_child(node, text);
let result = dump_node(node);
assert!(!result.is_null());
let len = xml_strlen(result);
let slice = { core::slice::from_raw_parts(result, len as usize) };
assert_eq!(slice, b"<foo>bar</foo>");
allocator::xmlFreeImpl(result as *mut c_void);
free_node(node);
}
}
#[test]
fn test_dump_doc_to_string() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let result = dump_doc(doc);
assert!(!result.is_null());
let len = xml_strlen(result);
let slice = { core::slice::from_raw_parts(result, len as usize) };
let expected = "<?xml version=\"1.0\"?>\n<root/>\n";
assert_eq!(slice, expected.as_bytes());
allocator::xmlFreeImpl(result as *mut c_void);
free_doc(doc);
}
}
#[test]
fn test_xmlDocDumpFormatMemory() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let mut mem: *mut xmlChar = ptr::null_mut();
let mut size: c_int = 0;
xmlDocDumpFormatMemory(doc, &mut mem, &mut size, 0);
assert!(!mem.is_null());
assert!(size > 0);
let slice = { core::slice::from_raw_parts(mem, size as usize) };
let expected = "<?xml version=\"1.0\"?>\n<root/>\n";
assert_eq!(slice, expected.as_bytes());
allocator::xmlFreeImpl(mem as *mut c_void);
free_doc(doc);
}
}
#[test]
fn test_serialize_escape_attribute() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
set_prop(root, c_str("desc"), c_str("a < b & c \"quoted\""));
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = doc_dump(buf, doc);
assert!(ret >= 0);
let expected =
"<?xml version=\"1.0\"?>\n<root desc=\"a < b & c "quoted"\"/>\n";
assert!(buf_eq_str(buf, expected));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_dump_document_fragment_serializes_children() {
unsafe {
let doc = new_doc(ptr::null());
let frag = crate::abi::exports_tree::xmlNewDocFragment(doc);
assert!(!frag.is_null());
let foo = new_child(frag, ptr::null_mut(), c_str("foo"));
assert!(!foo.is_null());
let buf = io::buf_create(-1);
assert!(!buf.is_null());
let ret = node_dump(buf, doc, frag, 0, 0);
assert!(ret >= 0);
assert!(buf_eq_str(buf, "<foo/>"));
io::buf_free(buf);
free_doc(doc);
}
}
#[test]
fn test_copy_doc_keeps_doc_as_root_parent() {
unsafe {
let doc = new_doc(ptr::null());
let root = new_node(ptr::null_mut(), c_str("root"));
doc_set_root_element(doc, root);
let child = new_child(root, ptr::null_mut(), c_str("kid"));
assert!(!child.is_null());
let copy = copy_doc(doc, 1);
assert!(!copy.is_null());
let copied_root = (*copy).children;
assert!(!copied_root.is_null());
assert_eq!((*copied_root).parent as *mut c_void, copy as *mut c_void);
assert_eq!((*copied_root).doc as *mut c_void, copy as *mut c_void);
assert_eq!((*copy).last as *mut c_void, copied_root as *mut c_void);
let copied_child = (*copied_root).children;
assert!(!copied_child.is_null());
assert_eq!(
(*copied_child).parent as *mut c_void,
copied_root as *mut c_void
);
free_doc(copy);
free_doc(doc);
}
}
}