Improve progress callback. - Pass zip archive and user provided context (void pointer) into callback. - Optional free function for context.
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 148455d..2fe891d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt
@@ -111,6 +111,7 @@ zip_name_locate.c zip_new.c zip_open.c + zip_progress.c zip_rename.c zip_replace.c zip_set_archive_comment.c
diff --git a/lib/Makefile.am b/lib/Makefile.am index 524f418..e631db1 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am
@@ -87,6 +87,7 @@ zip_name_locate.c \ zip_new.c \ zip_open.c \ + zip_progress.c \ zip_rename.c \ zip_replace.c \ zip_set_archive_comment.c \
diff --git a/lib/zip.h b/lib/zip.h index e8c9eaa..5de1a9e 100644 --- a/lib/zip.h +++ b/lib/zip.h
@@ -309,10 +309,12 @@ typedef zip_uint32_t zip_flags_t; typedef zip_int64_t (*zip_source_callback)(void *, void *, zip_uint64_t, zip_source_cmd_t); -typedef void (*zip_progress_callback_t)(double); - +typedef void (*zip_progress_callback)(zip_t *, double, void *); #ifndef ZIP_DISABLE_DEPRECATED +typedef void (*zip_progress_callback_t)(double); +ZIP_EXTERN void zip_register_progress_callback(zip_t *, zip_progress_callback_t); + ZIP_EXTERN zip_int64_t zip_add(zip_t *, const char *, zip_source_t *); /* use zip_file_add */ ZIP_EXTERN zip_int64_t zip_add_dir(zip_t *, const char *); /* use zip_dir_add */ ZIP_EXTERN const char *zip_get_file_comment(zip_t *, zip_uint64_t, int *, int); /* use zip_file_get_comment */ @@ -378,7 +380,7 @@ ZIP_EXTERN zip_int64_t zip_name_locate(zip_t *, const char *, zip_flags_t); ZIP_EXTERN zip_t *zip_open(const char *, int, int *); ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *, int, zip_error_t *); -ZIP_EXTERN void zip_register_progress_callback(zip_t *, zip_progress_callback_t); +ZIP_EXTERN int zip_register_progress_callback_with_state(zip_t *, double, zip_progress_callback, void (*)(void *), void *); ZIP_EXTERN int zip_set_archive_comment(zip_t *, const char *, zip_uint16_t); ZIP_EXTERN int zip_set_archive_flag(zip_t *, zip_flags_t, int); ZIP_EXTERN int zip_set_default_password(zip_t *, const char *);
diff --git a/lib/zip_close.c b/lib/zip_close.c index 3180e15..3a2ff0a 100644 --- a/lib/zip_close.c +++ b/lib/zip_close.c
@@ -51,18 +51,10 @@ #endif -typedef struct { - double last_update; /* last value callback function was called with */ - - double start; /* start of sub-progress setcion */ - double end; /* end of sub-progress setcion */ -} progress_state_t; - -static int add_data(zip_t *, zip_source_t *, zip_dirent_t *, progress_state_t *); -static int copy_data(zip_t *, zip_uint64_t, progress_state_t *); -static int copy_source(zip_t *, zip_source_t *, progress_state_t *, zip_int64_t); +static int add_data(zip_t *, zip_source_t *, zip_dirent_t *); +static int copy_data(zip_t *, zip_uint64_t); +static int copy_source(zip_t *, zip_source_t *, zip_int64_t); static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t); -static void _zip_progress(zip_t *, progress_state_t *, double); ZIP_EXTERN int zip_close(zip_t *za) @@ -72,7 +64,6 @@ int error; zip_filelist_t *filelist; int changed; - progress_state_t progress_state; if (za == NULL) return -1; @@ -129,22 +120,15 @@ free(filelist); return -1; } - - if (za->progress_callback) { - progress_state.last_update = 0.0; - za->progress_callback(0.0); - } + + _zip_progress_start(za->progress); error = 0; for (j=0; j<survivors; j++) { int new_data; zip_entry_t *entry; zip_dirent_t *de; - if (za->progress_callback) { - progress_state.start = (double)j / (double)survivors; - progress_state.end = (double)(j+1) / (double)survivors; - _zip_progress(za, &progress_state, 0.0); - } + _zip_progress_subrange(za->progress, (double)j / (double)survivors, (double)(j+1) / (double)survivors); i = filelist[j].idx; entry = za->entry+i; @@ -184,7 +168,7 @@ } /* add_data writes dirent */ - if (add_data(za, zs ? zs : entry->source, de, &progress_state) < 0) { + if (add_data(za, zs ? zs : entry->source, de) < 0) { error = 1; if (zs) zip_source_free(zs); @@ -211,7 +195,7 @@ error = 1; break; } - if (copy_data(za, de->comp_size, &progress_state) < 0) { + if (copy_data(za, de->comp_size) < 0) { error = 1; break; } @@ -237,9 +221,7 @@ return -1; } - if (za->progress_callback) { - _zip_progress(za, &progress_state, 1.0); - } + _zip_progress_end(za->progress); zip_discard(za); @@ -248,7 +230,7 @@ static int -add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, progress_state_t *progress_state) +add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { zip_int64_t offstart, offdata, offend, data_length; struct zip_stat st; @@ -430,7 +412,7 @@ return -1; } - ret = copy_source(za, src_final, progress_state, data_length); + ret = copy_source(za, src_final, data_length); if (zip_source_stat(src_final, &st) < 0) { _zip_error_set_from_source(&za->error, src_final); @@ -495,7 +477,7 @@ static int -copy_data(zip_t *za, zip_uint64_t len, progress_state_t *progress_state) +copy_data(zip_t *za, zip_uint64_t len) { zip_uint8_t buf[BUFSIZE]; size_t n; @@ -512,10 +494,8 @@ } len -= n; - - if (za->progress_callback) { - _zip_progress(za, progress_state, (total - (double)len) / total); - } + + _zip_progress_update(za->progress, (total - (double)len) / total); } return 0; @@ -523,7 +503,7 @@ static int -copy_source(zip_t *za, zip_source_t *src, progress_state_t *progress_state, zip_int64_t data_length) +copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { zip_uint8_t buf[BUFSIZE]; zip_int64_t n, current; @@ -541,9 +521,9 @@ ret = -1; break; } - if (n == sizeof(buf) && za->progress_callback && data_length > 0) { + if (n == sizeof(buf) && za->progress && data_length > 0) { current += n; - _zip_progress(za, progress_state, (double)current/(double)data_length); + _zip_progress_update(za->progress, (double)current/(double)data_length); } } @@ -602,20 +582,3 @@ return changed; } - -static void -_zip_progress(zip_t *za, progress_state_t *progress_state, double sub_current) -{ - double current; - - if (za->progress_callback == NULL) { - return; - } - - current = ZIP_MIN(ZIP_MAX(sub_current, 0.0), 1.0) * (progress_state->end - progress_state->start) + progress_state->start; - - if (current - progress_state->last_update > 0.001) { - za->progress_callback(current); - progress_state->last_update = current; - } -}
diff --git a/lib/zip_discard.c b/lib/zip_discard.c index ef891e3..93bdf3a 100644 --- a/lib/zip_discard.c +++ b/lib/zip_discard.c
@@ -71,6 +71,8 @@ } free(za->open_source); + _zip_progress_free(za->progress); + zip_error_fini(&za->error); free(za);
diff --git a/lib/zip_new.c b/lib/zip_new.c index a257a0d..96ece2d 100644 --- a/lib/zip_new.c +++ b/lib/zip_new.c
@@ -68,7 +68,7 @@ za->entry = NULL; za->nopen_source = za->nopen_source_alloc = 0; za->open_source = NULL; - za->progress_callback = NULL; + za->progress = NULL; return za; }
diff --git a/lib/zip_open.c b/lib/zip_open.c index e4e9d97..265eb2e 100644 --- a/lib/zip_open.c +++ b/lib/zip_open.c
@@ -157,12 +157,6 @@ } } -ZIP_EXTERN void -zip_register_progress_callback(zip_t *za, zip_progress_callback_t progress_callback) -{ - za->progress_callback = progress_callback; -} - zip_t * _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error)
diff --git a/lib/zip_progress.c b/lib/zip_progress.c new file mode 100644 index 0000000..b1b6e58 --- /dev/null +++ b/lib/zip_progress.c
@@ -0,0 +1,184 @@ +/* + zip_progress.c -- progress reporting + Copyright (C) 2017 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at <libzip@nih.at> + + 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. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 THE AUTHORS 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 <stdlib.h> + + +#define _ZIP_COMPILING_DEPRECATED +#include "zipint.h" + +struct zip_progress { + zip_t *za; + zip_progress_callback callback; + void (*ud_free)(void *); + + void *ud; + + double precision; + + /* state */ + double last_update; /* last value callback function was called with */ + + double start; /* start of sub-progress section */ + double end; /* end of sub-progress section */ +}; + + +void +_zip_progress_end(zip_progress_t *progress) { + _zip_progress_update(progress, 1.0); +} + + +void +_zip_progress_free(zip_progress_t *progress) { + if (progress == NULL) { + return; + } + + if (progress->ud_free) { + progress->ud_free(progress->ud); + } + + free(progress); +} + + +zip_progress_t * +_zip_progress_new(zip_t *za, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud) { + zip_progress_t *progress = (zip_progress_t *)malloc(sizeof(*progress)); + + if (progress == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + progress->za = za; + progress->callback = callback; + progress->ud_free = ud_free; + progress->ud = ud; + progress->precision = precision; + + return progress; +} + + +void +_zip_progress_start(zip_progress_t *progress) { + if (progress == NULL) { + return; + } + + progress->last_update = 0.0; + progress->callback(progress->za, 0.0, progress->ud); +} + + +void +_zip_progress_subrange(zip_progress_t *progress, double start, double end) { + if (progress == NULL) { + return; + } + + progress->start = start; + progress->end = end; + + _zip_progress_update(progress, 0.0); +} + +void +_zip_progress_update(zip_progress_t *progress, double sub_current) { + double current; + + if (progress == NULL) { + return; + } + + current = ZIP_MIN(ZIP_MAX(sub_current, 0.0), 1.0) * (progress->end - progress->start) + progress->start; + + if (current - progress->last_update > progress->precision) { + progress->callback(progress->za, current, progress->ud); + progress->last_update = current; + } +} + + +ZIP_EXTERN int +zip_register_progress_callback_with_state(zip_t *za, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud) { + zip_progress_t *progress = NULL; + + if (callback != NULL) { + if ((progress = _zip_progress_new(za, precision, callback, ud_free, ud)) == NULL) { + return -1; + } + } + + _zip_progress_free(za->progress); + za->progress = progress; + + return 0; +} + + +struct legacy_ud { + zip_progress_callback_t callback; +}; + + +static void +_zip_legacy_progress_callback(zip_t *za, double progress, void *vud) { + struct legacy_ud *ud = (struct legacy_ud *)vud; + + ud->callback(progress); +} + +ZIP_EXTERN void +zip_register_progress_callback(zip_t *za, zip_progress_callback_t progress_callback) +{ + struct legacy_ud *ud; + + if (progress_callback == NULL) { + zip_register_progress_callback_with_state(za, 0, NULL, NULL, NULL); + } + + if ((ud = (struct legacy_ud *)malloc(sizeof(*ud))) == NULL) { + return; + } + + ud->callback = progress_callback; + + if (zip_register_progress_callback_with_state(za, 0.001, _zip_legacy_progress_callback, free, ud) < 0) { + free(ud); + } +}
diff --git a/lib/zipint.h b/lib/zipint.h index 6883c3a..9e30585 100644 --- a/lib/zipint.h +++ b/lib/zipint.h
@@ -197,6 +197,7 @@ typedef enum zip_encoding_type zip_encoding_type_t; struct zip_hash; +struct zip_progress; typedef struct zip_cdir zip_cdir_t; typedef struct zip_dirent zip_dirent_t; @@ -205,6 +206,7 @@ typedef struct zip_string zip_string_t; typedef struct zip_buffer zip_buffer_t; typedef struct zip_hash zip_hash_t; +typedef struct zip_progress zip_progress_t; /* zip archive, part of API */ @@ -232,7 +234,7 @@ zip_hash_t *names; /* hash table for name lookup */ - zip_progress_callback_t progress_callback; /* progress callback for zip_close() */ + zip_progress_t *progress; /* progress callback for zip_close() */ }; /* file in zip archive, part of API */ @@ -488,6 +490,13 @@ zip_t *_zip_open(zip_source_t *, unsigned int, zip_error_t *); +void _zip_progress_end(zip_progress_t *progress); +void _zip_progress_free(zip_progress_t *progress); +zip_progress_t *_zip_progress_new(zip_t *za, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud); +void _zip_progress_start(zip_progress_t *progress); +void _zip_progress_subrange(zip_progress_t *progress, double start, double end); +void _zip_progress_update(zip_progress_t *progress, double value); + bool zip_random(zip_uint8_t *buffer, zip_uint16_t length); int _zip_read(zip_source_t *src, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error);
diff --git a/src/ziptool.c b/src/ziptool.c index 6c3f25e..99b27ff 100644 --- a/src/ziptool.c +++ b/src/ziptool.c
@@ -402,13 +402,13 @@ } static void -progress_callback(double percentage) { +progress_callback(zip_t *za, double percentage, void *ud) { printf("%.1lf%% done\n", percentage*100); } static int print_progress(int argc, char *argv[]) { - zip_register_progress_callback(za, progress_callback); + zip_register_progress_callback_with_state(za, 0.001, progress_callback, NULL, NULL); return 0; }
diff --git a/xcode/libzip.xcodeproj/project.pbxproj b/xcode/libzip.xcodeproj/project.pbxproj index 31e4288..e407e7f 100644 --- a/xcode/libzip.xcodeproj/project.pbxproj +++ b/xcode/libzip.xcodeproj/project.pbxproj
@@ -235,6 +235,8 @@ 4BD6CB6619E71CD100710654 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B01D70815B2F4CF002D5007 /* libz.dylib */; }; 4BD6CB6719E71CD100710654 /* libzip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B01D68B15B2F3F1002D5007 /* libzip.framework */; }; 4BD6CB6F19E71D6900710654 /* hole.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD6CB5E19E71B3B00710654 /* hole.c */; }; + 4BD708791EB1CF73003F351F /* zip_progress.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD708781EB1CF73003F351F /* zip_progress.c */; }; + 4BD7087A1EB1CF73003F351F /* zip_progress.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD708781EB1CF73003F351F /* zip_progress.c */; }; 4BDC724415B1B25E00236D3C /* zip_add_dir.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC71F115B1B25E00236D3C /* zip_add_dir.c */; }; 4BDC724515B1B25E00236D3C /* zip_add_entry.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC71F215B1B25E00236D3C /* zip_add_entry.c */; }; 4BDC724615B1B25E00236D3C /* zip_add.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC71F315B1B25E00236D3C /* zip_add.c */; }; @@ -813,6 +815,7 @@ 4BD6CB5C19E6A5D900710654 /* source_hole.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = source_hole.c; sourceTree = "<group>"; }; 4BD6CB5E19E71B3B00710654 /* hole.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hole.c; sourceTree = "<group>"; }; 4BD6CB6C19E71CD100710654 /* hole */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = hole; sourceTree = BUILT_PRODUCTS_DIR; }; + 4BD708781EB1CF73003F351F /* zip_progress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zip_progress.c; sourceTree = "<group>"; }; 4BDC71E015B182B200236D3C /* libzip_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = libzip_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4BDC71F115B1B25E00236D3C /* zip_add_dir.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = zip_add_dir.c; path = ../lib/zip_add_dir.c; sourceTree = "<group>"; }; 4BDC71F215B1B25E00236D3C /* zip_add_entry.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = zip_add_entry.c; path = ../lib/zip_add_entry.c; sourceTree = "<group>"; }; @@ -1372,6 +1375,7 @@ 4BDC721D15B1B25E00236D3C /* zip_name_locate.c */, 4BDC721E15B1B25E00236D3C /* zip_new.c */, 4BDC721F15B1B25E00236D3C /* zip_open.c */, + 4BD708781EB1CF73003F351F /* zip_progress.c */, 736ED9B81E3D688C00C36873 /* zip_random_unix.c */, 4BDC722015B1B25E00236D3C /* zip_rename.c */, 4BDC722115B1B25E00236D3C /* zip_replace.c */, @@ -1797,6 +1801,7 @@ 4B01D6A915B2F46B002D5007 /* zip_close.c in Sources */, 4B01D6AA15B2F46B002D5007 /* zip_delete.c in Sources */, 4B01D6AB15B2F46B002D5007 /* zip_dir_add.c in Sources */, + 4BD7087A1EB1CF73003F351F /* zip_progress.c in Sources */, 4B01D6AC15B2F46B002D5007 /* zip_dirent.c in Sources */, 4B01D6AD15B2F46B002D5007 /* zip_discard.c in Sources */, 4B0454B81E8E3E02002FA1F9 /* zip_source_get_compression_flags.c in Sources */, @@ -1979,6 +1984,7 @@ 736ED9C11E3D6B8300C36873 /* zip_source_winzip_aes_decode.c in Sources */, 4BDC724715B1B25E00236D3C /* zip_close.c in Sources */, 4BDC724815B1B25E00236D3C /* zip_delete.c in Sources */, + 4BD708791EB1CF73003F351F /* zip_progress.c in Sources */, 736ED9C21E3D6B8600C36873 /* zip_source_winzip_aes_encode.c in Sources */, 4BDC724915B1B25E00236D3C /* zip_dir_add.c in Sources */, 4B0454B91E8E3E03002FA1F9 /* zip_source_get_compression_flags.c in Sources */,