use core::ffi::c_void;
use core::ptr;
use std::os::raw::{c_char, c_int, c_uint};
use crate::abi::allocator;
use crate::abi::callbacks::xmlGenericErrorFunc;
use crate::abi::callbacks::xmlHashDeallocator;
use crate::abi::callbacks::xmlHashScannerFull;
use crate::abi::structs::*;
use crate::abi::types::xmlAttributeDefault::*;
use crate::abi::types::xmlAttributeType::*;
use crate::abi::types::xmlElementContentOccur::*;
use crate::abi::types::xmlElementContentType::*;
use crate::abi::types::xmlElementType::*;
use crate::abi::types::xmlElementTypeVal::*;
use crate::abi::types::*;
use crate::xml::dtd;
use crate::xml::entities;
use crate::xml::hash;
use crate::xml::string;
const VALID_CTXT_DEPTH_MAX: c_int = 256;
pub unsafe fn new_valid_ctxt() -> *mut _xmlValidCtxt {
let ctxt = allocator::xmlMallocZero(size_of::<_xmlValidCtxt>() as usize) as *mut _xmlValidCtxt;
if ctxt.is_null() {
return ptr::null_mut();
}
unsafe {
(*ctxt).valid = 1;
(*ctxt).node = ptr::null_mut();
(*ctxt).doc = ptr::null_mut();
(*ctxt).nodeNr = 0;
(*ctxt).nodeMax = 0;
(*ctxt).nodeTab = ptr::null_mut();
(*ctxt).flags = 0;
(*ctxt).vstate = ptr::null_mut();
(*ctxt).vstateNr = 0;
(*ctxt).vstateMax = 0;
(*ctxt).vstateTab = ptr::null_mut();
(*ctxt).am = ptr::null_mut();
(*ctxt).state = ptr::null_mut();
(*ctxt).error = None;
(*ctxt).warning = None;
(*ctxt).userData = ptr::null_mut();
}
ctxt
}
pub unsafe fn free_valid_ctxt(ctxt: *mut _xmlValidCtxt) {
if ctxt.is_null() {
return;
}
unsafe {
let c = &mut *ctxt;
if !c.nodeTab.is_null() {
allocator::xmlFree(c.nodeTab as *mut c_void);
}
if !c.am.is_null() {
}
if !c.state.is_null() {
}
allocator::xmlFree(ctxt as *mut c_void);
}
}
pub unsafe fn set_valid_errors(
ctxt: *mut _xmlValidCtxt,
err: Option<xmlGenericErrorFunc>,
warn: Option<xmlGenericErrorFunc>,
data: *mut c_void,
) {
if ctxt.is_null() {
return;
}
unsafe {
(*ctxt).error = err;
(*ctxt).warning = warn;
(*ctxt).userData = data;
}
}
unsafe fn vctxt_error(ctxt: *mut _xmlValidCtxt, msg: *const c_char) {
if ctxt.is_null() {
return;
}
unsafe {
let c = &mut *ctxt;
c.valid = 0;
if let Some(err) = c.error {
err(c.userData, msg);
}
}
}
unsafe fn vctxt_push_node(ctxt: *mut _xmlValidCtxt, node: *mut _xmlNode) -> c_int {
unsafe {
let c = &mut *ctxt;
if c.nodeNr >= c.nodeMax {
let new_max = if c.nodeMax == 0 { 4 } else { c.nodeMax * 2 };
let new_tab = allocator::xmlRealloc(
c.nodeTab as *mut c_void,
(new_max as usize) * size_of::<*mut _xmlNode>(),
) as *mut *mut _xmlNode;
if new_tab.is_null() {
return -1;
}
c.nodeTab = new_tab;
c.nodeMax = new_max;
}
*c.nodeTab.add(c.nodeNr as usize) = node;
c.nodeNr += 1;
c.node = node;
}
0
}
unsafe fn vctxt_pop_node(ctxt: *mut _xmlValidCtxt) {
unsafe {
let c = &mut *ctxt;
if c.nodeNr > 0 {
c.nodeNr -= 1;
}
if c.nodeNr > 0 {
c.node = *c.nodeTab.add((c.nodeNr - 1) as usize);
} else {
c.node = ptr::null_mut();
}
}
}
unsafe fn get_valid_dtd(doc: *mut _xmlDoc) -> *mut _xmlDtd {
if doc.is_null() {
return ptr::null_mut();
}
unsafe {
let d = &*doc;
if !d.intSubset.is_null() {
d.intSubset
} else {
d.extSubset
}
}
}
fn is_xml_name_start(c: char) -> bool {
matches!(c,
'a'..='z' | 'A'..='Z' | '_' | ':' |
'\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
'\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' |
'\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' |
'\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
'\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}'
)
}
fn is_xml_name_char(c: char) -> bool {
is_xml_name_start(c)
|| matches!(c,
'-' | '.' | '0'..='9' | '\u{B7}' |
'\u{0300}'..='\u{036F}' | '\u{203F}'..='\u{2040}'
)
}
pub unsafe fn validate_name(value: *const xmlChar) -> c_int {
if value.is_null() {
return 0;
}
let s = unsafe { string::xmlstr_to_bytes(value) };
let s = core::str::from_utf8(s).unwrap_or("");
if s.is_empty() {
return 0;
}
let mut chars = s.chars();
match chars.next() {
Some(c) if is_xml_name_start(c) => {}
_ => return 0,
}
for c in chars {
if !is_xml_name_char(c) {
return 0;
}
}
1
}
pub unsafe fn validate_names(value: *const xmlChar) -> c_int {
if value.is_null() {
return 0;
}
let s = unsafe { string::xmlstr_to_bytes(value) };
let s = core::str::from_utf8(s).unwrap_or("");
if s.is_empty() {
return 0;
}
for token in s.split_whitespace() {
if token.is_empty() {
return 0;
}
let mut chars = token.chars();
match chars.next() {
Some(c) if is_xml_name_start(c) => {}
_ => return 0,
}
for c in chars {
if !is_xml_name_char(c) {
return 0;
}
}
}
1
}
pub unsafe fn validate_nmtoken(value: *const xmlChar) -> c_int {
if value.is_null() {
return 0;
}
let s = unsafe { string::xmlstr_to_bytes(value) };
let s = core::str::from_utf8(s).unwrap_or("");
if s.is_empty() {
return 0;
}
for c in s.chars() {
if !is_xml_name_char(c) {
return 0;
}
}
1
}
pub unsafe fn validate_nmtokens(value: *const xmlChar) -> c_int {
if value.is_null() {
return 0;
}
let s = unsafe { string::xmlstr_to_bytes(value) };
let s = core::str::from_utf8(s).unwrap_or("");
if s.is_empty() {
return 0;
}
for token in s.split_whitespace() {
if token.is_empty() {
return 0;
}
for c in token.chars() {
if !is_xml_name_char(c) {
return 0;
}
}
}
1
}
pub unsafe fn validate_attribute_value(atype: c_int, value: *const xmlChar) -> c_int {
if value.is_null() {
return 0;
}
if atype == XML_ATTRIBUTE_CDATA as c_int {
return 1;
}
let s = unsafe { string::xmlstr_to_bytes(value) };
let s = core::str::from_utf8(s).unwrap_or("");
if s.is_empty() {
return 0;
}
match atype as u32 {
t if t == XML_ATTRIBUTE_CDATA as u32 => 1,
t if t == XML_ATTRIBUTE_ID as u32 => {
unsafe { validate_name(value) }
}
t if t == XML_ATTRIBUTE_IDREF as u32 => {
unsafe { validate_name(value) }
}
t if t == XML_ATTRIBUTE_IDREFS as u32 => {
unsafe { validate_names(value) }
}
t if t == XML_ATTRIBUTE_ENTITY as u32 => {
unsafe { validate_name(value) }
}
t if t == XML_ATTRIBUTE_ENTITIES as u32 => {
unsafe { validate_names(value) }
}
t if t == XML_ATTRIBUTE_NMTOKEN as u32 => unsafe { validate_nmtoken(value) },
t if t == XML_ATTRIBUTE_NMTOKENS as u32 => unsafe { validate_nmtokens(value) },
t if t == XML_ATTRIBUTE_ENUMERATION as u32 => {
1
}
t if t == XML_ATTRIBUTE_NOTATION as u32 => {
unsafe { validate_name(value) }
}
_ => 0,
}
}
pub unsafe fn validate_enumeration(
ctxt: *mut _xmlValidCtxt,
value: *const xmlChar,
tree: *mut _xmlEnumeration,
) -> c_int {
if value.is_null() || tree.is_null() {
return 0;
}
let mut cur = tree;
while !cur.is_null() {
unsafe {
if string::xml_strcmp(value, (*cur).name) == 0 {
return 1;
}
cur = (*cur).next;
}
}
unsafe {
let msg = string::xmlstr_to_string(value);
let err_msg = format!("Value '{}' is not a valid enumeration value\0", msg);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
}
0
}
pub unsafe fn validate_notation_use(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
notation_name: *const xmlChar,
) -> c_int {
if notation_name.is_null() {
return 0;
}
let dtd = unsafe { get_valid_dtd(doc) };
if dtd.is_null() {
unsafe {
vctxt_error(
ctxt,
b"No DTD available for notation validation\0" as *const u8 as *const c_char,
);
}
return 0;
}
unsafe {
let notations = (*dtd).notations;
if notations.is_null() {
vctxt_error(
ctxt,
b"No notations declared in DTD\0" as *const u8 as *const c_char,
);
return 0;
}
let notation = hash::hash_lookup(notations as *mut hash::HashTable, notation_name);
if notation.is_null() {
let msg = string::xmlstr_to_string(notation_name);
let err_msg = format!("Notation '{}' is not declared\0", msg);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
}
1
}
pub unsafe fn validate_id(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
node: *mut _xmlNode,
value: *const xmlChar,
) -> c_int {
if value.is_null() || doc.is_null() {
return 0;
}
if unsafe { validate_name(value) } == 0 {
unsafe {
let msg = string::xmlstr_to_string(value);
let err_msg = format!("ID value '{}' is not a valid XML Name\0", msg);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
}
return 0;
}
unsafe {
let doc_ref = &*doc;
if !doc_ref.ids.is_null() {
let existing = hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, value);
if !existing.is_null() {
let msg = string::xmlstr_to_string(value);
let err_msg = format!("Duplicate ID value '{}'\0", msg);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
}
}
unsafe {
if (*doc).ids.is_null() {
(*doc).ids = hash::hash_create(16) as *mut c_void;
}
hash::hash_add_entry(
(*doc).ids as *mut hash::HashTable,
value,
node as *mut c_void,
);
}
1
}
pub unsafe fn validate_id_ref(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
node: *mut _xmlNode,
value: *const xmlChar,
) -> c_int {
if value.is_null() || doc.is_null() {
return 0;
}
if unsafe { validate_name(value) } == 0 {
unsafe {
let msg = string::xmlstr_to_string(value);
let err_msg = format!("IDREF value '{}' is not a valid XML Name\0", msg);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
}
return 0;
}
unsafe {
let doc_ref = &*doc;
if doc_ref.ids.is_null()
|| hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, value).is_null()
{
let msg = string::xmlstr_to_string(value);
let err_msg = format!("IDREF '{}' references an unknown ID\0", msg);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
}
1
}
pub unsafe fn validate_id_refs(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
node: *mut _xmlNode,
value: *const xmlChar,
) -> c_int {
if value.is_null() || doc.is_null() {
return 0;
}
let s = unsafe { string::xmlstr_to_bytes(value) };
let s = core::str::from_utf8(s).unwrap_or("");
if s.is_empty() {
return 0;
}
let mut valid = 1;
for token in s.split_whitespace() {
if token.is_empty() {
continue;
}
let token_ptr = unsafe { string::bytes_to_xmlstr(token.as_bytes()) };
if token_ptr.is_null() {
valid = 0;
break;
}
let result = unsafe { validate_id_ref(ctxt, doc, node, token_ptr) };
unsafe {
allocator::xmlFree(token_ptr as *mut c_void);
}
if result == 0 {
valid = 0;
}
}
valid
}
pub unsafe fn validate_attribute_decl(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
elem: *mut _xmlNode,
attr: *mut _xmlAttribute,
) -> c_int {
if attr.is_null() {
return 0;
}
unsafe {
let a = &*attr;
let atype = a.atype as c_int;
if !a.defaultValue.is_null() {
if validate_attribute_value(atype, a.defaultValue) == 0 {
let name_str = string::xmlstr_to_string(a.name);
let val_str = string::xmlstr_to_string(a.defaultValue);
let err_msg = format!(
"Default value '{}' for attribute '{}' is not valid for its type\0",
val_str, name_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
}
if atype == XML_ATTRIBUTE_ENUMERATION as c_int && !a.tree.is_null() {
let mut cur = a.tree;
while !cur.is_null() {
if !(*cur).name.is_null() {
if validate_nmtoken((*cur).name) == 0 {
let val_str = string::xmlstr_to_string((*cur).name);
let err_msg =
format!("Enumeration value '{}' is not a valid NMTOKEN\0", val_str);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
}
cur = (*cur).next;
}
}
if atype == XML_ATTRIBUTE_NOTATION as c_int && !a.tree.is_null() {
let mut cur = a.tree;
while !cur.is_null() {
if !(*cur).name.is_null() {
if validate_notation_use(ctxt, doc, (*cur).name) == 0 {
let val_str = string::xmlstr_to_string((*cur).name);
let err_msg = format!(
"NOTATION value '{}' references undeclared notation\0",
val_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
}
cur = (*cur).next;
}
}
1
}
}
pub unsafe fn validate_element(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
elem: *mut _xmlNode,
) -> c_int {
if elem.is_null() || doc.is_null() || ctxt.is_null() {
return 0;
}
unsafe {
let e = &*elem;
if e.type_ != XML_ELEMENT_NODE as c_int {
return 1;
}
if vctxt_push_node(ctxt, elem) != 0 {
return 0;
}
let mut valid = 1;
let dtd = get_valid_dtd(doc);
if dtd.is_null() {
vctxt_pop_node(ctxt);
return 1;
}
let dtd_ref = &*dtd;
let elem_name = e.name;
let elem_decl = if !dtd_ref.elements.is_null() {
hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, elem_name)
} else {
ptr::null_mut()
};
if elem_decl.is_null() {
let name_str = string::xmlstr_to_string(elem_name);
let err_msg = format!("No declaration for element '{}'\0", name_str);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
vctxt_pop_node(ctxt);
return 0;
}
let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
let elem_type = elem_decl_ref.type_ as u32;
if elem_type == XML_ELEMENT_TYPE_EMPTY as u32 {
let mut child = e.children;
while !child.is_null() {
let child_type = (*child).type_ as u32;
if child_type != XML_TEXT_NODE as u32 && child_type != XML_CDATA_SECTION_NODE as u32
{
valid = 0;
let name_str = string::xmlstr_to_string(elem_name);
let err_msg = format!(
"Element '{}' is declared EMPTY but has child elements\0",
name_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
break;
}
child = (*child).next;
}
} else if elem_type == XML_ELEMENT_TYPE_ANY as u32 {
} else if elem_type == XML_ELEMENT_TYPE_MIXED as u32 {
let mut child = e.children;
while !child.is_null() {
let child_type = (*child).type_ as u32;
if child_type == XML_ELEMENT_NODE as u32 {
let child_name = (*child).name;
let result = dtd::valid_content_model(elem_decl_ref.content, &[child_name]);
if result != dtd::ContentModelResult::Valid {
let cname_str = string::xmlstr_to_string(child_name);
let ename_str = string::xmlstr_to_string(elem_name);
let err_msg = format!(
"Element '{}' is not allowed in mixed content of '{}'\0",
cname_str, ename_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
valid = 0;
}
}
child = (*child).next;
}
} else if elem_type == XML_ELEMENT_TYPE_ELEMENT as u32 {
let mut child_names: Vec<*const xmlChar> = Vec::new();
let mut child = e.children;
while !child.is_null() {
if (*child).type_ == XML_ELEMENT_NODE as c_int {
child_names.push((*child).name);
}
child = (*child).next;
}
let result = dtd::valid_content_model(elem_decl_ref.content, &child_names);
if result != dtd::ContentModelResult::Valid {
let ename_str = string::xmlstr_to_string(elem_name);
let err_msg = format!(
"Content model validation failed for element '{}'\0",
ename_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
valid = 0;
}
}
if !dtd_ref.attributes.is_null() {
let mut attr_prop = e.properties;
while !attr_prop.is_null() {
let attr_ref = &*attr_prop;
let attr_name = attr_ref.name;
let attr_decl = hash::hash_lookup2(
dtd_ref.attributes as *mut hash::HashTable,
elem_name,
attr_name,
);
if attr_decl.is_null() {
attr_prop = attr_ref.next;
continue;
}
let attr_decl_ref = &*(attr_decl as *mut _xmlAttribute);
let atype = attr_decl_ref.atype as c_int;
let attr_value = if !attr_ref.children.is_null() {
let text_node = attr_ref.children;
if (*text_node).type_ == XML_TEXT_NODE as c_int
|| (*text_node).type_ == XML_CDATA_SECTION_NODE as c_int
{
(*text_node).content
} else {
ptr::null()
}
} else {
ptr::null()
};
if !attr_value.is_null() {
if atype == XML_ATTRIBUTE_ENUMERATION as c_int && !attr_decl_ref.tree.is_null()
{
if validate_enumeration(ctxt, attr_value, attr_decl_ref.tree) == 0 {
let aname_str = string::xmlstr_to_string(attr_name);
let aval_str = string::xmlstr_to_string(attr_value);
let err_msg = format!(
"Attribute '{}' has value '{}' not in enumeration\0",
aname_str, aval_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
valid = 0;
}
} else if atype == XML_ATTRIBUTE_NOTATION as c_int {
if validate_notation_use(ctxt, doc, attr_value) == 0 {
let aname_str = string::xmlstr_to_string(attr_name);
let aval_str = string::xmlstr_to_string(attr_value);
let err_msg = format!(
"Attribute '{}' references undeclared notation '{}'\0",
aname_str, aval_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
valid = 0;
}
} else if validate_attribute_value(atype, attr_value) == 0 {
let aname_str = string::xmlstr_to_string(attr_name);
let aval_str = string::xmlstr_to_string(attr_value);
let err_msg = format!(
"Attribute '{}' has invalid value '{}' for its type\0",
aname_str, aval_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
valid = 0;
}
if atype == XML_ATTRIBUTE_ID as c_int {
if validate_id(ctxt, doc, elem, attr_value) == 0 {
valid = 0;
}
} else if atype == XML_ATTRIBUTE_IDREF as c_int {
if validate_id_ref(ctxt, doc, elem, attr_value) == 0 {
valid = 0;
}
} else if atype == XML_ATTRIBUTE_IDREFS as c_int {
if validate_id_refs(ctxt, doc, elem, attr_value) == 0 {
valid = 0;
}
}
}
attr_prop = attr_ref.next;
}
struct RequiredAttrCheck {
ctxt: *mut _xmlValidCtxt,
elem_name: *const xmlChar,
elem_props: *mut _xmlAttr,
valid: *mut c_int,
}
extern "C" fn check_required_attr(
payload: *mut c_void,
data: *mut c_void,
name: *const xmlChar,
name2: *const xmlChar,
_name3: *const xmlChar,
) {
if payload.is_null() || data.is_null() || name2.is_null() {
return;
}
let check = unsafe { &*(data as *mut RequiredAttrCheck) };
unsafe {
if string::xml_strcmp(name, check.elem_name) != 0 {
return;
}
let attr_decl = &*(payload as *mut _xmlAttribute);
if attr_decl.def == XML_ATTRIBUTE_REQUIRED as c_int {
let mut found = 0;
let mut prop = check.elem_props;
while !prop.is_null() {
if string::xml_strcmp((*prop).name, name2) == 0 {
found = 1;
break;
}
prop = (*prop).next;
}
if found == 0 {
let aname_str = string::xmlstr_to_string(name2);
let ename_str = string::xmlstr_to_string(check.elem_name);
let err_msg = format!(
"Required attribute '{}' missing on element '{}'\0",
aname_str, ename_str
);
vctxt_error(check.ctxt, err_msg.as_ptr() as *const c_char);
*(check.valid) = 0;
}
}
}
}
let mut required_valid = valid;
let check = RequiredAttrCheck {
ctxt,
elem_name,
elem_props: e.properties,
valid: &mut required_valid,
};
hash::hash_scan_full(
dtd_ref.attributes as *mut hash::HashTable,
Some(check_required_attr),
&check as *const RequiredAttrCheck as *mut c_void,
);
valid = required_valid;
}
let mut child = e.children;
while !child.is_null() {
if (*child).type_ == XML_ELEMENT_NODE as c_int {
if validate_element(ctxt, doc, child) == 0 {
valid = 0;
}
}
child = (*child).next;
}
vctxt_pop_node(ctxt);
valid
}
}
pub unsafe fn validate_document(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
if ctxt.is_null() || doc.is_null() {
return 0;
}
unsafe {
let c = &mut *ctxt;
c.doc = doc;
c.valid = 1;
let d = &*doc;
let mut root = d.children;
while !root.is_null() {
if (*root).type_ == XML_ELEMENT_NODE as c_int {
break;
}
root = (*root).next;
}
if root.is_null() {
vctxt_error(
ctxt,
b"No root element found in document\0" as *const u8 as *const c_char,
);
return 0;
}
if validate_element(ctxt, doc, root) == 0 {
return 0;
}
c.valid
}
}
pub unsafe fn validate_document_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
if ctxt.is_null() || doc.is_null() {
return 0;
}
unsafe {
let c = &mut *ctxt;
c.doc = doc;
let d = &*doc;
if d.refs.is_null() {
return c.valid;
}
struct IdRefCheckContext {
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
}
extern "C" fn check_idref(
_payload: *mut c_void,
data: *mut c_void,
_name: *const xmlChar,
name2: *const xmlChar,
_name3: *const xmlChar,
) {
if data.is_null() || name2.is_null() {
return;
}
let cx = unsafe { &*(data as *mut IdRefCheckContext) };
unsafe {
let doc_ref = &*cx.doc;
if doc_ref.ids.is_null()
|| hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, name2).is_null()
{
let ref_str = string::xmlstr_to_string(name2);
let err_msg = format!("IDREF '{}' does not reference a declared ID\0", ref_str);
vctxt_error(cx.ctxt, err_msg.as_ptr() as *const c_char);
}
}
}
let ctx = IdRefCheckContext { ctxt, doc };
hash::hash_scan_full(
d.refs as *mut hash::HashTable,
Some(check_idref),
&ctx as *const IdRefCheckContext as *mut c_void,
);
c.valid
}
}
pub unsafe fn validate_root(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
if ctxt.is_null() || doc.is_null() {
return 0;
}
unsafe {
let c = &mut *ctxt;
c.doc = doc;
c.valid = 1;
let d = &*doc;
let mut root = d.children;
while !root.is_null() {
if (*root).type_ == XML_ELEMENT_NODE as c_int {
break;
}
root = (*root).next;
}
if root.is_null() {
vctxt_error(
ctxt,
b"No root element found\0" as *const u8 as *const c_char,
);
return 0;
}
let dtd = get_valid_dtd(doc);
if dtd.is_null() {
return 1;
}
let dtd_ref = &*dtd;
if !dtd_ref.name.is_null() {
if string::xml_strcmp((*root).name, dtd_ref.name) != 0 {
let root_str = string::xmlstr_to_string((*root).name);
let dtd_str = string::xmlstr_to_string(dtd_ref.name);
let err_msg = format!(
"Root element '{}' does not match DTD root '{}'\0",
root_str, dtd_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
}
c.valid
}
}
pub unsafe fn validate_content(
ctxt: *mut _xmlValidCtxt,
node: *mut _xmlNode,
doc: *mut _xmlDoc,
) -> c_int {
if node.is_null() || doc.is_null() || ctxt.is_null() {
return 0;
}
unsafe {
let n = &*node;
if n.type_ != XML_ELEMENT_NODE as c_int {
return 1;
}
let dtd = get_valid_dtd(doc);
if dtd.is_null() {
return 1;
}
let dtd_ref = &*dtd;
if dtd_ref.elements.is_null() {
return 1;
}
let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, n.name);
if elem_decl.is_null() {
return 1;
}
let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
if elem_decl_ref.content.is_null() {
return 1;
}
let elem_type = elem_decl_ref.type_ as u32;
if elem_type == XML_ELEMENT_TYPE_EMPTY as u32 {
let mut child = n.children;
while !child.is_null() {
if (*child).type_ == XML_ELEMENT_NODE as c_int {
let name_str = string::xmlstr_to_string(n.name);
let err_msg =
format!("Element '{}' is EMPTY but has child elements\0", name_str);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
return 0;
}
child = (*child).next;
}
return 1;
}
if elem_type == XML_ELEMENT_TYPE_ANY as u32 {
return 1;
}
let mut child_names: Vec<*const xmlChar> = Vec::new();
let mut child = n.children;
while !child.is_null() {
if (*child).type_ == XML_ELEMENT_NODE as c_int {
child_names.push((*child).name);
}
child = (*child).next;
}
let result = dtd::valid_content_model(elem_decl_ref.content, &child_names);
if result != dtd::ContentModelResult::Valid {
let name_str = string::xmlstr_to_string(n.name);
let err_msg = format!(
"Content model validation failed for element '{}'\0",
name_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
0
} else {
1
}
}
}
pub unsafe fn is_mixed_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
if doc.is_null() || name.is_null() {
return 0;
}
let dtd = unsafe { get_valid_dtd(doc) };
if dtd.is_null() {
return 0;
}
unsafe {
let dtd_ref = &*dtd;
if dtd_ref.elements.is_null() {
return 0;
}
let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
if elem_decl.is_null() {
return 0;
}
let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
((elem_decl_ref.type_ as u32) == XML_ELEMENT_TYPE_MIXED as u32) as c_int
}
}
pub unsafe fn is_empty_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
if doc.is_null() || name.is_null() {
return 0;
}
let dtd = unsafe { get_valid_dtd(doc) };
if dtd.is_null() {
return 0;
}
unsafe {
let dtd_ref = &*dtd;
if dtd_ref.elements.is_null() {
return 0;
}
let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
if elem_decl.is_null() {
return 0;
}
let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
((elem_decl_ref.type_ as u32) == XML_ELEMENT_TYPE_EMPTY as u32) as c_int
}
}
pub unsafe fn validate_dtd(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
dtd: *mut _xmlDtd,
) -> c_int {
if ctxt.is_null() || dtd.is_null() {
return 0;
}
let c = unsafe { &mut *ctxt };
c.doc = doc;
c.valid = 1;
struct ValidateDtdCtx {
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
}
extern "C" fn validate_attr_decl_cb(
payload: *mut c_void,
data: *mut c_void,
_name: *const xmlChar,
_name2: *const xmlChar,
_name3: *const xmlChar,
) {
if payload.is_null() || data.is_null() {
return;
}
let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
unsafe {
let attr = payload as *mut _xmlAttribute;
validate_attribute_decl(ctx.ctxt, ctx.doc, ptr::null_mut(), attr);
}
}
extern "C" fn validate_elem_content_cb(
payload: *mut c_void,
data: *mut c_void,
_name: *const xmlChar,
_name2: *const xmlChar,
_name3: *const xmlChar,
) {
if payload.is_null() || data.is_null() {
return;
}
let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
unsafe {
let elem = &*(payload as *mut _xmlElement);
if !elem.content.is_null() {
validate_content_model_refs(ctx.ctxt, ctx.doc, elem.content);
}
}
}
unsafe {
let dtd_ref = &*dtd;
if !dtd_ref.attributes.is_null() {
let ctx = ValidateDtdCtx { ctxt, doc };
hash::hash_scan_full(
dtd_ref.attributes as *mut hash::HashTable,
Some(validate_attr_decl_cb),
&ctx as *const ValidateDtdCtx as *mut c_void,
);
}
if !dtd_ref.elements.is_null() {
let ctx = ValidateDtdCtx { ctxt, doc };
hash::hash_scan_full(
dtd_ref.elements as *mut hash::HashTable,
Some(validate_elem_content_cb),
&ctx as *const ValidateDtdCtx as *mut c_void,
);
}
c.valid
}
}
unsafe fn validate_content_model_refs(
ctxt: *mut _xmlValidCtxt,
doc: *mut _xmlDoc,
content: *mut _xmlElementContent,
) {
if content.is_null() {
return;
}
unsafe {
let c = &*content;
match c.type_ as u32 {
t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
if !c.name.is_null() {
let dtd = get_valid_dtd(doc);
if !dtd.is_null() {
let dtd_ref = &*dtd;
if !dtd_ref.elements.is_null() {
let decl =
hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, c.name);
if decl.is_null() {
let name_str = string::xmlstr_to_string(c.name);
let err_msg = format!(
"Element '{}' referenced in content model is not declared\0",
name_str
);
vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
}
}
}
}
}
t if t == XML_ELEMENT_CONTENT_SEQ as u32 || t == XML_ELEMENT_CONTENT_OR as u32 => {
validate_content_model_refs(ctxt, doc, c.c1);
validate_content_model_refs(ctxt, doc, c.c2);
}
_ => {}
}
}
}
pub unsafe fn validate_dtd_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
unsafe { validate_document_final(ctxt, doc) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::allocator;
use crate::abi::types::xmlElementTypeVal::*;
use crate::xml::dtd;
use crate::xml::tree;
unsafe fn c_str(s: &str) -> *const xmlChar {
let bytes = s.as_bytes();
let ptr = allocator::xmlMalloc(bytes.len() + 1) as *mut xmlChar;
assert!(!ptr.is_null());
std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
*ptr.add(bytes.len()) = 0;
ptr
}
unsafe fn make_test_doc() -> (*mut _xmlDoc, *mut _xmlDtd) {
let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
assert!(!doc.is_null());
let name = c_str("root");
let ext_id = c_str("--//Test//DTD//EN");
let sys_id = c_str("test.dtd");
let dtd = dtd::create_int_subset(doc, name, ext_id, sys_id);
assert!(!dtd.is_null());
(doc, dtd)
}
#[allow(unused)]
unsafe fn add_elem_decl(
dtd: *mut _xmlDtd,
name: *const xmlChar,
elem_type: c_int,
content: *mut _xmlElementContent,
) -> *mut _xmlElement {
let result = dtd::add_element_decl(dtd, name, elem_type, content);
result
}
unsafe fn create_root_elem(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
let node = tree::new_node(ptr::null_mut(), name);
assert!(!node.is_null());
tree::add_child(doc as *mut _xmlNode, node);
node
}
#[allow(unused)]
unsafe fn create_child_elem(parent: *mut _xmlNode, name: *const xmlChar) -> *mut _xmlNode {
let node = tree::new_node(ptr::null_mut(), name);
assert!(!node.is_null());
tree::add_child(parent, node);
node
}
#[test]
fn test_validate_name_null() {
unsafe {
assert_eq!(validate_name(ptr::null()), 0);
}
}
#[test]
fn test_validate_name_empty() {
unsafe {
let s = b"\0" as *const u8 as *const xmlChar;
assert_eq!(validate_name(s), 0);
}
}
#[test]
fn test_validate_name_valid() {
unsafe {
let tests = ["foo", "_bar", ":baz", "hello-world", "ns:elem", "a123"];
for t in &tests {
let s = c_str(t);
assert_eq!(validate_name(s), 1, "Expected '{}' to be a valid Name", t);
allocator::xmlFree(s as *mut c_void);
}
}
}
#[test]
fn test_validate_name_invalid() {
unsafe {
let tests = ["123abc", "-foo", ".bar", "foo bar", "a b"];
for t in &tests {
let s = c_str(t);
assert_eq!(validate_name(s), 0, "Expected '{}' to be invalid", t);
allocator::xmlFree(s as *mut c_void);
}
}
}
#[test]
fn test_validate_names_valid() {
unsafe {
let s = c_str("foo bar baz");
assert_eq!(validate_names(s), 1);
allocator::xmlFree(s as *mut c_void);
}
}
#[test]
fn test_validate_names_invalid() {
unsafe {
let s = c_str("foo 123bar baz");
assert_eq!(validate_names(s), 0);
allocator::xmlFree(s as *mut c_void);
}
}
#[test]
fn test_validate_nmtoken_null() {
unsafe {
assert_eq!(validate_nmtoken(ptr::null()), 0);
}
}
#[test]
fn test_validate_nmtoken_valid() {
unsafe {
let tests = ["foo", "123abc", "-foo", ".bar", "_test", ":ns"];
for t in &tests {
let s = c_str(t);
assert_eq!(
validate_nmtoken(s),
1,
"Expected '{}' to be a valid NMTOKEN",
t
);
allocator::xmlFree(s as *mut c_void);
}
}
}
#[test]
fn test_validate_nmtoken_invalid() {
unsafe {
let s = c_str("foo bar");
assert_eq!(validate_nmtoken(s), 0);
allocator::xmlFree(s as *mut c_void);
}
}
#[test]
fn test_validate_nmtokens_valid() {
unsafe {
let s = c_str("foo 123bar -baz");
assert_eq!(validate_nmtokens(s), 1);
allocator::xmlFree(s as *mut c_void);
}
}
#[test]
fn test_validate_attribute_value_cdata() {
unsafe {
let s = c_str("anything goes here!@#$%^&*()");
assert_eq!(validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, s), 1);
allocator::xmlFree(s as *mut c_void);
let empty = b"\0" as *const u8 as *const xmlChar;
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, empty),
1
);
}
}
#[test]
fn test_validate_attribute_value_id() {
unsafe {
let valid = c_str("myId");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_ID as c_int, valid),
1
);
allocator::xmlFree(valid as *mut c_void);
let invalid = c_str("123id");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_ID as c_int, invalid),
0
);
allocator::xmlFree(invalid as *mut c_void);
}
}
#[test]
fn test_validate_attribute_value_idref() {
unsafe {
let valid = c_str("someId");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, valid),
1
);
allocator::xmlFree(valid as *mut c_void);
}
}
#[test]
fn test_validate_attribute_value_idrefs() {
unsafe {
let valid = c_str("id1 id2 id3");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, valid),
1
);
allocator::xmlFree(valid as *mut c_void);
let invalid = c_str("id1 123id");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, invalid),
0
);
allocator::xmlFree(invalid as *mut c_void);
}
}
#[test]
fn test_validate_attribute_value_entity() {
unsafe {
let valid = c_str("myEntity");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_ENTITY as c_int, valid),
1
);
allocator::xmlFree(valid as *mut c_void);
}
}
#[test]
fn test_validate_attribute_value_nmtoken() {
unsafe {
let valid = c_str("123abc");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, valid),
1
);
allocator::xmlFree(valid as *mut c_void);
let invalid = c_str("foo bar");
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, invalid),
0
);
allocator::xmlFree(invalid as *mut c_void);
}
}
#[test]
fn test_validate_attribute_value_null() {
unsafe {
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, ptr::null()),
0
);
}
}
#[test]
fn test_validate_enumeration_valid() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
let red = c_str("red");
let green = c_str("green");
let blue = c_str("blue");
let e3 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
(*e3).name = string::xml_strdup(blue);
(*e3).next = ptr::null_mut();
let e2 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
(*e2).name = string::xml_strdup(green);
(*e2).next = e3;
let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
(*e1).name = string::xml_strdup(red);
(*e1).next = e2;
let value = c_str("green");
assert_eq!(validate_enumeration(ctxt, value, e1), 1);
assert_eq!((*ctxt).valid, 1);
allocator::xmlFree(value as *mut c_void);
allocator::xmlFree(red as *mut c_void);
allocator::xmlFree(green as *mut c_void);
allocator::xmlFree(blue as *mut c_void);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_enumeration_invalid() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
(*e1).name = string::xml_strdup(b"red\0" as *const u8 as *const xmlChar);
(*e1).next = ptr::null_mut();
let value = c_str("yellow");
assert_eq!(validate_enumeration(ctxt, value, e1), 0);
allocator::xmlFree(value as *mut c_void);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_notation_use_valid() {
unsafe {
let (doc, dtd) = make_test_doc();
let notation_name = c_str("GIF");
dtd::add_notation_decl(dtd, notation_name, ptr::null(), ptr::null());
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_notation_use(ctxt, doc, notation_name), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_notation_use_invalid() {
unsafe {
let (doc, _dtd) = make_test_doc();
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
let notation_name = c_str("UNDECLARED");
assert_eq!(validate_notation_use(ctxt, doc, notation_name), 0);
free_valid_ctxt(ctxt);
allocator::xmlFree(notation_name as *mut c_void);
tree::free_doc(doc);
}
}
#[test]
fn test_new_free_valid_ctxt() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!((*ctxt).valid, 1);
assert!((*ctxt).node.is_null());
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_free_valid_ctxt_null() {
unsafe {
free_valid_ctxt(ptr::null_mut());
}
}
#[test]
fn test_set_valid_errors_null() {
unsafe {
set_valid_errors(ptr::null_mut(), None, None, ptr::null_mut());
}
}
#[test]
fn test_validate_element_no_dtd() {
unsafe {
let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
assert!(!doc.is_null());
let root_name = c_str("root");
let root = create_root_elem(doc, root_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, root), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_element_empty_valid() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
let root = create_root_elem(doc, root_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, root), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_element_undeclared() {
unsafe {
let (doc, _dtd) = make_test_doc();
let root_name = c_str("root");
let root = create_root_elem(doc, root_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, root), 0);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_element_with_content() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
let child_name = c_str("child");
let child_content =
dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
assert!(!child_content.is_null());
(*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_ELEMENT as c_int,
child_content,
);
add_elem_decl(
dtd,
child_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
let root = create_root_elem(doc, root_name);
let _child = create_child_elem(root, child_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, root), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_element_invalid_content() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
let child_name = c_str("child");
let wrong_name = c_str("wrong");
let child_content =
dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
assert!(!child_content.is_null());
(*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_ELEMENT as c_int,
child_content,
);
add_elem_decl(
dtd,
child_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
add_elem_decl(
dtd,
wrong_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
let root = create_root_elem(doc, root_name);
create_child_elem(root, wrong_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, root), 0);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_root_match() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
create_root_elem(doc, root_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_root(ctxt, doc), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_root_no_dtd() {
unsafe {
let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
assert!(!doc.is_null());
let root_name = c_str("root");
create_root_elem(doc, root_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_root(ctxt, doc), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_document_valid() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
create_root_elem(doc, root_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_document(ctxt, doc), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_document_no_root() {
unsafe {
let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
assert!(!doc.is_null());
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_document(ctxt, doc), 0);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_content_valid() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
let child_name = c_str("child");
let child_content =
dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
assert!(!child_content.is_null());
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_ELEMENT as c_int,
child_content,
);
add_elem_decl(
dtd,
child_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
let root = create_root_elem(doc, root_name);
create_child_elem(root, child_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_content(ctxt, root, doc), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_is_mixed_element() {
unsafe {
let (doc, dtd) = make_test_doc();
let name = c_str("mixedElem");
add_elem_decl(dtd, name, XML_ELEMENT_TYPE_MIXED as c_int, ptr::null_mut());
assert_eq!(is_mixed_element(doc, name), 1);
let other = c_str("other");
assert_eq!(is_mixed_element(doc, other), 0);
allocator::xmlFree(other as *mut c_void);
tree::free_doc(doc);
}
}
#[test]
fn test_is_empty_element() {
unsafe {
let (doc, dtd) = make_test_doc();
let name = c_str("emptyElem");
add_elem_decl(dtd, name, XML_ELEMENT_TYPE_EMPTY as c_int, ptr::null_mut());
assert_eq!(is_empty_element(doc, name), 1);
let other = c_str("other");
assert_eq!(is_empty_element(doc, other), 0);
allocator::xmlFree(other as *mut c_void);
tree::free_doc(doc);
}
}
#[test]
fn test_is_mixed_element_no_dtd() {
unsafe {
let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
assert!(!doc.is_null());
let name = c_str("foo");
assert_eq!(is_mixed_element(doc, name), 0);
allocator::xmlFree(name as *mut c_void);
tree::free_doc(doc);
}
}
#[test]
fn test_is_empty_element_no_dtd() {
unsafe {
let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
assert!(!doc.is_null());
let name = c_str("foo");
assert_eq!(is_empty_element(doc, name), 0);
allocator::xmlFree(name as *mut c_void);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_dtd_null() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_dtd(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_element_null() {
unsafe {
let (doc, _dtd) = make_test_doc();
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, ptr::null_mut()), 0);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_document_null() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_document(ctxt, ptr::null_mut()), 0);
assert_eq!(validate_document(ptr::null_mut(), ptr::null_mut()), 0);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_document_final_null() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_document_final(ctxt, ptr::null_mut()), 0);
assert_eq!(validate_document_final(ptr::null_mut(), ptr::null_mut()), 0);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_attribute_decl_null() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(
validate_attribute_decl(ctxt, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()),
0
);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_content_null() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_content(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_root_null() {
unsafe {
assert_eq!(validate_root(ptr::null_mut(), ptr::null_mut()), 0);
}
}
#[test]
fn test_validate_enumeration_null() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_enumeration(ctxt, ptr::null(), ptr::null_mut()), 0);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_notation_use_null() {
unsafe {
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_notation_use(ctxt, ptr::null_mut(), ptr::null()), 0);
free_valid_ctxt(ctxt);
}
}
#[test]
fn test_validate_name_start_characters() {
unsafe {
let name = c_str("\u{C0}lph\u{E0}");
assert_eq!(validate_name(name), 1);
allocator::xmlFree(name as *mut c_void);
}
}
#[test]
fn test_validate_names_single() {
unsafe {
let s = c_str("singleName");
assert_eq!(validate_names(s), 1);
allocator::xmlFree(s as *mut c_void);
}
}
#[test]
fn test_validate_nmtokens_single() {
unsafe {
let s = c_str("123abc");
assert_eq!(validate_nmtokens(s), 1);
allocator::xmlFree(s as *mut c_void);
}
}
#[test]
fn test_validate_nmtokens_invalid() {
unsafe {
let s = c_str("foo\tbar"); assert_eq!(validate_nmtokens(s), 1); allocator::xmlFree(s as *mut c_void);
let s2 = c_str("foo@bar");
assert_eq!(validate_nmtokens(s2), 0);
allocator::xmlFree(s2 as *mut c_void);
}
}
#[test]
fn test_validate_attribute_value_empty_non_cdata() {
unsafe {
let empty = b"\0" as *const u8 as *const xmlChar;
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_ID as c_int, empty),
0
);
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, empty),
0
);
assert_eq!(
validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, empty),
0
);
}
}
#[test]
fn test_validate_attribute_value_unknown_type() {
unsafe {
let s = c_str("test");
assert_eq!(validate_attribute_value(999, s), 0);
allocator::xmlFree(s as *mut c_void);
}
}
#[test]
fn test_validate_element_any_content() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_ANY as c_int,
ptr::null_mut(),
);
let child_name = c_str("child");
add_elem_decl(
dtd,
child_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
let root = create_root_elem(doc, root_name);
create_child_elem(root, child_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, root), 1);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_element_empty_with_child() {
unsafe {
let (doc, dtd) = make_test_doc();
let root_name = c_str("root");
add_elem_decl(
dtd,
root_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
let child_name = c_str("child");
add_elem_decl(
dtd,
child_name,
XML_ELEMENT_TYPE_EMPTY as c_int,
ptr::null_mut(),
);
let root = create_root_elem(doc, root_name);
create_child_elem(root, child_name);
let ctxt = new_valid_ctxt();
assert!(!ctxt.is_null());
assert_eq!(validate_element(ctxt, doc, root), 0);
free_valid_ctxt(ctxt);
tree::free_doc(doc);
}
}
#[test]
fn test_validate_dtd_final_null() {
unsafe {
assert_eq!(validate_dtd_final(ptr::null_mut(), ptr::null_mut()), 0);
}
}
}