use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::*;
use std::convert::AsRef;
use std::io;
use std::ffi::{CStr, CString};
use std::fmt;
use std::fs;
use std::ptr;
use std::slice;
use std::str;
enum XmlParserOption {
Recover = 1, Noerror = 32, Nowarning = 64, }
enum HtmlParserOption {
Recover = 1, Noerror = 32, Nowarning = 64, }
pub enum XmlParseError {
GotNullPointer,
FileOpenError,
DocumentTooLarge,
}
impl fmt::Debug for XmlParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
XmlParseError::GotNullPointer => write!(f, "Got a Null pointer"),
XmlParseError::FileOpenError => write!(f, "Unable to open path to file."),
XmlParseError::DocumentTooLarge => write!(f, "Document too large for i32."),
}
}
}
const DEFAULT_ENCODING: *const ::std::os::raw::c_char = ptr::null();
const DEFAULT_URL: *const ::std::os::raw::c_char = ptr::null();
fn xml_open(filename: &str) -> io::Result<*mut ::std::os::raw::c_void> {
let ptr = Box::into_raw(Box::new(fs::File::open(filename)?));
Ok(ptr as *mut ::std::os::raw::c_void)
}
unsafe extern "C" fn xml_read(
context: *mut ::std::os::raw::c_void,
buffer: *mut ::std::os::raw::c_char,
len: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int
{
let file = context as *mut fs::File;
let buf = slice::from_raw_parts_mut(buffer as *mut u8, len as usize);
match io::Read::read(&mut *file, buf) {
Ok(v) => v as ::std::os::raw::c_int,
Err(_) => -1,
}
}
type XmlReadCallback = unsafe extern "C" fn(
*mut ::std::ffi::c_void,
*mut ::std::os::raw::c_char,
::std::os::raw::c_int
) -> ::std::os::raw::c_int;
unsafe extern "C" fn xml_close(
context: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int
{
let file = context as *mut fs::File;
let _ = Box::from_raw(file);
0
}
type XmlCloseCallback = unsafe extern "C" fn(
*mut ::std::ffi::c_void,
) -> ::std::os::raw::c_int;
fn usize_to_i32(value: usize) -> Result<i32, XmlParseError> {
if cfg!(target_pointer_width = "16") {
Ok(value as i32)
} else {
if value < i32::max_value() as usize {
Ok(value as i32)
} else {
Err(XmlParseError::DocumentTooLarge)
}
}
}
#[derive(PartialEq)]
pub enum ParseFormat {
XML,
HTML,
}
pub struct Parser {
pub format: ParseFormat,
}
impl Default for Parser {
fn default() -> Self {
Parser {
format: ParseFormat::XML,
}
}
}
impl Parser {
pub fn default_html() -> Self {
Parser {
format: ParseFormat::HTML,
}
}
pub fn parse_file(&self, filename: &str) -> Result<Document, XmlParseError> {
self.parse_file_with_encoding(filename, None)
}
pub fn parse_file_with_encoding(&self, filename: &str, encoding: Option<&str>)
-> Result<Document, XmlParseError>
{
let ioread: Option<XmlReadCallback> = Some(xml_read);
let ioclose: Option<XmlCloseCallback> = Some(xml_close);
let ioctx = match xml_open(filename) {
Ok(v) => v,
Err(_) => return Err(XmlParseError::FileOpenError),
};
let encoding_cstring: Option<CString> = encoding.
map(|v| CString::new(v).unwrap());
let encoding_ptr = match encoding_cstring {
Some(v) => v.as_ptr(),
None => DEFAULT_ENCODING,
};
let url_ptr = DEFAULT_URL;
unsafe {
xmlKeepBlanksDefault(1);
}
match self.format {
ParseFormat::XML => {
let options: i32 = XmlParserOption::Recover as i32
+ XmlParserOption::Noerror as i32
+ XmlParserOption::Nowarning as i32;
unsafe {
let doc_ptr = xmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
if doc_ptr.is_null() {
Err(XmlParseError::GotNullPointer)
} else {
Ok(Document::new_ptr(doc_ptr))
}
}
}
ParseFormat::HTML => {
let options: i32 = HtmlParserOption::Recover as i32
+ HtmlParserOption::Noerror as i32
+ HtmlParserOption::Nowarning as i32;
unsafe {
let doc_ptr = htmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
if doc_ptr.is_null() {
Err(XmlParseError::GotNullPointer)
} else {
Ok(Document::new_ptr(doc_ptr))
}
}
}
}
}
pub fn parse_string<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> Result<Document, XmlParseError> {
self.parse_string_with_encoding(input, None)
}
pub fn parse_string_with_encoding<Bytes: AsRef<[u8]>>(&self, input: Bytes, encoding: Option<&str>)
-> Result<Document, XmlParseError>
{
let input_bytes = input.as_ref();
let input_ptr = input_bytes.as_ptr() as *const ::std::os::raw::c_char;
let input_len = usize_to_i32(input_bytes.len())?;
let encoding_cstring: Option<CString> = encoding.
map(|v| CString::new(v).unwrap());
let encoding_ptr = match encoding_cstring {
Some(v) => v.as_ptr(),
None => DEFAULT_ENCODING,
};
let url_ptr = DEFAULT_URL;
match self.format {
ParseFormat::XML => unsafe {
let options: i32 = XmlParserOption::Recover as i32
+ XmlParserOption::Noerror as i32
+ XmlParserOption::Nowarning as i32;
let docptr = xmlReadMemory(
input_ptr,
input_len,
url_ptr,
encoding_ptr,
options,
);
if docptr.is_null() {
Err(XmlParseError::GotNullPointer)
} else {
Ok(Document::new_ptr(docptr))
}
},
ParseFormat::HTML => unsafe {
let options: i32 = HtmlParserOption::Recover as i32
+ HtmlParserOption::Noerror as i32
+ HtmlParserOption::Nowarning as i32;
let docptr = htmlReadMemory(
input_ptr,
input_len,
url_ptr,
encoding_ptr,
options,
);
if docptr.is_null() {
Err(XmlParseError::GotNullPointer)
} else {
Ok(Document::new_ptr(docptr))
}
},
}
}
pub fn is_well_formed_html<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> bool {
self.is_well_formed_html_with_encoding(input, None)
}
pub fn is_well_formed_html_with_encoding<Bytes: AsRef<[u8]>>(&self, input: Bytes, encoding: Option<&str>)
-> bool
{
let input_bytes = input.as_ref();
if input_bytes.is_empty() {
return false;
}
let input_ptr = input_bytes.as_ptr() as *const ::std::os::raw::c_char;
let input_len = match usize_to_i32(input_bytes.len()) {
Ok(v) => v,
Err(_) => return false,
};
let encoding_cstring: Option<CString> = encoding.
map(|v| CString::new(v).unwrap());
let encoding_ptr = match encoding_cstring {
Some(v) => v.as_ptr(),
None => DEFAULT_ENCODING,
};
let url_ptr = DEFAULT_URL;
match self.format {
ParseFormat::XML => false, ParseFormat::HTML => unsafe {
let ctxt = htmlNewParserCtxt();
setWellFormednessHandler(ctxt);
let docptr = htmlCtxtReadMemory (
ctxt,
input_ptr,
input_len,
url_ptr,
encoding_ptr,
10_596,
); let well_formed_final = if htmlWellFormed(ctxt) {
if !docptr.is_null() {
let node_ptr = xmlDocGetRootElement(docptr);
let name_ptr = xmlNodeGetName(node_ptr);
if name_ptr.is_null() {
false
}
else {
let c_root_name = CStr::from_ptr(name_ptr);
let root_name = str::from_utf8(c_root_name.to_bytes()).unwrap().to_owned();
root_name == "html"
}
} else {
false
}
} else {
false
};
if !ctxt.is_null() {
htmlFreeParserCtxt(ctxt);
}
if !docptr.is_null() {
xmlFreeDoc(docptr);
}
well_formed_final
},
}
}
}