1#![allow(
10 clippy::cast_possible_truncation,
11 clippy::cast_sign_loss,
12 clippy::cast_ptr_alignment,
13 clippy::missing_safety_doc
14)]
15
16use std::ffi::CStr;
17use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
18use std::ptr;
19
20use libc;
21
22use crate::abi::allocator::{xmlFree, xmlMalloc, xmlRealloc};
23use crate::abi::callbacks::{
24 xmlInputCloseCallback, xmlInputReadCallback, xmlOutputCloseCallback, xmlOutputWriteCallback,
25};
26use crate::abi::structs::{
27 _xmlBuf, _xmlBuffer, _xmlCharEncodingHandler, _xmlOutputBuffer, _xmlParserInputBuffer,
28};
29use crate::abi::types::{xmlChar, xmlCharEncoding, xmlCharPtr};
30use crate::xml::encoding;
31
32const DEFAULT_BUFFER_SIZE: c_uint = 4000;
38
39const MIN_BUFFER_SIZE: c_uint = 256;
41
42const XML_BUFFER_ALLOC_DOUBLEIT: c_int = 0;
44
45const XML_BUFFER_ALLOC_EXACT: c_int = 1;
47
48const XML_BUFFER_ALLOC_IMMUTABLE: c_int = 2;
50
51pub(crate) fn buf_create(size: c_int) -> *mut _xmlBuffer {
62 let buf_size = if size <= 0 {
63 DEFAULT_BUFFER_SIZE
64 } else {
65 size as c_uint
66 };
67
68 let buf_size = buf_size.max(MIN_BUFFER_SIZE);
70
71 let buf = unsafe { xmlMalloc(size_of::<_xmlBuffer>()) as *mut _xmlBuffer };
72 if buf.is_null() {
73 return ptr::null_mut();
74 }
75
76 let content = unsafe { xmlMalloc(buf_size as usize) as *mut xmlChar };
77 if content.is_null() {
78 unsafe { xmlFree(buf as *mut c_void) };
79 return ptr::null_mut();
80 }
81
82 unsafe {
84 ptr::write(content, 0);
85 }
86
87 unsafe {
88 ptr::write(
89 buf,
90 _xmlBuffer {
91 content,
92 use_: 0,
93 size: buf_size,
94 alloc: XML_BUFFER_ALLOC_DOUBLEIT,
95 contentIO: content, },
97 );
98 }
99
100 buf
101}
102
103pub(crate) fn buf_create_static(str: *const xmlChar, size: c_int) -> *mut _xmlBuffer {
111 if str.is_null() {
112 return ptr::null_mut();
113 }
114
115 let len = if size <= 0 {
116 let mut len: c_uint = 0;
118 unsafe {
119 while *str.add(len as usize) != 0 {
120 len += 1;
121 }
122 }
123 len
124 } else {
125 size as c_uint
126 };
127
128 let buf = unsafe { xmlMalloc(size_of::<_xmlBuffer>()) as *mut _xmlBuffer };
129 if buf.is_null() {
130 return ptr::null_mut();
131 }
132
133 unsafe {
134 ptr::write(
135 buf,
136 _xmlBuffer {
137 content: str as *mut xmlChar,
138 use_: len,
139 size: len + 1, alloc: XML_BUFFER_ALLOC_IMMUTABLE,
141 contentIO: ptr::null_mut(),
142 },
143 );
144 }
145
146 buf
147}
148
149pub(crate) fn buf_free(buf: *mut _xmlBuffer) {
154 if buf.is_null() {
155 return;
156 }
157
158 unsafe {
159 let alloc = (*buf).alloc;
160 let content = (*buf).content;
161 let content_io = (*buf).contentIO;
162
163 if alloc != XML_BUFFER_ALLOC_IMMUTABLE {
164 let base = if !content_io.is_null() {
168 content_io
169 } else {
170 content
171 };
172 if !base.is_null() {
173 xmlFree(base as *mut c_void);
174 }
175 }
176
177 xmlFree(buf as *mut c_void);
178 }
179}
180
181pub(crate) fn buf_empty(buf: *mut _xmlBuffer) {
185 if buf.is_null() {
186 return;
187 }
188
189 unsafe {
190 (*buf).use_ = 0;
191 if !(*buf).content.is_null() {
192 ptr::write((*buf).content, 0);
193 }
194 }
195}
196
197pub(crate) fn buf_content(buf: *mut _xmlBuffer) -> *mut xmlChar {
199 if buf.is_null() {
200 return ptr::null_mut();
201 }
202 unsafe { (*buf).content }
203}
204
205pub(crate) fn buf_length(buf: *mut _xmlBuffer) -> c_int {
207 if buf.is_null() {
208 return -1;
209 }
210 unsafe { (*buf).use_ as c_int }
211}
212
213pub(crate) fn buf_add(buf: *mut _xmlBuffer, str: *const xmlChar, len: c_int) -> c_int {
218 if buf.is_null() || str.is_null() || len <= 0 {
219 return 0;
220 }
221
222 let len = len as c_uint;
223 let b = unsafe { &mut *buf };
224
225 if b.alloc == XML_BUFFER_ALLOC_IMMUTABLE {
227 return -1;
228 }
229
230 let needed = b.use_.saturating_add(len).saturating_add(1);
232 if needed > b.size {
233 let new_size = if b.alloc == XML_BUFFER_ALLOC_EXACT {
235 needed
236 } else {
237 let mut doubled = b.size.saturating_mul(2).max(MIN_BUFFER_SIZE);
239 while doubled < needed {
240 doubled = doubled.saturating_mul(2);
241 }
242 doubled
243 };
244
245 let new_content =
246 unsafe { xmlRealloc(b.content as *mut c_void, new_size as usize) as *mut xmlChar };
247 if new_content.is_null() {
248 return -1;
249 }
250 b.content = new_content;
251 b.contentIO = new_content; b.size = new_size;
253 }
254
255 unsafe {
257 ptr::copy_nonoverlapping(str, b.content.add(b.use_ as usize), len as usize);
258 }
259 b.use_ = b.use_.saturating_add(len);
260
261 unsafe {
263 ptr::write(b.content.add(b.use_ as usize), 0);
264 }
265
266 len as c_int
267}
268
269pub(crate) fn buf_cat(buf: *mut _xmlBuffer, str: *const xmlChar) -> c_int {
271 if buf.is_null() || str.is_null() {
272 return -1;
273 }
274
275 let len = unsafe {
277 let mut i: c_uint = 0;
278 while *str.add(i as usize) != 0 {
279 i += 1;
280 }
281 i
282 };
283
284 buf_add(buf, str, len as c_int)
285}
286
287pub(crate) fn buf_ccat(buf: *mut _xmlBuffer, c: xmlChar) -> c_int {
289 buf_add(buf, &c as *const xmlChar, 1)
290}
291
292pub(crate) fn buf_shrink(buf: *mut _xmlBuffer, len: c_uint) -> c_int {
297 if buf.is_null() {
298 return -1;
299 }
300
301 let b = unsafe { &mut *buf };
302 if b.use_ == 0 {
303 return 0;
304 }
305
306 b.use_ = if len >= b.use_ { 0 } else { b.use_ - len };
307
308 unsafe {
310 ptr::write(b.content.add(b.use_ as usize), 0);
311 }
312
313 b.use_ as c_int
314}
315
316pub(crate) fn buf_add_head(buf: *mut _xmlBuffer, str: *const xmlChar, len: c_int) -> c_int {
323 if buf.is_null() || str.is_null() || len <= 0 {
324 return -1;
325 }
326 let len = len as c_uint;
327 unsafe {
328 let b = &mut *buf;
329 let needed = b.use_.saturating_add(len).saturating_add(1);
330 if needed > b.size {
331 let new_size = needed.saturating_mul(2).max(MIN_BUFFER_SIZE);
332 let new_content =
333 xmlRealloc(b.content as *mut c_void, new_size as usize) as *mut xmlChar;
334 if new_content.is_null() {
335 return -1;
336 }
337 b.content = new_content;
338 b.contentIO = new_content;
339 b.size = new_size;
340 }
341 if b.use_ > 0 {
343 core::ptr::copy(b.content, b.content.add(len as usize), b.use_ as usize);
344 }
345 core::ptr::copy_nonoverlapping(str, b.content, len as usize);
347 b.use_ = b.use_.saturating_add(len);
348 *b.content.add(b.use_ as usize) = 0;
349 }
350 0
351}
352
353pub(crate) fn buf_grow(buf: *mut _xmlBuffer, size: c_uint) -> c_int {
354 if buf.is_null() {
355 return -1;
356 }
357
358 let b = unsafe { &mut *buf };
359
360 if size <= b.size {
361 return 0; }
363
364 let new_content =
365 unsafe { xmlRealloc(b.content as *mut c_void, size as usize) as *mut xmlChar };
366 if new_content.is_null() {
367 return -1;
368 }
369
370 b.content = new_content;
371 b.contentIO = new_content;
372 b.size = size;
373
374 0
375}
376
377pub(crate) fn xml_buf_create(size: c_int) -> *mut _xmlBuf {
386 let buf_size = if size <= 0 {
387 DEFAULT_BUFFER_SIZE
388 } else {
389 size as c_uint
390 };
391 let buf_size = buf_size.max(MIN_BUFFER_SIZE);
392
393 let buf = unsafe { xmlMalloc(size_of::<_xmlBuf>()) as *mut _xmlBuf };
394 if buf.is_null() {
395 return ptr::null_mut();
396 }
397
398 let content = unsafe { xmlMalloc(buf_size as usize) as *mut xmlChar };
399 if content.is_null() {
400 unsafe { xmlFree(buf as *mut c_void) };
401 return ptr::null_mut();
402 }
403
404 unsafe {
405 ptr::write(content, 0);
406 }
407
408 unsafe {
409 ptr::write(
410 buf,
411 _xmlBuf {
412 content,
413 use_: 0,
414 size: buf_size,
415 alloc: XML_BUFFER_ALLOC_DOUBLEIT,
416 error: 0,
417 buffer: 0,
418 io: 0,
419 },
420 );
421 }
422
423 buf
424}
425
426pub(crate) fn xml_buf_free(buf: *mut _xmlBuf) {
430 if buf.is_null() {
431 return;
432 }
433
434 unsafe {
435 if !(*buf).content.is_null() {
436 xmlFree((*buf).content as *mut c_void);
437 }
438 xmlFree(buf as *mut c_void);
439 }
440}
441
442pub(crate) fn xml_buf_content(buf: *mut _xmlBuf) -> *mut xmlChar {
444 if buf.is_null() {
445 return ptr::null_mut();
446 }
447 unsafe { (*buf).content }
448}
449
450pub(crate) fn xml_buf_length(buf: *mut _xmlBuf) -> c_int {
452 if buf.is_null() {
453 return -1;
454 }
455 unsafe { (*buf).use_ as c_int }
456}
457
458pub(crate) fn xml_buf_add(buf: *mut _xmlBuf, str: *const xmlChar, len: c_int) -> c_int {
462 if buf.is_null() || str.is_null() || len <= 0 {
463 return 0;
464 }
465
466 let len = len as c_uint;
467 let b = unsafe { &mut *buf };
468
469 let needed = b.use_.saturating_add(len).saturating_add(1);
470 if needed > b.size {
471 let new_size = needed.saturating_mul(2).max(MIN_BUFFER_SIZE);
472 let new_content =
473 unsafe { xmlRealloc(b.content as *mut c_void, new_size as usize) as *mut xmlChar };
474 if new_content.is_null() {
475 return -1;
476 }
477 b.content = new_content;
478 b.size = new_size;
479 }
480
481 unsafe {
482 ptr::copy_nonoverlapping(str, b.content.add(b.use_ as usize), len as usize);
483 }
484 b.use_ = b.use_.saturating_add(len);
485
486 unsafe {
487 ptr::write(b.content.add(b.use_ as usize), 0);
488 }
489
490 len as c_int
491}
492
493pub(crate) fn xml_buf_cat(buf: *mut _xmlBuf, str: *const xmlChar) -> c_int {
495 if buf.is_null() || str.is_null() {
496 return -1;
497 }
498
499 let len = unsafe {
500 let mut i: c_uint = 0;
501 while *str.add(i as usize) != 0 {
502 i += 1;
503 }
504 i
505 };
506
507 xml_buf_add(buf, str, len as c_int)
508}
509
510pub(crate) fn xml_buf_grow(buf: *mut _xmlBuf, size: c_uint) -> c_int {
514 if buf.is_null() {
515 return -1;
516 }
517
518 let b = unsafe { &mut *buf };
519 if size <= b.size {
520 return 0;
521 }
522
523 let new_content =
524 unsafe { xmlRealloc(b.content as *mut c_void, size as usize) as *mut xmlChar };
525 if new_content.is_null() {
526 return -1;
527 }
528
529 b.content = new_content;
530 b.size = size;
531 0
532}
533
534pub(crate) fn xml_buf_shrink(buf: *mut _xmlBuf, len: c_uint) -> c_int {
538 if buf.is_null() {
539 return -1;
540 }
541
542 let b = unsafe { &mut *buf };
543 if b.use_ == 0 {
544 return 0;
545 }
546
547 b.use_ = if len >= b.use_ { 0 } else { b.use_ - len };
548
549 unsafe {
550 ptr::write(b.content.add(b.use_ as usize), 0);
551 }
552
553 b.use_ as c_int
554}
555
556fn encoding_from_int(enc: c_int) -> xmlCharEncoding {
562 match enc {
563 -1 => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
564 0 => xmlCharEncoding::XML_CHAR_ENCODING_NONE,
565 1 => xmlCharEncoding::XML_CHAR_ENCODING_UTF8,
566 2 => xmlCharEncoding::XML_CHAR_ENCODING_UTF16LE,
567 3 => xmlCharEncoding::XML_CHAR_ENCODING_UTF16BE,
568 4 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4LE,
569 5 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4BE,
570 6 => xmlCharEncoding::XML_CHAR_ENCODING_EBCDIC,
571 7 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4_2143,
572 8 => xmlCharEncoding::XML_CHAR_ENCODING_UCS4_3412,
573 9 => xmlCharEncoding::XML_CHAR_ENCODING_UCS2,
574 10 => xmlCharEncoding::XML_CHAR_ENCODING_8859_1,
575 11 => xmlCharEncoding::XML_CHAR_ENCODING_8859_2,
576 12 => xmlCharEncoding::XML_CHAR_ENCODING_8859_3,
577 13 => xmlCharEncoding::XML_CHAR_ENCODING_8859_4,
578 14 => xmlCharEncoding::XML_CHAR_ENCODING_8859_5,
579 15 => xmlCharEncoding::XML_CHAR_ENCODING_8859_6,
580 16 => xmlCharEncoding::XML_CHAR_ENCODING_8859_7,
581 17 => xmlCharEncoding::XML_CHAR_ENCODING_8859_8,
582 18 => xmlCharEncoding::XML_CHAR_ENCODING_8859_9,
583 19 => xmlCharEncoding::XML_CHAR_ENCODING_2022_JP,
584 20 => xmlCharEncoding::XML_CHAR_ENCODING_SHIFT_JIS,
585 21 => xmlCharEncoding::XML_CHAR_ENCODING_EUC_JP,
586 22 => xmlCharEncoding::XML_CHAR_ENCODING_ASCII,
587 _ => xmlCharEncoding::XML_CHAR_ENCODING_ERROR,
588 }
589}
590
591fn find_handler_for_encoding(enc: c_int) -> *mut _xmlCharEncodingHandler {
596 let enc_enum = encoding_from_int(enc);
597 if enc_enum == xmlCharEncoding::XML_CHAR_ENCODING_NONE
598 || enc_enum == xmlCharEncoding::XML_CHAR_ENCODING_UTF8
599 || enc_enum == xmlCharEncoding::XML_CHAR_ENCODING_ERROR
600 {
601 return ptr::null_mut();
602 }
603
604 if let Some(name) = encoding::encoding_name(enc_enum) {
606 let mut name_nul = name.to_vec();
608 name_nul.push(0);
609 let handler = encoding::find_encoding_handler(name_nul.as_ptr() as *const xmlChar);
610 if !handler.is_null() {
611 return handler;
612 }
613 }
614
615 ptr::null_mut()
616}
617
618fn allocate_input_buffer() -> *mut _xmlParserInputBuffer {
623 let buf =
624 unsafe { xmlMalloc(size_of::<_xmlParserInputBuffer>()) as *mut _xmlParserInputBuffer };
625 if buf.is_null() {
626 return ptr::null_mut();
627 }
628
629 unsafe {
630 ptr::write(
631 buf,
632 _xmlParserInputBuffer {
633 context: ptr::null_mut(),
634 readcallback: None,
635 closecallback: None,
636 encoder: ptr::null_mut(),
637 buffer: ptr::null_mut(),
638 raw: ptr::null_mut(),
639 compressed: 0,
640 error: 0,
641 rawconsumed: 0,
642 },
643 );
644 }
645
646 buf
647}
648
649pub(crate) fn input_buffer_create_mem(
654 buffer: *const c_char,
655 size: c_int,
656 enc: c_int,
657) -> *mut _xmlParserInputBuffer {
658 if buffer.is_null() || size <= 0 {
659 return ptr::null_mut();
660 }
661
662 let buf = allocate_input_buffer();
663 if buf.is_null() {
664 return ptr::null_mut();
665 }
666
667 let raw_buf = buf_create(size);
669 if raw_buf.is_null() {
670 unsafe { xmlFree(buf as *mut c_void) };
671 return ptr::null_mut();
672 }
673
674 buf_add(raw_buf, buffer as *const xmlChar, size);
676
677 let handler = find_handler_for_encoding(enc);
679 if !handler.is_null() {
680 let out_buf = buf_create((size as c_uint).saturating_mul(3).max(MIN_BUFFER_SIZE) as c_int);
683 if out_buf.is_null() {
684 buf_free(raw_buf);
685 unsafe { xmlFree(buf as *mut c_void) };
686 return ptr::null_mut();
687 }
688
689 let written = encoding::char_enc_in(handler, out_buf, raw_buf);
691 if written < 0 {
692 buf_free(raw_buf);
693 buf_free(out_buf);
694 unsafe { xmlFree(buf as *mut c_void) };
695 return ptr::null_mut();
696 }
697
698 unsafe {
699 (*buf).encoder = handler as *mut c_void;
700 (*buf).buffer = out_buf as *mut c_void;
701 (*buf).raw = raw_buf as *mut c_void;
702 }
703 } else {
704 unsafe {
706 (*buf).buffer = raw_buf as *mut c_void;
707 (*buf).raw = raw_buf as *mut c_void;
708 }
709 }
710
711 buf
712}
713
714unsafe extern "C" fn file_read_callback(
718 context: *mut c_void,
719 buffer: *mut c_char,
720 len: c_int,
721) -> c_int {
722 if context.is_null() || buffer.is_null() || len <= 0 {
723 return -1;
724 }
725
726 let fd = context as c_int;
727 let ret = libc::read(fd, buffer as *mut c_void, len as usize);
728 if ret < 0 {
729 return -1;
730 }
731 ret as c_int
732}
733
734unsafe extern "C" fn file_close_callback(context: *mut c_void) -> c_int {
736 if context.is_null() {
737 return -1;
738 }
739
740 let fd = context as c_int;
741 libc::close(fd)
742}
743
744pub(crate) fn input_buffer_create_file(
749 filename: *const c_char,
750 enc: c_int,
751) -> *mut _xmlParserInputBuffer {
752 if filename.is_null() {
753 return ptr::null_mut();
754 }
755
756 let filename_str = unsafe {
758 match CStr::from_ptr(filename).to_str() {
759 Ok(s) => s,
760 Err(_) => return ptr::null_mut(),
761 }
762 };
763
764 let fd = unsafe {
766 let path_c = std::ffi::CString::new(filename_str).unwrap_or_default();
767 libc::open(path_c.as_ptr(), libc::O_RDONLY)
768 };
769
770 if fd < 0 {
771 return ptr::null_mut();
772 }
773
774 let mut stat_buf: libc::stat = unsafe { std::mem::zeroed() };
776 let stat_ret = unsafe {
777 let path_c = std::ffi::CString::new(filename_str).unwrap_or_default();
778 libc::stat(path_c.as_ptr(), &mut stat_buf)
779 };
780
781 let file_size = if stat_ret == 0 {
782 stat_buf.st_size as usize
783 } else {
784 0
786 };
787
788 let read_size = if file_size > 0 {
790 file_size
791 } else {
792 4096 };
794
795 let mut data = vec![0u8; read_size];
796 let mut total_read: isize = 0;
797
798 loop {
799 let remaining = read_size.saturating_sub(total_read as usize);
800 if remaining == 0 {
801 let new_size = read_size.saturating_mul(2);
803 data.resize(new_size, 0u8);
804 }
805
806 let ret = unsafe {
807 libc::read(
808 fd,
809 data.as_mut_ptr().add(total_read as usize) as *mut c_void,
810 remaining,
811 )
812 };
813
814 if ret < 0 {
815 unsafe { libc::close(fd) };
817 return ptr::null_mut();
818 }
819
820 if ret == 0 {
821 break;
823 }
824
825 total_read += ret as isize;
826 }
827
828 unsafe { libc::close(fd) };
829
830 data.truncate(total_read as usize);
831
832 if data.is_empty() {
833 return ptr::null_mut();
834 }
835
836 input_buffer_create_mem(data.as_ptr() as *const c_char, data.len() as c_int, enc)
838}
839
840pub(crate) fn input_buffer_create_io(
845 ioread: Option<xmlInputReadCallback>,
846 ioclose: Option<xmlInputCloseCallback>,
847 ioctx: *mut c_void,
848 enc: c_int,
849) -> *mut _xmlParserInputBuffer {
850 let buf = allocate_input_buffer();
851 if buf.is_null() {
852 return ptr::null_mut();
853 }
854
855 let raw_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
857 if raw_buf.is_null() {
858 unsafe { xmlFree(buf as *mut c_void) };
859 return ptr::null_mut();
860 }
861
862 unsafe {
863 (*buf).context = ioctx;
864 (*buf).readcallback = ioread;
865 (*buf).closecallback = ioclose;
866 (*buf).raw = raw_buf as *mut c_void;
867 }
868
869 let handler = find_handler_for_encoding(enc);
871 if !handler.is_null() {
872 let out_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
873 if out_buf.is_null() {
874 buf_free(raw_buf);
875 unsafe { xmlFree(buf as *mut c_void) };
876 return ptr::null_mut();
877 }
878 unsafe {
879 (*buf).encoder = handler as *mut c_void;
880 (*buf).buffer = out_buf as *mut c_void;
881 }
882 } else {
883 unsafe {
884 (*buf).buffer = raw_buf as *mut c_void;
885 }
886 }
887
888 buf
889}
890
891pub(crate) fn input_buffer_create_fd(fd: c_int, enc: c_int) -> *mut _xmlParserInputBuffer {
895 if fd < 0 {
896 return ptr::null_mut();
897 }
898
899 input_buffer_create_io(
900 Some(file_read_callback as xmlInputReadCallback),
901 Some(file_close_callback as xmlInputCloseCallback),
902 fd as *mut c_void,
903 enc,
904 )
905}
906
907pub(crate) fn input_buffer_free(buf: *mut _xmlParserInputBuffer) {
912 if buf.is_null() {
913 return;
914 }
915
916 unsafe {
917 if let Some(close_cb) = (*buf).closecallback {
919 close_cb((*buf).context);
920 }
921
922 if !(*buf).raw.is_null() {
924 buf_free((*buf).raw as *mut _xmlBuffer);
925 }
926
927 if !(*buf).buffer.is_null() && (*buf).buffer != (*buf).raw {
929 buf_free((*buf).buffer as *mut _xmlBuffer);
930 }
931
932 xmlFree(buf as *mut c_void);
936 }
937}
938
939pub(crate) fn input_buffer_read(
948 buf: *mut _xmlParserInputBuffer,
949 buffer: *mut c_char,
950 len: c_int,
951) -> c_int {
952 if buf.is_null() || buffer.is_null() || len <= 0 {
953 return -1;
954 }
955
956 let b = unsafe { &mut *buf };
957
958 if b.error != 0 {
959 return -1;
960 }
961
962 if let Some(read_cb) = b.readcallback {
963 let raw_buf = b.raw as *mut _xmlBuffer;
966 if raw_buf.is_null() {
967 return -1;
968 }
969
970 let mut tmp = vec![0u8; len as usize];
972 let ret = unsafe { read_cb(b.context, tmp.as_mut_ptr() as *mut c_char, len) };
973
974 if ret < 0 {
975 b.error = 1;
976 return -1;
977 }
978
979 if ret == 0 {
980 return 0;
982 }
983
984 buf_add(raw_buf, tmp.as_ptr() as *const xmlChar, ret);
986
987 if !b.encoder.is_null() {
989 let out_buf = b.buffer as *mut _xmlBuffer;
990 if out_buf.is_null() {
991 return -1;
992 }
993
994 let handler = b.encoder as *mut _xmlCharEncodingHandler;
995 let conv_ret = encoding::char_enc_in(handler, out_buf, raw_buf);
996 if conv_ret < 0 {
997 b.error = 1;
998 return -1;
999 }
1000
1001 let out_b = unsafe { &*out_buf };
1003 let to_copy = (out_b.use_ as c_int).min(len);
1004 if to_copy > 0 {
1005 unsafe {
1006 ptr::copy_nonoverlapping(
1007 out_b.content,
1008 buffer as *mut xmlChar,
1009 to_copy as usize,
1010 );
1011 }
1012 buf_shrink(out_buf, to_copy as c_uint);
1014 }
1015 return to_copy;
1016 }
1017
1018 let raw_b = unsafe { &*raw_buf };
1020 let to_copy = (raw_b.use_ as c_int).min(len);
1021 if to_copy > 0 {
1022 unsafe {
1023 ptr::copy_nonoverlapping(raw_b.content, buffer as *mut xmlChar, to_copy as usize);
1024 }
1025 buf_shrink(raw_buf, to_copy as c_uint);
1026 }
1027 return to_copy;
1028 }
1029
1030 let src_buf = b.buffer as *mut _xmlBuffer;
1032 if src_buf.is_null() {
1033 return -1;
1034 }
1035
1036 let src = unsafe { &mut *src_buf };
1037 if src.content.is_null() || src.use_ == 0 {
1038 return 0;
1039 }
1040
1041 let to_copy = (src.use_ as c_int).min(len);
1042 if to_copy > 0 {
1043 unsafe {
1044 ptr::copy_nonoverlapping(src.content, buffer as *mut xmlChar, to_copy as usize);
1045 }
1046 unsafe {
1048 src.content = src.content.add(to_copy as usize);
1049 }
1050 src.use_ = src.use_.saturating_sub(to_copy as c_uint);
1051 }
1052
1053 to_copy
1054}
1055
1056pub(crate) fn input_buffer_push(
1061 buf: *mut _xmlParserInputBuffer,
1062 buffer: *const c_char,
1063 len: c_int,
1064) -> c_int {
1065 if buf.is_null() || buffer.is_null() || len <= 0 {
1066 return -1;
1067 }
1068
1069 let b = unsafe { &mut *buf };
1070
1071 if b.error != 0 {
1072 return -1;
1073 }
1074
1075 let raw_buf = b.raw as *mut _xmlBuffer;
1077 if raw_buf.is_null() {
1078 return -1;
1079 }
1080
1081 buf_add(raw_buf, buffer as *const xmlChar, len);
1082
1083 if !b.encoder.is_null() {
1085 let out_buf = b.buffer as *mut _xmlBuffer;
1086 if out_buf.is_null() {
1087 return -1;
1088 }
1089
1090 let handler = b.encoder as *mut _xmlCharEncodingHandler;
1091 let ret = encoding::char_enc_in(handler, out_buf, raw_buf);
1092 if ret < 0 {
1093 b.error = 1;
1094 return -1;
1095 }
1096 }
1097
1098 len
1099}
1100
1101pub(crate) fn input_buffer_set_encoder(
1106 buf: *mut _xmlParserInputBuffer,
1107 handler: *mut _xmlCharEncodingHandler,
1108) {
1109 if buf.is_null() {
1110 return;
1111 }
1112
1113 unsafe {
1114 (*buf).encoder = handler as *mut c_void;
1115 }
1116}
1117
1118unsafe extern "C" fn file_write_callback(
1124 context: *mut c_void,
1125 buffer: *const c_char,
1126 len: c_int,
1127) -> c_int {
1128 if context.is_null() || buffer.is_null() || len <= 0 {
1129 return -1;
1130 }
1131
1132 let fd = context as c_int;
1133 let ret = libc::write(fd, buffer as *const c_void, len as usize);
1134 if ret < 0 {
1135 return -1;
1136 }
1137 ret as c_int
1138}
1139
1140unsafe extern "C" fn file_close_output_callback(context: *mut c_void) -> c_int {
1142 if context.is_null() {
1143 return -1;
1144 }
1145
1146 let fd = context as c_int;
1147 libc::close(fd)
1148}
1149
1150unsafe extern "C" fn buffer_write_callback(
1152 context: *mut c_void,
1153 buffer: *const c_char,
1154 len: c_int,
1155) -> c_int {
1156 if context.is_null() || buffer.is_null() || len <= 0 {
1157 return -1;
1158 }
1159
1160 let target_buf = context as *mut _xmlBuffer;
1161 buf_add(target_buf, buffer as *const xmlChar, len)
1162}
1163
1164fn allocate_output_buffer() -> *mut _xmlOutputBuffer {
1168 let buf = unsafe { xmlMalloc(size_of::<_xmlOutputBuffer>()) as *mut _xmlOutputBuffer };
1169 if buf.is_null() {
1170 return ptr::null_mut();
1171 }
1172
1173 unsafe {
1174 ptr::write(
1175 buf,
1176 _xmlOutputBuffer {
1177 context: ptr::null_mut(),
1178 writecallback: None,
1179 closecallback: None,
1180 encoder: ptr::null_mut(),
1181 buffer: ptr::null_mut(),
1182 conv: ptr::null_mut(),
1183 written: 0,
1184 error: 0,
1185 },
1186 );
1187 }
1188
1189 buf
1190}
1191
1192pub(crate) fn output_buffer_create_filename(
1197 URI: *const c_char,
1198 encoder: *mut _xmlCharEncodingHandler,
1199 compression: c_int,
1200) -> *mut _xmlOutputBuffer {
1201 if URI.is_null() {
1202 return ptr::null_mut();
1203 }
1204
1205 let path_str = unsafe {
1206 match CStr::from_ptr(URI).to_str() {
1207 Ok(s) => s,
1208 Err(_) => return ptr::null_mut(),
1209 }
1210 };
1211
1212 let path_c = std::ffi::CString::new(path_str).unwrap_or_default();
1213
1214 let fd = unsafe {
1216 libc::open(
1217 path_c.as_ptr(),
1218 libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC,
1219 0o644,
1220 )
1221 };
1222
1223 if fd < 0 {
1224 return ptr::null_mut();
1225 }
1226
1227 let obuf = allocate_output_buffer();
1228 if obuf.is_null() {
1229 unsafe { libc::close(fd) };
1230 return ptr::null_mut();
1231 }
1232
1233 let buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1234 if buf.is_null() {
1235 unsafe {
1236 libc::close(fd);
1237 xmlFree(obuf as *mut c_void);
1238 }
1239 return ptr::null_mut();
1240 }
1241
1242 let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1243 if conv_buf.is_null() {
1244 unsafe {
1245 libc::close(fd);
1246 buf_free(buf);
1247 xmlFree(obuf as *mut c_void);
1248 }
1249 return ptr::null_mut();
1250 }
1251
1252 unsafe {
1253 (*obuf).context = fd as *mut c_void;
1254 (*obuf).writecallback = Some(file_write_callback as xmlOutputWriteCallback);
1255 (*obuf).closecallback = Some(file_close_output_callback as xmlOutputCloseCallback);
1256 (*obuf).encoder = encoder as *mut c_void;
1257 (*obuf).buffer = buf as *mut c_void;
1258 (*obuf).conv = conv_buf as *mut c_void;
1259 (*obuf).written = 0;
1260 (*obuf).error = 0;
1261 }
1262
1263 obuf
1264}
1265
1266pub(crate) fn output_buffer_create_fd(
1268 fd: c_int,
1269 encoder: *mut _xmlCharEncodingHandler,
1270) -> *mut _xmlOutputBuffer {
1271 if fd < 0 {
1272 return ptr::null_mut();
1273 }
1274
1275 let obuf = allocate_output_buffer();
1276 if obuf.is_null() {
1277 return ptr::null_mut();
1278 }
1279
1280 let buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1281 if buf.is_null() {
1282 unsafe { xmlFree(obuf as *mut c_void) };
1283 return ptr::null_mut();
1284 }
1285
1286 let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1287 if conv_buf.is_null() {
1288 unsafe {
1289 buf_free(buf);
1290 xmlFree(obuf as *mut c_void);
1291 }
1292 return ptr::null_mut();
1293 }
1294
1295 unsafe {
1296 (*obuf).context = fd as *mut c_void;
1297 (*obuf).writecallback = Some(file_write_callback as xmlOutputWriteCallback);
1298 (*obuf).closecallback = Some(file_close_output_callback as xmlOutputCloseCallback);
1299 (*obuf).encoder = encoder as *mut c_void;
1300 (*obuf).buffer = buf as *mut c_void;
1301 (*obuf).conv = conv_buf as *mut c_void;
1302 (*obuf).written = 0;
1303 (*obuf).error = 0;
1304 }
1305
1306 obuf
1307}
1308
1309pub(crate) fn output_buffer_create_io(
1311 iowrite: Option<xmlOutputWriteCallback>,
1312 ioclose: Option<xmlOutputCloseCallback>,
1313 ioctx: *mut c_void,
1314 encoder: *mut _xmlCharEncodingHandler,
1315) -> *mut _xmlOutputBuffer {
1316 let obuf = allocate_output_buffer();
1317 if obuf.is_null() {
1318 return ptr::null_mut();
1319 }
1320
1321 let buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1322 if buf.is_null() {
1323 unsafe { xmlFree(obuf as *mut c_void) };
1324 return ptr::null_mut();
1325 }
1326
1327 let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1328 if conv_buf.is_null() {
1329 unsafe {
1330 buf_free(buf);
1331 xmlFree(obuf as *mut c_void);
1332 }
1333 return ptr::null_mut();
1334 }
1335
1336 unsafe {
1337 (*obuf).context = ioctx;
1338 (*obuf).writecallback = iowrite;
1339 (*obuf).closecallback = ioclose;
1340 (*obuf).encoder = encoder as *mut c_void;
1341 (*obuf).buffer = buf as *mut c_void;
1342 (*obuf).conv = conv_buf as *mut c_void;
1343 (*obuf).written = 0;
1344 (*obuf).error = 0;
1345 }
1346
1347 obuf
1348}
1349
1350pub(crate) fn output_buffer_create_buffer(
1354 target_buf: *mut _xmlBuffer,
1355 encoder: *mut _xmlCharEncodingHandler,
1356) -> *mut _xmlOutputBuffer {
1357 if target_buf.is_null() {
1358 return ptr::null_mut();
1359 }
1360
1361 let obuf = allocate_output_buffer();
1362 if obuf.is_null() {
1363 return ptr::null_mut();
1364 }
1365
1366 let internal_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1368 if internal_buf.is_null() {
1369 unsafe { xmlFree(obuf as *mut c_void) };
1370 return ptr::null_mut();
1371 }
1372
1373 let conv_buf = buf_create(DEFAULT_BUFFER_SIZE as c_int);
1375 if conv_buf.is_null() {
1376 unsafe {
1377 buf_free(internal_buf);
1378 xmlFree(obuf as *mut c_void);
1379 }
1380 return ptr::null_mut();
1381 }
1382
1383 unsafe {
1384 (*obuf).context = target_buf as *mut c_void;
1385 (*obuf).writecallback = Some(buffer_write_callback as xmlOutputWriteCallback);
1386 (*obuf).closecallback = None;
1387 (*obuf).encoder = encoder as *mut c_void;
1388 (*obuf).buffer = internal_buf as *mut c_void;
1389 (*obuf).conv = conv_buf as *mut c_void;
1390 (*obuf).written = 0;
1391 (*obuf).error = 0;
1392 }
1393
1394 obuf
1395}
1396
1397pub(crate) fn output_buffer_flush(out: *mut _xmlOutputBuffer) -> c_int {
1404 if out.is_null() {
1405 return -1;
1406 }
1407
1408 let ob = unsafe { &mut *out };
1409
1410 if ob.error != 0 {
1411 return -1;
1412 }
1413
1414 let buf = ob.buffer as *mut _xmlBuffer;
1415 if buf.is_null() {
1416 return 0;
1417 }
1418
1419 let b = unsafe { &*buf };
1420 if b.use_ == 0 {
1421 return 0;
1422 }
1423
1424 let write_cb = match ob.writecallback {
1425 Some(cb) => cb,
1426 None => {
1427 buf_empty(buf);
1429 return 0;
1430 }
1431 };
1432
1433 let total_written = if !ob.encoder.is_null() {
1434 let handler = ob.encoder as *mut _xmlCharEncodingHandler;
1436 let conv = ob.conv as *mut _xmlBuffer;
1437
1438 buf_empty(conv);
1440
1441 let ret = encoding::char_enc_out(handler, conv, buf);
1442 if ret < 0 {
1443 ob.error = 1;
1444 return -1;
1445 }
1446
1447 let conv_b = unsafe { &*conv };
1449 if conv_b.use_ > 0 {
1450 let written = unsafe {
1451 write_cb(
1452 ob.context,
1453 conv_b.content as *const c_char,
1454 conv_b.use_ as c_int,
1455 )
1456 };
1457
1458 if written < 0 {
1459 ob.error = 1;
1460 return -1;
1461 }
1462
1463 ob.written = ob.written.saturating_add(written);
1464 buf_empty(conv);
1465 written
1466 } else {
1467 0
1468 }
1469 } else {
1470 let written = unsafe { write_cb(ob.context, b.content as *const c_char, b.use_ as c_int) };
1472
1473 if written < 0 {
1474 ob.error = 1;
1475 return -1;
1476 }
1477
1478 ob.written = ob.written.saturating_add(written);
1479 written
1480 };
1481
1482 buf_empty(buf);
1484
1485 total_written
1486}
1487
1488pub(crate) fn output_buffer_close(out: *mut _xmlOutputBuffer) -> c_int {
1493 if out.is_null() {
1494 return -1;
1495 }
1496
1497 let ob = unsafe { &mut *out };
1498
1499 let flush_ret = output_buffer_flush(out);
1501
1502 if let Some(close_cb) = ob.closecallback {
1504 unsafe {
1505 close_cb(ob.context);
1506 }
1507 }
1508
1509 if !ob.buffer.is_null() {
1511 buf_free(ob.buffer as *mut _xmlBuffer);
1512 }
1513 if !ob.conv.is_null() {
1514 buf_free(ob.conv as *mut _xmlBuffer);
1515 }
1516
1517 unsafe { xmlFree(out as *mut c_void) };
1520
1521 flush_ret
1522}
1523
1524pub(crate) fn output_buffer_write(
1531 out: *mut _xmlOutputBuffer,
1532 len: c_int,
1533 data: *const c_char,
1534) -> c_int {
1535 if out.is_null() || data.is_null() || len <= 0 {
1536 return -1;
1537 }
1538
1539 let ob = unsafe { &mut *out };
1540
1541 if ob.error != 0 {
1542 return -1;
1543 }
1544
1545 let buf = ob.buffer as *mut _xmlBuffer;
1546 if buf.is_null() {
1547 return -1;
1548 }
1549
1550 let ret = buf_add(buf, data as *const xmlChar, len);
1551 if ret < 0 {
1552 ob.error = 1;
1553 return -1;
1554 }
1555
1556 len
1557}
1558
1559pub(crate) fn output_buffer_write_string(out: *mut _xmlOutputBuffer, str: *const c_char) -> c_int {
1561 if out.is_null() || str.is_null() {
1562 return -1;
1563 }
1564
1565 let len = unsafe {
1566 let mut i: c_int = 0;
1567 while *str.add(i as usize) != 0 {
1568 i += 1;
1569 }
1570 i
1571 };
1572
1573 output_buffer_write(out, len, str)
1574}
1575
1576pub(crate) fn output_buffer_write_char(out: *mut _xmlOutputBuffer, c: c_char) -> c_int {
1578 output_buffer_write(out, 1, &c as *const c_char)
1579}
1580
1581pub(crate) fn output_buffer_get_content(out: *mut _xmlOutputBuffer) -> *const xmlChar {
1585 if out.is_null() {
1586 return ptr::null();
1587 }
1588
1589 let ob = unsafe { &*out };
1590 let buf = ob.buffer as *mut _xmlBuffer;
1591 if buf.is_null() {
1592 return ptr::null();
1593 }
1594
1595 buf_content(buf)
1596}
1597
1598pub(crate) fn check_file_exists(filename: *const c_char) -> c_int {
1606 if filename.is_null() {
1607 return -1;
1608 }
1609
1610 let path_str = unsafe {
1611 match CStr::from_ptr(filename).to_str() {
1612 Ok(s) => s,
1613 Err(_) => return -1,
1614 }
1615 };
1616
1617 let path_c = match std::ffi::CString::new(path_str) {
1618 Ok(c) => c,
1619 Err(_) => return -1,
1620 };
1621
1622 let mut stat_buf: libc::stat = unsafe { std::mem::zeroed() };
1623 let ret = unsafe { libc::stat(path_c.as_ptr(), &mut stat_buf) };
1624
1625 if ret == 0 {
1626 1
1627 } else {
1628 0
1629 }
1630}
1631
1632pub(crate) fn read_file_to_memory(filename: *const c_char, size: *mut c_int) -> *mut c_char {
1640 if filename.is_null() {
1641 return ptr::null_mut();
1642 }
1643
1644 let path_str = unsafe {
1645 match CStr::from_ptr(filename).to_str() {
1646 Ok(s) => s,
1647 Err(_) => return ptr::null_mut(),
1648 }
1649 };
1650
1651 let path_c = match std::ffi::CString::new(path_str) {
1652 Ok(c) => c,
1653 Err(_) => return ptr::null_mut(),
1654 };
1655
1656 let fd = unsafe { libc::open(path_c.as_ptr(), libc::O_RDONLY) };
1657 if fd < 0 {
1658 return ptr::null_mut();
1659 }
1660
1661 let mut stat_buf: libc::stat = unsafe { std::mem::zeroed() };
1663 let file_size = if unsafe { libc::stat(path_c.as_ptr(), &mut stat_buf) } == 0 {
1664 stat_buf.st_size as usize
1665 } else {
1666 0
1667 };
1668
1669 let chunk_size = 4096usize;
1671 let initial_capacity = if file_size > 0 { file_size } else { chunk_size };
1672
1673 let mut data = Vec::with_capacity(initial_capacity);
1674 let mut buf = vec![0u8; chunk_size];
1675
1676 loop {
1677 let ret = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, chunk_size) };
1678
1679 if ret < 0 {
1680 unsafe { libc::close(fd) };
1681 return ptr::null_mut();
1682 }
1683
1684 if ret == 0 {
1685 break; }
1687
1688 data.extend_from_slice(&buf[..ret as usize]);
1689 }
1690
1691 unsafe { libc::close(fd) };
1692
1693 if data.is_empty() {
1694 return ptr::null_mut();
1695 }
1696
1697 let result = unsafe { xmlMalloc(data.len()) as *mut c_char };
1699 if result.is_null() {
1700 return ptr::null_mut();
1701 }
1702
1703 unsafe {
1704 ptr::copy_nonoverlapping(data.as_ptr(), result as *mut u8, data.len());
1705 }
1706
1707 if !size.is_null() {
1708 unsafe {
1709 *size = data.len() as c_int;
1710 }
1711 }
1712
1713 result
1714}
1715
1716pub(crate) fn write_memory_to_file(
1721 filename: *const c_char,
1722 data: *const c_char,
1723 size: c_int,
1724) -> c_int {
1725 if filename.is_null() || data.is_null() || size <= 0 {
1726 return -1;
1727 }
1728
1729 let path_str = unsafe {
1730 match CStr::from_ptr(filename).to_str() {
1731 Ok(s) => s,
1732 Err(_) => return -1,
1733 }
1734 };
1735
1736 let path_c = match std::ffi::CString::new(path_str) {
1737 Ok(c) => c,
1738 Err(_) => return -1,
1739 };
1740
1741 let fd = unsafe {
1742 libc::open(
1743 path_c.as_ptr(),
1744 libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC,
1745 0o644,
1746 )
1747 };
1748
1749 if fd < 0 {
1750 return -1;
1751 }
1752
1753 let mut remaining = size as usize;
1754 let mut offset: usize = 0;
1755
1756 while remaining > 0 {
1757 let ret = unsafe {
1758 libc::write(
1759 fd,
1760 (data as *const u8).add(offset) as *const c_void,
1761 remaining,
1762 )
1763 };
1764
1765 if ret < 0 {
1766 unsafe { libc::close(fd) };
1767 return -1;
1768 }
1769
1770 let written = ret as usize;
1771 remaining -= written;
1772 offset += written;
1773 }
1774
1775 unsafe { libc::close(fd) };
1776 0
1777}
1778
1779pub(crate) fn get_cwd() -> *mut c_char {
1784 let mut size: usize = 1024;
1786
1787 loop {
1788 let buf = unsafe { xmlMalloc(size) as *mut c_char };
1789 if buf.is_null() {
1790 return ptr::null_mut();
1791 }
1792
1793 let ret = unsafe { libc::getcwd(buf as *mut c_char, size) };
1794 if !ret.is_null() {
1795 return buf;
1796 }
1797
1798 unsafe { xmlFree(buf as *mut c_void) };
1799
1800 let err = std::io::Error::last_os_error();
1802 if err.raw_os_error() == Some(libc::ERANGE) {
1803 size = size.saturating_mul(2);
1804 if size > 65536 {
1805 return ptr::null_mut(); }
1807 } else {
1808 return ptr::null_mut();
1809 }
1810 }
1811}
1812
1813#[cfg(test)]
1818mod tests {
1819 use super::*;
1820 use std::ffi::CString;
1821 use std::os::raw::c_char;
1822
1823 fn c(s: &str) -> CString {
1826 CString::new(s).unwrap()
1827 }
1828
1829 unsafe fn c_bytes(bytes: &[u8]) -> CString {
1831 CString::from_vec_unchecked(bytes.to_vec())
1832 }
1833
1834 fn i8_slice(s: &[u8]) -> &[i8] {
1836 unsafe { std::slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
1837 }
1838
1839 #[test]
1842 fn test_buf_create_free() {
1843 let buf = buf_create(100);
1844 assert!(!buf.is_null());
1845
1846 let b = unsafe { &*buf };
1847 assert!(!b.content.is_null());
1848 assert_eq!(b.use_, 0);
1849 assert!(b.size >= 100);
1850 assert_eq!(b.alloc, XML_BUFFER_ALLOC_DOUBLEIT);
1851
1852 unsafe {
1854 assert_eq!(*b.content, 0);
1855 }
1856
1857 buf_free(buf);
1858 }
1859
1860 #[test]
1861 fn test_buf_create_default_size() {
1862 let buf = buf_create(0);
1863 assert!(!buf.is_null());
1864
1865 let b = unsafe { &*buf };
1866 assert!(b.size >= MIN_BUFFER_SIZE);
1867
1868 buf_free(buf);
1869 }
1870
1871 #[test]
1872 fn test_buf_create_static() {
1873 let s: &[u8] = b"hello\0";
1874 let buf = buf_create_static(s.as_ptr() as *const xmlChar, 5);
1875 assert!(!buf.is_null());
1876
1877 let b = unsafe { &*buf };
1878 assert_eq!(b.use_, 5);
1879 assert_eq!(b.alloc, XML_BUFFER_ALLOC_IMMUTABLE);
1880
1881 unsafe {
1883 assert_eq!(*b.content.offset(0), b'h');
1884 assert_eq!(*b.content.offset(4), b'o');
1885 assert_eq!(*b.content.offset(5), 0);
1886 }
1887
1888 buf_free(buf); }
1890
1891 #[test]
1892 fn test_buf_add() {
1893 let buf = buf_create(10);
1894 assert!(!buf.is_null());
1895
1896 let s1: &[u8] = b"Hello\0";
1897 let ret = buf_add(buf, s1.as_ptr() as *const xmlChar, 5);
1898 assert_eq!(ret, 5);
1899
1900 let b = unsafe { &*buf };
1901 assert_eq!(b.use_, 5);
1902 unsafe {
1903 assert_eq!(*b.content.offset(0), b'H');
1904 assert_eq!(*b.content.offset(4), b'o');
1905 assert_eq!(*b.content.offset(5), 0); }
1907
1908 let s2: &[u8] = b" World!\0";
1910 let ret = buf_add(buf, s2.as_ptr() as *const xmlChar, 7);
1911 assert_eq!(ret, 7);
1912
1913 let b = unsafe { &*buf };
1914 assert_eq!(b.use_, 12);
1915 unsafe {
1916 assert_eq!(*b.content.offset(6), b'W');
1917 assert_eq!(*b.content.offset(11), b'!');
1918 assert_eq!(*b.content.offset(12), 0);
1919 }
1920
1921 buf_free(buf);
1922 }
1923
1924 #[test]
1925 fn test_buf_add_null() {
1926 let buf = buf_create(10);
1927 let ret = buf_add(buf, ptr::null(), 5);
1928 assert_eq!(ret, 0);
1929 buf_free(buf);
1930 }
1931
1932 #[test]
1933 fn test_buf_cat() {
1934 let buf = buf_create(10);
1935 let s: &[u8] = b"Hello\0";
1936 let ret = buf_cat(buf, s.as_ptr() as *const xmlChar);
1937 assert_eq!(ret, 5);
1938
1939 let b = unsafe { &*buf };
1940 assert_eq!(b.use_, 5);
1941
1942 buf_free(buf);
1943 }
1944
1945 #[test]
1946 fn test_buf_ccat() {
1947 let buf = buf_create(10);
1948 let ret = buf_ccat(buf, b'A' as xmlChar);
1949 assert_eq!(ret, 1);
1950
1951 let b = unsafe { &*buf };
1952 assert_eq!(b.use_, 1);
1953 unsafe {
1954 assert_eq!(*b.content, b'A');
1955 }
1956
1957 buf_free(buf);
1958 }
1959
1960 #[test]
1961 fn test_buf_empty() {
1962 let buf = buf_create(10);
1963 let s: &[u8] = b"Hello\0";
1964 buf_add(buf, s.as_ptr() as *const xmlChar, 5);
1965 assert_eq!(unsafe { &*buf }.use_, 5);
1966
1967 buf_empty(buf);
1968 let b = unsafe { &*buf };
1969 assert_eq!(b.use_, 0);
1970 unsafe {
1971 assert_eq!(*b.content, 0);
1972 }
1973
1974 buf_free(buf);
1975 }
1976
1977 #[test]
1978 fn test_buf_content() {
1979 let buf = buf_create(10);
1980 let content = buf_content(buf);
1981 assert!(!content.is_null());
1982 buf_free(buf);
1983 }
1984
1985 #[test]
1986 fn test_buf_length() {
1987 let buf = buf_create(10);
1988 assert_eq!(buf_length(buf), 0);
1989
1990 let s: &[u8] = b"Hi\0";
1991 buf_add(buf, s.as_ptr() as *const xmlChar, 2);
1992 assert_eq!(buf_length(buf), 2);
1993
1994 buf_free(buf);
1995 }
1996
1997 #[test]
1998 fn test_buf_shrink() {
1999 let buf = buf_create(10);
2000 let s: &[u8] = b"Hello World\0";
2001 buf_add(buf, s.as_ptr() as *const xmlChar, 11);
2002 assert_eq!(buf_length(buf), 11);
2003
2004 buf_shrink(buf, 5);
2005 assert_eq!(buf_length(buf), 6);
2006
2007 let b = unsafe { &*buf };
2008 unsafe {
2009 assert_eq!(*b.content.offset(6), 0); }
2011
2012 buf_shrink(buf, 100);
2014 assert_eq!(buf_length(buf), 0);
2015
2016 buf_free(buf);
2017 }
2018
2019 #[test]
2020 fn test_buf_grow() {
2021 let buf = buf_create(10);
2022 assert!(unsafe { &*buf }.size >= 10);
2023
2024 let ret = buf_grow(buf, 1000);
2025 assert_eq!(ret, 0);
2026 assert!(unsafe { &*buf }.size >= 1000);
2027
2028 buf_free(buf);
2029 }
2030
2031 #[test]
2032 fn test_buf_free_null() {
2033 buf_free(ptr::null_mut()); }
2035
2036 #[test]
2039 fn test_xml_buf_create_free() {
2040 let buf = xml_buf_create(100);
2041 assert!(!buf.is_null());
2042
2043 let b = unsafe { &*buf };
2044 assert!(!b.content.is_null());
2045 assert_eq!(b.use_, 0);
2046 assert!(b.size >= 100);
2047 assert_eq!(b.error, 0);
2048 assert_eq!(b.buffer, 0);
2049 assert_eq!(b.io, 0);
2050
2051 xml_buf_free(buf);
2052 }
2053
2054 #[test]
2055 fn test_xml_buf_add() {
2056 let buf = xml_buf_create(10);
2057 let s: &[u8] = b"Hello\0";
2058 let ret = xml_buf_add(buf, s.as_ptr() as *const xmlChar, 5);
2059 assert_eq!(ret, 5);
2060
2061 let b = unsafe { &*buf };
2062 assert_eq!(b.use_, 5);
2063
2064 xml_buf_free(buf);
2065 }
2066
2067 #[test]
2068 fn test_xml_buf_cat() {
2069 let buf = xml_buf_create(10);
2070 let s: &[u8] = b"Hello\0";
2071 let ret = xml_buf_cat(buf, s.as_ptr() as *const xmlChar);
2072 assert_eq!(ret, 5);
2073
2074 xml_buf_free(buf);
2075 }
2076
2077 #[test]
2078 fn test_xml_buf_content() {
2079 let buf = xml_buf_create(10);
2080 let content = xml_buf_content(buf);
2081 assert!(!content.is_null());
2082 xml_buf_free(buf);
2083 }
2084
2085 #[test]
2086 fn test_xml_buf_length() {
2087 let buf = xml_buf_create(10);
2088 assert_eq!(xml_buf_length(buf), 0);
2089
2090 let s: &[u8] = b"Hi\0";
2091 xml_buf_add(buf, s.as_ptr() as *const xmlChar, 2);
2092 assert_eq!(xml_buf_length(buf), 2);
2093
2094 xml_buf_free(buf);
2095 }
2096
2097 #[test]
2098 fn test_xml_buf_grow() {
2099 let buf = xml_buf_create(10);
2100 let ret = xml_buf_grow(buf, 500);
2101 assert_eq!(ret, 0);
2102 assert!(unsafe { &*buf }.size >= 500);
2103
2104 xml_buf_free(buf);
2105 }
2106
2107 #[test]
2108 fn test_xml_buf_shrink() {
2109 let buf = xml_buf_create(10);
2110 let s: &[u8] = b"Hello\0";
2111 xml_buf_add(buf, s.as_ptr() as *const xmlChar, 5);
2112 assert_eq!(xml_buf_length(buf), 5);
2113
2114 xml_buf_shrink(buf, 3);
2115 assert_eq!(xml_buf_length(buf), 2);
2116
2117 xml_buf_free(buf);
2118 }
2119
2120 #[test]
2123 fn test_input_buffer_create_mem() {
2124 let data = c("Hello XML");
2125 let buf = input_buffer_create_mem(data.as_ptr(), 9, 0); assert!(!buf.is_null());
2127
2128 let b = unsafe { &*buf };
2129 assert!(b.readcallback.is_none());
2130 assert!(!b.buffer.is_null());
2131 assert_eq!(b.error, 0);
2132
2133 let mut out = [0i8; 16];
2135 let ret = input_buffer_read(buf, out.as_mut_ptr(), 16);
2136 assert_eq!(ret, 9);
2137 assert_eq!(&out[..9], i8_slice(b"Hello XML"));
2138
2139 input_buffer_free(buf);
2140 }
2141
2142 #[test]
2143 fn test_input_buffer_create_mem_empty() {
2144 let buf = input_buffer_create_mem(ptr::null(), 0, 0);
2145 assert!(buf.is_null());
2146 }
2147
2148 #[test]
2149 fn test_input_buffer_read_partial() {
2150 let data = c("Hello XML World");
2151 let buf = input_buffer_create_mem(data.as_ptr(), 15, 0);
2152 assert!(!buf.is_null());
2153
2154 let mut out1 = [0i8; 5];
2156 let ret = input_buffer_read(buf, out1.as_mut_ptr(), 5);
2157 assert_eq!(ret, 5);
2158 assert_eq!(&out1[..5], i8_slice(b"Hello"));
2159
2160 let mut out2 = [0i8; 10];
2161 let ret = input_buffer_read(buf, out2.as_mut_ptr(), 10);
2162 assert_eq!(ret, 10);
2163 assert_eq!(&out2[..10], i8_slice(b" XML World"));
2164
2165 input_buffer_free(buf);
2166 }
2167
2168 #[test]
2169 fn test_input_buffer_push() {
2170 let buf = input_buffer_create_io(None, None, ptr::null_mut(), 0);
2171 assert!(!buf.is_null());
2172
2173 let data1 = c("<root>");
2174 let ret = input_buffer_push(buf, data1.as_ptr(), 6);
2175 assert_eq!(ret, 6);
2176
2177 let data2 = c("</root>");
2178 let ret = input_buffer_push(buf, data2.as_ptr(), 7);
2179 assert_eq!(ret, 7);
2180
2181 let mut out = [0i8; 32];
2183 let ret = input_buffer_read(buf, out.as_mut_ptr(), 32);
2184 assert_eq!(ret, 13);
2185 assert_eq!(&out[..13], i8_slice(b"<root></root>"));
2186
2187 input_buffer_free(buf);
2188 }
2189
2190 #[test]
2191 fn test_input_buffer_set_encoder() {
2192 let buf = input_buffer_create_mem(ptr::null(), 0, 0);
2193 let data = c("test");
2195 let buf = input_buffer_create_mem(data.as_ptr(), 4, 0);
2196 assert!(!buf.is_null());
2197
2198 input_buffer_set_encoder(buf, ptr::null_mut());
2200 let b = unsafe { &*buf };
2201 assert!(b.encoder.is_null());
2202
2203 input_buffer_free(buf);
2204 }
2205
2206 #[test]
2209 fn test_output_buffer_create_buffer() {
2210 let internal_buf = buf_create(100);
2211 assert!(!internal_buf.is_null());
2212
2213 let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
2214 assert!(!obuf.is_null());
2215
2216 let data = c("Hello Output");
2218 let ret = output_buffer_write(obuf, 12, data.as_ptr());
2219 assert_eq!(ret, 12);
2220
2221 let flushed = output_buffer_flush(obuf);
2223 assert_eq!(flushed, 12);
2224
2225 let content = output_buffer_get_content(obuf);
2227 assert!(content.is_null() || unsafe { *content } == 0);
2228
2229 let ctx = unsafe { (*obuf).context as *mut _xmlBuffer };
2231 let ctx_b = unsafe { &*ctx };
2232 assert_eq!(ctx_b.use_, 12);
2233 unsafe {
2234 assert_eq!(*ctx_b.content.offset(0), b'H' as xmlChar);
2235 }
2236
2237 output_buffer_close(obuf);
2240 }
2241
2242 #[test]
2243 fn test_output_buffer_write_string() {
2244 let internal_buf = buf_create(100);
2245 let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
2246 assert!(!obuf.is_null());
2247
2248 let s = c("Hello");
2249 let ret = output_buffer_write_string(obuf, s.as_ptr());
2250 assert_eq!(ret, 5);
2251
2252 output_buffer_close(obuf);
2254 }
2255
2256 #[test]
2257 fn test_output_buffer_write_char() {
2258 let internal_buf = buf_create(100);
2259 let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
2260 assert!(!obuf.is_null());
2261
2262 let ret = output_buffer_write_char(obuf, b'X' as c_char);
2263 assert_eq!(ret, 1);
2264
2265 output_buffer_close(obuf);
2266 }
2267
2268 #[test]
2269 fn test_output_buffer_get_content() {
2270 let internal_buf = buf_create(100);
2271 let obuf = output_buffer_create_buffer(internal_buf, ptr::null_mut());
2272 assert!(!obuf.is_null());
2273
2274 let content = output_buffer_get_content(obuf);
2275 assert!(!content.is_null());
2276
2277 output_buffer_close(obuf);
2278 }
2279
2280 #[test]
2283 fn test_check_file_exists() {
2284 let exists = check_file_exists(c("/dev/null").as_ptr());
2286 assert!(exists == 1);
2287
2288 let not_exists = check_file_exists(c("/tmp/__nonexistent_file_xyz123__").as_ptr());
2290 assert!(not_exists == 0);
2291 }
2292
2293 #[test]
2294 fn test_read_write_file() {
2295 let tmpfile = c("/tmp/libxml_rs_test_io_file.txt");
2296
2297 let data = c("Hello File I/O!");
2299 let ret = write_memory_to_file(tmpfile.as_ptr(), data.as_ptr(), 15);
2300 assert_eq!(ret, 0);
2301
2302 assert!(check_file_exists(tmpfile.as_ptr()) == 1);
2304
2305 let mut size: c_int = 0;
2307 let read_data = read_file_to_memory(tmpfile.as_ptr(), &mut size as *mut c_int);
2308 assert!(!read_data.is_null());
2309 assert_eq!(size, 15);
2310
2311 unsafe {
2312 let slice = std::slice::from_raw_parts(read_data as *const u8, size as usize);
2313 assert_eq!(slice, b"Hello File I/O!");
2314 }
2315
2316 unsafe { xmlFree(read_data as *mut c_void) };
2317
2318 std::fs::remove_file("/tmp/libxml_rs_test_io_file.txt").ok();
2320 }
2321
2322 #[test]
2323 fn test_read_file_nonexistent() {
2324 let result = read_file_to_memory(
2325 c("/tmp/__nonexistent_file_xyz456__").as_ptr(),
2326 ptr::null_mut(),
2327 );
2328 assert!(result.is_null());
2329 }
2330
2331 #[test]
2332 fn test_write_file_null() {
2333 let ret = write_memory_to_file(ptr::null(), c("data").as_ptr(), 4);
2334 assert_eq!(ret, -1);
2335 }
2336
2337 #[test]
2338 fn test_get_cwd() {
2339 let cwd = get_cwd();
2340 assert!(!cwd.is_null());
2341 unsafe {
2342 let s = CStr::from_ptr(cwd);
2343 assert!(!s.to_bytes().is_empty());
2344 xmlFree(cwd as *mut c_void);
2345 }
2346 }
2347
2348 #[test]
2351 fn test_buf_add_large_data() {
2352 let buf = buf_create(10);
2353 let mut large_data = Vec::new();
2354 large_data.resize(5000, b'X');
2355 large_data.push(0);
2356
2357 let ret = buf_add(buf, large_data.as_ptr() as *const xmlChar, 5000);
2358 assert_eq!(ret, 5000);
2359
2360 let b = unsafe { &*buf };
2361 assert_eq!(b.use_, 5000);
2362 assert!(b.size >= 5001);
2363
2364 buf_free(buf);
2365 }
2366
2367 #[test]
2368 fn test_input_buffer_free_null() {
2369 input_buffer_free(ptr::null_mut()); }
2371
2372 #[test]
2373 fn test_output_buffer_close_null() {
2374 let ret = output_buffer_close(ptr::null_mut());
2375 assert_eq!(ret, -1);
2376 }
2377
2378 #[test]
2379 fn test_buf_add_to_immutable() {
2380 let s: &[u8] = b"static\0";
2381 let buf = buf_create_static(s.as_ptr() as *const xmlChar, 6);
2382 assert!(!buf.is_null());
2383
2384 let data: &[u8] = b"more\0";
2386 let ret = buf_add(buf, data.as_ptr() as *const xmlChar, 4);
2387 assert_eq!(ret, -1); buf_free(buf);
2390 }
2391
2392 #[test]
2395 fn test_encoding_from_int() {
2396 assert_eq!(
2397 encoding_from_int(0),
2398 xmlCharEncoding::XML_CHAR_ENCODING_NONE
2399 );
2400 assert_eq!(
2401 encoding_from_int(1),
2402 xmlCharEncoding::XML_CHAR_ENCODING_UTF8
2403 );
2404 assert_eq!(
2405 encoding_from_int(10),
2406 xmlCharEncoding::XML_CHAR_ENCODING_8859_1
2407 );
2408 assert_eq!(
2409 encoding_from_int(22),
2410 xmlCharEncoding::XML_CHAR_ENCODING_ASCII
2411 );
2412 assert_eq!(
2413 encoding_from_int(999),
2414 xmlCharEncoding::XML_CHAR_ENCODING_ERROR
2415 );
2416 }
2417
2418 #[test]
2419 fn test_find_handler_for_encoding() {
2420 let handler = find_handler_for_encoding(1);
2422 assert!(handler.is_null());
2423
2424 let handler = find_handler_for_encoding(0);
2426 assert!(handler.is_null());
2427
2428 let handler = find_handler_for_encoding(-1);
2430 assert!(handler.is_null());
2431 }
2432
2433 #[test]
2434 fn test_input_buffer_with_encoding_latin1() {
2435 encoding::init_encodings();
2437
2438 let latin1_data: &[u8] = &[0x48, 0x65, 0x6C, 0x6C, 0xF6, 0x00]; let buf = input_buffer_create_mem(
2442 latin1_data.as_ptr() as *const c_char,
2443 5,
2444 10, );
2446 assert!(!buf.is_null());
2447
2448 let mut out = [0i8; 16];
2450 let ret = input_buffer_read(buf, out.as_mut_ptr(), 16);
2451 assert!(ret > 0);
2452
2453 let expected = b"Hell\xC3\xB6";
2455 assert_eq!(&out[..ret as usize], i8_slice(expected));
2456
2457 input_buffer_free(buf);
2458 }
2459
2460 #[test]
2461 fn test_output_buffer_with_encoding() {
2462 encoding::init_encodings();
2464
2465 let enc_name: &[u8] = b"ISO-8859-1\0";
2467 let handler = encoding::find_encoding_handler(enc_name.as_ptr() as *const xmlChar);
2468 assert!(!handler.is_null(), "Latin-1 handler should be available");
2469
2470 let internal_buf = buf_create(100);
2471 let obuf = output_buffer_create_buffer(internal_buf, handler);
2472 assert!(!obuf.is_null());
2473
2474 let utf8_data = unsafe { c_bytes(&[0x48, 0x65, 0x6C, 0x6C, 0xC3, 0xB6]) };
2476 let ret = output_buffer_write(obuf, 6, utf8_data.as_ptr());
2477 assert_eq!(ret, 6);
2478
2479 let flushed = output_buffer_flush(obuf);
2481 assert!(flushed > 0);
2482
2483 let ctx = unsafe { (*obuf).context as *mut _xmlBuffer };
2485 let ctx_b = unsafe { &*ctx };
2486 assert_eq!(ctx_b.use_, 5);
2488 unsafe {
2489 assert_eq!(*ctx_b.content.offset(0), 0x48); assert_eq!(*ctx_b.content.offset(4), 0xF6); }
2492
2493 output_buffer_close(obuf);
2511 buf_free(internal_buf);
2512 }
2513
2514 #[test]
2517 fn test_input_buffer_create_fd() {
2518 let fd =
2520 unsafe { libc::open(b"/dev/null\0" as *const u8 as *const c_char, libc::O_RDONLY) };
2521 assert!(fd >= 0);
2522
2523 let buf = input_buffer_create_fd(fd, 0);
2524 assert!(!buf.is_null());
2525
2526 let mut out = [0i8; 16];
2528 let ret = input_buffer_read(buf, out.as_mut_ptr(), 16);
2529 assert_eq!(ret, 0);
2530
2531 input_buffer_free(buf); }
2533
2534 #[test]
2537 fn test_input_buffer_create_io() {
2538 static mut TEST_DATA: &[u8] = b"Hello from callback!";
2540 static mut CALLED: bool = false;
2541
2542 unsafe extern "C" fn test_read(
2543 _ctx: *mut c_void,
2544 buffer: *mut c_char,
2545 len: c_int,
2546 ) -> c_int {
2547 if CALLED {
2548 return 0; }
2550 CALLED = true;
2551 let data = TEST_DATA;
2552 let to_copy = (data.len() as c_int).min(len);
2553 if to_copy > 0 {
2554 std::ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut u8, to_copy as usize);
2555 }
2556 to_copy
2557 }
2558
2559 unsafe extern "C" fn test_close(_ctx: *mut c_void) -> c_int {
2560 0
2561 }
2562
2563 let buf = input_buffer_create_io(
2564 Some(test_read as xmlInputReadCallback),
2565 Some(test_close as xmlInputCloseCallback),
2566 ptr::null_mut(),
2567 0,
2568 );
2569 assert!(!buf.is_null());
2570
2571 let mut out = [0i8; 32];
2572 let ret = input_buffer_read(buf, out.as_mut_ptr(), 32);
2573 assert!(ret > 0);
2574
2575 input_buffer_free(buf);
2576 }
2577}