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::xmlMemStrdup(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::xmlMalloc(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 err = _xmlError {
370 domain,
371 code,
372 message: formatted_msg,
373 level,
374 file: file as *mut c_char,
375 line,
376 str1: str1 as *mut c_char,
377 str2: str2 as *mut c_char,
378 str3: str3 as *mut c_char,
379 int1,
380 int2,
381 ctxt,
382 node: ptr::null_mut(),
383 };
384
385 globals::set_last_error(err);
386
387 if let Some(handler) = globals::get_structured_error_func() {
389 let ctx = globals::get_structured_error_ctx();
390 let err_ref = globals::get_last_error();
391 if !err_ref.is_null() {
392 handler(ctx, err_ref as *const _xmlError);
393 }
394 }
395
396 if let Some(handler) = globals::get_generic_error_func() {
398 if level != 0 {
399 let ctx = globals::get_generic_error_ctx();
400 if !formatted_msg.is_null() {
401 handler(ctx, formatted_msg as *const core::ffi::c_char);
402 } else if !msg.is_null() {
403 handler(ctx, msg);
404 }
405 }
406 }
407
408 }
414
415unsafe fn emit_legacy_message(level: &str, msg: *const c_char) {
421 if msg.is_null() {
422 return;
423 }
424 let len = libc::strlen(msg) as usize;
425 let text = core::slice::from_raw_parts(msg as *const u8, len);
426 let mut full = Vec::with_capacity(level.len() + 2 + len);
427 full.extend_from_slice(level.as_bytes());
428 full.push(b':');
429 full.push(b' ');
430 full.extend_from_slice(text);
431 if let Some(handler) = globals::get_generic_error_func() {
432 let ctx = globals::get_generic_error_ctx();
433 let mut cmsg = full.clone();
434 cmsg.push(0);
435 handler(ctx, cmsg.as_ptr() as *const c_char);
436 } else {
437 let _ = libc::write(2, full.as_ptr() as *const libc::c_void, full.len());
439 }
440}
441
442#[no_mangle]
453pub unsafe extern "C" fn xmlParserError(ctx: *mut c_void, msg: *const c_char) {
454 let _ = ctx;
455 unsafe { emit_legacy_message("error", msg) };
456}
457
458#[no_mangle]
460pub unsafe extern "C" fn xmlParserWarning(ctx: *mut c_void, msg: *const c_char) {
461 let _ = ctx;
462 unsafe { emit_legacy_message("warning", msg) };
463}
464
465#[no_mangle]
467pub unsafe extern "C" fn xmlParserValidityError(ctx: *mut c_void, msg: *const c_char) {
468 let _ = ctx;
469 unsafe { emit_legacy_message("validity error", msg) };
470}
471
472#[no_mangle]
474pub unsafe extern "C" fn xmlParserValidityWarning(ctx: *mut c_void, msg: *const c_char) {
475 let _ = ctx;
476 unsafe { emit_legacy_message("validity warning", msg) };
477}
478
479#[cfg(test)]
484mod tests {
485 use super::*;
486 use crate::abi::allocator;
487 use core::ffi::c_void;
488
489 #[test]
490 fn test_error_default_reset() {
491 unsafe {
492 let mut err = _xmlError {
493 domain: XML_FROM_PARSER,
494 code: XML_ERR_NO_MEMORY,
495 message: ptr::null_mut(),
496 level: XML_ERR_ERROR as c_int,
497 file: ptr::null_mut(),
498 line: 42,
499 str1: ptr::null_mut(),
500 str2: ptr::null_mut(),
501 str3: ptr::null_mut(),
502 int1: 0,
503 int2: 0,
504 ctxt: ptr::null_mut(),
505 node: ptr::null_mut(),
506 };
507
508 reset_error(&mut err);
509 assert_eq!(err.domain, XML_FROM_NONE);
510 assert_eq!(err.code, XML_ERR_OK as c_int);
511 assert_eq!(err.level, XML_ERR_NONE as c_int);
512 assert_eq!(err.line, 0);
513 }
514 }
515
516 #[test]
517 fn test_copy_error() {
518 unsafe {
519 let from = _xmlError {
520 domain: XML_FROM_PARSER,
521 code: XML_ERR_NO_MEMORY,
522 message: ptr::null_mut(),
523 level: XML_ERR_FATAL as c_int,
524 file: ptr::null_mut(),
525 line: 100,
526 str1: ptr::null_mut(),
527 str2: ptr::null_mut(),
528 str3: ptr::null_mut(),
529 int1: 1,
530 int2: 2,
531 ctxt: ptr::null_mut(),
532 node: ptr::null_mut(),
533 };
534 let mut to = _xmlError {
535 domain: XML_FROM_NONE,
536 code: XML_ERR_OK as c_int,
537 message: ptr::null_mut(),
538 level: XML_ERR_NONE as c_int,
539 file: ptr::null_mut(),
540 line: 0,
541 str1: ptr::null_mut(),
542 str2: ptr::null_mut(),
543 str3: ptr::null_mut(),
544 int1: 0,
545 int2: 0,
546 ctxt: ptr::null_mut(),
547 node: ptr::null_mut(),
548 };
549
550 let result = copy_error(&from, &mut to);
551 assert_eq!(result, 0);
552 assert_eq!(to.domain, XML_FROM_PARSER);
553 assert_eq!(to.code, XML_ERR_NO_MEMORY);
554 assert_eq!(to.level, XML_ERR_FATAL as c_int);
555 assert_eq!(to.line, 100);
556 assert_eq!(to.int1, 1);
557 assert_eq!(to.int2, 2);
558 }
559 }
560
561 #[test]
562 fn test_raise_and_get_last_error() {
563 unsafe {
564 reset_last_error();
565 assert!(get_last_error().is_null());
566
567 let file = b"test.xml\0" as *const u8 as *const c_char;
568 let str1 = b"element\0" as *const u8 as *const c_char;
569
570 raise_error(
571 ptr::null_mut(),
572 ptr::null_mut(),
573 ptr::null_mut(),
574 ptr::null_mut(),
575 ptr::null_mut(),
576 XML_FROM_PARSER,
577 XML_ERR_TAG_NAME_MISMATCH,
578 XML_ERR_ERROR as c_int,
579 file,
580 10,
581 str1,
582 ptr::null(),
583 ptr::null(),
584 0,
585 0,
586 ptr::null(),
587 );
588
589 let last = get_last_error();
590 assert!(!last.is_null());
591 assert_eq!((*last).domain, XML_FROM_PARSER);
592 assert_eq!((*last).code, XML_ERR_TAG_NAME_MISMATCH);
593 assert_eq!((*last).level, XML_ERR_ERROR as c_int);
594 assert_eq!((*last).line, 10);
595
596 let last_file = (*last).file;
598 assert!(!last_file.is_null());
599
600 reset_last_error();
601 assert!(get_last_error().is_null());
602 }
603 }
604
605 #[test]
606 fn test_structured_error_callback() {
607 unsafe {
608 reset_last_error();
609
610 let mut captured_domain: c_int = 0;
612 let captured_ptr = &mut captured_domain as *mut c_int as *mut c_void;
613
614 extern "C" fn test_handler(ctx: *mut c_void, _err: *const _xmlError) {
617 unsafe {
619 let captured = &mut *(ctx as *mut c_int);
620 *captured = 42;
621 }
622 }
623
624 set_structured_error_func(captured_ptr, Some(test_handler as xmlStructuredErrorFunc));
625
626 raise_error(
627 ptr::null_mut(),
628 ptr::null_mut(),
629 ptr::null_mut(),
630 ptr::null_mut(),
631 ptr::null_mut(),
632 XML_FROM_PARSER,
633 XML_ERR_OK as c_int,
634 XML_ERR_WARNING as c_int,
635 ptr::null(),
636 0,
637 ptr::null(),
638 ptr::null(),
639 ptr::null(),
640 0,
641 0,
642 ptr::null(),
643 );
644
645 assert_eq!(captured_domain, 42);
646
647 set_structured_error_func(ptr::null_mut(), None);
649 reset_last_error();
650 }
651 }
652
653 #[test]
654 fn test_format_error_message() {
655 unsafe {
656 let msg = b"test error\0" as *const u8 as *const c_char;
658 let formatted = format_error_message(
659 XML_FROM_NONE,
660 XML_ERR_OK as c_int,
661 msg,
662 ptr::null(),
663 ptr::null(),
664 ptr::null(),
665 );
666 assert!(!formatted.is_null());
667 let formatted_str = std::ffi::CStr::from_ptr(formatted);
668 assert_eq!(formatted_str.to_bytes(), b"test error");
669
670 allocator::xmlFree(formatted as *mut c_void);
672
673 let str1 = b"foo\0" as *const u8 as *const c_char;
675 let formatted2 = format_error_message(
676 XML_FROM_PARSER,
677 XML_ERR_OK as c_int,
678 ptr::null(),
679 str1,
680 ptr::null(),
681 ptr::null(),
682 );
683 assert!(!formatted2.is_null());
684 allocator::xmlFree(formatted2 as *mut c_void);
685 }
686 }
687}