1 /* Bzip2 encoding (ENCODING_BZIP2) backend */
13 #include <bzlib.h> /* Everything needs this after stdio.h */
19 #include "encoding/bzip2.h"
20 #include "encoding/encoding.h"
21 #include "util/memory.h"
23 /* How many bytes of compressed data to read before decompressing.
24 * This is currently defined as BZ_MAX_UNUSED to make the behaviour
25 * similar to BZ2_bzRead; but other values would work too. */
26 #define ELINKS_BZ_BUFFER_LENGTH BZ_MAX_UNUSED
31 /* The file descriptor from which we read. */
34 /* Initially 0; set to 1 when BZ2_bzDecompress indicates
35 * BZ_STREAM_END, which means it has found the bzip2-specific
36 * end-of-stream marker and all data has been decompressed.
37 * Then we neither read from the file nor call BZ2_bzDecompress
42 /* A buffer for data that has been read from the file but not
43 * yet decompressed. fbz_stream.next_in and fbz_stream.avail_in
44 * refer to this buffer. */
45 unsigned char buf
[ELINKS_BZ_BUFFER_LENGTH
];
49 bzip2_open(struct stream_encoded
*stream
, int fd
)
51 /* A zero-initialized bz_stream. The compiler ensures that all
52 * pointer members in it are null. (Can't do this with memset
53 * because C99 does not require all-bits-zero to be a null
55 static const bz_stream null_bz_stream
= {0};
57 struct bz2_enc_data
*data
= mem_alloc(sizeof(*data
));
65 /* Initialize all members of *data, except data->buf[], which
66 * will be initialized on demand by bzip2_read. */
67 copy_struct(&data
->fbz_stream
, &null_bz_stream
);
71 err
= BZ2_bzDecompressInit(&data
->fbz_stream
, 0, 0);
83 bzip2_read(struct stream_encoded
*stream
, unsigned char *buf
, int len
)
85 struct bz2_enc_data
*data
= (struct bz2_enc_data
*) stream
->data
;
92 if (data
->last_read
) return 0;
94 data
->fbz_stream
.avail_out
= len
;
95 data
->fbz_stream
.next_out
= buf
;
98 if (data
->fbz_stream
.avail_in
== 0) {
99 int l
= safe_read(data
->fdread
, data
->buf
,
100 ELINKS_BZ_BUFFER_LENGTH
);
106 return -1; /* I/O error */
108 /* EOF. It is error: we wait for more bytes */
112 data
->fbz_stream
.next_in
= data
->buf
;
113 data
->fbz_stream
.avail_in
= l
;
116 err
= BZ2_bzDecompress(&data
->fbz_stream
);
117 if (err
== BZ_STREAM_END
) {
120 } else if (err
!= BZ_OK
) {
123 } while (data
->fbz_stream
.avail_out
> 0);
125 assert(len
- data
->fbz_stream
.avail_out
== data
->fbz_stream
.next_out
- (char *) buf
);
126 return len
- data
->fbz_stream
.avail_out
;
130 #define BZIP2_SMALL 1
132 #define BZIP2_SMALL 0
135 static unsigned char *
136 bzip2_decode_buffer(struct stream_encoded
*st
, unsigned char *data
, int len
, int *new_len
)
138 struct bz2_enc_data
*enc_data
= (struct bz2_enc_data
*)st
->data
;
139 bz_stream
*stream
= &enc_data
->fbz_stream
;
140 unsigned char *buffer
= NULL
;
143 *new_len
= 0; /* default, left there if an error occurs */
145 stream
->next_in
= data
;
146 stream
->avail_in
= len
;
147 stream
->total_out_lo32
= 0;
148 stream
->total_out_hi32
= 0;
151 unsigned char *new_buffer
;
152 size_t size
= stream
->total_out_lo32
+ MAX_STR_LEN
;
154 /* FIXME: support for 64 bit. real size is
156 * (total_in_hi32 << * 32) + total_in_lo32
159 assertm(!stream
->total_out_hi32
, "64 bzip2 decoding not supported");
161 new_buffer
= mem_realloc(buffer
, size
);
163 error
= BZ_MEM_ERROR
;
168 stream
->next_out
= buffer
+ stream
->total_out_lo32
;
169 stream
->avail_out
= MAX_STR_LEN
;
171 error
= BZ2_bzDecompress(stream
);
172 if (error
== BZ_STREAM_END
) {
176 /* Apparently BZ_STREAM_END is not forced when the end of input
177 * is reached. At least lindi- reported that it caused a
178 * reproducable infinite loop. Maybe it has to do with decoding
179 * an incomplete file. */
180 } while (error
== BZ_OK
&& stream
->avail_in
> 0);
182 if (error
== BZ_STREAM_END
) {
183 BZ2_bzDecompressEnd(stream
);
184 enc_data
->after_end
= 1;
188 if (error
== BZ_OK
) {
189 *new_len
= stream
->total_out_lo32
;
192 if (buffer
) mem_free(buffer
);
198 bzip2_close(struct stream_encoded
*stream
)
200 struct bz2_enc_data
*data
= (struct bz2_enc_data
*) stream
->data
;
203 if (!data
->after_end
) {
204 BZ2_bzDecompressEnd(&data
->fbz_stream
);
206 if (data
->fdread
!= -1) {
214 static const unsigned char *const bzip2_extensions
[] = { ".bz2", ".tbz", NULL
};
216 const struct decoding_backend bzip2_decoding_backend
= {