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
707pub unsafe fn raise_error_streamed(
729 ctxt: *mut c_void,
730 domain: c_int,
731 code: c_int,
732 level: c_int,
733 file: *const c_char,
734 line: c_int,
735 col: c_int,
736 str1: *const c_char,
737 str2: *const c_char,
738 str3: *const c_char,
739 int1: c_int,
740 msg: *const c_char,
741 source_window: Option<(&[u8], usize)>,
742 enc_bytes: Option<[u8; 4]>,
743 delivery: GenericDelivery,
744) {
745 #[cfg(not(target_arch = "x86_64"))]
751 {
752 raise_error(
753 ctxt,
754 ptr::null_mut(),
755 ptr::null_mut(),
756 ptr::null_mut(),
757 ptr::null_mut(),
758 domain,
759 code,
760 level,
761 file,
762 line,
763 str1,
764 str2,
765 str3,
766 int1,
767 col,
768 msg,
769 );
770 return;
771 }
772
773 #[cfg(target_arch = "x86_64")]
774 {
775 if level == xmlErrorLevel::XML_ERR_WARNING as c_int
778 && unsafe { crate::abi::data_globals::xmlGetWarningsDefaultValue } == 0
779 {
780 return;
781 }
782
783 raise_error_streamed_x86_64(
784 ctxt,
785 domain,
786 code,
787 level,
788 file,
789 line,
790 col,
791 str1,
792 str2,
793 str3,
794 int1,
795 msg,
796 source_window,
797 enc_bytes,
798 delivery,
799 );
800 }
801}
802
803#[cfg(target_arch = "x86_64")]
805unsafe fn raise_error_streamed_x86_64(
806 ctxt: *mut c_void,
807 domain: c_int,
808 code: c_int,
809 level: c_int,
810 file: *const c_char,
811 line: c_int,
812 col: c_int,
813 str1: *const c_char,
814 str2: *const c_char,
815 str3: *const c_char,
816 int1: c_int,
817 msg: *const c_char,
818 source_window: Option<(&[u8], usize)>,
819 enc_bytes: Option<[u8; 4]>,
820 delivery: GenericDelivery,
821) {
822 if level == xmlErrorLevel::XML_ERR_WARNING as c_int
825 && unsafe { crate::abi::data_globals::xmlGetWarningsDefaultValue } == 0
826 {
827 return;
828 }
829
830 let formatted_msg = format_error_message(domain, code, msg, str1, str2, str3);
832 let file_copy = if file.is_null() {
833 ptr::null_mut()
834 } else {
835 crate::abi::allocator::xmlMemStrdupImpl(file) as *mut c_char
836 };
837 let str1_copy = if str1.is_null() {
838 ptr::null_mut()
839 } else {
840 crate::abi::allocator::xmlMemStrdupImpl(str1) as *mut c_char
841 };
842 let str2_copy = if str2.is_null() {
843 ptr::null_mut()
844 } else {
845 crate::abi::allocator::xmlMemStrdupImpl(str2) as *mut c_char
846 };
847 let str3_copy = if str3.is_null() {
848 ptr::null_mut()
849 } else {
850 crate::abi::allocator::xmlMemStrdupImpl(str3) as *mut c_char
851 };
852 let err = _xmlError {
853 domain,
854 code,
855 message: formatted_msg,
856 level,
857 file: file_copy,
858 line,
859 str1: str1_copy,
860 str2: str2_copy,
861 str3: str3_copy,
862 int1,
863 int2: col,
864 ctxt,
865 node: ptr::null_mut(),
866 };
867
868 globals::set_last_error(err);
869
870 if let Some(handler) = globals::get_structured_error_func() {
872 let ctx = globals::get_structured_error_ctx();
873 let err_ref = globals::get_last_error();
874 if !err_ref.is_null() {
875 handler(ctx, err_ref as *const _xmlError);
876 }
877 return;
878 }
879
880 match delivery {
881 GenericDelivery::Custom(channel, ctx) => {
882 if !msg.is_null() {
883 unsafe { channel(ctx, msg) };
885 }
886 }
887 GenericDelivery::Stream => {
888 if globals::get_generic_error_func().is_some() {
889 unsafe {
890 format_error_streamed(
891 domain,
892 code,
893 level,
894 formatted_msg,
895 file,
896 line,
897 source_window,
898 enc_bytes,
899 )
900 };
901 }
902 }
903 GenericDelivery::None => {}
904 }
905}
906
907#[no_mangle]
914pub unsafe extern "C" fn xmlParserError(ctx: *mut c_void, msg: *const c_char) {
915 let _ = ctx;
916 unsafe { emit_legacy_message("error", msg) };
917}
918
919#[no_mangle]
921pub unsafe extern "C" fn xmlParserWarning(ctx: *mut c_void, msg: *const c_char) {
922 let _ = ctx;
923 unsafe { emit_legacy_message("warning", msg) };
924}
925
926#[no_mangle]
928pub unsafe extern "C" fn xmlParserValidityError(ctx: *mut c_void, msg: *const c_char) {
929 let _ = ctx;
930 unsafe { emit_legacy_message("validity error", msg) };
931}
932
933#[no_mangle]
935pub unsafe extern "C" fn xmlParserValidityWarning(ctx: *mut c_void, msg: *const c_char) {
936 let _ = ctx;
937 unsafe { emit_legacy_message("validity warning", msg) };
938}
939
940#[cfg(test)]
945mod tests {
946 use super::*;
947 use crate::abi::allocator;
948 use core::ffi::c_void;
949
950 #[test]
951 fn test_error_default_reset() {
952 unsafe {
953 let mut err = _xmlError {
954 domain: XML_FROM_PARSER,
955 code: XML_ERR_NO_MEMORY,
956 message: ptr::null_mut(),
957 level: XML_ERR_ERROR as c_int,
958 file: ptr::null_mut(),
959 line: 42,
960 str1: ptr::null_mut(),
961 str2: ptr::null_mut(),
962 str3: ptr::null_mut(),
963 int1: 0,
964 int2: 0,
965 ctxt: ptr::null_mut(),
966 node: ptr::null_mut(),
967 };
968
969 reset_error(&mut err);
970 assert_eq!(err.domain, XML_FROM_NONE);
971 assert_eq!(err.code, XML_ERR_OK as c_int);
972 assert_eq!(err.level, XML_ERR_NONE as c_int);
973 assert_eq!(err.line, 0);
974 }
975 }
976
977 #[test]
978 fn test_copy_error() {
979 unsafe {
980 let from = _xmlError {
981 domain: XML_FROM_PARSER,
982 code: XML_ERR_NO_MEMORY,
983 message: ptr::null_mut(),
984 level: XML_ERR_FATAL as c_int,
985 file: ptr::null_mut(),
986 line: 100,
987 str1: ptr::null_mut(),
988 str2: ptr::null_mut(),
989 str3: ptr::null_mut(),
990 int1: 1,
991 int2: 2,
992 ctxt: ptr::null_mut(),
993 node: ptr::null_mut(),
994 };
995 let mut to = _xmlError {
996 domain: XML_FROM_NONE,
997 code: XML_ERR_OK as c_int,
998 message: ptr::null_mut(),
999 level: XML_ERR_NONE as c_int,
1000 file: ptr::null_mut(),
1001 line: 0,
1002 str1: ptr::null_mut(),
1003 str2: ptr::null_mut(),
1004 str3: ptr::null_mut(),
1005 int1: 0,
1006 int2: 0,
1007 ctxt: ptr::null_mut(),
1008 node: ptr::null_mut(),
1009 };
1010
1011 let result = copy_error(&from, &mut to);
1012 assert_eq!(result, 0);
1013 assert_eq!(to.domain, XML_FROM_PARSER);
1014 assert_eq!(to.code, XML_ERR_NO_MEMORY);
1015 assert_eq!(to.level, XML_ERR_FATAL as c_int);
1016 assert_eq!(to.line, 100);
1017 assert_eq!(to.int1, 1);
1018 assert_eq!(to.int2, 2);
1019 }
1020 }
1021
1022 #[test]
1023 fn test_raise_and_get_last_error() {
1024 unsafe {
1025 reset_last_error();
1026 assert!(get_last_error().is_null());
1027
1028 let file = b"test.xml\0" as *const u8 as *const c_char;
1029 let str1 = b"element\0" as *const u8 as *const c_char;
1030
1031 raise_error(
1032 ptr::null_mut(),
1033 ptr::null_mut(),
1034 ptr::null_mut(),
1035 ptr::null_mut(),
1036 ptr::null_mut(),
1037 XML_FROM_PARSER,
1038 XML_ERR_TAG_NAME_MISMATCH,
1039 XML_ERR_ERROR as c_int,
1040 file,
1041 10,
1042 str1,
1043 ptr::null(),
1044 ptr::null(),
1045 0,
1046 0,
1047 ptr::null(),
1048 );
1049
1050 let last = get_last_error();
1051 assert!(!last.is_null());
1052 assert_eq!((*last).domain, XML_FROM_PARSER);
1053 assert_eq!((*last).code, XML_ERR_TAG_NAME_MISMATCH);
1054 assert_eq!((*last).level, XML_ERR_ERROR as c_int);
1055 assert_eq!((*last).line, 10);
1056
1057 let last_file = (*last).file;
1059 assert!(!last_file.is_null());
1060
1061 reset_last_error();
1062 assert!(get_last_error().is_null());
1063 }
1064 }
1065
1066 #[test]
1067 fn test_structured_error_callback() {
1068 unsafe {
1069 reset_last_error();
1070
1071 let mut captured_domain: c_int = 0;
1073 let captured_ptr = &mut captured_domain as *mut c_int as *mut c_void;
1074
1075 extern "C" fn test_handler(ctx: *mut c_void, _err: *const _xmlError) {
1078 unsafe {
1080 let captured = &mut *(ctx as *mut c_int);
1081 *captured = 42;
1082 }
1083 }
1084
1085 set_structured_error_func(captured_ptr, Some(test_handler as xmlStructuredErrorFunc));
1086
1087 raise_error(
1088 ptr::null_mut(),
1089 ptr::null_mut(),
1090 ptr::null_mut(),
1091 ptr::null_mut(),
1092 ptr::null_mut(),
1093 XML_FROM_PARSER,
1094 XML_ERR_OK as c_int,
1095 XML_ERR_WARNING as c_int,
1096 ptr::null(),
1097 0,
1098 ptr::null(),
1099 ptr::null(),
1100 ptr::null(),
1101 0,
1102 0,
1103 ptr::null(),
1104 );
1105
1106 assert_eq!(captured_domain, 42);
1107
1108 set_structured_error_func(ptr::null_mut(), None);
1110 reset_last_error();
1111 }
1112 }
1113
1114 #[test]
1115 fn test_format_error_message() {
1116 unsafe {
1117 let msg = b"test error\0" as *const u8 as *const c_char;
1119 let formatted = format_error_message(
1120 XML_FROM_NONE,
1121 XML_ERR_OK as c_int,
1122 msg,
1123 ptr::null(),
1124 ptr::null(),
1125 ptr::null(),
1126 );
1127 assert!(!formatted.is_null());
1128 let formatted_str = std::ffi::CStr::from_ptr(formatted);
1129 assert_eq!(formatted_str.to_bytes(), b"test error");
1130
1131 allocator::xmlFreeImpl(formatted as *mut c_void);
1133
1134 let str1 = b"foo\0" as *const u8 as *const c_char;
1136 let formatted2 = format_error_message(
1137 XML_FROM_PARSER,
1138 XML_ERR_OK as c_int,
1139 ptr::null(),
1140 str1,
1141 ptr::null(),
1142 ptr::null(),
1143 );
1144 assert!(!formatted2.is_null());
1145 allocator::xmlFreeImpl(formatted2 as *mut c_void);
1146 }
1147 }
1148}