1use crate::abi::callbacks::{
70 xmlCharEncodingOutputFunc, xmlOutputCloseCallback, xmlOutputWriteCallback,
71};
72use crate::abi::structs::{_xmlDoc, _xmlNode, _xmlOutputBuffer};
73use crate::abi::types::xmlChar;
74use crate::xml::io;
75use std::os::raw::{c_char, c_int, c_long};
76use std::ptr;
77
78pub const XML_SAVE_FORMAT: c_int = 1 << 0;
80pub const XML_SAVE_NO_DECL: c_int = 1 << 1;
82pub const XML_SAVE_NO_EMPTY: c_int = 1 << 2;
84
85#[derive(Debug)]
87#[repr(C)]
88pub struct _xmlSaveCtxt {
89 pub buf: *mut _xmlOutputBuffer,
91 pub options: c_int,
93 pub format: c_int,
95 pub no_decl: c_int,
97 pub no_empty: c_int,
100 pub indent: *mut xmlChar,
102 pub escape: Option<xmlCharEncodingOutputFunc>,
104 pub attrEscape: Option<xmlCharEncodingOutputFunc>,
106}
107
108unsafe fn save_ctxt_new(buf: *mut _xmlOutputBuffer, options: c_int) -> *mut _xmlSaveCtxt {
110 if buf.is_null() {
111 return ptr::null_mut();
112 }
113 let ctxt = libc::calloc(1, core::mem::size_of::<_xmlSaveCtxt>()) as *mut _xmlSaveCtxt;
114 if ctxt.is_null() {
115 io::output_buffer_close(buf);
116 return ptr::null_mut();
117 }
118 (*ctxt).buf = buf;
119 (*ctxt).options = options;
120 (*ctxt).format = if (options & XML_SAVE_FORMAT) != 0 {
121 1
122 } else {
123 0
124 };
125 (*ctxt).no_decl = if (options & XML_SAVE_NO_DECL) != 0 {
126 1
127 } else {
128 0
129 };
130 (*ctxt).no_empty = if (options & XML_SAVE_NO_EMPTY) != 0 {
131 1
132 } else {
133 0
134 };
135 ctxt
136}
137
138unsafe fn encoding_handler(
140 encoding: *const c_char,
141) -> *mut crate::abi::structs::_xmlCharEncodingHandler {
142 if encoding.is_null() {
143 return ptr::null_mut();
144 }
145 crate::xml::encoding::xmlFindCharEncodingHandler(encoding)
146}
147
148#[no_mangle]
154pub unsafe extern "C" fn xmlSaveToFd(
155 fd: c_int,
156 encoding: *const c_char,
157 options: c_int,
158) -> *mut _xmlSaveCtxt {
159 let enc = unsafe { encoding_handler(encoding) };
160 let out = io::output_buffer_create_fd(fd, enc);
161 unsafe { save_ctxt_new(out, options) }
162}
163
164#[no_mangle]
170pub unsafe extern "C" fn xmlSaveToFilename(
171 filename: *const c_char,
172 encoding: *const c_char,
173 options: c_int,
174) -> *mut _xmlSaveCtxt {
175 let enc = unsafe { encoding_handler(encoding) };
176 let out = io::output_buffer_create_filename(filename, enc, 0);
177 unsafe { save_ctxt_new(out, options) }
178}
179
180#[no_mangle]
186pub unsafe extern "C" fn xmlSaveToBuffer(
187 buffer: *mut crate::abi::structs::_xmlBuffer,
188 encoding: *const c_char,
189 options: c_int,
190) -> *mut _xmlSaveCtxt {
191 let enc = unsafe { encoding_handler(encoding) };
192 let out = io::output_buffer_create_buffer(buffer, enc);
193 unsafe { save_ctxt_new(out, options) }
194}
195
196#[no_mangle]
202pub unsafe extern "C" fn xmlSaveToIO(
203 iowrite: Option<xmlOutputWriteCallback>,
204 ioclose: Option<xmlOutputCloseCallback>,
205 ioctx: *mut core::ffi::c_void,
206 encoding: *const c_char,
207 options: c_int,
208) -> *mut _xmlSaveCtxt {
209 let enc = unsafe { encoding_handler(encoding) };
210 let out = io::output_buffer_create_io(iowrite, ioclose, ioctx, enc);
211 unsafe { save_ctxt_new(out, options) }
212}
213
214#[no_mangle]
223pub unsafe extern "C" fn xmlSaveDoc(ctxt: *mut _xmlSaveCtxt, doc: *mut _xmlDoc) -> c_long {
224 unsafe { save_doc_or_tree(ctxt, doc as *mut _xmlNode) }
225}
226
227#[no_mangle]
236pub unsafe extern "C" fn xmlSaveTree(ctxt: *mut _xmlSaveCtxt, node: *mut _xmlNode) -> c_long {
237 unsafe { save_doc_or_tree(ctxt, node) }
238}
239
240unsafe fn save_doc_or_tree(ctxt: *mut _xmlSaveCtxt, node: *mut _xmlNode) -> c_long {
241 if ctxt.is_null() || node.is_null() {
242 return -1;
243 }
244 let buf = io::buf_create(-1);
245 if buf.is_null() {
246 return -1;
247 }
248 let indent = (*ctxt).indent;
249 let format = (*ctxt).format;
250 let no_decl = (*ctxt).no_decl;
251 crate::xml::tree::serialize_node_opts(node, buf, format, 0, indent, no_decl);
252
253 let before = io::buf_length(buf);
254 let content = io::buf_content(buf);
255 let ret = if before > 0 && !content.is_null() {
256 io::output_buffer_write((*ctxt).buf, before, content as *const c_char)
257 } else {
258 0
259 };
260 io::buf_free(buf);
261 if ret < 0 {
262 -1
263 } else {
264 ret as c_long
265 }
266}
267
268#[no_mangle]
274pub unsafe extern "C" fn xmlSaveFlush(ctxt: *mut _xmlSaveCtxt) -> c_int {
275 if ctxt.is_null() {
276 return -1;
277 }
278 io::output_buffer_flush((*ctxt).buf)
279}
280
281#[no_mangle]
293pub unsafe extern "C" fn xmlSaveClose(ctxt: *mut _xmlSaveCtxt) -> c_int {
294 if ctxt.is_null() {
295 return -1;
296 }
297 let flush_ret = if (*ctxt).buf.is_null() {
298 -1
299 } else {
300 io::output_buffer_flush((*ctxt).buf)
301 };
302 if !(*ctxt).buf.is_null() {
304 io::output_buffer_close((*ctxt).buf);
305 }
306 if !(*ctxt).indent.is_null() {
307 libc::free((*ctxt).indent as *mut libc::c_void);
308 }
309 libc::free(ctxt as *mut libc::c_void);
310 flush_ret
311}
312
313#[no_mangle]
325pub unsafe extern "C" fn xmlSaveFinish(ctxt: *mut _xmlSaveCtxt) -> c_int {
326 if ctxt.is_null() {
327 return -1;
328 }
329 let ret = if (*ctxt).buf.is_null() {
330 -1
331 } else {
332 io::output_buffer_close((*ctxt).buf)
333 };
334 if !(*ctxt).indent.is_null() {
335 libc::free((*ctxt).indent as *mut libc::c_void);
336 }
337 libc::free(ctxt as *mut libc::c_void);
338 if ret < 0 {
339 -ret
340 } else {
341 0
342 }
343}
344
345#[no_mangle]
353pub unsafe extern "C" fn xmlSaveSetIndentString(
354 ctxt: *mut _xmlSaveCtxt,
355 indent: *const c_char,
356) -> c_int {
357 if ctxt.is_null() || indent.is_null() {
361 return -1;
362 }
363 let len = libc::strlen(indent) as usize;
364 if len == 0 || len > 60 {
365 return -1;
366 }
367 if !(*ctxt).indent.is_null() {
368 libc::free((*ctxt).indent as *mut libc::c_void);
369 (*ctxt).indent = ptr::null_mut();
370 }
371 let copy = libc::malloc(len + 1) as *mut xmlChar;
372 if copy.is_null() {
373 return -1;
374 }
375 libc::memcpy(
376 copy as *mut libc::c_void,
377 indent as *const libc::c_void,
378 len + 1,
379 );
380 (*ctxt).indent = copy;
381 0
382}
383
384#[no_mangle]
390pub unsafe extern "C" fn xmlSaveSetEscape(
391 ctxt: *mut _xmlSaveCtxt,
392 escape: Option<xmlCharEncodingOutputFunc>,
393) -> c_int {
394 if ctxt.is_null() {
395 return -1;
396 }
397 (*ctxt).escape = escape;
398 0
399}
400
401#[no_mangle]
407pub unsafe extern "C" fn xmlSaveSetAttrEscape(
408 ctxt: *mut _xmlSaveCtxt,
409 escape: Option<xmlCharEncodingOutputFunc>,
410) -> c_int {
411 if ctxt.is_null() {
412 return -1;
413 }
414 (*ctxt).attrEscape = escape;
415 0
416}
417
418unsafe fn save_ctxt_wrap(buf: *mut _xmlOutputBuffer, options: c_int) -> *mut _xmlSaveCtxt {
422 if buf.is_null() {
423 return ptr::null_mut();
424 }
425 let ctxt = libc::calloc(1, core::mem::size_of::<_xmlSaveCtxt>()) as *mut _xmlSaveCtxt;
426 if ctxt.is_null() {
427 return ptr::null_mut();
428 }
429 (*ctxt).buf = buf;
430 (*ctxt).options = options;
431 (*ctxt).format = if (options & XML_SAVE_FORMAT) != 0 {
432 1
433 } else {
434 0
435 };
436 (*ctxt).no_decl = if (options & XML_SAVE_NO_DECL) != 0 {
437 1
438 } else {
439 0
440 };
441 (*ctxt).no_empty = if (options & XML_SAVE_NO_EMPTY) != 0 {
442 1
443 } else {
444 0
445 };
446 ctxt
447}
448
449#[no_mangle]
458pub unsafe extern "C" fn xmlSaveFormatFileTo(
459 buf: *mut _xmlOutputBuffer,
460 cur: *mut _xmlDoc,
461 encoding: *const c_char,
462 format: c_int,
463) -> c_int {
464 let _ = encoding;
465 let options = if format != 0 { XML_SAVE_FORMAT } else { 0 };
466 let ctxt = unsafe { save_ctxt_wrap(buf, options) };
467 if ctxt.is_null() {
468 return -1;
469 }
470 let ret = unsafe { xmlSaveDoc(ctxt, cur) };
471 let close_ret = unsafe { xmlSaveClose(ctxt) };
472 if ret < 0 {
473 -1
474 } else {
475 close_ret
476 }
477}
478
479#[no_mangle]
487pub unsafe extern "C" fn xmlSaveFileTo(
488 buf: *mut _xmlOutputBuffer,
489 cur: *mut _xmlDoc,
490 encoding: *const c_char,
491) -> c_int {
492 unsafe { xmlSaveFormatFileTo(buf, cur, encoding, 0) }
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498 use crate::xml::tree::new_doc;
499
500 fn doc_with_root() -> *mut _xmlDoc {
501 unsafe {
502 let doc = new_doc(c"1.0".as_ptr() as *const xmlChar);
503 let root =
504 crate::xml::tree::new_node(ptr::null_mut(), c"root".as_ptr() as *const xmlChar);
505 crate::xml::tree::doc_set_root_element(doc, root);
506 doc
507 }
508 }
509
510 #[test]
511 fn test_save_to_buffer_format_and_nodes() {
512 unsafe {
513 let doc = doc_with_root();
514 let buf = io::buf_create(-1);
515 let ctxt = xmlSaveToBuffer(buf, ptr::null(), XML_SAVE_FORMAT);
516 assert!(!ctxt.is_null());
517 assert!(xmlSaveDoc(ctxt, doc) >= 0);
518 assert_eq!(xmlSaveFinish(ctxt), 0);
519 let content = io::buf_content(buf);
520 let len = io::buf_length(buf);
521 let s = core::slice::from_raw_parts(content, len as usize);
522 let expected = "<?xml version=\"1.0\"?>\n<root/>\n";
523 assert_eq!(s, expected.as_bytes());
524 crate::xml::tree::free_doc(doc);
525 io::buf_free(buf);
526 }
527 }
528
529 #[test]
530 fn test_save_no_decl() {
531 unsafe {
532 let doc = doc_with_root();
533 let buf = io::buf_create(-1);
534 let ctxt = xmlSaveToBuffer(buf, ptr::null(), XML_SAVE_NO_DECL);
535 assert!(!ctxt.is_null());
536 xmlSaveDoc(ctxt, doc);
537 xmlSaveFinish(ctxt);
538 let content = io::buf_content(buf);
539 let len = io::buf_length(buf);
540 let s = core::slice::from_raw_parts(content, len as usize);
541 assert_eq!(s, b"<root/>\n");
542 crate::xml::tree::free_doc(doc);
543 io::buf_free(buf);
544 }
545 }
546
547 #[test]
548 fn test_save_set_indent_string() {
549 unsafe {
550 let doc = doc_with_root();
551 let child =
552 crate::xml::tree::new_node(ptr::null_mut(), c"child".as_ptr() as *const xmlChar);
553 crate::xml::tree::add_child(crate::xml::tree::doc_get_root_element(doc), child);
554 let buf = io::buf_create(-1);
555 let ctxt = xmlSaveToBuffer(buf, ptr::null(), XML_SAVE_FORMAT);
556 assert!(!ctxt.is_null());
557 assert_eq!(
558 xmlSaveSetIndentString(ctxt, c"\t".as_ptr() as *const c_char),
559 0
560 );
561 xmlSaveDoc(ctxt, doc);
562 xmlSaveFinish(ctxt);
563 let content = io::buf_content(buf);
564 let len = io::buf_length(buf);
565 let s = core::slice::from_raw_parts(content, len as usize);
566 let expected = "<?xml version=\"1.0\"?>\n<root>\n\t<child/>\n</root>\n";
567 assert_eq!(s, expected.as_bytes());
568 crate::xml::tree::free_doc(doc);
569 io::buf_free(buf);
570 }
571 }
572
573 #[test]
574 fn test_save_close_null_and_errors() {
575 unsafe {
576 assert!(xmlSaveToFd(-1, ptr::null(), 0).is_null());
577 assert_eq!(xmlSaveFlush(ptr::null_mut()), -1);
578 assert_eq!(xmlSaveFinish(ptr::null_mut()), -1);
579 assert_eq!(xmlSaveClose(ptr::null_mut()), -1);
580 assert_eq!(xmlSaveSetIndentString(ptr::null_mut(), ptr::null()), -1);
581 assert_eq!(xmlSaveSetEscape(ptr::null_mut(), None), -1);
582 assert_eq!(xmlSaveSetAttrEscape(ptr::null_mut(), None), -1);
583 assert_eq!(xmlSaveDoc(ptr::null_mut(), ptr::null_mut()), -1);
584 assert_eq!(xmlSaveTree(ptr::null_mut(), ptr::null_mut()), -1);
585 }
586 }
587}