1use core::ffi::c_void;
29use core::ptr;
30use std::os::raw::{c_char, c_int, c_uint};
31
32use crate::abi::allocator;
33use crate::abi::callbacks::xmlGenericErrorFunc;
34use crate::abi::callbacks::xmlHashDeallocator;
35use crate::abi::callbacks::xmlHashScannerFull;
36use crate::abi::structs::*;
37use crate::abi::types::xmlAttributeDefault::*;
38use crate::abi::types::xmlAttributeType::*;
39use crate::abi::types::xmlElementContentOccur::*;
40use crate::abi::types::xmlElementContentType::*;
41use crate::abi::types::xmlElementType::*;
42use crate::abi::types::xmlElementTypeVal::*;
43use crate::abi::types::xmlEntityType::*;
44use crate::abi::types::*;
45use crate::xml::dtd;
46use crate::xml::entities;
47use crate::xml::hash;
48use crate::xml::string;
49use crate::xml::tree;
50
51const VALID_CTXT_DEPTH_MAX: c_int = 256;
57
58pub unsafe fn new_valid_ctxt() -> *mut _xmlValidCtxt {
72 let ctxt = allocator::xmlMallocZero(size_of::<_xmlValidCtxt>() as usize) as *mut _xmlValidCtxt;
74 if ctxt.is_null() {
75 return ptr::null_mut();
76 }
77
78 unsafe {
79 (*ctxt).valid = 1;
80 (*ctxt).node = ptr::null_mut();
81 (*ctxt).doc = ptr::null_mut();
82 (*ctxt).nodeNr = 0;
83 (*ctxt).nodeMax = 0;
84 (*ctxt).nodeTab = ptr::null_mut();
85 (*ctxt).flags = 0;
86 (*ctxt).vstate = ptr::null_mut();
87 (*ctxt).vstateNr = 0;
88 (*ctxt).vstateMax = 0;
89 (*ctxt).vstateTab = ptr::null_mut();
90 (*ctxt).am = ptr::null_mut();
91 (*ctxt).state = ptr::null_mut();
92 (*ctxt).error = None;
93 (*ctxt).warning = None;
94 (*ctxt).userData = ptr::null_mut();
95 }
96
97 ctxt
98}
99
100pub unsafe fn free_valid_ctxt(ctxt: *mut _xmlValidCtxt) {
112 if ctxt.is_null() {
113 return;
114 }
115
116 unsafe {
117 let c = &mut *ctxt;
118
119 if !c.nodeTab.is_null() {
121 allocator::xmlFree(c.nodeTab as *mut c_void);
122 }
123
124 if !c.am.is_null() {
126 }
129
130 if !c.state.is_null() {
132 }
134
135 allocator::xmlFree(ctxt as *mut c_void);
136 }
137}
138
139pub unsafe fn set_valid_errors(
155 ctxt: *mut _xmlValidCtxt,
156 err: Option<xmlGenericErrorFunc>,
157 warn: Option<xmlGenericErrorFunc>,
158 data: *mut c_void,
159) {
160 if ctxt.is_null() {
161 return;
162 }
163
164 unsafe {
165 (*ctxt).error = err;
168 (*ctxt).warning = warn;
169 (*ctxt).userData = data;
170 }
171}
172
173unsafe fn vctxt_error(ctxt: *mut _xmlValidCtxt, msg: *const c_char) {
184 if ctxt.is_null() {
185 return;
186 }
187 unsafe {
188 let c = &mut *ctxt;
189 c.valid = 0;
190 if let Some(err) = c.error {
191 err(c.userData, msg);
192 }
193 }
194}
195
196unsafe fn vctxt_push_node(ctxt: *mut _xmlValidCtxt, node: *mut _xmlNode) -> c_int {
204 unsafe {
205 let c = &mut *ctxt;
206
207 if c.nodeNr >= c.nodeMax {
208 let new_max = if c.nodeMax == 0 { 4 } else { c.nodeMax * 2 };
209 let new_tab = allocator::xmlRealloc(
210 c.nodeTab as *mut c_void,
211 (new_max as usize) * size_of::<*mut _xmlNode>(),
212 ) as *mut *mut _xmlNode;
213 if new_tab.is_null() {
214 return -1;
215 }
216 c.nodeTab = new_tab;
217 c.nodeMax = new_max;
218 }
219
220 *c.nodeTab.add(c.nodeNr as usize) = node;
221 c.nodeNr += 1;
222 c.node = node;
223 }
224 0
225}
226
227unsafe fn vctxt_pop_node(ctxt: *mut _xmlValidCtxt) {
233 unsafe {
234 let c = &mut *ctxt;
235 if c.nodeNr > 0 {
236 c.nodeNr -= 1;
237 }
238 if c.nodeNr > 0 {
239 c.node = *c.nodeTab.add((c.nodeNr - 1) as usize);
240 } else {
241 c.node = ptr::null_mut();
242 }
243 }
244}
245
246unsafe fn get_valid_dtd(doc: *mut _xmlDoc) -> *mut _xmlDtd {
254 if doc.is_null() {
255 return ptr::null_mut();
256 }
257 unsafe {
258 let d = &*doc;
259 if !d.intSubset.is_null() {
260 d.intSubset
261 } else {
262 d.extSubset
263 }
264 }
265}
266
267pub(crate) fn is_xml_name_start(c: char) -> bool {
281 matches!(c,
282 'a'..='z' | 'A'..='Z' | '_' | ':' |
283 '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
284 '\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' |
285 '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' |
286 '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
287 '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' |
288 '\u{10000}'..='\u{EFFFF}'
289 )
290}
291
292pub(crate) fn is_xml_name_char(c: char) -> bool {
299 is_xml_name_start(c)
300 || matches!(c,
301 '-' | '.' | '0'..='9' | '\u{B7}' |
302 '\u{0300}'..='\u{036F}' | '\u{203F}'..='\u{2040}'
303 )
304}
305
306pub unsafe fn validate_name(value: *const xmlChar) -> c_int {
324 if value.is_null() {
325 return 0;
326 }
327
328 let s = unsafe { string::xmlstr_to_bytes(value) };
329 let s = core::str::from_utf8(s).unwrap_or("");
330
331 if s.is_empty() {
332 return 0;
333 }
334
335 let mut chars = s.chars();
336
337 match chars.next() {
339 Some(c) if is_xml_name_start(c) => {}
340 _ => return 0,
341 }
342
343 for c in chars {
345 if !is_xml_name_char(c) {
346 return 0;
347 }
348 }
349
350 1
351}
352
353pub unsafe fn validate_names(value: *const xmlChar) -> c_int {
367 if value.is_null() {
368 return 0;
369 }
370
371 let s = unsafe { string::xmlstr_to_bytes(value) };
372 let s = core::str::from_utf8(s).unwrap_or("");
373
374 if s.is_empty() {
375 return 0;
376 }
377
378 for token in s.split_whitespace() {
379 if token.is_empty() {
380 return 0;
381 }
382 let mut chars = token.chars();
383 match chars.next() {
384 Some(c) if is_xml_name_start(c) => {}
385 _ => return 0,
386 }
387 for c in chars {
388 if !is_xml_name_char(c) {
389 return 0;
390 }
391 }
392 }
393
394 1
395}
396
397pub unsafe fn validate_nmtoken(value: *const xmlChar) -> c_int {
416 if value.is_null() {
417 return 0;
418 }
419
420 let s = unsafe { string::xmlstr_to_bytes(value) };
421 let s = core::str::from_utf8(s).unwrap_or("");
422
423 if s.is_empty() {
424 return 0;
425 }
426
427 for c in s.chars() {
428 if !is_xml_name_char(c) {
429 return 0;
430 }
431 }
432
433 1
434}
435
436pub unsafe fn validate_nmtokens(value: *const xmlChar) -> c_int {
450 if value.is_null() {
451 return 0;
452 }
453
454 let s = unsafe { string::xmlstr_to_bytes(value) };
455 let s = core::str::from_utf8(s).unwrap_or("");
456
457 if s.is_empty() {
458 return 0;
459 }
460
461 for token in s.split_whitespace() {
462 if token.is_empty() {
463 return 0;
464 }
465 for c in token.chars() {
466 if !is_xml_name_char(c) {
467 return 0;
468 }
469 }
470 }
471
472 1
473}
474
475pub unsafe fn validate_attribute_value(atype: c_int, value: *const xmlChar) -> c_int {
493 match atype as u32 {
497 t if t == XML_ATTRIBUTE_ENTITIES as u32 || t == XML_ATTRIBUTE_IDREFS as u32 => {
498 validate_values_internal(value, 0)
499 }
500 t if t == XML_ATTRIBUTE_ENTITY as u32
501 || t == XML_ATTRIBUTE_IDREF as u32
502 || t == XML_ATTRIBUTE_ID as u32
503 || t == XML_ATTRIBUTE_NOTATION as u32 =>
504 {
505 validate_value_internal(value, 0)
506 }
507 t if t == XML_ATTRIBUTE_NMTOKENS as u32 || t == XML_ATTRIBUTE_ENUMERATION as u32 => {
508 validate_values_internal(value, XML_SCAN_NMTOKEN)
509 }
510 t if t == XML_ATTRIBUTE_NMTOKEN as u32 => validate_value_internal(value, XML_SCAN_NMTOKEN),
511 _ => 1, }
513}
514
515pub unsafe fn validate_enumeration(
537 ctxt: *mut _xmlValidCtxt,
538 value: *const xmlChar,
539 tree: *mut _xmlEnumeration,
540) -> c_int {
541 if value.is_null() || tree.is_null() {
542 return 0;
543 }
544
545 let mut cur = tree;
546 while !cur.is_null() {
547 unsafe {
548 if string::xml_strcmp(value, (*cur).name) == 0 {
549 return 1;
550 }
551 cur = (*cur).next;
552 }
553 }
554
555 unsafe {
557 let msg = string::xmlstr_to_string(value);
558 let err_msg = format!("Value '{}' is not a valid enumeration value\0", msg);
559 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
560 }
561 0
562}
563
564pub unsafe fn validate_notation_use(
584 ctxt: *mut _xmlValidCtxt,
585 doc: *mut _xmlDoc,
586 notation_name: *const xmlChar,
587) -> c_int {
588 if notation_name.is_null() {
589 return 0;
590 }
591
592 let dtd = unsafe { get_valid_dtd(doc) };
593 if dtd.is_null() {
594 unsafe {
595 vctxt_error(
596 ctxt,
597 b"No DTD available for notation validation\0" as *const u8 as *const c_char,
598 );
599 }
600 return 0;
601 }
602
603 unsafe {
605 let notations = (*dtd).notations;
606 if notations.is_null() {
607 vctxt_error(
608 ctxt,
609 b"No notations declared in DTD\0" as *const u8 as *const c_char,
610 );
611 return 0;
612 }
613
614 let notation = hash::hash_lookup(notations as *mut hash::HashTable, notation_name);
615 if notation.is_null() {
616 let msg = string::xmlstr_to_string(notation_name);
617 let err_msg = format!("Notation '{}' is not declared\0", msg);
618 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
619 return 0;
620 }
621 }
622
623 1
624}
625
626pub unsafe fn validate_id(
648 ctxt: *mut _xmlValidCtxt,
649 doc: *mut _xmlDoc,
650 node: *mut _xmlNode,
651 value: *const xmlChar,
652) -> c_int {
653 if value.is_null() || doc.is_null() {
654 return 0;
655 }
656
657 if unsafe { validate_name_value(value) } == 0 {
660 unsafe {
661 let msg = string::xmlstr_to_string(value);
662 let err_msg = format!("ID value '{}' is not a valid XML Name\0", msg);
663 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
664 }
665 return 0;
666 }
667
668 unsafe {
670 let doc_ref = &*doc;
671 if !doc_ref.ids.is_null() {
672 let existing = hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, value);
673 if !existing.is_null() {
674 let msg = string::xmlstr_to_string(value);
675 let err_msg = format!("Duplicate ID value '{}'\0", msg);
676 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
677 return 0;
678 }
679 }
680 }
681
682 unsafe {
684 if (*doc).ids.is_null() {
685 (*doc).ids = hash::hash_create(16) as *mut c_void;
686 }
687 hash::hash_add_entry(
688 (*doc).ids as *mut hash::HashTable,
689 value,
690 node as *mut c_void,
691 );
692 }
693
694 1
695}
696
697pub unsafe fn validate_id_ref(
714 ctxt: *mut _xmlValidCtxt,
715 doc: *mut _xmlDoc,
716 node: *mut _xmlNode,
717 value: *const xmlChar,
718) -> c_int {
719 if value.is_null() || doc.is_null() {
720 return 0;
721 }
722
723 if unsafe { validate_name_value(value) } == 0 {
726 unsafe {
727 let msg = string::xmlstr_to_string(value);
728 let err_msg = format!("IDREF value '{}' is not a valid XML Name\0", msg);
729 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
730 }
731 return 0;
732 }
733
734 unsafe {
736 let doc_ref = &*doc;
737 if doc_ref.ids.is_null()
738 || hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, value).is_null()
739 {
740 let msg = string::xmlstr_to_string(value);
744 let err_msg = format!("IDREF '{}' references an unknown ID\0", msg);
745 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
746 return 0;
747 }
748 }
749
750 1
751}
752
753pub unsafe fn validate_id_refs(
770 ctxt: *mut _xmlValidCtxt,
771 doc: *mut _xmlDoc,
772 node: *mut _xmlNode,
773 value: *const xmlChar,
774) -> c_int {
775 if value.is_null() || doc.is_null() {
776 return 0;
777 }
778
779 let s = unsafe { string::xmlstr_to_bytes(value) };
780 let s = core::str::from_utf8(s).unwrap_or("");
781
782 if s.is_empty() {
783 return 0;
784 }
785
786 let mut valid = 1;
787 for token in s.split_whitespace() {
788 if token.is_empty() {
789 continue;
790 }
791 let token_ptr = unsafe { string::bytes_to_xmlstr(token.as_bytes()) };
793 if token_ptr.is_null() {
794 valid = 0;
795 break;
796 }
797 let result = unsafe { validate_id_ref(ctxt, doc, node, token_ptr) };
798 unsafe {
799 allocator::xmlFree(token_ptr as *mut c_void);
800 }
801 if result == 0 {
802 valid = 0;
803 }
804 }
805
806 valid
807}
808
809pub unsafe fn validate_attribute_decl(
836 ctxt: *mut _xmlValidCtxt,
837 doc: *mut _xmlDoc,
838 elem: *mut _xmlNode,
839 attr: *mut _xmlAttribute,
840) -> c_int {
841 if attr.is_null() {
842 return 0;
843 }
844
845 unsafe {
846 let a = &*attr;
847 let atype = a.atype as c_int;
848
849 if !a.defaultValue.is_null() {
851 if validate_attribute_value(atype, a.defaultValue) == 0 {
852 let name_str = string::xmlstr_to_string(a.name);
853 let val_str = string::xmlstr_to_string(a.defaultValue);
854 let err_msg = format!(
855 "Default value '{}' for attribute '{}' is not valid for its type\0",
856 val_str, name_str
857 );
858 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
859 return 0;
860 }
861 }
862
863 if atype == XML_ATTRIBUTE_ENUMERATION as c_int && !a.tree.is_null() {
865 let mut cur = a.tree;
867 while !cur.is_null() {
868 if !(*cur).name.is_null() {
869 if validate_nmtoken_value((*cur).name) == 0 {
870 let val_str = string::xmlstr_to_string((*cur).name);
871 let err_msg =
872 format!("Enumeration value '{}' is not a valid NMTOKEN\0", val_str);
873 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
874 return 0;
875 }
876 }
877 cur = (*cur).next;
878 }
879 }
880
881 if atype == XML_ATTRIBUTE_NOTATION as c_int && !a.tree.is_null() {
883 let mut cur = a.tree;
884 while !cur.is_null() {
885 if !(*cur).name.is_null() {
886 if validate_notation_use(ctxt, doc, (*cur).name) == 0 {
887 let val_str = string::xmlstr_to_string((*cur).name);
888 let err_msg = format!(
889 "NOTATION value '{}' references undeclared notation\0",
890 val_str
891 );
892 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
893 return 0;
894 }
895 }
896 cur = (*cur).next;
897 }
898 }
899
900 1
901 }
902}
903
904pub unsafe fn validate_element(
933 ctxt: *mut _xmlValidCtxt,
934 doc: *mut _xmlDoc,
935 elem: *mut _xmlNode,
936) -> c_int {
937 if elem.is_null() || doc.is_null() || ctxt.is_null() {
938 return 0;
939 }
940
941 unsafe {
942 let e = &*elem;
943
944 if e.type_ != XML_ELEMENT_NODE as c_int {
946 return 1;
947 }
948
949 if vctxt_push_node(ctxt, elem) != 0 {
951 return 0;
952 }
953
954 let mut valid = 1;
955
956 let dtd = get_valid_dtd(doc);
958 if dtd.is_null() {
959 vctxt_pop_node(ctxt);
962 return 1;
963 }
964
965 let dtd_ref = &*dtd;
966
967 let elem_name = e.name;
969 let elem_decl = if !dtd_ref.elements.is_null() {
970 hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, elem_name)
971 } else {
972 ptr::null_mut()
973 };
974
975 if elem_decl.is_null() {
976 let name_str = string::xmlstr_to_string(elem_name);
977 let err_msg = format!("No declaration for element {}\0", name_str);
978 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
979 vctxt_pop_node(ctxt);
980 return 0;
981 }
982
983 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
984
985 let elem_type = elem_decl_ref.etype as u32;
987
988 if elem_type == XML_ELEMENT_TYPE_EMPTY as u32 {
989 let mut child = e.children;
991 while !child.is_null() {
992 let child_type = (*child).type_ as u32;
993 if child_type != XML_TEXT_NODE as u32 && child_type != XML_CDATA_SECTION_NODE as u32
994 {
995 valid = 0;
996 let name_str = string::xmlstr_to_string(elem_name);
997 let err_msg = format!(
998 "Element '{}' is declared EMPTY but has child elements\0",
999 name_str
1000 );
1001 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1002 break;
1003 }
1004 child = (*child).next;
1005 }
1006 } else if elem_type == XML_ELEMENT_TYPE_ANY as u32 {
1007 } else if elem_type == XML_ELEMENT_TYPE_MIXED as u32 {
1009 let mut child = e.children;
1011 while !child.is_null() {
1012 let child_type = (*child).type_ as u32;
1013 if child_type == XML_ELEMENT_NODE as u32 {
1014 let child_name = (*child).name;
1016 let result = dtd::valid_content_model(elem_decl_ref.content, &[child_name]);
1017 if result != dtd::ContentModelResult::Valid {
1018 let cname_str = string::xmlstr_to_string(child_name);
1019 let ename_str = string::xmlstr_to_string(elem_name);
1020 let err_msg = format!(
1021 "Element '{}' is not allowed in mixed content of '{}'\0",
1022 cname_str, ename_str
1023 );
1024 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1025 valid = 0;
1026 }
1027 }
1028 child = (*child).next;
1029 }
1030 } else if elem_type == XML_ELEMENT_TYPE_ELEMENT as u32 {
1031 let mut child_names: Vec<*const xmlChar> = Vec::new();
1033 let mut child = e.children;
1034 while !child.is_null() {
1035 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1036 child_names.push((*child).name);
1037 }
1038 child = (*child).next;
1039 }
1040
1041 let result = dtd::valid_content_model(elem_decl_ref.content, &child_names);
1042 if result != dtd::ContentModelResult::Valid {
1043 let ename_str = string::xmlstr_to_string(elem_name);
1044 let err_msg = format!(
1045 "Content model validation failed for element '{}'\0",
1046 ename_str
1047 );
1048 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1049 valid = 0;
1050 }
1051 }
1052
1053 if !dtd_ref.attributes.is_null() {
1055 let mut attr_prop = e.properties;
1057 while !attr_prop.is_null() {
1058 let attr_ref = &*attr_prop;
1059 let attr_name = attr_ref.name;
1060
1061 let attr_decl = hash::hash_lookup2(
1063 dtd_ref.attributes as *mut hash::HashTable,
1064 elem_name,
1065 attr_name,
1066 );
1067
1068 if attr_decl.is_null() {
1069 attr_prop = attr_ref.next;
1074 continue;
1075 }
1076
1077 let attr_decl_ref = &*(attr_decl as *mut _xmlAttribute);
1078 let atype = attr_decl_ref.atype as c_int;
1079
1080 let attr_value = if !attr_ref.children.is_null() {
1082 let text_node = attr_ref.children;
1084 if (*text_node).type_ == XML_TEXT_NODE as c_int
1085 || (*text_node).type_ == XML_CDATA_SECTION_NODE as c_int
1086 {
1087 (*text_node).content
1088 } else {
1089 ptr::null()
1090 }
1091 } else {
1092 ptr::null()
1093 };
1094
1095 if !attr_value.is_null() {
1097 if atype == XML_ATTRIBUTE_ENUMERATION as c_int && !attr_decl_ref.tree.is_null()
1098 {
1099 if validate_enumeration(ctxt, attr_value, attr_decl_ref.tree) == 0 {
1100 let aname_str = string::xmlstr_to_string(attr_name);
1101 let aval_str = string::xmlstr_to_string(attr_value);
1102 let err_msg = format!(
1103 "Attribute '{}' has value '{}' not in enumeration\0",
1104 aname_str, aval_str
1105 );
1106 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1107 valid = 0;
1108 }
1109 } else if atype == XML_ATTRIBUTE_NOTATION as c_int {
1110 if validate_notation_use(ctxt, doc, attr_value) == 0 {
1111 let aname_str = string::xmlstr_to_string(attr_name);
1112 let aval_str = string::xmlstr_to_string(attr_value);
1113 let err_msg = format!(
1114 "Attribute '{}' references undeclared notation '{}'\0",
1115 aname_str, aval_str
1116 );
1117 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1118 valid = 0;
1119 }
1120 } else if validate_attribute_value(atype, attr_value) == 0 {
1121 let aname_str = string::xmlstr_to_string(attr_name);
1122 let aval_str = string::xmlstr_to_string(attr_value);
1123 let err_msg = format!(
1124 "Attribute '{}' has invalid value '{}' for its type\0",
1125 aname_str, aval_str
1126 );
1127 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1128 valid = 0;
1129 }
1130
1131 if atype == XML_ATTRIBUTE_ID as c_int {
1133 if validate_id(ctxt, doc, elem, attr_value) == 0 {
1134 valid = 0;
1135 }
1136 } else if atype == XML_ATTRIBUTE_IDREF as c_int {
1137 if validate_id_ref(ctxt, doc, elem, attr_value) == 0 {
1138 valid = 0;
1139 }
1140 } else if atype == XML_ATTRIBUTE_IDREFS as c_int {
1141 if validate_id_refs(ctxt, doc, elem, attr_value) == 0 {
1142 valid = 0;
1143 }
1144 }
1145 }
1146
1147 attr_prop = attr_ref.next;
1148 }
1149
1150 struct RequiredAttrCheck {
1152 ctxt: *mut _xmlValidCtxt,
1153 elem_name: *const xmlChar,
1154 elem_props: *mut _xmlAttr,
1155 valid: *mut c_int,
1156 }
1157
1158 extern "C" fn check_required_attr(
1159 payload: *mut c_void,
1160 data: *mut c_void,
1161 name: *const xmlChar,
1162 name2: *const xmlChar,
1163 _name3: *const xmlChar,
1164 ) {
1165 if payload.is_null() || data.is_null() || name2.is_null() {
1166 return;
1167 }
1168
1169 let check = unsafe { &*(data as *mut RequiredAttrCheck) };
1171 unsafe {
1172 if string::xml_strcmp(name, check.elem_name) != 0 {
1174 return;
1175 }
1176
1177 let attr_decl = &*(payload as *mut _xmlAttribute);
1178
1179 if attr_decl.def == XML_ATTRIBUTE_REQUIRED as c_int {
1181 let mut found = 0;
1183 let mut prop = check.elem_props;
1184 while !prop.is_null() {
1185 if string::xml_strcmp((*prop).name, name2) == 0 {
1186 found = 1;
1187 break;
1188 }
1189 prop = (*prop).next;
1190 }
1191
1192 if found == 0 {
1193 let aname_str = string::xmlstr_to_string(name2);
1194 let ename_str = string::xmlstr_to_string(check.elem_name);
1195 let err_msg = format!(
1196 "Required attribute '{}' missing on element '{}'\0",
1197 aname_str, ename_str
1198 );
1199 vctxt_error(check.ctxt, err_msg.as_ptr() as *const c_char);
1200 *(check.valid) = 0;
1201 }
1202 }
1203 }
1204 }
1205
1206 let mut required_valid = valid;
1207 let check = RequiredAttrCheck {
1208 ctxt,
1209 elem_name,
1210 elem_props: e.properties,
1211 valid: &mut required_valid,
1212 };
1213
1214 hash::hash_scan_full(
1215 dtd_ref.attributes as *mut hash::HashTable,
1216 Some(check_required_attr),
1217 &check as *const RequiredAttrCheck as *mut c_void,
1218 );
1219
1220 valid = required_valid;
1221 }
1222
1223 let mut child = e.children;
1225 while !child.is_null() {
1226 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1227 if validate_element(ctxt, doc, child) == 0 {
1228 valid = 0;
1229 }
1230 }
1231 child = (*child).next;
1232 }
1233
1234 vctxt_pop_node(ctxt);
1235 valid
1236 }
1237}
1238
1239pub unsafe fn validate_document(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1259 if ctxt.is_null() || doc.is_null() {
1260 return 0;
1261 }
1262
1263 unsafe {
1264 let c = &mut *ctxt;
1265 c.doc = doc;
1266 c.valid = 1;
1267
1268 let d = &*doc;
1269
1270 if d.intSubset.is_null() && d.extSubset.is_null() {
1280 vctxt_error(ctxt, b"no DTD found!\0" as *const u8 as *const c_char);
1281 return 0;
1282 }
1283
1284 let mut root = d.children;
1286 while !root.is_null() {
1287 if (*root).type_ == XML_ELEMENT_NODE as c_int {
1288 break;
1289 }
1290 root = (*root).next;
1291 }
1292
1293 if root.is_null() {
1294 vctxt_error(
1295 ctxt,
1296 b"No root element found in document\0" as *const u8 as *const c_char,
1297 );
1298 return 0;
1299 }
1300
1301 if validate_element(ctxt, doc, root) == 0 {
1303 return 0;
1304 }
1305
1306 c.valid
1307 }
1308}
1309
1310pub unsafe fn validate_document_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1332 if ctxt.is_null() || doc.is_null() {
1333 return 0;
1334 }
1335
1336 unsafe {
1337 let c = &mut *ctxt;
1338 c.doc = doc;
1339
1340 let d = &*doc;
1341
1342 if d.refs.is_null() {
1344 return c.valid;
1345 }
1346
1347 struct IdRefCheckContext {
1349 ctxt: *mut _xmlValidCtxt,
1350 doc: *mut _xmlDoc,
1351 }
1352
1353 extern "C" fn check_idref(
1354 _payload: *mut c_void,
1355 data: *mut c_void,
1356 _name: *const xmlChar,
1357 name2: *const xmlChar,
1358 _name3: *const xmlChar,
1359 ) {
1360 if data.is_null() || name2.is_null() {
1361 return;
1362 }
1363
1364 let cx = unsafe { &*(data as *mut IdRefCheckContext) };
1366 unsafe {
1367 let doc_ref = &*cx.doc;
1368
1369 if doc_ref.ids.is_null()
1371 || hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, name2).is_null()
1372 {
1373 let ref_str = string::xmlstr_to_string(name2);
1374 let err_msg = format!("IDREF '{}' does not reference a declared ID\0", ref_str);
1375 vctxt_error(cx.ctxt, err_msg.as_ptr() as *const c_char);
1376 }
1377 }
1378 }
1379
1380 let ctx = IdRefCheckContext { ctxt, doc };
1381 hash::hash_scan_full(
1382 d.refs as *mut hash::HashTable,
1383 Some(check_idref),
1384 &ctx as *const IdRefCheckContext as *mut c_void,
1385 );
1386
1387 c.valid
1388 }
1389}
1390
1391pub unsafe fn validate_root(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1409 if ctxt.is_null() || doc.is_null() {
1410 return 0;
1411 }
1412
1413 unsafe {
1414 let c = &mut *ctxt;
1415 c.doc = doc;
1416 c.valid = 1;
1417
1418 let d = &*doc;
1419
1420 let mut root = d.children;
1422 while !root.is_null() {
1423 if (*root).type_ == XML_ELEMENT_NODE as c_int {
1424 break;
1425 }
1426 root = (*root).next;
1427 }
1428
1429 if root.is_null() {
1430 vctxt_error(
1431 ctxt,
1432 b"No root element found\0" as *const u8 as *const c_char,
1433 );
1434 return 0;
1435 }
1436
1437 let dtd = get_valid_dtd(doc);
1439 if dtd.is_null() {
1440 return 1;
1442 }
1443
1444 let dtd_ref = &*dtd;
1447 if !dtd_ref.name.is_null() {
1448 if string::xml_strcmp((*root).name, dtd_ref.name) != 0 {
1449 let root_str = string::xmlstr_to_string((*root).name);
1450 let dtd_str = string::xmlstr_to_string(dtd_ref.name);
1451 let err_msg = format!(
1452 "Root element '{}' does not match DTD root '{}'\0",
1453 root_str, dtd_str
1454 );
1455 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1456 return 0;
1457 }
1458 }
1459
1460 c.valid
1461 }
1462}
1463
1464pub unsafe fn validate_content(
1484 ctxt: *mut _xmlValidCtxt,
1485 node: *mut _xmlNode,
1486 doc: *mut _xmlDoc,
1487) -> c_int {
1488 if node.is_null() || doc.is_null() || ctxt.is_null() {
1489 return 0;
1490 }
1491
1492 unsafe {
1493 let n = &*node;
1494 if n.type_ != XML_ELEMENT_NODE as c_int {
1495 return 1;
1496 }
1497
1498 let dtd = get_valid_dtd(doc);
1499 if dtd.is_null() {
1500 return 1;
1501 }
1502
1503 let dtd_ref = &*dtd;
1504 if dtd_ref.elements.is_null() {
1505 return 1;
1506 }
1507
1508 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, n.name);
1509 if elem_decl.is_null() {
1510 return 1;
1511 }
1512
1513 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1514 if elem_decl_ref.content.is_null() {
1515 return 1;
1516 }
1517
1518 let elem_type = elem_decl_ref.etype as u32;
1519 if elem_type == XML_ELEMENT_TYPE_EMPTY as u32 {
1520 let mut child = n.children;
1522 while !child.is_null() {
1523 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1524 let name_str = string::xmlstr_to_string(n.name);
1525 let err_msg =
1526 format!("Element '{}' is EMPTY but has child elements\0", name_str);
1527 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1528 return 0;
1529 }
1530 child = (*child).next;
1531 }
1532 return 1;
1533 }
1534
1535 if elem_type == XML_ELEMENT_TYPE_ANY as u32 {
1536 return 1;
1537 }
1538
1539 let mut child_names: Vec<*const xmlChar> = Vec::new();
1541 let mut child = n.children;
1542 while !child.is_null() {
1543 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1544 child_names.push((*child).name);
1545 }
1546 child = (*child).next;
1547 }
1548
1549 let result = dtd::valid_content_model(elem_decl_ref.content, &child_names);
1550 if result != dtd::ContentModelResult::Valid {
1551 let name_str = string::xmlstr_to_string(n.name);
1552 let err_msg = format!(
1553 "Content model validation failed for element '{}'\0",
1554 name_str
1555 );
1556 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1557 0
1558 } else {
1559 1
1560 }
1561 }
1562}
1563
1564pub unsafe fn is_mixed_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
1582 if doc.is_null() || name.is_null() {
1583 return 0;
1584 }
1585
1586 let dtd = unsafe { get_valid_dtd(doc) };
1587 if dtd.is_null() {
1588 return 0;
1589 }
1590
1591 unsafe {
1592 let dtd_ref = &*dtd;
1593 if dtd_ref.elements.is_null() {
1594 return 0;
1595 }
1596
1597 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
1598 if elem_decl.is_null() {
1599 return 0;
1600 }
1601
1602 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1603 ((elem_decl_ref.etype as u32) == XML_ELEMENT_TYPE_MIXED as u32) as c_int
1604 }
1605}
1606
1607pub unsafe fn is_empty_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
1621 if doc.is_null() || name.is_null() {
1622 return 0;
1623 }
1624
1625 let dtd = unsafe { get_valid_dtd(doc) };
1626 if dtd.is_null() {
1627 return 0;
1628 }
1629
1630 unsafe {
1631 let dtd_ref = &*dtd;
1632 if dtd_ref.elements.is_null() {
1633 return 0;
1634 }
1635
1636 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
1637 if elem_decl.is_null() {
1638 return 0;
1639 }
1640
1641 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1642 ((elem_decl_ref.etype as u32) == XML_ELEMENT_TYPE_EMPTY as u32) as c_int
1643 }
1644}
1645
1646pub unsafe fn validate_dtd(
1670 ctxt: *mut _xmlValidCtxt,
1671 doc: *mut _xmlDoc,
1672 dtd: *mut _xmlDtd,
1673) -> c_int {
1674 if ctxt.is_null() || dtd.is_null() {
1675 return 0;
1676 }
1677
1678 let c = unsafe { &mut *ctxt };
1679 c.doc = doc;
1680 c.valid = 1;
1681
1682 struct ValidateDtdCtx {
1683 ctxt: *mut _xmlValidCtxt,
1684 doc: *mut _xmlDoc,
1685 }
1686
1687 extern "C" fn validate_attr_decl_cb(
1688 payload: *mut c_void,
1689 data: *mut c_void,
1690 _name: *const xmlChar,
1691 _name2: *const xmlChar,
1692 _name3: *const xmlChar,
1693 ) {
1694 if payload.is_null() || data.is_null() {
1695 return;
1696 }
1697
1698 let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
1700 unsafe {
1701 let attr = payload as *mut _xmlAttribute;
1702 validate_attribute_decl(ctx.ctxt, ctx.doc, ptr::null_mut(), attr);
1703 }
1704 }
1705
1706 extern "C" fn validate_elem_content_cb(
1707 payload: *mut c_void,
1708 data: *mut c_void,
1709 _name: *const xmlChar,
1710 _name2: *const xmlChar,
1711 _name3: *const xmlChar,
1712 ) {
1713 if payload.is_null() || data.is_null() {
1714 return;
1715 }
1716
1717 let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
1719 unsafe {
1720 let elem = &*(payload as *mut _xmlElement);
1721 if !elem.content.is_null() {
1722 validate_content_model_refs(ctx.ctxt, ctx.doc, elem.content);
1723 }
1724 }
1725 }
1726
1727 unsafe {
1728 let dtd_ref = &*dtd;
1729
1730 if !dtd_ref.attributes.is_null() {
1732 let ctx = ValidateDtdCtx { ctxt, doc };
1733 hash::hash_scan_full(
1734 dtd_ref.attributes as *mut hash::HashTable,
1735 Some(validate_attr_decl_cb),
1736 &ctx as *const ValidateDtdCtx as *mut c_void,
1737 );
1738 }
1739
1740 if !dtd_ref.elements.is_null() {
1742 let ctx = ValidateDtdCtx { ctxt, doc };
1743 hash::hash_scan_full(
1744 dtd_ref.elements as *mut hash::HashTable,
1745 Some(validate_elem_content_cb),
1746 &ctx as *const ValidateDtdCtx as *mut c_void,
1747 );
1748 }
1749
1750 c.valid
1751 }
1752}
1753
1754unsafe fn validate_content_model_refs(
1761 ctxt: *mut _xmlValidCtxt,
1762 doc: *mut _xmlDoc,
1763 content: *mut _xmlElementContent,
1764) {
1765 if content.is_null() {
1766 return;
1767 }
1768
1769 unsafe {
1770 let c = &*content;
1771
1772 match c.type_ as u32 {
1773 t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
1774 if !c.name.is_null() {
1776 let dtd = get_valid_dtd(doc);
1777 if !dtd.is_null() {
1778 let dtd_ref = &*dtd;
1779 if !dtd_ref.elements.is_null() {
1780 let decl =
1781 hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, c.name);
1782 if decl.is_null() {
1783 let name_str = string::xmlstr_to_string(c.name);
1784 let err_msg = format!(
1785 "Element '{}' referenced in content model is not declared\0",
1786 name_str
1787 );
1788 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1789 }
1790 }
1791 }
1792 }
1793 }
1794 t if t == XML_ELEMENT_CONTENT_SEQ as u32 || t == XML_ELEMENT_CONTENT_OR as u32 => {
1795 validate_content_model_refs(ctxt, doc, c.c1);
1796 validate_content_model_refs(ctxt, doc, c.c2);
1797 }
1798 _ => {}
1799 }
1800 }
1801}
1802
1803pub unsafe fn validate_dtd_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1824 unsafe { validate_document_final(ctxt, doc) }
1825}
1826
1827const XML_SCAN_NC: u32 = 1; const XML_SCAN_NMTOKEN: u32 = 2; fn is_blank_byte(b: u8) -> bool {
1855 b == b' ' || b == b'\t' || b == b'\n' || b == b'\r'
1856}
1857
1858fn utf8_char_len(lead: u8) -> usize {
1860 if lead < 0x80 {
1861 1
1862 } else if lead >= 0xC0 && lead <= 0xDF {
1863 2
1864 } else if lead >= 0xE0 && lead <= 0xEF {
1865 3
1866 } else if lead >= 0xF0 && lead <= 0xF7 {
1867 4
1868 } else {
1869 0
1870 }
1871}
1872
1873unsafe fn scan_name_offsets(bytes: &[u8], start: usize, flags: u32) -> usize {
1886 let stop = if flags & XML_SCAN_NC != 0 {
1887 Some(b':')
1888 } else {
1889 None
1890 };
1891 let mut i = start;
1892 let mut is_nmtoken = flags & XML_SCAN_NMTOKEN != 0;
1893 while i < bytes.len() {
1894 let b = bytes[i];
1895 if b < 0x80 {
1896 if stop == Some(b) {
1897 break;
1898 }
1899 let c = b as char;
1900 let ok = if is_nmtoken {
1901 is_xml_name_char(c)
1902 } else {
1903 is_xml_name_start(c)
1904 };
1905 if !ok {
1906 break;
1907 }
1908 i += 1;
1909 } else {
1910 let len = utf8_char_len(b);
1911 if len == 0 || i + len > bytes.len() {
1912 break;
1913 }
1914 let ch = match core::str::from_utf8(&bytes[i..i + len])
1915 .ok()
1916 .and_then(|s| s.chars().next())
1917 {
1918 Some(c) => c,
1919 None => break,
1920 };
1921 let ok = if is_nmtoken {
1922 is_xml_name_char(ch)
1923 } else {
1924 is_xml_name_start(ch)
1925 };
1926 if !ok {
1927 break;
1928 }
1929 i += len;
1930 }
1931 is_nmtoken = true;
1933 }
1934 i
1935}
1936
1937pub unsafe fn validate_ncname(value: *const xmlChar, space: c_int) -> c_int {
1943 if value.is_null() {
1944 return -1;
1945 }
1946 let bytes = string::xmlstr_to_bytes(value);
1947 let mut start = 0usize;
1948 if space != 0 {
1949 while start < bytes.len() && is_blank_byte(bytes[start]) {
1950 start += 1;
1951 }
1952 }
1953 let end = scan_name_offsets(bytes, start, XML_SCAN_NC);
1954 if end == start {
1955 return 1;
1956 }
1957 let mut end2 = end;
1958 if space != 0 {
1959 while end2 < bytes.len() && is_blank_byte(bytes[end2]) {
1960 end2 += 1;
1961 }
1962 }
1963 if end2 == bytes.len() {
1964 0
1965 } else {
1966 1
1967 }
1968}
1969
1970pub unsafe fn validate_qname(value: *const xmlChar, space: c_int) -> c_int {
1976 if value.is_null() {
1977 return -1;
1978 }
1979 let bytes = string::xmlstr_to_bytes(value);
1980 let mut start = 0usize;
1981 if space != 0 {
1982 while start < bytes.len() && is_blank_byte(bytes[start]) {
1983 start += 1;
1984 }
1985 }
1986 let mut end = scan_name_offsets(bytes, start, XML_SCAN_NC);
1987 if end == start {
1988 return 1;
1989 }
1990 if end < bytes.len() && bytes[end] == b':' {
1991 end += 1;
1992 let end2 = scan_name_offsets(bytes, end, XML_SCAN_NC);
1993 if end2 == end {
1994 return 1;
1995 }
1996 end = end2;
1997 }
1998 if space != 0 {
1999 while end < bytes.len() && is_blank_byte(bytes[end]) {
2000 end += 1;
2001 }
2002 }
2003 if end == bytes.len() {
2004 0
2005 } else {
2006 1
2007 }
2008}
2009
2010pub unsafe fn validate_name_space(value: *const xmlChar, space: c_int) -> c_int {
2021 if value.is_null() {
2022 return -1;
2023 }
2024 let bytes = string::xmlstr_to_bytes(value);
2025 let mut start = 0usize;
2026 if space != 0 {
2027 while start < bytes.len() && is_blank_byte(bytes[start]) {
2028 start += 1;
2029 }
2030 }
2031 let end = scan_name_offsets(bytes, start, 0);
2032 if end == start {
2033 return 1;
2034 }
2035 let mut end2 = end;
2036 if space != 0 {
2037 while end2 < bytes.len() && is_blank_byte(bytes[end2]) {
2038 end2 += 1;
2039 }
2040 }
2041 if end2 == bytes.len() {
2042 0
2043 } else {
2044 1
2045 }
2046}
2047
2048pub unsafe fn validate_nmtoken_space(value: *const xmlChar, space: c_int) -> c_int {
2054 if value.is_null() {
2055 return -1;
2056 }
2057 let bytes = string::xmlstr_to_bytes(value);
2058 let mut start = 0usize;
2059 if space != 0 {
2060 while start < bytes.len() && is_blank_byte(bytes[start]) {
2061 start += 1;
2062 }
2063 }
2064 let end = scan_name_offsets(bytes, start, XML_SCAN_NMTOKEN);
2065 if end == start {
2066 return 1;
2067 }
2068 let mut end2 = end;
2069 if space != 0 {
2070 while end2 < bytes.len() && is_blank_byte(bytes[end2]) {
2071 end2 += 1;
2072 }
2073 }
2074 if end2 == bytes.len() {
2075 0
2076 } else {
2077 1
2078 }
2079}
2080
2081unsafe fn validate_value_internal(value: *const xmlChar, flags: u32) -> c_int {
2084 if value.is_null() {
2085 return 0;
2086 }
2087 let bytes = string::xmlstr_to_bytes(value);
2088 if bytes.is_empty() {
2089 return 0;
2090 }
2091 let end = scan_name_offsets(bytes, 0, flags);
2092 if end == 0 {
2093 return 0;
2094 }
2095 if end == bytes.len() {
2096 1
2097 } else {
2098 0
2099 }
2100}
2101
2102unsafe fn validate_values_internal(value: *const xmlChar, flags: u32) -> c_int {
2105 if value.is_null() {
2106 return 0;
2107 }
2108 let bytes = string::xmlstr_to_bytes(value);
2109 let mut cur = scan_name_offsets(bytes, 0, flags);
2110 if cur == 0 {
2111 return 0;
2112 }
2113 while cur < bytes.len() && bytes[cur] == b' ' {
2114 while cur < bytes.len() && bytes[cur] == b' ' {
2115 cur += 1;
2116 }
2117 let end = scan_name_offsets(bytes, cur, flags);
2118 if end == cur {
2119 return 0;
2120 }
2121 cur = end;
2122 }
2123 if cur == bytes.len() {
2124 1
2125 } else {
2126 0
2127 }
2128}
2129
2130pub unsafe fn validate_name_value(value: *const xmlChar) -> c_int {
2136 validate_value_internal(value, 0)
2137}
2138
2139pub unsafe fn validate_names_value(value: *const xmlChar) -> c_int {
2145 validate_values_internal(value, 0)
2146}
2147
2148pub unsafe fn validate_nmtoken_value(value: *const xmlChar) -> c_int {
2154 validate_value_internal(value, XML_SCAN_NMTOKEN)
2155}
2156
2157pub unsafe fn validate_nmtokens_value(value: *const xmlChar) -> c_int {
2163 validate_values_internal(value, XML_SCAN_NMTOKEN)
2164}
2165
2166pub unsafe fn get_dtd_qelement_desc(
2177 dtd: *mut _xmlDtd,
2178 name: *const xmlChar,
2179 prefix: *const xmlChar,
2180) -> *mut _xmlElement {
2181 if dtd.is_null() {
2182 return ptr::null_mut();
2183 }
2184 unsafe {
2185 let elements = (*dtd).elements;
2186 if elements.is_null() {
2187 return ptr::null_mut();
2188 }
2189 hash::hash_lookup2(elements as *mut hash::HashTable, name, prefix) as *mut _xmlElement
2190 }
2191}
2192
2193pub unsafe fn get_dtd_qattr_desc(
2201 dtd: *mut _xmlDtd,
2202 elem: *const xmlChar,
2203 name: *const xmlChar,
2204 prefix: *const xmlChar,
2205) -> *mut _xmlAttribute {
2206 if dtd.is_null() || elem.is_null() || name.is_null() {
2207 return ptr::null_mut();
2208 }
2209 unsafe {
2210 let attrs = (*dtd).attributes;
2211 if attrs.is_null() {
2212 return ptr::null_mut();
2213 }
2214 hash::hash_lookup3(attrs as *mut hash::HashTable, name, prefix, elem) as *mut _xmlAttribute
2215 }
2216}
2217
2218pub unsafe fn get_dtd_notation_desc(dtd: *mut _xmlDtd, name: *const xmlChar) -> *mut _xmlNotation {
2224 if dtd.is_null() || name.is_null() {
2225 return ptr::null_mut();
2226 }
2227 unsafe {
2228 let notations = (*dtd).notations;
2229 if notations.is_null() {
2230 return ptr::null_mut();
2231 }
2232 hash::hash_lookup(notations as *mut hash::HashTable, name) as *mut _xmlNotation
2233 }
2234}
2235
2236unsafe fn split_qname4(name: *const xmlChar, prefix: *mut *mut xmlChar) -> *const xmlChar {
2245 if prefix.is_null() {
2246 return name;
2247 }
2248 unsafe {
2249 *prefix = ptr::null_mut();
2250 if name.is_null() {
2251 return ptr::null();
2252 }
2253 let bytes = string::xmlstr_to_bytes(name);
2254 match bytes.iter().position(|&b| b == b':') {
2255 None => name,
2256 Some(pos) => {
2257 let p = string::bytes_to_xmlstr(&bytes[..pos]);
2258 *prefix = p;
2259 name.add(pos + 1)
2260 }
2261 }
2262 }
2263}
2264
2265unsafe fn free_id(id: *mut _xmlID) {
2272 if id.is_null() {
2273 return;
2274 }
2275 unsafe {
2276 if !(*id).value.is_null() {
2277 allocator::xmlFree((*id).value as *mut c_void);
2278 }
2279 if !(*id).name.is_null() {
2280 allocator::xmlFree((*id).name as *mut c_void);
2281 }
2282 if !(*id).attr.is_null() {
2283 (*(*id).attr).id = ptr::null_mut();
2284 (*(*id).attr).atype = 0;
2285 }
2286 allocator::xmlFree(id as *mut c_void);
2287 }
2288}
2289
2290unsafe extern "C" fn free_id_entry(payload: *mut c_void, _name: *mut xmlChar) {
2293 free_id(payload as *mut _xmlID);
2294}
2295
2296unsafe fn add_id_internal(
2299 attr: *mut _xmlAttr,
2300 value: *const xmlChar,
2301 id_ptr: *mut *mut _xmlID,
2302) -> c_int {
2303 unsafe {
2304 if !id_ptr.is_null() {
2305 *id_ptr = ptr::null_mut();
2306 }
2307 if value.is_null() || *value == 0 {
2308 return 0;
2309 }
2310 if attr.is_null() {
2311 return 0;
2312 }
2313 let doc = (*attr).doc;
2314 if doc.is_null() {
2315 return 0;
2316 }
2317
2318 let mut table = (*doc).ids as *mut hash::HashTable;
2319 if table.is_null() {
2320 (*doc).ids = hash::hash_create(0) as *mut c_void;
2321 table = (*doc).ids as *mut hash::HashTable;
2322 if table.is_null() {
2323 return -1;
2324 }
2325 } else if !hash::hash_lookup(table, value).is_null() {
2326 return 0;
2327 }
2328
2329 let id = allocator::xmlMallocZero(size_of::<_xmlID>() as usize) as *mut _xmlID;
2330 if id.is_null() {
2331 return -1;
2332 }
2333 (*id).doc = doc;
2334 (*id).value = string::xml_strdup(value);
2335 if (*id).value.is_null() {
2336 free_id(id);
2337 return -1;
2338 }
2339 if !(*attr).id.is_null() {
2341 remove_id(doc, attr);
2342 }
2343 if hash::hash_add_entry(table, value, id as *mut c_void) != 0 {
2344 free_id(id);
2345 return -1;
2346 }
2347 if !id_ptr.is_null() {
2348 *id_ptr = id;
2349 }
2350 (*id).attr = attr;
2351 (*id).lineno = tree::get_line_no((*attr).parent);
2352 (*attr).atype = XML_ATTRIBUTE_ID as c_int;
2353 (*attr).id = id as *mut c_void;
2354 1
2355 }
2356}
2357
2358pub unsafe fn add_id(
2366 ctxt: *mut _xmlValidCtxt,
2367 doc: *mut _xmlDoc,
2368 value: *const xmlChar,
2369 attr: *mut _xmlAttr,
2370) -> *mut _xmlID {
2371 unsafe {
2372 if attr.is_null() || doc != (*attr).doc {
2373 return ptr::null_mut();
2374 }
2375 let mut id = ptr::null_mut();
2376 let res = add_id_internal(attr, value, &mut id);
2377 if res < 0 {
2378 vctxt_error(
2379 ctxt,
2380 b"Memory allocation failed : xmlAddID\0" as *const u8 as *const c_char,
2381 );
2382 } else if res == 0 && !ctxt.is_null() {
2383 let msg = format!("ID {} already defined\0", string::xmlstr_to_string(value));
2384 vctxt_error(ctxt, msg.as_ptr() as *const c_char);
2385 }
2386 id
2387 }
2388}
2389
2390pub unsafe fn remove_id(doc: *mut _xmlDoc, attr: *mut _xmlAttr) -> c_int {
2397 unsafe {
2398 if doc.is_null() {
2399 return -1;
2400 }
2401 if attr.is_null() || (*attr).id.is_null() {
2402 return -1;
2403 }
2404 let table = (*doc).ids as *mut hash::HashTable;
2405 if table.is_null() {
2406 return -1;
2407 }
2408 let value = (*((*attr).id as *mut _xmlID)).value;
2409 if hash::hash_remove_entry(table, value, Some(free_id_entry)) < 0 {
2410 return -1;
2411 }
2412 0
2413 }
2414}
2415
2416unsafe fn free_ref(r: *mut _xmlRef) {
2418 if r.is_null() {
2419 return;
2420 }
2421 unsafe {
2422 if !(*r).value.is_null() {
2423 allocator::xmlFree((*r).value as *mut c_void);
2424 }
2425 if !(*r).name.is_null() {
2426 allocator::xmlFree((*r).name as *mut c_void);
2427 }
2428 allocator::xmlFree(r as *mut c_void);
2429 }
2430}
2431
2432unsafe extern "C" fn free_ref_list_entry(data: *mut c_void) {
2434 free_ref(data as *mut _xmlRef);
2435}
2436
2437unsafe extern "C" fn dummy_compare(_a: *const c_void, _b: *const c_void) -> c_int {
2439 1
2440}
2441
2442unsafe extern "C" fn free_ref_table_entry(payload: *mut c_void, _name: *mut xmlChar) {
2444 crate::xml::list::list_delete(payload as *mut crate::xml::list::List);
2445}
2446
2447pub unsafe fn add_ref(
2454 ctxt: *mut _xmlValidCtxt,
2455 doc: *mut _xmlDoc,
2456 value: *const xmlChar,
2457 attr: *mut _xmlAttr,
2458) -> *mut _xmlRef {
2459 unsafe {
2460 if doc.is_null() || value.is_null() || attr.is_null() {
2461 return ptr::null_mut();
2462 }
2463
2464 let mut table = (*doc).refs as *mut hash::HashTable;
2465 if table.is_null() {
2466 (*doc).refs = hash::hash_create(0) as *mut c_void;
2467 table = (*doc).refs as *mut hash::HashTable;
2468 if table.is_null() {
2469 vctxt_error(
2470 ctxt,
2471 b"Memory allocation failed : xmlAddRef\0" as *const u8 as *const c_char,
2472 );
2473 return ptr::null_mut();
2474 }
2475 }
2476
2477 let ret = allocator::xmlMallocZero(size_of::<_xmlRef>() as usize) as *mut _xmlRef;
2478 if ret.is_null() {
2479 vctxt_error(
2480 ctxt,
2481 b"Memory allocation failed : xmlAddRef\0" as *const u8 as *const c_char,
2482 );
2483 return ptr::null_mut();
2484 }
2485 (*ret).value = string::xml_strdup(value);
2486 if (*ret).value.is_null() {
2487 free_ref(ret);
2488 vctxt_error(
2489 ctxt,
2490 b"Memory allocation failed : xmlAddRef\0" as *const u8 as *const c_char,
2491 );
2492 return ptr::null_mut();
2493 }
2494 let streaming = !ctxt.is_null()
2498 && !(*ctxt).userData.is_null()
2499 && (*((*ctxt).userData as *mut _xmlParserCtxt)).parseMode
2500 == crate::abi::types::xmlParserMode::XML_PARSE_READER as c_int;
2501 if streaming {
2502 (*ret).name = string::xml_strdup((*attr).name);
2503 (*ret).attr = ptr::null_mut();
2504 } else {
2505 (*ret).name = ptr::null();
2506 (*ret).attr = attr;
2507 }
2508 (*ret).lineno = tree::get_line_no((*attr).parent);
2509
2510 let ref_list = hash::hash_lookup(table, value) as *mut crate::xml::list::List;
2512 if ref_list.is_null() {
2513 let l = crate::xml::list::list_create(Some(free_ref_list_entry), Some(dummy_compare));
2514 if l.is_null() {
2515 free_ref(ret);
2516 vctxt_error(
2517 ctxt,
2518 b"Memory allocation failed : xmlAddRef\0" as *const u8 as *const c_char,
2519 );
2520 return ptr::null_mut();
2521 }
2522 if hash::hash_add_entry(table, value, l as *mut c_void) != 0 {
2523 crate::xml::list::list_delete(l);
2524 free_ref(ret);
2525 vctxt_error(
2526 ctxt,
2527 b"Memory allocation failed : xmlAddRef\0" as *const u8 as *const c_char,
2528 );
2529 return ptr::null_mut();
2530 }
2531 crate::xml::list::list_append(l, ret as *mut c_void);
2532 } else {
2533 if crate::xml::list::list_append(ref_list, ret as *mut c_void) != 0 {
2534 free_ref(ret);
2535 vctxt_error(
2536 ctxt,
2537 b"Memory allocation failed : xmlAddRef\0" as *const u8 as *const c_char,
2538 );
2539 return ptr::null_mut();
2540 }
2541 }
2542 ret
2543 }
2544}
2545
2546pub unsafe fn remove_ref(doc: *mut _xmlDoc, attr: *mut _xmlAttr) -> c_int {
2553 if doc.is_null() || attr.is_null() {
2557 return -1;
2558 }
2559 unsafe {
2560 let table = (*doc).refs as *mut hash::HashTable;
2561 if table.is_null() {
2562 return -1;
2563 }
2564 let mut removed = -1;
2565 struct ScanCtx {
2567 table: *mut hash::HashTable,
2568 attr: *mut _xmlAttr,
2569 removed: c_int,
2570 }
2571 extern "C" fn scan_remove(payload: *mut c_void, data: *mut c_void, name: *const xmlChar) {
2572 let ctx = unsafe { &mut *(data as *mut ScanCtx) };
2573 let l = payload as *mut crate::xml::list::List;
2574 let mut cur: *mut c_void = crate::xml::list::list_front(l);
2576 while !cur.is_null() {
2577 let next: *mut c_void = unsafe { (*(cur as *mut _xmlRef)).next as *mut c_void };
2578 let r = cur as *mut _xmlRef;
2579 if unsafe { (*r).attr } == ctx.attr {
2580 unsafe {
2581 crate::xml::list::list_remove_first(l, cur);
2582 }
2583 unsafe { ctx.removed = 0 };
2584 }
2585 cur = next;
2586 }
2587 if crate::xml::list::list_empty(l) != 0 {
2588 unsafe {
2589 hash::hash_remove_entry(ctx.table, name, Some(free_ref_table_entry));
2590 }
2591 }
2592 let _ = name;
2593 }
2594 let mut ctx = ScanCtx {
2595 table,
2596 attr,
2597 removed: -1,
2598 };
2599 hash::hash_scan(
2600 table,
2601 Some(scan_remove),
2602 &mut ctx as *mut ScanCtx as *mut c_void,
2603 );
2604 removed = ctx.removed;
2605 removed
2606 }
2607}
2608
2609pub unsafe fn add_id_safe(attr: *mut _xmlAttr, value: *const xmlChar) -> c_int {
2617 add_id_internal(attr, value, ptr::null_mut())
2618}
2619
2620pub unsafe fn free_id_table(table: *mut hash::HashTable) {
2626 hash::hash_free(table, Some(free_id_entry));
2627}
2628
2629pub unsafe fn free_ref_table(table: *mut hash::HashTable) {
2635 hash::hash_free(table, Some(free_ref_table_entry));
2636}
2637
2638pub unsafe fn get_id(doc: *mut _xmlDoc, id: *const xmlChar) -> *mut _xmlAttr {
2646 unsafe {
2647 if doc.is_null() || id.is_null() {
2648 return ptr::null_mut();
2649 }
2650 let table = (*doc).ids as *mut hash::HashTable;
2651 if table.is_null() {
2652 return ptr::null_mut();
2653 }
2654 let id_entry = hash::hash_lookup(table, id) as *mut _xmlID;
2655 if id_entry.is_null() {
2656 return ptr::null_mut();
2657 }
2658 if (*id_entry).attr.is_null() {
2659 doc as *mut _xmlAttr
2661 } else {
2662 (*id_entry).attr
2663 }
2664 }
2665}
2666
2667pub unsafe fn get_refs(doc: *mut _xmlDoc, id: *const xmlChar) -> *mut crate::xml::list::List {
2673 unsafe {
2674 if doc.is_null() || id.is_null() {
2675 return ptr::null_mut();
2676 }
2677 let table = (*doc).refs as *mut hash::HashTable;
2678 if table.is_null() {
2679 return ptr::null_mut();
2680 }
2681 hash::hash_lookup(table, id) as *mut crate::xml::list::List
2682 }
2683}
2684
2685pub unsafe fn is_id(doc: *mut _xmlDoc, elem: *mut _xmlNode, attr: *mut _xmlAttr) -> c_int {
2693 unsafe {
2694 if attr.is_null() || (*attr).name.is_null() {
2695 return 0;
2696 }
2697 if !doc.is_null() && (*doc).type_ == XML_HTML_DOCUMENT_NODE as c_int {
2698 if string::xml_strcmp(b"id\0" as *const u8 as *const xmlChar, (*attr).name) == 0 {
2699 return 1;
2700 }
2701 if elem.is_null() || (*elem).type_ != XML_ELEMENT_NODE as c_int {
2702 return 0;
2703 }
2704 if string::xml_strcmp(b"name\0" as *const u8 as *const xmlChar, (*attr).name) == 0
2705 && string::xml_strcmp(b"a\0" as *const u8 as *const xmlChar, (*elem).name) == 0
2706 {
2707 return 1;
2708 }
2709 } else {
2710 if !(*attr).ns.is_null()
2712 && !(*(*attr).ns).prefix.is_null()
2713 && string::xml_strcmp(
2714 (*(*attr).ns).prefix,
2715 b"xml\0" as *const u8 as *const xmlChar,
2716 ) == 0
2717 && string::xml_strcmp((*attr).name, b"id\0" as *const u8 as *const xmlChar) == 0
2718 {
2719 return 1;
2720 }
2721 if doc.is_null() || ((*doc).intSubset.is_null() && (*doc).extSubset.is_null()) {
2722 return 0;
2723 }
2724 if elem.is_null()
2725 || (*elem).type_ != XML_ELEMENT_NODE as c_int
2726 || (*elem).name.is_null()
2727 {
2728 return 0;
2729 }
2730 let mut fullname = (*elem).name;
2731 let mut owned = false;
2732 if !(*elem).ns.is_null() && !(*(*elem).ns).prefix.is_null() {
2733 let f = string::build_qname((*elem).name, (*(*elem).ns).prefix, ptr::null_mut(), 0);
2734 if f.is_null() {
2735 return -1;
2736 }
2737 fullname = f;
2738 owned = true;
2739 }
2740 let aprefix = if !(*attr).ns.is_null() {
2741 (*(*attr).ns).prefix
2742 } else {
2743 ptr::null()
2744 };
2745 let mut attr_decl =
2746 get_dtd_qattr_desc((*doc).intSubset, fullname, (*attr).name, aprefix);
2747 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
2748 attr_decl = get_dtd_qattr_desc((*doc).extSubset, fullname, (*attr).name, aprefix);
2749 }
2750 if owned {
2751 allocator::xmlFree(fullname as *mut c_void);
2752 }
2753 if !attr_decl.is_null() && (*attr_decl).atype == XML_ATTRIBUTE_ID as c_int {
2754 return 1;
2755 }
2756 }
2757 0
2758 }
2759}
2760
2761pub unsafe fn is_ref(doc: *mut _xmlDoc, elem: *mut _xmlNode, attr: *mut _xmlAttr) -> c_int {
2767 unsafe {
2768 if attr.is_null() {
2769 return 0;
2770 }
2771 let doc = if doc.is_null() { (*attr).doc } else { doc };
2772 if doc.is_null() {
2773 return 0;
2774 }
2775 if (*doc).intSubset.is_null() && (*doc).extSubset.is_null() {
2776 return 0;
2777 }
2778 if (*doc).type_ == XML_HTML_DOCUMENT_NODE as c_int {
2779 return 0;
2780 }
2781 if elem.is_null() {
2782 return 0;
2783 }
2784 let aprefix = if !(*attr).ns.is_null() {
2785 (*(*attr).ns).prefix
2786 } else {
2787 ptr::null()
2788 };
2789 let mut attr_decl =
2790 get_dtd_qattr_desc((*doc).intSubset, (*elem).name, (*attr).name, aprefix);
2791 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
2792 attr_decl = get_dtd_qattr_desc((*doc).extSubset, (*elem).name, (*attr).name, aprefix);
2793 }
2794 if !attr_decl.is_null()
2795 && ((*attr_decl).atype == XML_ATTRIBUTE_IDREF as c_int
2796 || (*attr_decl).atype == XML_ATTRIBUTE_IDREFS as c_int)
2797 {
2798 return 1;
2799 }
2800 0
2801 }
2802}
2803
2804pub unsafe fn get_dtd_element_desc(dtd: *mut _xmlDtd, name: *const xmlChar) -> *mut _xmlElement {
2811 unsafe {
2812 if dtd.is_null() || name.is_null() {
2813 return ptr::null_mut();
2814 }
2815 let elements = (*dtd).elements;
2816 if elements.is_null() {
2817 return ptr::null_mut();
2818 }
2819 let mut prefix = ptr::null_mut();
2820 let local = split_qname4(name, &mut prefix);
2821 if local.is_null() {
2822 if !prefix.is_null() {
2823 allocator::xmlFree(prefix as *mut c_void);
2824 }
2825 return ptr::null_mut();
2826 }
2827 let cur =
2828 hash::hash_lookup2(elements as *mut hash::HashTable, local, prefix) as *mut _xmlElement;
2829 if !prefix.is_null() {
2830 allocator::xmlFree(prefix as *mut c_void);
2831 }
2832 cur
2833 }
2834}
2835
2836pub unsafe fn get_dtd_attr_desc(
2844 dtd: *mut _xmlDtd,
2845 elem: *const xmlChar,
2846 name: *const xmlChar,
2847) -> *mut _xmlAttribute {
2848 unsafe {
2849 if dtd.is_null() || elem.is_null() || name.is_null() {
2850 return ptr::null_mut();
2851 }
2852 let attrs = (*dtd).attributes;
2853 if attrs.is_null() {
2854 return ptr::null_mut();
2855 }
2856 let mut prefix = ptr::null_mut();
2857 let local = split_qname4(name, &mut prefix);
2858 if local.is_null() {
2859 if !prefix.is_null() {
2860 allocator::xmlFree(prefix as *mut c_void);
2861 }
2862 return ptr::null_mut();
2863 }
2864 let cur = hash::hash_lookup3(attrs as *mut hash::HashTable, local, prefix, elem)
2865 as *mut _xmlAttribute;
2866 if !prefix.is_null() {
2867 allocator::xmlFree(prefix as *mut c_void);
2868 }
2869 cur
2870 }
2871}
2872
2873unsafe fn vctxt_error_node(ctxt: *mut _xmlValidCtxt, _node: *mut _xmlNode, msg: *const c_char) {
2883 vctxt_error(ctxt, msg);
2884}
2885
2886pub unsafe fn validate_element_decl(
2894 ctxt: *mut _xmlValidCtxt,
2895 doc: *mut _xmlDoc,
2896 elem: *mut _xmlElement,
2897) -> c_int {
2898 unsafe {
2899 if doc.is_null() || (*doc).intSubset.is_null() && (*doc).extSubset.is_null() {
2900 return 1;
2901 }
2902 if elem.is_null() {
2903 return 1;
2904 }
2905 let mut ret = 1;
2906
2907 if (*elem).etype == XML_ELEMENT_TYPE_MIXED as c_int {
2910 let mut cur = (*elem).content;
2911 while !cur.is_null() {
2912 if (*cur).type_ != XML_ELEMENT_CONTENT_OR as c_int {
2913 break;
2914 }
2915 if (*cur).c1.is_null() {
2916 break;
2917 }
2918 if (*(*cur).c1).type_ == XML_ELEMENT_CONTENT_ELEMENT as c_int {
2919 let name = (*(*cur).c1).name;
2920 let mut next = (*cur).c2;
2921 while !next.is_null() {
2922 if (*next).type_ == XML_ELEMENT_CONTENT_ELEMENT as c_int {
2923 if string::xml_strcmp((*next).name, name) == 0
2924 && string::xml_strcmp((*next).prefix, (*(*cur).c1).prefix) == 0
2925 {
2926 if (*(*cur).c1).prefix.is_null() {
2927 let msg = format!(
2928 "Definition of {} has duplicate references of {}\0",
2929 string::xmlstr_to_string((*elem).name),
2930 string::xmlstr_to_string(name)
2931 );
2932 vctxt_error_node(
2933 ctxt,
2934 elem as *mut _xmlNode,
2935 msg.as_ptr() as *const c_char,
2936 );
2937 } else {
2938 let msg = format!(
2939 "Definition of {} has duplicate references of {}:{}\0",
2940 string::xmlstr_to_string((*elem).name),
2941 string::xmlstr_to_string((*(*cur).c1).prefix),
2942 string::xmlstr_to_string(name)
2943 );
2944 vctxt_error_node(
2945 ctxt,
2946 elem as *mut _xmlNode,
2947 msg.as_ptr() as *const c_char,
2948 );
2949 }
2950 ret = 0;
2951 }
2952 break;
2953 }
2954 if (*next).c1.is_null() {
2955 break;
2956 }
2957 if (*(*next).c1).type_ != XML_ELEMENT_CONTENT_ELEMENT as c_int {
2958 break;
2959 }
2960 if string::xml_strcmp((*(*next).c1).name, name) == 0
2961 && string::xml_strcmp((*(*next).c1).prefix, (*(*cur).c1).prefix) == 0
2962 {
2963 if (*(*cur).c1).prefix.is_null() {
2964 let msg = format!(
2965 "Definition of {} has duplicate references to {}\0",
2966 string::xmlstr_to_string((*elem).name),
2967 string::xmlstr_to_string(name)
2968 );
2969 vctxt_error_node(
2970 ctxt,
2971 elem as *mut _xmlNode,
2972 msg.as_ptr() as *const c_char,
2973 );
2974 } else {
2975 let msg = format!(
2976 "Definition of {} has duplicate references to {}:{}\0",
2977 string::xmlstr_to_string((*elem).name),
2978 string::xmlstr_to_string((*(*cur).c1).prefix),
2979 string::xmlstr_to_string(name)
2980 );
2981 vctxt_error_node(
2982 ctxt,
2983 elem as *mut _xmlNode,
2984 msg.as_ptr() as *const c_char,
2985 );
2986 }
2987 ret = 0;
2988 }
2989 next = (*next).c2;
2990 }
2991 }
2992 cur = (*cur).c2;
2993 }
2994 }
2995
2996 let mut prefix = ptr::null_mut();
2999 let local_name = split_qname4((*elem).name, &mut prefix);
3000 if local_name.is_null() {
3001 vctxt_error(
3002 ctxt,
3003 b"Memory allocation failed : xmlValidateElementDecl\0" as *const u8
3004 as *const c_char,
3005 );
3006 if !prefix.is_null() {
3007 allocator::xmlFree(prefix as *mut c_void);
3008 }
3009 return 0;
3010 }
3011
3012 for subset in [(*doc).intSubset, (*doc).extSubset] {
3013 if subset.is_null() {
3014 continue;
3015 }
3016 let tst = get_dtd_qelement_desc(subset, local_name, prefix);
3017 if !tst.is_null()
3018 && tst != elem
3019 && ((*tst).prefix == (*elem).prefix
3020 || string::xml_strcmp((*tst).prefix, (*elem).prefix) == 0)
3021 && (*tst).etype != XML_ELEMENT_TYPE_UNDEFINED as c_int
3022 {
3023 let msg = format!(
3024 "Redefinition of element {}\0",
3025 string::xmlstr_to_string((*elem).name)
3026 );
3027 vctxt_error_node(ctxt, elem as *mut _xmlNode, msg.as_ptr() as *const c_char);
3028 ret = 0;
3029 }
3030 }
3031 if !prefix.is_null() {
3032 allocator::xmlFree(prefix as *mut c_void);
3033 }
3034 ret
3035 }
3036}
3037
3038pub unsafe fn validate_notation_decl(
3042 _ctxt: *mut _xmlValidCtxt,
3043 _doc: *mut _xmlDoc,
3044 _nota: *mut _xmlNotation,
3045) -> c_int {
3046 1
3047}
3048
3049pub unsafe fn validate_one_attribute(
3059 ctxt: *mut _xmlValidCtxt,
3060 doc: *mut _xmlDoc,
3061 elem: *mut _xmlNode,
3062 attr: *mut _xmlAttr,
3063 value: *const xmlChar,
3064) -> c_int {
3065 unsafe {
3066 if doc.is_null() {
3067 return 0;
3068 }
3069 if elem.is_null() || (*elem).name.is_null() {
3070 return 0;
3071 }
3072 if attr.is_null() || (*attr).name.is_null() {
3073 return 0;
3074 }
3075 let mut ret = 1;
3076
3077 let aprefix = if !(*attr).ns.is_null() {
3078 (*(*attr).ns).prefix
3079 } else {
3080 ptr::null()
3081 };
3082
3083 let mut attr_decl = ptr::null_mut();
3084 if !(*elem).ns.is_null() && !(*(*elem).ns).prefix.is_null() {
3085 let fullname =
3086 string::build_qname((*elem).name, (*(*elem).ns).prefix, ptr::null_mut(), 0);
3087 if fullname.is_null() {
3088 vctxt_error(
3089 ctxt,
3090 b"Memory allocation failed : xmlValidateOneAttribute\0" as *const u8
3091 as *const c_char,
3092 );
3093 return 0;
3094 }
3095 attr_decl = get_dtd_qattr_desc((*doc).intSubset, fullname, (*attr).name, aprefix);
3096 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
3097 attr_decl = get_dtd_qattr_desc((*doc).extSubset, fullname, (*attr).name, aprefix);
3098 }
3099 if fullname != (*elem).name as *mut xmlChar {
3100 allocator::xmlFree(fullname as *mut c_void);
3101 }
3102 }
3103 if attr_decl.is_null() {
3104 attr_decl = get_dtd_qattr_desc((*doc).intSubset, (*elem).name, (*attr).name, aprefix);
3105 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
3106 attr_decl =
3107 get_dtd_qattr_desc((*doc).extSubset, (*elem).name, (*attr).name, aprefix);
3108 }
3109 }
3110
3111 if attr_decl.is_null() {
3113 let msg = format!(
3114 "No declaration for attribute {} of element {}\0",
3115 string::xmlstr_to_string((*attr).name),
3116 string::xmlstr_to_string((*elem).name)
3117 );
3118 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3119 return 0;
3120 }
3121 if !(*attr).id.is_null() {
3122 remove_id(doc, attr);
3123 }
3124 (*attr).atype = (*attr_decl).atype;
3125
3126 let val = if (*doc).properties & crate::abi::types::xmlDocProperties::XML_DOC_OLD10 as c_int
3128 != 0
3129 {
3130 match (*attr_decl).atype as u32 {
3133 t if t == XML_ATTRIBUTE_ENTITIES as u32 || t == XML_ATTRIBUTE_IDREFS as u32 => {
3134 validate_values_internal(value, 0)
3135 }
3136 t if t == XML_ATTRIBUTE_ENTITY as u32
3137 || t == XML_ATTRIBUTE_IDREF as u32
3138 || t == XML_ATTRIBUTE_ID as u32
3139 || t == XML_ATTRIBUTE_NOTATION as u32 =>
3140 {
3141 validate_value_internal(value, 0)
3142 }
3143 t if t == XML_ATTRIBUTE_NMTOKENS as u32
3144 || t == XML_ATTRIBUTE_ENUMERATION as u32 =>
3145 {
3146 validate_values_internal(value, XML_SCAN_NMTOKEN)
3147 }
3148 t if t == XML_ATTRIBUTE_NMTOKEN as u32 => {
3149 validate_value_internal(value, XML_SCAN_NMTOKEN)
3150 }
3151 _ => 1,
3152 }
3153 } else {
3154 validate_attribute_value((*attr_decl).atype, value)
3155 };
3156 if val == 0 {
3157 let msg = format!(
3158 "Syntax of value for attribute {} of {} is not valid\0",
3159 string::xmlstr_to_string((*attr).name),
3160 string::xmlstr_to_string((*elem).name)
3161 );
3162 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3163 ret = 0;
3164 }
3165
3166 if (*attr_decl).def == XML_ATTRIBUTE_FIXED as c_int {
3168 if string::xml_strcmp(value, (*attr_decl).defaultValue) != 0 {
3169 let msg = format!(
3170 "Value for attribute {} of {} is different from default \"{}\n\0",
3171 string::xmlstr_to_string((*attr).name),
3172 string::xmlstr_to_string((*elem).name),
3173 string::xmlstr_to_string((*attr_decl).defaultValue)
3174 );
3175 let msg = format!(
3177 "Value for attribute {} of {} is different from default \"{}\"\0",
3178 string::xmlstr_to_string((*attr).name),
3179 string::xmlstr_to_string((*elem).name),
3180 string::xmlstr_to_string((*attr_decl).defaultValue)
3181 );
3182 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3183 ret = 0;
3184 }
3185 }
3186
3187 const XML_VCTXT_IN_ENTITY: c_uint = 4; if (*attr_decl).atype == XML_ATTRIBUTE_ID as c_int
3190 && (ctxt.is_null() || (*ctxt).flags & XML_VCTXT_IN_ENTITY == 0)
3191 {
3192 if add_id(ctxt, doc, value, attr).is_null() {
3193 ret = 0;
3194 }
3195 }
3196 if (*attr_decl).atype == XML_ATTRIBUTE_IDREF as c_int
3197 || (*attr_decl).atype == XML_ATTRIBUTE_IDREFS as c_int
3198 {
3199 if add_ref(ctxt, doc, value, attr).is_null() {
3200 ret = 0;
3201 }
3202 }
3203
3204 if (*attr_decl).atype == XML_ATTRIBUTE_NOTATION as c_int {
3206 let mut nota = get_dtd_notation_desc((*doc).intSubset, value);
3207 if nota.is_null() {
3208 nota = get_dtd_notation_desc((*doc).extSubset, value);
3209 }
3210 if nota.is_null() {
3211 let msg = format!(
3212 "Value \"{}\" for attribute {} of {} is not a declared Notation\0",
3213 string::xmlstr_to_string(value),
3214 string::xmlstr_to_string((*attr).name),
3215 string::xmlstr_to_string((*elem).name)
3216 );
3217 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3218 ret = 0;
3219 }
3220 let mut tree = (*attr_decl).tree;
3221 while !tree.is_null() {
3222 if string::xml_strcmp((*tree).name, value) == 0 {
3223 break;
3224 }
3225 tree = (*tree).next;
3226 }
3227 if tree.is_null() {
3228 let msg = format!(
3229 "Value \"{}\" for attribute {} of {} is not among the enumerated notations\0",
3230 string::xmlstr_to_string(value),
3231 string::xmlstr_to_string((*attr).name),
3232 string::xmlstr_to_string((*elem).name)
3233 );
3234 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3235 ret = 0;
3236 }
3237 }
3238
3239 if (*attr_decl).atype == XML_ATTRIBUTE_ENUMERATION as c_int {
3241 let mut tree = (*attr_decl).tree;
3242 while !tree.is_null() {
3243 if string::xml_strcmp((*tree).name, value) == 0 {
3244 break;
3245 }
3246 tree = (*tree).next;
3247 }
3248 if tree.is_null() {
3249 let msg = format!(
3250 "Value \"{}\" for attribute {} of {} is not among the enumerated set\0",
3251 string::xmlstr_to_string(value),
3252 string::xmlstr_to_string((*attr).name),
3253 string::xmlstr_to_string((*elem).name)
3254 );
3255 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3256 ret = 0;
3257 }
3258 }
3259
3260 if (*attr_decl).def == XML_ATTRIBUTE_FIXED as c_int
3262 && string::xml_strcmp((*attr_decl).defaultValue, value) != 0
3263 {
3264 let msg = format!(
3265 "Value for attribute {} of {} must be \"{}\"\0",
3266 string::xmlstr_to_string((*attr).name),
3267 string::xmlstr_to_string((*elem).name),
3268 string::xmlstr_to_string((*attr_decl).defaultValue)
3269 );
3270 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3271 ret = 0;
3272 }
3273
3274 if (*attr_decl).atype == XML_ATTRIBUTE_ENTITY as c_int {
3276 let ent = tree::get_doc_entity(doc, value);
3277 if ent.is_null() {
3278 let msg = format!(
3279 "ENTITY attribute {} reference an unknown entity \"{}\"\0",
3280 string::xmlstr_to_string((*attr).name),
3281 string::xmlstr_to_string(value)
3282 );
3283 vctxt_error_node(ctxt, doc as *mut _xmlNode, msg.as_ptr() as *const c_char);
3284 ret = 0;
3285 } else if (*ent).etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY as c_int {
3286 let msg = format!(
3287 "ENTITY attribute {} reference an entity \"{}\" of wrong type\0",
3288 string::xmlstr_to_string((*attr).name),
3289 string::xmlstr_to_string(value)
3290 );
3291 vctxt_error_node(ctxt, doc as *mut _xmlNode, msg.as_ptr() as *const c_char);
3292 ret = 0;
3293 }
3294 }
3295 ret
3296 }
3297}
3298
3299pub unsafe fn validate_one_namespace(
3306 ctxt: *mut _xmlValidCtxt,
3307 doc: *mut _xmlDoc,
3308 elem: *mut _xmlNode,
3309 prefix: *const xmlChar,
3310 ns: *mut _xmlNs,
3311 value: *const xmlChar,
3312) -> c_int {
3313 unsafe {
3314 if doc.is_null() {
3315 return 0;
3316 }
3317 if elem.is_null() || (*elem).name.is_null() {
3318 return 0;
3319 }
3320 if ns.is_null() || (*ns).href.is_null() {
3321 return 0;
3322 }
3323 let mut ret = 1;
3324
3325 let mut attr_decl = ptr::null_mut();
3326 if !prefix.is_null() {
3327 let fullname = string::build_qname((*elem).name, prefix, ptr::null_mut(), 0);
3328 if fullname.is_null() {
3329 vctxt_error(
3330 ctxt,
3331 b"Memory allocation failed : xmlValidateOneNamespace\0" as *const u8
3332 as *const c_char,
3333 );
3334 return 0;
3335 }
3336 if !(*ns).prefix.is_null() {
3337 attr_decl = get_dtd_qattr_desc(
3338 (*doc).intSubset,
3339 fullname,
3340 (*ns).prefix,
3341 b"xmlns\0" as *const u8 as *const xmlChar,
3342 );
3343 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
3344 attr_decl = get_dtd_qattr_desc(
3345 (*doc).extSubset,
3346 fullname,
3347 (*ns).prefix,
3348 b"xmlns\0" as *const u8 as *const xmlChar,
3349 );
3350 }
3351 } else {
3352 attr_decl = get_dtd_qattr_desc(
3353 (*doc).intSubset,
3354 fullname,
3355 b"xmlns\0" as *const u8 as *const xmlChar,
3356 ptr::null(),
3357 );
3358 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
3359 attr_decl = get_dtd_qattr_desc(
3360 (*doc).extSubset,
3361 fullname,
3362 b"xmlns\0" as *const u8 as *const xmlChar,
3363 ptr::null(),
3364 );
3365 }
3366 }
3367 if fullname != (*elem).name as *mut xmlChar {
3368 allocator::xmlFree(fullname as *mut c_void);
3369 }
3370 }
3371 if attr_decl.is_null() {
3372 if !(*ns).prefix.is_null() {
3373 attr_decl = get_dtd_qattr_desc(
3374 (*doc).intSubset,
3375 (*elem).name,
3376 (*ns).prefix,
3377 b"xmlns\0" as *const u8 as *const xmlChar,
3378 );
3379 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
3380 attr_decl = get_dtd_qattr_desc(
3381 (*doc).extSubset,
3382 (*elem).name,
3383 (*ns).prefix,
3384 b"xmlns\0" as *const u8 as *const xmlChar,
3385 );
3386 }
3387 } else {
3388 attr_decl = get_dtd_qattr_desc(
3389 (*doc).intSubset,
3390 (*elem).name,
3391 b"xmlns\0" as *const u8 as *const xmlChar,
3392 ptr::null(),
3393 );
3394 if attr_decl.is_null() && !(*doc).extSubset.is_null() {
3395 attr_decl = get_dtd_qattr_desc(
3396 (*doc).extSubset,
3397 (*elem).name,
3398 b"xmlns\0" as *const u8 as *const xmlChar,
3399 ptr::null(),
3400 );
3401 }
3402 }
3403 }
3404
3405 if attr_decl.is_null() {
3407 let msg = if !(*ns).prefix.is_null() {
3408 format!(
3409 "No declaration for attribute xmlns:{} of element {}\0",
3410 string::xmlstr_to_string((*ns).prefix),
3411 string::xmlstr_to_string((*elem).name)
3412 )
3413 } else {
3414 format!(
3415 "No declaration for attribute xmlns of element {}\0",
3416 string::xmlstr_to_string((*elem).name)
3417 )
3418 };
3419 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3420 return 0;
3421 }
3422
3423 let val = validate_attribute_value((*attr_decl).atype, value);
3424 if val == 0 {
3425 let msg = if !(*ns).prefix.is_null() {
3426 format!(
3427 "Syntax of value for attribute xmlns:{} of {} is not valid\0",
3428 string::xmlstr_to_string((*ns).prefix),
3429 string::xmlstr_to_string((*elem).name)
3430 )
3431 } else {
3432 format!(
3433 "Syntax of value for attribute xmlns of {} is not valid\0",
3434 string::xmlstr_to_string((*elem).name)
3435 )
3436 };
3437 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3438 ret = 0;
3439 }
3440
3441 if (*attr_decl).def == XML_ATTRIBUTE_FIXED as c_int
3443 && string::xml_strcmp(value, (*attr_decl).defaultValue) != 0
3444 {
3445 let msg = if !(*ns).prefix.is_null() {
3446 format!(
3447 "Value for attribute xmlns:{} of {} is different from default \"{}\"\0",
3448 string::xmlstr_to_string((*ns).prefix),
3449 string::xmlstr_to_string((*elem).name),
3450 string::xmlstr_to_string((*attr_decl).defaultValue)
3451 )
3452 } else {
3453 format!(
3454 "Value for attribute xmlns of {} is different from default \"{}\"\0",
3455 string::xmlstr_to_string((*elem).name),
3456 string::xmlstr_to_string((*attr_decl).defaultValue)
3457 )
3458 };
3459 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3460 ret = 0;
3461 }
3462 ret
3463 }
3464}
3465
3466pub unsafe fn validate_one_element(
3474 ctxt: *mut _xmlValidCtxt,
3475 doc: *mut _xmlDoc,
3476 elem: *mut _xmlNode,
3477) -> c_int {
3478 unsafe {
3479 if doc.is_null() {
3480 return 0;
3481 }
3482 if elem.is_null() {
3483 return 0;
3484 }
3485 match (*elem).type_ {
3486 t if t == XML_TEXT_NODE as c_int
3487 || t == XML_CDATA_SECTION_NODE as c_int
3488 || t == XML_ENTITY_REF_NODE as c_int
3489 || t == XML_PI_NODE as c_int
3490 || t == XML_COMMENT_NODE as c_int
3491 || t == XML_XINCLUDE_START as c_int
3492 || t == XML_XINCLUDE_END as c_int =>
3493 {
3494 return 1;
3495 }
3496 t if t == XML_ELEMENT_NODE as c_int => {}
3497 _ => {
3498 vctxt_error_node(
3499 ctxt,
3500 elem,
3501 b"unexpected element type\0" as *const u8 as *const c_char,
3502 );
3503 return 0;
3504 }
3505 }
3506
3507 let mut ret = 1;
3508 let mut extsubset = 0;
3509 let elem_decl = valid_get_elem_decl(ctxt, doc, elem, &mut extsubset);
3510 if elem_decl.is_null() {
3511 return 0;
3512 }
3513
3514 if (*ctxt).vstateNr == 0 {
3517 match (*elem_decl).etype as u32 {
3518 t if t == XML_ELEMENT_TYPE_UNDEFINED as u32 => {
3519 let msg = format!(
3520 "No declaration for element {}\0",
3521 string::xmlstr_to_string((*elem).name)
3522 );
3523 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3524 return 0;
3525 }
3526 t if t == XML_ELEMENT_TYPE_EMPTY as u32 => {
3527 if !(*elem).children.is_null() {
3528 let msg = format!(
3529 "Element {} was declared EMPTY this one has content\0",
3530 string::xmlstr_to_string((*elem).name)
3531 );
3532 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3533 ret = 0;
3534 }
3535 }
3536 t if t == XML_ELEMENT_TYPE_ANY as u32 => {}
3537 t if t == XML_ELEMENT_TYPE_MIXED as u32 => {
3538 if !(*elem_decl).content.is_null()
3539 && (*(*elem_decl).content).type_ == XML_ELEMENT_CONTENT_PCDATA as c_int
3540 {
3541 let mut child = (*elem).children;
3543 while !child.is_null() {
3544 if (*child).type_ == XML_ELEMENT_NODE as c_int {
3545 let msg = format!(
3546 "Element {} was declared #PCDATA but contains non text nodes\0",
3547 string::xmlstr_to_string((*elem).name)
3548 );
3549 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3550 ret = 0;
3551 break;
3552 }
3553 child = (*child).next;
3554 }
3555 } else {
3556 let mut child = (*elem).children;
3558 while !child.is_null() {
3559 if (*child).type_ == XML_ELEMENT_NODE as c_int {
3560 let mut fullname = (*child).name;
3561 let mut own = false;
3562 if !(*child).ns.is_null() && !(*(*child).ns).prefix.is_null() {
3563 let fnp = string::build_qname(
3564 (*child).name,
3565 (*(*child).ns).prefix,
3566 ptr::null_mut(),
3567 0,
3568 );
3569 if fnp.is_null() {
3570 vctxt_error(
3571 ctxt,
3572 b"Memory allocation failed : xmlValidateOneElement\0"
3573 as *const u8
3574 as *const c_char,
3575 );
3576 return 0;
3577 }
3578 fullname = fnp;
3579 own = true;
3580 }
3581 if validate_check_mixed(ctxt, (*elem_decl).content, fullname) != 1 {
3582 let msg = format!(
3583 "Element {} is not declared in {} list of possible children\0",
3584 string::xmlstr_to_string(fullname),
3585 string::xmlstr_to_string((*elem).name)
3586 );
3587 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3588 ret = 0;
3589 }
3590 if own {
3591 allocator::xmlFree(fullname as *mut c_void);
3592 }
3593 }
3594 child = (*child).next;
3595 }
3596 }
3597 }
3598 t if t == XML_ELEMENT_TYPE_ELEMENT as u32 => {
3599 let mut names: Vec<*const xmlChar> = Vec::new();
3602 let mut owned: Vec<*mut xmlChar> = Vec::new();
3603 let mut child = (*elem).children;
3604 while !child.is_null() {
3605 if (*child).type_ == XML_ELEMENT_NODE as c_int {
3606 let mut fullname = (*child).name;
3607 if !(*child).ns.is_null() && !(*(*child).ns).prefix.is_null() {
3608 let fnp = string::build_qname(
3609 (*child).name,
3610 (*(*child).ns).prefix,
3611 ptr::null_mut(),
3612 0,
3613 );
3614 if !fnp.is_null() {
3615 fullname = fnp;
3616 owned.push(fnp);
3617 }
3618 }
3619 names.push(fullname);
3620 }
3621 child = (*child).next;
3622 }
3623 let result = dtd::valid_content_model((*elem_decl).content, &names);
3624 for n in owned {
3625 allocator::xmlFree(n as *mut c_void);
3626 }
3627 if result != dtd::ContentModelResult::Valid {
3628 let msg = format!(
3629 "Element {} content does not follow the DTD\0",
3630 string::xmlstr_to_string((*elem).name)
3631 );
3632 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3633 ret = 0;
3634 }
3635 }
3636 _ => {}
3637 }
3638
3639 let mut attr = (*elem).properties;
3641 while !attr.is_null() {
3642 let aval = if !(*attr).children.is_null() {
3643 (*(*attr).children).content
3644 } else {
3645 ptr::null()
3646 };
3647 if validate_one_attribute(ctxt, doc, elem, attr, aval) == 0 {
3648 ret = 0;
3649 }
3650 attr = (*attr).next;
3651 }
3652 }
3653 ret
3654 }
3655}
3656
3657#[repr(C)]
3672struct ValidState {
3673 elem_decl: *mut _xmlElement,
3674 node: *mut _xmlNode,
3675 exec: *mut ContentModelExec,
3676}
3677
3678unsafe fn valid_get_elem_decl(
3681 ctxt: *mut _xmlValidCtxt,
3682 doc: *mut _xmlDoc,
3683 elem: *mut _xmlNode,
3684 extsubset: *mut c_int,
3685) -> *mut _xmlElement {
3686 unsafe {
3687 if ctxt.is_null() || doc.is_null() || elem.is_null() || (*elem).name.is_null() {
3688 return ptr::null_mut();
3689 }
3690 if !extsubset.is_null() {
3691 *extsubset = 0;
3692 }
3693 let mut elem_decl = ptr::null_mut();
3694
3695 let prefix = if !(*elem).ns.is_null() && !(*(*elem).ns).prefix.is_null() {
3696 (*(*elem).ns).prefix
3697 } else {
3698 ptr::null()
3699 };
3700 if !prefix.is_null() {
3701 elem_decl = get_dtd_qelement_desc((*doc).intSubset, (*elem).name, prefix);
3702 if elem_decl.is_null() && !(*doc).extSubset.is_null() {
3703 elem_decl = get_dtd_qelement_desc((*doc).extSubset, (*elem).name, prefix);
3704 if !elem_decl.is_null() && !extsubset.is_null() {
3705 *extsubset = 1;
3706 }
3707 }
3708 }
3709 if elem_decl.is_null() {
3710 elem_decl = get_dtd_qelement_desc((*doc).intSubset, (*elem).name, ptr::null());
3712 if elem_decl.is_null() && !(*doc).extSubset.is_null() {
3713 elem_decl = get_dtd_qelement_desc((*doc).extSubset, (*elem).name, ptr::null());
3714 if !elem_decl.is_null() && !extsubset.is_null() {
3715 *extsubset = 1;
3716 }
3717 }
3718 }
3719 if elem_decl.is_null() {
3720 let msg = format!(
3721 "No declaration for element {}\0",
3722 string::xmlstr_to_string((*elem).name)
3723 );
3724 vctxt_error_node(ctxt, elem, msg.as_ptr() as *const c_char);
3725 }
3726 elem_decl
3727 }
3728}
3729
3730unsafe fn validate_check_mixed(
3732 ctxt: *mut _xmlValidCtxt,
3733 cont: *mut _xmlElementContent,
3734 qname: *const xmlChar,
3735) -> c_int {
3736 unsafe {
3737 let mut plen: c_int = 0;
3738 let has_colon = string::split_qname3(qname, &mut plen) != 0;
3739 let local = if has_colon {
3743 qname.add(plen as usize + 1)
3744 } else {
3745 ptr::null()
3746 };
3747 let mut cur = cont;
3748 if local.is_null() {
3749 while !cur.is_null() {
3750 if (*cur).type_ == XML_ELEMENT_CONTENT_ELEMENT as c_int {
3751 if (*cur).prefix.is_null() && string::xml_strcmp((*cur).name, qname) == 0 {
3752 return 1;
3753 }
3754 } else if (*cur).type_ == XML_ELEMENT_CONTENT_OR as c_int
3755 && !(*cur).c1.is_null()
3756 && (*(*cur).c1).type_ == XML_ELEMENT_CONTENT_ELEMENT as c_int
3757 {
3758 if (*(*cur).c1).prefix.is_null()
3759 && string::xml_strcmp((*(*cur).c1).name, qname) == 0
3760 {
3761 return 1;
3762 }
3763 } else if (*cur).type_ != XML_ELEMENT_CONTENT_OR as c_int
3764 || (*cur).c1.is_null()
3765 || (*(*cur).c1).type_ != XML_ELEMENT_CONTENT_PCDATA as c_int
3766 {
3767 vctxt_error(
3768 ctxt,
3769 b"Internal: MIXED struct corrupted\0" as *const u8 as *const c_char,
3770 );
3771 break;
3772 }
3773 cur = (*cur).c2;
3774 }
3775 } else {
3776 while !cur.is_null() {
3777 if (*cur).type_ == XML_ELEMENT_CONTENT_ELEMENT as c_int {
3778 if !(*cur).prefix.is_null()
3779 && prefix_matches((*cur).prefix, qname, plen)
3780 && string::xml_strcmp((*cur).name, local) == 0
3781 {
3782 return 1;
3783 }
3784 } else if (*cur).type_ == XML_ELEMENT_CONTENT_OR as c_int
3785 && !(*cur).c1.is_null()
3786 && (*(*cur).c1).type_ == XML_ELEMENT_CONTENT_ELEMENT as c_int
3787 {
3788 if !(*(*cur).c1).prefix.is_null()
3789 && prefix_matches((*(*cur).c1).prefix, qname, plen)
3790 && string::xml_strcmp((*(*cur).c1).name, local) == 0
3791 {
3792 return 1;
3793 }
3794 } else if (*cur).type_ != XML_ELEMENT_CONTENT_OR as c_int
3795 || (*cur).c1.is_null()
3796 || (*(*cur).c1).type_ != XML_ELEMENT_CONTENT_PCDATA as c_int
3797 {
3798 vctxt_error(
3799 ctxt,
3800 b"Internal: MIXED struct corrupted\0" as *const u8 as *const c_char,
3801 );
3802 break;
3803 }
3804 cur = (*cur).c2;
3805 }
3806 }
3807 0
3808 }
3809}
3810
3811unsafe fn prefix_matches(prefix: *const xmlChar, qname: *const xmlChar, len: c_int) -> bool {
3814 unsafe {
3815 let p = string::xmlstr_to_bytes(prefix);
3816 let q = string::xmlstr_to_bytes(qname);
3817 p.len() == len as usize && q.len() >= len as usize && p[..len as usize] == q[..len as usize]
3818 }
3819}
3820
3821#[repr(C)]
3830pub struct ContentModelNfa {
3831 transitions: Vec<(u32, *const xmlChar, u32)>,
3833 start: u32,
3835 accept: Vec<u32>,
3837}
3838
3839#[repr(C)]
3841pub struct ContentModelExec {
3842 nfa: *mut ContentModelNfa,
3844 current: Vec<u32>,
3846}
3847
3848struct NfaBuilder {
3850 transitions: Vec<(u32, *const xmlChar, u32)>,
3851 n_states: u32,
3852}
3853
3854impl NfaBuilder {
3855 fn new() -> Self {
3856 NfaBuilder {
3857 transitions: Vec::new(),
3858 n_states: 0,
3859 }
3860 }
3861 fn new_state(&mut self) -> u32 {
3862 let s = self.n_states;
3863 self.n_states += 1;
3864 s
3865 }
3866 fn eps(&mut self, from: u32, to: u32) {
3867 self.transitions.push((from, ptr::null(), to));
3868 }
3869 fn name_trans(&mut self, from: u32, name: *const xmlChar, to: u32) {
3870 self.transitions.push((from, name, to));
3871 }
3872}
3873
3874unsafe fn compile_content_sub(
3879 b: &mut NfaBuilder,
3880 model: *mut _xmlElementContent,
3881) -> (u32, Vec<u32>) {
3882 if model.is_null() {
3883 let s = b.new_state();
3884 return (s, vec![s]);
3885 }
3886 let m = unsafe { &*model };
3887 let (mut in_s, mut outs) = match m.type_ as u32 {
3888 t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
3889 let s = b.new_state();
3890 let to = b.new_state();
3891 b.name_trans(s, m.name, to);
3892 (s, vec![to])
3893 }
3894 t if t == XML_ELEMENT_CONTENT_SEQ as u32 => {
3895 let (in1, out1) = compile_content_sub(b, m.c1);
3896 let (in2, out2) = compile_content_sub(b, m.c2);
3897 for &o in &out1 {
3898 b.eps(o, in2);
3899 }
3900 (in1, out2)
3901 }
3902 t if t == XML_ELEMENT_CONTENT_OR as u32 => {
3903 let (in1, out1) = compile_content_sub(b, m.c1);
3904 let (in2, out2) = compile_content_sub(b, m.c2);
3905 let s = b.new_state();
3906 b.eps(s, in1);
3907 b.eps(s, in2);
3908 let mut all = out1;
3909 all.extend(out2);
3910 (s, all)
3911 }
3912 _ => {
3917 let s = b.new_state();
3918 (s, vec![s])
3919 }
3920 };
3921 match m.ocur as u32 {
3922 o if o == XML_ELEMENT_CONTENT_OPT as u32 => {
3923 let s = b.new_state();
3924 b.eps(s, in_s);
3925 for &o2 in &outs {
3926 b.eps(s, o2);
3927 }
3928 in_s = s;
3929 }
3930 o if o == XML_ELEMENT_CONTENT_MULT as u32 => {
3931 let s = b.new_state();
3932 b.eps(s, in_s);
3933 for &o2 in &outs {
3934 b.eps(s, o2);
3935 b.eps(o2, s);
3936 }
3937 in_s = s;
3938 }
3939 o if o == XML_ELEMENT_CONTENT_PLUS as u32 => {
3940 let s = b.new_state();
3941 b.eps(s, in_s);
3942 for &o2 in &outs {
3943 b.eps(o2, s);
3944 }
3945 in_s = s;
3946 }
3947 _ => {}
3948 }
3949 (in_s, outs)
3950}
3951
3952unsafe fn content_has_pcdata(model: *mut _xmlElementContent) -> bool {
3954 if model.is_null() {
3955 return false;
3956 }
3957 unsafe {
3958 let m = &*model;
3959 if m.type_ == XML_ELEMENT_CONTENT_PCDATA as c_int {
3960 return true;
3961 }
3962 content_has_pcdata(m.c1) || content_has_pcdata(m.c2)
3963 }
3964}
3965
3966unsafe fn build_content_nfa(content: *mut _xmlElementContent) -> *mut ContentModelNfa {
3972 unsafe {
3973 if content.is_null() {
3974 return ptr::null_mut();
3975 }
3976 let mut b = NfaBuilder::new();
3977 let (start, outs) = compile_content_sub(&mut b, content);
3978 let nfa = Box::new(ContentModelNfa {
3979 transitions: b.transitions,
3980 start,
3981 accept: outs,
3982 });
3983 Box::into_raw(nfa)
3984 }
3985}
3986
3987pub unsafe fn free_content_model_nfa(nfa: *mut ContentModelNfa) {
3993 if nfa.is_null() {
3994 return;
3995 }
3996 unsafe {
3997 ptr::drop_in_place(nfa);
3998 allocator::xmlFree(nfa as *mut c_void);
3999 }
4000}
4001
4002unsafe fn eps_closure(nfa: &ContentModelNfa, states: &[u32]) -> Vec<u32> {
4004 let mut out = states.to_vec();
4005 let mut stack = states.to_vec();
4006 while let Some(s) = stack.pop() {
4007 for &(from, name, to) in &nfa.transitions {
4008 if from == s && name.is_null() && !out.contains(&to) {
4009 out.push(to);
4010 stack.push(to);
4011 }
4012 }
4013 }
4014 out.sort_unstable();
4015 out.dedup();
4016 out
4017}
4018
4019unsafe fn new_content_exec(nfa: *mut ContentModelNfa) -> *mut ContentModelExec {
4021 unsafe {
4022 let exec = allocator::xmlMalloc(size_of::<ContentModelExec>()) as *mut ContentModelExec;
4023 if exec.is_null() {
4024 return ptr::null_mut();
4025 }
4026 let cur = eps_closure(&*nfa, &[(*nfa).start]);
4027 ptr::write(&mut (*exec).nfa, nfa);
4028 ptr::write(&mut (*exec).current, cur);
4029 exec
4030 }
4031}
4032
4033unsafe fn free_content_exec(exec: *mut ContentModelExec) {
4035 if exec.is_null() {
4036 return;
4037 }
4038 unsafe {
4039 ptr::drop_in_place(&mut (*exec).current);
4040 allocator::xmlFree(exec as *mut c_void);
4041 }
4042}
4043
4044unsafe fn content_exec_push(exec: *mut ContentModelExec, value: *const xmlChar) -> c_int {
4049 unsafe {
4050 if exec.is_null() {
4051 return -1;
4052 }
4053 let nfa = &*(*exec).nfa;
4054 if value.is_null() {
4055 let cur = eps_closure(nfa, &(*exec).current);
4056 return if cur.iter().any(|&s| nfa.accept.contains(&s)) {
4057 1
4058 } else {
4059 0
4060 };
4061 }
4062 let mut next: Vec<u32> = Vec::new();
4063 for &s in &(*exec).current {
4064 for &(from, name, to) in &nfa.transitions {
4065 if from == s && !name.is_null() && string::xml_strcmp(name, value) == 0 {
4066 next.push(to);
4067 }
4068 }
4069 }
4070 next.sort_unstable();
4071 next.dedup();
4072 if next.is_empty() {
4073 return -1;
4074 }
4075 let closed = eps_closure(nfa, &next);
4076 (*exec).current = closed;
4077 if (*exec).current.iter().any(|&s| nfa.accept.contains(&s)) {
4078 1
4079 } else {
4080 0
4081 }
4082 }
4083}
4084
4085unsafe fn vstate_vpush(
4087 ctxt: *mut _xmlValidCtxt,
4088 elem_decl: *mut _xmlElement,
4089 node: *mut _xmlNode,
4090) -> c_int {
4091 unsafe {
4092 if (*ctxt).vstateNr >= (*ctxt).vstateMax {
4093 let new_max = if (*ctxt).vstateMax == 0 {
4094 10
4095 } else {
4096 (*ctxt).vstateMax * 2
4097 };
4098 let new_tab = allocator::xmlRealloc(
4099 (*ctxt).vstateTab as *mut c_void,
4100 (new_max as usize) * size_of::<ValidState>(),
4101 ) as *mut ValidState;
4102 if new_tab.is_null() {
4103 vctxt_error(
4104 ctxt,
4105 b"Memory allocation failed : xmlValidCtxt\0" as *const u8 as *const c_char,
4106 );
4107 return -1;
4108 }
4109 (*ctxt).vstateTab = new_tab as *mut c_void;
4110 (*ctxt).vstateMax = new_max;
4111 }
4112 let idx = (*ctxt).vstateNr as usize;
4113 let tab = (*ctxt).vstateTab as *mut ValidState;
4114 (*tab.add(idx)).elem_decl = elem_decl;
4115 (*tab.add(idx)).node = node;
4116 (*tab.add(idx)).exec = ptr::null_mut();
4117 if !elem_decl.is_null() && (*elem_decl).etype == XML_ELEMENT_TYPE_ELEMENT as c_int {
4118 if (*elem_decl).cont_model.is_null() {
4119 validate_build_content_model(ctxt, elem_decl);
4120 }
4121 if !(*elem_decl).cont_model.is_null() {
4122 let exec = new_content_exec((*elem_decl).cont_model as *mut ContentModelNfa);
4123 if exec.is_null() {
4124 vctxt_error(
4125 ctxt,
4126 b"Memory allocation failed : xmlValidCtxt\0" as *const u8 as *const c_char,
4127 );
4128 return -1;
4129 }
4130 (*tab.add(idx)).exec = exec;
4131 } else {
4132 let msg = format!(
4133 "Failed to build content model regexp for {}\0",
4134 string::xmlstr_to_string((*elem_decl).name)
4135 );
4136 vctxt_error_node(ctxt, node, msg.as_ptr() as *const c_char);
4137 }
4138 }
4139 (*ctxt).vstate = tab.add(idx) as *mut c_void;
4140 (*ctxt).vstateNr += 1;
4141 0
4142 }
4143}
4144
4145unsafe fn vstate_vpop(ctxt: *mut _xmlValidCtxt) -> c_int {
4147 unsafe {
4148 if (*ctxt).vstateNr < 1 {
4149 return -1;
4150 }
4151 (*ctxt).vstateNr -= 1;
4152 let idx = (*ctxt).vstateNr as usize;
4153 let tab = (*ctxt).vstateTab as *mut ValidState;
4154 let elem_decl = (*tab.add(idx)).elem_decl;
4155 (*tab.add(idx)).elem_decl = ptr::null_mut();
4156 (*tab.add(idx)).node = ptr::null_mut();
4157 if !elem_decl.is_null() && (*elem_decl).etype == XML_ELEMENT_TYPE_ELEMENT as c_int {
4158 if !(*tab.add(idx)).exec.is_null() {
4159 free_content_exec((*tab.add(idx)).exec);
4160 }
4161 }
4162 (*tab.add(idx)).exec = ptr::null_mut();
4163 if (*ctxt).vstateNr >= 1 {
4164 (*ctxt).vstate = tab.add((*ctxt).vstateNr as usize - 1) as *mut c_void;
4165 } else {
4166 (*ctxt).vstate = ptr::null_mut();
4167 }
4168 0
4169 }
4170}
4171
4172pub unsafe fn validate_build_content_model(
4180 ctxt: *mut _xmlValidCtxt,
4181 elem: *mut _xmlElement,
4182) -> c_int {
4183 unsafe {
4184 if ctxt.is_null() {
4185 return 0;
4186 }
4187 if (*elem).type_ != XML_ELEMENT_DECL as c_int {
4188 return 0;
4189 }
4190 if (*elem).etype != XML_ELEMENT_TYPE_ELEMENT as c_int {
4191 return 1;
4192 }
4193 if !(*elem).cont_model.is_null() {
4194 return 1;
4195 }
4196 if (*elem).content.is_null() {
4197 return 1;
4198 }
4199 if content_has_pcdata((*elem).content) {
4200 let msg = format!(
4201 "Found PCDATA in content model of {}\0",
4202 string::xmlstr_to_string((*elem).name)
4203 );
4204 vctxt_error_node(ctxt, elem as *mut _xmlNode, msg.as_ptr() as *const c_char);
4205 return 0;
4206 }
4207 let nfa = build_content_nfa((*elem).content);
4208 if nfa.is_null() {
4209 vctxt_error(
4210 ctxt,
4211 b"Memory allocation failed : xmlValidBuildContentModel\0" as *const u8
4212 as *const c_char,
4213 );
4214 return 0;
4215 }
4216 (*elem).cont_model = nfa as *mut c_void;
4217 1
4218 }
4219}
4220
4221pub unsafe fn validate_push_element(
4229 ctxt: *mut _xmlValidCtxt,
4230 doc: *mut _xmlDoc,
4231 elem: *mut _xmlNode,
4232 qname: *const xmlChar,
4233) -> c_int {
4234 unsafe {
4235 let mut ret = 1;
4236 if ctxt.is_null() {
4237 return 0;
4238 }
4239 if (*ctxt).vstateNr > 0 && !(*ctxt).vstate.is_null() {
4240 let state = (*ctxt).vstate as *mut ValidState;
4241 let elem_decl = (*state).elem_decl;
4242 if !elem_decl.is_null() {
4243 match (*elem_decl).etype as u32 {
4244 t if t == XML_ELEMENT_TYPE_UNDEFINED as u32 => ret = 0,
4245 t if t == XML_ELEMENT_TYPE_EMPTY as u32 => {
4246 let msg = format!(
4247 "Element {} was declared EMPTY this one has content\0",
4248 string::xmlstr_to_string((*(*state).node).name)
4249 );
4250 vctxt_error_node(ctxt, (*state).node, msg.as_ptr() as *const c_char);
4251 ret = 0;
4252 }
4253 t if t == XML_ELEMENT_TYPE_ANY as u32 => {}
4254 t if t == XML_ELEMENT_TYPE_MIXED as u32 => {
4255 if !(*elem_decl).content.is_null()
4256 && (*(*elem_decl).content).type_ == XML_ELEMENT_CONTENT_PCDATA as c_int
4257 {
4258 let msg = format!(
4259 "Element {} was declared #PCDATA but contains non text nodes\0",
4260 string::xmlstr_to_string((*(*state).node).name)
4261 );
4262 vctxt_error_node(ctxt, (*state).node, msg.as_ptr() as *const c_char);
4263 ret = 0;
4264 } else {
4265 ret = validate_check_mixed(ctxt, (*elem_decl).content, qname);
4266 if ret != 1 {
4267 let msg = format!(
4268 "Element {} is not declared in {} list of possible children\0",
4269 string::xmlstr_to_string(qname),
4270 string::xmlstr_to_string((*(*state).node).name)
4271 );
4272 vctxt_error_node(
4273 ctxt,
4274 (*state).node,
4275 msg.as_ptr() as *const c_char,
4276 );
4277 }
4278 }
4279 }
4280 t if t == XML_ELEMENT_TYPE_ELEMENT as u32 => {
4281 if !(*state).exec.is_null() {
4282 ret = content_exec_push((*state).exec, qname);
4283 if ret < 0 {
4284 let msg = format!(
4285 "Element {} content does not follow the DTD, Misplaced {}\0",
4286 string::xmlstr_to_string((*(*state).node).name),
4287 string::xmlstr_to_string(qname)
4288 );
4289 vctxt_error_node(
4290 ctxt,
4291 (*state).node,
4292 msg.as_ptr() as *const c_char,
4293 );
4294 ret = 0;
4295 } else {
4296 ret = 1;
4297 }
4298 }
4299 }
4300 _ => {}
4301 }
4302 }
4303 }
4304 let mut extsubset = 0;
4305 let e_decl = valid_get_elem_decl(ctxt, doc, elem, &mut extsubset);
4306 let _ = vstate_vpush(ctxt, e_decl, elem);
4308 ret
4309 }
4310}
4311
4312pub unsafe fn validate_push_cdata(
4319 ctxt: *mut _xmlValidCtxt,
4320 data: *const xmlChar,
4321 len: c_int,
4322) -> c_int {
4323 unsafe {
4324 let mut ret = 1;
4325 if ctxt.is_null() {
4326 return 0;
4327 }
4328 if len <= 0 {
4329 return 1;
4330 }
4331 if (*ctxt).vstateNr > 0 && !(*ctxt).vstate.is_null() {
4332 let state = (*ctxt).vstate as *mut ValidState;
4333 let elem_decl = (*state).elem_decl;
4334 if !elem_decl.is_null() {
4335 match (*elem_decl).etype as u32 {
4336 t if t == XML_ELEMENT_TYPE_UNDEFINED as u32 => ret = 0,
4337 t if t == XML_ELEMENT_TYPE_EMPTY as u32 => {
4338 let msg = format!(
4339 "Element {} was declared EMPTY this one has content\0",
4340 string::xmlstr_to_string((*(*state).node).name)
4341 );
4342 vctxt_error_node(ctxt, (*state).node, msg.as_ptr() as *const c_char);
4343 ret = 0;
4344 }
4345 t if t == XML_ELEMENT_TYPE_ANY as u32 || t == XML_ELEMENT_TYPE_MIXED as u32 => {
4346 }
4347 t if t == XML_ELEMENT_TYPE_ELEMENT as u32 => {
4348 let bytes = core::slice::from_raw_parts(data, len as usize);
4349 for &b in bytes {
4350 if !is_blank_byte(b) {
4351 let msg = format!(
4352 "Element {} content does not follow the DTD, Text not allowed\0",
4353 string::xmlstr_to_string((*(*state).node).name)
4354 );
4355 vctxt_error_node(
4356 ctxt,
4357 (*state).node,
4358 msg.as_ptr() as *const c_char,
4359 );
4360 ret = 0;
4361 break;
4362 }
4363 }
4364 }
4365 _ => {}
4366 }
4367 }
4368 }
4369 ret
4370 }
4371}
4372
4373pub unsafe fn validate_pop_element(
4380 ctxt: *mut _xmlValidCtxt,
4381 _doc: *mut _xmlDoc,
4382 _elem: *mut _xmlNode,
4383 _qname: *const xmlChar,
4384) -> c_int {
4385 unsafe {
4386 let mut ret = 1;
4387 if ctxt.is_null() {
4388 return 0;
4389 }
4390 if (*ctxt).vstateNr > 0 && !(*ctxt).vstate.is_null() {
4391 let state = (*ctxt).vstate as *mut ValidState;
4392 let elem_decl = (*state).elem_decl;
4393 if !elem_decl.is_null() && (*elem_decl).etype == XML_ELEMENT_TYPE_ELEMENT as c_int {
4394 if !(*state).exec.is_null() {
4395 ret = content_exec_push((*state).exec, ptr::null());
4396 if ret <= 0 {
4397 let msg = format!(
4398 "Element {} content does not follow the DTD, Expecting more children\0",
4399 string::xmlstr_to_string((*(*state).node).name)
4400 );
4401 vctxt_error_node(ctxt, (*state).node, msg.as_ptr() as *const c_char);
4402 ret = 0;
4403 } else {
4404 ret = 1;
4405 }
4406 }
4407 }
4408 let _ = vstate_vpop(ctxt);
4409 }
4410 ret
4411 }
4412}
4413
4414#[cfg(test)]
4419mod tests {
4420 use super::*;
4421 use crate::abi::allocator;
4422 use crate::abi::types::xmlElementTypeVal::*;
4423 use crate::xml::dtd;
4424 use crate::xml::tree;
4425
4426 unsafe fn c_str(s: &str) -> *const xmlChar {
4430 let bytes = s.as_bytes();
4431 let ptr = allocator::xmlMalloc(bytes.len() + 1) as *mut xmlChar;
4432 assert!(!ptr.is_null());
4433 std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
4434 *ptr.add(bytes.len()) = 0;
4435 ptr
4436 }
4437
4438 unsafe fn make_test_doc() -> (*mut _xmlDoc, *mut _xmlDtd) {
4440 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
4441 assert!(!doc.is_null());
4442
4443 let name = c_str("root");
4444 let ext_id = c_str("--//Test//DTD//EN");
4445 let sys_id = c_str("test.dtd");
4446 let dtd = dtd::create_int_subset(doc, name, ext_id, sys_id);
4447 assert!(!dtd.is_null());
4448
4449 (doc, dtd)
4450 }
4451
4452 #[allow(unused)]
4454 unsafe fn add_elem_decl(
4455 dtd: *mut _xmlDtd,
4456 name: *const xmlChar,
4457 elem_type: c_int,
4458 content: *mut _xmlElementContent,
4459 ) -> *mut _xmlElement {
4460 let result = dtd::add_element_decl(dtd, name, elem_type, content);
4461 result
4462 }
4463
4464 unsafe fn create_root_elem(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
4466 let node = tree::new_node(ptr::null_mut(), name);
4467 assert!(!node.is_null());
4468 tree::add_child(doc as *mut _xmlNode, node);
4469 node
4470 }
4471
4472 #[allow(unused)]
4474 unsafe fn create_child_elem(parent: *mut _xmlNode, name: *const xmlChar) -> *mut _xmlNode {
4475 let node = tree::new_node(ptr::null_mut(), name);
4476 assert!(!node.is_null());
4477 tree::add_child(parent, node);
4478 node
4479 }
4480
4481 #[test]
4484 fn test_validate_name_null() {
4485 unsafe {
4486 assert_eq!(validate_name(ptr::null()), 0);
4487 }
4488 }
4489
4490 #[test]
4491 fn test_validate_name_empty() {
4492 unsafe {
4493 let s = b"\0" as *const u8 as *const xmlChar;
4494 assert_eq!(validate_name(s), 0);
4495 }
4496 }
4497
4498 #[test]
4499 fn test_validate_name_valid() {
4500 unsafe {
4501 let tests = ["foo", "_bar", ":baz", "hello-world", "ns:elem", "a123"];
4502 for t in &tests {
4503 let s = c_str(t);
4504 assert_eq!(validate_name(s), 1, "Expected '{}' to be a valid Name", t);
4505 allocator::xmlFree(s as *mut c_void);
4506 }
4507 }
4508 }
4509
4510 #[test]
4511 fn test_validate_name_invalid() {
4512 unsafe {
4513 let tests = ["123abc", "-foo", ".bar", "foo bar", "a b"];
4514 for t in &tests {
4515 let s = c_str(t);
4516 assert_eq!(validate_name(s), 0, "Expected '{}' to be invalid", t);
4517 allocator::xmlFree(s as *mut c_void);
4518 }
4519 }
4520 }
4521
4522 #[test]
4523 fn test_validate_names_valid() {
4524 unsafe {
4525 let s = c_str("foo bar baz");
4526 assert_eq!(validate_names(s), 1);
4527 allocator::xmlFree(s as *mut c_void);
4528 }
4529 }
4530
4531 #[test]
4532 fn test_validate_names_invalid() {
4533 unsafe {
4534 let s = c_str("foo 123bar baz");
4535 assert_eq!(validate_names(s), 0);
4536 allocator::xmlFree(s as *mut c_void);
4537 }
4538 }
4539
4540 #[test]
4543 fn test_validate_nmtoken_null() {
4544 unsafe {
4545 assert_eq!(validate_nmtoken(ptr::null()), 0);
4546 }
4547 }
4548
4549 #[test]
4550 fn test_validate_nmtoken_valid() {
4551 unsafe {
4552 let tests = ["foo", "123abc", "-foo", ".bar", "_test", ":ns"];
4553 for t in &tests {
4554 let s = c_str(t);
4555 assert_eq!(
4556 validate_nmtoken(s),
4557 1,
4558 "Expected '{}' to be a valid NMTOKEN",
4559 t
4560 );
4561 allocator::xmlFree(s as *mut c_void);
4562 }
4563 }
4564 }
4565
4566 #[test]
4567 fn test_validate_nmtoken_invalid() {
4568 unsafe {
4569 let s = c_str("foo bar");
4570 assert_eq!(validate_nmtoken(s), 0);
4571 allocator::xmlFree(s as *mut c_void);
4572 }
4573 }
4574
4575 #[test]
4576 fn test_validate_nmtokens_valid() {
4577 unsafe {
4578 let s = c_str("foo 123bar -baz");
4579 assert_eq!(validate_nmtokens(s), 1);
4580 allocator::xmlFree(s as *mut c_void);
4581 }
4582 }
4583
4584 #[test]
4587 fn test_validate_attribute_value_cdata() {
4588 unsafe {
4589 let s = c_str("anything goes here!@#$%^&*()");
4590 assert_eq!(validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, s), 1);
4591 allocator::xmlFree(s as *mut c_void);
4592
4593 let empty = b"\0" as *const u8 as *const xmlChar;
4595 assert_eq!(
4596 validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, empty),
4597 1
4598 );
4599 }
4600 }
4601
4602 #[test]
4603 fn test_validate_attribute_value_id() {
4604 unsafe {
4605 let valid = c_str("myId");
4606 assert_eq!(
4607 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, valid),
4608 1
4609 );
4610 allocator::xmlFree(valid as *mut c_void);
4611
4612 let invalid = c_str("123id");
4613 assert_eq!(
4614 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, invalid),
4615 0
4616 );
4617 allocator::xmlFree(invalid as *mut c_void);
4618 }
4619 }
4620
4621 #[test]
4622 fn test_validate_attribute_value_idref() {
4623 unsafe {
4624 let valid = c_str("someId");
4625 assert_eq!(
4626 validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, valid),
4627 1
4628 );
4629 allocator::xmlFree(valid as *mut c_void);
4630 }
4631 }
4632
4633 #[test]
4634 fn test_validate_attribute_value_idrefs() {
4635 unsafe {
4636 let valid = c_str("id1 id2 id3");
4637 assert_eq!(
4638 validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, valid),
4639 1
4640 );
4641 allocator::xmlFree(valid as *mut c_void);
4642
4643 let invalid = c_str("id1 123id");
4644 assert_eq!(
4645 validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, invalid),
4646 0
4647 );
4648 allocator::xmlFree(invalid as *mut c_void);
4649 }
4650 }
4651
4652 #[test]
4653 fn test_validate_attribute_value_entity() {
4654 unsafe {
4655 let valid = c_str("myEntity");
4656 assert_eq!(
4657 validate_attribute_value(XML_ATTRIBUTE_ENTITY as c_int, valid),
4658 1
4659 );
4660 allocator::xmlFree(valid as *mut c_void);
4661 }
4662 }
4663
4664 #[test]
4665 fn test_validate_attribute_value_nmtoken() {
4666 unsafe {
4667 let valid = c_str("123abc");
4668 assert_eq!(
4669 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, valid),
4670 1
4671 );
4672 allocator::xmlFree(valid as *mut c_void);
4673
4674 let invalid = c_str("foo bar");
4675 assert_eq!(
4676 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, invalid),
4677 0
4678 );
4679 allocator::xmlFree(invalid as *mut c_void);
4680 }
4681 }
4682
4683 #[test]
4684 fn test_validate_attribute_value_null() {
4685 unsafe {
4686 assert_eq!(
4690 validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, ptr::null()),
4691 1
4692 );
4693 assert_eq!(
4694 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, ptr::null()),
4695 0
4696 );
4697 }
4698 }
4699
4700 #[test]
4703 fn test_validate_enumeration_valid() {
4704 unsafe {
4705 let ctxt = new_valid_ctxt();
4706 assert!(!ctxt.is_null());
4707
4708 let red = c_str("red");
4709 let green = c_str("green");
4710 let blue = c_str("blue");
4711
4712 let e3 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
4713 (*e3).name = string::xml_strdup(blue);
4714 (*e3).next = ptr::null_mut();
4715
4716 let e2 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
4717 (*e2).name = string::xml_strdup(green);
4718 (*e2).next = e3;
4719
4720 let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
4721 (*e1).name = string::xml_strdup(red);
4722 (*e1).next = e2;
4723
4724 let value = c_str("green");
4725 assert_eq!(validate_enumeration(ctxt, value, e1), 1);
4726 assert_eq!((*ctxt).valid, 1);
4727
4728 allocator::xmlFree(value as *mut c_void);
4729 allocator::xmlFree(red as *mut c_void);
4730 allocator::xmlFree(green as *mut c_void);
4731 allocator::xmlFree(blue as *mut c_void);
4732 free_valid_ctxt(ctxt);
4733 }
4734 }
4735
4736 #[test]
4737 fn test_validate_enumeration_invalid() {
4738 unsafe {
4739 let ctxt = new_valid_ctxt();
4740 assert!(!ctxt.is_null());
4741
4742 let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
4743 (*e1).name = string::xml_strdup(b"red\0" as *const u8 as *const xmlChar);
4744 (*e1).next = ptr::null_mut();
4745
4746 let value = c_str("yellow");
4747 assert_eq!(validate_enumeration(ctxt, value, e1), 0);
4748
4749 allocator::xmlFree(value as *mut c_void);
4750 free_valid_ctxt(ctxt);
4751 }
4752 }
4753
4754 #[test]
4757 fn test_validate_notation_use_valid() {
4758 unsafe {
4759 let (doc, dtd) = make_test_doc();
4760
4761 let notation_name = c_str("GIF");
4762 dtd::add_notation_decl(dtd, notation_name, ptr::null(), ptr::null());
4763
4764 let ctxt = new_valid_ctxt();
4765 assert!(!ctxt.is_null());
4766
4767 assert_eq!(validate_notation_use(ctxt, doc, notation_name), 1);
4768
4769 free_valid_ctxt(ctxt);
4770 tree::free_doc(doc);
4771 }
4772 }
4773
4774 #[test]
4775 fn test_validate_notation_use_invalid() {
4776 unsafe {
4777 let (doc, _dtd) = make_test_doc();
4778
4779 let ctxt = new_valid_ctxt();
4780 assert!(!ctxt.is_null());
4781
4782 let notation_name = c_str("UNDECLARED");
4783 assert_eq!(validate_notation_use(ctxt, doc, notation_name), 0);
4784
4785 free_valid_ctxt(ctxt);
4786 allocator::xmlFree(notation_name as *mut c_void);
4787 tree::free_doc(doc);
4788 }
4789 }
4790
4791 #[test]
4794 fn test_new_free_valid_ctxt() {
4795 unsafe {
4796 let ctxt = new_valid_ctxt();
4797 assert!(!ctxt.is_null());
4798 assert_eq!((*ctxt).valid, 1);
4799 assert!((*ctxt).node.is_null());
4800 free_valid_ctxt(ctxt);
4801 }
4802 }
4803
4804 #[test]
4805 fn test_free_valid_ctxt_null() {
4806 unsafe {
4807 free_valid_ctxt(ptr::null_mut());
4808 }
4809 }
4810
4811 #[test]
4814 fn test_set_valid_errors_null() {
4815 unsafe {
4816 set_valid_errors(ptr::null_mut(), None, None, ptr::null_mut());
4817 }
4818 }
4819
4820 #[test]
4823 fn test_validate_element_no_dtd() {
4824 unsafe {
4825 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
4826 assert!(!doc.is_null());
4827
4828 let root_name = c_str("root");
4829 let root = create_root_elem(doc, root_name);
4830
4831 let ctxt = new_valid_ctxt();
4832 assert!(!ctxt.is_null());
4833
4834 assert_eq!(validate_element(ctxt, doc, root), 1);
4836
4837 free_valid_ctxt(ctxt);
4838 tree::free_doc(doc);
4839 }
4840 }
4841
4842 #[test]
4843 fn test_validate_element_empty_valid() {
4844 unsafe {
4845 let (doc, dtd) = make_test_doc();
4846
4847 let root_name = c_str("root");
4848 add_elem_decl(
4849 dtd,
4850 root_name,
4851 XML_ELEMENT_TYPE_EMPTY as c_int,
4852 ptr::null_mut(),
4853 );
4854
4855 let root = create_root_elem(doc, root_name);
4856
4857 let ctxt = new_valid_ctxt();
4858 assert!(!ctxt.is_null());
4859
4860 assert_eq!(validate_element(ctxt, doc, root), 1);
4861
4862 free_valid_ctxt(ctxt);
4863 tree::free_doc(doc);
4864 }
4865 }
4866
4867 #[test]
4868 fn test_validate_element_undeclared() {
4869 unsafe {
4870 let (doc, _dtd) = make_test_doc();
4871
4872 let root_name = c_str("root");
4873 let root = create_root_elem(doc, root_name);
4874
4875 let ctxt = new_valid_ctxt();
4876 assert!(!ctxt.is_null());
4877
4878 assert_eq!(validate_element(ctxt, doc, root), 0);
4880
4881 free_valid_ctxt(ctxt);
4882 tree::free_doc(doc);
4883 }
4884 }
4885
4886 #[test]
4887 fn test_validate_element_with_content() {
4888 unsafe {
4889 let (doc, dtd) = make_test_doc();
4890
4891 let root_name = c_str("root");
4893 let child_name = c_str("child");
4894
4895 let child_content =
4897 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
4898 assert!(!child_content.is_null());
4899 (*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
4900
4901 add_elem_decl(
4902 dtd,
4903 root_name,
4904 XML_ELEMENT_TYPE_ELEMENT as c_int,
4905 child_content,
4906 );
4907 add_elem_decl(
4908 dtd,
4909 child_name,
4910 XML_ELEMENT_TYPE_EMPTY as c_int,
4911 ptr::null_mut(),
4912 );
4913
4914 let root = create_root_elem(doc, root_name);
4915 let _child = create_child_elem(root, child_name);
4916
4917 let ctxt = new_valid_ctxt();
4918 assert!(!ctxt.is_null());
4919
4920 assert_eq!(validate_element(ctxt, doc, root), 1);
4921
4922 free_valid_ctxt(ctxt);
4923 tree::free_doc(doc);
4924 }
4925 }
4926
4927 #[test]
4928 fn test_validate_element_invalid_content() {
4929 unsafe {
4930 let (doc, dtd) = make_test_doc();
4931
4932 let root_name = c_str("root");
4933 let child_name = c_str("child");
4934 let wrong_name = c_str("wrong");
4935
4936 let child_content =
4938 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
4939 assert!(!child_content.is_null());
4940 (*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
4941
4942 add_elem_decl(
4943 dtd,
4944 root_name,
4945 XML_ELEMENT_TYPE_ELEMENT as c_int,
4946 child_content,
4947 );
4948 add_elem_decl(
4949 dtd,
4950 child_name,
4951 XML_ELEMENT_TYPE_EMPTY as c_int,
4952 ptr::null_mut(),
4953 );
4954 add_elem_decl(
4955 dtd,
4956 wrong_name,
4957 XML_ELEMENT_TYPE_EMPTY as c_int,
4958 ptr::null_mut(),
4959 );
4960
4961 let root = create_root_elem(doc, root_name);
4962 create_child_elem(root, wrong_name);
4964
4965 let ctxt = new_valid_ctxt();
4966 assert!(!ctxt.is_null());
4967
4968 assert_eq!(validate_element(ctxt, doc, root), 0);
4969
4970 free_valid_ctxt(ctxt);
4971 tree::free_doc(doc);
4972 }
4973 }
4974
4975 #[test]
4978 fn test_validate_root_match() {
4979 unsafe {
4980 let (doc, dtd) = make_test_doc();
4981
4982 let root_name = c_str("root");
4983 add_elem_decl(
4984 dtd,
4985 root_name,
4986 XML_ELEMENT_TYPE_EMPTY as c_int,
4987 ptr::null_mut(),
4988 );
4989 create_root_elem(doc, root_name);
4990
4991 let ctxt = new_valid_ctxt();
4992 assert!(!ctxt.is_null());
4993
4994 assert_eq!(validate_root(ctxt, doc), 1);
4995
4996 free_valid_ctxt(ctxt);
4997 tree::free_doc(doc);
4998 }
4999 }
5000
5001 #[test]
5002 fn test_validate_root_no_dtd() {
5003 unsafe {
5004 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
5005 assert!(!doc.is_null());
5006
5007 let root_name = c_str("root");
5008 create_root_elem(doc, root_name);
5009
5010 let ctxt = new_valid_ctxt();
5011 assert!(!ctxt.is_null());
5012
5013 assert_eq!(validate_root(ctxt, doc), 1);
5015
5016 free_valid_ctxt(ctxt);
5017 tree::free_doc(doc);
5018 }
5019 }
5020
5021 #[test]
5024 fn test_validate_document_valid() {
5025 unsafe {
5026 let (doc, dtd) = make_test_doc();
5027
5028 let root_name = c_str("root");
5029 add_elem_decl(
5030 dtd,
5031 root_name,
5032 XML_ELEMENT_TYPE_EMPTY as c_int,
5033 ptr::null_mut(),
5034 );
5035 create_root_elem(doc, root_name);
5036
5037 let ctxt = new_valid_ctxt();
5038 assert!(!ctxt.is_null());
5039
5040 assert_eq!(validate_document(ctxt, doc), 1);
5041
5042 free_valid_ctxt(ctxt);
5043 tree::free_doc(doc);
5044 }
5045 }
5046
5047 #[test]
5048 fn test_validate_document_no_root() {
5049 unsafe {
5050 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
5051 assert!(!doc.is_null());
5052
5053 let ctxt = new_valid_ctxt();
5054 assert!(!ctxt.is_null());
5055
5056 assert_eq!(validate_document(ctxt, doc), 0);
5057
5058 free_valid_ctxt(ctxt);
5059 tree::free_doc(doc);
5060 }
5061 }
5062
5063 #[test]
5066 fn test_validate_content_valid() {
5067 unsafe {
5068 let (doc, dtd) = make_test_doc();
5069
5070 let root_name = c_str("root");
5071 let child_name = c_str("child");
5072
5073 let child_content =
5074 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
5075 assert!(!child_content.is_null());
5076
5077 add_elem_decl(
5078 dtd,
5079 root_name,
5080 XML_ELEMENT_TYPE_ELEMENT as c_int,
5081 child_content,
5082 );
5083 add_elem_decl(
5084 dtd,
5085 child_name,
5086 XML_ELEMENT_TYPE_EMPTY as c_int,
5087 ptr::null_mut(),
5088 );
5089
5090 let root = create_root_elem(doc, root_name);
5091 create_child_elem(root, child_name);
5092
5093 let ctxt = new_valid_ctxt();
5094 assert!(!ctxt.is_null());
5095
5096 assert_eq!(validate_content(ctxt, root, doc), 1);
5097
5098 free_valid_ctxt(ctxt);
5099 tree::free_doc(doc);
5100 }
5101 }
5102
5103 #[test]
5106 fn test_is_mixed_element() {
5107 unsafe {
5108 let (doc, dtd) = make_test_doc();
5109 let name = c_str("mixedElem");
5110 add_elem_decl(dtd, name, XML_ELEMENT_TYPE_MIXED as c_int, ptr::null_mut());
5111
5112 assert_eq!(is_mixed_element(doc, name), 1);
5113
5114 let other = c_str("other");
5115 assert_eq!(is_mixed_element(doc, other), 0);
5116
5117 allocator::xmlFree(other as *mut c_void);
5118 tree::free_doc(doc);
5119 }
5120 }
5121
5122 #[test]
5123 fn test_is_empty_element() {
5124 unsafe {
5125 let (doc, dtd) = make_test_doc();
5126 let name = c_str("emptyElem");
5127 add_elem_decl(dtd, name, XML_ELEMENT_TYPE_EMPTY as c_int, ptr::null_mut());
5128
5129 assert_eq!(is_empty_element(doc, name), 1);
5130
5131 let other = c_str("other");
5132 assert_eq!(is_empty_element(doc, other), 0);
5133
5134 allocator::xmlFree(other as *mut c_void);
5135 tree::free_doc(doc);
5136 }
5137 }
5138
5139 #[test]
5140 fn test_is_mixed_element_no_dtd() {
5141 unsafe {
5142 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
5143 assert!(!doc.is_null());
5144
5145 let name = c_str("foo");
5146 assert_eq!(is_mixed_element(doc, name), 0);
5147
5148 allocator::xmlFree(name as *mut c_void);
5149 tree::free_doc(doc);
5150 }
5151 }
5152
5153 #[test]
5154 fn test_is_empty_element_no_dtd() {
5155 unsafe {
5156 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
5157 assert!(!doc.is_null());
5158
5159 let name = c_str("foo");
5160 assert_eq!(is_empty_element(doc, name), 0);
5161
5162 allocator::xmlFree(name as *mut c_void);
5163 tree::free_doc(doc);
5164 }
5165 }
5166
5167 #[test]
5170 fn test_validate_dtd_null() {
5171 unsafe {
5172 let ctxt = new_valid_ctxt();
5173 assert!(!ctxt.is_null());
5174 assert_eq!(validate_dtd(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
5175 free_valid_ctxt(ctxt);
5176 }
5177 }
5178
5179 #[test]
5182 fn test_validate_element_null() {
5183 unsafe {
5184 let (doc, _dtd) = make_test_doc();
5185 let ctxt = new_valid_ctxt();
5186 assert!(!ctxt.is_null());
5187
5188 assert_eq!(validate_element(ctxt, doc, ptr::null_mut()), 0);
5189
5190 free_valid_ctxt(ctxt);
5191 tree::free_doc(doc);
5192 }
5193 }
5194
5195 #[test]
5196 fn test_validate_document_null() {
5197 unsafe {
5198 let ctxt = new_valid_ctxt();
5199 assert!(!ctxt.is_null());
5200
5201 assert_eq!(validate_document(ctxt, ptr::null_mut()), 0);
5202 assert_eq!(validate_document(ptr::null_mut(), ptr::null_mut()), 0);
5203
5204 free_valid_ctxt(ctxt);
5205 }
5206 }
5207
5208 #[test]
5209 fn test_validate_document_final_null() {
5210 unsafe {
5211 let ctxt = new_valid_ctxt();
5212 assert!(!ctxt.is_null());
5213
5214 assert_eq!(validate_document_final(ctxt, ptr::null_mut()), 0);
5215 assert_eq!(validate_document_final(ptr::null_mut(), ptr::null_mut()), 0);
5216
5217 free_valid_ctxt(ctxt);
5218 }
5219 }
5220
5221 #[test]
5222 fn test_validate_attribute_decl_null() {
5223 unsafe {
5224 let ctxt = new_valid_ctxt();
5225 assert!(!ctxt.is_null());
5226
5227 assert_eq!(
5228 validate_attribute_decl(ctxt, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()),
5229 0
5230 );
5231
5232 free_valid_ctxt(ctxt);
5233 }
5234 }
5235
5236 #[test]
5237 fn test_validate_content_null() {
5238 unsafe {
5239 let ctxt = new_valid_ctxt();
5240 assert!(!ctxt.is_null());
5241
5242 assert_eq!(validate_content(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
5243
5244 free_valid_ctxt(ctxt);
5245 }
5246 }
5247
5248 #[test]
5249 fn test_validate_root_null() {
5250 unsafe {
5251 assert_eq!(validate_root(ptr::null_mut(), ptr::null_mut()), 0);
5252 }
5253 }
5254
5255 #[test]
5256 fn test_validate_enumeration_null() {
5257 unsafe {
5258 let ctxt = new_valid_ctxt();
5259 assert!(!ctxt.is_null());
5260
5261 assert_eq!(validate_enumeration(ctxt, ptr::null(), ptr::null_mut()), 0);
5262
5263 free_valid_ctxt(ctxt);
5264 }
5265 }
5266
5267 #[test]
5268 fn test_validate_notation_use_null() {
5269 unsafe {
5270 let ctxt = new_valid_ctxt();
5271 assert!(!ctxt.is_null());
5272
5273 assert_eq!(validate_notation_use(ctxt, ptr::null_mut(), ptr::null()), 0);
5274
5275 free_valid_ctxt(ctxt);
5276 }
5277 }
5278
5279 #[test]
5280 fn test_validate_name_start_characters() {
5281 unsafe {
5282 let name = c_str("\u{C0}lph\u{E0}");
5284 assert_eq!(validate_name(name), 1);
5285 allocator::xmlFree(name as *mut c_void);
5286 }
5287 }
5288
5289 #[test]
5290 fn test_validate_names_single() {
5291 unsafe {
5292 let s = c_str("singleName");
5293 assert_eq!(validate_names(s), 1);
5294 allocator::xmlFree(s as *mut c_void);
5295 }
5296 }
5297
5298 #[test]
5299 fn test_validate_nmtokens_single() {
5300 unsafe {
5301 let s = c_str("123abc");
5302 assert_eq!(validate_nmtokens(s), 1);
5303 allocator::xmlFree(s as *mut c_void);
5304 }
5305 }
5306
5307 #[test]
5308 fn test_validate_nmtokens_invalid() {
5309 unsafe {
5310 let s = c_str("foo\tbar"); assert_eq!(validate_nmtokens(s), 1); allocator::xmlFree(s as *mut c_void);
5313
5314 let s2 = c_str("foo@bar");
5316 assert_eq!(validate_nmtokens(s2), 0);
5317 allocator::xmlFree(s2 as *mut c_void);
5318 }
5319 }
5320
5321 #[test]
5322 fn test_validate_attribute_value_empty_non_cdata() {
5323 unsafe {
5324 let empty = b"\0" as *const u8 as *const xmlChar;
5325 assert_eq!(
5326 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, empty),
5327 0
5328 );
5329 assert_eq!(
5330 validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, empty),
5331 0
5332 );
5333 assert_eq!(
5334 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, empty),
5335 0
5336 );
5337 }
5338 }
5339
5340 #[test]
5341 fn test_validate_attribute_value_unknown_type() {
5342 unsafe {
5343 let s = c_str("test");
5346 assert_eq!(validate_attribute_value(999, s), 1);
5347 allocator::xmlFree(s as *mut c_void);
5348 }
5349 }
5350
5351 #[test]
5352 fn test_validate_element_any_content() {
5353 unsafe {
5354 let (doc, dtd) = make_test_doc();
5355
5356 let root_name = c_str("root");
5357 add_elem_decl(
5358 dtd,
5359 root_name,
5360 XML_ELEMENT_TYPE_ANY as c_int,
5361 ptr::null_mut(),
5362 );
5363
5364 let child_name = c_str("child");
5365 add_elem_decl(
5366 dtd,
5367 child_name,
5368 XML_ELEMENT_TYPE_EMPTY as c_int,
5369 ptr::null_mut(),
5370 );
5371
5372 let root = create_root_elem(doc, root_name);
5373 create_child_elem(root, child_name);
5374
5375 let ctxt = new_valid_ctxt();
5376 assert!(!ctxt.is_null());
5377
5378 assert_eq!(validate_element(ctxt, doc, root), 1);
5380
5381 free_valid_ctxt(ctxt);
5382 tree::free_doc(doc);
5383 }
5384 }
5385
5386 #[test]
5387 fn test_validate_element_empty_with_child() {
5388 unsafe {
5389 let (doc, dtd) = make_test_doc();
5390
5391 let root_name = c_str("root");
5392 add_elem_decl(
5393 dtd,
5394 root_name,
5395 XML_ELEMENT_TYPE_EMPTY as c_int,
5396 ptr::null_mut(),
5397 );
5398
5399 let child_name = c_str("child");
5400 add_elem_decl(
5401 dtd,
5402 child_name,
5403 XML_ELEMENT_TYPE_EMPTY as c_int,
5404 ptr::null_mut(),
5405 );
5406
5407 let root = create_root_elem(doc, root_name);
5408 create_child_elem(root, child_name);
5409
5410 let ctxt = new_valid_ctxt();
5411 assert!(!ctxt.is_null());
5412
5413 assert_eq!(validate_element(ctxt, doc, root), 0);
5415
5416 free_valid_ctxt(ctxt);
5417 tree::free_doc(doc);
5418 }
5419 }
5420
5421 #[test]
5422 fn test_validate_dtd_final_null() {
5423 unsafe {
5424 assert_eq!(validate_dtd_final(ptr::null_mut(), ptr::null_mut()), 0);
5425 }
5426 }
5427}