1use core::ffi::c_void;
32use core::fmt::Write;
33use core::ptr;
34use std::os::raw::{c_char, c_int};
35
36use crate::abi::callbacks::{xmlGenericErrorFunc, xmlStructuredErrorFunc};
37use crate::abi::structs::_xmlError;
38use crate::abi::types::xmlErrorLevel::*;
39use crate::abi::types::*;
40use crate::xml::globals;
41
42pub unsafe fn set_generic_error_func(ctx: *mut c_void, handler: Option<xmlGenericErrorFunc>) {
59 unsafe { globals::set_generic_error_func(ctx, handler) };
61}
62
63pub unsafe fn set_structured_error_func(ctx: *mut c_void, handler: Option<xmlStructuredErrorFunc>) {
75 unsafe { globals::set_structured_error_func(ctx, handler) };
77}
78
79pub fn get_last_error() -> *mut _xmlError {
90 globals::get_last_error()
91}
92
93pub unsafe fn copy_error(from: *const _xmlError, to: *mut _xmlError) -> c_int {
107 if from.is_null() || to.is_null() {
108 return -1;
109 }
110 unsafe {
112 ptr::copy_nonoverlapping(from, to, 1);
113 }
114 0
115}
116
117pub unsafe fn reset_error(err: *mut _xmlError) {
129 if err.is_null() {
130 return;
131 }
132 unsafe {
134 ptr::write(
135 err,
136 _xmlError {
137 domain: XML_FROM_NONE,
138 code: XML_ERR_OK as c_int,
139 message: ptr::null_mut(),
140 level: XML_ERR_NONE as c_int,
141 file: ptr::null_mut(),
142 line: 0,
143 str1: ptr::null_mut(),
144 str2: ptr::null_mut(),
145 str3: ptr::null_mut(),
146 int1: 0,
147 int2: 0,
148 ctxt: ptr::null_mut(),
149 node: ptr::null_mut(),
150 },
151 );
152 }
153}
154
155pub fn reset_last_error() {
163 globals::reset_last_error();
164}
165
166pub fn format_error_message(
181 _domain: c_int,
182 _code: c_int,
183 msg: *const c_char,
184 str1: *const c_char,
185 str2: *const c_char,
186 str3: *const c_char,
187) -> *mut c_char {
188 if !msg.is_null() {
191 let msg_str = unsafe { crate::abi::allocator::xmlMemStrdupImpl(msg) };
193 return msg_str as *mut c_char;
194 }
195
196 let mut buf: [u8; 1024] = [0; 1024];
200 let mut pos = 0;
201
202 let domain_str = match _domain {
204 XML_FROM_PARSER => "parser",
205 XML_FROM_TREE => "tree",
206 XML_FROM_NAMESPACE => "namespace",
207 XML_FROM_DTD => "dtd",
208 XML_FROM_HTML => "html",
209 XML_FROM_MEMORY => "memory",
210 XML_FROM_OUTPUT => "output",
211 XML_FROM_IO => "io",
212 XML_FROM_XPATH => "xpath",
213 XML_FROM_XPOINTER => "xpointer",
214 XML_FROM_XINCLUDE => "xinclude",
215 XML_FROM_CATALOG => "catalog",
216 XML_FROM_C14N => "c14n",
217 XML_FROM_XSLT => "xslt",
218 XML_FROM_VALID => "valid",
219 XML_FROM_CHECK => "check",
220 XML_FROM_WRITER => "writer",
221 XML_FROM_MODULE => "module",
222 XML_FROM_I18N => "i18n",
223 XML_FROM_SCHEMATRONV => "schematron",
224 XML_FROM_BUFFER => "buffer",
225 XML_FROM_URI => "uri",
226 XML_FROM_NONE => "",
227 XML_FROM_FTP => "ftp",
228 XML_FROM_HTTP => "http",
229 XML_FROM_REGEXP => "regexp",
230 XML_FROM_DATATYPE => "datatype",
231 XML_FROM_SCHEMASP => "schema parser",
232 XML_FROM_SCHEMASV => "schema validator",
233 XML_FROM_RELAXNGP => "relaxng parser",
234 XML_FROM_RELAXNGV => "relaxng validator",
235 _ => "unknown",
236 };
237
238 if !domain_str.is_empty() {
239 let bytes = domain_str.as_bytes();
240 let len = bytes.len().min(buf.len().saturating_sub(pos + 2));
241 buf[pos..pos + len].copy_from_slice(&bytes[..len]);
242 pos += len;
243 buf[pos] = b' ';
244 pos += 1;
245 }
246
247 if !str1.is_null() {
249 let s = unsafe { crate::abi::versioning::c_str_to_bytes(str1).unwrap_or_default() };
251 if pos + s.len() + 3 <= buf.len() {
252 buf[pos] = b'\'';
253 pos += 1;
254 buf[pos..pos + s.len()].copy_from_slice(s);
255 pos += s.len();
256 buf[pos] = b'\'';
257 pos += 1;
258 buf[pos] = b' ';
259 pos += 1;
260 }
261 }
262
263 if !str2.is_null() {
265 let s = unsafe { crate::abi::versioning::c_str_to_bytes(str2).unwrap_or_default() };
266 if pos + s.len() + 3 <= buf.len() {
267 buf[pos] = b'\'';
268 pos += 1;
269 buf[pos..pos + s.len()].copy_from_slice(s);
270 pos += s.len();
271 buf[pos] = b'\'';
272 pos += 1;
273 buf[pos] = b' ';
274 pos += 1;
275 }
276 }
277
278 if !str3.is_null() {
280 let s = unsafe { crate::abi::versioning::c_str_to_bytes(str3).unwrap_or_default() };
281 if pos + s.len() + 3 <= buf.len() {
282 buf[pos] = b'\'';
283 pos += 1;
284 buf[pos..pos + s.len()].copy_from_slice(s);
285 pos += s.len();
286 buf[pos] = b'\'';
287 pos += 1;
288 buf[pos] = b' ';
289 pos += 1;
290 }
291 }
292
293 if pos < buf.len() {
295 buf[pos] = 0;
296 } else {
297 buf[buf.len() - 1] = 0;
298 }
299
300 let result = unsafe { crate::abi::allocator::xmlMallocImpl(pos + 1) };
302 if result.is_null() {
303 return ptr::null_mut();
304 }
305 unsafe {
306 ptr::copy_nonoverlapping(buf.as_ptr(), result as *mut u8, pos + 1);
307 }
308 result as *mut c_char
309}
310
311pub unsafe fn raise_error(
348 ctxt: *mut c_void,
349 _ctxt2: *mut c_void,
350 _ctxt3: *mut c_void,
351 _ctxt4: *mut c_void,
352 _ctxt5: *mut c_void,
353 domain: c_int,
354 code: c_int,
355 level: c_int,
356 file: *const c_char,
357 line: c_int,
358 str1: *const c_char,
359 str2: *const c_char,
360 str3: *const c_char,
361 int1: c_int,
362 int2: c_int,
363 msg: *const c_char,
364) {
365 let formatted_msg = format_error_message(domain, code, msg, str1, str2, str3);
367
368 let file_copy = if file.is_null() {
371 ptr::null_mut()
372 } else {
373 crate::abi::allocator::xmlMemStrdupImpl(file) as *mut c_char
374 };
375 let str1_copy = if str1.is_null() {
376 ptr::null_mut()
377 } else {
378 crate::abi::allocator::xmlMemStrdupImpl(str1) as *mut c_char
379 };
380 let str2_copy = if str2.is_null() {
381 ptr::null_mut()
382 } else {
383 crate::abi::allocator::xmlMemStrdupImpl(str2) as *mut c_char
384 };
385 let str3_copy = if str3.is_null() {
386 ptr::null_mut()
387 } else {
388 crate::abi::allocator::xmlMemStrdupImpl(str3) as *mut c_char
389 };
390
391 let err = _xmlError {
393 domain,
394 code,
395 message: formatted_msg,
396 level,
397 file: file_copy,
398 line,
399 str1: str1_copy,
400 str2: str2_copy,
401 str3: str3_copy,
402 int1,
403 int2: 0,
404 ctxt,
405 node: ptr::null_mut(),
406 };
407
408 globals::set_last_error(err);
409
410 if let Some(handler) = globals::get_structured_error_func() {
412 let ctx = globals::get_structured_error_ctx();
413 let err_ref = globals::get_last_error();
414 if !err_ref.is_null() {
415 handler(ctx, err_ref as *const _xmlError);
416 }
417 }
418
419 if let Some(handler) = globals::get_generic_error_func() {
421 if level != 0 {
422 let ctx = globals::get_generic_error_ctx();
423 if !formatted_msg.is_null() {
424 handler(ctx, formatted_msg as *const core::ffi::c_char);
425 } else if !msg.is_null() {
426 handler(ctx, msg);
427 }
428 }
429 }
430
431 }
437
438unsafe fn emit_legacy_message(level: &str, msg: *const c_char) {
444 if msg.is_null() {
445 return;
446 }
447 let len = libc::strlen(msg) as usize;
448 let text = core::slice::from_raw_parts(msg as *const u8, len);
449 let mut full = Vec::with_capacity(level.len() + 2 + len);
450 full.extend_from_slice(level.as_bytes());
451 full.push(b':');
452 full.push(b' ');
453 full.extend_from_slice(text);
454 if let Some(handler) = globals::get_generic_error_func() {
455 let ctx = globals::get_generic_error_ctx();
456 let mut cmsg = full.clone();
457 cmsg.push(0);
458 handler(ctx, cmsg.as_ptr() as *const c_char);
459 } else {
460 let _ = libc::write(2, full.as_ptr() as *const libc::c_void, full.len());
462 }
463}
464
465#[cfg(target_arch = "x86_64")]
479#[inline]
480unsafe fn ch_call0(handler: xmlGenericErrorFunc, data: *mut c_void, fmt: *const c_char) {
481 unsafe {
487 core::arch::asm!(
488 "xor edx, edx",
489 "xor ecx, ecx",
490 "call {h}",
491 h = in(reg) handler as usize,
492 in("rdi") data,
493 in("rsi") fmt,
494 out("rdx") _, out("rcx") _,
495 lateout("rax") _, lateout("r8") _, lateout("r9") _, lateout("r10") _, lateout("r11") _,
496 );
497 }
498}
499
500#[cfg(target_arch = "x86_64")]
502#[inline]
503unsafe fn ch_call1(handler: xmlGenericErrorFunc, data: *mut c_void, fmt: *const c_char, a1: usize) {
504 unsafe {
507 core::arch::asm!(
508 "xor ecx, ecx",
509 "call {h}",
510 h = in(reg) handler as usize,
511 in("rdi") data,
512 in("rsi") fmt,
513 in("rdx") a1,
514 out("rcx") _,
515 lateout("rax") _, lateout("r8") _, lateout("r9") _, lateout("r10") _, lateout("r11") _,
516 );
517 }
518}
519
520#[cfg(target_arch = "x86_64")]
522#[inline]
523unsafe fn ch_call2(
524 handler: xmlGenericErrorFunc,
525 data: *mut c_void,
526 fmt: *const c_char,
527 a1: usize,
528 a2: usize,
529) {
530 unsafe {
532 core::arch::asm!(
533 "call {h}",
534 h = in(reg) handler as usize,
535 in("rdi") data,
536 in("rsi") fmt,
537 in("rdx") a1,
538 in("rcx") a2,
539 lateout("rax") _, lateout("r8") _, lateout("r9") _, lateout("r10") _, lateout("r11") _,
540 );
541 }
542}
543
544#[cfg(target_arch = "x86_64")]
557unsafe fn format_error_streamed(
558 domain: c_int,
559 code: c_int,
560 level: c_int,
561 message: *const c_char,
562 file: *const c_char,
563 line: c_int,
564 source_window: Option<(&[u8], usize)>,
565 enc_bytes: Option<[u8; 4]>,
566) {
567 let Some(handler) = globals::get_generic_error_func() else {
569 return;
570 };
571 let data = globals::get_generic_error_ctx();
572
573 if !file.is_null() {
575 ch_call2(
576 handler,
577 data,
578 b"%s:%d: \0".as_ptr() as *const c_char,
579 file as usize,
580 line as usize,
581 );
582 } else if line != 0
583 && (domain == XML_FROM_PARSER
584 || domain == XML_FROM_SCHEMASV
585 || domain == XML_FROM_SCHEMASP
586 || domain == XML_FROM_DTD
587 || domain == XML_FROM_RELAXNGP
588 || domain == XML_FROM_RELAXNGV)
589 {
590 ch_call1(
591 handler,
592 data,
593 b"Entity: line %d: \0".as_ptr() as *const c_char,
594 line as usize,
595 );
596 }
597
598 let dom: &[u8] = match domain {
600 XML_FROM_PARSER => b"parser \0",
601 XML_FROM_NAMESPACE => b"namespace \0",
602 XML_FROM_DTD | XML_FROM_VALID => b"validity \0",
603 XML_FROM_HTML => b"HTML parser \0",
604 XML_FROM_MEMORY => b"memory \0",
605 XML_FROM_OUTPUT => b"output \0",
606 XML_FROM_IO => b"I/O \0",
607 XML_FROM_XINCLUDE => b"XInclude \0",
608 XML_FROM_XPATH => b"XPath \0",
609 XML_FROM_XPOINTER => b"parser \0",
610 XML_FROM_REGEXP => b"regexp \0",
611 XML_FROM_MODULE => b"module \0",
612 XML_FROM_SCHEMASV => b"Schemas validity \0",
613 XML_FROM_SCHEMASP => b"Schemas parser \0",
614 XML_FROM_RELAXNGP => b"Relax-NG parser \0",
615 XML_FROM_RELAXNGV => b"Relax-NG validity \0",
616 XML_FROM_CATALOG => b"Catalog \0",
617 XML_FROM_C14N => b"C14N \0",
618 XML_FROM_XSLT => b"XSLT \0",
619 XML_FROM_I18N => b"encoding \0",
620 XML_FROM_SCHEMATRONV => b"schematron \0",
621 XML_FROM_BUFFER => b"internal buffer \0",
622 XML_FROM_URI => b"URI \0",
623 _ => b"\0",
624 };
625 if !dom.is_empty() && dom[0] != 0 {
626 ch_call0(handler, data, dom.as_ptr() as *const c_char);
627 }
628
629 let lvl: &[u8] = if level == XML_ERR_NONE as c_int {
631 b": \0"
632 } else if level == XML_ERR_WARNING as c_int {
633 b"warning : \0"
634 } else if level == XML_ERR_ERROR as c_int || level == XML_ERR_FATAL as c_int {
635 b"error : \0"
636 } else {
637 b"\0"
638 };
639 if !lvl.is_empty() && lvl[0] != 0 {
640 ch_call0(handler, data, lvl.as_ptr() as *const c_char);
641 }
642
643 if !message.is_null() {
645 let msg = message as *const u8;
646 let mut len = 0usize;
647 while unsafe { *msg.add(len) } != 0 {
648 len += 1;
649 }
650 let ends_nl = len > 0 && unsafe { *msg.add(len - 1) } == b'\n';
651 let fmt: &[u8] = if ends_nl { b"%s\0" } else { b"%s\n\0" };
652 ch_call1(handler, data, fmt.as_ptr() as *const c_char, msg as usize);
653 }
654
655 if code == XML_ERR_INVALID_ENCODING {
658 if let Some(bytes) = enc_bytes {
659 ch_call0(handler, data, b"Bytes:\0".as_ptr() as *const c_char);
660 for b in bytes {
661 let hex = format!(" 0x{:02X}\0", b);
663 ch_call0(handler, data, hex.as_ptr() as *const c_char);
664 }
665 ch_call0(handler, data, b"\n\0".as_ptr() as *const c_char);
666 }
667 }
668
669 if let Some((window, caret)) = source_window {
671 let mut win = window.to_vec();
672 win.push(0);
673 ch_call1(
674 handler,
675 data,
676 b"%s\n\0".as_ptr() as *const c_char,
677 win.as_ptr() as usize,
678 );
679 let mut caret_line = Vec::with_capacity(caret + 2);
680 for &b in window.iter().take(caret) {
681 caret_line.push(if b == b'\t' { b'\t' } else { b' ' });
682 }
683 caret_line.push(b'^');
684 caret_line.push(0);
685 ch_call1(
686 handler,
687 data,
688 b"%s\n\0".as_ptr() as *const c_char,
689 caret_line.as_ptr() as usize,
690 );
691 }
692}
693
694#[derive(Clone, Copy)]
697pub enum GenericDelivery {
698 Custom(xmlGenericErrorFunc, *mut c_void),
700 Stream,
703 None,
705}
706
707#[cfg(target_arch = "x86_64")]
729pub unsafe fn raise_error_streamed(
730 ctxt: *mut c_void,
731 domain: c_int,
732 code: c_int,
733 level: c_int,
734 file: *const c_char,
735 line: c_int,
736 col: c_int,
737 str1: *const c_char,
738 str2: *const c_char,
739 str3: *const c_char,
740 int1: c_int,
741 msg: *const c_char,
742 source_window: Option<(&[u8], usize)>,
743 enc_bytes: Option<[u8; 4]>,
744 delivery: GenericDelivery,
745) {
746 if level == xmlErrorLevel::XML_ERR_WARNING as c_int
749 && unsafe { crate::abi::data_globals::xmlGetWarningsDefaultValue } == 0
750 {
751 return;
752 }
753
754 let formatted_msg = format_error_message(domain, code, msg, str1, str2, str3);
756 let file_copy = if file.is_null() {
757 ptr::null_mut()
758 } else {
759 crate::abi::allocator::xmlMemStrdupImpl(file) as *mut c_char
760 };
761 let str1_copy = if str1.is_null() {
762 ptr::null_mut()
763 } else {
764 crate::abi::allocator::xmlMemStrdupImpl(str1) as *mut c_char
765 };
766 let str2_copy = if str2.is_null() {
767 ptr::null_mut()
768 } else {
769 crate::abi::allocator::xmlMemStrdupImpl(str2) as *mut c_char
770 };
771 let str3_copy = if str3.is_null() {
772 ptr::null_mut()
773 } else {
774 crate::abi::allocator::xmlMemStrdupImpl(str3) as *mut c_char
775 };
776 let err = _xmlError {
777 domain,
778 code,
779 message: formatted_msg,
780 level,
781 file: file_copy,
782 line,
783 str1: str1_copy,
784 str2: str2_copy,
785 str3: str3_copy,
786 int1,
787 int2: col,
788 ctxt,
789 node: ptr::null_mut(),
790 };
791
792 globals::set_last_error(err);
793
794 if let Some(handler) = globals::get_structured_error_func() {
796 let ctx = globals::get_structured_error_ctx();
797 let err_ref = globals::get_last_error();
798 if !err_ref.is_null() {
799 handler(ctx, err_ref as *const _xmlError);
800 }
801 return;
802 }
803
804 match delivery {
805 GenericDelivery::Custom(channel, ctx) => {
806 if !msg.is_null() {
807 unsafe { channel(ctx, msg) };
809 }
810 }
811 GenericDelivery::Stream => {
812 if globals::get_generic_error_func().is_some() {
813 unsafe {
814 format_error_streamed(
815 domain,
816 code,
817 level,
818 formatted_msg,
819 file,
820 line,
821 source_window,
822 enc_bytes,
823 )
824 };
825 }
826 }
827 GenericDelivery::None => {}
828 }
829}
830
831#[no_mangle]
838pub unsafe extern "C" fn xmlParserError(ctx: *mut c_void, msg: *const c_char) {
839 let _ = ctx;
840 unsafe { emit_legacy_message("error", msg) };
841}
842
843#[no_mangle]
845pub unsafe extern "C" fn xmlParserWarning(ctx: *mut c_void, msg: *const c_char) {
846 let _ = ctx;
847 unsafe { emit_legacy_message("warning", msg) };
848}
849
850#[no_mangle]
852pub unsafe extern "C" fn xmlParserValidityError(ctx: *mut c_void, msg: *const c_char) {
853 let _ = ctx;
854 unsafe { emit_legacy_message("validity error", msg) };
855}
856
857#[no_mangle]
859pub unsafe extern "C" fn xmlParserValidityWarning(ctx: *mut c_void, msg: *const c_char) {
860 let _ = ctx;
861 unsafe { emit_legacy_message("validity warning", msg) };
862}
863
864#[cfg(test)]
869mod tests {
870 use super::*;
871 use crate::abi::allocator;
872 use core::ffi::c_void;
873
874 #[test]
875 fn test_error_default_reset() {
876 unsafe {
877 let mut err = _xmlError {
878 domain: XML_FROM_PARSER,
879 code: XML_ERR_NO_MEMORY,
880 message: ptr::null_mut(),
881 level: XML_ERR_ERROR as c_int,
882 file: ptr::null_mut(),
883 line: 42,
884 str1: ptr::null_mut(),
885 str2: ptr::null_mut(),
886 str3: ptr::null_mut(),
887 int1: 0,
888 int2: 0,
889 ctxt: ptr::null_mut(),
890 node: ptr::null_mut(),
891 };
892
893 reset_error(&mut err);
894 assert_eq!(err.domain, XML_FROM_NONE);
895 assert_eq!(err.code, XML_ERR_OK as c_int);
896 assert_eq!(err.level, XML_ERR_NONE as c_int);
897 assert_eq!(err.line, 0);
898 }
899 }
900
901 #[test]
902 fn test_copy_error() {
903 unsafe {
904 let from = _xmlError {
905 domain: XML_FROM_PARSER,
906 code: XML_ERR_NO_MEMORY,
907 message: ptr::null_mut(),
908 level: XML_ERR_FATAL as c_int,
909 file: ptr::null_mut(),
910 line: 100,
911 str1: ptr::null_mut(),
912 str2: ptr::null_mut(),
913 str3: ptr::null_mut(),
914 int1: 1,
915 int2: 2,
916 ctxt: ptr::null_mut(),
917 node: ptr::null_mut(),
918 };
919 let mut to = _xmlError {
920 domain: XML_FROM_NONE,
921 code: XML_ERR_OK as c_int,
922 message: ptr::null_mut(),
923 level: XML_ERR_NONE as c_int,
924 file: ptr::null_mut(),
925 line: 0,
926 str1: ptr::null_mut(),
927 str2: ptr::null_mut(),
928 str3: ptr::null_mut(),
929 int1: 0,
930 int2: 0,
931 ctxt: ptr::null_mut(),
932 node: ptr::null_mut(),
933 };
934
935 let result = copy_error(&from, &mut to);
936 assert_eq!(result, 0);
937 assert_eq!(to.domain, XML_FROM_PARSER);
938 assert_eq!(to.code, XML_ERR_NO_MEMORY);
939 assert_eq!(to.level, XML_ERR_FATAL as c_int);
940 assert_eq!(to.line, 100);
941 assert_eq!(to.int1, 1);
942 assert_eq!(to.int2, 2);
943 }
944 }
945
946 #[test]
947 fn test_raise_and_get_last_error() {
948 unsafe {
949 reset_last_error();
950 assert!(get_last_error().is_null());
951
952 let file = b"test.xml\0" as *const u8 as *const c_char;
953 let str1 = b"element\0" as *const u8 as *const c_char;
954
955 raise_error(
956 ptr::null_mut(),
957 ptr::null_mut(),
958 ptr::null_mut(),
959 ptr::null_mut(),
960 ptr::null_mut(),
961 XML_FROM_PARSER,
962 XML_ERR_TAG_NAME_MISMATCH,
963 XML_ERR_ERROR as c_int,
964 file,
965 10,
966 str1,
967 ptr::null(),
968 ptr::null(),
969 0,
970 0,
971 ptr::null(),
972 );
973
974 let last = get_last_error();
975 assert!(!last.is_null());
976 assert_eq!((*last).domain, XML_FROM_PARSER);
977 assert_eq!((*last).code, XML_ERR_TAG_NAME_MISMATCH);
978 assert_eq!((*last).level, XML_ERR_ERROR as c_int);
979 assert_eq!((*last).line, 10);
980
981 let last_file = (*last).file;
983 assert!(!last_file.is_null());
984
985 reset_last_error();
986 assert!(get_last_error().is_null());
987 }
988 }
989
990 #[test]
991 fn test_structured_error_callback() {
992 unsafe {
993 reset_last_error();
994
995 let mut captured_domain: c_int = 0;
997 let captured_ptr = &mut captured_domain as *mut c_int as *mut c_void;
998
999 extern "C" fn test_handler(ctx: *mut c_void, _err: *const _xmlError) {
1002 unsafe {
1004 let captured = &mut *(ctx as *mut c_int);
1005 *captured = 42;
1006 }
1007 }
1008
1009 set_structured_error_func(captured_ptr, Some(test_handler as xmlStructuredErrorFunc));
1010
1011 raise_error(
1012 ptr::null_mut(),
1013 ptr::null_mut(),
1014 ptr::null_mut(),
1015 ptr::null_mut(),
1016 ptr::null_mut(),
1017 XML_FROM_PARSER,
1018 XML_ERR_OK as c_int,
1019 XML_ERR_WARNING as c_int,
1020 ptr::null(),
1021 0,
1022 ptr::null(),
1023 ptr::null(),
1024 ptr::null(),
1025 0,
1026 0,
1027 ptr::null(),
1028 );
1029
1030 assert_eq!(captured_domain, 42);
1031
1032 set_structured_error_func(ptr::null_mut(), None);
1034 reset_last_error();
1035 }
1036 }
1037
1038 #[test]
1039 fn test_format_error_message() {
1040 unsafe {
1041 let msg = b"test error\0" as *const u8 as *const c_char;
1043 let formatted = format_error_message(
1044 XML_FROM_NONE,
1045 XML_ERR_OK as c_int,
1046 msg,
1047 ptr::null(),
1048 ptr::null(),
1049 ptr::null(),
1050 );
1051 assert!(!formatted.is_null());
1052 let formatted_str = std::ffi::CStr::from_ptr(formatted);
1053 assert_eq!(formatted_str.to_bytes(), b"test error");
1054
1055 allocator::xmlFreeImpl(formatted as *mut c_void);
1057
1058 let str1 = b"foo\0" as *const u8 as *const c_char;
1060 let formatted2 = format_error_message(
1061 XML_FROM_PARSER,
1062 XML_ERR_OK as c_int,
1063 ptr::null(),
1064 str1,
1065 ptr::null(),
1066 ptr::null(),
1067 );
1068 assert!(!formatted2.is_null());
1069 allocator::xmlFreeImpl(formatted2 as *mut c_void);
1070 }
1071 }
1072}