diff --git a/.gitignore b/.gitignore index 464a2463f..5fd9c62e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +/build/ +/xcodeproj/ + # Object files *.o *.ko @@ -29,6 +32,6 @@ *.x86_64 *.hex -/build/ - +# Temporary *.swp +.DS_Store diff --git a/.travis.yml b/.travis.yml index 43f68f5db..049d63d1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ compiler: script: - mkdir build - cd build - - cmake .. && make + - cmake .. && make && make test diff --git a/CMakeLists.txt b/CMakeLists.txt index d4196f8ea..7bb8f1a47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,14 @@ if (MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") endif (MSVC) +# libzip set(SRC src/miniz.h src/zip.h src/zip.c) - add_library(${CMAKE_PROJECT_NAME} ${SRC}) + +# zip_test +add_executable(zip_test test/main.c) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) +target_link_libraries(zip_test ${CMAKE_PROJECT_NAME}) + +enable_testing() +add_test(NAME zip_test COMMAND zip_test) diff --git a/README.md b/README.md index 3843676d1..1b0d556ce 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -### A portable (OSX/Linux/Windows), simple zip library written in C +### A portable (OSX/Linux/Windows), simple zip library written in C This is done by hacking awesome [miniz](https://code.google.com/p/miniz) library and layering functions on top of the miniz v1.15 API. [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d70fb0b050b74ef7aed70087e377e7d7)](https://www.codacy.com/app/kuba--/zip?utm_source=github.com&utm_medium=referral&utm_content=kuba--/zip&utm_campaign=Badge_Grade) @@ -10,67 +10,128 @@ This is done by hacking awesome [miniz](https://code.google.com/p/miniz) library [osx-linux-badge]: https://img.shields.io/travis/kuba--/zip/master.svg?label=linux/osx "Travis CI build status" [osx-linux-link]: https://travis-ci.org/kuba--/zip "Travis CI build status" -# The Idea - +# The Idea + ... Some day, I was looking for zip library written in C for my project, but I could not find anything simple enough and lightweight. -Everything what I tried required 'crazy mental gymnastics' to integrate or had some limitations or was too heavy. -I hate frameworks, factories and adding new dependencies. If I must to install all those dependencies and link new library, I'm getting almost sick. -I wanted something powerfull and small enough, so I could add just a few files and compile them into my project. -And finally I found miniz. +Everything what I tried required 'crazy mental gymnastics' to integrate or had some limitations or was too heavy. +I hate frameworks, factories and adding new dependencies. If I must to install all those dependencies and link new library, I'm getting almost sick. +I wanted something powerfull and small enough, so I could add just a few files and compile them into my project. +And finally I found miniz. Miniz is a lossless, high performance data compression library in a single source file. I only needed simple interface to append buffers or files to the current zip-entry. Thanks to this feature I'm able to merge many files/buffers and compress them on-the-fly. It was the reason, why I decided to write zip module on top of the miniz. It required a little bit hacking and wrapping some functions, but I kept simplicity. So, you can grab these 3 files and compile them into your project. I hope that interface is also extremely simple, so you will not have any problems to understand it. -# Example +### Example (compress) ```c #include #include -#include "zip.h" +#include + +int main() { + /* + Create a new zip archive with default compression level (6) + */ + struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + // we should check if zip is NULL and if any other function returned < 0 + { + zip_entry_open(zip, "foo-1.txt"); + { + char *buf = "Some data here..."; + zip_entry_write(zip, buf, strlen(buf)); + } + zip_entry_close(zip); + + zip_entry_open(zip, "foo-2.txt"); + { + // merge 3 files into one entry and compress them on-the-fly. + zip_entry_fwrite(zip, "foo-2.1.txt"); + zip_entry_fwrite(zip, "foo-2.2.txt"); + zip_entry_fwrite(zip, "foo-2.3.txt"); + } + zip_entry_close(zip); + } + // always remember to close and release resources + zip_close(zip); + + /* + Append to existing zip archive + */ + zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a'); + // we should check if zip is NULL + { + zip_entry_open(zip, "foo-3.txt"); + { + char *buf = "Append some data here..."; + zip_entry_write(zip, buf, strlen(buf)); + } + zip_entry_close(zip); + } + // always remember to close and release resources + zip_close(zip); + + return 0; +} +``` + +### Example (decompress) + +```c +#include +#include +#include + +#include // callback function int on_extract_entry(const char *filename, void *arg) { - static int i = 0; - int n = *(int *)arg; - printf("Extracted: %s (%d of %d)\n", filename, ++i, n); + static int i = 0; + int n = *(int *)arg; + printf("Extracted: %s (%d of %d)\n", filename, ++i, n); - return 0; + return 0; } - int main() { - /* - Create a new zip archive with default compression level (6) - */ - struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 0); - // we should check if zip is NULL - { - zip_entry_open(zip, "foo-1.txt"); - { - char *buf = "Some data here..."; - zip_entry_write(zip, buf, strlen(buf)); - } - zip_entry_close(zip); - - zip_entry_open(zip, "foo-2.txt"); - { - // merge 3 files into one entry and compress them on-the-fly. - zip_entry_fwrite(zip, "foo-2.1.txt"); - zip_entry_fwrite(zip, "foo-2.2.txt"); - zip_entry_fwrite(zip, "foo-2.3.txt"); - } - zip_entry_close(zip); - } - // always remember to close and release resources - zip_close(zip); - - - /* - Extract a zip archive into /tmp folder - */ - int arg = 2; - zip_extract("foo.zip", "/tmp", on_extract_entry, &arg); - - return 0; + /* + Extract the zip archive into /tmp folder + */ + int arg = 2; + zip_extract("foo.zip", "/tmp", on_extract_entry, &arg); + + /* + ...or open the zip archive with only read access + */ + void *buf = NULL; + size_t bufsize; + + struct zip_t *zip = zip_open("foo.zip", 0, 'r'); + // we should check if zip is NULL and if any other function returned < 0 + { + zip_entry_open(zip, "foo-1.txt"); + { + // extract into memory + zip_entry_read(zip, &buf, &bufsize); + printf("Read(foo-1.txt): %zu bytes: %.*s\n", bufsize, (int)bufsize, + buf); + } + zip_entry_close(zip); + + zip_entry_open(zip, "foo-2.txt"); + { + // extract into a file + zip_entry_fread(zip, "foo-2.txt"); + } + zip_entry_close(zip); + } + // always remember to close and release resources + zip_close(zip); + + // do something with buffer... and remember to free memory + free(buf); + + return 0; } ``` + + diff --git a/src/zip.c b/src/zip.c index 5166f9600..d88162c7e 100644 --- a/src/zip.c +++ b/src/zip.c @@ -1,12 +1,12 @@ /* - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*/ + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ #include "zip.h" #include "miniz.h" @@ -40,7 +40,7 @@ #define ISSLASH(C) ((C) == '/') #endif -#define cleanup(ptr) \ +#define CLEANUP(ptr) \ do { \ if (ptr) { \ free((void *)ptr); \ @@ -48,6 +48,20 @@ } \ } while (0) +static void __suppress_unused__(void) { + (void)__suppress_unused__; + (void)zip_open; + (void)zip_close; + (void)zip_entry_open; + (void)zip_entry_close; + (void)zip_entry_write; + (void)zip_entry_fwrite; + (void)zip_entry_read; + (void)zip_entry_fread; + (void)zip_create; + (void)zip_extract; +} + static char *basename(const char *name) { char const *p; char const *base = name += FILESYSTEM_PREFIX_LEN(name); @@ -83,6 +97,7 @@ static int mkpath(const char *path) { } struct zip_entry_t { + int index; const char *name; mz_uint64 uncomp_size; mz_uint64 comp_size; @@ -99,52 +114,64 @@ struct zip_t { mz_zip_archive archive; mz_uint level; struct zip_entry_t entry; + char mode; }; -struct zip_t *zip_open(const char *zipname, int level, int append) { +struct zip_t *zip_open(const char *zipname, int level, char mode) { struct zip_t *zip = NULL; - struct MZ_FILE_STAT_STRUCT fstat; if (!zipname || strlen(zipname) < 1) { // zip_t archive name is empty or NULL - return NULL; + goto cleanup; } if (level < 0) level = MZ_DEFAULT_LEVEL; if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level - return NULL; + goto cleanup; } zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); - if (zip) { - zip->level = level; + if (!zip) goto cleanup; + + zip->level = level; + zip->mode = mode; + switch (mode) { + case 'w': + // Create a new archive. + if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + break; - if (append && MZ_FILE_STAT(zipname, &fstat) == 0) { - // Append to an existing archive. + case 'r': + case 'a': if (!mz_zip_reader_init_file( &(zip->archive), zipname, level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { - cleanup(zip); - return NULL; + // An archive file does not exist or cannot initialize + // zip_archive reader + goto cleanup; } - if (!mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { + if (mode == 'a' && + !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { mz_zip_reader_end(&(zip->archive)); - cleanup(zip); - return NULL; + goto cleanup; } - } else { - // Create a new archive. - if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) { - // Cannot initialize zip_archive writer - cleanup(zip); - return NULL; - } - } + + break; + + default: + goto cleanup; } return zip; + +cleanup: + CLEANUP(zip); + return NULL; } void zip_close(struct zip_t *zip) { @@ -152,9 +179,11 @@ void zip_close(struct zip_t *zip) { // Always finalize, even if adding failed for some reason, so we have a // valid central directory. mz_zip_writer_finalize_archive(&(zip->archive)); + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); - cleanup(zip); + CLEANUP(zip); } } @@ -172,6 +201,14 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { return -1; } + pzip = &(zip->archive); + + if (zip->mode == 'r') { + zip->entry.index = mz_zip_reader_locate_file(pzip, entryname, NULL, 0); + return (zip->entry.index < 0) ? -1 : 0; + } + + zip->entry.index = zip->archive.m_total_files; zip->entry.name = STRCLONE(entryname); if (!zip->entry.name) { // Cannot parse zip entry name @@ -187,7 +224,6 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); zip->entry.method = 0; - pzip = &(zip->archive); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); @@ -256,20 +292,24 @@ int zip_entry_close(struct zip_t *zip) { time_t t; struct tm *tm; mz_uint16 dos_time, dos_date; + int status = -1; if (!zip) { // zip_t handler is not initialized return -1; } + if (zip->mode == 'r') { + return 0; + } + pzip = &(zip->archive); level = zip->level & 0xF; if (level) { done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { // Cannot flush compressed buffer - cleanup(zip->entry.name); - return -1; + goto cleanup; } zip->entry.comp_size = zip->entry.state.m_comp_size; zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs; @@ -288,8 +328,7 @@ int zip_entry_close(struct zip_t *zip) { if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) { // No zip64 support, yet - cleanup(zip->entry.name); - return -1; + goto cleanup; } if (!mz_zip_writer_create_local_dir_header( @@ -297,16 +336,14 @@ int zip_entry_close(struct zip_t *zip) { zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date)) { // Cannot create zip entry header - cleanup(zip->entry.name); - return -1; + goto cleanup; } if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, zip->entry.header, sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { // Cannot write zip entry header - cleanup(zip->entry.name); - return -1; + goto cleanup; } if (!mz_zip_writer_add_to_central_dir( @@ -315,15 +352,16 @@ int zip_entry_close(struct zip_t *zip) { zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, 0)) { // Cannot write to zip central dir - cleanup(zip->entry.name); - return -1; + goto cleanup; } pzip->m_total_files++; pzip->m_archive_size = zip->entry.offset; + status = 0; - cleanup(zip->entry.name); - return 0; +cleanup: + CLEANUP(zip->entry.name); + return status; } int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { @@ -393,6 +431,55 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) { return status; } +int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + *buf = mz_zip_reader_extract_to_heap(pzip, idx, bufsize, 0); + return (*buf) ? 0 : -1; +} + +int zip_entry_fread(struct zip_t *zip, const char *filename) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->mode != 'r' || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + pzip = &(zip->archive); + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + return (mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) ? 0 : -1; +} + int zip_create(const char *zipname, const char *filenames[], size_t len) { int status = 0; size_t i; @@ -436,7 +523,7 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) { int zip_extract(const char *zipname, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { - int status = 0; + int status = -1; mz_uint i, n; char path[MAX_PATH + 1] = {0}; mz_zip_archive zip_archive; @@ -461,8 +548,7 @@ int zip_extract(const char *zipname, const char *dir, // Now try to open the archive. if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { // Cannot initialize zip_archive reader - status = -1; - goto finally; + return -1; } strcpy(path, dir); @@ -480,36 +566,33 @@ int zip_extract(const char *zipname, const char *dir, for (i = 0; i < n; ++i) { if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) { // Cannot get information about zip archive; - status = -1; - break; + goto out; } strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); if (mkpath(path) < 0) { // Cannot make a path - status = -1; - break; + goto out; } if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) { // Cannot extract zip archive to file - status = -1; - break; + goto out; } if (on_extract) { if (on_extract(path, arg) < 0) { - status = -1; - break; + goto out; } } } + status = 0; +out: // Close the archive, freeing any resources it was using if (!mz_zip_reader_end(&zip_archive)) { // Cannot end zip reader status = -1; } -finally: return status; } diff --git a/src/zip.h b/src/zip.h index 2bddc023c..74a9cd4e3 100644 --- a/src/zip.h +++ b/src/zip.h @@ -1,15 +1,15 @@ /* - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*/ + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ #pragma once -#ifndef ZIP_H +#ifndef ZIP_H #define ZIP_H #include @@ -19,72 +19,150 @@ extern "C" { #endif #ifndef MAX_PATH -#define MAX_PATH 32767 /* # chars in a path name including NULL */ +#define MAX_PATH 32767 /* # chars in a path name including NULL */ #endif -#define ZIP_DEFAULT_COMPRESSION_LEVEL 6 +#define ZIP_DEFAULT_COMPRESSION_LEVEL 6 -/* This data structure is used throughout the library to represent zip archive - forward declaration. */ +/* + This data structure is used throughout the library to represent zip archive + - forward declaration. +*/ struct zip_t; /* - Opens zip archive with compression level. - If append is 0 then new archive will be created, otherwise function will try to append to the specified zip archive, - instead of creating a new one. - Compression levels: 0-9 are the standard zlib-style levels. - Returns pointer to zip_t structure or NULL on error. + Opens zip archive with compression level using the given mode. + + Args: + zipname: zip archive file name. + level: compression level (0-9 are the standard zlib-style levels). + mode: file access mode. + 'r': opens a file for reading/extracting (the file must exists). + 'w': creates an empty file for writing. + 'a': appends to an existing archive. + + Returns: + The zip archive handler or NULL on error */ -struct zip_t *zip_open(const char *zipname, int level, int append); +struct zip_t *zip_open(const char *zipname, int level, char mode); + +/* + Closes zip archive, releases resources - always finalize. -/* Closes zip archive, releases resources - always finalize. */ + Args: + zip: zip archive handler. +*/ void zip_close(struct zip_t *zip); /* Opens a new entry for writing in a zip archive. - Returns negative number (< 0) on error, 0 on success. + + Args: + zip: zip archive handler. + entryname: an entry name in local dictionary. + + Returns: + The return code - 0 on success, negative number (< 0) on error. */ int zip_entry_open(struct zip_t *zip, const char *entryname); /* - Closes zip entry, flushes buffer and releases resources. - Returns negative number (< 0) on error, 0 on success. + Closes a zip entry, flushes buffer and releases resources. + + Args: + zip: zip archive handler. + + Returns: + The return code - 0 on success, negative number (< 0) on error. */ int zip_entry_close(struct zip_t *zip); /* Compresses an input buffer for the current zip entry. - Returns negative number (< 0) on error, 0 on success. + + Args: + zip: zip archive handler. + buf: input buffer. + bufsize: input buffer size (in bytes). + + Returns: + The return code - 0 on success, negative number (< 0) on error. */ int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); /* Compresses a file for the current zip entry. - Returns negative number (< 0) on error, 0 on success. + + Args: + zip: zip archive handler. + filename: input file. + + Returns: + The return code - 0 on success, negative number (< 0) on error. */ int zip_entry_fwrite(struct zip_t *zip, const char *filename); /* - Creates a new archive and puts len files into a single zip archive - Returns negative number (< 0) on error, 0 on success. + Extracts the current zip entry into output buffer. + + Args: + zip: zip archive handler. + buf: output buffer. + bufsize: output buffer size (in bytes) + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); + +/* + Extracts the current zip entry into output file. + + Args: + zip: zip archive handler. + filename: output file. + + Returns: + The return code - 0 on success, negative number (< 0) on error. +*/ +int zip_entry_fread(struct zip_t *zip, const char *filename); + +/* + Creates a new archive and puts files into a single zip archive. + + Args: + zipname: zip archive file. + filenames: input files. + len: number of input files. + + Returns: + The return code - 0 on success, negative number (< 0) on error. */ int zip_create(const char *zipname, const char *filenames[], size_t len); /* - Extracts a zip archive file into dir. - If on_extract_entry is not NULL, the callback will be called after successfully extracted each zip entry. - Returning a negative value from the callback will cause abort the extract and return an error. + Extracts a zip archive file into directory. + + If on_extract_entry 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 + some data to the on_extract_entry callback. - The last argument (void *arg) is optional, which you can use to pass some data to the on_extract_entry callback. + Args: + zipname: zip archive file. + dir: output directory. + on_extract_entry: on extract callback. - Returns negative number (< 0) on error, 0 on success. + Returns: + The return code - 0 on success, negative number (< 0) on error. */ -int zip_extract(const char *zipname, const char *dir, int (* on_extract_entry)(const char *filename, void *arg), void *arg); +int zip_extract(const char *zipname, const char *dir, + int (*on_extract_entry)(const char *filename, void *arg), + void *arg); #ifdef __cplusplus } #endif #endif - - - diff --git a/test/main.c b/test/main.c new file mode 100644 index 000000000..c76e422aa --- /dev/null +++ b/test/main.c @@ -0,0 +1,96 @@ +#include +#include +#include + +#include + +// callback function +int on_extract_entry(const char *filename, void *arg) { + static int i = 0; + int n = *(int *)arg; + printf("Extracted: %s (%d of %d)\n", filename, ++i, n); + + return 0; +} + +int main() { + /* + Create a new zip archive with default compression level (6) + */ + struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + // we should check if zip is NULL and if any other function returned < 0 + { + zip_entry_open(zip, "foo-1.txt"); + { + char *buf = "Some data here..."; + zip_entry_write(zip, buf, strlen(buf)); + } + zip_entry_close(zip); + + zip_entry_open(zip, "foo-2.txt"); + { + // merge 3 files into one entry and compress them on-the-fly. + zip_entry_fwrite(zip, "foo-2.1.txt"); + zip_entry_fwrite(zip, "foo-2.2.txt"); + zip_entry_fwrite(zip, "foo-2.3.txt"); + } + zip_entry_close(zip); + } + // always remember to close and release resources + zip_close(zip); + + /* + Append to existing zip archive + */ + zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a'); + // we should check if zip is NULL + { + zip_entry_open(zip, "foo-3.txt"); + { + char *buf = "Append some data here..."; + zip_entry_write(zip, buf, strlen(buf)); + } + zip_entry_close(zip); + } + // always remember to close and release resources + zip_close(zip); + + /* + Extract the zip archive into /tmp folder + */ + int arg = 2; + zip_extract("foo.zip", "/tmp", on_extract_entry, &arg); + + /* + ...or open the zip archive with only read access + */ + void *buf = NULL; + size_t bufsize; + + zip = zip_open("foo.zip", 0, 'r'); + // we should check if zip is NULL and if any other function returned < 0 + { + zip_entry_open(zip, "foo-1.txt"); + { + // extract into memory + zip_entry_read(zip, &buf, &bufsize); + printf("Read(foo-1.txt): %zu bytes: %.*s\n", bufsize, (int)bufsize, + buf); + } + zip_entry_close(zip); + + zip_entry_open(zip, "foo-2.txt"); + { + // extract into a file + zip_entry_fread(zip, "foo-2.txt"); + } + zip_entry_close(zip); + } + // always remember to close and release resources + zip_close(zip); + + // do something with buffer... and remember to free memory + free(buf); + + return 0; +}