use core::ffi::CStr;
use core::ptr;
use std::mem::size_of;
use std::os::raw::{c_char, c_int, c_void};
use crate::abi::allocator::{xmlFree, xmlMalloc, xmlMallocZero};
use crate::abi::callbacks::{xmlInputCloseCallback, xmlInputReadCallback};
use crate::abi::structs::{_xmlParserCtxt, _xmlParserInput, _xmlParserInputBuffer, _xmlSAXHandler};
use crate::xml::parser::input::{InputBuffer, InputStack};
use crate::xml::parser::state::XmlParser;
use crate::xml::sax::xmlSAX2InitDefaultSAXHandler;
#[allow(non_snake_case)]
pub(crate) unsafe fn create_parser_ctxt() -> *mut _xmlParserCtxt {
let ctxt = unsafe { xmlMallocZero(size_of::<_xmlParserCtxt>()) } as *mut _xmlParserCtxt;
if ctxt.is_null() {
return ptr::null_mut();
}
let sax = unsafe { xmlMallocZero(size_of::<_xmlSAXHandler>()) } as *mut _xmlSAXHandler;
if sax.is_null() {
unsafe { xmlFree(ctxt as *mut c_void) };
return ptr::null_mut();
}
unsafe { xmlSAX2InitDefaultSAXHandler(sax) };
unsafe {
let c = &mut *ctxt;
c.sax = sax;
c.userData = ctxt as *mut c_void;
c.wellFormed = 1;
c.instate = 0; c.keepBlanks = 1;
c.replaceEntities = 0;
c.linenumbers = 1;
c.errNo = crate::abi::types::XML_ERR_OK;
c.options = 0;
}
ctxt
}
#[allow(non_snake_case)]
pub(crate) unsafe fn free_parser_ctxt(ctxt: *mut _xmlParserCtxt) {
if ctxt.is_null() {
return;
}
unsafe {
let sax = (*ctxt).sax;
if !sax.is_null() {
xmlFree(sax as *mut c_void);
}
let input_nr = (*ctxt).inputNr;
let input_tab = (*ctxt).inputTab;
if !input_tab.is_null() {
for i in 0..input_nr {
let input = *input_tab.add(i as usize);
if !input.is_null() {
free_parser_input(input);
}
}
xmlFree(input_tab as *mut c_void);
}
let cur_input = (*ctxt).input;
if !cur_input.is_null() {
if input_tab.is_null() {
free_parser_input(cur_input);
}
}
let node_tab = (*ctxt).nodeTab;
if !node_tab.is_null() {
xmlFree(node_tab as *mut c_void);
}
let name_tab = (*ctxt).nameTab;
if !name_tab.is_null() {
xmlFree(name_tab as *mut c_void);
}
let private_data = (*ctxt)._private;
if !private_data.is_null() {
let _ = Box::from_raw(private_data as *mut InputBuffer);
}
xmlFree(ctxt as *mut c_void);
}
}
pub(crate) unsafe fn input_from_memory(buffer: *const c_char, size: c_int) -> InputBuffer {
let slice = if size > 0 && !buffer.is_null() {
unsafe { std::slice::from_raw_parts(buffer as *const u8, size as usize) }
} else {
&[]
};
InputBuffer::from_memory(slice, None)
}
pub(crate) unsafe fn input_from_file(filename: *const c_char) -> Result<InputBuffer, ()> {
if filename.is_null() {
return Err(());
}
let path = unsafe { CStr::from_ptr(filename) };
let path_str = path.to_str().map_err(|_| ())?;
InputBuffer::from_file(path_str).map_err(|_| ())
}
pub(crate) unsafe fn input_from_io(
ioread: Option<xmlInputReadCallback>,
ioclose: Option<xmlInputCloseCallback>,
ioctx: *mut c_void,
) -> InputBuffer {
let (read, close) = match (ioread, ioclose) {
(Some(r), Some(c)) => (r, c),
_ => return InputBuffer::from_memory(&[], None),
};
match unsafe { InputBuffer::from_callback(read, close, ioctx) } {
Ok(buf) => buf,
Err(_) => {
InputBuffer::from_memory(&[], None)
}
}
}
#[allow(non_snake_case)]
pub(crate) unsafe fn setup_parser_input(ctxt: *mut _xmlParserCtxt, input: InputBuffer) {
let input_buf_ptr = Box::into_raw(Box::new(input));
unsafe {
let c = &mut *ctxt;
c._private = input_buf_ptr as *mut c_void;
let parser_input = alloc_parser_input(&*input_buf_ptr, None);
c.input = parser_input;
let tab_size = 4 * size_of::<*mut _xmlParserInput>();
let tab = xmlMalloc(tab_size) as *mut *mut _xmlParserInput;
if tab.is_null() {
c.inputTab = ptr::null_mut();
c.inputMax = 0;
c.inputNr = 0;
return;
}
ptr::write_bytes(tab, 0, 4);
*tab = parser_input;
c.inputTab = tab;
c.inputMax = 4;
c.inputNr = 1;
}
}
pub(crate) unsafe fn parse_document(ctxt: *mut _xmlParserCtxt) -> c_int {
let input_buf = {
let c = &mut *ctxt;
let ptr = c._private as *mut InputBuffer;
c._private = ptr::null_mut();
if ptr.is_null() {
return -1;
}
unsafe { Box::from_raw(ptr) }
};
let input_stack = InputStack::new(*input_buf);
let mut parser = unsafe { XmlParser::new(input_stack, ctxt) };
parser.parse_document()
}
pub(crate) unsafe fn parse_chunk(
ctxt: *mut _xmlParserCtxt,
chunk: *const c_char,
size: c_int,
terminate: c_int,
) -> c_int {
let chunk_slice = if size > 0 && !chunk.is_null() {
unsafe { std::slice::from_raw_parts(chunk as *const u8, size as usize) }
} else {
&[]
};
let stored_buf: Option<Box<InputBuffer>> = unsafe {
let c = &mut *ctxt;
if c._private.is_null() {
None
} else {
let ptr = c._private as *mut InputBuffer;
c._private = ptr::null_mut();
Some(unsafe { Box::from_raw(ptr) })
}
};
let mut input_stack = if let Some(buf) = stored_buf {
InputStack::new(*buf)
} else {
InputStack::new(InputBuffer::from_memory(&[], None))
};
if !chunk_slice.is_empty() {
let chunk_buf = InputBuffer::from_memory(chunk_slice, None);
input_stack.push(chunk_buf);
}
let mut parser = unsafe { XmlParser::new(input_stack, ctxt) };
if terminate != 0 {
parser.parse_document()
} else {
0
}
}
pub(crate) unsafe fn alloc_parser_input(
input: &InputBuffer,
filename: Option<&str>,
) -> *mut _xmlParserInput {
let ptr = unsafe { xmlMallocZero(size_of::<_xmlParserInput>()) } as *mut _xmlParserInput;
if ptr.is_null() {
return ptr::null_mut();
}
unsafe {
let pi = &mut *ptr;
input.populate_parser_input(pi);
if let Some(fname) = filename {
pi.filename = fname.as_ptr() as *const c_char;
}
pi.buf = ptr::null_mut();
pi.directory = ptr::null();
pi.free = None;
pi.encoding = ptr::null();
pi.version = ptr::null();
pi.flags = 0;
pi.id = 0;
pi.parentConsumed = 0;
pi.entity = ptr::null_mut();
}
ptr
}
pub(crate) unsafe fn alloc_parser_input_buffer() -> *mut _xmlParserInputBuffer {
unsafe { xmlMallocZero(size_of::<_xmlParserInputBuffer>()) as *mut _xmlParserInputBuffer }
}
pub(crate) unsafe fn free_parser_input(input: *mut _xmlParserInput) {
if input.is_null() {
return;
}
unsafe { xmlFree(input as *mut c_void) };
}
pub(crate) unsafe fn free_parser_input_buffer(buf: *mut _xmlParserInputBuffer) {
if buf.is_null() {
return;
}
unsafe { xmlFree(buf as *mut c_void) };
}