1#![allow(
17 missing_docs,
18 non_snake_case,
19 non_camel_case_types,
20 non_upper_case_globals
21)]
22
23use core::ffi::c_void;
24use core::ptr;
25use std::os::raw::{c_char, c_int, c_uint};
26
27use crate::abi::allocator;
28
29use crate::abi::structs::*;
30use crate::abi::types::*;
31use crate::xml::io;
32use crate::xml::tree;
33
34#[derive(Clone, Copy, PartialEq, Eq, Debug)]
44enum WriterState {
45 None,
47 Element,
49 Attribute,
51 CData,
53 Comment,
55 PI,
57 DTD,
59 DTDText,
61 DTDElem,
63 DTDElemText,
65 DTDAttr,
67 DTDAttrText,
69 DTDEntity,
71 DTDEntityText,
73 DTDNotation,
75 XMLDecl,
77}
78
79pub struct XmlTextWriter {
92 output: *mut _xmlOutputBuffer,
94 indent: c_int,
96 indent_string: Vec<u8>,
98 qchar: u8,
100 doindent: bool,
102 depth: c_int,
104 stack: Vec<Vec<u8>>,
106 encoding: Vec<u8>,
108 errors: Vec<String>,
110 state: WriterState,
112 doc: *mut _xmlDoc,
114 in_start_tag: bool,
116 elem_stack: Vec<(Vec<u8>, Vec<u8>)>,
119 entity_pe: bool,
122 encoder_active: bool,
126 pending_ns: Vec<(Vec<u8>, Vec<u8>)>,
129 dtd_depth: c_int,
132}
133
134impl XmlTextWriter {
135 unsafe fn new(output: *mut _xmlOutputBuffer) -> *mut Self {
141 let writer = allocator::xmlMallocZero(size_of::<XmlTextWriter>() as usize) as *mut Self;
142 if writer.is_null() {
143 return ptr::null_mut();
144 }
145 unsafe {
146 (*writer).output = output;
147 (*writer).indent = 0;
148 (*writer).indent_string = b" \0".to_vec();
149 (*writer).qchar = b'"';
150 (*writer).doindent = true;
151 (*writer).depth = 0;
152 (*writer).stack = Vec::new();
153 (*writer).encoding = b"UTF-8\0".to_vec();
154 (*writer).errors = Vec::new();
155 (*writer).state = WriterState::None;
156 (*writer).doc = ptr::null_mut();
157 (*writer).in_start_tag = false;
158 (*writer).elem_stack = Vec::new();
159 (*writer).entity_pe = false;
160 (*writer).encoder_active = false;
161 (*writer).pending_ns = Vec::new();
162 (*writer).dtd_depth = 0;
163 }
164 writer
165 }
166
167 unsafe fn write_raw(&mut self, data: *const u8, len: c_int) -> c_int {
173 if self.output.is_null() || data.is_null() || len <= 0 {
174 return -1;
175 }
176 let rc = io::output_buffer_write(self.output, len, data as *const c_char);
177 if self.encoder_active {
180 0
181 } else {
182 rc
183 }
184 }
185
186 unsafe fn write_str(&mut self, s: *const u8) -> c_int {
188 if self.output.is_null() || s.is_null() {
189 return -1;
190 }
191 let rc = io::output_buffer_write_string(self.output, s as *const c_char);
192 if self.encoder_active {
193 0
194 } else {
195 rc
196 }
197 }
198
199 unsafe fn write_slice(&mut self, slice: &[u8]) -> c_int {
203 if self.output.is_null() || slice.is_empty() {
204 return -1;
205 }
206 let rc = io::output_buffer_write(
207 self.output,
208 slice.len() as c_int,
209 slice.as_ptr() as *const c_char,
210 );
211 if self.encoder_active {
212 0
213 } else {
214 rc
215 }
216 }
217
218 unsafe fn write_byte(&mut self, b: u8) -> c_int {
220 if self.output.is_null() {
221 return -1;
222 }
223 let rc = io::output_buffer_write_char(self.output, b as c_char);
224 if self.encoder_active {
225 0
226 } else {
227 rc
228 }
229 }
230
231 unsafe fn write_indent(&mut self) -> c_int {
235 if self.indent == 0 {
236 return 0;
237 }
238 let indent_str = self.indent_string.clone();
242 let body = if indent_str.last() == Some(&0) {
243 &indent_str[..indent_str.len() - 1]
244 } else {
245 &indent_str[..]
246 };
247 let count = self.depth + self.dtd_depth;
248 for _ in 0..count {
249 self.write_slice(body);
250 }
251 count
252 }
253
254 unsafe fn close_start_tag(&mut self) -> (bool, c_int) {
262 if self.in_start_tag {
263 self.in_start_tag = false;
264 let mut sum: c_int = self.flush_pending_ns();
265 sum += self.write_byte(b'>');
266 (true, sum)
267 } else {
268 (false, 0)
269 }
270 }
271
272 unsafe fn flush_pending_ns(&mut self) -> c_int {
275 let mut sum: c_int = 0;
276 let pending = core::mem::take(&mut self.pending_ns);
277 for (prefix, uri) in pending {
278 sum += self.write_byte(b' ');
279 if prefix.is_empty() {
280 sum += self.write_slice(b"xmlns=\"");
281 } else {
282 sum += self.write_slice(b"xmlns:");
283 sum += self.write_slice(&prefix);
284 sum += self.write_slice(b"=\"");
285 }
286 sum += self.write_slice(&uri);
287 sum += self.write_byte(b'"');
288 }
289 sum
290 }
291
292 fn can_write_content(&self) -> bool {
294 matches!(
295 self.state,
296 WriterState::None
297 | WriterState::Element
298 | WriterState::Attribute
299 | WriterState::CData
300 | WriterState::Comment
301 | WriterState::PI
302 | WriterState::DTD
303 | WriterState::DTDElem
304 | WriterState::DTDAttr
305 | WriterState::DTDEntity
306 | WriterState::DTDNotation
307 | WriterState::XMLDecl
308 )
309 }
310}
311
312#[no_mangle]
328pub unsafe extern "C" fn xmlFreeTextWriter(writer: *mut XmlTextWriter) {
329 if writer.is_null() {
330 return;
331 }
332 if !(*writer).output.is_null() {
335 io::output_buffer_flush((*writer).output);
336 }
337 unsafe {
339 ptr::drop_in_place(&mut (*writer).indent_string);
340 ptr::drop_in_place(&mut (*writer).stack);
341 ptr::drop_in_place(&mut (*writer).encoding);
342 ptr::drop_in_place(&mut (*writer).errors);
343 ptr::drop_in_place(&mut (*writer).elem_stack);
344 }
345 unsafe { allocator::xmlFreeImpl(writer as *mut c_void) };
347}
348
349#[no_mangle]
365pub unsafe extern "C" fn xmlNewTextWriter(out: *mut _xmlOutputBuffer) -> *mut XmlTextWriter {
366 if out.is_null() {
367 return ptr::null_mut();
368 }
369 XmlTextWriter::new(out)
371}
372
373#[no_mangle]
385pub unsafe extern "C" fn xmlNewTextWriterFilename(
386 uri: *const c_char,
387 compression: c_int,
388) -> *mut XmlTextWriter {
389 if uri.is_null() {
390 return ptr::null_mut();
391 }
392 let out = io::output_buffer_create_filename(uri, ptr::null_mut(), compression);
394 if out.is_null() {
395 return ptr::null_mut();
396 }
397 XmlTextWriter::new(out)
398}
399
400#[no_mangle]
412pub unsafe extern "C" fn xmlNewTextWriterMemory(
413 buf: *mut _xmlBuffer,
414 compression: c_int,
415) -> *mut XmlTextWriter {
416 let _ = compression;
417 if buf.is_null() {
418 return ptr::null_mut();
419 }
420 let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
422 if out.is_null() {
423 return ptr::null_mut();
424 }
425 XmlTextWriter::new(out)
426}
427
428#[no_mangle]
440pub unsafe extern "C" fn xmlNewTextWriterDoc(
441 doc: *mut *mut _xmlDoc,
442 compression: c_int,
443) -> *mut XmlTextWriter {
444 let _ = compression;
445 if doc.is_null() {
446 return ptr::null_mut();
447 }
448 let new_doc = tree::new_doc(b"1.0\0" as *const u8);
451 if new_doc.is_null() {
452 return ptr::null_mut();
453 }
454 unsafe { *doc = new_doc };
455
456 let buf = io::buf_create(io::DEFAULT_BUFFER_SIZE as c_int);
458 if buf.is_null() {
459 tree::free_doc(new_doc);
460 return ptr::null_mut();
461 }
462
463 let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
464 if out.is_null() {
465 io::buf_free(buf);
466 tree::free_doc(new_doc);
467 return ptr::null_mut();
468 }
469
470 let writer = XmlTextWriter::new(out);
471 if !writer.is_null() {
472 unsafe { (*writer).doc = new_doc };
473 }
474 writer
475}
476
477#[no_mangle]
490pub unsafe extern "C" fn xmlNewTextWriterTree(
491 doc: *mut _xmlDoc,
492 node: *mut _xmlNode,
493 compression: c_int,
494) -> *mut XmlTextWriter {
495 let _ = compression;
496 let _ = node; if doc.is_null() {
498 return ptr::null_mut();
499 }
500
501 let buf = io::buf_create(io::DEFAULT_BUFFER_SIZE as c_int);
502 if buf.is_null() {
503 return ptr::null_mut();
504 }
505
506 let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
507 if out.is_null() {
508 io::buf_free(buf);
509 return ptr::null_mut();
510 }
511
512 let writer = XmlTextWriter::new(out);
513 if !writer.is_null() {
514 unsafe { (*writer).doc = doc };
515 }
516 writer
517}
518
519#[no_mangle]
541pub unsafe extern "C" fn xmlTextWriterStartDocument(
542 writer: *mut XmlTextWriter,
543 version: *const c_char,
544 encoding: *const c_char,
545 standalone: *const c_char,
546) -> c_int {
547 if writer.is_null() {
548 return -1;
549 }
550 let w = unsafe { &mut *writer };
552
553 let mut sum: c_int = 0;
556 sum += w.write_raw(b"<?xml version=" as *const u8, 14);
557 sum += w.write_byte(w.qchar);
558
559 let ver = if version.is_null() {
560 b"1.0\0" as *const u8
561 } else {
562 version as *const u8
563 };
564 sum += w.write_str(ver);
565 sum += w.write_byte(w.qchar);
566
567 if !encoding.is_null() {
568 sum += w.write_raw(b" encoding=" as *const u8, 10);
569 sum += w.write_byte(w.qchar);
570 sum += w.write_str(encoding as *const u8);
571 sum += w.write_byte(w.qchar);
572 w.encoder_active = true;
576 }
577
578 if !standalone.is_null() {
579 sum += w.write_raw(b" standalone=" as *const u8, 12);
580 sum += w.write_byte(w.qchar);
581 sum += w.write_str(standalone as *const u8);
582 sum += w.write_byte(w.qchar);
583 }
584
585 sum += w.write_raw(b"?>\n" as *const u8, 3);
586
587 w.state = WriterState::XMLDecl;
588 sum
589}
590
591#[no_mangle]
605pub unsafe extern "C" fn xmlTextWriterEndDocument(writer: *mut XmlTextWriter) -> c_int {
606 if writer.is_null() {
607 return -1;
608 }
609 let w = unsafe { &mut *writer };
611
612 let mut sum: c_int = 0;
614 while w.depth > 0 {
615 sum += xmlTextWriterEndElement(writer);
616 }
617
618 if w.indent == 0 {
621 sum += w.write_byte(b'\n');
622 }
623
624 if !w.output.is_null() {
626 sum += io::output_buffer_flush(w.output);
627 }
628
629 w.state = WriterState::None;
630 sum
631}
632
633#[no_mangle]
650pub unsafe extern "C" fn xmlTextWriterStartElement(
651 writer: *mut XmlTextWriter,
652 name: *const xmlChar,
653) -> c_int {
654 if writer.is_null() || name.is_null() {
655 return -1;
656 }
657 let w = unsafe { &mut *writer };
659
660 let (closed, cnt) = w.close_start_tag();
664 let mut sum: c_int = cnt;
665 if closed && w.indent != 0 {
666 sum += w.write_byte(b'\n');
667 }
668
669 sum += w.write_indent();
671
672 sum += w.write_byte(b'<');
674 sum += w.write_str(name);
675
676 let name_bytes = unsafe { c_str_to_vec(name) };
678 w.elem_stack.push((b"".to_vec(), name_bytes.clone()));
679 let stack_name = if name_bytes.last() == Some(&0) {
681 name_bytes[..name_bytes.len() - 1].to_vec()
682 } else {
683 name_bytes.clone()
684 };
685 w.stack.push(stack_name);
686 w.depth += 1;
687 w.in_start_tag = true;
688 w.state = WriterState::Element;
689
690 sum
691}
692
693#[no_mangle]
705pub unsafe extern "C" fn xmlTextWriterEndElement(writer: *mut XmlTextWriter) -> c_int {
706 if writer.is_null() {
707 return -1;
708 }
709 let w = unsafe { &mut *writer };
711
712 if w.depth <= 0 {
713 return -1;
714 }
715
716 let mut sum: c_int = 0;
721 if w.in_start_tag {
722 sum += w.flush_pending_ns();
723 sum += w.write_raw(b"/>" as *const u8, 2);
724 w.in_start_tag = false;
725 w.doindent = true;
726 w.stack.pop();
727 } else {
728 if w.indent != 0 && w.doindent {
729 sum += w.write_indent();
730 w.doindent = true;
731 } else {
732 w.doindent = true;
733 }
734 let name = w.stack.pop().unwrap_or_default();
735 sum += w.write_raw(b"</" as *const u8, 2);
736 sum += w.write_slice(&name);
737 sum += w.write_byte(b'>');
738 }
739
740 if w.indent != 0 {
741 sum += w.write_byte(b'\n');
742 }
743
744 w.depth -= 1;
745 w.elem_stack.pop();
746 w.state = WriterState::None;
747
748 sum
749}
750
751#[no_mangle]
767pub unsafe extern "C" fn xmlTextWriterStartElementNS(
768 writer: *mut XmlTextWriter,
769 prefix: *const xmlChar,
770 name: *const xmlChar,
771 namespaceURI: *const xmlChar,
772) -> c_int {
773 if writer.is_null() || name.is_null() {
774 return -1;
775 }
776 let w = unsafe { &mut *writer };
778
779 let (closed, cnt) = w.close_start_tag();
782 let mut sum: c_int = cnt;
783 if closed && w.indent != 0 {
784 sum += w.write_byte(b'\n');
785 }
786 sum += w.write_indent();
787
788 sum += w.write_byte(b'<');
789
790 let prefix_bytes = if prefix.is_null() {
791 Vec::new()
792 } else {
793 unsafe { c_str_to_vec(prefix) }
794 };
795
796 let name_bytes = unsafe { c_str_to_vec(name) };
797
798 if !prefix_bytes.is_empty() {
799 let p = if prefix_bytes.last() == Some(&0) {
801 &prefix_bytes[..prefix_bytes.len() - 1]
802 } else {
803 &prefix_bytes
804 };
805 sum += w.write_slice(p);
806 sum += w.write_byte(b':');
807 }
808 let n = if name_bytes.last() == Some(&0) {
810 &name_bytes[..name_bytes.len() - 1]
811 } else {
812 &name_bytes
813 };
814 sum += w.write_slice(n);
815
816 if !namespaceURI.is_null() {
819 let ns_uri_bytes = unsafe { c_str_to_vec(namespaceURI) };
820 let uri_body = if ns_uri_bytes.last() == Some(&0) {
821 ns_uri_bytes[..ns_uri_bytes.len() - 1].to_vec()
822 } else {
823 ns_uri_bytes
824 };
825 let prefix_body = if prefix_bytes.last() == Some(&0) {
826 prefix_bytes[..prefix_bytes.len() - 1].to_vec()
827 } else {
828 prefix_bytes.clone()
829 };
830 w.pending_ns.push((prefix_body, uri_body));
831 }
832
833 w.elem_stack.push((prefix_bytes, name_bytes.clone()));
834 let stack_name = if name_bytes.last() == Some(&0) {
836 name_bytes[..name_bytes.len() - 1].to_vec()
837 } else {
838 name_bytes
839 };
840 w.stack.push(stack_name);
841 w.depth += 1;
842 w.in_start_tag = true;
843 w.state = WriterState::Element;
844
845 sum
846}
847
848#[no_mangle]
863pub unsafe extern "C" fn xmlTextWriterWriteElement(
864 writer: *mut XmlTextWriter,
865 name: *const xmlChar,
866 content: *const xmlChar,
867) -> c_int {
868 if writer.is_null() || name.is_null() {
869 return -1;
870 }
871 let ret = xmlTextWriterStartElement(writer, name);
872 if ret == -1 {
873 return ret;
874 }
875 if !content.is_null() {
876 let ret2 = xmlTextWriterWriteString(writer, content);
877 if ret2 == -1 {
878 return ret2;
879 }
880 }
881 xmlTextWriterEndElement(writer)
882}
883
884#[no_mangle]
901pub unsafe extern "C" fn xmlTextWriterWriteElementNS(
902 writer: *mut XmlTextWriter,
903 prefix: *const xmlChar,
904 name: *const xmlChar,
905 nsURI: *const xmlChar,
906 content: *const xmlChar,
907) -> c_int {
908 if writer.is_null() || name.is_null() {
909 return -1;
910 }
911 let ret = xmlTextWriterStartElementNS(writer, prefix, name, nsURI);
912 if ret == -1 {
913 return ret;
914 }
915 if !content.is_null() {
916 let ret2 = xmlTextWriterWriteString(writer, content);
917 if ret2 == -1 {
918 return ret2;
919 }
920 }
921 xmlTextWriterEndElement(writer)
922}
923
924#[no_mangle]
936pub unsafe extern "C" fn xmlTextWriterFullEndElement(writer: *mut XmlTextWriter) -> c_int {
937 if writer.is_null() {
938 return -1;
939 }
940 let w = unsafe { &mut *writer };
942
943 if w.depth <= 0 {
944 return -1;
945 }
946
947 let mut sum: c_int = 0;
950 if w.in_start_tag {
951 sum += w.write_byte(b'>');
952 w.in_start_tag = false;
953 }
954
955 if w.indent != 0 && w.doindent {
956 sum += w.write_indent();
957 w.doindent = true;
958 } else {
959 w.doindent = true;
960 }
961
962 let name = w.stack.pop().unwrap_or_default();
964 sum += w.write_raw(b"</" as *const u8, 2);
965 sum += w.write_slice(&name);
966 sum += w.write_byte(b'>');
967
968 if w.indent != 0 {
969 sum += w.write_byte(b'\n');
970 }
971
972 w.depth -= 1;
973 w.elem_stack.pop();
974 w.state = WriterState::None;
975
976 sum
977}
978
979#[no_mangle]
998pub unsafe extern "C" fn xmlTextWriterWriteAttribute(
999 writer: *mut XmlTextWriter,
1000 name: *const xmlChar,
1001 content: *const xmlChar,
1002) -> c_int {
1003 if writer.is_null() || name.is_null() || content.is_null() {
1004 return -1;
1005 }
1006 let w = unsafe { &mut *writer };
1008
1009 if !w.in_start_tag {
1010 return -1;
1011 }
1012
1013 let mut sum: c_int = 0;
1015 sum += w.write_byte(b' ');
1016 sum += w.write_str(name);
1017 sum += w.write_raw(b"=" as *const u8, 1);
1018 sum += w.write_byte(w.qchar);
1019
1020 sum += unsafe { write_attr_escaped(w, content) };
1022
1023 sum += w.write_byte(w.qchar);
1024 w.state = WriterState::Element;
1027
1028 sum
1029}
1030
1031#[no_mangle]
1048pub unsafe extern "C" fn xmlTextWriterWriteAttributeNS(
1049 writer: *mut XmlTextWriter,
1050 prefix: *const xmlChar,
1051 name: *const xmlChar,
1052 nsURI: *const xmlChar,
1053 content: *const xmlChar,
1054) -> c_int {
1055 let _ = nsURI;
1056 if writer.is_null() || name.is_null() || content.is_null() {
1057 return -1;
1058 }
1059 let w = unsafe { &mut *writer };
1061
1062 if !w.in_start_tag {
1063 return -1;
1064 }
1065
1066 let mut sum: c_int = 0;
1067 sum += w.write_byte(b' ');
1068
1069 if !prefix.is_null() {
1070 sum += w.write_str(prefix);
1071 sum += w.write_byte(b':');
1072 }
1073 sum += w.write_str(name);
1074
1075 sum += w.write_raw(b"=" as *const u8, 1);
1076 sum += w.write_byte(w.qchar);
1077
1078 sum += unsafe { write_attr_escaped(w, content) };
1080
1081 sum += w.write_byte(w.qchar);
1082 w.state = WriterState::Element;
1085
1086 sum
1087}
1088
1089#[no_mangle]
1104
1105#[no_mangle]
1118pub unsafe extern "C" fn xmlTextWriterStartAttribute(
1119 writer: *mut XmlTextWriter,
1120 name: *const xmlChar,
1121) -> c_int {
1122 if writer.is_null() || name.is_null() {
1123 return -1;
1124 }
1125 let w = unsafe { &mut *writer };
1127
1128 if !w.in_start_tag {
1129 return -1;
1130 }
1131
1132 let mut sum: c_int = 0;
1133 sum += w.write_byte(b' ');
1134 sum += w.write_str(name);
1135 sum += w.write_raw(b"=" as *const u8, 1);
1136 sum += w.write_byte(w.qchar);
1137 w.state = WriterState::Attribute;
1138
1139 sum
1140}
1141
1142#[no_mangle]
1158pub unsafe extern "C" fn xmlTextWriterStartAttributeNS(
1159 writer: *mut XmlTextWriter,
1160 prefix: *const xmlChar,
1161 name: *const xmlChar,
1162 nsURI: *const xmlChar,
1163) -> c_int {
1164 let _ = nsURI;
1165 if writer.is_null() || name.is_null() {
1166 return -1;
1167 }
1168 let w = unsafe { &mut *writer };
1170
1171 if !w.in_start_tag {
1172 return -1;
1173 }
1174
1175 let mut sum: c_int = 0;
1176 sum += w.write_byte(b' ');
1177 if !prefix.is_null() {
1178 sum += w.write_str(prefix);
1179 sum += w.write_byte(b':');
1180 }
1181 sum += w.write_str(name);
1182 sum += w.write_raw(b"=" as *const u8, 1);
1183 sum += w.write_byte(w.qchar);
1184 w.state = WriterState::Attribute;
1185
1186 sum
1187}
1188
1189#[no_mangle]
1201pub unsafe extern "C" fn xmlTextWriterEndAttribute(writer: *mut XmlTextWriter) -> c_int {
1202 if writer.is_null() {
1203 return -1;
1204 }
1205 let w = unsafe { &mut *writer };
1207
1208 if w.state != WriterState::Attribute {
1209 return -1;
1210 }
1211
1212 w.write_byte(w.qchar);
1213 w.state = WriterState::Element;
1214
1215 1
1216}
1217
1218unsafe fn encode_special_chars(content: *const xmlChar) -> Vec<u8> {
1229 let mut out = Vec::new();
1230 let mut p = content;
1231 unsafe {
1232 while !p.is_null() && *p != 0 {
1233 match *p {
1236 b'&' => out.extend_from_slice(b"&"),
1237 b'<' => out.extend_from_slice(b"<"),
1238 b'>' => out.extend_from_slice(b">"),
1239 b'"' => out.extend_from_slice(b"""),
1240 c => out.push(c),
1241 }
1242 p = p.add(1);
1243 }
1244 }
1245 out.push(0);
1246 out
1247}
1248
1249unsafe fn write_attr_escaped(w: &mut XmlTextWriter, content: *const xmlChar) -> c_int {
1258 let mut sum: c_int = 0;
1259 let mut p = content;
1260 unsafe {
1261 while !p.is_null() && *p != 0 {
1262 let c = *p;
1263 sum += match c {
1268 b'\n' => w.write_slice(b" "),
1269 b'\r' => w.write_slice(b" "),
1270 b'\t' => w.write_slice(b"	"),
1271 b'&' => w.write_slice(b"&"),
1272 b'<' => w.write_slice(b"<"),
1273 b'>' => w.write_slice(b">"),
1274 b'"' => w.write_slice(b"""),
1275 c => w.write_byte(c),
1276 };
1277 p = p.add(1);
1278 }
1279 }
1280 sum
1281}
1282
1283#[no_mangle]
1301pub unsafe extern "C" fn xmlTextWriterWriteString(
1302 writer: *mut XmlTextWriter,
1303 content: *const xmlChar,
1304) -> c_int {
1305 if writer.is_null() || content.is_null() {
1306 return -1;
1307 }
1308 let w = unsafe { &mut *writer };
1310
1311 match w.state {
1312 WriterState::Attribute => unsafe { write_attr_escaped(w, content) },
1313 WriterState::Element => {
1314 let esc = unsafe { encode_special_chars(content) };
1315 let (_, cnt) = w.close_start_tag();
1316 let mut sum: c_int = cnt;
1317 if !esc.is_empty() {
1318 sum += w.write_slice(&esc[..esc.len() - 1]);
1319 }
1320 w.doindent = false;
1321 sum
1322 }
1323 WriterState::None if w.depth > 0 => {
1324 let esc = unsafe { encode_special_chars(content) };
1326 let mut sum: c_int = 0;
1327 if !esc.is_empty() {
1328 sum += w.write_slice(&esc[..esc.len() - 1]);
1329 }
1330 w.doindent = false;
1331 sum
1332 }
1333 _ => {
1334 let rc = unsafe { xmlTextWriterWriteRaw(writer, content) };
1339 w.doindent = false;
1340 rc
1341 }
1342 }
1343}
1344
1345#[no_mangle]
1362pub unsafe extern "C" fn xmlTextWriterWriteRaw(
1363 writer: *mut XmlTextWriter,
1364 content: *const xmlChar,
1365) -> c_int {
1366 if writer.is_null() || content.is_null() {
1367 return -1;
1368 }
1369 let w = unsafe { &mut *writer };
1371
1372 let mut sum: c_int = 0;
1374 match w.state {
1375 WriterState::Element => {
1376 let (_, cnt) = w.close_start_tag();
1377 sum += cnt;
1378 }
1379 WriterState::PI => {
1380 sum += w.write_byte(b' ');
1381 }
1382 WriterState::DTD => {
1383 w.state = WriterState::DTDText;
1384 if w.indent != 0 {
1385 sum += w.write_slice(b" [\n");
1386 } else {
1387 sum += w.write_slice(b" [");
1388 }
1389 }
1390 WriterState::DTDElem => {
1391 sum += w.write_byte(b' ');
1392 w.state = WriterState::DTDElemText;
1393 }
1394 WriterState::DTDAttr => {
1395 sum += w.write_byte(b' ');
1396 w.state = WriterState::DTDAttrText;
1397 }
1398 WriterState::DTDEntity => {
1399 sum += w.write_byte(b' ');
1400 sum += w.write_byte(w.qchar);
1401 w.state = WriterState::DTDEntityText;
1402 }
1403 _ => {}
1404 }
1405
1406 if w.indent != 0 {
1407 w.doindent = false;
1408 }
1409
1410 sum += w.write_str(content);
1411 sum
1412}
1413
1414#[no_mangle]
1429pub unsafe extern "C" fn xmlTextWriterWriteRawLen(
1430 writer: *mut XmlTextWriter,
1431 content: *const xmlChar,
1432 len: c_int,
1433) -> c_int {
1434 if writer.is_null() || content.is_null() || len < 0 {
1435 return -1;
1436 }
1437 let w = unsafe { &mut *writer };
1439
1440 let mut sum: c_int = 0;
1442 match w.state {
1443 WriterState::Element => {
1444 let (_, cnt) = w.close_start_tag();
1445 sum += cnt;
1446 }
1447 WriterState::PI => {
1448 sum += w.write_byte(b' ');
1449 }
1450 WriterState::DTD => {
1451 w.state = WriterState::DTDText;
1452 if w.indent != 0 {
1453 sum += w.write_slice(b" [\n");
1454 } else {
1455 sum += w.write_slice(b" [");
1456 }
1457 }
1458 WriterState::DTDElem => {
1459 sum += w.write_byte(b' ');
1460 w.state = WriterState::DTDElemText;
1461 }
1462 WriterState::DTDAttr => {
1463 sum += w.write_byte(b' ');
1464 w.state = WriterState::DTDAttrText;
1465 }
1466 WriterState::DTDEntity => {
1467 sum += w.write_byte(b' ');
1468 sum += w.write_byte(w.qchar);
1469 w.state = WriterState::DTDEntityText;
1470 }
1471 _ => {}
1472 }
1473
1474 if w.indent != 0 {
1475 w.doindent = false;
1476 }
1477
1478 if len > 0 {
1479 sum += w.write_raw(content, len);
1480 }
1481 sum
1482}
1483
1484#[no_mangle]
1496
1497#[no_mangle]
1513pub unsafe extern "C" fn xmlTextWriterWriteBase64(
1514 writer: *mut XmlTextWriter,
1515 data: *const c_char,
1516 start: c_int,
1517 len: c_int,
1518) -> c_int {
1519 if writer.is_null() || data.is_null() || len <= 0 || start < 0 {
1520 return -1;
1521 }
1522 let w = unsafe { &mut *writer };
1524
1525 w.close_start_tag();
1526
1527 let data_slice =
1529 unsafe { core::slice::from_raw_parts(data.add(start as usize) as *const u8, len as usize) };
1530 let encoded = base64_encode(data_slice);
1531 w.write_slice(&encoded);
1532
1533 0
1534}
1535
1536#[no_mangle]
1552pub unsafe extern "C" fn xmlTextWriterWriteBinHex(
1553 writer: *mut XmlTextWriter,
1554 data: *const c_char,
1555 start: c_int,
1556 len: c_int,
1557) -> c_int {
1558 if writer.is_null() || data.is_null() || len <= 0 || start < 0 {
1559 return -1;
1560 }
1561 let w = unsafe { &mut *writer };
1563
1564 w.close_start_tag();
1565
1566 let data_slice =
1568 unsafe { core::slice::from_raw_parts(data.add(start as usize) as *const u8, len as usize) };
1569 let encoded = hex_encode(data_slice);
1570 w.write_slice(&encoded);
1571
1572 0
1573}
1574
1575#[no_mangle]
1588pub unsafe extern "C" fn xmlTextWriterWriteCDATA(
1589 writer: *mut XmlTextWriter,
1590 content: *const xmlChar,
1591) -> c_int {
1592 let mut sum: c_int = 0;
1593 let ret = unsafe { xmlTextWriterStartCDATA(writer) };
1594 if ret == -1 {
1595 return -1;
1596 }
1597 sum += ret;
1598 if !content.is_null() {
1599 let ret2 = unsafe { xmlTextWriterWriteString(writer, content) };
1600 if ret2 == -1 {
1601 return -1;
1602 }
1603 sum += ret2;
1604 }
1605 let ret3 = unsafe { xmlTextWriterEndCDATA(writer) };
1606 if ret3 == -1 {
1607 return -1;
1608 }
1609 sum + ret3
1610}
1611
1612#[no_mangle]
1624pub unsafe extern "C" fn xmlTextWriterStartCDATA(writer: *mut XmlTextWriter) -> c_int {
1625 if writer.is_null() {
1626 return -1;
1627 }
1628 let w = unsafe { &mut *writer };
1630
1631 let (closed, cnt) = w.close_start_tag();
1634 let mut sum: c_int = cnt;
1635 if closed && w.indent != 0 {
1636 sum += w.write_byte(b'\n');
1637 }
1638 sum += w.write_slice(b"<![CDATA[");
1639 w.state = WriterState::CData;
1640 sum
1641}
1642
1643#[no_mangle]
1655pub unsafe extern "C" fn xmlTextWriterEndCDATA(writer: *mut XmlTextWriter) -> c_int {
1656 if writer.is_null() {
1657 return -1;
1658 }
1659 let w = unsafe { &mut *writer };
1661 if w.state != WriterState::CData {
1662 return -1;
1663 }
1664 let sum: c_int = w.write_slice(b"]]>");
1665 w.state = WriterState::None;
1666 sum
1667}
1668
1669#[no_mangle]
1682pub unsafe extern "C" fn xmlTextWriterWriteComment(
1683 writer: *mut XmlTextWriter,
1684 content: *const xmlChar,
1685) -> c_int {
1686 let mut sum: c_int = 0;
1687 let ret = unsafe { xmlTextWriterStartComment(writer) };
1688 if ret < 0 {
1689 return -1;
1690 }
1691 sum += ret;
1692 let ret2 = unsafe { xmlTextWriterWriteString(writer, content) };
1693 if ret2 < 0 {
1694 return -1;
1695 }
1696 sum += ret2;
1697 let ret3 = unsafe { xmlTextWriterEndComment(writer) };
1698 if ret3 < 0 {
1699 return -1;
1700 }
1701 sum + ret3
1702}
1703
1704#[no_mangle]
1716pub unsafe extern "C" fn xmlTextWriterStartComment(writer: *mut XmlTextWriter) -> c_int {
1717 if writer.is_null() {
1718 return -1;
1719 }
1720 let w = unsafe { &mut *writer };
1722 let (closed, cnt) = w.close_start_tag();
1723 let mut sum: c_int = cnt;
1724 if closed && w.indent != 0 {
1725 sum += w.write_byte(b'\n');
1726 }
1727 sum += w.write_indent();
1728 sum += w.write_slice(b"<!--");
1729 w.state = WriterState::Comment;
1730 sum
1731}
1732
1733#[no_mangle]
1745pub unsafe extern "C" fn xmlTextWriterEndComment(writer: *mut XmlTextWriter) -> c_int {
1746 if writer.is_null() {
1747 return -1;
1748 }
1749 let w = unsafe { &mut *writer };
1751 if w.state != WriterState::Comment {
1752 return -1;
1753 }
1754 let mut sum: c_int = w.write_slice(b"-->");
1755 if w.indent != 0 {
1756 sum += w.write_byte(b'\n');
1757 }
1758 w.state = WriterState::None;
1759 sum
1760}
1761
1762#[no_mangle]
1777pub unsafe extern "C" fn xmlTextWriterWritePI(
1778 writer: *mut XmlTextWriter,
1779 target: *const xmlChar,
1780 content: *const xmlChar,
1781) -> c_int {
1782 let mut sum: c_int = 0;
1783 let ret = unsafe { xmlTextWriterStartPI(writer, target) };
1784 if ret == -1 {
1785 return -1;
1786 }
1787 sum += ret;
1788 if !content.is_null() {
1789 let ret2 = unsafe { xmlTextWriterWriteString(writer, content) };
1790 if ret2 == -1 {
1791 return -1;
1792 }
1793 sum += ret2;
1794 }
1795 let ret3 = unsafe { xmlTextWriterEndPI(writer) };
1796 if ret3 == -1 {
1797 return -1;
1798 }
1799 sum + ret3
1800}
1801
1802#[no_mangle]
1815pub unsafe extern "C" fn xmlTextWriterStartPI(
1816 writer: *mut XmlTextWriter,
1817 target: *const xmlChar,
1818) -> c_int {
1819 if writer.is_null() || target.is_null() || unsafe { *target } == 0 {
1820 return -1;
1821 }
1822 let w = unsafe { &mut *writer };
1824 let (closed, cnt) = w.close_start_tag();
1825 let mut sum: c_int = cnt;
1826 if closed && w.indent != 0 {
1827 sum += w.write_byte(b'\n');
1828 }
1829 sum += w.write_slice(b"<?");
1830 sum += w.write_str(target);
1831 w.state = WriterState::PI;
1834 sum
1835}
1836
1837#[no_mangle]
1849pub unsafe extern "C" fn xmlTextWriterEndPI(writer: *mut XmlTextWriter) -> c_int {
1850 if writer.is_null() {
1851 return -1;
1852 }
1853 let w = unsafe { &mut *writer };
1855 if w.state != WriterState::PI {
1856 return -1;
1857 }
1858 let mut sum: c_int = w.write_slice(b"?>");
1859 if w.indent != 0 {
1860 sum += w.write_byte(b'\n');
1861 }
1862 w.state = WriterState::None;
1863 sum
1864}
1865
1866#[no_mangle]
1887pub unsafe extern "C" fn xmlTextWriterWriteDTD(
1888 writer: *mut XmlTextWriter,
1889 name: *const xmlChar,
1890 pubid: *const xmlChar,
1891 sysid: *const xmlChar,
1892 subset: *const xmlChar,
1893) -> c_int {
1894 let mut sum: c_int = 0;
1895 let ret = unsafe { xmlTextWriterStartDTD(writer, name, pubid, sysid) };
1896 if ret == -1 {
1897 return ret;
1898 }
1899 sum += ret;
1900 if !subset.is_null() {
1901 let ret2 = unsafe { xmlTextWriterWriteString(writer, subset) };
1902 if ret2 == -1 {
1903 return ret2;
1904 }
1905 sum += ret2;
1906 }
1907 let ret3 = unsafe { xmlTextWriterEndDTD(writer) };
1908 if ret3 == -1 {
1909 return ret3;
1910 }
1911 sum + ret3
1912}
1913
1914#[no_mangle]
1929pub unsafe extern "C" fn xmlTextWriterWriteDTDElement(
1930 writer: *mut XmlTextWriter,
1931 name: *const xmlChar,
1932 content: *const xmlChar,
1933) -> c_int {
1934 if content.is_null() {
1935 return -1;
1936 }
1937 let mut sum: c_int = 0;
1938 let ret = unsafe { xmlTextWriterStartDTDElement(writer, name) };
1939 if ret == -1 {
1940 return ret;
1941 }
1942 sum += ret;
1943 let ret2 = unsafe { xmlTextWriterWriteString(writer, content) };
1944 if ret2 == -1 {
1945 return ret2;
1946 }
1947 sum += ret2;
1948 let ret3 = unsafe { xmlTextWriterEndDTDElement(writer) };
1949 if ret3 == -1 {
1950 return ret3;
1951 }
1952 sum + ret3
1953}
1954
1955#[no_mangle]
1970pub unsafe extern "C" fn xmlTextWriterWriteDTDAttribute(
1971 writer: *mut XmlTextWriter,
1972 name: *const xmlChar,
1973 content: *const xmlChar,
1974) -> c_int {
1975 if content.is_null() {
1976 return -1;
1977 }
1978 let mut sum: c_int = 0;
1982 let ret = unsafe { xmlTextWriterStartDTDAttlist(writer, name) };
1983 if ret == -1 {
1984 return ret;
1985 }
1986 sum += ret;
1987 let ret2 = unsafe { xmlTextWriterWriteString(writer, content) };
1988 if ret2 == -1 {
1989 return ret2;
1990 }
1991 sum += ret2;
1992 let ret3 = unsafe { xmlTextWriterEndDTDAttlist(writer) };
1993 if ret3 == -1 {
1994 return ret3;
1995 }
1996 sum + ret3
1997}
1998
1999#[no_mangle]
2014pub unsafe extern "C" fn xmlTextWriterWriteDTDEntity(
2015 writer: *mut XmlTextWriter,
2016 pe: c_int,
2017 name: *const xmlChar,
2018 pubid: *const xmlChar,
2019 sysid: *const xmlChar,
2020 ndataid: *const xmlChar,
2021 content: *const xmlChar,
2022) -> c_int {
2023 if content.is_null() && pubid.is_null() && sysid.is_null() {
2024 return -1;
2025 }
2026 if pe != 0 && !ndataid.is_null() {
2027 return -1;
2028 }
2029 if pubid.is_null() && sysid.is_null() {
2030 return unsafe { xmlTextWriterWriteDTDInternalEntity(writer, pe, name, content) };
2031 }
2032 unsafe { xmlTextWriterWriteDTDExternalEntity(writer, pe, name, pubid, sysid, ndataid) }
2033}
2034
2035#[no_mangle]
2042pub unsafe extern "C" fn xmlTextWriterWriteDTDInternalEntity(
2043 writer: *mut XmlTextWriter,
2044 pe: c_int,
2045 name: *const xmlChar,
2046 content: *const xmlChar,
2047) -> c_int {
2048 if name.is_null() || unsafe { *name } == 0 || content.is_null() {
2049 return -1;
2050 }
2051 let mut sum: c_int = 0;
2052 let ret = unsafe { xmlTextWriterStartDTDEntity(writer, pe, name) };
2053 if ret == -1 {
2054 return -1;
2055 }
2056 sum += ret;
2057 let ret2 = unsafe { xmlTextWriterWriteString(writer, content) };
2058 if ret2 == -1 {
2059 return -1;
2060 }
2061 sum += ret2;
2062 let ret3 = unsafe { xmlTextWriterEndDTDEntity(writer) };
2063 if ret3 == -1 {
2064 return -1;
2065 }
2066 sum + ret3
2067}
2068
2069#[no_mangle]
2077pub unsafe extern "C" fn xmlTextWriterWriteDTDExternalEntity(
2078 writer: *mut XmlTextWriter,
2079 pe: c_int,
2080 name: *const xmlChar,
2081 pubid: *const xmlChar,
2082 sysid: *const xmlChar,
2083 ndataid: *const xmlChar,
2084) -> c_int {
2085 if pubid.is_null() && sysid.is_null() {
2086 return -1;
2087 }
2088 if pe != 0 && !ndataid.is_null() {
2089 return -1;
2090 }
2091 let mut sum: c_int = 0;
2092 let ret = unsafe { xmlTextWriterStartDTDEntity(writer, pe, name) };
2093 if ret == -1 {
2094 return -1;
2095 }
2096 sum += ret;
2097 let ret2 =
2098 unsafe { xmlTextWriterWriteDTDExternalEntityContents(writer, pubid, sysid, ndataid) };
2099 if ret2 < 0 {
2100 return -1;
2101 }
2102 sum += ret2;
2103 let ret3 = unsafe { xmlTextWriterEndDTDEntity(writer) };
2104 if ret3 == -1 {
2105 return -1;
2106 }
2107 sum + ret3
2108}
2109
2110#[no_mangle]
2118pub unsafe extern "C" fn xmlTextWriterWriteDTDExternalEntityContents(
2119 writer: *mut XmlTextWriter,
2120 pubid: *const xmlChar,
2121 sysid: *const xmlChar,
2122 ndataid: *const xmlChar,
2123) -> c_int {
2124 if writer.is_null() {
2125 return -1;
2126 }
2127 let w = unsafe { &mut *writer };
2128 if w.state != WriterState::DTDEntity {
2131 return -1;
2132 }
2133 if w.entity_pe && !ndataid.is_null() {
2134 return -1;
2136 }
2137 let mut sum: c_int = 0;
2138 if !pubid.is_null() {
2139 if sysid.is_null() {
2140 return -1;
2141 }
2142 sum += w.write_slice(b" PUBLIC ");
2143 sum += w.write_byte(w.qchar);
2144 sum += w.write_str(pubid);
2145 sum += w.write_byte(w.qchar);
2146 }
2147 if !sysid.is_null() {
2148 if pubid.is_null() {
2149 sum += w.write_slice(b" SYSTEM");
2150 }
2151 sum += w.write_byte(b' ');
2152 sum += w.write_byte(w.qchar);
2153 sum += w.write_str(sysid);
2154 sum += w.write_byte(w.qchar);
2155 }
2156 if !ndataid.is_null() {
2157 sum += w.write_slice(b" NDATA ");
2158 sum += w.write_str(ndataid);
2159 }
2160 sum
2161}
2162
2163#[no_mangle]
2179pub unsafe extern "C" fn xmlTextWriterWriteDTDNotation(
2180 writer: *mut XmlTextWriter,
2181 name: *const xmlChar,
2182 pubid: *const xmlChar,
2183 sysid: *const xmlChar,
2184) -> c_int {
2185 if writer.is_null() || name.is_null() || unsafe { *name } == 0 {
2186 return -1;
2187 }
2188 let w = unsafe { &mut *writer };
2189 let mut sum: c_int = 0;
2190 if w.state == WriterState::DTD {
2191 sum += w.write_slice(b" [");
2193 if w.indent != 0 {
2194 sum += w.write_byte(b'\n');
2195 }
2196 w.state = WriterState::DTDText;
2197 } else if w.state != WriterState::DTDText {
2198 return -1;
2199 }
2200 sum += w.write_indent();
2201 sum += w.write_slice(b"<!NOTATION ");
2202 sum += w.write_str(name);
2203 if !pubid.is_null() {
2204 sum += w.write_slice(b" PUBLIC ");
2205 sum += w.write_byte(w.qchar);
2206 sum += w.write_str(pubid);
2207 sum += w.write_byte(w.qchar);
2208 }
2209 if !sysid.is_null() {
2210 if pubid.is_null() {
2211 sum += w.write_slice(b" SYSTEM");
2212 }
2213 sum += w.write_byte(b' ');
2214 sum += w.write_byte(w.qchar);
2215 sum += w.write_str(sysid);
2216 sum += w.write_byte(w.qchar);
2217 }
2218 sum += w.write_byte(b'>');
2219 sum
2220}
2221
2222#[no_mangle]
2240pub unsafe extern "C" fn xmlTextWriterStartDTD(
2241 writer: *mut XmlTextWriter,
2242 name: *const xmlChar,
2243 pubid: *const xmlChar,
2244 sysid: *const xmlChar,
2245) -> c_int {
2246 if writer.is_null() || name.is_null() || unsafe { *name } == 0 {
2247 return -1;
2248 }
2249 let w = unsafe { &mut *writer };
2251 if w.depth > 0 {
2252 return -1;
2254 }
2255
2256 let mut sum: c_int = 0;
2257 sum += w.write_slice(b"<!DOCTYPE ");
2258 sum += w.write_str(name);
2259
2260 if !pubid.is_null() {
2261 if sysid.is_null() {
2262 return -1;
2264 }
2265 if w.indent != 0 {
2266 sum += w.write_byte(b'\n');
2267 } else {
2268 sum += w.write_byte(b' ');
2269 }
2270 sum += w.write_slice(b"PUBLIC ");
2271 sum += w.write_byte(w.qchar);
2272 sum += w.write_str(pubid);
2273 sum += w.write_byte(w.qchar);
2274 }
2275 if !sysid.is_null() {
2276 if pubid.is_null() {
2277 if w.indent != 0 {
2278 sum += w.write_byte(b'\n');
2279 } else {
2280 sum += w.write_byte(b' ');
2281 }
2282 sum += w.write_slice(b"SYSTEM ");
2283 } else if w.indent != 0 {
2284 sum += w.write_slice(b"\n ");
2286 } else {
2287 sum += w.write_byte(b' ');
2288 }
2289 sum += w.write_byte(w.qchar);
2290 sum += w.write_str(sysid);
2291 sum += w.write_byte(w.qchar);
2292 }
2293
2294 w.state = WriterState::DTD;
2295 sum
2296}
2297
2298#[no_mangle]
2310pub unsafe extern "C" fn xmlTextWriterEndDTD(writer: *mut XmlTextWriter) -> c_int {
2311 if writer.is_null() {
2312 return -1;
2313 }
2314 let w = unsafe { &mut *writer };
2316
2317 if w.state != WriterState::DTD && w.state != WriterState::DTDText {
2318 return -1;
2319 }
2320 let mut sum: c_int = 0;
2321 if w.state == WriterState::DTDText {
2322 sum += w.write_byte(b']');
2323 }
2324 sum += w.write_byte(b'>');
2325 if w.indent != 0 {
2326 sum += w.write_byte(b'\n');
2327 }
2328 w.state = WriterState::None;
2329 sum
2330}
2331
2332unsafe fn dtd_child_transition(w: &mut XmlTextWriter) -> bool {
2335 match w.state {
2336 WriterState::DTD => {
2337 w.write_slice(b" [");
2338 if w.indent != 0 {
2339 w.write_byte(b'\n');
2340 }
2341 w.state = WriterState::DTDText;
2342 true
2343 }
2344 WriterState::DTDText => true,
2345 _ => false,
2346 }
2347}
2348
2349#[no_mangle]
2362pub unsafe extern "C" fn xmlTextWriterStartDTDElement(
2363 writer: *mut XmlTextWriter,
2364 name: *const xmlChar,
2365) -> c_int {
2366 if writer.is_null() || name.is_null() || unsafe { *name } == 0 {
2367 return -1;
2368 }
2369 let w = unsafe { &mut *writer };
2371 if !unsafe { dtd_child_transition(w) } {
2372 return -1;
2373 }
2374 w.dtd_depth += 1;
2375 let mut sum: c_int = 0;
2376 sum += w.write_indent();
2377 sum += w.write_slice(b"<!ELEMENT ");
2378 sum += w.write_str(name);
2379 w.state = WriterState::DTDElem;
2380 sum
2381}
2382
2383#[no_mangle]
2395pub unsafe extern "C" fn xmlTextWriterEndDTDElement(writer: *mut XmlTextWriter) -> c_int {
2396 if writer.is_null() {
2397 return -1;
2398 }
2399 let w = unsafe { &mut *writer };
2401 if w.state != WriterState::DTDElem && w.state != WriterState::DTDElemText {
2402 return -1;
2403 }
2404 let mut sum: c_int = w.write_byte(b'>');
2405 if w.indent != 0 {
2406 sum += w.write_byte(b'\n');
2407 }
2408 w.state = WriterState::DTDText;
2409 w.dtd_depth -= 1;
2410 sum
2411}
2412
2413#[no_mangle]
2426pub unsafe extern "C" fn xmlTextWriterStartDTDAttribute(
2427 writer: *mut XmlTextWriter,
2428 name: *const xmlChar,
2429) -> c_int {
2430 if writer.is_null() || name.is_null() || unsafe { *name } == 0 {
2431 return -1;
2432 }
2433 let w = unsafe { &mut *writer };
2435 if !unsafe { dtd_child_transition(w) } {
2436 return -1;
2437 }
2438 w.dtd_depth += 1;
2439 let mut sum: c_int = 0;
2440 sum += w.write_indent();
2441 sum += w.write_slice(b"<!ATTLIST ");
2442 sum += w.write_str(name);
2443 w.state = WriterState::DTDAttr;
2444 sum
2445}
2446
2447#[no_mangle]
2459pub unsafe extern "C" fn xmlTextWriterEndDTDAttribute(writer: *mut XmlTextWriter) -> c_int {
2460 if writer.is_null() {
2461 return -1;
2462 }
2463 let w = unsafe { &mut *writer };
2465 if w.state != WriterState::DTDAttr && w.state != WriterState::DTDAttrText {
2466 return -1;
2467 }
2468 let mut sum: c_int = w.write_byte(b'>');
2469 if w.indent != 0 {
2470 sum += w.write_byte(b'\n');
2471 }
2472 w.state = WriterState::DTDText;
2473 w.dtd_depth -= 1;
2474 sum
2475}
2476
2477#[no_mangle]
2490pub unsafe extern "C" fn xmlTextWriterStartDTDEntity(
2491 writer: *mut XmlTextWriter,
2492 pe: c_int,
2493 name: *const xmlChar,
2494) -> c_int {
2495 if writer.is_null() || name.is_null() || unsafe { *name } == 0 {
2496 return -1;
2497 }
2498 let w = unsafe { &mut *writer };
2500 if !unsafe { dtd_child_transition(w) } {
2501 return -1;
2502 }
2503 w.dtd_depth += 1;
2504 let mut sum: c_int = 0;
2505 sum += w.write_indent();
2506 sum += w.write_slice(b"<!ENTITY ");
2507 if pe != 0 {
2508 sum += w.write_slice(b"% ");
2509 }
2510 sum += w.write_str(name);
2511 w.state = WriterState::DTDEntity;
2512 w.entity_pe = pe != 0;
2513 sum
2514}
2515
2516#[no_mangle]
2528pub unsafe extern "C" fn xmlTextWriterEndDTDEntity(writer: *mut XmlTextWriter) -> c_int {
2529 if writer.is_null() {
2530 return -1;
2531 }
2532 let w = unsafe { &mut *writer };
2534 let mut sum: c_int = 0;
2535 if w.state == WriterState::DTDEntityText {
2536 sum += w.write_byte(w.qchar);
2537 } else if w.state != WriterState::DTDEntity {
2538 return -1;
2539 }
2540 sum += w.write_byte(b'>');
2541 if w.indent != 0 {
2542 sum += w.write_byte(b'\n');
2543 }
2544 w.state = WriterState::DTDText;
2545 w.entity_pe = false;
2546 w.dtd_depth -= 1;
2547 sum
2548}
2549
2550#[no_mangle]
2557pub unsafe extern "C" fn xmlTextWriterStartDTDAttlist(
2558 writer: *mut XmlTextWriter,
2559 name: *const xmlChar,
2560) -> c_int {
2561 if writer.is_null() || name.is_null() || unsafe { *name } == 0 {
2562 return -1;
2563 }
2564 let w = unsafe { &mut *writer };
2566 if !unsafe { dtd_child_transition(w) } {
2567 return -1;
2568 }
2569 w.dtd_depth += 1;
2570 let mut sum: c_int = 0;
2571 sum += w.write_indent();
2572 sum += w.write_slice(b"<!ATTLIST ");
2573 sum += w.write_str(name);
2574 w.state = WriterState::DTDAttr;
2575 sum
2576}
2577
2578#[no_mangle]
2584pub unsafe extern "C" fn xmlTextWriterEndDTDAttlist(writer: *mut XmlTextWriter) -> c_int {
2585 if writer.is_null() {
2586 return -1;
2587 }
2588 let w = unsafe { &mut *writer };
2590 if w.state != WriterState::DTDAttr && w.state != WriterState::DTDAttrText {
2591 return -1;
2592 }
2593 let mut sum: c_int = w.write_byte(b'>');
2594 if w.indent != 0 {
2595 sum += w.write_byte(b'\n');
2596 }
2597 w.state = WriterState::DTDText;
2598 w.dtd_depth -= 1;
2599 sum
2600}
2601
2602#[no_mangle]
2610pub unsafe extern "C" fn xmlTextWriterWriteDTDAttlist(
2611 writer: *mut XmlTextWriter,
2612 name: *const xmlChar,
2613 content: *const xmlChar,
2614) -> c_int {
2615 if content.is_null() {
2616 return -1;
2617 }
2618 let mut sum: c_int = 0;
2619 let ret = unsafe { xmlTextWriterStartDTDAttlist(writer, name) };
2620 if ret == -1 {
2621 return -1;
2622 }
2623 sum += ret;
2624 let ret2 = unsafe { xmlTextWriterWriteString(writer, content) };
2625 if ret2 == -1 {
2626 return -1;
2627 }
2628 sum += ret2;
2629 let ret3 = unsafe { xmlTextWriterEndDTDAttlist(writer) };
2630 if ret3 == -1 {
2631 return -1;
2632 }
2633 sum + ret3
2634}
2635
2636#[no_mangle]
2652pub unsafe extern "C" fn xmlTextWriterFlush(writer: *mut XmlTextWriter) -> c_int {
2653 if writer.is_null() {
2654 return -1;
2655 }
2656 let w = unsafe { &mut *writer };
2658
2659 if w.output.is_null() {
2660 return -1;
2661 }
2662
2663 w.close_start_tag();
2665
2666 io::output_buffer_flush(w.output)
2667}
2668
2669#[no_mangle]
2681pub unsafe extern "C" fn xmlTextWriterSetIndent(
2682 writer: *mut XmlTextWriter,
2683 indent: c_int,
2684) -> c_int {
2685 if writer.is_null() {
2686 return -1;
2687 }
2688 unsafe { (*writer).indent = indent };
2690 0
2691}
2692
2693#[no_mangle]
2706pub unsafe extern "C" fn xmlTextWriterSetIndentString(
2707 writer: *mut XmlTextWriter,
2708 str: *const xmlChar,
2709) -> c_int {
2710 if writer.is_null() || str.is_null() {
2711 return -1;
2712 }
2713 let w = unsafe { &mut *writer };
2715 w.indent_string = unsafe { c_str_to_vec(str) };
2716 0
2717}
2718
2719#[no_mangle]
2729pub unsafe extern "C" fn xmlTextWriterSetQuoteChar(
2730 writer: *mut XmlTextWriter,
2731 quotechar: xmlChar,
2732) -> c_int {
2733 if writer.is_null() || (quotechar != b'\'' && quotechar != b'"') {
2734 return -1;
2735 }
2736 unsafe { (*writer).qchar = quotechar };
2738 0
2739}
2740
2741#[no_mangle]
2755pub unsafe extern "C" fn xmlTextWriterClose(writer: *mut XmlTextWriter) -> c_int {
2756 if writer.is_null() {
2757 return crate::abi::types::XML_ERR_ARGUMENT as c_int;
2758 }
2759 let w = unsafe { &mut *writer };
2760 if w.output.is_null() {
2761 return crate::abi::types::XML_ERR_ARGUMENT as c_int;
2762 }
2763 let result = io::output_buffer_close(w.output);
2764 w.output = ptr::null_mut();
2765 if result >= 0 {
2766 crate::abi::types::XML_ERR_OK as c_int
2767 } else {
2768 -result
2769 }
2770}
2771
2772#[repr(C)]
2781#[derive(Clone, Copy)]
2782struct VaListTag {
2783 gp_offset: c_uint,
2784 fp_offset: c_uint,
2785 overflow_arg_area: *mut c_void,
2786 reg_save_area: *mut c_void,
2787}
2788
2789unsafe extern "C" {
2791 fn vsnprintf(s: *mut c_char, n: usize, format: *const c_char, ap: *mut VaListTag) -> c_int;
2792}
2793
2794unsafe fn vformat_buf(format: *const c_char, args: *mut VaListTag) -> Result<Vec<u8>, ()> {
2805 let mut size: usize = 8192;
2806 loop {
2807 let mut buf = vec![0u8; size];
2808 let mut copy = unsafe { core::ptr::read(args) };
2811 let n = unsafe { vsnprintf(buf.as_mut_ptr() as *mut c_char, size, format, &mut copy) };
2812 if n >= 0 && (n as usize) < size {
2813 buf.truncate(n as usize);
2814 return Ok(buf);
2815 }
2816 if size >= (1 << 26) {
2817 return Err(());
2818 }
2819 size *= 2;
2820 }
2821}
2822
2823#[no_mangle]
2827pub unsafe extern "C" fn xmlTextWriterWriteVFormatRaw(
2828 writer: *mut XmlTextWriter,
2829 format: *const c_char,
2830 argptr: *mut VaListTag,
2831) -> c_int {
2832 if writer.is_null() {
2833 return -1;
2834 }
2835 let buf = match unsafe { vformat_buf(format, argptr) } {
2836 Ok(b) => b,
2837 Err(()) => return -1,
2838 };
2839 unsafe { xmlTextWriterWriteRaw(writer, buf.as_ptr() as *const xmlChar) }
2840}
2841
2842#[no_mangle]
2843pub unsafe extern "C" fn xmlTextWriterWriteVFormatString(
2844 writer: *mut XmlTextWriter,
2845 format: *const c_char,
2846 argptr: *mut VaListTag,
2847) -> c_int {
2848 if writer.is_null() || format.is_null() {
2849 return -1;
2850 }
2851 let buf = match unsafe { vformat_buf(format, argptr) } {
2852 Ok(b) => b,
2853 Err(()) => return -1,
2854 };
2855 unsafe { xmlTextWriterWriteString(writer, buf.as_ptr() as *const xmlChar) }
2856}
2857
2858#[no_mangle]
2859pub unsafe extern "C" fn xmlTextWriterWriteVFormatComment(
2860 writer: *mut XmlTextWriter,
2861 format: *const c_char,
2862 argptr: *mut VaListTag,
2863) -> c_int {
2864 if writer.is_null() {
2865 return -1;
2866 }
2867 let buf = match unsafe { vformat_buf(format, argptr) } {
2868 Ok(b) => b,
2869 Err(()) => return -1,
2870 };
2871 unsafe { xmlTextWriterWriteComment(writer, buf.as_ptr() as *const xmlChar) }
2872}
2873
2874#[no_mangle]
2875pub unsafe extern "C" fn xmlTextWriterWriteVFormatCDATA(
2876 writer: *mut XmlTextWriter,
2877 format: *const c_char,
2878 argptr: *mut VaListTag,
2879) -> c_int {
2880 if writer.is_null() {
2881 return -1;
2882 }
2883 let buf = match unsafe { vformat_buf(format, argptr) } {
2884 Ok(b) => b,
2885 Err(()) => return -1,
2886 };
2887 unsafe { xmlTextWriterWriteCDATA(writer, buf.as_ptr() as *const xmlChar) }
2888}
2889
2890#[no_mangle]
2891pub unsafe extern "C" fn xmlTextWriterWriteVFormatPI(
2892 writer: *mut XmlTextWriter,
2893 target: *const xmlChar,
2894 format: *const c_char,
2895 argptr: *mut VaListTag,
2896) -> c_int {
2897 if writer.is_null() {
2898 return -1;
2899 }
2900 let buf = match unsafe { vformat_buf(format, argptr) } {
2901 Ok(b) => b,
2902 Err(()) => return -1,
2903 };
2904 unsafe { xmlTextWriterWritePI(writer, target, buf.as_ptr() as *const xmlChar) }
2905}
2906
2907#[no_mangle]
2908pub unsafe extern "C" fn xmlTextWriterWriteVFormatElement(
2909 writer: *mut XmlTextWriter,
2910 name: *const xmlChar,
2911 format: *const c_char,
2912 argptr: *mut VaListTag,
2913) -> c_int {
2914 if writer.is_null() {
2915 return -1;
2916 }
2917 let buf = match unsafe { vformat_buf(format, argptr) } {
2918 Ok(b) => b,
2919 Err(()) => return -1,
2920 };
2921 unsafe { xmlTextWriterWriteElement(writer, name, buf.as_ptr() as *const xmlChar) }
2922}
2923
2924#[no_mangle]
2925pub unsafe extern "C" fn xmlTextWriterWriteVFormatElementNS(
2926 writer: *mut XmlTextWriter,
2927 prefix: *const xmlChar,
2928 name: *const xmlChar,
2929 namespaceURI: *const xmlChar,
2930 format: *const c_char,
2931 argptr: *mut VaListTag,
2932) -> c_int {
2933 if writer.is_null() {
2934 return -1;
2935 }
2936 let buf = match unsafe { vformat_buf(format, argptr) } {
2937 Ok(b) => b,
2938 Err(()) => return -1,
2939 };
2940 unsafe {
2941 xmlTextWriterWriteElementNS(
2942 writer,
2943 prefix,
2944 name,
2945 namespaceURI,
2946 buf.as_ptr() as *const xmlChar,
2947 )
2948 }
2949}
2950
2951#[no_mangle]
2952pub unsafe extern "C" fn xmlTextWriterWriteVFormatAttribute(
2953 writer: *mut XmlTextWriter,
2954 name: *const xmlChar,
2955 format: *const c_char,
2956 argptr: *mut VaListTag,
2957) -> c_int {
2958 if writer.is_null() {
2959 return -1;
2960 }
2961 let buf = match unsafe { vformat_buf(format, argptr) } {
2962 Ok(b) => b,
2963 Err(()) => return -1,
2964 };
2965 unsafe { xmlTextWriterWriteAttribute(writer, name, buf.as_ptr() as *const xmlChar) }
2966}
2967
2968#[no_mangle]
2969pub unsafe extern "C" fn xmlTextWriterWriteVFormatAttributeNS(
2970 writer: *mut XmlTextWriter,
2971 prefix: *const xmlChar,
2972 name: *const xmlChar,
2973 namespaceURI: *const xmlChar,
2974 format: *const c_char,
2975 argptr: *mut VaListTag,
2976) -> c_int {
2977 if writer.is_null() {
2978 return -1;
2979 }
2980 let buf = match unsafe { vformat_buf(format, argptr) } {
2981 Ok(b) => b,
2982 Err(()) => return -1,
2983 };
2984 unsafe {
2985 xmlTextWriterWriteAttributeNS(
2986 writer,
2987 prefix,
2988 name,
2989 namespaceURI,
2990 buf.as_ptr() as *const xmlChar,
2991 )
2992 }
2993}
2994
2995#[no_mangle]
2996pub unsafe extern "C" fn xmlTextWriterWriteVFormatDTD(
2997 writer: *mut XmlTextWriter,
2998 name: *const xmlChar,
2999 pubid: *const xmlChar,
3000 sysid: *const xmlChar,
3001 format: *const c_char,
3002 argptr: *mut VaListTag,
3003) -> c_int {
3004 if writer.is_null() {
3005 return -1;
3006 }
3007 let buf = match unsafe { vformat_buf(format, argptr) } {
3008 Ok(b) => b,
3009 Err(()) => return -1,
3010 };
3011 unsafe { xmlTextWriterWriteDTD(writer, name, pubid, sysid, buf.as_ptr() as *const xmlChar) }
3012}
3013
3014#[no_mangle]
3015pub unsafe extern "C" fn xmlTextWriterWriteVFormatDTDElement(
3016 writer: *mut XmlTextWriter,
3017 name: *const xmlChar,
3018 format: *const c_char,
3019 argptr: *mut VaListTag,
3020) -> c_int {
3021 if writer.is_null() {
3022 return -1;
3023 }
3024 let buf = match unsafe { vformat_buf(format, argptr) } {
3025 Ok(b) => b,
3026 Err(()) => return -1,
3027 };
3028 unsafe { xmlTextWriterWriteDTDElement(writer, name, buf.as_ptr() as *const xmlChar) }
3029}
3030
3031#[no_mangle]
3032pub unsafe extern "C" fn xmlTextWriterWriteVFormatDTDAttlist(
3033 writer: *mut XmlTextWriter,
3034 name: *const xmlChar,
3035 format: *const c_char,
3036 argptr: *mut VaListTag,
3037) -> c_int {
3038 if writer.is_null() {
3039 return -1;
3040 }
3041 let buf = match unsafe { vformat_buf(format, argptr) } {
3042 Ok(b) => b,
3043 Err(()) => return -1,
3044 };
3045 unsafe { xmlTextWriterWriteDTDAttlist(writer, name, buf.as_ptr() as *const xmlChar) }
3046}
3047
3048#[no_mangle]
3049pub unsafe extern "C" fn xmlTextWriterWriteVFormatDTDInternalEntity(
3050 writer: *mut XmlTextWriter,
3051 pe: c_int,
3052 name: *const xmlChar,
3053 format: *const c_char,
3054 argptr: *mut VaListTag,
3055) -> c_int {
3056 if writer.is_null() {
3057 return -1;
3058 }
3059 let buf = match unsafe { vformat_buf(format, argptr) } {
3060 Ok(b) => b,
3061 Err(()) => return -1,
3062 };
3063 unsafe { xmlTextWriterWriteDTDInternalEntity(writer, pe, name, buf.as_ptr() as *const xmlChar) }
3064}
3065
3066#[cfg(target_arch = "x86_64")]
3088mod format_shims {
3089 use super::*;
3090
3091 macro_rules! vfmt_shim {
3096 ($name:ident, $vname:ident, $gp:literal, $aptr:tt, ($($pty:ty),*)) => {
3097 #[no_mangle]
3102 pub unsafe extern "C" fn $name() -> c_int {
3103 unsafe {
3104 core::arch::asm!(
3105 "sub rsp, 240",
3106 "mov [rsp+0], rdi",
3107 "mov [rsp+8], rsi",
3108 "mov [rsp+16], rdx",
3109 "mov [rsp+24], rcx",
3110 "mov [rsp+32], r8",
3111 "mov [rsp+40], r9",
3112 "movaps [rsp+48], xmm0",
3113 "movaps [rsp+64], xmm1",
3114 "movaps [rsp+80], xmm2",
3115 "movaps [rsp+96], xmm3",
3116 "movaps [rsp+112], xmm4",
3117 "movaps [rsp+128], xmm5",
3118 "movaps [rsp+144], xmm6",
3119 "movaps [rsp+160], xmm7",
3120 concat!("mov dword ptr [rsp+176], ", $gp),
3121 "mov dword ptr [rsp+180], 48",
3122 "lea rax, [rsp+256]",
3123 "mov [rsp+184], rax",
3124 "lea rax, [rsp]",
3125 "mov [rsp+192], rax",
3126 concat!("lea ", stringify!($aptr), ", [rsp+176]"),
3127 concat!("call ", stringify!($vname)),
3128 "add rsp, 240",
3129 "add rsp, 8",
3130 "ret",
3131 options(noreturn),
3132 );
3133 }
3134 }
3135 };
3136 }
3137
3138 vfmt_shim!(
3139 xmlTextWriterWriteFormatRaw,
3140 xmlTextWriterWriteVFormatRaw,
3141 16,
3142 rdx,
3143 (*mut XmlTextWriter, *const c_char)
3144 );
3145 vfmt_shim!(
3146 xmlTextWriterWriteFormatString,
3147 xmlTextWriterWriteVFormatString,
3148 16,
3149 rdx,
3150 (*mut XmlTextWriter, *const c_char)
3151 );
3152 vfmt_shim!(
3153 xmlTextWriterWriteFormatComment,
3154 xmlTextWriterWriteVFormatComment,
3155 16,
3156 rdx,
3157 (*mut XmlTextWriter, *const c_char)
3158 );
3159 vfmt_shim!(
3160 xmlTextWriterWriteFormatCDATA,
3161 xmlTextWriterWriteVFormatCDATA,
3162 16,
3163 rdx,
3164 (*mut XmlTextWriter, *const c_char)
3165 );
3166 vfmt_shim!(
3167 xmlTextWriterWriteFormatPI,
3168 xmlTextWriterWriteVFormatPI,
3169 24,
3170 rcx,
3171 (*mut XmlTextWriter, *const xmlChar, *const c_char)
3172 );
3173 vfmt_shim!(
3174 xmlTextWriterWriteFormatElement,
3175 xmlTextWriterWriteVFormatElement,
3176 24,
3177 rcx,
3178 (*mut XmlTextWriter, *const xmlChar, *const c_char)
3179 );
3180 vfmt_shim!(
3181 xmlTextWriterWriteFormatAttribute,
3182 xmlTextWriterWriteVFormatAttribute,
3183 24,
3184 rcx,
3185 (*mut XmlTextWriter, *const xmlChar, *const c_char)
3186 );
3187 vfmt_shim!(
3188 xmlTextWriterWriteFormatDTDElement,
3189 xmlTextWriterWriteVFormatDTDElement,
3190 24,
3191 rcx,
3192 (*mut XmlTextWriter, *const xmlChar, *const c_char)
3193 );
3194 vfmt_shim!(
3195 xmlTextWriterWriteFormatDTDAttlist,
3196 xmlTextWriterWriteVFormatDTDAttlist,
3197 24,
3198 rcx,
3199 (*mut XmlTextWriter, *const xmlChar, *const c_char)
3200 );
3201 vfmt_shim!(
3202 xmlTextWriterWriteFormatDTDInternalEntity,
3203 xmlTextWriterWriteVFormatDTDInternalEntity,
3204 32,
3205 r8,
3206 (*mut XmlTextWriter, c_int, *const xmlChar, *const c_char)
3207 );
3208 vfmt_shim!(
3209 xmlTextWriterWriteFormatDTD,
3210 xmlTextWriterWriteVFormatDTD,
3211 40,
3212 r9,
3213 (
3214 *mut XmlTextWriter,
3215 *const xmlChar,
3216 *const xmlChar,
3217 *const xmlChar,
3218 *const c_char
3219 )
3220 );
3221 vfmt_shim!(
3222 xmlTextWriterWriteFormatElementNS,
3223 xmlTextWriterWriteVFormatElementNS,
3224 40,
3225 r9,
3226 (
3227 *mut XmlTextWriter,
3228 *const xmlChar,
3229 *const xmlChar,
3230 *const xmlChar,
3231 *const c_char
3232 )
3233 );
3234 vfmt_shim!(
3235 xmlTextWriterWriteFormatAttributeNS,
3236 xmlTextWriterWriteVFormatAttributeNS,
3237 40,
3238 r9,
3239 (
3240 *mut XmlTextWriter,
3241 *const xmlChar,
3242 *const xmlChar,
3243 *const xmlChar,
3244 *const c_char
3245 )
3246 );
3247}
3248
3249#[cfg(not(target_arch = "x86_64"))]
3252mod format_fallback {
3253 use super::*;
3254 macro_rules! fmt_stub {
3255 ($($name:ident),*) => {$(
3256 #[no_mangle]
3257 pub unsafe extern "C" fn $name(_writer: *mut XmlTextWriter, _format: *const c_char) -> c_int {
3258 -1
3259 }
3260 )*};
3261 }
3262 fmt_stub!(
3263 xmlTextWriterWriteFormatRaw,
3264 xmlTextWriterWriteFormatString,
3265 xmlTextWriterWriteFormatComment,
3266 xmlTextWriterWriteFormatCDATA,
3267 xmlTextWriterWriteFormatPI,
3268 xmlTextWriterWriteFormatElement,
3269 xmlTextWriterWriteFormatElementNS,
3270 xmlTextWriterWriteFormatAttribute,
3271 xmlTextWriterWriteFormatAttributeNS,
3272 xmlTextWriterWriteFormatDTD,
3273 xmlTextWriterWriteFormatDTDElement,
3274 xmlTextWriterWriteFormatDTDAttlist,
3275 xmlTextWriterWriteFormatDTDInternalEntity
3276 );
3277}
3278
3279unsafe fn c_str_to_vec(s: *const u8) -> Vec<u8> {
3289 if s.is_null() {
3290 return Vec::new();
3291 }
3292 let len = tree::xml_strlen(s);
3293 let mut v = Vec::with_capacity(len as usize + 1);
3294 unsafe {
3295 for i in 0..len as isize {
3296 v.push(*s.offset(i));
3297 }
3298 v.push(0);
3299 }
3300 v
3301}
3302
3303fn base64_encode(data: &[u8]) -> Vec<u8> {
3305 const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3306 let mut result = Vec::with_capacity((data.len() + 2) / 3 * 4);
3307 for chunk in data.chunks(3) {
3308 let b0 = chunk[0];
3309 let b1 = chunk.get(1).copied().unwrap_or(0);
3310 let b2 = chunk.get(2).copied().unwrap_or(0);
3311
3312 result.push(CHARS[((b0 >> 2) & 0x3F) as usize]);
3313 result.push(CHARS[(((b0 << 4) | (b1 >> 4)) & 0x3F) as usize]);
3314 result.push(if chunk.len() > 1 {
3315 CHARS[(((b1 << 2) | (b2 >> 6)) & 0x3F) as usize]
3316 } else {
3317 b'='
3318 });
3319 result.push(if chunk.len() > 2 {
3320 CHARS[(b2 & 0x3F) as usize]
3321 } else {
3322 b'='
3323 });
3324 }
3325 result
3326}
3327
3328fn hex_encode(data: &[u8]) -> Vec<u8> {
3330 const CHARS: &[u8] = b"0123456789abcdef";
3331 let mut result = Vec::with_capacity(data.len() * 2);
3332 for &b in data {
3333 result.push(CHARS[((b >> 4) & 0x0F) as usize]);
3334 result.push(CHARS[(b & 0x0F) as usize]);
3335 }
3336 result
3337}
3338
3339#[cfg(test)]
3344mod tests {
3345 use super::*;
3346 use core::ptr;
3347
3348 unsafe fn create_test_writer() -> (*mut XmlTextWriter, *mut _xmlBuffer) {
3350 let buf = io::buf_create(256);
3351 assert!(!buf.is_null(), "buf_create failed");
3352 let out = io::output_buffer_create_buffer(buf, ptr::null_mut());
3353 assert!(!out.is_null(), "output_buffer_create_buffer failed");
3354 let writer = xmlNewTextWriter(out);
3355 assert!(!writer.is_null(), "xmlNewTextWriter failed");
3356 (writer, buf)
3357 }
3358
3359 unsafe fn buf_to_string(buf: *mut _xmlBuffer) -> String {
3361 let content = io::buf_content(buf);
3362 let len = io::buf_length(buf);
3363 if content.is_null() || len <= 0 {
3364 return String::new();
3365 }
3366 let slice = unsafe { core::slice::from_raw_parts(content, len as usize) };
3367 String::from_utf8_lossy(slice).to_string()
3368 }
3369
3370 unsafe fn flush_and_get(writer: *mut XmlTextWriter, buf: *mut _xmlBuffer) -> String {
3372 xmlTextWriterFlush(writer);
3373 buf_to_string(buf)
3374 }
3375
3376 #[test]
3381 fn test_write_simple_document() {
3382 unsafe {
3383 let (writer, buf) = create_test_writer();
3384
3385 let r = xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3386 assert_eq!(r, 0, "StartDocument failed");
3387
3388 let r = xmlTextWriterStartElement(writer, b"root\0" as *const u8);
3389 assert_eq!(r, 0, "StartElement(root) failed");
3390
3391 let r = xmlTextWriterWriteString(writer, b"Hello, World!\0" as *const u8);
3392 assert_eq!(r, 0, "WriteString failed");
3393
3394 let r = xmlTextWriterEndElement(writer);
3395 assert_eq!(r, 0, "EndElement failed");
3396
3397 let r = xmlTextWriterEndDocument(writer);
3398 assert!(r > 0, "EndDocument failed (rc={})", r);
3399
3400 let result = flush_and_get(writer, buf);
3401 assert!(
3402 result.contains("<?xml version=\"1.0\"?>"),
3403 "Missing XML declaration. Got: {}",
3404 result
3405 );
3406 assert!(
3407 result.contains("<root>"),
3408 "Missing <root> start tag. Got: {}",
3409 result
3410 );
3411 assert!(
3412 result.contains("Hello, World!"),
3413 "Missing content. Got: {}",
3414 result
3415 );
3416 assert!(
3417 result.contains("</root>"),
3418 "Missing </root> end tag. Got: {}",
3419 result
3420 );
3421
3422 xmlFreeTextWriter(writer);
3423 io::buf_free(buf);
3424 }
3425 }
3426
3427 #[test]
3432 fn test_write_element_with_attributes() {
3433 unsafe {
3434 let (writer, buf) = create_test_writer();
3435
3436 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3437 xmlTextWriterStartElement(writer, b"root\0" as *const u8);
3438 xmlTextWriterWriteAttribute(writer, b"id\0" as *const u8, b"123\0" as *const u8);
3439 xmlTextWriterWriteAttribute(
3440 writer,
3441 b"name\0" as *const u8,
3442 b"test & demo\0" as *const u8,
3443 );
3444 xmlTextWriterEndElement(writer);
3445 xmlTextWriterEndDocument(writer);
3446
3447 let result = flush_and_get(writer, buf);
3448 assert!(
3449 result.contains("id=\"123\""),
3450 "Missing id attribute. Got: {}",
3451 result
3452 );
3453 assert!(
3454 result.contains("name=\"test & demo\""),
3455 "Missing or improperly escaped name attribute. Got: {}",
3456 result
3457 );
3458 assert!(
3459 result.contains("<root"),
3460 "Missing root element. Got: {}",
3461 result
3462 );
3463
3464 xmlFreeTextWriter(writer);
3465 io::buf_free(buf);
3466 }
3467 }
3468
3469 #[test]
3474 fn test_write_with_namespaces() {
3475 unsafe {
3476 let (writer, buf) = create_test_writer();
3477
3478 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3479 xmlTextWriterStartElementNS(
3480 writer,
3481 b"ns\0" as *const u8,
3482 b"root\0" as *const u8,
3483 b"http://example.com/ns\0" as *const u8,
3484 );
3485 xmlTextWriterWriteAttributeNS(
3486 writer,
3487 ptr::null(),
3488 b"attr\0" as *const u8,
3489 ptr::null(),
3490 b"value\0" as *const u8,
3491 );
3492 xmlTextWriterEndElement(writer);
3493 xmlTextWriterEndDocument(writer);
3494
3495 let result = flush_and_get(writer, buf);
3496 assert!(
3497 result.contains("ns:root"),
3498 "Missing namespace prefix. Got: {}",
3499 result
3500 );
3501 assert!(
3502 result.contains("xmlns:ns=\"http://example.com/ns\""),
3503 "Missing xmlns declaration. Got: {}",
3504 result
3505 );
3506
3507 xmlFreeTextWriter(writer);
3508 io::buf_free(buf);
3509 }
3510 }
3511
3512 #[test]
3517 fn test_write_text_cdata_comment_pi() {
3518 unsafe {
3519 let (writer, buf) = create_test_writer();
3520
3521 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3522
3523 xmlTextWriterStartElement(writer, b"doc\0" as *const u8);
3524 xmlTextWriterWriteString(writer, b"text content\0" as *const u8);
3525 xmlTextWriterEndElement(writer);
3526
3527 xmlTextWriterWriteComment(writer, b"a comment\0" as *const u8);
3528
3529 xmlTextWriterWritePI(writer, b"target\0" as *const u8, b"data\0" as *const u8);
3530
3531 xmlTextWriterStartElement(writer, b"cdata\0" as *const u8);
3532 xmlTextWriterWriteCDATA(writer, b"<greeting>Hello</greeting>\0" as *const u8);
3533 xmlTextWriterEndElement(writer);
3534
3535 xmlTextWriterEndDocument(writer);
3536
3537 let result = flush_and_get(writer, buf);
3538 assert!(
3539 result.contains("text content"),
3540 "Missing text content. Got: {}",
3541 result
3542 );
3543 assert!(
3544 result.contains("<!--a comment-->"),
3545 "Missing comment. Got: {}",
3546 result
3547 );
3548 assert!(
3549 result.contains("<?target data?>"),
3550 "Missing PI. Got: {}",
3551 result
3552 );
3553 assert!(
3554 result.contains("<![CDATA["),
3555 "Missing CDATA start. Got: {}",
3556 result
3557 );
3558 assert!(
3559 result.contains("<greeting>Hello</greeting>"),
3560 "Missing CDATA content. Got: {}",
3561 result
3562 );
3563
3564 xmlFreeTextWriter(writer);
3565 io::buf_free(buf);
3566 }
3567 }
3568
3569 #[test]
3574 fn test_write_dtd() {
3575 unsafe {
3576 let (writer, buf) = create_test_writer();
3577
3578 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3579
3580 xmlTextWriterWriteDTD(
3581 writer,
3582 b"html\0" as *const u8,
3583 ptr::null(),
3584 b"http://www.w3.org/TR/html4/strict.dtd\0" as *const u8,
3585 ptr::null(),
3586 );
3587
3588 xmlTextWriterStartElement(writer, b"html\0" as *const u8);
3589 xmlTextWriterEndElement(writer);
3590 xmlTextWriterEndDocument(writer);
3591
3592 let result = flush_and_get(writer, buf);
3593 assert!(
3594 result.contains("<!DOCTYPE html SYSTEM"),
3595 "Missing DTD. Got: {}",
3596 result
3597 );
3598
3599 xmlFreeTextWriter(writer);
3600 io::buf_free(buf);
3601 }
3602 }
3603
3604 #[test]
3609 fn test_write_dtd_with_subset() {
3610 unsafe {
3611 let (writer, buf) = create_test_writer();
3612
3613 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3614
3615 xmlTextWriterStartDTD(writer, b"root\0" as *const u8, ptr::null(), ptr::null());
3616 xmlTextWriterWriteDTDElement(
3617 writer,
3618 b"child\0" as *const u8,
3619 b"(#PCDATA)\0" as *const u8,
3620 );
3621 xmlTextWriterWriteDTDAttribute(
3622 writer,
3623 b"child\0" as *const u8,
3624 b"id CDATA #IMPLIED\0" as *const u8,
3625 );
3626 xmlTextWriterWriteDTDEntity(
3627 writer,
3628 0, b"copy\0" as *const u8,
3630 ptr::null(), ptr::null(), ptr::null(), b"Copyright Me\0" as *const u8,
3634 );
3635 xmlTextWriterWriteDTDNotation(
3636 writer,
3637 b"note\0" as *const u8,
3638 b"PublicID\0" as *const u8,
3639 ptr::null(),
3640 );
3641 xmlTextWriterEndDTD(writer);
3642
3643 xmlTextWriterStartElement(writer, b"root\0" as *const u8);
3644 xmlTextWriterEndElement(writer);
3645 xmlTextWriterEndDocument(writer);
3646
3647 let result = flush_and_get(writer, buf);
3648 assert!(
3649 result.contains("<!DOCTYPE root"),
3650 "Missing DTD start. Got: {}",
3651 result
3652 );
3653 assert!(
3654 result.contains("<!ELEMENT child (#PCDATA)>"),
3655 "Missing DTD element. Got: {}",
3656 result
3657 );
3658 assert!(
3659 result.contains("<!ATTLIST child id CDATA #IMPLIED>"),
3660 "Missing DTD attribute. Got: {}",
3661 result
3662 );
3663 assert!(
3664 result.contains("<!ENTITY copy \"Copyright Me\">"),
3665 "Missing DTD entity. Got: {}",
3666 result
3667 );
3668 assert!(
3669 result.contains("<!NOTATION note PUBLIC \"PublicID\">"),
3670 "Missing DTD notation. Got: {}",
3671 result
3672 );
3673
3674 xmlFreeTextWriter(writer);
3675 io::buf_free(buf);
3676 }
3677 }
3678
3679 #[test]
3684 fn test_indentation_control() {
3685 unsafe {
3686 let (writer, buf) = create_test_writer();
3687
3688 xmlTextWriterSetIndent(writer, 1);
3690 xmlTextWriterSetIndentString(writer, b"\t\0" as *const u8);
3691
3692 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3693 xmlTextWriterStartElement(writer, b"root\0" as *const u8);
3694 xmlTextWriterStartElement(writer, b"child\0" as *const u8);
3695 xmlTextWriterWriteString(writer, b"content\0" as *const u8);
3696 xmlTextWriterEndElement(writer);
3697 xmlTextWriterEndElement(writer);
3698 xmlTextWriterEndDocument(writer);
3699
3700 let result = flush_and_get(writer, buf);
3701
3702 assert!(
3704 result.contains('\t'),
3705 "Expected tab indentation. Got: {}",
3706 result
3707 );
3708 assert!(result.contains("<root>"), "Missing root. Got: {}", result);
3710 assert!(result.contains("<child>"), "Missing child. Got: {}", result);
3711
3712 xmlFreeTextWriter(writer);
3713 io::buf_free(buf);
3714 }
3715 }
3716
3717 #[test]
3722 fn test_memory_output() {
3723 unsafe {
3724 let buf = io::buf_create(256);
3725 assert!(!buf.is_null(), "buf_create failed");
3726
3727 let writer = xmlNewTextWriterMemory(buf, 0);
3728 assert!(!writer.is_null(), "xmlNewTextWriterMemory failed");
3729
3730 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3731 xmlTextWriterStartElement(writer, b"root\0" as *const u8);
3732 xmlTextWriterWriteString(writer, b"memory test\0" as *const u8);
3733 xmlTextWriterEndElement(writer);
3734 xmlTextWriterEndDocument(writer);
3735
3736 xmlTextWriterFlush(writer);
3737 let result = buf_to_string(buf);
3738 assert!(
3739 result.contains("memory test"),
3740 "Missing content in memory output. Got: {}",
3741 result
3742 );
3743
3744 xmlFreeTextWriter(writer);
3745 io::buf_free(buf);
3746 }
3747 }
3748
3749 #[test]
3754 fn test_flush_and_close() {
3755 unsafe {
3756 let (writer, buf) = create_test_writer();
3757
3758 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3759 xmlTextWriterStartElement(writer, b"root\0" as *const u8);
3760 xmlTextWriterWriteString(writer, b"flush me\0" as *const u8);
3761
3762 let r = xmlTextWriterFlush(writer);
3764 assert!(r >= 0, "Flush should return non-negative, got {}", r);
3765
3766 xmlTextWriterEndElement(writer);
3767 xmlTextWriterEndDocument(writer);
3768
3769 xmlFreeTextWriter(writer);
3770 io::buf_free(buf);
3771 }
3772 }
3773
3774 #[test]
3779 fn test_null_handling() {
3780 unsafe {
3781 assert_eq!(
3783 xmlTextWriterStartDocument(ptr::null_mut(), ptr::null(), ptr::null(), ptr::null()),
3784 -1
3785 );
3786 assert_eq!(xmlTextWriterEndDocument(ptr::null_mut()), -1);
3787 assert_eq!(
3788 xmlTextWriterStartElement(ptr::null_mut(), b"x\0" as *const u8),
3789 -1
3790 );
3791 assert_eq!(xmlTextWriterEndElement(ptr::null_mut()), -1);
3792 assert_eq!(
3793 xmlTextWriterWriteString(ptr::null_mut(), b"x\0" as *const u8),
3794 -1
3795 );
3796 assert_eq!(
3797 xmlTextWriterWriteRaw(ptr::null_mut(), b"x\0" as *const u8),
3798 -1
3799 );
3800 assert_eq!(
3801 xmlTextWriterWriteCDATA(ptr::null_mut(), b"x\0" as *const u8),
3802 -1
3803 );
3804 assert_eq!(
3805 xmlTextWriterWriteComment(ptr::null_mut(), b"x\0" as *const u8),
3806 -1
3807 );
3808 assert_eq!(
3809 xmlTextWriterWritePI(ptr::null_mut(), b"x\0" as *const u8, ptr::null()),
3810 -1
3811 );
3812 assert_eq!(xmlTextWriterFlush(ptr::null_mut()), -1);
3813 assert_eq!(xmlTextWriterSetIndent(ptr::null_mut(), 1), -1);
3814 assert_eq!(
3815 xmlTextWriterSetIndentString(ptr::null_mut(), b" \0" as *const u8),
3816 -1
3817 );
3818 assert_eq!(
3819 xmlTextWriterWriteAttribute(
3820 ptr::null_mut(),
3821 b"n\0" as *const u8,
3822 b"v\0" as *const u8
3823 ),
3824 -1
3825 );
3826
3827 xmlFreeTextWriter(ptr::null_mut());
3829 }
3830 }
3831
3832 #[test]
3837 fn test_nested_elements() {
3838 unsafe {
3839 let (writer, buf) = create_test_writer();
3840
3841 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3842 xmlTextWriterStartElement(writer, b"a\0" as *const u8);
3843 xmlTextWriterStartElement(writer, b"b\0" as *const u8);
3844 xmlTextWriterStartElement(writer, b"c\0" as *const u8);
3845 xmlTextWriterWriteString(writer, b"deep\0" as *const u8);
3846 xmlTextWriterEndElement(writer);
3847 xmlTextWriterEndElement(writer);
3848 xmlTextWriterEndElement(writer);
3849 xmlTextWriterEndDocument(writer);
3850
3851 let result = flush_and_get(writer, buf);
3852 assert!(result.contains("<a>"), "Missing <a>. Got: {}", result);
3853 assert!(result.contains("<b>"), "Missing <b>. Got: {}", result);
3854 assert!(result.contains("<c>"), "Missing <c>. Got: {}", result);
3855 assert!(result.contains("</a>"), "Missing </a>. Got: {}", result);
3856 assert!(result.contains("</b>"), "Missing </b>. Got: {}", result);
3857 assert!(result.contains("</c>"), "Missing </c>. Got: {}", result);
3858
3859 xmlFreeTextWriter(writer);
3860 io::buf_free(buf);
3861 }
3862 }
3863
3864 #[test]
3869 fn test_self_closing_element() {
3870 unsafe {
3871 let (writer, buf) = create_test_writer();
3872
3873 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3874 xmlTextWriterStartElement(writer, b"empty\0" as *const u8);
3875 xmlTextWriterEndElement(writer);
3876 xmlTextWriterEndDocument(writer);
3877
3878 let result = flush_and_get(writer, buf);
3879 assert!(
3880 result.contains("<empty/>"),
3881 "Expected self-closing <empty/>. Got: {}",
3882 result
3883 );
3884
3885 xmlFreeTextWriter(writer);
3886 io::buf_free(buf);
3887 }
3888 }
3889
3890 #[test]
3895 fn test_full_end_element() {
3896 unsafe {
3897 let (writer, buf) = create_test_writer();
3898
3899 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3900 xmlTextWriterStartElement(writer, b"container\0" as *const u8);
3901 xmlTextWriterFullEndElement(writer);
3902 xmlTextWriterEndDocument(writer);
3903
3904 let result = flush_and_get(writer, buf);
3905 assert!(
3906 result.contains("<container>"),
3907 "Missing <container>. Got: {}",
3908 result
3909 );
3910 assert!(
3911 result.contains("</container>"),
3912 "Missing </container>. Got: {}",
3913 result
3914 );
3915
3916 xmlFreeTextWriter(writer);
3917 io::buf_free(buf);
3918 }
3919 }
3920
3921 #[test]
3926 fn test_write_element_inline() {
3927 unsafe {
3928 let (writer, buf) = create_test_writer();
3929
3930 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3931 xmlTextWriterWriteElement(writer, b"greeting\0" as *const u8, b"Hello\0" as *const u8);
3932 xmlTextWriterEndDocument(writer);
3933
3934 let result = flush_and_get(writer, buf);
3935 assert!(
3936 result.contains("<greeting>Hello</greeting>"),
3937 "Expected <greeting>Hello</greeting>. Got: {}",
3938 result
3939 );
3940
3941 xmlFreeTextWriter(writer);
3942 io::buf_free(buf);
3943 }
3944 }
3945
3946 #[test]
3951 fn test_text_escaping() {
3952 unsafe {
3953 let (writer, buf) = create_test_writer();
3954
3955 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3956 xmlTextWriterStartElement(writer, b"esc\0" as *const u8);
3957 xmlTextWriterWriteString(writer, b"a < b & b > a\0" as *const u8);
3958 xmlTextWriterEndElement(writer);
3959 xmlTextWriterEndDocument(writer);
3960
3961 let result = flush_and_get(writer, buf);
3962 assert!(
3963 result.contains("a < b & b > a"),
3964 "Expected escaped content. Got: {}",
3965 result
3966 );
3967
3968 xmlFreeTextWriter(writer);
3969 io::buf_free(buf);
3970 }
3971 }
3972
3973 #[test]
3978 fn test_raw_content() {
3979 unsafe {
3980 let (writer, buf) = create_test_writer();
3981
3982 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
3983 xmlTextWriterStartElement(writer, b"raw\0" as *const u8);
3984 xmlTextWriterWriteRaw(writer, b"<unencoded>&special;</unencoded>\0" as *const u8);
3985 xmlTextWriterEndElement(writer);
3986 xmlTextWriterEndDocument(writer);
3987
3988 let result = flush_and_get(writer, buf);
3989 assert!(
3990 result.contains("<unencoded>&special;</unencoded>"),
3991 "Expected raw unencoded content. Got: {}",
3992 result
3993 );
3994
3995 xmlFreeTextWriter(writer);
3996 io::buf_free(buf);
3997 }
3998 }
3999
4000 #[test]
4005 fn test_base64_write() {
4006 unsafe {
4007 let (writer, buf) = create_test_writer();
4008
4009 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
4010 xmlTextWriterStartElement(writer, b"data\0" as *const u8);
4011 let test_data = b"Hello, World!";
4012 xmlTextWriterWriteBase64(
4013 writer,
4014 test_data.as_ptr() as *const c_char,
4015 0,
4016 test_data.len() as c_int,
4017 );
4018 xmlTextWriterEndElement(writer);
4019 xmlTextWriterEndDocument(writer);
4020
4021 let result = flush_and_get(writer, buf);
4022 assert!(
4023 result.contains("SGVsbG8sIFdvcmxkIQ"),
4024 "Expected Base64-encoded content. Got: {}",
4025 result
4026 );
4027
4028 xmlFreeTextWriter(writer);
4029 io::buf_free(buf);
4030 }
4031 }
4032
4033 #[test]
4038 fn test_incremental_cdata_comment_pi() {
4039 unsafe {
4040 let (writer, buf) = create_test_writer();
4041
4042 xmlTextWriterStartDocument(writer, ptr::null(), ptr::null(), ptr::null());
4043
4044 xmlTextWriterStartElement(writer, b"inc\0" as *const u8);
4046 xmlTextWriterStartCDATA(writer);
4047 xmlTextWriterWriteString(writer, b"cdata content\0" as *const u8);
4048 xmlTextWriterEndCDATA(writer);
4049 xmlTextWriterEndElement(writer);
4050
4051 xmlTextWriterStartComment(writer);
4053 xmlTextWriterWriteString(writer, b"comment text\0" as *const u8);
4054 xmlTextWriterEndComment(writer);
4055
4056 xmlTextWriterStartPI(writer, b"xml-stylesheet\0" as *const u8);
4058 xmlTextWriterWriteString(
4059 writer,
4060 b"type=\"text/xsl\" href=\"style.xsl\"\0" as *const u8,
4061 );
4062 xmlTextWriterEndPI(writer);
4063
4064 xmlTextWriterEndDocument(writer);
4065
4066 let result = flush_and_get(writer, buf);
4067 assert!(
4068 result.contains("<![CDATA["),
4069 "Missing CDATA. Got: {}",
4070 result
4071 );
4072 assert!(
4073 result.contains("<!--comment text-->"),
4074 "Missing comment. Got: {}",
4075 result
4076 );
4077 assert!(
4078 result.contains("<?xml-stylesheet"),
4079 "Missing PI. Got: {}",
4080 result
4081 );
4082
4083 xmlFreeTextWriter(writer);
4084 io::buf_free(buf);
4085 }
4086 }
4087
4088 #[test]
4093 fn test_new_writer_filename_null() {
4094 unsafe {
4095 let writer = xmlNewTextWriterFilename(ptr::null(), 0);
4096 assert!(writer.is_null(), "Expected NULL for null URI");
4097 }
4098 }
4099
4100 #[test]
4105 fn test_new_writer_null_output() {
4106 unsafe {
4107 let writer = xmlNewTextWriter(ptr::null_mut());
4108 assert!(writer.is_null(), "Expected NULL for null output");
4109 }
4110 }
4111}