1#![allow(unsafe_op_in_unsafe_fn)]
2
3use core::ffi::{c_char, c_int, c_uint, c_void, CStr};
4use core::{mem, ptr};
5
6use libc::FILE;
7use libc::{fclose, fdopen, ferror, fflush, fgetc, fopen, fread, fwrite, ungetc};
8
9use crate::allocator::Allocator;
10use crate::bzlib::prefix;
11use crate::bzlib::BZ_MAX_UNUSED_U32;
12use crate::bzlib::{bz_stream, BZ2_bzCompressEnd, BZ2_bzDecompressEnd};
13use crate::bzlib::{Action, BzStream, ReturnCode};
14use crate::bzlib::{
15 BZ2_bzCompressHelp, BZ2_bzCompressInitHelp, BZ2_bzDecompressHelp, BZ2_bzDecompressInitHelp,
16};
17use crate::BZ_MAX_UNUSED;
18
19#[cfg(doc)]
20use crate::{
21 BZ2_bzCompressInit, BZ2_bzDecompressInit, BZ_CONFIG_ERROR, BZ_DATA_ERROR, BZ_DATA_ERROR_MAGIC,
22 BZ_FINISH, BZ_FINISH_OK, BZ_FLUSH, BZ_FLUSH_OK, BZ_IO_ERROR, BZ_MEM_ERROR, BZ_OK,
23 BZ_OUTBUFF_FULL, BZ_PARAM_ERROR, BZ_RUN, BZ_RUN_OK, BZ_SEQUENCE_ERROR, BZ_STREAM_END,
24 BZ_UNEXPECTED_EOF,
25};
26
27#[cfg(not(target_os = "windows"))]
29extern "C" {
30 #[cfg_attr(target_os = "macos", link_name = "__stdinp")]
31 static mut stdin: *mut FILE;
32 #[cfg_attr(target_os = "macos", link_name = "__stdoutp")]
33 static mut stdout: *mut FILE;
34}
35
36#[cfg(target_os = "windows")]
37extern "C" {
38 fn __acrt_iob_func(idx: libc::c_uint) -> *mut FILE;
39}
40
41#[cfg(not(target_os = "windows"))]
42macro_rules! STDIN {
43 () => {
44 stdin
45 };
46}
47
48#[cfg(target_os = "windows")]
49macro_rules! STDIN {
50 () => {
51 __acrt_iob_func(0)
52 };
53}
54
55#[cfg(not(target_os = "windows"))]
56macro_rules! STDOUT {
57 () => {
58 stdout
59 };
60}
61
62#[cfg(target_os = "windows")]
63macro_rules! STDOUT {
64 () => {
65 __acrt_iob_func(1)
66 };
67}
68
69#[allow(non_camel_case_types)]
83pub struct BZFILE {
84 handle: *mut FILE,
85 buf: [i8; BZ_MAX_UNUSED as usize],
86 bufN: i32,
87 strm: bz_stream,
88 lastErr: ReturnCode,
89 operation: Operation,
90 initialisedOk: bool,
91}
92
93unsafe fn myfeof(f: *mut FILE) -> bool {
94 let c = fgetc(f);
95 if c == -1 {
96 return true;
97 }
98
99 ungetc(c, f);
100
101 false
102}
103
104macro_rules! BZ_SETERR_RAW {
105 ($bzerror:expr, $bzf:expr, $return_code:expr) => {
106 if let Some(bzerror) = $bzerror.as_deref_mut() {
107 *bzerror = $return_code as c_int;
108 }
109
110 if let Some(bzf) = $bzf.as_deref_mut() {
111 bzf.lastErr = $return_code;
112 }
113 };
114}
115
116macro_rules! BZ_SETERR {
117 ($bzerror:expr, $bzf:expr, $return_code:expr) => {
118 if let Some(bzerror) = $bzerror.as_deref_mut() {
119 *bzerror = $return_code as c_int;
120 }
121
122 $bzf.lastErr = $return_code;
123 };
124}
125
126#[export_name = prefix!(BZ2_bzWriteOpen)]
160pub unsafe extern "C" fn BZ2_bzWriteOpen(
161 bzerror: *mut c_int,
162 f: *mut FILE,
163 blockSize100k: c_int,
164 verbosity: c_int,
165 workFactor: c_int,
166) -> *mut BZFILE {
167 BZ2_bzWriteOpenHelp(bzerror.as_mut(), f, blockSize100k, verbosity, workFactor)
168}
169
170unsafe fn BZ2_bzWriteOpenHelp(
171 mut bzerror: Option<&mut c_int>,
172 f: *mut FILE,
173 blockSize100k: c_int,
174 verbosity: c_int,
175 mut workFactor: c_int,
176) -> *mut BZFILE {
177 let mut bzf: Option<&mut BZFILE> = None;
178
179 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_OK);
180
181 if f.is_null()
182 || !(1..=9).contains(&blockSize100k)
183 || !(0..=250).contains(&workFactor)
184 || !(0..=4).contains(&verbosity)
185 {
186 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
187 return ptr::null_mut();
188 }
189
190 if ferror(f) != 0 {
191 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
192 return ptr::null_mut();
193 }
194
195 let Some(allocator) = Allocator::DEFAULT else {
196 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
197 return ptr::null_mut();
198 };
199
200 let Some(bzf) = allocator.allocate_zeroed::<BZFILE>(1) else {
201 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_MEM_ERROR);
202 return ptr::null_mut();
203 };
204
205 let bzf = unsafe { &mut *bzf };
207
208 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
209
210 bzf.initialisedOk = false;
211 bzf.bufN = 0;
212 bzf.handle = f;
213 bzf.operation = Operation::Writing;
214 bzf.strm.bzalloc = None;
215 bzf.strm.bzfree = None;
216 bzf.strm.opaque = ptr::null_mut();
217
218 if workFactor == 0 {
219 workFactor = 30;
220 }
221
222 match BZ2_bzCompressInitHelp(
223 BzStream::from_mut(&mut bzf.strm),
224 blockSize100k,
225 verbosity,
226 workFactor,
227 ) {
228 ReturnCode::BZ_OK => {
229 bzf.strm.avail_in = 0;
230 bzf.initialisedOk = true;
231
232 bzf as *mut BZFILE
233 }
234 error => {
235 BZ_SETERR!(bzerror, bzf, error);
236 allocator.deallocate(bzf, 1);
237
238 ptr::null_mut()
239 }
240 }
241}
242
243#[export_name = prefix!(BZ2_bzWrite)]
271pub unsafe extern "C" fn BZ2_bzWrite(
272 bzerror: *mut c_int,
273 b: *mut BZFILE,
274 buf: *const c_void,
275 len: c_int,
276) {
277 BZ2_bzWriteHelp(bzerror.as_mut(), b.as_mut(), buf, len)
278}
279
280unsafe fn BZ2_bzWriteHelp(
281 mut bzerror: Option<&mut c_int>,
282 mut b: Option<&mut BZFILE>,
283 buf: *const c_void,
284 len: c_int,
285) {
286 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
287
288 let Some(bzf) = b.as_mut() else {
289 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
290 return;
291 };
292
293 if buf.is_null() || len < 0 as c_int {
294 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
295 return;
296 }
297
298 if !matches!(bzf.operation, Operation::Writing) {
299 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
300 return;
301 }
302
303 if ferror(bzf.handle) != 0 {
304 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
305 return;
306 }
307
308 if len == 0 {
309 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
310 return;
311 }
312
313 bzf.strm.avail_in = len as c_uint;
314 bzf.strm.next_in = buf.cast::<c_char>();
315
316 loop {
317 bzf.strm.avail_out = BZ_MAX_UNUSED_U32;
318 bzf.strm.next_out = bzf.buf.as_mut_ptr().cast::<c_char>();
319 match BZ2_bzCompressHelp(
320 unsafe { BzStream::from_mut(&mut bzf.strm) },
321 Action::Run as c_int,
322 ) {
323 ReturnCode::BZ_RUN_OK => {
324 if bzf.strm.avail_out < BZ_MAX_UNUSED_U32 {
325 let n1 = (BZ_MAX_UNUSED_U32 - bzf.strm.avail_out) as usize;
326 let n2 = fwrite(
327 bzf.buf.as_mut_ptr().cast::<c_void>(),
328 mem::size_of::<u8>(),
329 n1,
330 bzf.handle,
331 );
332 if n1 != n2 || ferror(bzf.handle) != 0 {
333 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
334 return;
335 }
336 }
337 if bzf.strm.avail_in == 0 {
338 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
339 return;
340 }
341 }
342 error => {
343 BZ_SETERR!(bzerror, bzf, error);
344 return;
345 }
346 }
347 }
348}
349
350#[export_name = prefix!(BZ2_bzWriteClose)]
381pub unsafe extern "C" fn BZ2_bzWriteClose(
382 bzerror: *mut c_int,
383 b: *mut BZFILE,
384 abandon: c_int,
385 nbytes_in: *mut c_uint,
386 nbytes_out: *mut c_uint,
387) {
388 BZ2_bzWriteCloseHelp(
389 bzerror.as_mut(),
390 b,
391 abandon,
392 nbytes_in.as_mut(),
393 nbytes_out.as_mut(),
394 )
395}
396
397unsafe fn BZ2_bzWriteCloseHelp(
398 bzerror: Option<&mut c_int>,
399 b: *mut BZFILE,
400 abandon: c_int,
401 nbytes_in: Option<&mut c_uint>,
402 nbytes_out: Option<&mut c_uint>,
403) {
404 BZ2_bzWriteClose64Help(bzerror, b, abandon, nbytes_in, None, nbytes_out, None);
405}
406
407#[export_name = prefix!(BZ2_bzWriteClose64)]
440pub unsafe extern "C" fn BZ2_bzWriteClose64(
441 bzerror: *mut c_int,
442 b: *mut BZFILE,
443 abandon: c_int,
444 nbytes_in_lo32: *mut c_uint,
445 nbytes_in_hi32: *mut c_uint,
446 nbytes_out_lo32: *mut c_uint,
447 nbytes_out_hi32: *mut c_uint,
448) {
449 BZ2_bzWriteClose64Help(
450 bzerror.as_mut(),
451 b,
452 abandon,
453 nbytes_in_lo32.as_mut(),
454 nbytes_in_hi32.as_mut(),
455 nbytes_out_lo32.as_mut(),
456 nbytes_out_hi32.as_mut(),
457 )
458}
459
460unsafe fn BZ2_bzWriteClose64Help(
461 mut bzerror: Option<&mut c_int>,
462 b: *mut BZFILE,
463 abandon: c_int,
464 mut nbytes_in_lo32: Option<&mut c_uint>,
465 mut nbytes_in_hi32: Option<&mut c_uint>,
466 mut nbytes_out_lo32: Option<&mut c_uint>,
467 mut nbytes_out_hi32: Option<&mut c_uint>,
468) {
469 let mut b = b.as_mut();
470 let Some(bzf) = b else {
471 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
472 return;
473 };
474
475 if !matches!(bzf.operation, Operation::Writing) {
476 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
477 return;
478 }
479
480 #[cfg(not(miri))]
481 if !bzf.handle.is_null() && ferror(bzf.handle) != 0 {
482 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
483 return;
484 }
485
486 if let Some(nbytes_in_lo32) = nbytes_in_lo32.as_deref_mut() {
487 *nbytes_in_lo32 = 0
488 }
489 if let Some(nbytes_in_hi32) = nbytes_in_hi32.as_deref_mut() {
490 *nbytes_in_hi32 = 0;
491 }
492 if let Some(nbytes_out_lo32) = nbytes_out_lo32.as_deref_mut() {
493 *nbytes_out_lo32 = 0;
494 }
495 if let Some(nbytes_out_hi32) = nbytes_out_hi32.as_deref_mut() {
496 *nbytes_out_hi32 = 0;
497 }
498
499 if abandon == 0 && bzf.lastErr == ReturnCode::BZ_OK {
500 loop {
501 bzf.strm.avail_out = BZ_MAX_UNUSED_U32;
502 bzf.strm.next_out = (bzf.buf).as_mut_ptr().cast::<c_char>();
503 match BZ2_bzCompressHelp(BzStream::from_mut(&mut bzf.strm), 2 as c_int) {
504 ret @ (ReturnCode::BZ_FINISH_OK | ReturnCode::BZ_STREAM_END) => {
505 if bzf.strm.avail_out < BZ_MAX_UNUSED_U32 {
506 let n1 = (BZ_MAX_UNUSED_U32 - bzf.strm.avail_out) as usize;
507 let n2 = fwrite(
508 bzf.buf.as_mut_ptr().cast::<c_void>(),
509 mem::size_of::<u8>(),
510 n1,
511 bzf.handle,
512 );
513 if n1 != n2 || ferror(bzf.handle) != 0 {
514 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
515 return;
516 }
517 }
518
519 if let ReturnCode::BZ_STREAM_END = ret {
520 break;
521 }
522 }
523 ret => {
524 BZ_SETERR!(bzerror, bzf, ret);
525 return;
526 }
527 }
528 }
529 }
530
531 if abandon == 0 && ferror(bzf.handle) == 0 {
532 fflush(bzf.handle);
533 if ferror(bzf.handle) != 0 {
534 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
535 return;
536 }
537 }
538
539 if let Some(nbytes_in_lo32) = nbytes_in_lo32 {
540 *nbytes_in_lo32 = bzf.strm.total_in_lo32;
541 }
542 if let Some(nbytes_in_hi32) = nbytes_in_hi32 {
543 *nbytes_in_hi32 = bzf.strm.total_in_hi32;
544 }
545 if let Some(nbytes_out_lo32) = nbytes_out_lo32 {
546 *nbytes_out_lo32 = bzf.strm.total_out_lo32;
547 }
548 if let Some(nbytes_out_hi32) = nbytes_out_hi32 {
549 *nbytes_out_hi32 = bzf.strm.total_out_hi32;
550 }
551
552 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
553
554 BZ2_bzCompressEnd(&mut bzf.strm);
555
556 let Some(allocator) = Allocator::DEFAULT else {
557 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
558 return;
559 };
560
561 allocator.deallocate(bzf, 1);
562}
563
564#[export_name = prefix!(BZ2_bzReadOpen)]
608pub unsafe extern "C" fn BZ2_bzReadOpen(
609 bzerror: *mut c_int,
610 f: *mut FILE,
611 verbosity: c_int,
612 small: c_int,
613 unused: *mut c_void,
614 nUnused: c_int,
615) -> *mut BZFILE {
616 BZ2_bzReadOpenHelp(bzerror.as_mut(), f, verbosity, small, unused, nUnused)
617}
618
619unsafe fn BZ2_bzReadOpenHelp(
620 mut bzerror: Option<&mut c_int>,
621 f: *mut FILE,
622 verbosity: c_int,
623 small: c_int,
624 unused: *mut c_void,
625 nUnused: c_int,
626) -> *mut BZFILE {
627 let mut bzf: Option<&mut BZFILE> = None;
628
629 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_OK);
630
631 if f.is_null()
632 || !(0..=1).contains(&small)
633 || !(0..=4).contains(&verbosity)
634 || (unused.is_null() && nUnused != 0)
635 || (!unused.is_null() && !(0..=BZ_MAX_UNUSED_U32 as c_int).contains(&nUnused))
636 {
637 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
638 return ptr::null_mut::<BZFILE>();
639 }
640
641 if ferror(f) != 0 {
642 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
643 return ptr::null_mut::<BZFILE>();
644 }
645
646 let Some(allocator) = Allocator::DEFAULT else {
647 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
648 return ptr::null_mut();
649 };
650
651 let Some(bzf) = allocator.allocate_zeroed::<BZFILE>(1) else {
652 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_MEM_ERROR);
653 return ptr::null_mut();
654 };
655
656 let bzf = unsafe { &mut *bzf };
658
659 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
660
661 bzf.initialisedOk = false;
662 bzf.handle = f;
663 bzf.bufN = 0;
664 bzf.operation = Operation::Reading;
665 bzf.strm.bzalloc = None;
666 bzf.strm.bzfree = None;
667 bzf.strm.opaque = ptr::null_mut();
668
669 if nUnused > 0 {
670 ptr::copy(
671 unused as *mut i8,
672 bzf.buf[bzf.bufN as usize..].as_mut_ptr(),
673 nUnused as usize,
674 );
675 bzf.bufN += nUnused;
676 }
677
678 match BZ2_bzDecompressInitHelp(BzStream::from_mut(&mut bzf.strm), verbosity, small) {
679 ReturnCode::BZ_OK => {
680 bzf.strm.avail_in = bzf.bufN as c_uint;
681 bzf.strm.next_in = bzf.buf.as_mut_ptr().cast::<c_char>();
682 bzf.initialisedOk = true;
683 }
684 ret => {
685 BZ_SETERR!(bzerror, bzf, ret);
686
687 allocator.deallocate(bzf, 1);
688
689 return ptr::null_mut();
690 }
691 }
692
693 bzf as *mut BZFILE
694}
695
696#[export_name = prefix!(BZ2_bzReadClose)]
721pub unsafe extern "C" fn BZ2_bzReadClose(bzerror: *mut c_int, b: *mut BZFILE) {
722 let mut bzerror = bzerror.as_mut();
723 let mut b = b.as_mut();
724
725 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
726
727 let Some(bzf) = b else {
728 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
729 return;
730 };
731
732 if !matches!(bzf.operation, Operation::Reading) {
733 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
734 return;
735 }
736
737 if bzf.initialisedOk {
738 BZ2_bzDecompressEnd(&mut bzf.strm);
739 }
740
741 let Some(allocator) = Allocator::DEFAULT else {
742 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
743 return;
744 };
745
746 allocator.deallocate(bzf, 1)
747}
748
749#[export_name = prefix!(BZ2_bzRead)]
784pub unsafe extern "C" fn BZ2_bzRead(
785 bzerror: *mut c_int,
786 b: *mut BZFILE,
787 buf: *mut c_void,
788 len: c_int,
789) -> c_int {
790 BZ2_bzReadHelp(bzerror.as_mut(), b.as_mut(), buf, len)
791}
792
793unsafe fn BZ2_bzReadHelp(
794 mut bzerror: Option<&mut c_int>,
795 mut b: Option<&mut BZFILE>,
796 buf: *mut c_void,
797 len: c_int,
798) -> c_int {
799 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
800
801 let Some(bzf) = b.as_mut() else {
802 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
803 return 0;
804 };
805
806 if buf.is_null() || len < 0 {
807 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
808 return 0;
809 }
810
811 if !matches!(bzf.operation, Operation::Reading) {
812 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
813 return 0;
814 }
815
816 if len == 0 as c_int {
817 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
818 return 0;
819 }
820
821 bzf.strm.avail_out = len as c_uint;
822 bzf.strm.next_out = buf as *mut c_char;
823 loop {
824 if ferror(bzf.handle) != 0 {
825 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
826 return 0;
827 }
828
829 if bzf.strm.avail_in == 0 && !myfeof(bzf.handle) {
830 let n = fread(
831 (bzf.buf).as_mut_ptr() as *mut c_void,
832 ::core::mem::size_of::<u8>(),
833 5000,
834 bzf.handle,
835 ) as i32;
836
837 if ferror(bzf.handle) != 0 {
838 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
839 return 0;
840 }
841
842 bzf.bufN = n;
843 bzf.strm.avail_in = bzf.bufN as c_uint;
844 bzf.strm.next_in = (bzf.buf).as_mut_ptr().cast::<c_char>();
845 }
846
847 match BZ2_bzDecompressHelp(unsafe { BzStream::from_mut(&mut bzf.strm) }) {
848 ReturnCode::BZ_OK => {
849 if myfeof(bzf.handle) && bzf.strm.avail_in == 0 && bzf.strm.avail_out > 0 {
850 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_UNEXPECTED_EOF);
851 return 0;
852 } else if bzf.strm.avail_out == 0 {
853 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
854 return len;
855 } else {
856 continue;
857 }
858 }
859 ReturnCode::BZ_STREAM_END => {
860 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_STREAM_END);
861 return (len as c_uint - bzf.strm.avail_out) as c_int;
862 }
863 error => {
864 BZ_SETERR!(bzerror, bzf, error);
865 return 0;
866 }
867 }
868 }
869}
870
871#[export_name = prefix!(BZ2_bzReadGetUnused)]
904pub unsafe extern "C" fn BZ2_bzReadGetUnused(
905 bzerror: *mut c_int,
906 b: *mut BZFILE,
907 unused: *mut *mut c_void,
908 nUnused: *mut c_int,
909) {
910 BZ2_bzReadGetUnusedHelp(
911 bzerror.as_mut(),
912 b.as_mut(),
913 unused.as_mut(),
914 nUnused.as_mut(),
915 )
916}
917
918unsafe fn BZ2_bzReadGetUnusedHelp(
919 mut bzerror: Option<&mut c_int>,
920 mut b: Option<&mut BZFILE>,
921 unused: Option<&mut *mut c_void>,
922 nUnused: Option<&mut c_int>,
923) {
924 let Some(bzf) = b.as_mut() else {
925 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
926 return;
927 };
928
929 if bzf.lastErr != ReturnCode::BZ_STREAM_END {
930 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
931 return;
932 }
933
934 let (Some(unused), Some(nUnused)) = (unused, nUnused) else {
935 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
936 return;
937 };
938
939 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
940
941 *nUnused = bzf.strm.avail_in as c_int;
942 *unused = bzf.strm.next_in as *mut c_void;
943}
944
945#[derive(Copy, Clone)]
946pub(crate) enum Operation {
947 Reading,
948 Writing,
949}
950
951enum OpenMode {
952 Pointer,
953 FileDescriptor(i32),
954}
955
956unsafe fn bzopen_or_bzdopen(path: Option<&CStr>, open_mode: OpenMode, mode: &CStr) -> *mut BZFILE {
957 let mut bzerr = 0;
958 let mut unused: [c_char; BZ_MAX_UNUSED as usize] = [0; BZ_MAX_UNUSED as usize];
959
960 let mut blockSize100k = 9;
961 let verbosity = 0;
962 let workFactor = 30;
963 let nUnused = 0;
964
965 let mut smallMode = false;
966 let mut operation = Operation::Reading;
967
968 for c in mode.to_bytes() {
969 match c {
970 b'r' => operation = Operation::Reading,
971 b'w' => operation = Operation::Writing,
972 b's' => smallMode = true,
973 b'0'..=b'9' => blockSize100k = (*c - b'0') as i32,
974 _ => {}
975 }
976 }
977
978 let mode = match open_mode {
979 OpenMode::Pointer => match operation {
980 Operation::Reading => b"rbe\0".as_slice(),
981 Operation::Writing => b"wbe\0".as_slice(),
982 },
983 OpenMode::FileDescriptor(_) => match operation {
984 Operation::Reading => b"rb\0".as_slice(),
985 Operation::Writing => b"wb\0".as_slice(),
986 },
987 };
988
989 let mode2 = mode.as_ptr().cast_mut().cast::<c_char>();
990
991 let default_file = match operation {
992 Operation::Reading => STDIN!(),
993 Operation::Writing => STDOUT!(),
994 };
995
996 let (fp, close_handle) = match open_mode {
997 OpenMode::Pointer => match path {
998 None => (default_file, false),
999 Some(path) if path.is_empty() => (default_file, false),
1000 Some(path) => (fopen(path.as_ptr(), mode2), true),
1001 },
1002 OpenMode::FileDescriptor(fd) => (fdopen(fd, mode2), true),
1003 };
1004
1005 if fp.is_null() {
1006 return ptr::null_mut();
1007 }
1008
1009 let bzfp = match operation {
1010 Operation::Reading => BZ2_bzReadOpen(
1011 &mut bzerr,
1012 fp,
1013 verbosity,
1014 smallMode as i32,
1015 unused.as_mut_ptr() as *mut c_void,
1016 nUnused,
1017 ),
1018 Operation::Writing => BZ2_bzWriteOpen(
1019 &mut bzerr,
1020 fp,
1021 blockSize100k.clamp(1, 9),
1022 verbosity,
1023 workFactor,
1024 ),
1025 };
1026
1027 if bzfp.is_null() {
1028 if close_handle {
1029 fclose(fp);
1030 }
1031 return ptr::null_mut();
1032 }
1033
1034 bzfp
1035}
1036
1037#[export_name = prefix!(BZ2_bzopen)]
1052pub unsafe extern "C" fn BZ2_bzopen(path: *const c_char, mode: *const c_char) -> *mut BZFILE {
1053 let mode = if mode.is_null() {
1054 return ptr::null_mut();
1055 } else {
1056 CStr::from_ptr(mode)
1057 };
1058
1059 let path = if path.is_null() {
1060 None
1061 } else {
1062 Some(CStr::from_ptr(path))
1063 };
1064
1065 bzopen_or_bzdopen(path, OpenMode::Pointer, mode)
1066}
1067
1068#[export_name = prefix!(BZ2_bzdopen)]
1081pub unsafe extern "C" fn BZ2_bzdopen(fd: c_int, mode: *const c_char) -> *mut BZFILE {
1082 let mode = if mode.is_null() {
1083 return ptr::null_mut();
1084 } else {
1085 CStr::from_ptr(mode)
1086 };
1087
1088 bzopen_or_bzdopen(None, OpenMode::FileDescriptor(fd), mode)
1089}
1090
1091#[export_name = prefix!(BZ2_bzread)]
1112pub unsafe extern "C" fn BZ2_bzread(b: *mut BZFILE, buf: *mut c_void, len: c_int) -> c_int {
1113 BZ2_bzreadHelp(b.as_mut(), buf, len)
1114}
1115
1116unsafe fn BZ2_bzreadHelp(mut b: Option<&mut BZFILE>, buf: *mut c_void, len: c_int) -> c_int {
1117 let mut bzerr = 0;
1118
1119 if let Some(b) = b.as_deref_mut() {
1120 if b.lastErr == ReturnCode::BZ_STREAM_END {
1121 return 0;
1122 }
1123 }
1124
1125 let nread = BZ2_bzReadHelp(Some(&mut bzerr), b, buf, len);
1126 if bzerr == 0 || bzerr == ReturnCode::BZ_STREAM_END as i32 {
1127 nread
1128 } else {
1129 -1
1130 }
1131}
1132
1133#[export_name = prefix!(BZ2_bzwrite)]
1154pub unsafe extern "C" fn BZ2_bzwrite(b: *mut BZFILE, buf: *const c_void, len: c_int) -> c_int {
1155 BZ2_bzwriteHelp(b.as_mut(), buf, len)
1156}
1157
1158unsafe fn BZ2_bzwriteHelp(b: Option<&mut BZFILE>, buf: *const c_void, len: c_int) -> c_int {
1159 let mut bzerr = 0;
1160 BZ2_bzWriteHelp(Some(&mut bzerr), b, buf, len);
1161
1162 match bzerr {
1163 0 => len,
1164 _ => -1,
1165 }
1166}
1167
1168#[export_name = prefix!(BZ2_bzflush)]
1180pub unsafe extern "C" fn BZ2_bzflush(mut _b: *mut BZFILE) -> c_int {
1181 0
1183}
1184
1185#[export_name = prefix!(BZ2_bzclose)]
1197pub unsafe extern "C" fn BZ2_bzclose(b: *mut BZFILE) {
1198 let mut bzerr: c_int = 0;
1199
1200 if b.is_null() {
1201 return;
1202 }
1203
1204 let operation = (*b).operation;
1205 let handle = (*b).handle;
1206
1207 match operation {
1208 Operation::Reading => {
1209 BZ2_bzReadClose(&raw mut bzerr, b);
1210 }
1211 Operation::Writing => {
1212 BZ2_bzWriteCloseHelp(Some(&mut bzerr), b, false as i32, None, None);
1213 if bzerr != 0 {
1214 BZ2_bzWriteCloseHelp(None, b, true as i32, None, None);
1215 }
1216 }
1217 }
1218
1219 if !handle.is_null() && handle != STDIN!() && handle != STDOUT!() {
1220 fclose(handle);
1221 }
1222}
1223
1224const BZERRORSTRINGS: [&str; 16] = [
1225 "OK\0",
1226 "SEQUENCE_ERROR\0",
1227 "PARAM_ERROR\0",
1228 "MEM_ERROR\0",
1229 "DATA_ERROR\0",
1230 "DATA_ERROR_MAGIC\0",
1231 "IO_ERROR\0",
1232 "UNEXPECTED_EOF\0",
1233 "OUTBUFF_FULL\0",
1234 "CONFIG_ERROR\0",
1235 "???\0",
1236 "???\0",
1237 "???\0",
1238 "???\0",
1239 "???\0",
1240 "???\0",
1241];
1242
1243#[export_name = prefix!(BZ2_bzerror)]
1260pub unsafe extern "C" fn BZ2_bzerror(b: *const BZFILE, errnum: *mut c_int) -> *const c_char {
1261 let errnum = errnum.as_mut();
1263 match b.as_ref() {
1264 Some(b) => BZ2_bzerrorHelp(b, errnum),
1265 None => {
1266 if let Some(errnum) = errnum {
1267 *errnum = crate::BZ_PARAM_ERROR;
1268 }
1269 let msg = "BZ2_bzerror was passed a NULL pointer\0";
1270 msg.as_ptr().cast::<c_char>()
1271 }
1272 }
1273}
1274
1275fn BZ2_bzerrorHelp(b: &BZFILE, errnum: Option<&mut c_int>) -> *const c_char {
1276 let err = Ord::min(0, b.lastErr as c_int);
1277 if let Some(errnum) = errnum {
1278 *errnum = err;
1279 };
1280 let msg = match BZERRORSTRINGS.get(-err as usize) {
1281 Some(msg) => msg,
1282 None => "???\0",
1283 };
1284 msg.as_ptr().cast::<c_char>()
1285}
1286
1287#[cfg(test)]
1288mod tests {
1289 use super::*;
1290
1291 #[test]
1292 fn bz_error_file_is_null_ptr() {
1293 let mut errnum = 0;
1294 let ptr = unsafe { BZ2_bzerror(core::ptr::null(), &mut errnum) };
1295 let cstr = unsafe { CStr::from_ptr(ptr) };
1296
1297 assert_eq!(
1298 cstr.to_str().unwrap(),
1299 "BZ2_bzerror was passed a NULL pointer"
1300 )
1301 }
1302
1303 #[test]
1304 fn bz_error_errnum_is_null_ptr() {
1305 let bz_file = BZFILE {
1306 handle: core::ptr::null_mut(),
1307 buf: [0; 5000],
1308 bufN: 0,
1309 strm: bz_stream::zeroed(),
1310 lastErr: ReturnCode::BZ_OK,
1311 operation: Operation::Reading,
1312 initialisedOk: false,
1313 };
1314
1315 unsafe { BZ2_bzerror(&bz_file, core::ptr::null_mut()) };
1316 }
1317
1318 #[test]
1319 fn error_messages() {
1320 let mut bz_file = BZFILE {
1321 handle: core::ptr::null_mut(),
1322 buf: [0; 5000],
1323 bufN: 0,
1324 strm: bz_stream::zeroed(),
1325 lastErr: ReturnCode::BZ_OK,
1326 operation: Operation::Reading,
1327 initialisedOk: false,
1328 };
1329
1330 let return_codes = [
1331 ReturnCode::BZ_OK,
1332 ReturnCode::BZ_RUN_OK,
1333 ReturnCode::BZ_FLUSH_OK,
1334 ReturnCode::BZ_FINISH_OK,
1335 ReturnCode::BZ_STREAM_END,
1336 ReturnCode::BZ_SEQUENCE_ERROR,
1337 ReturnCode::BZ_PARAM_ERROR,
1338 ReturnCode::BZ_MEM_ERROR,
1339 ReturnCode::BZ_DATA_ERROR,
1340 ReturnCode::BZ_DATA_ERROR_MAGIC,
1341 ReturnCode::BZ_IO_ERROR,
1342 ReturnCode::BZ_UNEXPECTED_EOF,
1343 ReturnCode::BZ_OUTBUFF_FULL,
1344 ReturnCode::BZ_CONFIG_ERROR,
1345 ];
1346
1347 for return_code in return_codes {
1348 bz_file.lastErr = return_code;
1349
1350 let mut errnum = 0;
1351 let ptr = unsafe { BZ2_bzerror(&bz_file, &mut errnum) };
1352 assert!(!ptr.is_null());
1353 let cstr = unsafe { CStr::from_ptr(ptr) };
1354
1355 let msg = cstr.to_str().unwrap();
1356
1357 let expected = match return_code {
1358 ReturnCode::BZ_OK => "OK",
1359 ReturnCode::BZ_RUN_OK => "OK",
1360 ReturnCode::BZ_FLUSH_OK => "OK",
1361 ReturnCode::BZ_FINISH_OK => "OK",
1362 ReturnCode::BZ_STREAM_END => "OK",
1363 ReturnCode::BZ_SEQUENCE_ERROR => "SEQUENCE_ERROR",
1364 ReturnCode::BZ_PARAM_ERROR => "PARAM_ERROR",
1365 ReturnCode::BZ_MEM_ERROR => "MEM_ERROR",
1366 ReturnCode::BZ_DATA_ERROR => "DATA_ERROR",
1367 ReturnCode::BZ_DATA_ERROR_MAGIC => "DATA_ERROR_MAGIC",
1368 ReturnCode::BZ_IO_ERROR => "IO_ERROR",
1369 ReturnCode::BZ_UNEXPECTED_EOF => "UNEXPECTED_EOF",
1370 ReturnCode::BZ_OUTBUFF_FULL => "OUTBUFF_FULL",
1371 ReturnCode::BZ_CONFIG_ERROR => "CONFIG_ERROR",
1372 };
1373
1374 assert_eq!(msg, expected);
1375
1376 if (return_code as i32) < 0 {
1377 assert_eq!(return_code as i32, errnum);
1378 } else {
1379 assert_eq!(0, errnum);
1380 }
1381 }
1382 }
1383
1384 #[test]
1385 fn bzclose_write() {
1386 let Some(allocator) = Allocator::DEFAULT else {
1387 return;
1388 };
1389
1390 {
1391 let bzf_ptr: *mut BZFILE = allocator.allocate_zeroed(1).unwrap();
1392 let mut bzerr: c_int = 0;
1393
1394 unsafe {
1395 (*bzf_ptr).operation = Operation::Writing;
1396 (*bzf_ptr).initialisedOk = false;
1397 (*bzf_ptr).handle = core::ptr::null_mut();
1398 BZ2_bzWriteClose64(
1399 &raw mut bzerr,
1400 bzf_ptr,
1401 1, ptr::null_mut(),
1403 ptr::null_mut(),
1404 ptr::null_mut(),
1405 ptr::null_mut(),
1406 );
1407 }
1408 }
1409
1410 {
1411 let bzf_ptr: *mut BZFILE = allocator.allocate_zeroed(1).unwrap();
1412
1413 unsafe {
1414 (*bzf_ptr).operation = Operation::Writing;
1415 (*bzf_ptr).initialisedOk = false;
1416 (*bzf_ptr).handle = core::ptr::null_mut();
1417 BZ2_bzclose(bzf_ptr);
1418 }
1419 }
1420 }
1421
1422 #[test]
1423 fn bzclose_read() {
1424 let Some(allocator) = Allocator::DEFAULT else {
1425 return;
1426 };
1427
1428 {
1429 let bzf_ptr: *mut BZFILE = allocator.allocate_zeroed(1).unwrap();
1430 let mut bzerr: c_int = 0;
1431
1432 unsafe {
1433 (*bzf_ptr).operation = Operation::Reading;
1434 (*bzf_ptr).initialisedOk = false;
1435 BZ2_bzReadClose(&raw mut bzerr, bzf_ptr);
1436 }
1437 }
1438
1439 {
1440 let bzf_ptr: *mut BZFILE = allocator.allocate_zeroed(1).unwrap();
1441
1442 unsafe {
1443 (*bzf_ptr).operation = Operation::Reading;
1444 (*bzf_ptr).initialisedOk = false;
1445 BZ2_bzclose(bzf_ptr);
1446 }
1447 }
1448 }
1449}