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.as_mut(),
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: Option<&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.as_mut(),
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 mut b: Option<&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 Some(bzf) = b else {
470 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
471 return;
472 };
473
474 if !matches!(bzf.operation, Operation::Writing) {
475 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
476 return;
477 }
478
479 if ferror(bzf.handle) != 0 {
480 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
481 return;
482 }
483
484 if let Some(nbytes_in_lo32) = nbytes_in_lo32.as_deref_mut() {
485 *nbytes_in_lo32 = 0
486 }
487 if let Some(nbytes_in_hi32) = nbytes_in_hi32.as_deref_mut() {
488 *nbytes_in_hi32 = 0;
489 }
490 if let Some(nbytes_out_lo32) = nbytes_out_lo32.as_deref_mut() {
491 *nbytes_out_lo32 = 0;
492 }
493 if let Some(nbytes_out_hi32) = nbytes_out_hi32.as_deref_mut() {
494 *nbytes_out_hi32 = 0;
495 }
496
497 if abandon == 0 && bzf.lastErr == ReturnCode::BZ_OK {
498 loop {
499 bzf.strm.avail_out = BZ_MAX_UNUSED_U32;
500 bzf.strm.next_out = (bzf.buf).as_mut_ptr().cast::<c_char>();
501 match BZ2_bzCompressHelp(BzStream::from_mut(&mut bzf.strm), 2 as c_int) {
502 ret @ (ReturnCode::BZ_FINISH_OK | ReturnCode::BZ_STREAM_END) => {
503 if bzf.strm.avail_out < BZ_MAX_UNUSED_U32 {
504 let n1 = (BZ_MAX_UNUSED_U32 - bzf.strm.avail_out) as usize;
505 let n2 = fwrite(
506 bzf.buf.as_mut_ptr().cast::<c_void>(),
507 mem::size_of::<u8>(),
508 n1,
509 bzf.handle,
510 );
511 if n1 != n2 || ferror(bzf.handle) != 0 {
512 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
513 }
514 }
515
516 if let ReturnCode::BZ_STREAM_END = ret {
517 break;
518 }
519 }
520 ret => {
521 BZ_SETERR!(bzerror, bzf, ret);
522 return;
523 }
524 }
525 }
526 }
527
528 if abandon == 0 && ferror(bzf.handle) == 0 {
529 fflush(bzf.handle);
530 if ferror(bzf.handle) != 0 {
531 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
532 return;
533 }
534 }
535
536 if let Some(nbytes_in_lo32) = nbytes_in_lo32 {
537 *nbytes_in_lo32 = bzf.strm.total_in_lo32;
538 }
539 if let Some(nbytes_in_hi32) = nbytes_in_hi32 {
540 *nbytes_in_hi32 = bzf.strm.total_in_hi32;
541 }
542 if let Some(nbytes_out_lo32) = nbytes_out_lo32 {
543 *nbytes_out_lo32 = bzf.strm.total_out_lo32;
544 }
545 if let Some(nbytes_out_hi32) = nbytes_out_hi32 {
546 *nbytes_out_hi32 = bzf.strm.total_out_hi32;
547 }
548
549 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
550
551 BZ2_bzCompressEnd(&mut bzf.strm);
552
553 let Some(allocator) = Allocator::DEFAULT else {
554 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
555 return;
556 };
557
558 allocator.deallocate(bzf, 1);
559}
560
561#[export_name = prefix!(BZ2_bzReadOpen)]
605pub unsafe extern "C" fn BZ2_bzReadOpen(
606 bzerror: *mut c_int,
607 f: *mut FILE,
608 verbosity: c_int,
609 small: c_int,
610 unused: *mut c_void,
611 nUnused: c_int,
612) -> *mut BZFILE {
613 BZ2_bzReadOpenHelp(bzerror.as_mut(), f, verbosity, small, unused, nUnused)
614}
615
616unsafe fn BZ2_bzReadOpenHelp(
617 mut bzerror: Option<&mut c_int>,
618 f: *mut FILE,
619 verbosity: c_int,
620 small: c_int,
621 unused: *mut c_void,
622 nUnused: c_int,
623) -> *mut BZFILE {
624 let mut bzf: Option<&mut BZFILE> = None;
625
626 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_OK);
627
628 if f.is_null()
629 || !(0..=1).contains(&small)
630 || !(0..=4).contains(&verbosity)
631 || (unused.is_null() && nUnused != 0)
632 || (!unused.is_null() && !(0..=BZ_MAX_UNUSED_U32 as c_int).contains(&nUnused))
633 {
634 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
635 return ptr::null_mut::<BZFILE>();
636 }
637
638 if ferror(f) != 0 {
639 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
640 return ptr::null_mut::<BZFILE>();
641 }
642
643 let Some(allocator) = Allocator::DEFAULT else {
644 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
645 return ptr::null_mut();
646 };
647
648 let Some(bzf) = allocator.allocate_zeroed::<BZFILE>(1) else {
649 BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_MEM_ERROR);
650 return ptr::null_mut();
651 };
652
653 let bzf = unsafe { &mut *bzf };
655
656 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
657
658 bzf.initialisedOk = false;
659 bzf.handle = f;
660 bzf.bufN = 0;
661 bzf.operation = Operation::Reading;
662 bzf.strm.bzalloc = None;
663 bzf.strm.bzfree = None;
664 bzf.strm.opaque = ptr::null_mut();
665
666 if nUnused > 0 {
667 ptr::copy(
668 unused as *mut i8,
669 bzf.buf[bzf.bufN as usize..].as_mut_ptr(),
670 nUnused as usize,
671 );
672 bzf.bufN += nUnused;
673 }
674
675 match BZ2_bzDecompressInitHelp(BzStream::from_mut(&mut bzf.strm), verbosity, small) {
676 ReturnCode::BZ_OK => {
677 bzf.strm.avail_in = bzf.bufN as c_uint;
678 bzf.strm.next_in = bzf.buf.as_mut_ptr().cast::<c_char>();
679 bzf.initialisedOk = true;
680 }
681 ret => {
682 BZ_SETERR!(bzerror, bzf, ret);
683
684 allocator.deallocate(bzf, 1);
685
686 return ptr::null_mut();
687 }
688 }
689
690 bzf as *mut BZFILE
691}
692
693#[export_name = prefix!(BZ2_bzReadClose)]
718pub unsafe extern "C" fn BZ2_bzReadClose(bzerror: *mut c_int, b: *mut BZFILE) {
719 BZ2_bzReadCloseHelp(bzerror.as_mut(), b.as_mut())
720}
721
722unsafe fn BZ2_bzReadCloseHelp(mut bzerror: Option<&mut c_int>, mut b: Option<&mut BZFILE>) {
723 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
724
725 let Some(bzf) = b else {
726 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
727 return;
728 };
729
730 if !matches!(bzf.operation, Operation::Reading) {
731 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
732 return;
733 }
734
735 if bzf.initialisedOk {
736 BZ2_bzDecompressEnd(&mut bzf.strm);
737 }
738
739 let Some(allocator) = Allocator::DEFAULT else {
740 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
741 return;
742 };
743
744 allocator.deallocate(bzf, 1)
745}
746
747#[export_name = prefix!(BZ2_bzRead)]
782pub unsafe extern "C" fn BZ2_bzRead(
783 bzerror: *mut c_int,
784 b: *mut BZFILE,
785 buf: *mut c_void,
786 len: c_int,
787) -> c_int {
788 BZ2_bzReadHelp(bzerror.as_mut(), b.as_mut(), buf, len)
789}
790
791unsafe fn BZ2_bzReadHelp(
792 mut bzerror: Option<&mut c_int>,
793 mut b: Option<&mut BZFILE>,
794 buf: *mut c_void,
795 len: c_int,
796) -> c_int {
797 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
798
799 let Some(bzf) = b.as_mut() else {
800 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
801 return 0;
802 };
803
804 if buf.is_null() || len < 0 {
805 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
806 return 0;
807 }
808
809 if !matches!(bzf.operation, Operation::Reading) {
810 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
811 return 0;
812 }
813
814 if len == 0 as c_int {
815 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
816 return 0;
817 }
818
819 bzf.strm.avail_out = len as c_uint;
820 bzf.strm.next_out = buf as *mut c_char;
821 loop {
822 if ferror(bzf.handle) != 0 {
823 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
824 return 0;
825 }
826
827 if bzf.strm.avail_in == 0 && !myfeof(bzf.handle) {
828 let n = fread(
829 (bzf.buf).as_mut_ptr() as *mut c_void,
830 ::core::mem::size_of::<u8>(),
831 5000,
832 bzf.handle,
833 ) as i32;
834
835 if ferror(bzf.handle) != 0 {
836 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
837 return 0;
838 }
839
840 bzf.bufN = n;
841 bzf.strm.avail_in = bzf.bufN as c_uint;
842 bzf.strm.next_in = (bzf.buf).as_mut_ptr().cast::<c_char>();
843 }
844
845 match BZ2_bzDecompressHelp(unsafe { BzStream::from_mut(&mut bzf.strm) }) {
846 ReturnCode::BZ_OK => {
847 if myfeof(bzf.handle) && bzf.strm.avail_in == 0 && bzf.strm.avail_out > 0 {
848 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_UNEXPECTED_EOF);
849 return 0;
850 } else if bzf.strm.avail_out == 0 {
851 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
852 return len;
853 } else {
854 continue;
855 }
856 }
857 ReturnCode::BZ_STREAM_END => {
858 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_STREAM_END);
859 return (len as c_uint - bzf.strm.avail_out) as c_int;
860 }
861 error => {
862 BZ_SETERR!(bzerror, bzf, error);
863 return 0;
864 }
865 }
866 }
867}
868
869#[export_name = prefix!(BZ2_bzReadGetUnused)]
902pub unsafe extern "C" fn BZ2_bzReadGetUnused(
903 bzerror: *mut c_int,
904 b: *mut BZFILE,
905 unused: *mut *mut c_void,
906 nUnused: *mut c_int,
907) {
908 BZ2_bzReadGetUnusedHelp(
909 bzerror.as_mut(),
910 b.as_mut(),
911 unused.as_mut(),
912 nUnused.as_mut(),
913 )
914}
915
916unsafe fn BZ2_bzReadGetUnusedHelp(
917 mut bzerror: Option<&mut c_int>,
918 mut b: Option<&mut BZFILE>,
919 unused: Option<&mut *mut c_void>,
920 nUnused: Option<&mut c_int>,
921) {
922 let Some(bzf) = b.as_mut() else {
923 BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
924 return;
925 };
926
927 if bzf.lastErr != ReturnCode::BZ_STREAM_END {
928 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
929 return;
930 }
931
932 let (Some(unused), Some(nUnused)) = (unused, nUnused) else {
933 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
934 return;
935 };
936
937 BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
938
939 *nUnused = bzf.strm.avail_in as c_int;
940 *unused = bzf.strm.next_in as *mut c_void;
941}
942
943#[derive(Copy, Clone)]
944pub(crate) enum Operation {
945 Reading,
946 Writing,
947}
948
949enum OpenMode {
950 Pointer,
951 FileDescriptor(i32),
952}
953
954unsafe fn bzopen_or_bzdopen(path: Option<&CStr>, open_mode: OpenMode, mode: &CStr) -> *mut BZFILE {
955 let mut bzerr = 0;
956 let mut unused: [c_char; BZ_MAX_UNUSED as usize] = [0; BZ_MAX_UNUSED as usize];
957
958 let mut blockSize100k = 9;
959 let verbosity = 0;
960 let workFactor = 30;
961 let nUnused = 0;
962
963 let mut smallMode = false;
964 let mut operation = Operation::Reading;
965
966 for c in mode.to_bytes() {
967 match c {
968 b'r' => operation = Operation::Reading,
969 b'w' => operation = Operation::Writing,
970 b's' => smallMode = true,
971 b'0'..=b'9' => blockSize100k = (*c - b'0') as i32,
972 _ => {}
973 }
974 }
975
976 let mode = match open_mode {
977 OpenMode::Pointer => match operation {
978 Operation::Reading => b"rbe\0".as_slice(),
979 Operation::Writing => b"rbe\0".as_slice(),
980 },
981 OpenMode::FileDescriptor(_) => match operation {
982 Operation::Reading => b"rb\0".as_slice(),
983 Operation::Writing => b"rb\0".as_slice(),
984 },
985 };
986
987 let mode2 = mode.as_ptr().cast_mut().cast::<c_char>();
988
989 let default_file = match operation {
990 Operation::Reading => STDIN!(),
991 Operation::Writing => STDOUT!(),
992 };
993
994 let fp = match open_mode {
995 OpenMode::Pointer => match path {
996 None => default_file,
997 Some(path) if path.is_empty() => default_file,
998 Some(path) => fopen(path.as_ptr(), mode2),
999 },
1000 OpenMode::FileDescriptor(fd) => fdopen(fd, mode2),
1001 };
1002
1003 if fp.is_null() {
1004 return ptr::null_mut();
1005 }
1006
1007 let bzfp = match operation {
1008 Operation::Reading => BZ2_bzReadOpen(
1009 &mut bzerr,
1010 fp,
1011 verbosity,
1012 smallMode as i32,
1013 unused.as_mut_ptr() as *mut c_void,
1014 nUnused,
1015 ),
1016 Operation::Writing => BZ2_bzWriteOpen(
1017 &mut bzerr,
1018 fp,
1019 blockSize100k.clamp(1, 9),
1020 verbosity,
1021 workFactor,
1022 ),
1023 };
1024
1025 if bzfp.is_null() {
1026 if fp != STDIN!() && fp != STDOUT!() {
1027 fclose(fp);
1028 }
1029 return ptr::null_mut();
1030 }
1031
1032 bzfp
1033}
1034
1035#[export_name = prefix!(BZ2_bzopen)]
1050pub unsafe extern "C" fn BZ2_bzopen(path: *const c_char, mode: *const c_char) -> *mut BZFILE {
1051 let mode = if mode.is_null() {
1052 return ptr::null_mut();
1053 } else {
1054 CStr::from_ptr(mode)
1055 };
1056
1057 let path = if path.is_null() {
1058 None
1059 } else {
1060 Some(CStr::from_ptr(path))
1061 };
1062
1063 bzopen_or_bzdopen(path, OpenMode::Pointer, mode)
1064}
1065
1066#[export_name = prefix!(BZ2_bzdopen)]
1079pub unsafe extern "C" fn BZ2_bzdopen(fd: c_int, mode: *const c_char) -> *mut BZFILE {
1080 let mode = if mode.is_null() {
1081 return ptr::null_mut();
1082 } else {
1083 CStr::from_ptr(mode)
1084 };
1085
1086 bzopen_or_bzdopen(None, OpenMode::FileDescriptor(fd), mode)
1087}
1088
1089#[export_name = prefix!(BZ2_bzread)]
1110pub unsafe extern "C" fn BZ2_bzread(b: *mut BZFILE, buf: *mut c_void, len: c_int) -> c_int {
1111 BZ2_bzreadHelp(b.as_mut(), buf, len)
1112}
1113
1114unsafe fn BZ2_bzreadHelp(mut b: Option<&mut BZFILE>, buf: *mut c_void, len: c_int) -> c_int {
1115 let mut bzerr = 0;
1116
1117 if let Some(b) = b.as_deref_mut() {
1118 if b.lastErr == ReturnCode::BZ_STREAM_END {
1119 return 0;
1120 }
1121 }
1122
1123 let nread = BZ2_bzReadHelp(Some(&mut bzerr), b, buf, len);
1124 if bzerr == 0 || bzerr == ReturnCode::BZ_STREAM_END as i32 {
1125 nread
1126 } else {
1127 -1
1128 }
1129}
1130
1131#[export_name = prefix!(BZ2_bzwrite)]
1152pub unsafe extern "C" fn BZ2_bzwrite(b: *mut BZFILE, buf: *const c_void, len: c_int) -> c_int {
1153 BZ2_bzwriteHelp(b.as_mut(), buf, len)
1154}
1155
1156unsafe fn BZ2_bzwriteHelp(b: Option<&mut BZFILE>, buf: *const c_void, len: c_int) -> c_int {
1157 let mut bzerr = 0;
1158 BZ2_bzWriteHelp(Some(&mut bzerr), b, buf, len);
1159
1160 match bzerr {
1161 0 => len,
1162 _ => -1,
1163 }
1164}
1165
1166#[export_name = prefix!(BZ2_bzflush)]
1178pub unsafe extern "C" fn BZ2_bzflush(mut _b: *mut BZFILE) -> c_int {
1179 0
1181}
1182
1183#[export_name = prefix!(BZ2_bzclose)]
1195pub unsafe extern "C" fn BZ2_bzclose(b: *mut BZFILE) {
1196 BZ2_bzcloseHelp(b.as_mut())
1197}
1198
1199unsafe fn BZ2_bzcloseHelp(mut b: Option<&mut BZFILE>) {
1200 let mut bzerr: c_int = 0;
1201
1202 let operation = if let Some(bzf) = &mut b {
1203 bzf.operation
1204 } else {
1205 return;
1206 };
1207
1208 match operation {
1209 Operation::Reading => {
1210 BZ2_bzReadCloseHelp(Some(&mut bzerr), b.as_deref_mut());
1211 }
1212 Operation::Writing => {
1213 BZ2_bzWriteCloseHelp(Some(&mut bzerr), b.as_deref_mut(), false as i32, None, None);
1214 if bzerr != 0 {
1215 BZ2_bzWriteCloseHelp(None, b.as_deref_mut(), true as i32, None, None);
1216 }
1217 }
1218 }
1219
1220 if let Some(bzf) = b {
1221 if bzf.handle != STDIN!() && bzf.handle != STDOUT!() {
1222 fclose(bzf.handle);
1223 }
1224 }
1225}
1226
1227const BZERRORSTRINGS: [&str; 16] = [
1228 "OK\0",
1229 "SEQUENCE_ERROR\0",
1230 "PARAM_ERROR\0",
1231 "MEM_ERROR\0",
1232 "DATA_ERROR\0",
1233 "DATA_ERROR_MAGIC\0",
1234 "IO_ERROR\0",
1235 "UNEXPECTED_EOF\0",
1236 "OUTBUFF_FULL\0",
1237 "CONFIG_ERROR\0",
1238 "???\0",
1239 "???\0",
1240 "???\0",
1241 "???\0",
1242 "???\0",
1243 "???\0",
1244];
1245
1246#[export_name = prefix!(BZ2_bzerror)]
1263pub unsafe extern "C" fn BZ2_bzerror(b: *const BZFILE, errnum: *mut c_int) -> *const c_char {
1264 BZ2_bzerrorHelp(
1265 b.as_ref().expect("Passed null pointer to BZ2_bzerror"),
1266 errnum.as_mut(),
1267 )
1268}
1269
1270fn BZ2_bzerrorHelp(b: &BZFILE, errnum: Option<&mut c_int>) -> *const c_char {
1271 let err = Ord::min(0, b.lastErr as c_int);
1272 if let Some(errnum) = errnum {
1273 *errnum = err;
1274 };
1275 let msg = match BZERRORSTRINGS.get(-err as usize) {
1276 Some(msg) => msg,
1277 None => "???\0",
1278 };
1279 msg.as_ptr().cast::<c_char>()
1280}
1281
1282#[cfg(test)]
1283mod tests {
1284 use super::*;
1285
1286 #[test]
1287 fn error_messages() {
1288 let mut bz_file = BZFILE {
1289 handle: core::ptr::null_mut(),
1290 buf: [0; 5000],
1291 bufN: 0,
1292 strm: bz_stream::zeroed(),
1293 lastErr: ReturnCode::BZ_OK,
1294 operation: Operation::Reading,
1295 initialisedOk: false,
1296 };
1297
1298 let return_codes = [
1299 ReturnCode::BZ_OK,
1300 ReturnCode::BZ_RUN_OK,
1301 ReturnCode::BZ_FLUSH_OK,
1302 ReturnCode::BZ_FINISH_OK,
1303 ReturnCode::BZ_STREAM_END,
1304 ReturnCode::BZ_SEQUENCE_ERROR,
1305 ReturnCode::BZ_PARAM_ERROR,
1306 ReturnCode::BZ_MEM_ERROR,
1307 ReturnCode::BZ_DATA_ERROR,
1308 ReturnCode::BZ_DATA_ERROR_MAGIC,
1309 ReturnCode::BZ_IO_ERROR,
1310 ReturnCode::BZ_UNEXPECTED_EOF,
1311 ReturnCode::BZ_OUTBUFF_FULL,
1312 ReturnCode::BZ_CONFIG_ERROR,
1313 ];
1314
1315 for return_code in return_codes {
1316 bz_file.lastErr = return_code;
1317
1318 let mut errnum = 0;
1319 let ptr = unsafe { BZ2_bzerror(&bz_file as *const BZFILE, &mut errnum) };
1320 assert!(!ptr.is_null());
1321 let cstr = unsafe { CStr::from_ptr(ptr) };
1322
1323 let msg = cstr.to_str().unwrap();
1324
1325 let expected = match return_code {
1326 ReturnCode::BZ_OK => "OK",
1327 ReturnCode::BZ_RUN_OK => "OK",
1328 ReturnCode::BZ_FLUSH_OK => "OK",
1329 ReturnCode::BZ_FINISH_OK => "OK",
1330 ReturnCode::BZ_STREAM_END => "OK",
1331 ReturnCode::BZ_SEQUENCE_ERROR => "SEQUENCE_ERROR",
1332 ReturnCode::BZ_PARAM_ERROR => "PARAM_ERROR",
1333 ReturnCode::BZ_MEM_ERROR => "MEM_ERROR",
1334 ReturnCode::BZ_DATA_ERROR => "DATA_ERROR",
1335 ReturnCode::BZ_DATA_ERROR_MAGIC => "DATA_ERROR_MAGIC",
1336 ReturnCode::BZ_IO_ERROR => "IO_ERROR",
1337 ReturnCode::BZ_UNEXPECTED_EOF => "UNEXPECTED_EOF",
1338 ReturnCode::BZ_OUTBUFF_FULL => "OUTBUFF_FULL",
1339 ReturnCode::BZ_CONFIG_ERROR => "CONFIG_ERROR",
1340 };
1341
1342 assert_eq!(msg, expected);
1343
1344 if (return_code as i32) < 0 {
1345 assert_eq!(return_code as i32, errnum);
1346 } else {
1347 assert_eq!(0, errnum);
1348 }
1349 }
1350 }
1351}