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() {
413 let ctx = globals::get_structured_error_ctx();
414 let err_ref = globals::get_last_error();
415 if !err_ref.is_null() {
416 handler(ctx, err_ref as *const _xmlError);
417 }
418 } else if let Some(handler) = globals::get_generic_error_func() {
419 if level != 0 {
420 let ctx = globals::get_generic_error_ctx();
421 if !formatted_msg.is_null() {
422 handler(ctx, formatted_msg as *const core::ffi::c_char);
423 } else if !msg.is_null() {
424 handler(ctx, msg);
425 }
426 }
427 }
428
429 }
435
436unsafe fn emit_legacy_message(level: &str, msg: *const c_char) {
442 if msg.is_null() {
443 return;
444 }
445 let len = libc::strlen(msg) as usize;
446 let text = core::slice::from_raw_parts(msg as *const u8, len);
447 let mut full = Vec::with_capacity(level.len() + 2 + len);
448 full.extend_from_slice(level.as_bytes());
449 full.push(b':');
450 full.push(b' ');
451 full.extend_from_slice(text);
452 if let Some(handler) = globals::get_generic_error_func() {
453 let ctx = globals::get_generic_error_ctx();
454 let mut cmsg = full.clone();
455 cmsg.push(0);
456 handler(ctx, cmsg.as_ptr() as *const c_char);
457 } else {
458 let _ = libc::write(2, full.as_ptr() as *const libc::c_void, full.len());
460 }
461}
462
463#[cfg(target_arch = "x86_64")]
477#[inline]
478unsafe fn ch_call0(handler: xmlGenericErrorFunc, data: *mut c_void, fmt: *const c_char) {
479 unsafe {
485 core::arch::asm!(
486 "xor edx, edx",
487 "xor ecx, ecx",
488 "call {h}",
489 h = in(reg) handler as usize,
490 in("rdi") data,
491 in("rsi") fmt,
492 out("rdx") _, out("rcx") _,
493 lateout("rax") _, lateout("r8") _, lateout("r9") _, lateout("r10") _, lateout("r11") _,
494 );
495 }
496}
497
498#[cfg(target_arch = "x86_64")]
500#[inline]
501unsafe fn ch_call1(handler: xmlGenericErrorFunc, data: *mut c_void, fmt: *const c_char, a1: usize) {
502 unsafe {
505 core::arch::asm!(
506 "xor ecx, ecx",
507 "call {h}",
508 h = in(reg) handler as usize,
509 in("rdi") data,
510 in("rsi") fmt,
511 in("rdx") a1,
512 out("rcx") _,
513 lateout("rax") _, lateout("r8") _, lateout("r9") _, lateout("r10") _, lateout("r11") _,
514 );
515 }
516}
517
518#[cfg(target_arch = "x86_64")]
520#[inline]
521unsafe fn ch_call2(
522 handler: xmlGenericErrorFunc,
523 data: *mut c_void,
524 fmt: *const c_char,
525 a1: usize,
526 a2: usize,
527) {
528 unsafe {
530 core::arch::asm!(
531 "call {h}",
532 h = in(reg) handler as usize,
533 in("rdi") data,
534 in("rsi") fmt,
535 in("rdx") a1,
536 in("rcx") a2,
537 lateout("rax") _, lateout("r8") _, lateout("r9") _, lateout("r10") _, lateout("r11") _,
538 );
539 }
540}
541
542#[cfg(target_arch = "x86_64")]
555unsafe fn format_error_streamed(
556 domain: c_int,
557 code: c_int,
558 level: c_int,
559 message: *const c_char,
560 file: *const c_char,
561 line: c_int,
562 source_window: Option<(&[u8], usize)>,
563 enc_bytes: Option<[u8; 4]>,
564) {
565 let Some(handler) = globals::get_generic_error_func() else {
567 return;
568 };
569 let data = globals::get_generic_error_ctx();
570
571 if !file.is_null() {
573 ch_call2(
574 handler,
575 data,
576 b"%s:%d: \0".as_ptr() as *const c_char,
577 file as usize,
578 line as usize,
579 );
580 } else if line != 0
581 && (domain == XML_FROM_PARSER
582 || domain == XML_FROM_SCHEMASV
583 || domain == XML_FROM_SCHEMASP
584 || domain == XML_FROM_DTD
585 || domain == XML_FROM_RELAXNGP
586 || domain == XML_FROM_RELAXNGV)
587 {
588 ch_call1(
589 handler,
590 data,
591 b"Entity: line %d: \0".as_ptr() as *const c_char,
592 line as usize,
593 );
594 }
595
596 let dom: &[u8] = match domain {
598 XML_FROM_PARSER => b"parser \0",
599 XML_FROM_NAMESPACE => b"namespace \0",
600 XML_FROM_DTD | XML_FROM_VALID => b"validity \0",
601 XML_FROM_HTML => b"HTML parser \0",
602 XML_FROM_MEMORY => b"memory \0",
603 XML_FROM_OUTPUT => b"output \0",
604 XML_FROM_IO => b"I/O \0",
605 XML_FROM_XINCLUDE => b"XInclude \0",
606 XML_FROM_XPATH => b"XPath \0",
607 XML_FROM_XPOINTER => b"parser \0",
608 XML_FROM_REGEXP => b"regexp \0",
609 XML_FROM_MODULE => b"module \0",
610 XML_FROM_SCHEMASV => b"Schemas validity \0",
611 XML_FROM_SCHEMASP => b"Schemas parser \0",
612 XML_FROM_RELAXNGP => b"Relax-NG parser \0",
613 XML_FROM_RELAXNGV => b"Relax-NG validity \0",
614 XML_FROM_CATALOG => b"Catalog \0",
615 XML_FROM_C14N => b"C14N \0",
616 XML_FROM_XSLT => b"XSLT \0",
617 XML_FROM_I18N => b"encoding \0",
618 XML_FROM_SCHEMATRONV => b"schematron \0",
619 XML_FROM_BUFFER => b"internal buffer \0",
620 XML_FROM_URI => b"URI \0",
621 _ => b"\0",
622 };
623 if !dom.is_empty() && dom[0] != 0 {
624 ch_call0(handler, data, dom.as_ptr() as *const c_char);
625 }
626
627 let lvl: &[u8] = if level == XML_ERR_NONE as c_int {
629 b": \0"
630 } else if level == XML_ERR_WARNING as c_int {
631 b"warning : \0"
632 } else if level == XML_ERR_ERROR as c_int || level == XML_ERR_FATAL as c_int {
633 b"error : \0"
634 } else {
635 b"\0"
636 };
637 if !lvl.is_empty() && lvl[0] != 0 {
638 ch_call0(handler, data, lvl.as_ptr() as *const c_char);
639 }
640
641 if !message.is_null() {
643 let msg = message as *const u8;
644 let mut len = 0usize;
645 while unsafe { *msg.add(len) } != 0 {
646 len += 1;
647 }
648 let ends_nl = len > 0 && unsafe { *msg.add(len - 1) } == b'\n';
649 let fmt: &[u8] = if ends_nl { b"%s\0" } else { b"%s\n\0" };
650 ch_call1(handler, data, fmt.as_ptr() as *const c_char, msg as usize);
651 }
652
653 if code == XML_ERR_INVALID_ENCODING {
656 if let Some(bytes) = enc_bytes {
657 ch_call0(handler, data, b"Bytes:\0".as_ptr() as *const c_char);
658 for b in bytes {
659 let hex = format!(" 0x{:02X}\0", b);
661 ch_call0(handler, data, hex.as_ptr() as *const c_char);
662 }
663 ch_call0(handler, data, b"\n\0".as_ptr() as *const c_char);
664 }
665 }
666
667 if let Some((window, caret)) = source_window {
669 let mut win = window.to_vec();
670 win.push(0);
671 ch_call1(
672 handler,
673 data,
674 b"%s\n\0".as_ptr() as *const c_char,
675 win.as_ptr() as usize,
676 );
677 let mut caret_line = Vec::with_capacity(caret + 2);
678 for &b in window.iter().take(caret) {
679 caret_line.push(if b == b'\t' { b'\t' } else { b' ' });
680 }
681 caret_line.push(b'^');
682 caret_line.push(0);
683 ch_call1(
684 handler,
685 data,
686 b"%s\n\0".as_ptr() as *const c_char,
687 caret_line.as_ptr() as usize,
688 );
689 }
690}
691
692#[derive(Clone, Copy)]
695pub enum GenericDelivery {
696 Custom(xmlGenericErrorFunc, *mut c_void),
698 Stream,
701 None,
703}
704
705pub unsafe fn raise_error_streamed(
727 ctxt: *mut c_void,
728 domain: c_int,
729 code: c_int,
730 level: c_int,
731 file: *const c_char,
732 line: c_int,
733 col: c_int,
734 str1: *const c_char,
735 str2: *const c_char,
736 str3: *const c_char,
737 int1: c_int,
738 msg: *const c_char,
739 source_window: Option<(&[u8], usize)>,
740 enc_bytes: Option<[u8; 4]>,
741 delivery: GenericDelivery,
742) {
743 #[cfg(not(target_arch = "x86_64"))]
749 {
750 raise_error(
751 ctxt,
752 ptr::null_mut(),
753 ptr::null_mut(),
754 ptr::null_mut(),
755 ptr::null_mut(),
756 domain,
757 code,
758 level,
759 file,
760 line,
761 str1,
762 str2,
763 str3,
764 int1,
765 col,
766 msg,
767 );
768 return;
769 }
770
771 #[cfg(target_arch = "x86_64")]
772 {
773 if level == xmlErrorLevel::XML_ERR_WARNING as c_int
776 && unsafe { crate::abi::data_globals::xmlGetWarningsDefaultValue } == 0
777 {
778 return;
779 }
780
781 raise_error_streamed_x86_64(
782 ctxt,
783 domain,
784 code,
785 level,
786 file,
787 line,
788 col,
789 str1,
790 str2,
791 str3,
792 int1,
793 msg,
794 source_window,
795 enc_bytes,
796 delivery,
797 );
798 }
799}
800
801#[cfg(target_arch = "x86_64")]
803unsafe fn raise_error_streamed_x86_64(
804 ctxt: *mut c_void,
805 domain: c_int,
806 code: c_int,
807 level: c_int,
808 file: *const c_char,
809 line: c_int,
810 col: c_int,
811 str1: *const c_char,
812 str2: *const c_char,
813 str3: *const c_char,
814 int1: c_int,
815 msg: *const c_char,
816 source_window: Option<(&[u8], usize)>,
817 enc_bytes: Option<[u8; 4]>,
818 delivery: GenericDelivery,
819) {
820 if level == xmlErrorLevel::XML_ERR_WARNING as c_int
823 && unsafe { crate::abi::data_globals::xmlGetWarningsDefaultValue } == 0
824 {
825 return;
826 }
827
828 let formatted_msg = format_error_message(domain, code, msg, str1, str2, str3);
830 let file_copy = if file.is_null() {
831 ptr::null_mut()
832 } else {
833 crate::abi::allocator::xmlMemStrdupImpl(file) as *mut c_char
834 };
835 let str1_copy = if str1.is_null() {
836 ptr::null_mut()
837 } else {
838 crate::abi::allocator::xmlMemStrdupImpl(str1) as *mut c_char
839 };
840 let str2_copy = if str2.is_null() {
841 ptr::null_mut()
842 } else {
843 crate::abi::allocator::xmlMemStrdupImpl(str2) as *mut c_char
844 };
845 let str3_copy = if str3.is_null() {
846 ptr::null_mut()
847 } else {
848 crate::abi::allocator::xmlMemStrdupImpl(str3) as *mut c_char
849 };
850 let err = _xmlError {
851 domain,
852 code,
853 message: formatted_msg,
854 level,
855 file: file_copy,
856 line,
857 str1: str1_copy,
858 str2: str2_copy,
859 str3: str3_copy,
860 int1,
861 int2: col,
862 ctxt,
863 node: ptr::null_mut(),
864 };
865
866 globals::set_last_error(err);
867
868 if let Some(handler) = globals::get_structured_error_func() {
870 let ctx = globals::get_structured_error_ctx();
871 let err_ref = globals::get_last_error();
872 if !err_ref.is_null() {
873 handler(ctx, err_ref as *const _xmlError);
874 }
875 return;
876 }
877
878 match delivery {
879 GenericDelivery::Custom(channel, ctx) => {
880 if !msg.is_null() {
881 unsafe { channel(ctx, msg) };
883 }
884 }
885 GenericDelivery::Stream => {
886 if globals::get_generic_error_func().is_some() {
887 unsafe {
888 format_error_streamed(
889 domain,
890 code,
891 level,
892 formatted_msg,
893 file,
894 line,
895 source_window,
896 enc_bytes,
897 )
898 };
899 }
900 }
901 GenericDelivery::None => {}
902 }
903}
904
905#[no_mangle]
912pub unsafe extern "C" fn xmlParserError(ctx: *mut c_void, msg: *const c_char) {
913 let _ = ctx;
914 unsafe { emit_legacy_message("error", msg) };
915}
916
917#[no_mangle]
919pub unsafe extern "C" fn xmlParserWarning(ctx: *mut c_void, msg: *const c_char) {
920 let _ = ctx;
921 unsafe { emit_legacy_message("warning", msg) };
922}
923
924#[no_mangle]
926pub unsafe extern "C" fn xmlParserValidityError(ctx: *mut c_void, msg: *const c_char) {
927 let _ = ctx;
928 unsafe { emit_legacy_message("validity error", msg) };
929}
930
931#[no_mangle]
933pub unsafe extern "C" fn xmlParserValidityWarning(ctx: *mut c_void, msg: *const c_char) {
934 let _ = ctx;
935 unsafe { emit_legacy_message("validity warning", msg) };
936}
937
938#[cfg(test)]
943mod tests {
944 use super::*;
945 use crate::abi::allocator;
946 use core::ffi::c_void;
947
948 #[test]
949 fn test_error_default_reset() {
950 unsafe {
951 let mut err = _xmlError {
952 domain: XML_FROM_PARSER,
953 code: XML_ERR_NO_MEMORY,
954 message: ptr::null_mut(),
955 level: XML_ERR_ERROR as c_int,
956 file: ptr::null_mut(),
957 line: 42,
958 str1: ptr::null_mut(),
959 str2: ptr::null_mut(),
960 str3: ptr::null_mut(),
961 int1: 0,
962 int2: 0,
963 ctxt: ptr::null_mut(),
964 node: ptr::null_mut(),
965 };
966
967 reset_error(&mut err);
968 assert_eq!(err.domain, XML_FROM_NONE);
969 assert_eq!(err.code, XML_ERR_OK as c_int);
970 assert_eq!(err.level, XML_ERR_NONE as c_int);
971 assert_eq!(err.line, 0);
972 }
973 }
974
975 #[test]
976 fn test_copy_error() {
977 unsafe {
978 let from = _xmlError {
979 domain: XML_FROM_PARSER,
980 code: XML_ERR_NO_MEMORY,
981 message: ptr::null_mut(),
982 level: XML_ERR_FATAL as c_int,
983 file: ptr::null_mut(),
984 line: 100,
985 str1: ptr::null_mut(),
986 str2: ptr::null_mut(),
987 str3: ptr::null_mut(),
988 int1: 1,
989 int2: 2,
990 ctxt: ptr::null_mut(),
991 node: ptr::null_mut(),
992 };
993 let mut to = _xmlError {
994 domain: XML_FROM_NONE,
995 code: XML_ERR_OK as c_int,
996 message: ptr::null_mut(),
997 level: XML_ERR_NONE as c_int,
998 file: ptr::null_mut(),
999 line: 0,
1000 str1: ptr::null_mut(),
1001 str2: ptr::null_mut(),
1002 str3: ptr::null_mut(),
1003 int1: 0,
1004 int2: 0,
1005 ctxt: ptr::null_mut(),
1006 node: ptr::null_mut(),
1007 };
1008
1009 let result = copy_error(&from, &mut to);
1010 assert_eq!(result, 0);
1011 assert_eq!(to.domain, XML_FROM_PARSER);
1012 assert_eq!(to.code, XML_ERR_NO_MEMORY);
1013 assert_eq!(to.level, XML_ERR_FATAL as c_int);
1014 assert_eq!(to.line, 100);
1015 assert_eq!(to.int1, 1);
1016 assert_eq!(to.int2, 2);
1017 }
1018 }
1019
1020 #[test]
1021 fn test_raise_and_get_last_error() {
1022 unsafe {
1023 reset_last_error();
1024 assert!(get_last_error().is_null());
1025
1026 let file = b"test.xml\0" as *const u8 as *const c_char;
1027 let str1 = b"element\0" as *const u8 as *const c_char;
1028
1029 raise_error(
1030 ptr::null_mut(),
1031 ptr::null_mut(),
1032 ptr::null_mut(),
1033 ptr::null_mut(),
1034 ptr::null_mut(),
1035 XML_FROM_PARSER,
1036 XML_ERR_TAG_NAME_MISMATCH,
1037 XML_ERR_ERROR as c_int,
1038 file,
1039 10,
1040 str1,
1041 ptr::null(),
1042 ptr::null(),
1043 0,
1044 0,
1045 ptr::null(),
1046 );
1047
1048 let last = get_last_error();
1049 assert!(!last.is_null());
1050 assert_eq!((*last).domain, XML_FROM_PARSER);
1051 assert_eq!((*last).code, XML_ERR_TAG_NAME_MISMATCH);
1052 assert_eq!((*last).level, XML_ERR_ERROR as c_int);
1053 assert_eq!((*last).line, 10);
1054
1055 let last_file = (*last).file;
1057 assert!(!last_file.is_null());
1058
1059 reset_last_error();
1060 assert!(get_last_error().is_null());
1061 }
1062 }
1063
1064 #[test]
1065 fn test_structured_error_callback() {
1066 unsafe {
1067 reset_last_error();
1068
1069 let mut captured_domain: c_int = 0;
1071 let captured_ptr = &mut captured_domain as *mut c_int as *mut c_void;
1072
1073 extern "C" fn test_handler(ctx: *mut c_void, _err: *const _xmlError) {
1076 unsafe {
1078 let captured = &mut *(ctx as *mut c_int);
1079 *captured = 42;
1080 }
1081 }
1082
1083 set_structured_error_func(captured_ptr, Some(test_handler as xmlStructuredErrorFunc));
1084
1085 raise_error(
1086 ptr::null_mut(),
1087 ptr::null_mut(),
1088 ptr::null_mut(),
1089 ptr::null_mut(),
1090 ptr::null_mut(),
1091 XML_FROM_PARSER,
1092 XML_ERR_OK as c_int,
1093 XML_ERR_WARNING as c_int,
1094 ptr::null(),
1095 0,
1096 ptr::null(),
1097 ptr::null(),
1098 ptr::null(),
1099 0,
1100 0,
1101 ptr::null(),
1102 );
1103
1104 assert_eq!(captured_domain, 42);
1105
1106 set_structured_error_func(ptr::null_mut(), None);
1108 reset_last_error();
1109 }
1110 }
1111
1112 #[test]
1113 fn test_format_error_message() {
1114 unsafe {
1115 let msg = b"test error\0" as *const u8 as *const c_char;
1117 let formatted = format_error_message(
1118 XML_FROM_NONE,
1119 XML_ERR_OK as c_int,
1120 msg,
1121 ptr::null(),
1122 ptr::null(),
1123 ptr::null(),
1124 );
1125 assert!(!formatted.is_null());
1126 let formatted_str = std::ffi::CStr::from_ptr(formatted);
1127 assert_eq!(formatted_str.to_bytes(), b"test error");
1128
1129 allocator::xmlFreeImpl(formatted as *mut c_void);
1131
1132 let str1 = b"foo\0" as *const u8 as *const c_char;
1134 let formatted2 = format_error_message(
1135 XML_FROM_PARSER,
1136 XML_ERR_OK as c_int,
1137 ptr::null(),
1138 str1,
1139 ptr::null(),
1140 ptr::null(),
1141 );
1142 assert!(!formatted2.is_null());
1143 allocator::xmlFreeImpl(formatted2 as *mut c_void);
1144 }
1145 }
1146}