use std::{cmp::Ordering, mem::take, ptr::null_mut, rc::Rc};
use crate::{
error::{__xml_raise_error, XmlErrorDomain, XmlErrorLevel, XmlParserErrors},
io::{XmlOutputBuffer, write_quoted},
list::XmlList,
tree::{
NodeCommon, XML_XML_NAMESPACE, XmlAttr, XmlAttrPtr, XmlDocPtr, XmlElementType,
XmlGenericNodePtr, XmlNode, XmlNodePtr, XmlNs, XmlNsPtr, xml_free_prop_list,
xml_new_ns_prop,
},
uri::{XmlURI, build_uri},
xpath::XmlNodeSet,
};
#[doc(alias = "xmlC14NMode")]
#[repr(C)]
pub enum XmlC14NMode {
XmlC14N1_0 = 0,
XmlC14NExclusive1_0 = 1,
XmlC14N1_1 = 2,
}
#[repr(C)]
pub enum XmlC14NPosition {
XmlC14NBeforeDocumentElement = 0,
XmlC14NInsideDocumentElement = 1,
XmlC14NAfterDocumentElement = 2,
}
#[repr(C)]
#[derive(Default)]
pub struct XmlC14NVisibleNsStack {
ns_cur_end: usize,
ns_prev_start: usize,
ns_prev_end: usize,
ns_tab: Vec<XmlNsPtr>,
node_tab: Vec<XmlNodePtr>,
}
impl XmlC14NVisibleNsStack {
#[doc(alias = "xmlC14NVisibleNsStackSave")]
fn save(&self, state: &mut XmlC14NVisibleNsStack) {
state.ns_cur_end = self.ns_cur_end;
state.ns_prev_start = self.ns_prev_start;
state.ns_prev_end = self.ns_prev_end;
}
#[doc(alias = "xmlC14NVisibleNsStackFind")]
fn find(&self, ns: &XmlNs) -> bool {
let prefix = ns.prefix();
let prefix = prefix.as_deref().unwrap_or("");
let href = ns.href();
let href = href.as_deref().unwrap_or("");
let has_empty_ns =
xml_c14n_str_equal(Some(prefix), None) && xml_c14n_str_equal(Some(href), None);
let start = if has_empty_ns { 0 } else { self.ns_prev_start };
for &ns1 in self.ns_tab[start..self.ns_cur_end].iter().rev() {
if xml_c14n_str_equal(Some(prefix), ns1.prefix().as_deref()) {
return xml_c14n_str_equal(Some(href), ns1.href().as_deref());
}
}
has_empty_ns
}
#[doc(alias = "xmlC14NVisibleNsStackAdd")]
fn add(&mut self, ns: XmlNsPtr, node: XmlNodePtr) {
if self.ns_cur_end == self.ns_tab.len() {
self.ns_tab.push(ns);
self.node_tab.push(node);
} else {
self.ns_tab[self.ns_cur_end] = ns;
self.node_tab[self.ns_cur_end] = node;
}
self.ns_cur_end += 1;
}
#[doc(alias = "xmlC14NVisibleNsStackShift")]
fn shift(&mut self) {
self.ns_prev_start = self.ns_prev_end;
self.ns_prev_end = self.ns_cur_end;
}
#[doc(alias = "xmlC14NVisibleNsStackRestore")]
fn restore(&mut self, state: &XmlC14NVisibleNsStack) {
self.ns_cur_end = state.ns_cur_end;
self.ns_prev_start = state.ns_prev_start;
self.ns_prev_end = state.ns_prev_end;
}
}
#[doc(alias = "xmlC14NIsVisibleCallback")]
pub type XmlC14NIsVisibleCallback<T> =
fn(user_data: &T, node: Option<XmlGenericNodePtr>, parent: Option<XmlGenericNodePtr>) -> i32;
#[repr(C)]
pub struct XmlC14NCtx<'a, T> {
doc: XmlDocPtr,
is_visible_callback: Option<XmlC14NIsVisibleCallback<T>>,
user_data: T,
with_comments: bool,
buf: XmlOutputBuffer<'a>,
pos: XmlC14NPosition,
parent_is_doc: bool,
ns_rendered: Box<XmlC14NVisibleNsStack>,
mode: XmlC14NMode,
inclusive_ns_prefixes: Option<Vec<String>>,
error: XmlParserErrors,
}
impl<T> XmlC14NCtx<'_, T> {
fn is_visible(
&self,
node: Option<XmlGenericNodePtr>,
parent: Option<XmlGenericNodePtr>,
) -> bool {
if let Some(callback) = self.is_visible_callback {
callback(&self.user_data, node, parent) != 0
} else {
true
}
}
fn is_exclusive(&self) -> bool {
matches!(self.mode, XmlC14NMode::XmlC14NExclusive1_0)
}
#[doc(alias = "xmlC14NCheckForRelativeNamespaces")]
fn check_for_relative_namespaces(&self, cur: &XmlNode) -> i32 {
if !matches!(cur.element_type(), XmlElementType::XmlElementNode) {
xml_c14n_err_param("checking for relative namespaces");
return -1;
}
let mut ns = cur.ns_def;
while let Some(now) = ns {
let href = now.href().unwrap();
if !href.is_empty() {
let Some(uri) = XmlURI::parse(&href) else {
xml_c14n_err_internal("parsing namespace uri");
return -1;
};
let scheme = uri.scheme.as_deref().unwrap();
if scheme.is_empty() {
xml_c14n_err_relative_namespace(scheme);
return -1;
}
}
ns = now.next;
}
0
}
#[doc(alias = "xmlC14NPrintNamespaces")]
fn print_namespaces(&mut self, ns: &XmlNs) -> i32 {
if let Some(prefix) = ns.prefix() {
self.buf.write_str(" xmlns:").ok();
self.buf.write_str(&prefix).ok();
self.buf.write_str("=").ok();
} else {
self.buf.write_str(" xmlns=").ok();
}
if let Some(href) = ns.href.as_deref() {
write_quoted(&mut self.buf, href).ok();
} else {
self.buf.write_str("\"\"").ok();
}
1
}
#[doc(alias = "xmlC14NProcessNamespacesAxis")]
fn process_namespaces_axis(&mut self, mut cur: XmlNodePtr, visible: bool) -> i32 {
let mut has_empty_ns = false;
if !matches!(cur.element_type(), XmlElementType::XmlElementNode) {
xml_c14n_err_param("processing namespaces axis (c14n)");
return -1;
}
let mut list = XmlList::new(None, Rc::new(|ns1, ns2| xml_c14n_ns_compare(*ns1, *ns2)));
let mut n = Some(cur);
while let Some(node) = n {
let mut ns = node.ns_def;
while let Some(now) = ns {
let prefix = now.prefix();
let cur_doc = cur.doc;
let tmp = cur.search_ns(cur_doc, prefix.as_deref());
if tmp == Some(now)
&& !xml_c14n_is_xml_ns(now)
&& self.is_visible(Some(now.into()), Some(cur.into()))
{
let already_rendered = (*self.ns_rendered).find(&now);
if visible {
(*self.ns_rendered).add(now, cur);
}
if !already_rendered {
list.insert_lower_bound(now);
}
if now.prefix().map_or(0, |pre| pre.len()) == 0 {
has_empty_ns = true;
}
}
ns = now.next;
}
n = node.parent().and_then(|p| XmlNodePtr::try_from(p).ok());
}
if visible && !has_empty_ns && !(*self.ns_rendered).find(&XmlNs::default()) {
self.print_namespaces(&XmlNs::default());
}
list.walk(|data| self.print_namespaces(data) != 0);
0
}
#[doc(alias = "xmlC14NProcessAttrsAxis")]
fn process_attrs_axis(&mut self, cur: XmlNodePtr, parent_visible: bool) -> i32 {
let mut attrs_to_delete = None::<XmlAttrPtr>;
if !matches!(cur.element_type(), XmlElementType::XmlElementNode) {
xml_c14n_err_param("processing attributes axis");
return -1;
}
let mut list = XmlList::new(
None,
Rc::new(|&attr1, &attr2| xml_c14n_attrs_compare(attr1, attr2)),
);
match self.mode {
XmlC14NMode::XmlC14N1_0 => {
let mut attr = cur.properties;
while let Some(now) = attr {
if self.is_visible(Some(now.into()), Some(cur.into())) {
list.insert_lower_bound(now);
}
attr = now.next;
}
if parent_visible
&& cur.parent().is_some()
&& !self.is_visible(cur.parent(), cur.parent().and_then(|cur| cur.parent()))
{
let mut tmp = cur
.parent()
.and_then(|node| XmlNodePtr::try_from(node).ok());
while let Some(cur_node) = tmp {
let mut attr = cur_node.properties;
while let Some(now) = attr {
if xml_c14n_is_xml_attr(now) && list.search(&now).is_none() {
list.insert_lower_bound(now);
}
attr = now.next;
}
tmp = cur_node.parent().and_then(|p| XmlNodePtr::try_from(p).ok());
}
}
}
XmlC14NMode::XmlC14NExclusive1_0 => {
let mut attr = cur.properties;
while let Some(now) = attr {
if self.is_visible(Some(now.into()), Some(cur.into())) {
list.insert_lower_bound(now);
}
attr = now.next;
}
}
XmlC14NMode::XmlC14N1_1 => {
let mut xml_base_attr = None;
let mut xml_lang_attr = None;
let mut xml_space_attr = None;
let mut attr = cur.properties;
while let Some(now) = attr {
if !parent_visible || !xml_c14n_is_xml_attr(now) {
if self.is_visible(Some(now.into()), Some(cur.into())) {
list.insert_lower_bound(now);
}
} else {
let mut matched: i32 = 0;
if matched == 0
&& xml_lang_attr.is_none()
&& now.name().as_deref() == Some("lang")
{
xml_lang_attr = attr;
matched = 1;
}
if matched == 0
&& xml_space_attr.is_none()
&& now.name().as_deref() == Some("space")
{
xml_space_attr = attr;
matched = 1;
}
if matched == 0
&& xml_base_attr.is_none()
&& now.name().as_deref() == Some("base")
{
xml_base_attr = attr;
matched = 1;
}
if matched == 0 && self.is_visible(Some(now.into()), Some(cur.into())) {
list.insert_lower_bound(now);
}
}
attr = now.next;
}
if parent_visible {
if xml_lang_attr.is_none() {
xml_lang_attr =
self.find_hidden_parent_attr(cur.parent(), "lang", XML_XML_NAMESPACE);
}
if let Some(attr) = xml_lang_attr {
list.insert_lower_bound(attr);
}
if xml_space_attr.is_none() {
xml_space_attr =
self.find_hidden_parent_attr(cur.parent(), "space", XML_XML_NAMESPACE);
}
if let Some(attr) = xml_space_attr {
list.insert_lower_bound(attr);
}
if xml_base_attr.is_none() {
xml_base_attr =
self.find_hidden_parent_attr(cur.parent(), "base", XML_XML_NAMESPACE);
}
if let Some(attr) = xml_base_attr {
xml_base_attr = self.fixup_base_attr(&attr);
if let Some(mut attr) = xml_base_attr {
list.insert_lower_bound(attr);
attr.next = attrs_to_delete;
attrs_to_delete = Some(attr);
}
}
}
}
}
list.walk(|data| self.print_attrs(*data));
unsafe {
xml_free_prop_list(attrs_to_delete);
}
0
}
#[doc(alias = "xmlC14NProcessElementNode")]
fn process_element_node(&mut self, cur: XmlNodePtr, visible: bool) -> i32 {
let mut parent_is_doc = false;
if !matches!(cur.element_type(), XmlElementType::XmlElementNode) {
xml_c14n_err_param("processing element node");
return -1;
}
if self.check_for_relative_namespaces(&cur) < 0 {
xml_c14n_err_internal("checking for relative namespaces");
return -1;
}
let mut state: XmlC14NVisibleNsStack = XmlC14NVisibleNsStack::default();
self.ns_rendered.save(&mut state);
if visible {
if self.parent_is_doc {
parent_is_doc = self.parent_is_doc;
self.parent_is_doc = false;
self.pos = XmlC14NPosition::XmlC14NInsideDocumentElement;
}
self.buf.write_str("<").ok();
if let Some(prefix) = cur.ns.as_deref().and_then(|ns| ns.prefix()) {
self.buf.write_str(&prefix).ok();
self.buf.write_str(":").ok();
}
self.buf.write_str(&cur.name).ok();
}
let ret = if !self.is_exclusive() {
self.process_namespaces_axis(cur, visible)
} else {
self.exc_c14n_process_namespaces_axis(cur, visible)
};
if ret < 0 {
xml_c14n_err_internal("processing namespaces axis");
return -1;
}
if visible {
(*self.ns_rendered).shift();
}
let ret = self.process_attrs_axis(cur, visible);
if ret < 0 {
xml_c14n_err_internal("processing attributes axis");
return -1;
}
if visible {
self.buf.write_str(">").ok();
}
if let Some(children) = cur.children() {
let ret = self.process_node_list(Some(children));
if ret < 0 {
xml_c14n_err_internal("processing childrens list");
return -1;
}
}
if visible {
self.buf.write_str("</").ok();
if let Some(prefix) = cur.ns.as_deref().and_then(|ns| ns.prefix()) {
self.buf.write_str(&prefix).ok();
self.buf.write_str(":").ok();
}
self.buf.write_str(&cur.name).ok();
self.buf.write_str(">").ok();
if parent_is_doc {
self.parent_is_doc = parent_is_doc;
self.pos = XmlC14NPosition::XmlC14NAfterDocumentElement;
}
}
(*self.ns_rendered).restore(&state);
0
}
#[doc(alias = "xmlC14NProcessNodeList")]
fn process_node_list(&mut self, mut cur: Option<XmlGenericNodePtr>) -> i32 {
let mut ret = 0;
while let Some(now) = cur {
ret = self.process_node(now);
cur = now.next();
if ret < 0 {
break;
}
}
ret
}
#[doc(alias = "xmlC14NProcessNode")]
fn process_node(&mut self, cur: XmlGenericNodePtr) -> i32 {
let mut ret: i32 = 0;
let visible = self.is_visible(Some(cur), cur.parent());
match cur.element_type() {
XmlElementType::XmlElementNode => {
let cur = XmlNodePtr::try_from(cur).unwrap();
ret = self.process_element_node(cur, visible);
}
XmlElementType::XmlCDATASectionNode | XmlElementType::XmlTextNode => {
let cur = XmlNodePtr::try_from(cur).unwrap();
if let Some(content) = cur.content.as_deref().filter(|_| visible) {
let buffer = normalize_text(content);
self.buf.write_str(&buffer).ok();
}
}
XmlElementType::XmlPINode => {
if visible {
if matches!(self.pos, XmlC14NPosition::XmlC14NAfterDocumentElement) {
self.buf.write_str("\x0A<?").ok();
} else {
self.buf.write_str("<?").ok();
}
self.buf.write_str(&cur.name().unwrap()).ok();
let cur = XmlNodePtr::try_from(cur).unwrap();
if let Some(content) = cur.content.as_deref().filter(|cont| !cont.is_empty()) {
self.buf.write_str(" ").ok();
let buffer = normalize_pi(content);
self.buf.write_str(&buffer).ok();
}
if matches!(self.pos, XmlC14NPosition::XmlC14NBeforeDocumentElement) {
self.buf.write_str("?>\x0A").ok();
} else {
self.buf.write_str("?>").ok();
}
}
}
XmlElementType::XmlCommentNode => {
if visible && self.with_comments {
if matches!(self.pos, XmlC14NPosition::XmlC14NAfterDocumentElement) {
self.buf.write_str("\x0A<!--").ok();
} else {
self.buf.write_str("<!--").ok();
}
let cur = XmlNodePtr::try_from(cur).unwrap();
if let Some(content) = cur.content.as_deref() {
let buffer = normalize_comment(content);
self.buf.write_str(&buffer).ok();
}
if matches!(self.pos, XmlC14NPosition::XmlC14NBeforeDocumentElement) {
self.buf.write_str("-->\x0A").ok();
} else {
self.buf.write_str("-->").ok();
}
}
}
XmlElementType::XmlDocumentNode | XmlElementType::XmlDocumentFragNode => {
if let Some(children) = cur.children() {
self.pos = XmlC14NPosition::XmlC14NBeforeDocumentElement;
self.parent_is_doc = true;
ret = self.process_node_list(Some(children));
}
}
#[cfg(feature = "html")]
XmlElementType::XmlHTMLDocumentNode => {
if let Some(children) = cur.children() {
self.pos = XmlC14NPosition::XmlC14NBeforeDocumentElement;
self.parent_is_doc = true;
ret = self.process_node_list(Some(children));
}
}
XmlElementType::XmlAttributeNode => {
xml_c14n_err_invalid_node("XML_ATTRIBUTE_NODE", "processing node");
return -1;
}
XmlElementType::XmlNamespaceDecl => {
xml_c14n_err_invalid_node("XML_NAMESPACE_DECL", "processing node");
return -1;
}
XmlElementType::XmlEntityRefNode => {
xml_c14n_err_invalid_node("XML_ENTITY_REF_NODE", "processing node");
return -1;
}
XmlElementType::XmlEntityNode => {
xml_c14n_err_invalid_node("XML_ENTITY_NODE", "processing node");
return -1;
}
XmlElementType::XmlDocumentTypeNode
| XmlElementType::XmlNotationNode
| XmlElementType::XmlDTDNode
| XmlElementType::XmlElementDecl
| XmlElementType::XmlAttributeDecl
| XmlElementType::XmlEntityDecl => {
}
#[cfg(feature = "xinclude")]
XmlElementType::XmlXIncludeStart | XmlElementType::XmlXIncludeEnd => {
}
_ => {
xml_c14n_err_unknown_node(cur.element_type() as i32, "processing node");
return -1;
}
}
ret
}
#[doc(alias = "xmlExcC14NProcessNamespacesAxis")]
fn exc_c14n_process_namespaces_axis(&mut self, mut cur: XmlNodePtr, visible: bool) -> i32 {
let mut has_empty_ns = false;
let mut has_visibly_utilized_empty_ns = false;
let mut has_empty_ns_in_inclusive_list = false;
if !matches!(cur.element_type(), XmlElementType::XmlElementNode) {
xml_c14n_err_param("processing namespaces axis (exc c14n)");
return -1;
}
if !self.is_exclusive() {
xml_c14n_err_param("processing namespaces axis (exc c14n)");
return -1;
}
let mut list = XmlList::new(None, Rc::new(|ns1, ns2| xml_c14n_ns_compare(*ns1, *ns2)));
if let Some(inclusive_ns_prefixes) = self.inclusive_ns_prefixes.as_deref() {
for prefix in inclusive_ns_prefixes {
let prefix = if prefix == "#default" || prefix.is_empty() {
has_empty_ns_in_inclusive_list = true;
None
} else {
Some(prefix.as_str())
};
let cur_doc = cur.doc;
let ns = cur.search_ns(cur_doc, prefix);
if let Some(ns) = ns.filter(|&ns| {
!xml_c14n_is_xml_ns(ns) && self.is_visible(Some(ns.into()), Some(cur.into()))
}) {
let already_rendered = (*self.ns_rendered).find(&ns);
if visible {
(*self.ns_rendered).add(ns, cur);
}
if !already_rendered {
list.insert_lower_bound(ns);
}
if (*ns).prefix().map_or(0, |pre| pre.len()) == 0 {
has_empty_ns = true;
}
}
}
}
let ns = if cur.ns.is_some() {
cur.ns
} else {
let cur_doc = cur.doc;
has_visibly_utilized_empty_ns = true;
cur.search_ns(cur_doc, None)
};
if let Some(ns) = ns.filter(|&ns| !xml_c14n_is_xml_ns(ns)) {
if visible
&& self.is_visible(Some(ns.into()), Some(cur.into()))
&& !self.exc_c14n_visible_ns_stack_find(&self.ns_rendered, &ns)
{
list.insert_lower_bound(ns);
}
if visible {
(*self.ns_rendered).add(ns, cur);
}
if (*ns).prefix().map_or(0, |pre| pre.len()) == 0 {
has_empty_ns = true;
}
}
let mut attr = cur.properties;
while let Some(cur_attr) = attr {
if let Some(attr_ns) = cur_attr.ns.filter(|&ns| {
!xml_c14n_is_xml_ns(ns) && self.is_visible(Some(cur_attr.into()), Some(cur.into()))
}) {
let already_rendered =
self.exc_c14n_visible_ns_stack_find(&self.ns_rendered, &attr_ns);
(*self.ns_rendered).add(attr_ns, cur);
if !already_rendered && visible {
list.insert_lower_bound(attr_ns);
}
if attr_ns.prefix().map_or(0, |pre| pre.len()) == 0 {
has_empty_ns = true;
}
} else if cur_attr.ns.is_some_and(|ns| {
ns.prefix().map_or(0, |pre| pre.len()) == 0
&& ns.href.as_deref().unwrap().is_empty()
}) {
has_visibly_utilized_empty_ns = true;
}
attr = cur_attr.next;
}
if visible
&& has_visibly_utilized_empty_ns
&& !has_empty_ns
&& !has_empty_ns_in_inclusive_list
{
let already_rendered =
self.exc_c14n_visible_ns_stack_find(&self.ns_rendered, &XmlNs::default());
if !already_rendered {
self.print_namespaces(&XmlNs::default());
}
} else if visible
&& !has_empty_ns
&& has_empty_ns_in_inclusive_list
&& !(*self.ns_rendered).find(&XmlNs::default())
{
self.print_namespaces(&XmlNs::default());
}
list.walk(|data| self.print_namespaces(data) != 0);
0
}
#[doc(alias = "xmlC14NVisibleNsStackFind")]
fn exc_c14n_visible_ns_stack_find(&self, cur: &XmlC14NVisibleNsStack, ns: &XmlNs) -> bool {
let prefix = ns.prefix();
let prefix = prefix.as_deref().unwrap_or("");
let href = ns.href();
let href = href.as_deref().unwrap_or("");
let has_empty_ns =
xml_c14n_str_equal(Some(prefix), None) && xml_c14n_str_equal(Some(href), None);
for (i, &ns1) in cur.ns_tab[..cur.ns_cur_end].iter().enumerate().rev() {
if xml_c14n_str_equal(Some(prefix), ns1.prefix().as_deref()) {
if xml_c14n_str_equal(Some(href), ns1.href().as_deref()) {
let node = cur.node_tab[i];
return self.is_visible(Some(ns1.into()), Some(node.into()));
} else {
return false;
}
}
}
has_empty_ns
}
#[doc(alias = "xmlC14NFindHiddenParentAttr")]
fn find_hidden_parent_attr(
&self,
mut cur: Option<XmlGenericNodePtr>,
name: &str,
ns: &str,
) -> Option<XmlAttrPtr> {
while let Some(now) = cur.filter(|&now| !self.is_visible(Some(now), now.parent())) {
if let Ok(now) = XmlNodePtr::try_from(now) {
if let Some(res) = now.has_ns_prop(name, Some(ns)) {
return Some(res.unwrap());
}
}
cur = now.parent();
}
None
}
#[doc(alias = "xmlC14NFixupBaseAttr")]
fn fixup_base_attr(&mut self, xml_base_attr: &XmlAttr) -> Option<XmlAttrPtr> {
let Some(parent) = xml_base_attr.parent() else {
xml_c14n_err_param("processing xml:base attribute");
return None;
};
let Some(mut res) = xml_base_attr
.children()
.and_then(|c| c.get_string(Some(self.doc), 1))
else {
xml_c14n_err_internal("processing xml:base attribute - can't get attr value");
return None;
};
let mut cur = parent.parent();
while let Some(cur_node) = cur.filter(|&cur| !self.is_visible(Some(cur), cur.parent())) {
if let Ok(cur) = XmlNodePtr::try_from(cur_node) {
if let Some(attr) = cur.has_ns_prop("base", Some(XML_XML_NAMESPACE)) {
let Some(mut tmp_str) = (match attr {
Ok(attr) => attr
.children()
.and_then(|c| c.get_string(Some(self.doc), 1)),
Err(attr) => attr
.children()
.and_then(|c| c.get_string(Some(self.doc), 1)),
}) else {
xml_c14n_err_internal(
"processing xml:base attribute - can't get attr value",
);
return None;
};
let tmp_str_len = tmp_str.len();
if tmp_str_len > 1 && tmp_str.as_bytes()[tmp_str_len - 2] == b'.' {
tmp_str.push('/');
}
let Some(tmp_str2) = build_uri(&res, &tmp_str) else {
xml_c14n_err_internal(
"processing xml:base attribute - can't construct uri",
);
return None;
};
res = tmp_str2;
}
}
cur = cur_node.parent();
}
if res.is_empty() {
return None;
}
let Some(attr) = xml_new_ns_prop(None, xml_base_attr.ns, "base", Some(&res)) else {
xml_c14n_err_internal("processing xml:base attribute - can't construct attribute");
return None;
};
Some(attr)
}
#[doc(alias = "xmlC14NPrintAttrs")]
fn print_attrs(&mut self, attr: XmlAttrPtr) -> bool {
self.buf.write_str(" ").ok();
if let Some(prefix) = attr
.ns
.as_deref()
.and_then(|ns| ns.prefix())
.filter(|p| !p.is_empty())
{
self.buf.write_str(&prefix).ok();
self.buf.write_str(":").ok();
}
self.buf.write_str(&attr.name().unwrap()).ok();
self.buf.write_str("=\"").ok();
if let Some(value) = attr
.children()
.and_then(|c| c.get_string(Some(self.doc), 1))
{
let buffer = normalize_attr(&value);
self.buf.write_str(&buffer).ok();
}
self.buf.write_str("\"").ok();
true
}
}
pub type XmlC14NCtxPtr<'a, T> = *mut XmlC14NCtx<'a, T>;
#[repr(C)]
pub enum XmlC14NNormalizationMode {
XmlC14NNormalizeAttr = 0,
XmlC14NNormalizeComment = 1,
XmlC14NNormalizePI = 2,
XmlC14NNormalizeText = 3,
}
fn xml_c14n_is_node_in_nodeset(
nodes: &Option<&mut XmlNodeSet>,
node: Option<XmlGenericNodePtr>,
parent: Option<XmlGenericNodePtr>,
) -> i32 {
if let (Some(nodes), Some(node)) = (nodes, node) {
if let Ok(node) = XmlNsPtr::try_from(node) {
let mut ns = XmlNs {
href: node.href.clone(),
prefix: node.prefix.clone(),
..*node
};
ns.node = ns.next.map(|ns| ns.into());
if let Some(parent) =
parent.filter(|p| p.element_type() == XmlElementType::XmlAttributeNode)
{
ns.node = parent.parent();
} else if let Some(parent) = parent {
ns.node = Some(parent);
}
return nodes.contains(Some(unsafe {
XmlNsPtr::from_raw(&raw mut ns).unwrap().unwrap().into()
})) as i32;
} else {
return nodes.contains(Some(node)) as i32;
}
}
1
}
#[doc(alias = "xmlC14NDocSaveTo")]
pub fn xml_c14n_doc_save_to<'a>(
doc: XmlDocPtr,
nodes: Option<&'a mut XmlNodeSet>,
mode: XmlC14NMode,
inclusive_ns_prefixes: Option<Vec<String>>,
with_comments: bool,
buf: XmlOutputBuffer<'a>,
) -> Result<
XmlC14NCtx<'a, Option<&'a mut XmlNodeSet>>,
Option<XmlC14NCtx<'a, Option<&'a mut XmlNodeSet>>>,
> {
xml_c14n_execute(
doc,
xml_c14n_is_node_in_nodeset,
nodes,
mode,
inclusive_ns_prefixes,
with_comments,
buf,
)
}
#[doc(alias = "xmlC14NErrParam")]
fn xml_c14n_err_param(extra: &str) {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromC14N,
XmlParserErrors::XmlErrInternalError,
XmlErrorLevel::XmlErrError,
None,
0,
Some(extra.to_owned().into()),
None,
None,
0,
0,
"Invalid parameter : {}\n",
extra
);
}
#[doc(alias = "xmlC14NErrMemory")]
fn xml_c14n_err_memory(extra: &str) {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromC14N,
XmlParserErrors::XmlErrNoMemory,
XmlErrorLevel::XmlErrError,
None,
0,
Some(extra.to_owned().into()),
None,
None,
0,
0,
"Memory allocation failed : {}\n",
extra
);
}
#[doc(alias = "xmlC14NErrInternal")]
fn xml_c14n_err_internal(extra: &str) {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromC14N,
XmlParserErrors::XmlErrInternalError,
XmlErrorLevel::XmlErrError,
None,
0,
Some(extra.to_owned().into()),
None,
None,
0,
0,
"Internal error : {}\n",
extra
);
}
#[doc(alias = "xmlC14NDocDumpMemory")]
pub fn xml_c14n_doc_dump_memory(
doc: XmlDocPtr,
nodes: Option<&mut XmlNodeSet>,
mode: XmlC14NMode,
inclusive_ns_prefixes: Option<Vec<String>>,
with_comments: bool,
doc_txt_ptr: &mut String,
) -> i32 {
doc_txt_ptr.clear();
let Some(buf) = XmlOutputBuffer::from_wrapped_encoder(None) else {
xml_c14n_err_memory("creating output buffer");
return -1;
};
match xml_c14n_doc_save_to(doc, nodes, mode, inclusive_ns_prefixes, with_comments, buf) {
Ok(mut ctx) => {
let ret = ctx.buf.buffer.len() as i32;
if ret >= 0 {
let text = String::from_utf8(take(&mut ctx.buf.buffer));
match text {
Ok(text) => *doc_txt_ptr = text,
Err(_) => {
xml_c14n_err_memory("copying canonicalized document");
return -1;
}
}
}
ret
}
Err(ctx) => {
xml_c14n_err_internal("saving doc to output buffer");
if let Some(mut ctx) = ctx {
ctx.buf.flush();
}
-1
}
}
}
#[doc(alias = "xmlC14NDocSave")]
pub fn xml_c14n_doc_save(
doc: XmlDocPtr,
nodes: Option<&mut XmlNodeSet>,
mode: XmlC14NMode,
inclusive_ns_prefixes: Option<Vec<String>>,
with_comments: bool,
filename: &str,
) -> i32 {
let Some(buf) = XmlOutputBuffer::from_uri(filename, None) else {
xml_c14n_err_internal("creating temporary filename");
return -1;
};
match xml_c14n_doc_save_to(doc, nodes, mode, inclusive_ns_prefixes, with_comments, buf) {
Ok(mut ctx) => {
let is_ok = ctx.buf.error.is_ok();
if is_ok {
ctx.buf.flush();
ctx.buf.written
} else {
-1
}
}
Err(ctx) => {
xml_c14n_err_internal("canonize document to buffer");
if let Some(mut ctx) = ctx {
ctx.buf.flush();
}
-1
}
}
}
#[doc(alias = "xmlC14NErr")]
fn xml_c14n_err<T>(
mut ctxt: Option<&mut XmlC14NCtx<'_, T>>,
node: Option<XmlGenericNodePtr>,
error: XmlParserErrors,
msg: &str,
) {
if let Some(ctxt) = ctxt.as_mut() {
ctxt.error = error;
}
let ctxt = ctxt.map_or(null_mut(), |ctxt| ctxt as *mut XmlC14NCtx<'_, T>);
__xml_raise_error!(
None,
None,
None,
ctxt as _,
node,
XmlErrorDomain::XmlFromC14N,
error,
XmlErrorLevel::XmlErrError,
None,
0,
None,
None,
None,
0,
0,
Some(msg),
);
}
#[doc(alias = "xmlC14NNewCtx")]
fn xml_c14n_new_ctx<T>(
doc: XmlDocPtr,
is_visible_callback: Option<XmlC14NIsVisibleCallback<T>>,
user_data: T,
mode: XmlC14NMode,
inclusive_ns_prefixes: Option<Vec<String>>,
with_comments: bool,
buf: XmlOutputBuffer<'_>,
) -> Option<XmlC14NCtx<'_, T>> {
if buf.encoder.is_some() {
xml_c14n_err::<T>(
None,
Some(doc.into()),
XmlParserErrors::XmlC14NRequiresUtf8,
"xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n",
);
return None;
}
let mut context = XmlC14NCtx {
doc,
with_comments,
is_visible_callback,
user_data,
buf,
parent_is_doc: true,
pos: XmlC14NPosition::XmlC14NBeforeDocumentElement,
ns_rendered: Box::new(XmlC14NVisibleNsStack::default()),
mode: XmlC14NMode::XmlC14N1_0,
inclusive_ns_prefixes: None,
error: XmlParserErrors::default(),
};
context.mode = mode;
if context.is_exclusive() {
context.inclusive_ns_prefixes = inclusive_ns_prefixes;
}
Some(context)
}
#[doc(alias = "xmlC14NErrRelativeNamespace")]
fn xml_c14n_err_relative_namespace(ns_uri: &str) {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromC14N,
XmlParserErrors::XmlC14NRelativeNamespace,
XmlErrorLevel::XmlErrError,
None,
0,
None,
None,
None,
0,
0,
"Relative namespace UR is invalid here : {}\n",
ns_uri
);
}
#[doc(alias = "xmlC14NNsCompare")]
fn xml_c14n_ns_compare(ns1: XmlNsPtr, ns2: XmlNsPtr) -> Ordering {
if ns1 == ns2 {
return Ordering::Equal;
}
match (ns1.prefix(), ns2.prefix()) {
(Some(p1), Some(p2)) => p1.cmp(&p2),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
}
}
#[doc(alias = "xmlC14NIsXmlNs")]
fn xml_c14n_is_xml_ns(ns: XmlNsPtr) -> bool {
ns.prefix().as_deref() == Some("xml") && ns.href().as_deref() == Some(XML_XML_NAMESPACE)
}
#[doc(alias = "xmlC14NStrEqual")]
fn xml_c14n_str_equal(str1: Option<&str>, str2: Option<&str>) -> bool {
match (str1, str2) {
(Some(str1), Some(str2)) => str1 == str2,
(Some(s), None) | (None, Some(s)) => s.is_empty(),
(None, None) => true,
}
}
#[doc(alias = "xmlC14NAttrsCompare")]
fn xml_c14n_attrs_compare(attr1: XmlAttrPtr, attr2: XmlAttrPtr) -> Ordering {
if attr1 == attr2 {
return Ordering::Equal;
}
if attr1.ns == attr2.ns {
return attr1.name().cmp(&attr2.name());
}
let Some(attr1_ns) = attr1.ns else {
return Ordering::Less;
};
let Some(attr2_ns) = attr2.ns else {
return Ordering::Greater;
};
if attr1_ns.prefix().is_none() {
return Ordering::Less;
}
if attr2_ns.prefix().is_none() {
return Ordering::Greater;
}
match attr1_ns.href().cmp(&attr2_ns.href()) {
Ordering::Equal => attr1.name().cmp(&attr2.name()),
diff => diff,
}
}
#[doc(alias = "xmlC14NIsXmlAttr")]
fn xml_c14n_is_xml_attr(attr: XmlAttrPtr) -> bool {
let ns = attr.ns;
ns.is_some_and(xml_c14n_is_xml_ns)
}
#[doc(alias = "xmlC11NNormalizeString")]
fn normalize_string(input: &str, mode: XmlC14NNormalizationMode) -> String {
let mut out = String::new();
for cur in input.chars() {
if cur == '<'
&& matches!(
mode,
XmlC14NNormalizationMode::XmlC14NNormalizeAttr
| XmlC14NNormalizationMode::XmlC14NNormalizeText
)
{
out.push_str("<");
} else if cur == '>' && matches!(mode, XmlC14NNormalizationMode::XmlC14NNormalizeText) {
out.push_str(">");
} else if cur == '&'
&& matches!(
mode,
XmlC14NNormalizationMode::XmlC14NNormalizeAttr
| XmlC14NNormalizationMode::XmlC14NNormalizeText
)
{
out.push_str("&");
} else if cur == '"' && matches!(mode, XmlC14NNormalizationMode::XmlC14NNormalizeAttr) {
out.push_str(""");
} else if cur == '\x09' && matches!(mode, XmlC14NNormalizationMode::XmlC14NNormalizeAttr) {
out.push_str("	");
} else if cur == '\x0A' && matches!(mode, XmlC14NNormalizationMode::XmlC14NNormalizeAttr) {
out.push_str("
");
} else if cur == '\x0D'
&& matches!(
mode,
XmlC14NNormalizationMode::XmlC14NNormalizeAttr
| XmlC14NNormalizationMode::XmlC14NNormalizeText
| XmlC14NNormalizationMode::XmlC14NNormalizeComment
| XmlC14NNormalizationMode::XmlC14NNormalizePI
)
{
out.push_str("
");
} else {
out.push(cur);
}
}
out
}
#[doc(alias = "xmlC11NNormalizeAttr")]
fn normalize_attr(a: &str) -> String {
normalize_string(a, XmlC14NNormalizationMode::XmlC14NNormalizeAttr)
}
#[doc(alias = "xmlC11NNormalizeText")]
fn normalize_text(a: &str) -> String {
normalize_string(a, XmlC14NNormalizationMode::XmlC14NNormalizeText)
}
#[doc(alias = "xmlC11NNormalizeComment")]
fn normalize_comment(a: &str) -> String {
normalize_string(a, XmlC14NNormalizationMode::XmlC14NNormalizeComment)
}
#[doc(alias = "xmlC11NNormalizePI")]
fn normalize_pi(a: &str) -> String {
normalize_string(a, XmlC14NNormalizationMode::XmlC14NNormalizePI)
}
#[doc(alias = "xmlC14NErrInvalidNode")]
fn xml_c14n_err_invalid_node(node_type: &str, extra: &str) {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromC14N,
XmlParserErrors::XmlC14NInvalidNode,
XmlErrorLevel::XmlErrError,
None,
0,
Some(extra.to_owned().into()),
None,
None,
0,
0,
"Node {} is invalid here : {}\n",
node_type,
extra
);
}
#[doc(alias = "xmlC14NErrUnknownNode")]
fn xml_c14n_err_unknown_node(node_type: i32, extra: &str) {
__xml_raise_error!(
None,
None,
None,
null_mut(),
None,
XmlErrorDomain::XmlFromC14N,
XmlParserErrors::XmlC14NUnknowNode,
XmlErrorLevel::XmlErrError,
None,
0,
Some(extra.to_owned().into()),
None,
None,
0,
0,
"Unknown node type {} found : {}\n",
node_type,
extra
);
}
#[doc(alias = "xmlC14NExecute")]
pub fn xml_c14n_execute<T>(
doc: XmlDocPtr,
is_visible_callback: XmlC14NIsVisibleCallback<T>,
user_data: T,
mode: XmlC14NMode,
inclusive_ns_prefixes: Option<Vec<String>>,
with_comments: bool,
buf: XmlOutputBuffer<'_>,
) -> Result<XmlC14NCtx<'_, T>, Option<XmlC14NCtx<'_, T>>> {
if buf.encoder.is_some() {
xml_c14n_err::<T>(
None,
Some(doc.into()),
XmlParserErrors::XmlC14NRequiresUtf8,
"xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n",
);
return Err(None);
}
let children = doc.children;
let mut ctx = xml_c14n_new_ctx(
doc,
Some(is_visible_callback),
user_data,
mode,
inclusive_ns_prefixes,
with_comments,
buf,
)
.unwrap();
if let Some(children) = children {
let ret = ctx.process_node_list(Some(children));
if ret < 0 {
xml_c14n_err_internal("processing docs children list");
return Err(Some(ctx));
}
}
let ret = ctx.buf.flush();
if ret < 0 {
xml_c14n_err_internal("flushing output buffer");
return Err(Some(ctx));
}
Ok(ctx)
}