xar unarchiver (.xar, .pkg, .xip)
1require "./memfile"
2
3@[Link("bzip2")]
4lib LibBzip2
5 alias BZFILE = Void
6
7 fun BZ2_bzReadOpen(bzerror : Pointer(Int32), f : Pointer(Void), verbosity : Int32, small : Int32, unused : Pointer(Void), nUnused : Int32) : Pointer(BZFILE)
8 fun BZ2_bzRead(bzerror : Pointer(Int32), b : Pointer(BZFILE), buf : Pointer(UInt8), len : Int32) : Int32
9 fun BZ2_bzReadClose(bzerror : Pointer(Int32), b : Pointer(BZFILE)) : Void
10end
11
12class Bzip2::Reader
13 @io : IO
14 @bzfile : Pointer(LibBzip2::BZFILE)
15 @bzerror : Pointer(Int32)
16 @file_ptr : Pointer(Void) # MemFile pointer
17
18 def initialize(io : IO)
19 @io = io
20
21 # Create a memory-backed FILE* pointer from IO
22 @file_ptr = MemFile.from_io(io)
23 raise "Could not map memfile" unless @file_ptr
24
25 @bzerror = Pointer(Int32).malloc(1)
26 @bzfile = LibBzip2.BZ2_bzReadOpen(@bzerror, @file_ptr, 0, 0, Pointer(Void).null, 0)
27 raise "Failed to open Bzip2 stream, error=#{@bzerror.value}" unless @bzfile
28 end
29
30 def getb_to_end : Bytes
31 result = Bytes.new(0)
32 buffer = Bytes.new(4096)
33
34 while true
35 bytes_read = LibBzip2.BZ2_bzRead(@bzerror, @bzfile, buffer.to_unsafe, buffer.size)
36 case @bzerror.value
37 when 0 # BZ_OK
38 result += buffer[0, bytes_read]
39 when 4 # BZ_STREAM_END
40 result += buffer[0, bytes_read] if bytes_read > 0
41 break
42 else
43 raise "BZ2_bzRead failed with error #{@bzerror.value}"
44 end
45 end
46
47 result
48 end
49
50 def finalize
51 LibBzip2.BZ2_bzReadClose(@bzerror, @bzfile)
52 end
53end