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 let hash_table = if is_param {
81 d.pentities as *mut hash::HashTable
82 } else {
83 d.entities as *mut hash::HashTable
84 };
85
86 let existing = hash::hash_lookup(hash_table, name);
88 if !existing.is_null() {
89 return existing as *mut _xmlEntity;
90 }
91
92 let entity = allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
94 if entity.is_null() {
95 return ptr::null_mut();
96 }
97
98 (*entity).type_ = XML_ENTITY_DECL as c_int;
99 (*entity).name = string::xml_strdup(name);
100 (*entity).etype = etype;
101 (*entity).ExternalID = string::xml_strdup(ExternalID);
102 (*entity).SystemID = string::xml_strdup(SystemID);
103 (*entity).content = string::xml_strdup(content);
104 (*entity).orig = ptr::null_mut();
105 (*entity).length = if content.is_null() {
106 0
107 } else {
108 string::xml_strlen(content) as c_int
109 };
110 (*entity).flags = 0;
111 (*entity).expandedSize = 0;
112 (*entity).URI = ptr::null_mut();
113 (*entity).parent = dtd;
114 (*entity).doc = d.doc;
115 (*entity).nexte = ptr::null_mut();
116 (*entity).owner = 0;
117
118 let ret = hash::hash_add_entry(hash_table, name, entity as *mut c_void);
120 if ret != 0 {
121 free_entity_internal(entity, false);
123 return ptr::null_mut();
124 }
125
126 entity
127 }
128}
129
130pub unsafe fn get_entity(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
146 if doc.is_null() || name.is_null() {
147 return ptr::null_mut();
148 }
149
150 unsafe {
151 let d = &*doc;
152
153 if !d.intSubset.is_null() {
155 let entities = (*d.intSubset).entities as *mut hash::HashTable;
156 let found = hash::hash_lookup(entities, name);
157 if !found.is_null() {
158 return found as *mut _xmlEntity;
159 }
160 }
161
162 if !d.extSubset.is_null() {
164 let entities = (*d.extSubset).entities as *mut hash::HashTable;
165 let found = hash::hash_lookup(entities, name);
166 if !found.is_null() {
167 return found as *mut _xmlEntity;
168 }
169 }
170
171 ptr::null_mut()
172 }
173}
174
175pub unsafe fn get_parameter_entity(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlEntity {
190 if doc.is_null() || name.is_null() {
191 return ptr::null_mut();
192 }
193
194 unsafe {
195 let d = &*doc;
196
197 if !d.intSubset.is_null() {
199 let pentities = (*d.intSubset).pentities as *mut hash::HashTable;
200 let found = hash::hash_lookup(pentities, name);
201 if !found.is_null() {
202 return found as *mut _xmlEntity;
203 }
204 }
205
206 if !d.extSubset.is_null() {
208 let pentities = (*d.extSubset).pentities as *mut hash::HashTable;
209 let found = hash::hash_lookup(pentities, name);
210 if !found.is_null() {
211 return found as *mut _xmlEntity;
212 }
213 }
214
215 ptr::null_mut()
216 }
217}
218
219pub unsafe fn copy_entity(entity: *mut _xmlEntity) -> *mut _xmlEntity {
231 if entity.is_null() {
232 return ptr::null_mut();
233 }
234
235 unsafe {
236 let e = &*entity;
237
238 let copy = allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
240 if copy.is_null() {
241 return ptr::null_mut();
242 }
243
244 (*copy).type_ = XML_ENTITY_DECL as c_int;
245 (*copy).name = string::xml_strdup(e.name);
246 (*copy).etype = e.etype;
247 (*copy).ExternalID = string::xml_strdup(e.ExternalID);
248 (*copy).SystemID = string::xml_strdup(e.SystemID);
249 (*copy).content = string::xml_strdup(e.content);
250 (*copy).orig = string::xml_strdup(e.orig);
251 (*copy).length = e.length;
252 (*copy).flags = e.flags;
253 (*copy).expandedSize = e.expandedSize;
254 (*copy).URI = string::xml_strdup(e.URI);
255 (*copy).parent = e.parent;
256 (*copy).doc = e.doc;
257 (*copy).nexte = ptr::null_mut();
258 (*copy).owner = e.owner;
259
260 copy
261 }
262}
263
264pub unsafe fn free_entity(entity: *mut _xmlEntity) {
276 free_entity_internal(entity, true);
277}
278
279unsafe fn free_entity_internal(entity: *mut _xmlEntity, free_children: bool) {
286 if entity.is_null() {
287 return;
288 }
289
290 unsafe {
291 let e = &*entity;
292
293 if !e.name.is_null() {
294 allocator::xmlFree(e.name as *mut c_void);
295 }
296 if !e.content.is_null() {
297 allocator::xmlFree(e.content as *mut c_void);
298 }
299 if !e.orig.is_null() {
300 allocator::xmlFree(e.orig as *mut c_void);
301 }
302 if !e.ExternalID.is_null() {
303 allocator::xmlFree(e.ExternalID as *mut c_void);
304 }
305 if !e.SystemID.is_null() {
306 allocator::xmlFree(e.SystemID as *mut c_void);
307 }
308 if !e.URI.is_null() {
309 allocator::xmlFree(e.URI as *mut c_void);
310 }
311
312 if free_children && !e.children.is_null() {
314 }
318
319 allocator::xmlFree(entity as *mut c_void);
320 }
321}
322
323#[inline]
329pub fn is_parameter_entity(etype: c_int) -> bool {
330 etype == XML_INTERNAL_PARAMETER_ENTITY as c_int
331 || etype == XML_EXTERNAL_PARAMETER_ENTITY as c_int
332}
333
334#[inline]
336pub fn is_external_entity(etype: c_int) -> bool {
337 etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int
338 || etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY as c_int
339 || etype == XML_EXTERNAL_PARAMETER_ENTITY as c_int
340}
341
342#[inline]
344pub fn is_predefined_entity(etype: c_int) -> bool {
345 etype == XML_INTERNAL_PREDEFINED_ENTITY as c_int
346}
347
348pub unsafe fn encode_entities_reentrant(_doc: *mut _xmlDoc, input: *const xmlChar) -> *mut xmlChar {
367 if input.is_null() {
368 return ptr::null_mut();
369 }
370
371 unsafe {
372 let len = string::xml_strlen(input);
374 let mut out_len: usize = 0;
375
376 for i in 0..len {
377 match *input.add(i) {
378 b'<' => out_len += 4, b'>' => out_len += 4, b'&' => out_len += 5, b'"' => out_len += 6, b'\'' => out_len += 6, c => out_len += 1,
384 }
385 }
386
387 let output = allocator::xmlMalloc(out_len + 1) as *mut xmlChar;
389 if output.is_null() {
390 return ptr::null_mut();
391 }
392
393 let mut j: usize = 0;
395 for i in 0..len {
396 let c = *input.add(i);
397 match c {
398 b'<' => {
399 ptr::copy_nonoverlapping(b"<" as *const u8, output.add(j), 4);
400 j += 4;
401 }
402 b'>' => {
403 ptr::copy_nonoverlapping(b">" as *const u8, output.add(j), 4);
404 j += 4;
405 }
406 b'&' => {
407 ptr::copy_nonoverlapping(b"&" as *const u8, output.add(j), 5);
408 j += 5;
409 }
410 b'"' => {
411 ptr::copy_nonoverlapping(b""" as *const u8, output.add(j), 6);
412 j += 6;
413 }
414 b'\'' => {
415 ptr::copy_nonoverlapping(b"'" as *const u8, output.add(j), 6);
416 j += 6;
417 }
418 _ => {
419 *output.add(j) = c;
420 j += 1;
421 }
422 }
423 }
424
425 *output.add(out_len) = 0; output
427 }
428}
429
430pub unsafe fn string_decode_entities(
457 doc: *mut _xmlDoc,
458 input: *const xmlChar,
459 what: c_int,
460 end: xmlChar,
461 end2: xmlChar,
462 end3: xmlChar,
463) -> *mut xmlChar {
464 if input.is_null() {
465 return ptr::null_mut();
466 }
467
468 unsafe {
469 let len = string::xml_strlen(input);
470 if len == 0 {
471 let empty = allocator::xmlMalloc(1) as *mut xmlChar;
473 if !empty.is_null() {
474 *empty = 0;
475 }
476 return empty;
477 }
478
479 let max_out = len * 4 + 1; let output = allocator::xmlMalloc(max_out) as *mut xmlChar;
482 if output.is_null() {
483 return ptr::null_mut();
484 }
485
486 let mut out_pos: usize = 0;
487 let mut i: usize = 0;
488
489 while i < len {
490 let c = *input.add(i);
491
492 if (end != 0 && c == end) || (end2 != 0 && c == end2) || (end3 != 0 && c == end3) {
494 break;
495 }
496
497 if c == b'&' {
498 i += 1;
500
501 if i < len && *input.add(i) == b'#' {
503 i += 1;
504 let (decoded_char, _consumed) = decode_numeric_ref(input, &mut i, len);
505 if decoded_char != 0 {
506 if decoded_char < 0x80 {
508 if out_pos < max_out - 1 {
509 *output.add(out_pos) = decoded_char as u8;
510 out_pos += 1;
511 }
512 } else if decoded_char < 0x800 {
513 if out_pos < max_out - 2 {
514 *output.add(out_pos) = 0xC0 | ((decoded_char >> 6) as u8);
515 *output.add(out_pos + 1) = 0x80 | ((decoded_char & 0x3F) as u8);
516 out_pos += 2;
517 }
518 } else {
519 if out_pos < max_out - 3 {
520 *output.add(out_pos) = 0xE0 | ((decoded_char >> 12) as u8);
521 *output.add(out_pos + 1) =
522 0x80 | (((decoded_char >> 6) & 0x3F) as u8);
523 *output.add(out_pos + 2) = 0x80 | ((decoded_char & 0x3F) as u8);
524 out_pos += 3;
525 }
526 }
527 }
528 continue;
531 }
532
533 let mut entity_name_start = i;
535 while i < len
536 && *input.add(i) != b';'
537 && *input.add(i) != b'&'
538 && *input.add(i) != 0
539 {
540 i += 1;
541 }
542
543 if i < len && *input.add(i) == b';' {
544 let name_len = i - entity_name_start;
546 if name_len > 0 {
547 let name_buf = allocator::xmlMalloc(name_len + 1) as *mut xmlChar;
549 if !name_buf.is_null() {
550 ptr::copy_nonoverlapping(
551 input.add(entity_name_start),
552 name_buf,
553 name_len,
554 );
555 *name_buf.add(name_len) = 0;
556
557 let mut entity: *mut _xmlEntity = ptr::null_mut();
559 if what != 1 {
560 if !doc.is_null() {
562 entity = get_entity(doc, name_buf as *const xmlChar);
563 }
564 }
565
566 if entity.is_null() {
567 entity = lookup_predefined_entity(name_buf as *const xmlChar);
569 }
570
571 if !entity.is_null() && !(*entity).content.is_null() {
572 let content = (*entity).content;
574 let content_len = string::xml_strlen(content);
575 for j in 0..content_len {
576 if out_pos < max_out - 1 {
577 *output.add(out_pos) = *content.add(j);
578 out_pos += 1;
579 }
580 }
581 } else {
582 if out_pos < max_out - 2 {
584 *output.add(out_pos) = b'&';
585 out_pos += 1;
586 }
587 for j in 0..name_len {
588 if out_pos < max_out - 2 {
589 *output.add(out_pos) = *input.add(entity_name_start + j);
590 out_pos += 1;
591 }
592 }
593 if out_pos < max_out - 1 {
594 *output.add(out_pos) = b';';
595 out_pos += 1;
596 }
597 }
598
599 allocator::xmlFree(name_buf as *mut c_void);
600 }
601 }
602 } else {
605 if out_pos < max_out - 1 {
607 *output.add(out_pos) = b'&';
608 out_pos += 1;
609 }
610 }
613 } else {
614 if out_pos < max_out - 1 {
616 *output.add(out_pos) = c;
617 out_pos += 1;
618 }
619 }
620
621 i += 1;
622 }
623
624 *output.add(out_pos) = 0; output
626 }
627}
628
629unsafe fn decode_numeric_ref(input: *const xmlChar, pos: &mut usize, len: usize) -> (u32, usize) {
633 unsafe {
634 let mut consumed: usize = 0;
635
636 if *pos >= len {
637 return (0, 0);
638 }
639
640 if *input.add(*pos) == b'x' || *input.add(*pos) == b'X' {
641 *pos += 1;
643 consumed += 1;
644
645 let mut value: u32 = 0;
646 while *pos < len {
647 let c = *input.add(*pos);
648 if c == b';' {
649 *pos += 1;
650 consumed += 1;
651 return (value, consumed);
652 }
653 let digit = match c {
654 b'0'..=b'9' => c - b'0',
655 b'a'..=b'f' => c - b'a' + 10,
656 b'A'..=b'F' => c - b'A' + 10,
657 _ => break,
658 };
659 value = value.wrapping_mul(16).wrapping_add(digit as u32);
660 *pos += 1;
661 consumed += 1;
662 }
663 (value, consumed)
664 } else {
665 let mut value: u32 = 0;
667 while *pos < len {
668 let c = *input.add(*pos);
669 if c == b';' {
670 *pos += 1;
671 consumed += 1;
672 return (value, consumed);
673 }
674 if !c.is_ascii_digit() {
675 break;
676 }
677 value = value.wrapping_mul(10).wrapping_add((c - b'0') as u32);
678 *pos += 1;
679 consumed += 1;
680 }
681 (value, consumed)
682 }
683 }
684}
685
686struct SyncPtr<T>(pub T);
688unsafe impl<T> Sync for SyncPtr<T> {}
689
690unsafe fn lookup_predefined_entity(name: *const xmlChar) -> *mut _xmlEntity {
694 if name.is_null() {
695 return ptr::null_mut();
696 }
697
698 static PREDEFINED_ENTITIES: SyncPtr<[_xmlEntity; 5]> = SyncPtr([
700 _xmlEntity {
701 _private: ptr::null_mut(),
702 type_: XML_ENTITY_DECL as c_int,
703 name: b"lt\0" as *const u8 as *const xmlChar,
704 children: ptr::null_mut(),
705 last: ptr::null_mut(),
706 parent: ptr::null_mut(),
707 next: ptr::null_mut(),
708 prev: ptr::null_mut(),
709 doc: ptr::null_mut(),
710 orig: ptr::null_mut(),
711 content: b"<\0" as *const u8 as *mut xmlChar,
712 length: 1,
713 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
714 ExternalID: ptr::null(),
715 SystemID: ptr::null(),
716 nexte: ptr::null_mut(),
717 URI: ptr::null(),
718 owner: 0,
719 flags: 0,
720 expandedSize: 0,
721 },
722 _xmlEntity {
723 _private: ptr::null_mut(),
724 type_: XML_ENTITY_DECL as c_int,
725 name: b"gt\0" as *const u8 as *const xmlChar,
726 children: ptr::null_mut(),
727 last: ptr::null_mut(),
728 parent: ptr::null_mut(),
729 next: ptr::null_mut(),
730 prev: ptr::null_mut(),
731 doc: ptr::null_mut(),
732 orig: ptr::null_mut(),
733 content: b">\0" as *const u8 as *mut xmlChar,
734 length: 1,
735 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
736 ExternalID: ptr::null(),
737 SystemID: ptr::null(),
738 nexte: ptr::null_mut(),
739 URI: ptr::null(),
740 owner: 0,
741 flags: 0,
742 expandedSize: 0,
743 },
744 _xmlEntity {
745 _private: ptr::null_mut(),
746 type_: XML_ENTITY_DECL as c_int,
747 name: b"amp\0" as *const u8 as *const xmlChar,
748 children: ptr::null_mut(),
749 last: ptr::null_mut(),
750 parent: ptr::null_mut(),
751 next: ptr::null_mut(),
752 prev: ptr::null_mut(),
753 doc: ptr::null_mut(),
754 orig: ptr::null_mut(),
755 content: b"&\0" as *const u8 as *mut xmlChar,
756 length: 1,
757 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
758 ExternalID: ptr::null(),
759 SystemID: ptr::null(),
760 nexte: ptr::null_mut(),
761 URI: ptr::null(),
762 owner: 0,
763 flags: 0,
764 expandedSize: 0,
765 },
766 _xmlEntity {
767 _private: ptr::null_mut(),
768 type_: XML_ENTITY_DECL as c_int,
769 name: b"quot\0" as *const u8 as *const xmlChar,
770 children: ptr::null_mut(),
771 last: ptr::null_mut(),
772 parent: ptr::null_mut(),
773 next: ptr::null_mut(),
774 prev: ptr::null_mut(),
775 doc: ptr::null_mut(),
776 orig: ptr::null_mut(),
777 content: b"\"\0" as *const u8 as *mut xmlChar,
778 length: 1,
779 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
780 ExternalID: ptr::null(),
781 SystemID: ptr::null(),
782 nexte: ptr::null_mut(),
783 URI: ptr::null(),
784 owner: 0,
785 flags: 0,
786 expandedSize: 0,
787 },
788 _xmlEntity {
789 _private: ptr::null_mut(),
790 type_: XML_ENTITY_DECL as c_int,
791 name: b"apos\0" as *const u8 as *const xmlChar,
792 children: ptr::null_mut(),
793 last: ptr::null_mut(),
794 parent: ptr::null_mut(),
795 next: ptr::null_mut(),
796 prev: ptr::null_mut(),
797 doc: ptr::null_mut(),
798 orig: ptr::null_mut(),
799 content: b"'\0" as *const u8 as *mut xmlChar,
800 length: 1,
801 etype: XML_INTERNAL_PREDEFINED_ENTITY as c_int,
802 ExternalID: ptr::null(),
803 SystemID: ptr::null(),
804 nexte: ptr::null_mut(),
805 URI: ptr::null(),
806 owner: 0,
807 flags: 0,
808 expandedSize: 0,
809 },
810 ]);
811
812 unsafe {
813 let idx = match *name.add(0) as char {
815 'l' => {
816 if string::xml_strcmp(name, b"lt\0" as *const u8 as *const xmlChar) == 0 {
817 0
818 } else {
819 return ptr::null_mut();
820 }
821 }
822 'g' => {
823 if string::xml_strcmp(name, b"gt\0" as *const u8 as *const xmlChar) == 0 {
824 1
825 } else {
826 return ptr::null_mut();
827 }
828 }
829 'a' => {
830 if string::xml_strcmp(name, b"amp\0" as *const u8 as *const xmlChar) == 0 {
831 2
832 } else if string::xml_strcmp(name, b"apos\0" as *const u8 as *const xmlChar) == 0 {
833 4
834 } else {
835 return ptr::null_mut();
836 }
837 }
838 'q' => {
839 if string::xml_strcmp(name, b"quot\0" as *const u8 as *const xmlChar) == 0 {
840 3
841 } else {
842 return ptr::null_mut();
843 }
844 }
845 _ => return ptr::null_mut(),
846 };
847
848 &PREDEFINED_ENTITIES.0[idx] as *const _xmlEntity as *mut _xmlEntity
849 }
850}
851
852pub unsafe fn get_entity_content(entity: *mut _xmlEntity) -> *mut xmlChar {
862 if entity.is_null() {
863 return ptr::null_mut();
864 }
865
866 unsafe {
867 let e = &*entity;
868 if e.content.is_null() {
869 return ptr::null_mut();
870 }
871 string::xml_strdup(e.content)
872 }
873}
874
875pub fn check_entity_expansion_limit(expanded_size: c_ulong) -> c_int {
883 if expanded_size > XML_ENTITY_CONTENT_EXPANSION_MAX as c_ulong {
884 -1
885 } else {
886 0
887 }
888}
889
890pub fn check_entity_recursion_depth(depth: c_int) -> c_int {
894 if depth > XML_ENTITY_CONTENT_DEPTH_MAX {
895 -1
896 } else {
897 0
898 }
899}
900
901#[cfg(test)]
906mod tests {
907 use super::*;
908 use crate::abi::allocator::xmlFree;
909 use core::ffi::c_void;
910 use core::ptr;
911
912 unsafe fn c_str(s: &[u8]) -> *const xmlChar {
913 let len = s.len();
914 let buf = allocator::xmlMalloc(len + 1) as *mut xmlChar;
915 assert!(!buf.is_null());
916 ptr::copy_nonoverlapping(s.as_ptr(), buf, len);
917 *buf.add(len) = 0;
918 buf as *const xmlChar
919 }
920
921 unsafe fn make_doc_and_dtd() -> (*mut _xmlDoc, *mut _xmlDtd) {
922 let doc = allocator::xmlMallocZero(size_of::<_xmlDoc>() as usize) as *mut _xmlDoc;
923 assert!(!doc.is_null());
924 (*doc).type_ = XML_DOCUMENT_NODE as c_int;
925 (*doc).doc = doc;
926 let dtd = allocator::xmlMallocZero(size_of::<_xmlDtd>() as usize) as *mut _xmlDtd;
927 assert!(!dtd.is_null());
928 (*dtd).type_ = XML_DTD_NODE as c_int;
929 (*dtd).parent = doc;
930 (*dtd).doc = doc;
931 (*dtd).entities = hash::hash_create(8) as *mut c_void;
932 (*dtd).pentities = hash::hash_create(8) as *mut c_void;
933 (*doc).intSubset = dtd;
934 (doc, dtd)
935 }
936
937 #[test]
940 fn test_add_entity_general() {
941 unsafe {
942 let (doc, dtd) = make_doc_and_dtd();
943 let name = c_str(b"myEntity");
944 let content = c_str(b"Hello, World!");
945
946 let entity = add_entity(
947 dtd,
948 name,
949 XML_INTERNAL_GENERAL_ENTITY as c_int,
950 ptr::null(),
951 ptr::null(),
952 content,
953 );
954 assert!(!entity.is_null());
955 assert_eq!((*entity).etype, XML_INTERNAL_GENERAL_ENTITY as c_int);
956 assert_eq!((*entity).length, 13);
957
958 let found = get_entity(doc, name);
960 assert_eq!(found, entity);
961
962 hash::hash_free(
965 (*dtd).entities as *mut hash::HashTable,
966 Some(entity_deallocator),
967 );
968 hash::hash_free(
969 (*dtd).pentities as *mut hash::HashTable,
970 Some(entity_deallocator),
971 );
972 allocator::xmlFree(dtd as *mut c_void);
973 allocator::xmlFree(doc as *mut c_void);
974 }
975 }
976
977 unsafe extern "C" fn entity_deallocator(payload: *mut c_void, _name: *mut u8) {
978 if !payload.is_null() {
979 free_entity(payload as *mut _xmlEntity);
980 }
981 }
982
983 #[test]
984 fn test_add_entity_parameter() {
985 unsafe {
986 let (doc, dtd) = make_doc_and_dtd();
987 let name = c_str(b"myParam");
988 let content = c_str(b"parameter content");
989
990 let entity = add_entity(
991 dtd,
992 name,
993 XML_INTERNAL_PARAMETER_ENTITY as c_int,
994 ptr::null(),
995 ptr::null(),
996 content,
997 );
998 assert!(!entity.is_null());
999 assert_eq!((*entity).etype, XML_INTERNAL_PARAMETER_ENTITY as c_int);
1000
1001 let found = hash::hash_lookup((*dtd).pentities as *mut hash::HashTable, name);
1003 assert_eq!(found, entity as *mut c_void);
1004
1005 let not_found = hash::hash_lookup((*dtd).entities as *mut hash::HashTable, name);
1007 assert!(not_found.is_null());
1008
1009 hash::hash_free(
1010 (*dtd).entities as *mut hash::HashTable,
1011 Some(entity_deallocator),
1012 );
1013 hash::hash_free(
1014 (*dtd).pentities as *mut hash::HashTable,
1015 Some(entity_deallocator),
1016 );
1017 allocator::xmlFree(dtd as *mut c_void);
1018 allocator::xmlFree(doc as *mut c_void);
1019 }
1020 }
1021
1022 #[test]
1023 fn test_add_entity_null_dtd() {
1024 unsafe {
1025 let entity = add_entity(
1026 ptr::null_mut(),
1027 c_str(b"test"),
1028 XML_INTERNAL_GENERAL_ENTITY as c_int,
1029 ptr::null(),
1030 ptr::null(),
1031 ptr::null(),
1032 );
1033 assert!(entity.is_null());
1034 }
1035 }
1036
1037 #[test]
1038 fn test_add_entity_duplicate() {
1039 unsafe {
1040 let (doc, dtd) = make_doc_and_dtd();
1041 let name = c_str(b"dup");
1042 let content = c_str(b"original");
1043
1044 let e1 = add_entity(
1045 dtd,
1046 name,
1047 XML_INTERNAL_GENERAL_ENTITY as c_int,
1048 ptr::null(),
1049 ptr::null(),
1050 content,
1051 );
1052 assert!(!e1.is_null());
1053
1054 let e2 = add_entity(
1055 dtd,
1056 name,
1057 XML_INTERNAL_GENERAL_ENTITY as c_int,
1058 ptr::null(),
1059 ptr::null(),
1060 c_str(b"replacement"),
1061 );
1062 assert_eq!(e1, e2); hash::hash_free(
1065 (*dtd).entities as *mut hash::HashTable,
1066 Some(entity_deallocator),
1067 );
1068 hash::hash_free(
1069 (*dtd).pentities as *mut hash::HashTable,
1070 Some(entity_deallocator),
1071 );
1072 allocator::xmlFree(dtd as *mut c_void);
1073 allocator::xmlFree(doc as *mut c_void);
1074 }
1075 }
1076
1077 #[test]
1078 fn test_add_entity_external() {
1079 unsafe {
1080 let (doc, dtd) = make_doc_and_dtd();
1081 let name = c_str(b"extEntity");
1082 let sysid = c_str(b"http://example.com/entity.xml");
1083
1084 let entity = add_entity(
1085 dtd,
1086 name,
1087 XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int,
1088 ptr::null(),
1089 sysid,
1090 ptr::null(),
1091 );
1092 assert!(!entity.is_null());
1093 assert_eq!((*entity).etype, XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int);
1094 assert_eq!(string::xml_strcmp((*entity).SystemID, sysid), 0);
1095
1096 hash::hash_free(
1097 (*dtd).entities as *mut hash::HashTable,
1098 Some(entity_deallocator),
1099 );
1100 hash::hash_free(
1101 (*dtd).pentities as *mut hash::HashTable,
1102 Some(entity_deallocator),
1103 );
1104 allocator::xmlFree(dtd as *mut c_void);
1105 allocator::xmlFree(doc as *mut c_void);
1106 }
1107 }
1108
1109 #[test]
1110 fn test_get_entity_null_doc() {
1111 unsafe {
1112 let found = get_entity(ptr::null_mut(), c_str(b"test"));
1113 assert!(found.is_null());
1114 }
1115 }
1116
1117 #[test]
1118 fn test_get_entity_not_found() {
1119 unsafe {
1120 let (doc, dtd) = make_doc_and_dtd();
1121 let found = get_entity(doc, c_str(b"nonexistent"));
1122 assert!(found.is_null());
1123
1124 hash::hash_free(
1125 (*dtd).entities as *mut hash::HashTable,
1126 Some(entity_deallocator),
1127 );
1128 hash::hash_free(
1129 (*dtd).pentities as *mut hash::HashTable,
1130 Some(entity_deallocator),
1131 );
1132 allocator::xmlFree(dtd as *mut c_void);
1133 allocator::xmlFree(doc as *mut c_void);
1134 }
1135 }
1136
1137 #[test]
1138 fn test_get_parameter_entity() {
1139 unsafe {
1140 let (doc, dtd) = make_doc_and_dtd();
1141 let name = c_str(b"param1");
1142 let content = c_str(b"param content");
1143
1144 let entity = add_entity(
1145 dtd,
1146 name,
1147 XML_INTERNAL_PARAMETER_ENTITY as c_int,
1148 ptr::null(),
1149 ptr::null(),
1150 content,
1151 );
1152 assert!(!entity.is_null());
1153
1154 let found = get_parameter_entity(doc, name);
1155 assert_eq!(found, entity);
1156
1157 hash::hash_free(
1158 (*dtd).entities as *mut hash::HashTable,
1159 Some(entity_deallocator),
1160 );
1161 hash::hash_free(
1162 (*dtd).pentities as *mut hash::HashTable,
1163 Some(entity_deallocator),
1164 );
1165 allocator::xmlFree(dtd as *mut c_void);
1166 allocator::xmlFree(doc as *mut c_void);
1167 }
1168 }
1169
1170 #[test]
1171 fn test_copy_entity() {
1172 unsafe {
1173 let name = c_str(b"srcEntity");
1174 let content = c_str(b"source content");
1175
1176 let entity =
1178 allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
1179 assert!(!entity.is_null());
1180 (*entity).type_ = XML_ENTITY_DECL as c_int;
1181 (*entity).name = string::xml_strdup(name);
1182 (*entity).content = string::xml_strdup(content);
1183 (*entity).length = 14;
1184 (*entity).etype = XML_INTERNAL_GENERAL_ENTITY as c_int;
1185
1186 let copy = copy_entity(entity);
1187 assert!(!copy.is_null());
1188 assert_ne!(copy, entity);
1189 assert_eq!((*copy).etype, XML_INTERNAL_GENERAL_ENTITY as c_int);
1190 assert_eq!((*copy).length, 14);
1191 assert_eq!(string::xml_strcmp((*copy).name, name), 0);
1192 assert_eq!(string::xml_strcmp((*copy).content, content), 0);
1193
1194 free_entity(copy);
1195 free_entity(entity);
1196 }
1197 }
1198
1199 #[test]
1200 fn test_copy_entity_null() {
1201 unsafe {
1202 assert!(copy_entity(ptr::null_mut()).is_null());
1203 }
1204 }
1205
1206 #[test]
1207 fn test_free_entity_null() {
1208 unsafe {
1209 free_entity(ptr::null_mut()); }
1211 }
1212
1213 #[test]
1216 fn test_is_parameter_entity() {
1217 assert!(is_parameter_entity(XML_INTERNAL_PARAMETER_ENTITY as c_int));
1218 assert!(is_parameter_entity(XML_EXTERNAL_PARAMETER_ENTITY as c_int));
1219 assert!(!is_parameter_entity(XML_INTERNAL_GENERAL_ENTITY as c_int));
1220 assert!(!is_parameter_entity(
1221 XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int
1222 ));
1223 }
1224
1225 #[test]
1226 fn test_is_external_entity() {
1227 assert!(is_external_entity(
1228 XML_EXTERNAL_GENERAL_PARSED_ENTITY as c_int
1229 ));
1230 assert!(is_external_entity(
1231 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY as c_int
1232 ));
1233 assert!(is_external_entity(XML_EXTERNAL_PARAMETER_ENTITY as c_int));
1234 assert!(!is_external_entity(XML_INTERNAL_GENERAL_ENTITY as c_int));
1235 assert!(!is_external_entity(XML_INTERNAL_PARAMETER_ENTITY as c_int));
1236 }
1237
1238 #[test]
1239 fn test_is_predefined_entity() {
1240 assert!(is_predefined_entity(
1241 XML_INTERNAL_PREDEFINED_ENTITY as c_int
1242 ));
1243 assert!(!is_predefined_entity(XML_INTERNAL_GENERAL_ENTITY as c_int));
1244 }
1245
1246 #[test]
1249 fn test_encode_entities_reentrant_null() {
1250 unsafe {
1251 let result = encode_entities_reentrant(ptr::null_mut(), ptr::null());
1252 assert!(result.is_null());
1253 }
1254 }
1255
1256 #[test]
1257 fn test_encode_entities_reentrant_no_special() {
1258 unsafe {
1259 let input = c_str(b"Hello, World!");
1260 let result = encode_entities_reentrant(ptr::null_mut(), input);
1261 assert!(!result.is_null());
1262 assert_eq!(string::xml_strcmp(result, input), 0);
1263 allocator::xmlFree(result as *mut c_void);
1264 }
1265 }
1266
1267 #[test]
1268 fn test_encode_entities_reentrant_lt_gt() {
1269 unsafe {
1270 let input = c_str(b"a < b > c");
1271 let result = encode_entities_reentrant(ptr::null_mut(), input);
1272 assert!(!result.is_null());
1273 let expected = c_str(b"a < b > c");
1274 assert_eq!(string::xml_strcmp(result, expected), 0);
1275 allocator::xmlFree(result as *mut c_void);
1276 }
1277 }
1278
1279 #[test]
1280 fn test_encode_entities_reentrant_amp() {
1281 unsafe {
1282 let input = c_str(b"a & b");
1283 let result = encode_entities_reentrant(ptr::null_mut(), input);
1284 assert!(!result.is_null());
1285 let expected = c_str(b"a & b");
1286 assert_eq!(string::xml_strcmp(result, expected), 0);
1287 allocator::xmlFree(result as *mut c_void);
1288 }
1289 }
1290
1291 #[test]
1292 fn test_encode_entities_reentrant_quotes() {
1293 unsafe {
1294 let input = c_str(b"\"hello\" 'world'");
1295 let result = encode_entities_reentrant(ptr::null_mut(), input);
1296 assert!(!result.is_null());
1297 let expected = c_str(b""hello" 'world'");
1298 assert_eq!(string::xml_strcmp(result, expected), 0);
1299 allocator::xmlFree(result as *mut c_void);
1300 }
1301 }
1302
1303 #[test]
1304 fn test_encode_entities_reentrant_all() {
1305 unsafe {
1306 let input = c_str(b"<tag attr=\"value\">&'more'</tag>");
1307 let result = encode_entities_reentrant(ptr::null_mut(), input);
1308 assert!(!result.is_null());
1309 let expected =
1310 c_str(b"<tag attr="value">&'more'</tag>");
1311 assert_eq!(string::xml_strcmp(result, expected), 0);
1312 allocator::xmlFree(result as *mut c_void);
1313 }
1314 }
1315
1316 #[test]
1319 fn test_decode_entities_null_input() {
1320 unsafe {
1321 let result = string_decode_entities(ptr::null_mut(), ptr::null(), 0, 0, 0, 0);
1322 assert!(result.is_null());
1323 }
1324 }
1325
1326 #[test]
1327 fn test_decode_entities_empty() {
1328 unsafe {
1329 let input = c_str(b"");
1330 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1331 assert!(!result.is_null());
1332 assert_eq!(*result, 0);
1333 allocator::xmlFree(result as *mut c_void);
1334 }
1335 }
1336
1337 #[test]
1338 fn test_decode_entities_no_refs() {
1339 unsafe {
1340 let input = c_str(b"Hello, World!");
1341 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1342 assert!(!result.is_null());
1343 assert_eq!(string::xml_strcmp(result, input), 0);
1344 allocator::xmlFree(result as *mut c_void);
1345 }
1346 }
1347
1348 #[test]
1349 fn test_decode_numeric_decimal() {
1350 unsafe {
1351 let input = c_str(b"A");
1353 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1354 assert!(!result.is_null());
1355 let expected = c_str(b"A");
1356 assert_eq!(string::xml_strcmp(result, expected), 0);
1357 allocator::xmlFree(result as *mut c_void);
1358 }
1359 }
1360
1361 #[test]
1362 fn test_decode_numeric_hex() {
1363 unsafe {
1364 let input = c_str(b"A");
1366 let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1367 assert!(!result.is_null());
1368 let expected = c_str(b"A");
1369 assert_eq!(string::xml_strcmp(result, expected), 0);
1370 allocator::xmlFree(result as *mut c_void);
1371 }
1372 }
1373
1374 #[test]
1375 fn test_decode_numeric_mixed() {
1376 unsafe {
1377 let input = c_str(b"Hello World!"); let result = string_decode_entities(ptr::null_mut(), input, 0, 0, 0, 0);
1379 assert!(!result.is_null());
1380 let expected = c_str(b"Hello World!");
1381 assert_eq!(string::xml_strcmp(result, expected), 0);
1382 allocator::xmlFree(result as *mut c_void);
1383 }
1384 }
1385
1386 #[test]
1387 fn test_decode_predefined_entities() {
1388 unsafe {
1389 let (doc, dtd) = make_doc_and_dtd();
1390 let input = c_str(b"a < b > c & d");
1391
1392 add_entity(
1394 dtd,
1395 c_str(b"lt"),
1396 XML_INTERNAL_GENERAL_ENTITY as c_int,
1397 ptr::null(),
1398 ptr::null(),
1399 c_str(b"<"),
1400 );
1401 add_entity(
1402 dtd,
1403 c_str(b"gt"),
1404 XML_INTERNAL_GENERAL_ENTITY as c_int,
1405 ptr::null(),
1406 ptr::null(),
1407 c_str(b">"),
1408 );
1409 add_entity(
1410 dtd,
1411 c_str(b"amp"),
1412 XML_INTERNAL_GENERAL_ENTITY as c_int,
1413 ptr::null(),
1414 ptr::null(),
1415 c_str(b"&"),
1416 );
1417
1418 let result = string_decode_entities(doc, input, 0, 0, 0, 0);
1419 assert!(!result.is_null());
1420 let expected = c_str(b"a < b > c & d");
1421 assert_eq!(string::xml_strcmp(result, expected), 0);
1422
1423 allocator::xmlFree(result as *mut c_void);
1424
1425 hash::hash_free(
1426 (*dtd).entities as *mut hash::HashTable,
1427 Some(entity_deallocator),
1428 );
1429 hash::hash_free(
1430 (*dtd).pentities as *mut hash::HashTable,
1431 Some(entity_deallocator),
1432 );
1433 allocator::xmlFree(dtd as *mut c_void);
1434 allocator::xmlFree(doc as *mut c_void);
1435 }
1436 }
1437
1438 #[test]
1441 fn test_check_entity_expansion_limit() {
1442 assert_eq!(check_entity_expansion_limit(100), 0);
1443 assert_eq!(check_entity_expansion_limit(1_000_000), 0);
1444 assert_eq!(check_entity_expansion_limit(1_000_001), -1);
1445 }
1446
1447 #[test]
1448 fn test_check_entity_recursion_depth() {
1449 assert_eq!(check_entity_recursion_depth(10), 0);
1450 assert_eq!(check_entity_recursion_depth(32), 0);
1451 assert_eq!(check_entity_recursion_depth(33), -1);
1452 }
1453
1454 #[test]
1457 fn test_get_entity_content() {
1458 unsafe {
1459 let content = c_str(b"entity content");
1460 let entity =
1461 allocator::xmlMallocZero(size_of::<_xmlEntity>() as usize) as *mut _xmlEntity;
1462 assert!(!entity.is_null());
1463 (*entity).content = string::xml_strdup(content);
1464
1465 let retrieved = get_entity_content(entity);
1466 assert!(!retrieved.is_null());
1467 assert_eq!(string::xml_strcmp(retrieved, content), 0);
1468
1469 allocator::xmlFree(retrieved as *mut c_void);
1470 free_entity(entity);
1471 }
1472 }
1473
1474 #[test]
1475 fn test_get_entity_content_null() {
1476 unsafe {
1477 assert!(get_entity_content(ptr::null_mut()).is_null());
1478 }
1479 }
1480}