From c555a9636f72c03e25e5a525a7725cb7e6a67680 Mon Sep 17 00:00:00 2001 From: jinfeihan57 <38484902+jinfeihan57@users.noreply.github.com> Date: Tue, 18 Aug 2020 17:00:01 +0800 Subject: [PATCH] zip_extract_stream() (#131) Add zip_extract_stream --- src/zip.c | 114 +++++++++++++++++++++++++++++++++++++++++----------- src/zip.h | 19 +++++++++ test/test.c | 36 +++++++++++++++++ 3 files changed, 145 insertions(+), 24 deletions(-) diff --git a/src/zip.c b/src/zip.c index 3b2821e..1c6d77d 100644 --- a/src/zip.c +++ b/src/zip.c @@ -832,40 +832,24 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) { return status; } -int zip_extract(const char *zipname, const char *dir, - int (*on_extract)(const char *filename, void *arg), void *arg) { +static inline int extract(mz_zip_archive *zip_archive, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg){ int status = -1; mz_uint i, n; char path[MAX_PATH + 1]; char symlink_to[MAX_PATH + 1]; - mz_zip_archive zip_archive; mz_zip_archive_file_stat info; size_t dirlen = 0; mz_uint32 xattr = 0; memset(path, 0, sizeof(path)); memset(symlink_to, 0, sizeof(symlink_to)); - if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { - // Cannot memset zip archive - return -1; - } - - if (!zipname || !dir) { - // Cannot parse zip archive name - return -1; - } dirlen = strlen(dir); if (dirlen + 1 > MAX_PATH) { return -1; } - // Now try to open the archive. - if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { - // Cannot initialize zip_archive reader - return -1; - } - memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); #if defined(_MSC_VER) @@ -884,9 +868,9 @@ int zip_extract(const char *zipname, const char *dir, } // Get and print information about each file in the archive. - n = mz_zip_reader_get_num_files(&zip_archive); + n = mz_zip_reader_get_num_files(zip_archive); for (i = 0; i < n; ++i) { - if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) { + if (!mz_zip_reader_file_stat(zip_archive, i, &info)) { // Cannot get information about zip archive; goto out; } @@ -912,7 +896,7 @@ int zip_extract(const char *zipname, const char *dir, defined(__MINGW32__) #else if (info.m_uncomp_size > MAX_PATH || - !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, + !mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to, MAX_PATH, 0, NULL, 0)) { goto out; } @@ -922,8 +906,8 @@ int zip_extract(const char *zipname, const char *dir, } #endif } else { - if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) { - if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) { + if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) { + if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) { // Cannot extract zip archive to file goto out; } @@ -950,10 +934,92 @@ int zip_extract(const char *zipname, const char *dir, out: // Close the archive, freeing any resources it was using - if (!mz_zip_reader_end(&zip_archive)) { + if (!mz_zip_reader_end(zip_archive)) { // Cannot end zip reader status = -1; } + return status; + +} + +static inline mz_zip_archive * zip_archive_init_file(const char *zipname, mz_uint32 flags){ + mz_zip_archive *zip_archive = NULL; + zip_archive = (mz_zip_archive *)malloc(sizeof(mz_zip_archive)); + if (!zip_archive){ + // malloc failed. + return NULL; + } + if (!memset(zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + free(zip_archive); + return NULL; + } + // Now try to open the archive. + if (!mz_zip_reader_init_file(zip_archive, zipname, flags)) { + // Cannot initialize zip_archive reader + free(zip_archive); + return NULL; + } + return zip_archive; +} + +int zip_extract(const char *zipname, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg) { + if (!zipname || !dir) { + // Cannot parse zip archive name + return -1; + } + // init zip_archive and set reader + mz_zip_archive *zip_archive = zip_archive_init_file(zipname, 0); + if (!zip_archive){ + return -1; + } + + int status = extract(zip_archive, dir, on_extract, arg); + + free(zip_archive); return status; } + +static inline mz_zip_archive * zip_archive_init_mem(const void *stream, + size_t size, mz_uint32 flags){ + mz_zip_archive *zip_archive = NULL; + zip_archive = (mz_zip_archive *)malloc(sizeof(mz_zip_archive)); + if (!zip_archive){ + // malloc failed. + return NULL; + } + if (!memset(zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + free(zip_archive); + return NULL; + } + // Now try to open the archive. + if (!mz_zip_reader_init_mem(zip_archive, stream, size, flags)) { + // Cannot initialize zip_archive reader + free(zip_archive); + return NULL; + } + return zip_archive; +} + +int zip_extract_stream(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg) { + if (!stream || !dir) { + // Cannot parse zip archive stream + return -1; + } + // init zip_archive and set reader + mz_zip_archive *zip_archive = zip_archive_init_mem(stream, size, 0); + if (!zip_archive){ + return -1; + } + + int status = extract(zip_archive, dir, on_extract, arg); + + free(zip_archive); + + return status; + +} \ No newline at end of file diff --git a/src/zip.h b/src/zip.h index cd3ab5c..450d46f 100644 --- a/src/zip.h +++ b/src/zip.h @@ -313,6 +313,25 @@ extern int zip_extract(const char *zipname, const char *dir, int (*on_extract_entry)(const char *filename, void *arg), void *arg); +/** + * Extracts a zip archive stream into directory. + * + * If on_extract is not NULL, the callback will be called after + * successfully extracted each zip entry. + * Returning a negative value from the callback will cause abort and return an + * error. The last argument (void *arg) is optional, which you can use to pass + * data to the on_extract callback. + * + * @param stream zip archive stream. + * @param size stream size. + * @param dir output directory. + * @param on_extract on extract callback. + * @param arg opaque pointer. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_extract_stream(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg); /** @} */ #ifdef __cplusplus diff --git a/test/test.c b/test/test.c index 89cbb30..c092a6d 100644 --- a/test/test.c +++ b/test/test.c @@ -462,6 +462,41 @@ static void test_unix_permissions(void) { remove(RFILE); remove(ZIPNAME); #endif + } + +static void test_extract_stream(void) { + + remove(ZIPNAME); + + struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + assert(zip != NULL); + + assert(0 == zip_entry_open(zip, RFILE)); + assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1))); + assert(0 == zip_entry_close(zip)); + + zip_close(zip); + + remove(RFILE); + + FILE *fp = NULL; + fp = fopen(ZIPNAME, "r+"); + assert(fp != NULL); + + fseek(fp, 0L, SEEK_END); + size_t filesize = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + char stream[filesize]; + memset(stream, 0, filesize); + size_t size = fread(stream, sizeof(char), filesize, fp); + assert(filesize == size); + + assert(0 == zip_extract_stream(stream, size, ".", NULL, NULL)); + + fclose(fp); + remove(RFILE); + remove(ZIPNAME); } int main(int argc, char *argv[]) { @@ -485,6 +520,7 @@ int main(int argc, char *argv[]) { test_exe_permissions(); test_mtime(); test_unix_permissions(); + test_extract_stream(); remove(ZIPNAME); return 0;