use core::ffi::c_void;
use core::fmt::Write;
use core::ptr;
use std::os::raw::{c_char, c_int};
use crate::abi::callbacks::{xmlGenericErrorFunc, xmlStructuredErrorFunc};
use crate::abi::structs::_xmlError;
use crate::abi::types::xmlErrorLevel::*;
use crate::abi::types::*;
use crate::xml::globals;
pub unsafe fn set_generic_error_func(ctx: *mut c_void, handler: Option<xmlGenericErrorFunc>) {
unsafe { globals::set_generic_error_func(ctx, handler) };
}
pub unsafe fn set_structured_error_func(ctx: *mut c_void, handler: Option<xmlStructuredErrorFunc>) {
unsafe { globals::set_structured_error_func(ctx, handler) };
}
pub fn get_last_error() -> *mut _xmlError {
globals::get_last_error()
}
pub unsafe fn copy_error(from: *const _xmlError, to: *mut _xmlError) -> c_int {
if from.is_null() || to.is_null() {
return -1;
}
unsafe {
ptr::copy_nonoverlapping(from, to, 1);
}
0
}
pub unsafe fn reset_error(err: *mut _xmlError) {
if err.is_null() {
return;
}
unsafe {
ptr::write(
err,
_xmlError {
domain: XML_FROM_NONE,
code: XML_ERR_OK as c_int,
message: ptr::null_mut(),
level: XML_ERR_NONE as c_int,
file: ptr::null_mut(),
line: 0,
str1: ptr::null_mut(),
str2: ptr::null_mut(),
str3: ptr::null_mut(),
int1: 0,
int2: 0,
ctxt: ptr::null_mut(),
node: ptr::null_mut(),
},
);
}
}
pub fn reset_last_error() {
globals::reset_last_error();
}
pub fn format_error_message(
_domain: c_int,
_code: c_int,
msg: *const c_char,
str1: *const c_char,
str2: *const c_char,
str3: *const c_char,
) -> *mut c_char {
if !msg.is_null() {
let msg_str = unsafe { crate::abi::allocator::xmlMemStrdup(msg) };
return msg_str as *mut c_char;
}
let mut buf: [u8; 1024] = [0; 1024];
let mut pos = 0;
let domain_str = match _domain {
XML_FROM_PARSER => "parser",
XML_FROM_TREE => "tree",
XML_FROM_NAMESPACE => "namespace",
XML_FROM_DTD => "dtd",
XML_FROM_HTML => "html",
XML_FROM_MEMORY => "memory",
XML_FROM_OUTPUT => "output",
XML_FROM_IO => "io",
XML_FROM_XPATH => "xpath",
XML_FROM_XPOINTER => "xpointer",
XML_FROM_XINCLUDE => "xinclude",
XML_FROM_CATALOG => "catalog",
XML_FROM_C14N => "c14n",
XML_FROM_XSLT => "xslt",
XML_FROM_VALID => "valid",
XML_FROM_CHECK => "check",
XML_FROM_WRITER => "writer",
XML_FROM_MODULE => "module",
XML_FROM_I18N => "i18n",
XML_FROM_SCHEMATRONV => "schematron",
XML_FROM_BUFFER => "buffer",
XML_FROM_URI => "uri",
XML_FROM_NONE => "",
XML_FROM_FTP => "ftp",
XML_FROM_HTTP => "http",
XML_FROM_REGEXP => "regexp",
XML_FROM_DATATYPE => "datatype",
XML_FROM_SCHEMASP => "schema parser",
XML_FROM_SCHEMASV => "schema validator",
XML_FROM_RELAXNGP => "relaxng parser",
XML_FROM_RELAXNGV => "relaxng validator",
_ => "unknown",
};
if !domain_str.is_empty() {
let bytes = domain_str.as_bytes();
let len = bytes.len().min(buf.len().saturating_sub(pos + 2));
buf[pos..pos + len].copy_from_slice(&bytes[..len]);
pos += len;
buf[pos] = b' ';
pos += 1;
}
if !str1.is_null() {
let s = unsafe { crate::abi::versioning::c_str_to_bytes(str1).unwrap_or_default() };
if pos + s.len() + 3 <= buf.len() {
buf[pos] = b'\'';
pos += 1;
buf[pos..pos + s.len()].copy_from_slice(s);
pos += s.len();
buf[pos] = b'\'';
pos += 1;
buf[pos] = b' ';
pos += 1;
}
}
if !str2.is_null() {
let s = unsafe { crate::abi::versioning::c_str_to_bytes(str2).unwrap_or_default() };
if pos + s.len() + 3 <= buf.len() {
buf[pos] = b'\'';
pos += 1;
buf[pos..pos + s.len()].copy_from_slice(s);
pos += s.len();
buf[pos] = b'\'';
pos += 1;
buf[pos] = b' ';
pos += 1;
}
}
if !str3.is_null() {
let s = unsafe { crate::abi::versioning::c_str_to_bytes(str3).unwrap_or_default() };
if pos + s.len() + 3 <= buf.len() {
buf[pos] = b'\'';
pos += 1;
buf[pos..pos + s.len()].copy_from_slice(s);
pos += s.len();
buf[pos] = b'\'';
pos += 1;
buf[pos] = b' ';
pos += 1;
}
}
if pos < buf.len() {
buf[pos] = 0;
} else {
buf[buf.len() - 1] = 0;
}
let result = unsafe { crate::abi::allocator::xmlMalloc(pos + 1) };
if result.is_null() {
return ptr::null_mut();
}
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), result as *mut u8, pos + 1);
}
result as *mut c_char
}
pub unsafe fn raise_error(
ctxt: *mut c_void,
_ctxt2: *mut c_void,
_ctxt3: *mut c_void,
_ctxt4: *mut c_void,
_ctxt5: *mut c_void,
domain: c_int,
code: c_int,
level: c_int,
file: *const c_char,
line: c_int,
str1: *const c_char,
str2: *const c_char,
str3: *const c_char,
int1: c_int,
int2: c_int,
msg: *const c_char,
) {
let formatted_msg = format_error_message(domain, code, msg, str1, str2, str3);
let err = _xmlError {
domain,
code,
message: formatted_msg,
level,
file: file as *mut c_char,
line,
str1: str1 as *mut c_char,
str2: str2 as *mut c_char,
str3: str3 as *mut c_char,
int1,
int2,
ctxt,
node: ptr::null_mut(),
};
globals::set_last_error(err);
if let Some(handler) = globals::get_structured_error_func() {
let ctx = globals::get_structured_error_ctx();
let err_ref = globals::get_last_error();
if !err_ref.is_null() {
handler(ctx, err_ref as *const _xmlError);
}
}
if let Some(handler) = globals::get_generic_error_func() {
if level != 0 {
let ctx = globals::get_generic_error_ctx();
if !formatted_msg.is_null() {
handler(ctx, formatted_msg as *const core::ffi::c_char);
} else if !msg.is_null() {
handler(ctx, msg);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::allocator;
use core::ffi::c_void;
#[test]
fn test_error_default_reset() {
unsafe {
let mut err = _xmlError {
domain: XML_FROM_PARSER,
code: XML_ERR_NO_MEMORY,
message: ptr::null_mut(),
level: XML_ERR_ERROR as c_int,
file: ptr::null_mut(),
line: 42,
str1: ptr::null_mut(),
str2: ptr::null_mut(),
str3: ptr::null_mut(),
int1: 0,
int2: 0,
ctxt: ptr::null_mut(),
node: ptr::null_mut(),
};
reset_error(&mut err);
assert_eq!(err.domain, XML_FROM_NONE);
assert_eq!(err.code, XML_ERR_OK as c_int);
assert_eq!(err.level, XML_ERR_NONE as c_int);
assert_eq!(err.line, 0);
}
}
#[test]
fn test_copy_error() {
unsafe {
let from = _xmlError {
domain: XML_FROM_PARSER,
code: XML_ERR_NO_MEMORY,
message: ptr::null_mut(),
level: XML_ERR_FATAL as c_int,
file: ptr::null_mut(),
line: 100,
str1: ptr::null_mut(),
str2: ptr::null_mut(),
str3: ptr::null_mut(),
int1: 1,
int2: 2,
ctxt: ptr::null_mut(),
node: ptr::null_mut(),
};
let mut to = _xmlError {
domain: XML_FROM_NONE,
code: XML_ERR_OK as c_int,
message: ptr::null_mut(),
level: XML_ERR_NONE as c_int,
file: ptr::null_mut(),
line: 0,
str1: ptr::null_mut(),
str2: ptr::null_mut(),
str3: ptr::null_mut(),
int1: 0,
int2: 0,
ctxt: ptr::null_mut(),
node: ptr::null_mut(),
};
let result = copy_error(&from, &mut to);
assert_eq!(result, 0);
assert_eq!(to.domain, XML_FROM_PARSER);
assert_eq!(to.code, XML_ERR_NO_MEMORY);
assert_eq!(to.level, XML_ERR_FATAL as c_int);
assert_eq!(to.line, 100);
assert_eq!(to.int1, 1);
assert_eq!(to.int2, 2);
}
}
#[test]
fn test_raise_and_get_last_error() {
unsafe {
reset_last_error();
assert!(get_last_error().is_null());
let file = b"test.xml\0" as *const u8 as *const c_char;
let str1 = b"element\0" as *const u8 as *const c_char;
raise_error(
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
XML_FROM_PARSER,
XML_ERR_TAG_NAME_MISMATCH,
XML_ERR_ERROR as c_int,
file,
10,
str1,
ptr::null(),
ptr::null(),
0,
0,
ptr::null(),
);
let last = get_last_error();
assert!(!last.is_null());
assert_eq!((*last).domain, XML_FROM_PARSER);
assert_eq!((*last).code, XML_ERR_TAG_NAME_MISMATCH);
assert_eq!((*last).level, XML_ERR_ERROR as c_int);
assert_eq!((*last).line, 10);
let last_file = (*last).file;
assert!(!last_file.is_null());
reset_last_error();
assert!(get_last_error().is_null());
}
}
#[test]
fn test_structured_error_callback() {
unsafe {
reset_last_error();
let mut captured_domain: c_int = 0;
let captured_ptr = &mut captured_domain as *mut c_int as *mut c_void;
extern "C" fn test_handler(ctx: *mut c_void, _err: *const _xmlError) {
unsafe {
let captured = &mut *(ctx as *mut c_int);
*captured = 42;
}
}
set_structured_error_func(captured_ptr, Some(test_handler as xmlStructuredErrorFunc));
raise_error(
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
XML_FROM_PARSER,
XML_ERR_OK as c_int,
XML_ERR_WARNING as c_int,
ptr::null(),
0,
ptr::null(),
ptr::null(),
ptr::null(),
0,
0,
ptr::null(),
);
assert_eq!(captured_domain, 42);
set_structured_error_func(ptr::null_mut(), None);
reset_last_error();
}
}
#[test]
fn test_format_error_message() {
unsafe {
let msg = b"test error\0" as *const u8 as *const c_char;
let formatted = format_error_message(
XML_FROM_NONE,
XML_ERR_OK as c_int,
msg,
ptr::null(),
ptr::null(),
ptr::null(),
);
assert!(!formatted.is_null());
let formatted_str = std::ffi::CStr::from_ptr(formatted);
assert_eq!(formatted_str.to_bytes(), b"test error");
allocator::xmlFree(formatted as *mut c_void);
let str1 = b"foo\0" as *const u8 as *const c_char;
let formatted2 = format_error_message(
XML_FROM_PARSER,
XML_ERR_OK as c_int,
ptr::null(),
str1,
ptr::null(),
ptr::null(),
);
assert!(!formatted2.is_null());
allocator::xmlFree(formatted2 as *mut c_void);
}
}
}