#![allow(
missing_docs,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
use core::ffi::c_void;
use core::ptr;
use std::os::raw::{c_char, c_int};
use crate::abi::allocator;
use crate::abi::structs::*;
use crate::abi::types::*;
use crate::xml::io;
use crate::xml::tree;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum WriterState {
None,
Element,
Attribute,
CData,
Comment,
PI,
DTD,
DTDElem,
DTDAttr,
DTDEntity,
DTDNotation,
XMLDecl,
}
pub struct XmlTextWriter {
output: *mut _xmlOutputBuffer,
indent: c_int,
indent_string: Vec<u8>,
depth: c_int,
stack: Vec<Vec<u8>>,
encoding: Vec<u8>,
errors: Vec<String>,
state: WriterState,
doc: *mut _xmlDoc,
in_start_tag: bool,
elem_stack: Vec<(Vec<u8>, Vec<u8>)>,
}
impl XmlTextWriter {
unsafe fn new(output: *mut _xmlOutputBuffer) -> *mut Self {
let writer = allocator::xmlMallocZero(size_of::<XmlTextWriter>() as usize) as *mut Self;
if writer.is_null() {
return ptr::null_mut();
}
unsafe {
(*writer).output = output;
(*writer).indent = 0;
(*writer).indent_string = b" \0".to_vec();
(*writer).depth = 0;
(*writer).stack = Vec::new();
(*writer).encoding = b"UTF-8\0".to_vec();
(*writer).errors = Vec::new();
(*writer).state = WriterState::None;
(*writer).doc = ptr::null_mut();
(*writer).in_start_tag = false;
(*writer).elem_stack = Vec::new();
}
writer
}
unsafe fn write_raw(&mut self, data: *const u8, len: c_int) {
if self.output.is_null() || data.is_null() || len <= 0 {
return;
}
io::output_buffer_write(self.output, len, data as *const c_char);
}
unsafe fn write_str(&mut self, s: *const u8) {
if self.output.is_null() || s.is_null() {
return;
}
io::output_buffer_write_string(self.output, s as *const c_char);
}
unsafe fn write_slice(&mut self, slice: &[u8]) {
if self.output.is_null() || slice.is_empty() {
return;
}
io::output_buffer_write(
self.output,
slice.len() as c_int,
slice.as_ptr() as *const c_char,
);
}
unsafe fn write_byte(&mut self, b: u8) {
if self.output.is_null() {
return;
}
io::output_buffer_write_char(self.output, b as c_char);
}
unsafe fn write_indent(&mut self) {
if self.indent == 0 {
return;
}
self.write_byte(b'\n');
let indent_str = self.indent_string.clone();
for _ in 0..self.depth {
self.write_slice(&indent_str);
}
}
unsafe fn close_start_tag(&mut self) {
if self.in_start_tag {
self.write_byte(b'>');
self.in_start_tag = false;
}
}
fn can_write_content(&self) -> bool {
matches!(
self.state,
WriterState::None
| WriterState::Element
| WriterState::Attribute
| WriterState::CData
| WriterState::Comment
| WriterState::PI
| WriterState::DTD
| WriterState::DTDElem
| WriterState::DTDAttr
| WriterState::DTDEntity
| WriterState::DTDNotation
| WriterState::XMLDecl
)
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlFreeTextWriter(writer: *mut XmlTextWriter) {
if writer.is_null() {
return;
}
if !(*writer).output.is_null() {
io::output_buffer_flush((*writer).output);
}
unsafe {
ptr::drop_in_place(&mut (*writer).indent_string);
ptr::drop_in_place(&mut (*writer).stack);
ptr::drop_in_place(&mut (*writer).encoding);
ptr::drop_in_place(&mut (*writer).errors);
ptr::drop_in_place(&mut (*writer).elem_stack);
}
unsafe { allocator::xmlFree(writer as *mut c_void) };
}
#[no_mangle]
pub unsafe extern "C" fn xmlNewTextWriter(out: *mut _xmlOutputBuffer) -> *mut XmlTextWriter {
if out.is_null() {
return ptr::null_mut();
}
XmlTextWriter::new(out)
}
#[no_mangle]
pub unsafe extern "C" fn xmlNewTextWriterFilename(
uri: *const c_char,
compression: c_int,
) -> *mut XmlTextWriter {
if uri.is_null() {
return ptr::null_mut();
}
let out = io::output_buffer_create_filename(uri, ptr::null_mut(), compression);
if out.is_null() {
return ptr::null_mut();
}
XmlTextWriter::new(out)
}
#[no_mangle]
pub unsafe extern "C" fn xmlNewTextWriterMemory(
buf: *mut _xmlBuffer,
compression: c_int,
) -> *mut XmlTextWriter {
let _ = compression;
if buf.is_null() {
return ptr::null_mut();
}
let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
if out.is_null() {
return ptr::null_mut();
}
XmlTextWriter::new(out)
}
#[no_mangle]
pub unsafe extern "C" fn xmlNewTextWriterDoc(
doc: *mut *mut _xmlDoc,
compression: c_int,
) -> *mut XmlTextWriter {
let _ = compression;
if doc.is_null() {
return ptr::null_mut();
}
let new_doc = tree::new_doc(b"1.0\0" as *const u8);
if new_doc.is_null() {
return ptr::null_mut();
}
unsafe { *doc = new_doc };
let buf = io::buf_create(io::DEFAULT_BUFFER_SIZE as c_int);
if buf.is_null() {
tree::free_doc(new_doc);
return ptr::null_mut();
}
let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
if out.is_null() {
io::buf_free(buf);
tree::free_doc(new_doc);
return ptr::null_mut();
}
let writer = XmlTextWriter::new(out);
if !writer.is_null() {
unsafe { (*writer).doc = new_doc };
}
writer
}
#[no_mangle]
pub unsafe extern "C" fn xmlNewTextWriterTree(
doc: *mut _xmlDoc,
node: *mut _xmlNode,
compression: c_int,
) -> *mut XmlTextWriter {
let _ = compression;
let _ = node; if doc.is_null() {
return ptr::null_mut();
}
let buf = io::buf_create(io::DEFAULT_BUFFER_SIZE as c_int);
if buf.is_null() {
return ptr::null_mut();
}
let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
if out.is_null() {
io::buf_free(buf);
return ptr::null_mut();
}
let writer = XmlTextWriter::new(out);
if !writer.is_null() {
unsafe { (*writer).doc = doc };
}
writer
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartDocument(
writer: *mut XmlTextWriter,
version: *const c_char,
encoding: *const c_char,
standalone: *const c_char,
) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.write_raw(b"<?xml version=\"" as *const u8, 15);
let ver = if version.is_null() {
b"1.0\0" as *const u8
} else {
version as *const u8
};
w.write_str(ver);
w.write_raw(b"\"" as *const u8, 1);
let enc = if encoding.is_null() {
ptr::null()
} else {
encoding as *const u8
};
if !enc.is_null() {
w.write_raw(b" encoding=\"" as *const u8, 11);
w.write_str(enc);
w.write_byte(b'"');
}
if !standalone.is_null() {
let sa = standalone as *const u8;
w.write_raw(b" standalone=\"" as *const u8, 13);
w.write_str(sa);
w.write_byte(b'"');
}
w.write_raw(b"?>" as *const u8, 2);
if w.indent != 0 {
w.write_byte(b'\n');
}
w.state = WriterState::XMLDecl;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndDocument(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
while w.depth > 0 {
xmlTextWriterEndElement(writer);
}
if w.indent != 0 {
w.write_byte(b'\n');
}
if !w.output.is_null() {
io::output_buffer_flush(w.output);
}
w.state = WriterState::None;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartElement(
writer: *mut XmlTextWriter,
name: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_byte(b'<');
w.write_str(name);
let name_bytes = unsafe { c_str_to_vec(name) };
w.elem_stack.push((b"".to_vec(), name_bytes.clone()));
let stack_name = if name_bytes.last() == Some(&0) {
name_bytes[..name_bytes.len() - 1].to_vec()
} else {
name_bytes.clone()
};
w.stack.push(stack_name);
w.depth += 1;
w.in_start_tag = true;
w.state = WriterState::Element;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndElement(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.depth <= 0 {
return -1;
}
if w.in_start_tag {
w.write_raw(b"/>" as *const u8, 2);
w.in_start_tag = false;
} else {
if w.indent != 0 {
w.write_byte(b'\n');
let indent_str = w.indent_string.clone();
for _ in 0..(w.depth - 1) {
w.write_slice(&indent_str);
}
}
let name = w.stack.pop().unwrap_or_default();
w.write_raw(b"</" as *const u8, 2);
w.write_slice(&name);
w.write_byte(b'>');
}
w.depth -= 1;
w.elem_stack.pop();
w.state = WriterState::None;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartElementNS(
writer: *mut XmlTextWriter,
prefix: *const xmlChar,
name: *const xmlChar,
namespaceURI: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_byte(b'<');
let prefix_bytes = if prefix.is_null() {
Vec::new()
} else {
unsafe { c_str_to_vec(prefix) }
};
let name_bytes = unsafe { c_str_to_vec(name) };
if !prefix_bytes.is_empty() {
let p = if prefix_bytes.last() == Some(&0) {
&prefix_bytes[..prefix_bytes.len() - 1]
} else {
&prefix_bytes
};
w.write_slice(p);
w.write_byte(b':');
}
let n = if name_bytes.last() == Some(&0) {
&name_bytes[..name_bytes.len() - 1]
} else {
&name_bytes
};
w.write_slice(n);
if !namespaceURI.is_null() {
let ns_uri_bytes = unsafe { c_str_to_vec(namespaceURI) };
if !prefix_bytes.is_empty() {
w.write_raw(b" xmlns:" as *const u8, 7);
let p = if prefix_bytes.last() == Some(&0) {
&prefix_bytes[..prefix_bytes.len() - 1]
} else {
&prefix_bytes
};
w.write_slice(p);
} else {
w.write_raw(b" xmlns" as *const u8, 6);
}
w.write_raw(b"=\"" as *const u8, 2);
let ns = if ns_uri_bytes.last() == Some(&0) {
&ns_uri_bytes[..ns_uri_bytes.len() - 1]
} else {
&ns_uri_bytes
};
w.write_slice(ns);
w.write_byte(b'"');
}
w.elem_stack.push((prefix_bytes, name_bytes.clone()));
let stack_name = if name_bytes.last() == Some(&0) {
name_bytes[..name_bytes.len() - 1].to_vec()
} else {
name_bytes
};
w.stack.push(stack_name);
w.depth += 1;
w.in_start_tag = true;
w.state = WriterState::Element;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteElement(
writer: *mut XmlTextWriter,
name: *const xmlChar,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let ret = xmlTextWriterStartElement(writer, name);
if ret != 0 {
return ret;
}
if !content.is_null() {
let ret2 = xmlTextWriterWriteString(writer, content);
if ret2 != 0 {
return ret2;
}
}
xmlTextWriterEndElement(writer)
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteElementNS(
writer: *mut XmlTextWriter,
prefix: *const xmlChar,
name: *const xmlChar,
nsURI: *const xmlChar,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let ret = xmlTextWriterStartElementNS(writer, prefix, name, nsURI);
if ret != 0 {
return ret;
}
if !content.is_null() {
let ret2 = xmlTextWriterWriteString(writer, content);
if ret2 != 0 {
return ret2;
}
}
xmlTextWriterEndElement(writer)
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterFullEndElement(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.depth <= 0 {
return -1;
}
if w.in_start_tag {
w.write_byte(b'>');
w.in_start_tag = false;
}
if w.indent != 0 {
w.write_byte(b'\n');
let indent_str = w.indent_string.clone();
for _ in 0..(w.depth - 1) {
w.write_slice(&indent_str);
}
}
let name = w.stack.pop().unwrap_or_default();
w.write_raw(b"</" as *const u8, 2);
w.write_slice(&name);
w.write_byte(b'>');
w.depth -= 1;
w.elem_stack.pop();
w.state = WriterState::None;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteAttribute(
writer: *mut XmlTextWriter,
name: *const xmlChar,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() || content.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if !w.in_start_tag {
return -1;
}
w.write_byte(b' ');
w.write_str(name);
w.write_raw(b"=\"" as *const u8, 2);
tree::serialize_attr_value(
unsafe { &mut *((*w.output).buffer as *mut _xmlBuffer) },
content,
);
w.write_byte(b'"');
w.state = WriterState::Attribute;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteAttributeNS(
writer: *mut XmlTextWriter,
prefix: *const xmlChar,
name: *const xmlChar,
nsURI: *const xmlChar,
content: *const xmlChar,
) -> c_int {
let _ = nsURI;
if writer.is_null() || name.is_null() || content.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if !w.in_start_tag {
return -1;
}
w.write_byte(b' ');
if !prefix.is_null() {
w.write_str(prefix);
w.write_byte(b':');
}
w.write_str(name);
w.write_raw(b"=\"" as *const u8, 2);
let buf = unsafe { &mut *((*w.output).buffer as *mut _xmlBuffer) };
tree::serialize_attr_value(buf, content);
w.write_byte(b'"');
w.state = WriterState::Attribute;
0
}
#[no_mangle]
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartAttribute(
writer: *mut XmlTextWriter,
name: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if !w.in_start_tag {
return -1;
}
w.write_byte(b' ');
w.write_str(name);
w.write_raw(b"=\"" as *const u8, 2);
w.state = WriterState::Attribute;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartAttributeNS(
writer: *mut XmlTextWriter,
prefix: *const xmlChar,
name: *const xmlChar,
nsURI: *const xmlChar,
) -> c_int {
let _ = nsURI;
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if !w.in_start_tag {
return -1;
}
w.write_byte(b' ');
if !prefix.is_null() {
w.write_str(prefix);
w.write_byte(b':');
}
w.write_str(name);
w.write_raw(b"=\"" as *const u8, 2);
w.state = WriterState::Attribute;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndAttribute(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::Attribute {
return -1;
}
w.write_byte(b'"');
w.state = WriterState::Element;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteString(
writer: *mut XmlTextWriter,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || content.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
let buf = if w.output.is_null() {
return -1;
} else {
unsafe { &mut *((*w.output).buffer as *mut _xmlBuffer) }
};
let len = tree::xml_strlen(content);
tree::serialize_text(buf, content, len);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteRaw(
writer: *mut XmlTextWriter,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || content.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_str(content);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteRawLen(
writer: *mut XmlTextWriter,
content: *const xmlChar,
len: c_int,
) -> c_int {
if writer.is_null() || content.is_null() || len <= 0 {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_raw(content, len);
0
}
#[no_mangle]
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteBase64(
writer: *mut XmlTextWriter,
data: *const c_char,
start: c_int,
len: c_int,
) -> c_int {
if writer.is_null() || data.is_null() || len <= 0 || start < 0 {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
let data_slice =
unsafe { core::slice::from_raw_parts(data.add(start as usize) as *const u8, len as usize) };
let encoded = base64_encode(data_slice);
w.write_slice(&encoded);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteBinHex(
writer: *mut XmlTextWriter,
data: *const c_char,
start: c_int,
len: c_int,
) -> c_int {
if writer.is_null() || data.is_null() || len <= 0 || start < 0 {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
let data_slice =
unsafe { core::slice::from_raw_parts(data.add(start as usize) as *const u8, len as usize) };
let encoded = hex_encode(data_slice);
w.write_slice(&encoded);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteCDATA(
writer: *mut XmlTextWriter,
content: *const xmlChar,
) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_raw(b"<![CDATA[" as *const u8, 9);
if !content.is_null() {
w.write_str(content);
}
w.write_raw(b"]]>" as *const u8, 3);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartCDATA(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_raw(b"<![CDATA[" as *const u8, 9);
w.state = WriterState::CData;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndCDATA(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::CData {
return -1;
}
w.write_raw(b"]]>" as *const u8, 3);
w.state = WriterState::None;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteComment(
writer: *mut XmlTextWriter,
content: *const xmlChar,
) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!--" as *const u8, 4);
if !content.is_null() {
w.write_str(content);
}
w.write_raw(b"-->" as *const u8, 3);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartComment(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!--" as *const u8, 4);
w.state = WriterState::Comment;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndComment(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::Comment {
return -1;
}
w.write_raw(b"-->" as *const u8, 3);
w.state = WriterState::None;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWritePI(
writer: *mut XmlTextWriter,
target: *const xmlChar,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || target.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<?" as *const u8, 2);
w.write_str(target);
if !content.is_null() && unsafe { *content != 0 } {
w.write_byte(b' ');
w.write_str(content);
}
w.write_raw(b"?>" as *const u8, 2);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartPI(
writer: *mut XmlTextWriter,
target: *const xmlChar,
) -> c_int {
if writer.is_null() || target.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<?" as *const u8, 2);
w.write_str(target);
w.write_byte(b' ');
w.state = WriterState::PI;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndPI(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::PI {
return -1;
}
w.write_raw(b"?>" as *const u8, 2);
w.state = WriterState::None;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteDTD(
writer: *mut XmlTextWriter,
name: *const xmlChar,
pubid: *const xmlChar,
sysid: *const xmlChar,
subset: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_raw(b"<!DOCTYPE " as *const u8, 10);
w.write_str(name);
if !pubid.is_null() {
w.write_raw(b" PUBLIC \"" as *const u8, 9);
w.write_str(pubid);
w.write_byte(b'"');
if !sysid.is_null() {
w.write_byte(b' ');
w.write_byte(b'"');
w.write_str(sysid);
w.write_byte(b'"');
}
} else if !sysid.is_null() {
w.write_raw(b" SYSTEM \"" as *const u8, 9);
w.write_str(sysid);
w.write_byte(b'"');
}
if !subset.is_null() {
w.write_raw(b" [" as *const u8, 2);
w.write_str(subset);
w.write_byte(b']');
}
w.write_byte(b'>');
if w.indent != 0 {
w.write_byte(b'\n');
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteDTDElement(
writer: *mut XmlTextWriter,
name: *const xmlChar,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() || content.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!ELEMENT " as *const u8, 10);
w.write_str(name);
w.write_byte(b' ');
w.write_str(content);
w.write_raw(b">" as *const u8, 1);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteDTDAttribute(
writer: *mut XmlTextWriter,
name: *const xmlChar,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() || content.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!ATTLIST " as *const u8, 10);
w.write_str(name);
w.write_byte(b' ');
w.write_str(content);
w.write_raw(b">" as *const u8, 1);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteDTDEntity(
writer: *mut XmlTextWriter,
name: *const xmlChar,
content: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() || content.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!ENTITY " as *const u8, 9);
w.write_str(name);
w.write_raw(b" \"" as *const u8, 2);
w.write_str(content);
w.write_raw(b"\">" as *const u8, 2);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterWriteDTDNotation(
writer: *mut XmlTextWriter,
name: *const xmlChar,
pubid: *const xmlChar,
sysid: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!NOTATION " as *const u8, 11);
w.write_str(name);
if !pubid.is_null() {
w.write_raw(b" PUBLIC \"" as *const u8, 9);
w.write_str(pubid);
w.write_byte(b'"');
if !sysid.is_null() {
w.write_byte(b' ');
w.write_byte(b'"');
w.write_str(sysid);
w.write_byte(b'"');
}
} else if !sysid.is_null() {
w.write_raw(b" SYSTEM \"" as *const u8, 9);
w.write_str(sysid);
w.write_byte(b'"');
}
w.write_raw(b">" as *const u8, 1);
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartDTD(
writer: *mut XmlTextWriter,
name: *const xmlChar,
pubid: *const xmlChar,
sysid: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_raw(b"<!DOCTYPE " as *const u8, 10);
w.write_str(name);
if !pubid.is_null() {
w.write_raw(b" PUBLIC \"" as *const u8, 9);
w.write_str(pubid);
w.write_byte(b'"');
if !sysid.is_null() {
w.write_byte(b' ');
w.write_byte(b'"');
w.write_str(sysid);
w.write_byte(b'"');
}
} else if !sysid.is_null() {
w.write_raw(b" SYSTEM \"" as *const u8, 9);
w.write_str(sysid);
w.write_byte(b'"');
}
w.write_raw(b" [" as *const u8, 2);
w.state = WriterState::DTD;
if w.indent != 0 {
w.write_byte(b'\n');
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndDTD(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::DTD {
return -1;
}
if w.indent != 0 {
w.write_byte(b'\n');
}
w.write_raw(b"]>" as *const u8, 2);
if w.indent != 0 {
w.write_byte(b'\n');
}
w.state = WriterState::None;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartDTDElement(
writer: *mut XmlTextWriter,
name: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!ELEMENT " as *const u8, 10);
w.write_str(name);
w.write_byte(b' ');
w.state = WriterState::DTDElem;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndDTDElement(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::DTDElem {
return -1;
}
w.write_raw(b">" as *const u8, 1);
w.state = WriterState::DTD;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartDTDAttribute(
writer: *mut XmlTextWriter,
name: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!ATTLIST " as *const u8, 10);
w.write_str(name);
w.write_byte(b' ');
w.state = WriterState::DTDAttr;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndDTDAttribute(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::DTDAttr {
return -1;
}
w.write_raw(b">" as *const u8, 1);
w.state = WriterState::DTD;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterStartDTDEntity(
writer: *mut XmlTextWriter,
name: *const xmlChar,
) -> c_int {
if writer.is_null() || name.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.close_start_tag();
w.write_indent();
w.write_raw(b"<!ENTITY " as *const u8, 9);
w.write_str(name);
w.write_raw(b" \"" as *const u8, 2);
w.state = WriterState::DTDEntity;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterEndDTDEntity(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.state != WriterState::DTDEntity {
return -1;
}
w.write_raw(b"\">" as *const u8, 2);
w.state = WriterState::DTD;
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterFlush(writer: *mut XmlTextWriter) -> c_int {
if writer.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
if w.output.is_null() {
return -1;
}
w.close_start_tag();
io::output_buffer_flush(w.output)
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterSetIndent(
writer: *mut XmlTextWriter,
indent: c_int,
) -> c_int {
if writer.is_null() {
return -1;
}
unsafe { (*writer).indent = indent };
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlTextWriterSetIndentString(
writer: *mut XmlTextWriter,
str: *const xmlChar,
) -> c_int {
if writer.is_null() || str.is_null() {
return -1;
}
let w = unsafe { &mut *writer };
w.indent_string = unsafe { c_str_to_vec(str) };
0
}
unsafe fn c_str_to_vec(s: *const u8) -> Vec<u8> {
if s.is_null() {
return Vec::new();
}
let len = tree::xml_strlen(s);
let mut v = Vec::with_capacity(len as usize + 1);
unsafe {
for i in 0..len as isize {
v.push(*s.offset(i));
}
v.push(0);
}
v
}
fn base64_encode(data: &[u8]) -> Vec<u8> {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut result = Vec::with_capacity((data.len() + 2) / 3 * 4);
for chunk in data.chunks(3) {
let b0 = chunk[0];
let b1 = chunk.get(1).copied().unwrap_or(0);
let b2 = chunk.get(2).copied().unwrap_or(0);
result.push(CHARS[((b0 >> 2) & 0x3F) as usize]);
result.push(CHARS[(((b0 << 4) | (b1 >> 4)) & 0x3F) as usize]);
result.push(if chunk.len() > 1 {
CHARS[(((b1 << 2) | (b2 >> 6)) & 0x3F) as usize]
} else {
b'='
});
result.push(if chunk.len() > 2 {
CHARS[(b2 & 0x3F) as usize]
} else {
b'='
});
}
result
}
fn hex_encode(data: &[u8]) -> Vec<u8> {
const CHARS: &[u8] = b"0123456789abcdef";
let mut result = Vec::with_capacity(data.len() * 2);
for &b in data {
result.push(CHARS[((b >> 4) & 0x0F) as usize]);
result.push(CHARS[(b & 0x0F) as usize]);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use core::ptr;
unsafe fn create_test_writer() -> (*mut XmlTextWriter, *mut _xmlBuffer) {
let buf = io::buf_create(256);
assert!(!buf.is_null(), "buf_create failed");
let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
assert!(!out.is_null(), "output_buffer_create_buffer failed");
let writer = xmlNewTextWriter(out);
assert!(!writer.is_null(), "xmlNewTextWriter failed");
(writer, buf)
}
unsafe fn buf_to_string(buf: *mut _xmlBuffer) -> String {
let content = io::buf_content(buf);
let len = io::buf_length(buf);
if content.is_null() || len <= 0 {
return String::new();
}
let slice = unsafe { core::slice::from_raw_parts(content, len as usize) };
String::from_utf8_lossy(slice).to_string()
}
unsafe fn flush_and_get(writer: *mut XmlTextWriter, buf: *mut _xmlBuffer) -> String {
xmlTextWriterFlush(writer);
buf_to_string(buf)
}
#[test]
fn test_write_simple_document() {
unsafe {
let (writer, buf) = create_test_writer();
let r = xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
assert_eq!(r, 0, "StartDocument failed");
let r = xmlTextWriterStartElement(writer, b"root\0" as *const u8);
assert_eq!(r, 0, "StartElement(root) failed");
let r = xmlTextWriterWriteString(writer, b"Hello, World!\0" as *const u8);
assert_eq!(r, 0, "WriteString failed");
let r = xmlTextWriterEndElement(writer);
assert_eq!(r, 0, "EndElement failed");
let r = xmlTextWriterEndDocument(writer);
assert_eq!(r, 0, "EndDocument failed");
let result = flush_and_get(writer, buf);
assert!(
result.contains("<?xml version=\"1.0\"?>"),
"Missing XML declaration. Got: {}",
result
);
assert!(
result.contains("<root>"),
"Missing <root> start tag. Got: {}",
result
);
assert!(
result.contains("Hello, World!"),
"Missing content. Got: {}",
result
);
assert!(
result.contains("</root>"),
"Missing </root> end tag. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_write_element_with_attributes() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"root\0" as *const u8);
xmlTextWriterWriteAttribute(writer, b"id\0" as *const u8, b"123\0" as *const u8);
xmlTextWriterWriteAttribute(
writer,
b"name\0" as *const u8,
b"test & demo\0" as *const u8,
);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("id=\"123\""),
"Missing id attribute. Got: {}",
result
);
assert!(
result.contains("name=\"test & demo\""),
"Missing or improperly escaped name attribute. Got: {}",
result
);
assert!(
result.contains("<root"),
"Missing root element. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_write_with_namespaces() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElementNS(
writer,
b"ns\0" as *const u8,
b"root\0" as *const u8,
b"http://example.com/ns\0" as *const u8,
);
xmlTextWriterWriteAttributeNS(
writer,
ptr::null(),
b"attr\0" as *const u8,
ptr::null(),
b"value\0" as *const u8,
);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("ns:root"),
"Missing namespace prefix. Got: {}",
result
);
assert!(
result.contains("xmlns:ns=\"http://example.com/ns\""),
"Missing xmlns declaration. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_write_text_cdata_comment_pi() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"doc\0" as *const u8);
xmlTextWriterWriteString(writer, b"text content\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterWriteComment(writer, b"a comment\0" as *const u8);
xmlTextWriterWritePI(writer, b"target\0" as *const u8, b"data\0" as *const u8);
xmlTextWriterStartElement(writer, b"cdata\0" as *const u8);
xmlTextWriterWriteCDATA(writer, b"<greeting>Hello</greeting>\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("text content"),
"Missing text content. Got: {}",
result
);
assert!(
result.contains("<!--a comment-->"),
"Missing comment. Got: {}",
result
);
assert!(
result.contains("<?target data?>"),
"Missing PI. Got: {}",
result
);
assert!(
result.contains("<![CDATA["),
"Missing CDATA start. Got: {}",
result
);
assert!(
result.contains("<greeting>Hello</greeting>"),
"Missing CDATA content. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_write_dtd() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterWriteDTD(
writer,
b"html\0" as *const u8,
ptr::null(),
b"http://www.w3.org/TR/html4/strict.dtd\0" as *const u8,
ptr::null(),
);
xmlTextWriterStartElement(writer, b"html\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("<!DOCTYPE html SYSTEM"),
"Missing DTD. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_write_dtd_with_subset() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartDTD(writer, b"root\0" as *const u8, ptr::null(), ptr::null());
xmlTextWriterWriteDTDElement(
writer,
b"child\0" as *const u8,
b"(#PCDATA)\0" as *const u8,
);
xmlTextWriterWriteDTDAttribute(
writer,
b"child\0" as *const u8,
b"id CDATA #IMPLIED\0" as *const u8,
);
xmlTextWriterWriteDTDEntity(
writer,
b"copy\0" as *const u8,
b"Copyright Me\0" as *const u8,
);
xmlTextWriterWriteDTDNotation(
writer,
b"note\0" as *const u8,
b"PublicID\0" as *const u8,
ptr::null(),
);
xmlTextWriterEndDTD(writer);
xmlTextWriterStartElement(writer, b"root\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("<!DOCTYPE root"),
"Missing DTD start. Got: {}",
result
);
assert!(
result.contains("<!ELEMENT child (#PCDATA)>"),
"Missing DTD element. Got: {}",
result
);
assert!(
result.contains("<!ATTLIST child id CDATA #IMPLIED>"),
"Missing DTD attribute. Got: {}",
result
);
assert!(
result.contains("<!ENTITY copy \"Copyright Me\">"),
"Missing DTD entity. Got: {}",
result
);
assert!(
result.contains("<!NOTATION note PUBLIC \"PublicID\">"),
"Missing DTD notation. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_indentation_control() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterSetIndent(writer, 1);
xmlTextWriterSetIndentString(writer, b"\t\0" as *const u8);
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"root\0" as *const u8);
xmlTextWriterStartElement(writer, b"child\0" as *const u8);
xmlTextWriterWriteString(writer, b"content\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains('\t'),
"Expected tab indentation. Got: {}",
result
);
assert!(result.contains("<root>"), "Missing root. Got: {}", result);
assert!(result.contains("<child>"), "Missing child. Got: {}", result);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_memory_output() {
unsafe {
let buf = io::buf_create(256);
assert!(!buf.is_null(), "buf_create failed");
let writer = xmlNewTextWriterMemory(buf, 0);
assert!(!writer.is_null(), "xmlNewTextWriterMemory failed");
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"root\0" as *const u8);
xmlTextWriterWriteString(writer, b"memory test\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
xmlTextWriterFlush(writer);
let result = buf_to_string(buf);
assert!(
result.contains("memory test"),
"Missing content in memory output. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_flush_and_close() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"root\0" as *const u8);
xmlTextWriterWriteString(writer, b"flush me\0" as *const u8);
let r = xmlTextWriterFlush(writer);
assert!(r >= 0, "Flush should return non-negative, got {}", r);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_null_handling() {
unsafe {
assert_eq!(
xmlTextWriterStartDocument(ptr::null_mut(), ptr::null(), ptr::null(), ptr::null()),
-1
);
assert_eq!(xmlTextWriterEndDocument(ptr::null_mut()), -1);
assert_eq!(
xmlTextWriterStartElement(ptr::null_mut(), b"x\0" as *const u8),
-1
);
assert_eq!(xmlTextWriterEndElement(ptr::null_mut()), -1);
assert_eq!(
xmlTextWriterWriteString(ptr::null_mut(), b"x\0" as *const u8),
-1
);
assert_eq!(
xmlTextWriterWriteRaw(ptr::null_mut(), b"x\0" as *const u8),
-1
);
assert_eq!(
xmlTextWriterWriteCDATA(ptr::null_mut(), b"x\0" as *const u8),
-1
);
assert_eq!(
xmlTextWriterWriteComment(ptr::null_mut(), b"x\0" as *const u8),
-1
);
assert_eq!(
xmlTextWriterWritePI(ptr::null_mut(), b"x\0" as *const u8, ptr::null()),
-1
);
assert_eq!(xmlTextWriterFlush(ptr::null_mut()), -1);
assert_eq!(xmlTextWriterSetIndent(ptr::null_mut(), 1), -1);
assert_eq!(
xmlTextWriterSetIndentString(ptr::null_mut(), b" \0" as *const u8),
-1
);
assert_eq!(
xmlTextWriterWriteAttribute(
ptr::null_mut(),
b"n\0" as *const u8,
b"v\0" as *const u8
),
-1
);
xmlFreeTextWriter(ptr::null_mut());
}
}
#[test]
fn test_nested_elements() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"a\0" as *const u8);
xmlTextWriterStartElement(writer, b"b\0" as *const u8);
xmlTextWriterStartElement(writer, b"c\0" as *const u8);
xmlTextWriterWriteString(writer, b"deep\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndElement(writer);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(result.contains("<a>"), "Missing <a>. Got: {}", result);
assert!(result.contains("<b>"), "Missing <b>. Got: {}", result);
assert!(result.contains("<c>"), "Missing <c>. Got: {}", result);
assert!(result.contains("</a>"), "Missing </a>. Got: {}", result);
assert!(result.contains("</b>"), "Missing </b>. Got: {}", result);
assert!(result.contains("</c>"), "Missing </c>. Got: {}", result);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_self_closing_element() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"empty\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("<empty/>"),
"Expected self-closing <empty/>. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_full_end_element() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"container\0" as *const u8);
xmlTextWriterFullEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("<container>"),
"Missing <container>. Got: {}",
result
);
assert!(
result.contains("</container>"),
"Missing </container>. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_write_element_inline() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterWriteElement(writer, b"greeting\0" as *const u8, b"Hello\0" as *const u8);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("<greeting>Hello</greeting>"),
"Expected <greeting>Hello</greeting>. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_text_escaping() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"esc\0" as *const u8);
xmlTextWriterWriteString(writer, b"a < b & b > a\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("a < b & b > a"),
"Expected escaped content. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_raw_content() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"raw\0" as *const u8);
xmlTextWriterWriteRaw(writer, b"<unencoded>&special;</unencoded>\0" as *const u8);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("<unencoded>&special;</unencoded>"),
"Expected raw unencoded content. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_base64_write() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"data\0" as *const u8);
let test_data = b"Hello, World!";
xmlTextWriterWriteBase64(
writer,
test_data.as_ptr() as *const c_char,
0,
test_data.len() as c_int,
);
xmlTextWriterEndElement(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("SGVsbG8sIFdvcmxkIQ"),
"Expected Base64-encoded content. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_incremental_cdata_comment_pi() {
unsafe {
let (writer, buf) = create_test_writer();
xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
xmlTextWriterStartElement(writer, b"inc\0" as *const u8);
xmlTextWriterStartCDATA(writer);
xmlTextWriterWriteString(writer, b"cdata content\0" as *const u8);
xmlTextWriterEndCDATA(writer);
xmlTextWriterEndElement(writer);
xmlTextWriterStartComment(writer);
xmlTextWriterWriteString(writer, b"comment text\0" as *const u8);
xmlTextWriterEndComment(writer);
xmlTextWriterStartPI(writer, b"xml-stylesheet\0" as *const u8);
xmlTextWriterWriteString(
writer,
b"type=\"text/xsl\" href=\"style.xsl\"\0" as *const u8,
);
xmlTextWriterEndPI(writer);
xmlTextWriterEndDocument(writer);
let result = flush_and_get(writer, buf);
assert!(
result.contains("<![CDATA["),
"Missing CDATA. Got: {}",
result
);
assert!(
result.contains("<!--comment text-->"),
"Missing comment. Got: {}",
result
);
assert!(
result.contains("<?xml-stylesheet"),
"Missing PI. Got: {}",
result
);
xmlFreeTextWriter(writer);
io::buf_free(buf);
}
}
#[test]
fn test_new_writer_filename_null() {
unsafe {
let writer = xmlNewTextWriterFilename(ptr::null(), 0);
assert!(writer.is_null(), "Expected NULL for null URI");
}
}
#[test]
fn test_new_writer_null_output() {
unsafe {
let writer = xmlNewTextWriter(ptr::null_mut());
assert!(writer.is_null(), "Expected NULL for null output");
}
}
}