1use core::ffi::c_void;
8use core::ptr;
9use std::os::raw::{c_char, c_int, c_uint, c_ulong};
10
11use crate::abi::allocator;
12use crate::abi::structs::*;
13use crate::abi::types::xmlElementType::*;
14use crate::abi::types::xmlEntityType::*;
15use crate::abi::types::*;
16use crate::xml::hash;
17use crate::xml::string;
18
19pub const XML_ENTITY_CONTENT_DEPTH_MAX: c_int = 32;
29
30pub const XML_ENTITY_CONTENT_EXPANSION_MAX: c_uint = 1_000_000;
36
37pub unsafe fn add_entity(
64 dtd: *mut _xmlDtd,
65 name: *const xmlChar,
66 etype: c_int,
67 ExternalID: *const xmlChar,
68 SystemID: *const xmlChar,
69 content: *const xmlChar,
70) -> *mut _xmlEntity {
71 if dtd.is_null() || name.is_null() {
72 return ptr::null_mut();
73 }
74
75 unsafe {
76 let d = &*dtd;
77
78 let is_param = is_parameter_entity(etype);
80 if is_param {
83 if (*dtd).pentities.is_null() {
84 (*dtd).pentities = hash::hash_create(8) as *mut c_void;
85 }
86 } else if (*dtd).entities.is_null() {
87 (*dtd).entities = hash::hash_create(8) as *mut c_void;
88 }
89 let hash_table = if is_param {
90 d.pentities as *mut hash::HashTable
91 } else {
92 d.entities as *mut hash::HashTable
93 };
94
95 let existing = hash::hash_lookup(hash_table, name);
97 if !existing.is_null() {
98 return existing as *mut _xmlEntity;
99 }
100
101 let entity = allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
103 if entity.is_null() {
104 return ptr::null_mut();
105 }
106
107 (*entity).type_ = XML_ENTITY_DECL as c_int;
108 (*entity).name = string::xml_strdup(name);
109 (*entity).etype = etype;
110 (*entity).ExternalID = string::xml_strdup(ExternalID);
111 (*entity).SystemID = string::xml_strdup(SystemID);
112 (*entity).content = string::xml_strdup(content);
113 (*entity).orig = ptr::null_mut();
114 (*entity).length = if content.is_null() {
115 0
116 } else {
117 string::xml_strlen(content) as c_int
118 };
119 (*entity).flags = 0;
120 (*entity).expandedSize = 0;
121 (*entity).URI = ptr::null_mut();
122 (*entity).parent = dtd;
123 (*entity).doc = d.doc;
124 (*entity).nexte = ptr::null_mut();
125 (*entity).owner = 0;
126
127 let ret = hash::hash_add_entry(hash_table, name, entity as *mut c_void);
129 if ret != 0 {
130 free_entity_internal(entity, false);
132 return ptr::null_mut();
133 }
134
135 if (*dtd).last.is_null() {
138 (*dtd).children = entity as *mut _xmlNode;
139 (*dtd).last = entity as *mut _xmlNode;
140 } else {
141 (*(*dtd).last).next = entity as *mut _xmlNode;
142 (*entity).prev = (*dtd).last;
143 (*dtd).last = entity as *mut _xmlNode;
144 }
145
146 entity
147 }
148}
149
150pub unsafe fn get_entity(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
166 if doc.is_null() || name.is_null() {
167 return ptr::null_mut();
168 }
169
170 unsafe {
171 let d = &*doc;
172
173 if !d.intSubset.is_null() {
175 let entities = (*d.intSubset).entities as *mut hash::HashTable;
176 let found = hash::hash_lookup(entities, name);
177 if !found.is_null() {
178 return found as *mut _xmlEntity;
179 }
180 }
181
182 if !d.extSubset.is_null() {
184 let entities = (*d.extSubset).entities as *mut hash::HashTable;
185 let found = hash::hash_lookup(entities, name);
186 if !found.is_null() {
187 return found as *mut _xmlEntity;
188 }
189 }
190
191 ptr::null_mut()
192 }
193}
194
195pub unsafe fn get_entity_from_dtd(dtd: *mut _xmlDtd, name: *const xmlChar) -> *mut _xmlEntity {
203 if dtd.is_null() || name.is_null() {
204 return ptr::null_mut();
205 }
206 unsafe {
207 let entities = (*dtd).entities as *mut hash::HashTable;
208 let found = hash::hash_lookup(entities, name);
209 if found.is_null() {
210 ptr::null_mut()
211 } else {
212 found as *mut _xmlEntity
213 }
214 }
215}
216
217pub unsafe fn get_parameter_entity(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
232 if doc.is_null() || name.is_null() {
233 return ptr::null_mut();
234 }
235
236 unsafe {
237 let d = &*doc;
238
239 if !d.intSubset.is_null() {
241 let pentities = (*d.intSubset).pentities as *mut hash::HashTable;
242 let found = hash::hash_lookup(pentities, name);
243 if !found.is_null() {
244 return found as *mut _xmlEntity;
245 }
246 }
247
248 if !d.extSubset.is_null() {
250 let pentities = (*d.extSubset).pentities as *mut hash::HashTable;
251 let found = hash::hash_lookup(pentities, name);
252 if !found.is_null() {
253 return found as *mut _xmlEntity;
254 }
255 }
256
257 ptr::null_mut()
258 }
259}
260
261pub unsafe fn copy_entity(entity: *mut _xmlEntity) -> *mut _xmlEntity {
273 if entity.is_null() {
274 return ptr::null_mut();
275 }
276
277 unsafe {
278 let e = &*entity;
279
280 let copy = allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
282 if copy.is_null() {
283 return ptr::null_mut();
284 }
285
286 (*copy).type_ = XML_ENTITY_DECL as c_int;
287 (*copy).name = string::xml_strdup(e.name);
288 (*copy).etype = e.etype;
289 (*copy).ExternalID = string::xml_strdup(e.ExternalID);
290 (*copy).SystemID = string::xml_strdup(e.SystemID);
291 (*copy).content = string::xml_strdup(e.content);
292 (*copy).orig = string::xml_strdup(e.orig);
293 (*copy).length = e.length;
294 (*copy).flags = e.flags;
295 (*copy).expandedSize = e.expandedSize;
296 (*copy).URI = string::xml_strdup(e.URI);
297 (*copy).parent = e.parent;
298 (*copy).doc = e.doc;
299 (*copy).nexte = ptr::null_mut();
300 (*copy).owner = e.owner;
301
302 copy
303 }
304}
305
306pub unsafe fn free_entity(entity: *mut _xmlEntity) {
318 free_entity_internal(entity, true);
319}
320
321unsafe fn free_entity_internal(entity: *mut _xmlEntity, free_children: bool) {
328 if entity.is_null() {
329 return;
330 }
331
332 unsafe {
333 let e = &*entity;
334
335 if !e.name.is_null() {
336 allocator::xmlFreeImpl(e.name as *mut c_void);
337 }
338 if !e.content.is_null() {
339 allocator::xmlFreeImpl(e.content as *mut c_void);
340 }
341 if !e.orig.is_null() {
342 allocator::xmlFreeImpl(e.orig as *mut c_void);
343 }
344 if !e.ExternalID.is_null() {
345 allocator::xmlFreeImpl(e.ExternalID as *mut c_void);
346 }
347 if !e.SystemID.is_null() {
348 allocator::xmlFreeImpl(e.SystemID as *mut c_void);
349 }
350 if !e.URI.is_null() {
351 allocator::xmlFreeImpl(e.URI as *mut c_void);
352 }
353
354 if free_children && !e.children.is_null() {
356 }
360
361 allocator::xmlFreeImpl(entity as *mut c_void);
362 }
363}
364
365#[inline]
371pub fn is_parameter_entity(etype: c_int) -> bool {
372 etype == XML_INTERNAL_PARAMETER_ENTITY as c_int
373 || etype == XML_EXTERNAL_PARAMETER_ENTITY as c_int
374}
375
376#[inline]
378pub fn is_external_entity(etype: c_int) -> bool {
379 etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int
380 || etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY as c_int
381 || etype == XML_EXTERNAL_PARAMETER_ENTITY as c_int
382}
383
384#[inline]
386pub fn is_predefined_entity(etype: c_int) -> bool {
387 etype == XML_INTERNAL_PREDEFINED_ENTITY as c_int
388}
389
390pub unsafe fn encode_entities_reentrant(_doc: *mut _xmlDoc, input: *const xmlChar) -> *mut xmlChar {
409 if input.is_null() {
410 return ptr::null_mut();
411 }
412
413 unsafe {
414 let len = string::xml_strlen(input);
416 let mut out_len: usize = 0;
417
418 for i in 0..len {
419 match *input.add(i) {
420 b'<' => out_len += 4, b'>' => out_len += 4, b'&' => out_len += 5, b'"' => out_len += 6, b'\'' => out_len += 6, c => out_len += 1,
426 }
427 }
428
429 let output = allocator::xmlMallocImpl(out_len + 1) as *mut xmlChar;
431 if output.is_null() {
432 return ptr::null_mut();
433 }
434
435 let mut j: usize = 0;
437 for i in 0..len {
438 let c = *input.add(i);
439 match c {
440 b'<' => {
441 ptr::copy_nonoverlapping(b"<" as *const u8, output.add(j), 4);
442 j += 4;
443 }
444 b'>' => {
445 ptr::copy_nonoverlapping(b">" as *const u8, output.add(j), 4);
446 j += 4;
447 }
448 b'&' => {
449 ptr::copy_nonoverlapping(b"&" as *const u8, output.add(j), 5);
450 j += 5;
451 }
452 b'"' => {
453 ptr::copy_nonoverlapping(b""" as *const u8, output.add(j), 6);
454 j += 6;
455 }
456 b'\'' => {
457 ptr::copy_nonoverlapping(b"'" as *const u8, output.add(j), 6);
458 j += 6;
459 }
460 _ => {
461 *output.add(j) = c;
462 j += 1;
463 }
464 }
465 }
466
467 *output.add(out_len) = 0; output
469 }
470}
471
472pub unsafe fn string_decode_entities(
499 doc: *mut _xmlDoc,
500 input: *const xmlChar,
501 what: c_int,
502 end: xmlChar,
503 end2: xmlChar,
504 end3: xmlChar,
505) -> *mut xmlChar {
506 if input.is_null() {
507 return ptr::null_mut();
508 }
509
510 unsafe {
511 let len = string::xml_strlen(input);
512 if len == 0 {
513 let empty = allocator::xmlMallocImpl(1) as *mut xmlChar;
515 if !empty.is_null() {
516 *empty = 0;
517 }
518 return empty;
519 }
520
521 let max_out = len * 4 + 1; let output = allocator::xmlMallocImpl(max_out) as *mut xmlChar;
524 if output.is_null() {
525 return ptr::null_mut();
526 }
527
528 let mut out_pos: usize = 0;
529 let mut i: usize = 0;
530
531 while i < len {
532 let c = *input.add(i);
533
534 if (end != 0 && c == end) || (end2 != 0 && c == end2) || (end3 != 0 && c == end3) {
536 break;
537 }
538
539 if c == b'&' {
540 i += 1;
542
543 if i < len && *input.add(i) == b'#' {
545 i += 1;
546 let (decoded_char, _consumed) = decode_numeric_ref(input, &mut i, len);
547 if decoded_char != 0 {
548 if decoded_char < 0x80 {
550 if out_pos < max_out - 1 {
551 *output.add(out_pos) = decoded_char as u8;
552 out_pos += 1;
553 }
554 } else if decoded_char < 0x800 {
555 if out_pos < max_out - 2 {
556 *output.add(out_pos) = 0xC0 | ((decoded_char >> 6) as u8);
557 *output.add(out_pos + 1) = 0x80 | ((decoded_char & 0x3F) as u8);
558 out_pos += 2;
559 }
560 } else {
561 if out_pos < max_out - 3 {
562 *output.add(out_pos) = 0xE0 | ((decoded_char >> 12) as u8);
563 *output.add(out_pos + 1) =
564 0x80 | (((decoded_char >> 6) & 0x3F) as u8);
565 *output.add(out_pos + 2) = 0x80 | ((decoded_char & 0x3F) as u8);
566 out_pos += 3;
567 }
568 }
569 }
570 continue;
573 }
574
575 let mut entity_name_start = i;
577 while i < len
578 && *input.add(i) != b';'
579 && *input.add(i) != b'&'
580 && *input.add(i) != 0
581 {
582 i += 1;
583 }
584
585 if i < len && *input.add(i) == b';' {
586 let name_len = i - entity_name_start;
588 if name_len > 0 {
589 let name_buf = allocator::xmlMallocImpl(name_len + 1) as *mut xmlChar;
591 if !name_buf.is_null() {
592 ptr::copy_nonoverlapping(
593 input.add(entity_name_start),
594 name_buf,
595 name_len,
596 );
597 *name_buf.add(name_len) = 0;
598
599 let mut entity: *mut _xmlEntity = ptr::null_mut();
601 if what != 1 {
602 if !doc.is_null() {
604 entity = get_entity(doc, name_buf as *const xmlChar);
605 }
606 }
607
608 if entity.is_null() {
609 entity = lookup_predefined_entity(name_buf as *const xmlChar);
611 }
612
613 if !entity.is_null() && !(*entity).content.is_null() {
614 let content = (*entity).content;
616 let content_len = string::xml_strlen(content);
617 for j in 0..content_len {
618 if out_pos < max_out - 1 {
619 *output.add(out_pos) = *content.add(j);
620 out_pos += 1;
621 }
622 }
623 } else {
624 if out_pos < max_out - 2 {
626 *output.add(out_pos) = b'&';
627 out_pos += 1;
628 }
629 for j in 0..name_len {
630 if out_pos < max_out - 2 {
631 *output.add(out_pos) = *input.add(entity_name_start + j);
632 out_pos += 1;
633 }
634 }
635 if out_pos < max_out - 1 {
636 *output.add(out_pos) = b';';
637 out_pos += 1;
638 }
639 }
640
641 allocator::xmlFreeImpl(name_buf as *mut c_void);
642 }
643 }
644 } else {
647 if out_pos < max_out - 1 {
649 *output.add(out_pos) = b'&';
650 out_pos += 1;
651 }
652 }
655 } else {
656 if out_pos < max_out - 1 {
658 *output.add(out_pos) = c;
659 out_pos += 1;
660 }
661 }
662
663 i += 1;
664 }
665
666 *output.add(out_pos) = 0; output
668 }
669}
670
671unsafe fn decode_numeric_ref(input: *const xmlChar, pos: &mut usize, len: usize) -> (u32, usize) {
675 unsafe {
676 let mut consumed: usize = 0;
677
678 if *pos >= len {
679 return (0, 0);
680 }
681
682 if *input.add(*pos) == b'x' || *input.add(*pos) == b'X' {
683 *pos += 1;
685 consumed += 1;
686
687 let mut value: u32 = 0;
688 while *pos < len {
689 let c = *input.add(*pos);
690 if c == b';' {
691 *pos += 1;
692 consumed += 1;
693 return (value, consumed);
694 }
695 let digit = match c {
696 b'0'..=b'9' => c - b'0',
697 b'a'..=b'f' => c - b'a' + 10,
698 b'A'..=b'F' => c - b'A' + 10,
699 _ => break,
700 };
701 value = value.wrapping_mul(16).wrapping_add(digit as u32);
702 *pos += 1;
703 consumed += 1;
704 }
705 (value, consumed)
706 } else {
707 let mut value: u32 = 0;
709 while *pos < len {
710 let c = *input.add(*pos);
711 if c == b';' {
712 *pos += 1;
713 consumed += 1;
714 return (value, consumed);
715 }
716 if !c.is_ascii_digit() {
717 break;
718 }
719 value = value.wrapping_mul(10).wrapping_add((c - b'0') as u32);
720 *pos += 1;
721 consumed += 1;
722 }
723 (value, consumed)
724 }
725 }
726}
727
728struct SyncPtr<T>(pub T);
730unsafe impl<T> Sync for SyncPtr<T> {}
731
732unsafe fn lookup_predefined_entity(name: *const xmlChar) -> *mut _xmlEntity {
736 if name.is_null() {
737 return ptr::null_mut();
738 }
739
740 static PREDEFINED_ENTITIES: SyncPtr<[_xmlEntity; 5]> = SyncPtr([
742 _xmlEntity {
743 _private: ptr::null_mut(),
744 type_: XML_ENTITY_DECL as c_int,
745 name: b"lt\0" as *const u8 as *const xmlChar,
746 children: ptr::null_mut(),
747 last: ptr::null_mut(),
748 parent: ptr::null_mut(),
749 next: ptr::null_mut(),
750 prev: ptr::null_mut(),
751 doc: ptr::null_mut(),
752 orig: ptr::null_mut(),
753 content: b"<\0" as *const u8 as *mut xmlChar,
754 length: 1,
755 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
756 ExternalID: ptr::null(),
757 SystemID: ptr::null(),
758 nexte: ptr::null_mut(),
759 URI: ptr::null(),
760 owner: 0,
761 flags: 0,
762 expandedSize: 0,
763 },
764 _xmlEntity {
765 _private: ptr::null_mut(),
766 type_: XML_ENTITY_DECL as c_int,
767 name: b"gt\0" as *const u8 as *const xmlChar,
768 children: ptr::null_mut(),
769 last: ptr::null_mut(),
770 parent: ptr::null_mut(),
771 next: ptr::null_mut(),
772 prev: ptr::null_mut(),
773 doc: ptr::null_mut(),
774 orig: ptr::null_mut(),
775 content: b">\0" as *const u8 as *mut xmlChar,
776 length: 1,
777 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
778 ExternalID: ptr::null(),
779 SystemID: ptr::null(),
780 nexte: ptr::null_mut(),
781 URI: ptr::null(),
782 owner: 0,
783 flags: 0,
784 expandedSize: 0,
785 },
786 _xmlEntity {
787 _private: ptr::null_mut(),
788 type_: XML_ENTITY_DECL as c_int,
789 name: b"amp\0" as *const u8 as *const xmlChar,
790 children: ptr::null_mut(),
791 last: ptr::null_mut(),
792 parent: ptr::null_mut(),
793 next: ptr::null_mut(),
794 prev: ptr::null_mut(),
795 doc: ptr::null_mut(),
796 orig: ptr::null_mut(),
797 content: b"&\0" as *const u8 as *mut xmlChar,
798 length: 1,
799 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
800 ExternalID: ptr::null(),
801 SystemID: ptr::null(),
802 nexte: ptr::null_mut(),
803 URI: ptr::null(),
804 owner: 0,
805 flags: 0,
806 expandedSize: 0,
807 },
808 _xmlEntity {
809 _private: ptr::null_mut(),
810 type_: XML_ENTITY_DECL as c_int,
811 name: b"quot\0" as *const u8 as *const xmlChar,
812 children: ptr::null_mut(),
813 last: ptr::null_mut(),
814 parent: ptr::null_mut(),
815 next: ptr::null_mut(),
816 prev: ptr::null_mut(),
817 doc: ptr::null_mut(),
818 orig: ptr::null_mut(),
819 content: b"\"\0" as *const u8 as *mut xmlChar,
820 length: 1,
821 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
822 ExternalID: ptr::null(),
823 SystemID: ptr::null(),
824 nexte: ptr::null_mut(),
825 URI: ptr::null(),
826 owner: 0,
827 flags: 0,
828 expandedSize: 0,
829 },
830 _xmlEntity {
831 _private: ptr::null_mut(),
832 type_: XML_ENTITY_DECL as c_int,
833 name: b"apos\0" as *const u8 as *const xmlChar,
834 children: ptr::null_mut(),
835 last: ptr::null_mut(),
836 parent: ptr::null_mut(),
837 next: ptr::null_mut(),
838 prev: ptr::null_mut(),
839 doc: ptr::null_mut(),
840 orig: ptr::null_mut(),
841 content: b"'\0" as *const u8 as *mut xmlChar,
842 length: 1,
843 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
844 ExternalID: ptr::null(),
845 SystemID: ptr::null(),
846 nexte: ptr::null_mut(),
847 URI: ptr::null(),
848 owner: 0,
849 flags: 0,
850 expandedSize: 0,
851 },
852 ]);
853
854 unsafe {
855 let idx = match *name.add(0) as char {
857 'l' => {
858 if string::xml_strcmp(name, b"lt\0" as *const u8 as *const xmlChar) == 0 {
859 0
860 } else {
861 return ptr::null_mut();
862 }
863 }
864 'g' => {
865 if string::xml_strcmp(name, b"gt\0" as *const u8 as *const xmlChar) == 0 {
866 1
867 } else {
868 return ptr::null_mut();
869 }
870 }
871 'a' => {
872 if string::xml_strcmp(name, b"amp\0" as *const u8 as *const xmlChar) == 0 {
873 2
874 } else if string::xml_strcmp(name, b"apos\0" as *const u8 as *const xmlChar) == 0 {
875 4
876 } else {
877 return ptr::null_mut();
878 }
879 }
880 'q' => {
881 if string::xml_strcmp(name, b"quot\0" as *const u8 as *const xmlChar) == 0 {
882 3
883 } else {
884 return ptr::null_mut();
885 }
886 }
887 _ => return ptr::null_mut(),
888 };
889
890 &PREDEFINED_ENTITIES.0[idx] as *const _xmlEntity as *mut _xmlEntity
891 }
892}
893
894pub unsafe fn get_entity_content(entity: *mut _xmlEntity) -> *mut xmlChar {
904 if entity.is_null() {
905 return ptr::null_mut();
906 }
907
908 unsafe {
909 let e = &*entity;
910 if e.content.is_null() {
911 return ptr::null_mut();
912 }
913 string::xml_strdup(e.content)
914 }
915}
916
917pub fn check_entity_expansion_limit(expanded_size: c_ulong) -> c_int {
925 if expanded_size > XML_ENTITY_CONTENT_EXPANSION_MAX as c_ulong {
926 -1
927 } else {
928 0
929 }
930}
931
932pub fn check_entity_recursion_depth(depth: c_int) -> c_int {
936 if depth > XML_ENTITY_CONTENT_DEPTH_MAX {
937 -1
938 } else {
939 0
940 }
941}
942
943#[cfg(test)]
948mod tests {
949 use super::*;
950 use crate::abi::allocator::xmlFreeImpl;
951 use core::ffi::c_void;
952 use core::ptr;
953
954 unsafe fn c_str(s: &[u8]) -> *const xmlChar {
955 let len = s.len();
956 let buf = allocator::xmlMallocImpl(len + 1) as *mut xmlChar;
957 assert!(!buf.is_null());
958 ptr::copy_nonoverlapping(s.as_ptr(), buf, len);
959 *buf.add(len) = 0;
960 buf as *const xmlChar
961 }
962
963 unsafe fn make_doc_and_dtd() -> (*mut _xmlDoc, *mut _xmlDtd) {
964 let doc = allocator::xmlMallocZero(size_of::<_xmlDoc>() as usize) as *mut _xmlDoc;
965 assert!(!doc.is_null());
966 (*doc).type_ = XML_DOCUMENT_NODE as c_int;
967 (*doc).doc = doc;
968 let dtd = allocator::xmlMallocZero(size_of::<_xmlDtd>() as usize) as *mut _xmlDtd;
969 assert!(!dtd.is_null());
970 (*dtd).type_ = XML_DTD_NODE as c_int;
971 (*dtd).parent = doc;
972 (*dtd).doc = doc;
973 (*dtd).entities = hash::hash_create(8) as *mut c_void;
974 (*dtd).pentities = hash::hash_create(8) as *mut c_void;
975 (*doc).intSubset = dtd;
976 (doc, dtd)
977 }
978
979 #[test]
982 fn test_add_entity_general() {
983 unsafe {
984 let (doc, dtd) = make_doc_and_dtd();
985 let name = c_str(b"myEntity");
986 let content = c_str(b"Hello, World!");
987
988 let entity = add_entity(
989 dtd,
990 name,
991 XML_INTERNAL_GENERAL_ENTITY as c_int,
992 ptr::null(),
993 ptr::null(),
994 content,
995 );
996 assert!(!entity.is_null());
997 assert_eq!((*entity).etype, XML_INTERNAL_GENERAL_ENTITY as c_int);
998 assert_eq!((*entity).length, 13);
999
1000 let found = get_entity(doc, name);
1002 assert_eq!(found, entity);
1003
1004 hash::hash_free(
1007 (*dtd).entities as *mut hash::HashTable,
1008 Some(entity_deallocator),
1009 );
1010 hash::hash_free(
1011 (*dtd).pentities as *mut hash::HashTable,
1012 Some(entity_deallocator),
1013 );
1014 allocator::xmlFreeImpl(dtd as *mut c_void);
1015 allocator::xmlFreeImpl(doc as *mut c_void);
1016 }
1017 }
1018
1019 unsafe extern "C" fn entity_deallocator(payload: *mut c_void, _name: *mut u8) {
1020 if !payload.is_null() {
1021 free_entity(payload as *mut _xmlEntity);
1022 }
1023 }
1024
1025 #[test]
1026 fn test_add_entity_parameter() {
1027 unsafe {
1028 let (doc, dtd) = make_doc_and_dtd();
1029 let name = c_str(b"myParam");
1030 let content = c_str(b"parameter content");
1031
1032 let entity = add_entity(
1033 dtd,
1034 name,
1035 XML_INTERNAL_PARAMETER_ENTITY as c_int,
1036 ptr::null(),
1037 ptr::null(),
1038 content,
1039 );
1040 assert!(!entity.is_null());
1041 assert_eq!((*entity).etype, XML_INTERNAL_PARAMETER_ENTITY as c_int);
1042
1043 let found = hash::hash_lookup((*dtd).pentities as *mut hash::HashTable, name);
1045 assert_eq!(found, entity as *mut c_void);
1046
1047 let not_found = hash::hash_lookup((*dtd).entities as *mut hash::HashTable, name);
1049 assert!(not_found.is_null());
1050
1051 hash::hash_free(
1052 (*dtd).entities as *mut hash::HashTable,
1053 Some(entity_deallocator),
1054 );
1055 hash::hash_free(
1056 (*dtd).pentities as *mut hash::HashTable,
1057 Some(entity_deallocator),
1058 );
1059 allocator::xmlFreeImpl(dtd as *mut c_void);
1060 allocator::xmlFreeImpl(doc as *mut c_void);
1061 }
1062 }
1063
1064 #[test]
1065 fn test_add_entity_null_dtd() {
1066 unsafe {
1067 let entity = add_entity(
1068 ptr::null_mut(),
1069 c_str(b"test"),
1070 XML_INTERNAL_GENERAL_ENTITY as c_int,
1071 ptr::null(),
1072 ptr::null(),
1073 ptr::null(),
1074 );
1075 assert!(entity.is_null());
1076 }
1077 }
1078
1079 #[test]
1080 fn test_add_entity_duplicate() {
1081 unsafe {
1082 let (doc, dtd) = make_doc_and_dtd();
1083 let name = c_str(b"dup");
1084 let content = c_str(b"original");
1085
1086 let e1 = add_entity(
1087 dtd,
1088 name,
1089 XML_INTERNAL_GENERAL_ENTITY as c_int,
1090 ptr::null(),
1091 ptr::null(),
1092 content,
1093 );
1094 assert!(!e1.is_null());
1095
1096 let e2 = add_entity(
1097 dtd,
1098 name,
1099 XML_INTERNAL_GENERAL_ENTITY as c_int,
1100 ptr::null(),
1101 ptr::null(),
1102 c_str(b"replacement"),
1103 );
1104 assert_eq!(e1, e2); hash::hash_free(
1107 (*dtd).entities as *mut hash::HashTable,
1108 Some(entity_deallocator),
1109 );
1110 hash::hash_free(
1111 (*dtd).pentities as *mut hash::HashTable,
1112 Some(entity_deallocator),
1113 );
1114 allocator::xmlFreeImpl(dtd as *mut c_void);
1115 allocator::xmlFreeImpl(doc as *mut c_void);
1116 }
1117 }
1118
1119 #[test]
1120 fn test_add_entity_external() {
1121 unsafe {
1122 let (doc, dtd) = make_doc_and_dtd();
1123 let name = c_str(b"extEntity");
1124 let sysid = c_str(b"http://example.com/entity.xml");
1125
1126 let entity = add_entity(
1127 dtd,
1128 name,
1129 XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int,
1130 ptr::null(),
1131 sysid,
1132 ptr::null(),
1133 );
1134 assert!(!entity.is_null());
1135 assert_eq!((*entity).etype, XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int);
1136 assert_eq!(string::xml_strcmp((*entity).SystemID, sysid), 0);
1137
1138 hash::hash_free(
1139 (*dtd).entities as *mut hash::HashTable,
1140 Some(entity_deallocator),
1141 );
1142 hash::hash_free(
1143 (*dtd).pentities as *mut hash::HashTable,
1144 Some(entity_deallocator),
1145 );
1146 allocator::xmlFreeImpl(dtd as *mut c_void);
1147 allocator::xmlFreeImpl(doc as *mut c_void);
1148 }
1149 }
1150
1151 #[test]
1152 fn test_get_entity_null_doc() {
1153 unsafe {
1154 let found = get_entity(ptr::null_mut(), c_str(b"test"));
1155 assert!(found.is_null());
1156 }
1157 }
1158
1159 #[test]
1160 fn test_get_entity_not_found() {
1161 unsafe {
1162 let (doc, dtd) = make_doc_and_dtd();
1163 let found = get_entity(doc, c_str(b"nonexistent"));
1164 assert!(found.is_null());
1165
1166 hash::hash_free(
1167 (*dtd).entities as *mut hash::HashTable,
1168 Some(entity_deallocator),
1169 );
1170 hash::hash_free(
1171 (*dtd).pentities as *mut hash::HashTable,
1172 Some(entity_deallocator),
1173 );
1174 allocator::xmlFreeImpl(dtd as *mut c_void);
1175 allocator::xmlFreeImpl(doc as *mut c_void);
1176 }
1177 }
1178
1179 #[test]
1180 fn test_get_parameter_entity() {
1181 unsafe {
1182 let (doc, dtd) = make_doc_and_dtd();
1183 let name = c_str(b"param1");
1184 let content = c_str(b"param content");
1185
1186 let entity = add_entity(
1187 dtd,
1188 name,
1189 XML_INTERNAL_PARAMETER_ENTITY as c_int,
1190 ptr::null(),
1191 ptr::null(),
1192 content,
1193 );
1194 assert!(!entity.is_null());
1195
1196 let found = get_parameter_entity(doc, name);
1197 assert_eq!(found, entity);
1198
1199 hash::hash_free(
1200 (*dtd).entities as *mut hash::HashTable,
1201 Some(entity_deallocator),
1202 );
1203 hash::hash_free(
1204 (*dtd).pentities as *mut hash::HashTable,
1205 Some(entity_deallocator),
1206 );
1207 allocator::xmlFreeImpl(dtd as *mut c_void);
1208 allocator::xmlFreeImpl(doc as *mut c_void);
1209 }
1210 }
1211
1212 #[test]
1213 fn test_copy_entity() {
1214 unsafe {
1215 let name = c_str(b"srcEntity");
1216 let content = c_str(b"source content");
1217
1218 let entity =
1220 allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
1221 assert!(!entity.is_null());
1222 (*entity).type_ = XML_ENTITY_DECL as c_int;
1223 (*entity).name = string::xml_strdup(name);
1224 (*entity).content = string::xml_strdup(content);
1225 (*entity).length = 14;
1226 (*entity).etype = XML_INTERNAL_GENERAL_ENTITY as c_int;
1227
1228 let copy = copy_entity(entity);
1229 assert!(!copy.is_null());
1230 assert_ne!(copy, entity);
1231 assert_eq!((*copy).etype, XML_INTERNAL_GENERAL_ENTITY as c_int);
1232 assert_eq!((*copy).length, 14);
1233 assert_eq!(string::xml_strcmp((*copy).name, name), 0);
1234 assert_eq!(string::xml_strcmp((*copy).content, content), 0);
1235
1236 free_entity(copy);
1237 free_entity(entity);
1238 }
1239 }
1240
1241 #[test]
1242 fn test_copy_entity_null() {
1243 unsafe {
1244 assert!(copy_entity(ptr::null_mut()).is_null());
1245 }
1246 }
1247
1248 #[test]
1249 fn test_free_entity_null() {
1250 unsafe {
1251 free_entity(ptr::null_mut()); }
1253 }
1254
1255 #[test]
1258 fn test_is_parameter_entity() {
1259 assert!(is_parameter_entity(XML_INTERNAL_PARAMETER_ENTITY as c_int));
1260 assert!(is_parameter_entity(XML_EXTERNAL_PARAMETER_ENTITY as c_int));
1261 assert!(!is_parameter_entity(XML_INTERNAL_GENERAL_ENTITY as c_int));
1262 assert!(!is_parameter_entity(
1263 XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int
1264 ));
1265 }
1266
1267 #[test]
1268 fn test_is_external_entity() {
1269 assert!(is_external_entity(
1270 XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int
1271 ));
1272 assert!(is_external_entity(
1273 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY as c_int
1274 ));
1275 assert!(is_external_entity(XML_EXTERNAL_PARAMETER_ENTITY as c_int));
1276 assert!(!is_external_entity(XML_INTERNAL_GENERAL_ENTITY as c_int));
1277 assert!(!is_external_entity(XML_INTERNAL_PARAMETER_ENTITY as c_int));
1278 }
1279
1280 #[test]
1281 fn test_is_predefined_entity() {
1282 assert!(is_predefined_entity(
1283 XML_INTERNAL_PREDEFINED_ENTITY as c_int
1284 ));
1285 assert!(!is_predefined_entity(XML_INTERNAL_GENERAL_ENTITY as c_int));
1286 }
1287
1288 #[test]
1291 fn test_encode_entities_reentrant_null() {
1292 unsafe {
1293 let result = encode_entities_reentrant(ptr::null_mut(), ptr::null());
1294 assert!(result.is_null());
1295 }
1296 }
1297
1298 #[test]
1299 fn test_encode_entities_reentrant_no_special() {
1300 unsafe {
1301 let input = c_str(b"Hello, World!");
1302 let result = encode_entities_reentrant(ptr::null_mut(), input);
1303 assert!(!result.is_null());
1304 assert_eq!(string::xml_strcmp(result, input), 0);
1305 allocator::xmlFreeImpl(result as *mut c_void);
1306 }
1307 }
1308
1309 #[test]
1310 fn test_encode_entities_reentrant_lt_gt() {
1311 unsafe {
1312 let input = c_str(b"a < b > c");
1313 let result = encode_entities_reentrant(ptr::null_mut(), input);
1314 assert!(!result.is_null());
1315 let expected = c_str(b"a < b > c");
1316 assert_eq!(string::xml_strcmp(result, expected), 0);
1317 allocator::xmlFreeImpl(result as *mut c_void);
1318 }
1319 }
1320
1321 #[test]
1322 fn test_encode_entities_reentrant_amp() {
1323 unsafe {
1324 let input = c_str(b"a & b");
1325 let result = encode_entities_reentrant(ptr::null_mut(), input);
1326 assert!(!result.is_null());
1327 let expected = c_str(b"a & b");
1328 assert_eq!(string::xml_strcmp(result, expected), 0);
1329 allocator::xmlFreeImpl(result as *mut c_void);
1330 }
1331 }
1332
1333 #[test]
1334 fn test_encode_entities_reentrant_quotes() {
1335 unsafe {
1336 let input = c_str(b"\"hello\" 'world'");
1337 let result = encode_entities_reentrant(ptr::null_mut(), input);
1338 assert!(!result.is_null());
1339 let expected = c_str(b""hello" 'world'");
1340 assert_eq!(string::xml_strcmp(result, expected), 0);
1341 allocator::xmlFreeImpl(result as *mut c_void);
1342 }
1343 }
1344
1345 #[test]
1346 fn test_encode_entities_reentrant_all() {
1347 unsafe {
1348 let input = c_str(b"<tag attr=\"value\">&'more'</tag>");
1349 let result = encode_entities_reentrant(ptr::null_mut(), input);
1350 assert!(!result.is_null());
1351 let expected =
1352 c_str(b"<tag attr="value">&'more'</tag>");
1353 assert_eq!(string::xml_strcmp(result, expected), 0);
1354 allocator::xmlFreeImpl(result as *mut c_void);
1355 }
1356 }
1357
1358 #[test]
1361 fn test_decode_entities_null_input() {
1362 unsafe {
1363 let result = string_decode_entities(ptr::null_mut(), ptr::null(), 0, 0, 0, 0);
1364 assert!(result.is_null());
1365 }
1366 }
1367
1368 #[test]
1369 fn test_decode_entities_empty() {
1370 unsafe {
1371 let input = c_str(b"");
1372 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1373 assert!(!result.is_null());
1374 assert_eq!(*result, 0);
1375 allocator::xmlFreeImpl(result as *mut c_void);
1376 }
1377 }
1378
1379 #[test]
1380 fn test_decode_entities_no_refs() {
1381 unsafe {
1382 let input = c_str(b"Hello, World!");
1383 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1384 assert!(!result.is_null());
1385 assert_eq!(string::xml_strcmp(result, input), 0);
1386 allocator::xmlFreeImpl(result as *mut c_void);
1387 }
1388 }
1389
1390 #[test]
1391 fn test_decode_numeric_decimal() {
1392 unsafe {
1393 let input = c_str(b"A");
1395 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1396 assert!(!result.is_null());
1397 let expected = c_str(b"A");
1398 assert_eq!(string::xml_strcmp(result, expected), 0);
1399 allocator::xmlFreeImpl(result as *mut c_void);
1400 }
1401 }
1402
1403 #[test]
1404 fn test_decode_numeric_hex() {
1405 unsafe {
1406 let input = c_str(b"A");
1408 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1409 assert!(!result.is_null());
1410 let expected = c_str(b"A");
1411 assert_eq!(string::xml_strcmp(result, expected), 0);
1412 allocator::xmlFreeImpl(result as *mut c_void);
1413 }
1414 }
1415
1416 #[test]
1417 fn test_decode_numeric_mixed() {
1418 unsafe {
1419 let input = c_str(b"Hello World!"); let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1421 assert!(!result.is_null());
1422 let expected = c_str(b"Hello World!");
1423 assert_eq!(string::xml_strcmp(result, expected), 0);
1424 allocator::xmlFreeImpl(result as *mut c_void);
1425 }
1426 }
1427
1428 #[test]
1429 fn test_decode_predefined_entities() {
1430 unsafe {
1431 let (doc, dtd) = make_doc_and_dtd();
1432 let input = c_str(b"a < b > c & d");
1433
1434 add_entity(
1436 dtd,
1437 c_str(b"lt"),
1438 XML_INTERNAL_GENERAL_ENTITY as c_int,
1439 ptr::null(),
1440 ptr::null(),
1441 c_str(b"<"),
1442 );
1443 add_entity(
1444 dtd,
1445 c_str(b"gt"),
1446 XML_INTERNAL_GENERAL_ENTITY as c_int,
1447 ptr::null(),
1448 ptr::null(),
1449 c_str(b">"),
1450 );
1451 add_entity(
1452 dtd,
1453 c_str(b"amp"),
1454 XML_INTERNAL_GENERAL_ENTITY as c_int,
1455 ptr::null(),
1456 ptr::null(),
1457 c_str(b"&"),
1458 );
1459
1460 let result = string_decode_entities(doc, input, 0, 0, 0, 0);
1461 assert!(!result.is_null());
1462 let expected = c_str(b"a < b > c & d");
1463 assert_eq!(string::xml_strcmp(result, expected), 0);
1464
1465 allocator::xmlFreeImpl(result as *mut c_void);
1466
1467 hash::hash_free(
1468 (*dtd).entities as *mut hash::HashTable,
1469 Some(entity_deallocator),
1470 );
1471 hash::hash_free(
1472 (*dtd).pentities as *mut hash::HashTable,
1473 Some(entity_deallocator),
1474 );
1475 allocator::xmlFreeImpl(dtd as *mut c_void);
1476 allocator::xmlFreeImpl(doc as *mut c_void);
1477 }
1478 }
1479
1480 #[test]
1483 fn test_check_entity_expansion_limit() {
1484 assert_eq!(check_entity_expansion_limit(100), 0);
1485 assert_eq!(check_entity_expansion_limit(1_000_000), 0);
1486 assert_eq!(check_entity_expansion_limit(1_000_001), -1);
1487 }
1488
1489 #[test]
1490 fn test_check_entity_recursion_depth() {
1491 assert_eq!(check_entity_recursion_depth(10), 0);
1492 assert_eq!(check_entity_recursion_depth(32), 0);
1493 assert_eq!(check_entity_recursion_depth(33), -1);
1494 }
1495
1496 #[test]
1499 fn test_get_entity_content() {
1500 unsafe {
1501 let content = c_str(b"entity content");
1502 let entity =
1503 allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
1504 assert!(!entity.is_null());
1505 (*entity).content = string::xml_strdup(content);
1506
1507 let retrieved = get_entity_content(entity);
1508 assert!(!retrieved.is_null());
1509 assert_eq!(string::xml_strcmp(retrieved, content), 0);
1510
1511 allocator::xmlFreeImpl(retrieved as *mut c_void);
1512 free_entity(entity);
1513 }
1514 }
1515
1516 #[test]
1517 fn test_get_entity_content_null() {
1518 unsafe {
1519 assert!(get_entity_content(ptr::null_mut()).is_null());
1520 }
1521 }
1522}