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