1use crate::abi::callbacks::{
23 xmlCharEncodingOutputFunc, xmlOutputCloseCallback, xmlOutputWriteCallback,
24};
25use crate::abi::structs::{_xmlDoc, _xmlNode, _xmlOutputBuffer};
26use crate::abi::types::xmlChar;
27use crate::xml::io;
28use std::os::raw::{c_char, c_int, c_long};
29use std::ptr;
30
31pub const XML_SAVE_FORMAT: c_int = 1 << 0;
33pub const XML_SAVE_NO_DECL: c_int = 1 << 1;
35pub const XML_SAVE_NO_EMPTY: c_int = 1 << 2;
37
38#[derive(Debug)]
40#[repr(C)]
41pub struct _xmlSaveCtxt {
42 pub buf: *mut _xmlOutputBuffer,
44 pub options: c_int,
46 pub format: c_int,
48 pub no_decl: c_int,
50 pub no_empty: c_int,
53 pub indent: *mut xmlChar,
55 pub escape: Option<xmlCharEncodingOutputFunc>,
57 pub attrEscape: Option<xmlCharEncodingOutputFunc>,
59}
60
61unsafe fn save_ctxt_new(buf: *mut _xmlOutputBuffer, options: c_int) -> *mut _xmlSaveCtxt {
63 if buf.is_null() {
64 return ptr::null_mut();
65 }
66 let ctxt = libc::calloc(1, core::mem::size_of::<_xmlSaveCtxt>()) as *mut _xmlSaveCtxt;
67 if ctxt.is_null() {
68 io::output_buffer_close(buf);
69 return ptr::null_mut();
70 }
71 (*ctxt).buf = buf;
72 (*ctxt).options = options;
73 (*ctxt).format = if (options & XML_SAVE_FORMAT) != 0 {
74 1
75 } else {
76 0
77 };
78 (*ctxt).no_decl = if (options & XML_SAVE_NO_DECL) != 0 {
79 1
80 } else {
81 0
82 };
83 (*ctxt).no_empty = if (options & XML_SAVE_NO_EMPTY) != 0 {
84 1
85 } else {
86 0
87 };
88 ctxt
89}
90
91unsafe fn encoding_handler(
93 encoding: *const c_char,
94) -> *mut crate::abi::structs::_xmlCharEncodingHandler {
95 if encoding.is_null() {
96 return ptr::null_mut();
97 }
98 crate::xml::encoding::xmlFindCharEncodingHandler(encoding)
99}
100
101#[no_mangle]
107pub unsafe extern "C" fn xmlSaveToFd(
108 fd: c_int,
109 encoding: *const c_char,
110 options: c_int,
111) -> *mut _xmlSaveCtxt {
112 let enc = unsafe { encoding_handler(encoding) };
113 let out = io::output_buffer_create_fd(fd, enc);
114 unsafe { save_ctxt_new(out, options) }
115}
116
117#[no_mangle]
123pub unsafe extern "C" fn xmlSaveToFilename(
124 filename: *const c_char,
125 encoding: *const c_char,
126 options: c_int,
127) -> *mut _xmlSaveCtxt {
128 let enc = unsafe { encoding_handler(encoding) };
129 let out = io::output_buffer_create_filename(filename, enc, 0);
130 unsafe { save_ctxt_new(out, options) }
131}
132
133#[no_mangle]
139pub unsafe extern "C" fn xmlSaveToBuffer(
140 buffer: *mut crate::abi::structs::_xmlBuffer,
141 encoding: *const c_char,
142 options: c_int,
143) -> *mut _xmlSaveCtxt {
144 let enc = unsafe { encoding_handler(encoding) };
145 let out = io::output_buffer_create_buffer(buffer, enc);
146 unsafe { save_ctxt_new(out, options) }
147}
148
149#[no_mangle]
155pub unsafe extern "C" fn xmlSaveToIO(
156 iowrite: Option<xmlOutputWriteCallback>,
157 ioclose: Option<xmlOutputCloseCallback>,
158 ioctx: *mut core::ffi::c_void,
159 encoding: *const c_char,
160 options: c_int,
161) -> *mut _xmlSaveCtxt {
162 let enc = unsafe { encoding_handler(encoding) };
163 let out = io::output_buffer_create_io(iowrite, ioclose, ioctx, enc);
164 unsafe { save_ctxt_new(out, options) }
165}
166
167#[no_mangle]
176pub unsafe extern "C" fn xmlSaveDoc(ctxt: *mut _xmlSaveCtxt, doc: *mut _xmlDoc) -> c_long {
177 unsafe { save_doc_or_tree(ctxt, doc as *mut _xmlNode) }
178}
179
180#[no_mangle]
189pub unsafe extern "C" fn xmlSaveTree(ctxt: *mut _xmlSaveCtxt, node: *mut _xmlNode) -> c_long {
190 unsafe { save_doc_or_tree(ctxt, node) }
191}
192
193unsafe fn save_doc_or_tree(ctxt: *mut _xmlSaveCtxt, node: *mut _xmlNode) -> c_long {
194 if ctxt.is_null() || node.is_null() {
195 return -1;
196 }
197 let buf = io::buf_create(-1);
198 if buf.is_null() {
199 return -1;
200 }
201 let indent = (*ctxt).indent;
202 let format = (*ctxt).format;
203 let no_decl = (*ctxt).no_decl;
204 crate::xml::tree::serialize_node_opts(node, buf, format, 0, indent, no_decl);
205
206 let before = io::buf_length(buf);
207 let content = io::buf_content(buf);
208 let ret = if before > 0 && !content.is_null() {
209 io::output_buffer_write((*ctxt).buf, before, content as *const c_char)
210 } else {
211 0
212 };
213 io::buf_free(buf);
214 if ret < 0 {
215 -1
216 } else {
217 ret as c_long
218 }
219}
220
221#[no_mangle]
227pub unsafe extern "C" fn xmlSaveFlush(ctxt: *mut _xmlSaveCtxt) -> c_int {
228 if ctxt.is_null() {
229 return -1;
230 }
231 io::output_buffer_flush((*ctxt).buf)
232}
233
234#[no_mangle]
246pub unsafe extern "C" fn xmlSaveClose(ctxt: *mut _xmlSaveCtxt) -> c_int {
247 if ctxt.is_null() {
248 return -1;
249 }
250 let flush_ret = if (*ctxt).buf.is_null() {
251 -1
252 } else {
253 io::output_buffer_flush((*ctxt).buf)
254 };
255 if !(*ctxt).buf.is_null() {
257 io::output_buffer_close((*ctxt).buf);
258 }
259 if !(*ctxt).indent.is_null() {
260 libc::free((*ctxt).indent as *mut libc::c_void);
261 }
262 libc::free(ctxt as *mut libc::c_void);
263 flush_ret
264}
265
266#[no_mangle]
278pub unsafe extern "C" fn xmlSaveFinish(ctxt: *mut _xmlSaveCtxt) -> c_int {
279 if ctxt.is_null() {
280 return -1;
281 }
282 let ret = if (*ctxt).buf.is_null() {
283 -1
284 } else {
285 io::output_buffer_close((*ctxt).buf)
286 };
287 if !(*ctxt).indent.is_null() {
288 libc::free((*ctxt).indent as *mut libc::c_void);
289 }
290 libc::free(ctxt as *mut libc::c_void);
291 if ret < 0 {
292 -ret
293 } else {
294 0
295 }
296}
297
298#[no_mangle]
306pub unsafe extern "C" fn xmlSaveSetIndentString(
307 ctxt: *mut _xmlSaveCtxt,
308 indent: *const c_char,
309) -> c_int {
310 if ctxt.is_null() || indent.is_null() {
314 return -1;
315 }
316 let len = libc::strlen(indent) as usize;
317 if len == 0 || len > 60 {
318 return -1;
319 }
320 if !(*ctxt).indent.is_null() {
321 libc::free((*ctxt).indent as *mut libc::c_void);
322 (*ctxt).indent = ptr::null_mut();
323 }
324 let copy = libc::malloc(len + 1) as *mut xmlChar;
325 if copy.is_null() {
326 return -1;
327 }
328 libc::memcpy(
329 copy as *mut libc::c_void,
330 indent as *const libc::c_void,
331 len + 1,
332 );
333 (*ctxt).indent = copy;
334 0
335}
336
337#[no_mangle]
343pub unsafe extern "C" fn xmlSaveSetEscape(
344 ctxt: *mut _xmlSaveCtxt,
345 escape: Option<xmlCharEncodingOutputFunc>,
346) -> c_int {
347 if ctxt.is_null() {
348 return -1;
349 }
350 (*ctxt).escape = escape;
351 0
352}
353
354#[no_mangle]
360pub unsafe extern "C" fn xmlSaveSetAttrEscape(
361 ctxt: *mut _xmlSaveCtxt,
362 escape: Option<xmlCharEncodingOutputFunc>,
363) -> c_int {
364 if ctxt.is_null() {
365 return -1;
366 }
367 (*ctxt).attrEscape = escape;
368 0
369}
370
371unsafe fn save_ctxt_wrap(buf: *mut _xmlOutputBuffer, options: c_int) -> *mut _xmlSaveCtxt {
375 if buf.is_null() {
376 return ptr::null_mut();
377 }
378 let ctxt = libc::calloc(1, core::mem::size_of::<_xmlSaveCtxt>()) as *mut _xmlSaveCtxt;
379 if ctxt.is_null() {
380 return ptr::null_mut();
381 }
382 (*ctxt).buf = buf;
383 (*ctxt).options = options;
384 (*ctxt).format = if (options & XML_SAVE_FORMAT) != 0 {
385 1
386 } else {
387 0
388 };
389 (*ctxt).no_decl = if (options & XML_SAVE_NO_DECL) != 0 {
390 1
391 } else {
392 0
393 };
394 (*ctxt).no_empty = if (options & XML_SAVE_NO_EMPTY) != 0 {
395 1
396 } else {
397 0
398 };
399 ctxt
400}
401
402#[no_mangle]
411pub unsafe extern "C" fn xmlSaveFormatFileTo(
412 buf: *mut _xmlOutputBuffer,
413 cur: *mut _xmlDoc,
414 encoding: *const c_char,
415 format: c_int,
416) -> c_int {
417 let _ = encoding;
418 let options = if format != 0 { XML_SAVE_FORMAT } else { 0 };
419 let ctxt = unsafe { save_ctxt_wrap(buf, options) };
420 if ctxt.is_null() {
421 return -1;
422 }
423 let ret = unsafe { xmlSaveDoc(ctxt, cur) };
424 let close_ret = unsafe { xmlSaveClose(ctxt) };
425 if ret < 0 {
426 -1
427 } else {
428 close_ret
429 }
430}
431
432#[no_mangle]
440pub unsafe extern "C" fn xmlSaveFileTo(
441 buf: *mut _xmlOutputBuffer,
442 cur: *mut _xmlDoc,
443 encoding: *const c_char,
444) -> c_int {
445 unsafe { xmlSaveFormatFileTo(buf, cur, encoding, 0) }
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451 use crate::xml::tree::new_doc;
452
453 fn doc_with_root() -> *mut _xmlDoc {
454 unsafe {
455 let doc = new_doc(c"1.0".as_ptr() as *const xmlChar);
456 let root =
457 crate::xml::tree::new_node(ptr::null_mut(), c"root".as_ptr() as *const xmlChar);
458 crate::xml::tree::doc_set_root_element(doc, root);
459 doc
460 }
461 }
462
463 #[test]
464 fn test_save_to_buffer_format_and_nodes() {
465 unsafe {
466 let doc = doc_with_root();
467 let buf = io::buf_create(-1);
468 let ctxt = xmlSaveToBuffer(buf, ptr::null(), XML_SAVE_FORMAT);
469 assert!(!ctxt.is_null());
470 assert!(xmlSaveDoc(ctxt, doc) >= 0);
471 assert_eq!(xmlSaveFinish(ctxt), 0);
472 let content = io::buf_content(buf);
473 let len = io::buf_length(buf);
474 let s = core::slice::from_raw_parts(content, len as usize);
475 let expected = "<?xml version=\"1.0\"?>\n<root/>\n";
476 assert_eq!(s, expected.as_bytes());
477 crate::xml::tree::free_doc(doc);
478 io::buf_free(buf);
479 }
480 }
481
482 #[test]
483 fn test_save_no_decl() {
484 unsafe {
485 let doc = doc_with_root();
486 let buf = io::buf_create(-1);
487 let ctxt = xmlSaveToBuffer(buf, ptr::null(), XML_SAVE_NO_DECL);
488 assert!(!ctxt.is_null());
489 xmlSaveDoc(ctxt, doc);
490 xmlSaveFinish(ctxt);
491 let content = io::buf_content(buf);
492 let len = io::buf_length(buf);
493 let s = core::slice::from_raw_parts(content, len as usize);
494 assert_eq!(s, b"<root/>\n");
495 crate::xml::tree::free_doc(doc);
496 io::buf_free(buf);
497 }
498 }
499
500 #[test]
501 fn test_save_set_indent_string() {
502 unsafe {
503 let doc = doc_with_root();
504 let child =
505 crate::xml::tree::new_node(ptr::null_mut(), c"child".as_ptr() as *const xmlChar);
506 crate::xml::tree::add_child(crate::xml::tree::doc_get_root_element(doc), child);
507 let buf = io::buf_create(-1);
508 let ctxt = xmlSaveToBuffer(buf, ptr::null(), XML_SAVE_FORMAT);
509 assert!(!ctxt.is_null());
510 assert_eq!(
511 xmlSaveSetIndentString(ctxt, c"\t".as_ptr() as *const c_char),
512 0
513 );
514 xmlSaveDoc(ctxt, doc);
515 xmlSaveFinish(ctxt);
516 let content = io::buf_content(buf);
517 let len = io::buf_length(buf);
518 let s = core::slice::from_raw_parts(content, len as usize);
519 let expected = "<?xml version=\"1.0\"?>\n<root>\n\t<child/>\n</root>\n";
520 assert_eq!(s, expected.as_bytes());
521 crate::xml::tree::free_doc(doc);
522 io::buf_free(buf);
523 }
524 }
525
526 #[test]
527 fn test_save_close_null_and_errors() {
528 unsafe {
529 assert!(xmlSaveToFd(-1, ptr::null(), 0).is_null());
530 assert_eq!(xmlSaveFlush(ptr::null_mut()), -1);
531 assert_eq!(xmlSaveFinish(ptr::null_mut()), -1);
532 assert_eq!(xmlSaveClose(ptr::null_mut()), -1);
533 assert_eq!(xmlSaveSetIndentString(ptr::null_mut(), ptr::null()), -1);
534 assert_eq!(xmlSaveSetEscape(ptr::null_mut(), None), -1);
535 assert_eq!(xmlSaveSetAttrEscape(ptr::null_mut(), None), -1);
536 assert_eq!(xmlSaveDoc(ptr::null_mut(), ptr::null_mut()), -1);
537 assert_eq!(xmlSaveTree(ptr::null_mut(), ptr::null_mut()), -1);
538 }
539 }
540}