blob: 4d0cb15f0a553ec6503a6da7550a7bd546eda100 [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2015 Filip Pizlo. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tsf_internal.h"
#include "tsf_format.h"
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
#ifdef HAVE_BZIP2
#include <bzlib.h>
#endif
#define Cz(reader) ((z_stream*)((reader)->stream))
#define Cbz(reader) ((bz_stream*)((reader)->stream))
tsf_zip_rdr_t *tsf_zip_reader_create(tsf_partial_reader_t reader,
void *reader_arg,
tsf_zip_mode_t mode,
const tsf_zip_rdr_attr_t *attr) {
int res;
tsf_zip_rdr_t *result=malloc(sizeof(tsf_zip_rdr_t));
if (result==NULL) {
tsf_set_errno("Could not malloc tsf_zip_rdr_t");
return NULL;
}
result->decomp_eof=tsf_false;
result->reader=reader;
result->reader_arg=reader_arg;
result->mode=mode;
switch (mode) {
case TSF_ZIP_ZLIB:
result->buf_size=attr->z.buf_size;
break;
case TSF_ZIP_BZIP2:
result->buf_size=attr->b.buf_size;
break;
case TSF_ZIP_NONE:
tsf_set_error(TSF_E_INVALID_ARG,
"TSF_ZIP_NONE is not a valid mode for "
"tsf_zip_reader_create()");
goto failure_1;
default:
tsf_set_error(TSF_E_INVALID_ARG,
"%d is not a valid mode number for "
"tsf_zip_reader_create()",mode);
goto failure_1;
}
result->buf=malloc(result->buf_size);
if (result->buf==NULL) {
tsf_set_errno("Could not malloc buffer");
goto failure_1;
}
result->abstract.next_in=result->buf;
result->abstract.avail_in=0;
switch (mode) {
case TSF_ZIP_ZLIB:
#ifdef HAVE_ZLIB
result->stream=malloc(sizeof(z_stream));
if (result->stream==NULL) {
tsf_set_errno("Could not malloc z_stream");
goto failure_2;
}
Cz(result)->zalloc=NULL;
Cz(result)->zfree=NULL;
Cz(result)->opaque=NULL;
if (attr->z.advanced_init) {
res=inflateInit2(Cz(result),
attr->z.windowBits);
} else {
res=inflateInit(Cz(result));
}
if (res!=Z_OK) {
tsf_set_zlib_error(res,Cz(result)->msg,
"Trying to initialize inflation in ZLib");
free(Cz(result));
goto failure_2;
}
break;
#else
tsf_set_error(TSF_E_NOT_SUPPORTED,
"TSF_ZIP_ZLIB is not supported by "
"tsf_zip_reader_create() because ZLib support "
"was not configured into the TSF library");
goto failure_2;
#endif
case TSF_ZIP_BZIP2:
#ifdef HAVE_BZIP2
result->stream=malloc(sizeof(bz_stream));
if (result->stream==NULL) {
tsf_set_errno("Could not malloc bz_stream");
goto failure_2;
}
Cbz(result)->bzalloc=NULL;
Cbz(result)->bzfree=NULL;
Cbz(result)->opaque=NULL;
res=BZ2_bzDecompressInit(Cbz(result),
attr->b.verbosity,
attr->b.small);
if (res!=BZ_OK) {
tsf_set_libbzip2_error(res,
"Trying to initialize decompression in libbzip2");
free(Cbz(result));
goto failure_2;
}
break;
#else
tsf_set_error(TSF_E_NOT_SUPPORTED,
"TSF_ZIP_BZIP2 is not supported by "
"tsf_zip_reader_create() because libbzip2 "
"support was not configured into the TSF "
"library");
goto failure_2;
#endif
default:
tsf_abort("should've already caught this case");
break;
}
return result;
failure_2:
free(result->buf);
failure_1:
free(result);
return NULL;
}
void tsf_zip_reader_destroy(tsf_zip_rdr_t *reader) {
switch (reader->mode) {
#ifdef HAVE_ZLIB
case TSF_ZIP_ZLIB:
inflateEnd(Cz(reader));
break;
#endif
#ifdef HAVE_BZIP2
case TSF_ZIP_BZIP2:
BZ2_bzDecompressEnd(Cbz(reader));
break;
#endif
default:
tsf_abort("unknown zip reader mode");
break;
}
free(reader->stream);
free(reader->buf);
free(reader);
}
tsf_bool_t tsf_zip_reader_read(void *_reader,
void *data,
uint32_t len) {
tsf_zip_rdr_t *reader = _reader;
int32_t reader_res;
tsf_zip_result_t zip_res;
if (len == 0) {
return tsf_true;
}
if (reader->decomp_eof) {
reader_res = reader->reader(reader->reader_arg,
reader->buf, 1);
tsf_assert(reader_res == -1 || reader_res == 1);
if (reader_res == 1) {
tsf_set_error(TSF_E_NO_EOF,
"Expected EOF (because the "
"decompressor reached its end "
"of stream), but did not get "
"one (I was still able to read a byte)");
}
return tsf_false;
}
reader->abstract.next_out = data;
reader->abstract.avail_out = len;
for (;;) {
/* is there ever a case where we would want to call inflate even though
the input buffer is empty? */
if (reader->abstract.avail_in != 0) {
zip_res = tsf_zip_abstract_inflate(reader->mode,
reader->stream,
&(reader->abstract),
TSF_ZIP_ACT_RUN);
if (zip_res == TSF_ZIP_RES_ERROR) {
return tsf_false;
} else if (zip_res == TSF_ZIP_RES_END) {
if (reader->abstract.avail_in != 0) {
tsf_set_error(TSF_E_NO_EOF,
"Expected EOF (because the "
"decompressor reached its end "
"of stream), but did not get "
"one (there was still data left "
"in the buffer)");
return tsf_false;
}
reader->decomp_eof = tsf_true;
if (reader->abstract.avail_out == 0) {
break;
}
tsf_set_error((void*)reader->abstract.next_out == data ?
TSF_E_UNEXPECTED_EOF : TSF_E_ERRONEOUS_EOF,
NULL);
return tsf_false;
}
if (reader->abstract.avail_out == 0) {
break;
}
tsf_assert(reader->abstract.avail_in == 0);
}
reader_res = reader->reader(reader->reader_arg,
reader->buf,
reader->buf_size);
if (reader_res == -1) {
if (tsf_get_error_code() == TSF_E_UNEXPECTED_EOF) {
tsf_set_error(TSF_E_ERRONEOUS_EOF,
"Got an EOF from the reader before the "
"decompressor had reached end of stream");
}
return tsf_false;
}
reader->abstract.next_in = reader->buf;
reader->abstract.avail_in = reader_res;
}
return tsf_true;
}