Hash file names to speed up some operations. In particular, zip_file_add and zip_name_locate. Add consistency check for duplicate file names, now that it's cheap.
diff --git a/TODO b/TODO index 124d79e..bbdb9d5 100644 --- a/TODO +++ b/TODO
@@ -101,6 +101,7 @@ Test Case Issues ================ +* unchange on added file * test seek in zip_source_crc * test cases for set_extra*, delete_extra*, *extra_field* * test cases for in memory archives
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 3bfc622..2a0962f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt
@@ -100,6 +100,7 @@ zip_get_name.c zip_get_num_entries.c zip_get_num_files.c + zip_hash.c zip_io_util.c zip_memdup.c zip_name_locate.c
diff --git a/lib/Makefile.am b/lib/Makefile.am index c43ed4b..8cd2e6e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am
@@ -58,6 +58,7 @@ zip_get_num_entries.c \ zip_get_num_files.c \ zip_get_name.c \ + zip_hash.c \ zip_io_util.c \ zip_memdup.c \ zip_name_locate.c \
diff --git a/lib/zip_delete.c b/lib/zip_delete.c index b3e7abb..7be0926 100644 --- a/lib/zip_delete.c +++ b/lib/zip_delete.c
@@ -38,6 +38,8 @@ ZIP_EXTERN int zip_delete(zip_t *za, zip_uint64_t idx) { + const char *name; + if (idx >= za->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; @@ -48,6 +50,14 @@ return -1; } + if ((name=_zip_get_name(za, idx, 0, &za->error)) == NULL) { + return -1; + } + + if (!_zip_hash_delete(za->names, (const zip_uint8_t *)name, &za->error)) { + return -1; + } + /* allow duplicate file names, because the file will * be removed directly afterwards */ if (_zip_unchange(za, idx, 1) != 0)
diff --git a/lib/zip_discard.c b/lib/zip_discard.c index db22370..cf12215 100644 --- a/lib/zip_discard.c +++ b/lib/zip_discard.c
@@ -58,6 +58,8 @@ _zip_string_free(za->comment_orig); _zip_string_free(za->comment_changes); + _zip_hash_free(za->names); + if (za->entry) { for (i=0; i<za->nentry; i++) _zip_entry_finalize(za->entry+i);
diff --git a/lib/zip_hash.c b/lib/zip_hash.c new file mode 100644 index 0000000..ec6e336 --- /dev/null +++ b/lib/zip_hash.c
@@ -0,0 +1,263 @@ +/* + zip_hash.c -- hash table string -> uint64 + Copyright (C) 2015 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> +#include <string.h> +#include "zipint.h" + +struct zip_hash_entry { + const zip_uint8_t *name; + zip_int64_t orig_index; + zip_int64_t current_index; + struct zip_hash_entry *next; +}; +typedef struct zip_hash_entry zip_hash_entry_t; + +struct zip_hash { + zip_uint16_t table_size; + zip_hash_entry_t **table; +}; + +zip_hash_t * +_zip_hash_new(zip_uint16_t table_size, zip_error_t *error) +{ + zip_hash_t *hash; + + if (table_size == 0) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return NULL; + } + + if ((hash=(zip_hash_t *)malloc(sizeof(zip_hash_t))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + hash->table_size = table_size; + if ((hash->table=(zip_hash_entry_t**)calloc(table_size, sizeof(zip_hash_entry_t *))) == NULL) { + free(hash); + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + return hash; +} + +static void +_free_list(zip_hash_entry_t *entry) +{ + zip_hash_entry_t *next; + do { + next = entry->next; + free(entry); + entry = next; + } while (entry != NULL); +} + +void +_zip_hash_free(zip_hash_t *hash) +{ + zip_uint16_t i; + + if (hash == NULL) { + return; + } + + for (i=0; i<hash->table_size; i++) { + if (hash->table[i] != NULL) { + _free_list(hash->table[i]); + } + } + free(hash->table); + free(hash); +} + +static zip_uint16_t +_hash_string(const zip_uint8_t *name, zip_uint16_t size) +{ +#define HASH_MULTIPLIER 33 + zip_uint16_t value = 5381; + + if (name == NULL) + return 0; + + while (*name != 0) { + value = (zip_uint16_t)(((value * HASH_MULTIPLIER) + (zip_uint8_t)*name) % size); + name++; + } + + return value; +} + +/* insert into hash, return error on existence or memory issues */ +bool +_zip_hash_add(zip_hash_t *hash, const zip_uint8_t *name, zip_uint64_t index, zip_flags_t flags, zip_error_t *error) +{ + zip_uint16_t hash_value; + zip_hash_entry_t *entry; + + if (hash == NULL || name == NULL || index > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return false; + } + + hash_value = _hash_string(name, hash->table_size); + for (entry = hash->table[hash_value]; entry != NULL; entry = entry->next) { + if (strcmp((const char *)name, (const char *)entry->name) == 0) { + if (((flags & ZIP_FL_UNCHANGED) && entry->orig_index != -1) || entry->current_index != -1) { + zip_error_set(error, ZIP_ER_EXISTS, 0); + return false; + } + else { + break; + } + } + } + + if (entry == NULL) { + if ((entry=(zip_hash_entry_t *)malloc(sizeof(zip_hash_entry_t))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + entry->name = name; + entry->next = hash->table[hash_value]; + hash->table[hash_value] = entry; + entry->orig_index = -1; + } + + if (flags & ZIP_FL_UNCHANGED) { + entry->orig_index = (zip_int64_t)index; + } + entry->current_index = (zip_int64_t)index; + + return true; +} + +/* remove entry from hash, error if not found */ +bool +_zip_hash_delete(zip_hash_t *hash, const zip_uint8_t *name, zip_error_t *error) +{ + zip_uint16_t hash_value; + zip_hash_entry_t *entry, *previous; + + if (hash == NULL || name == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return false; + } + + hash_value = _hash_string(name, hash->table_size); + previous = NULL; + entry = hash->table[hash_value]; + while (entry) { + if (strcmp((const char *)name, (const char *)entry->name) == 0) { + if (entry->orig_index == -1) { + if (previous) { + previous->next = entry->next; + } + else { + hash->table[hash_value] = entry->next; + } + free(entry); + } + else { + entry->current_index = -1; + } + return true; + } + previous = entry; + entry = entry->next; + }; + + zip_error_set(error, ZIP_ER_NOENT, 0); + return false; +} + +/* find value for entry in hash, -1 if not found */ +zip_int64_t +_zip_hash_lookup(zip_hash_t *hash, const zip_uint8_t *name, zip_flags_t flags, zip_error_t *error) +{ + zip_uint16_t hash_value; + zip_hash_entry_t *entry; + + if (hash == NULL || name == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return -1; + } + + hash_value = _hash_string(name, hash->table_size); + for (entry = hash->table[hash_value]; entry != NULL; entry = entry->next) { + if (strcmp((const char *)name, (const char *)entry->name) == 0) { + if (flags & ZIP_FL_UNCHANGED) { + if (entry->orig_index != -1) { + return entry->orig_index; + } + } + else { + if (entry->current_index != -1) { + return entry->current_index; + } + } + break; + } + } + + zip_error_set(error, ZIP_ER_NOENT, 0); + return -1; +} + +void +_zip_hash_revert(zip_hash_t *hash) +{ + zip_uint16_t i; + zip_hash_entry_t *entry, *previous; + + for (i = 0; i < hash->table_size; i++) { + previous = NULL; + entry = hash->table[i]; + while (entry) { + if (entry->orig_index == -1) { + if (previous) { + previous->next = entry->next; + } + else { + hash->table[i] = entry->next; + } + free(entry); + } + else { + entry->current_index = entry->orig_index; + } + previous = entry; + entry = entry->next; + } + } +}
diff --git a/lib/zip_name_locate.c b/lib/zip_name_locate.c index 820ea0c..896132c 100644 --- a/lib/zip_name_locate.c +++ b/lib/zip_name_locate.c
@@ -62,27 +62,33 @@ return -1; } - cmp = (flags & ZIP_FL_NOCASE) ? strcasecmp : strcmp; + if (flags & (ZIP_FL_NOCASE|ZIP_FL_NODIR|ZIP_FL_ENC_CP437)) { + /* can't use hash table */ + cmp = (flags & ZIP_FL_NOCASE) ? strcasecmp : strcmp; - for (i=0; i<za->nentry; i++) { - fn = _zip_get_name(za, i, flags, error); - - /* newly added (partially filled) entry or error */ - if (fn == NULL) - continue; - - if (flags & ZIP_FL_NODIR) { - p = strrchr(fn, '/'); - if (p) - fn = p+1; + for (i=0; i<za->nentry; i++) { + fn = _zip_get_name(za, i, flags, error); + + /* newly added (partially filled) entry or error */ + if (fn == NULL) + continue; + + if (flags & ZIP_FL_NODIR) { + p = strrchr(fn, '/'); + if (p) + fn = p+1; + } + + if (cmp(fname, fn) == 0) { + _zip_error_clear(error); + return (zip_int64_t)i; + } } - if (cmp(fname, fn) == 0) { - _zip_error_clear(error); - return (zip_int64_t)i; - } + zip_error_set(error, ZIP_ER_NOENT, 0); + return -1; } - - zip_error_set(error, ZIP_ER_NOENT, 0); - return -1; + else { + return _zip_hash_lookup(za->names, (const zip_uint8_t *)fname, flags, error); + } }
diff --git a/lib/zip_new.c b/lib/zip_new.c index d54a247..905e45c 100644 --- a/lib/zip_new.c +++ b/lib/zip_new.c
@@ -52,6 +52,11 @@ return NULL; } + if ((za->names = _zip_hash_new(ZIP_HASH_TABLE_SIZE, error)) == NULL) { + free(za); + return NULL; + } + za->src = NULL; za->open_flags = 0; zip_error_init(&za->error);
diff --git a/lib/zip_open.c b/lib/zip_open.c index 1f5e4d8..3f866a9 100644 --- a/lib/zip_open.c +++ b/lib/zip_open.c
@@ -183,7 +183,7 @@ zip_t *za; zip_cdir_t *cdir; struct zip_stat st; - zip_uint64_t len; + zip_uint64_t len, idx; zip_stat_init(&st); if (zip_source_stat(src, &st) < 0) { @@ -222,11 +222,31 @@ za->nentry = cdir->nentry; za->nentry_alloc = cdir->nentry_alloc; za->comment_orig = cdir->comment; - - za->ch_flags = za->flags; free(cdir); + for (idx = 0; idx < za->nentry; idx++) { + const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error); + if (name == NULL) { + /* keep src so discard does not get rid of it */ + zip_source_keep(src); + zip_discard(za); + return NULL; + } + + if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) { + if (za->error.zip_err != ZIP_ER_EXISTS || (flags & ZIP_CHECKCONS)) { + _zip_error_copy(error, &za->error); + /* keep src so discard does not get rid of it */ + zip_source_keep(src); + zip_discard(za); + return NULL; + } + } + } + + za->ch_flags = za->flags; + return za; }
diff --git a/lib/zip_set_name.c b/lib/zip_set_name.c index 5a10381..fd60e16 100644 --- a/lib/zip_set_name.c +++ b/lib/zip_set_name.c
@@ -43,8 +43,10 @@ { zip_entry_t *e; zip_string_t *str; - int changed; + bool same_as_orig; zip_int64_t i; + const zip_uint8_t *old_name, *new_name; + zip_string_t *old_str; if (idx >= za->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); @@ -81,35 +83,76 @@ e = za->entry+idx; - if (e->changes) { - _zip_string_free(e->changes->filename); - e->changes->filename = NULL; - e->changes->changed &= ~ZIP_DIRENT_FILENAME; + if (e->orig) + same_as_orig = _zip_string_equal(e->orig->filename, str); + else + same_as_orig = false; + + if (!same_as_orig && e->changes == NULL) { + if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_string_free(str); + return -1; + } } - if (e->orig) - changed = !_zip_string_equal(e->orig->filename, str); - else - changed = 1; - - if (changed) { - if (e->changes == NULL) { - if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { - zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - _zip_string_free(str); - return -1; - } - } - e->changes->filename = str; - e->changes->changed |= ZIP_DIRENT_FILENAME; + if ((new_name = _zip_string_get(same_as_orig ? e->orig->filename : str, NULL, 0, &za->error)) == NULL) { + _zip_string_free(str); + return -1; + } + + if (e->changes) { + old_str = e->changes->filename; + } + else if (e->orig) { + old_str = e->orig->filename; } else { - _zip_string_free(str); - if (e->changes && e->changes->changed == 0) { - _zip_dirent_free(e->changes); - e->changes = NULL; + old_str = NULL; + } + + if (old_str) { + if ((old_name = _zip_string_get(old_str, NULL, 0, &za->error)) == NULL) { + _zip_string_free(str); + return -1; } } + else { + old_name = NULL; + } + + if (_zip_hash_add(za->names, new_name, idx, 0, &za->error) == false) { + _zip_string_free(str); + return -1; + } + if (old_name) { + _zip_hash_delete(za->names, old_name, NULL); + } + + if (same_as_orig) { + if (e->changes) { + if (e->changes->changed & ZIP_DIRENT_FILENAME) { + _zip_string_free(e->changes->filename); + e->changes->changed &= ~ZIP_DIRENT_FILENAME; + if (e->changes->changed == 0) { + _zip_dirent_free(e->changes); + e->changes = NULL; + } + else { + /* TODO: what if not cloned? can that happen? */ + e->changes->filename = e->orig->filename; + } + } + } + _zip_string_free(str); + } + else { + if (e->changes->changed & ZIP_DIRENT_FILENAME) { + _zip_string_free(e->changes->filename); + } + e->changes->changed |= ZIP_DIRENT_FILENAME; + e->changes->filename = str; + } return 0; }
diff --git a/lib/zip_unchange.c b/lib/zip_unchange.c index 6c8a495..c753712 100644 --- a/lib/zip_unchange.c +++ b/lib/zip_unchange.c
@@ -48,6 +48,7 @@ _zip_unchange(zip_t *za, zip_uint64_t idx, int allow_duplicates) { zip_int64_t i; + const char *orig_name, *changed_name; if (idx >= za->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); @@ -55,9 +56,32 @@ } if (!allow_duplicates && za->entry[idx].changes && (za->entry[idx].changes->changed & ZIP_DIRENT_FILENAME)) { - i = _zip_name_locate(za, _zip_get_name(za, idx, ZIP_FL_UNCHANGED, NULL), 0, NULL); - if (i >= 0 && (zip_uint64_t)i != idx) { - zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + if (za->entry[idx].orig != NULL) { + if ((orig_name=_zip_get_name(za, idx, ZIP_FL_UNCHANGED, &za->error)) == NULL) { + return -1; + } + + i = _zip_name_locate(za, orig_name, 0, NULL); + if (i >= 0 && (zip_uint64_t)i != idx) { + zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + return -1; + } + } + else { + orig_name = NULL; + } + + if ((changed_name=_zip_get_name(za, idx, 0, &za->error)) == NULL) { + return -1; + } + + if (orig_name) { + if (_zip_hash_add(za->names, (const zip_uint8_t *)orig_name, idx, 0, &za->error) == false) { + return -1; + } + } + if (_zip_hash_delete(za->names, (const zip_uint8_t *)changed_name, &za->error) == false) { + _zip_hash_delete(za->names, (const zip_uint8_t *)orig_name, NULL); return -1; } }
diff --git a/lib/zip_unchange_all.c b/lib/zip_unchange_all.c index 6007683..a72c068 100644 --- a/lib/zip_unchange_all.c +++ b/lib/zip_unchange_all.c
@@ -43,6 +43,8 @@ int ret; zip_uint64_t i; + _zip_hash_revert(za->names); + ret = 0; for (i=0; i<za->nentry; i++) ret |= _zip_unchange(za, i, 1);
diff --git a/lib/zipint.h b/lib/zipint.h index a53d62c..91ae88b 100644 --- a/lib/zipint.h +++ b/lib/zipint.h
@@ -266,13 +266,19 @@ typedef enum zip_encoding_type zip_encoding_type_t; +#ifndef ZIP_HASH_TABLE_SIZE +#define ZIP_HASH_TABLE_SIZE 8192 +#endif + +struct zip_hash; + typedef struct zip_cdir zip_cdir_t; typedef struct zip_dirent zip_dirent_t; typedef struct zip_entry zip_entry_t; typedef struct zip_extra_field zip_extra_field_t; typedef struct zip_string zip_string_t; typedef struct zip_buffer zip_buffer_t; - +typedef struct zip_hash zip_hash_t; /* zip archive, part of API */ @@ -287,7 +293,7 @@ char *default_password; /* password used when no other supplied */ zip_string_t *comment_orig; /* archive comment */ - zip_string_t *comment_changes; /* changed archive comment */ + zip_string_t *comment_changes; /* changed archive comment */ bool comment_changed; /* whether archive comment was changed */ zip_uint64_t nentry; /* number of entries */ @@ -297,6 +303,8 @@ unsigned int nopen_source; /* number of open sources using archive */ unsigned int nopen_source_alloc; /* number of sources allocated */ zip_source_t **open_source; /* open sources using archive */ + + zip_hash_t *names; /* hash table for name lookup */ char *tempdir; /* custom temp dir (needed e.g. for OS X sandboxing) */ }; @@ -323,7 +331,7 @@ struct zip_dirent { zip_uint32_t changed; bool local_extra_fields_read; /* whether we already read in local header extra fields */ - bool cloned; /* whether this instance is cloned, and thus shares non-changed strings */ + bool cloned; /* whether this instance is cloned, and thus shares non-changed strings */ zip_uint16_t version_madeby; /* (c) version of creator */ zip_uint16_t version_needed; /* (cl) version needed to extract */ @@ -516,6 +524,13 @@ enum zip_encoding_type _zip_guess_encoding(zip_string_t *, enum zip_encoding_type); zip_uint8_t *_zip_cp437_to_utf8(const zip_uint8_t * const, zip_uint32_t, zip_uint32_t *, zip_error_t *); +bool _zip_hash_add(zip_hash_t *hash, const zip_uint8_t *name, zip_uint64_t index, zip_flags_t flags, zip_error_t *error); +bool _zip_hash_delete(zip_hash_t *hash, const zip_uint8_t *key, zip_error_t *error); +void _zip_hash_free(zip_hash_t *hash); +zip_int64_t _zip_hash_lookup(zip_hash_t *hash, const zip_uint8_t *name, zip_flags_t flags, zip_error_t *error); +zip_hash_t *_zip_hash_new(zip_uint16_t hash_size, zip_error_t *error); +void _zip_hash_revert(zip_hash_t *hash); + zip_t *_zip_open(zip_source_t *, unsigned int, zip_error_t *); int _zip_read(zip_source_t *src, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error);
diff --git a/regress/Makefile.am b/regress/Makefile.am index fdccb90..338516f 100644 --- a/regress/Makefile.am +++ b/regress/Makefile.am
@@ -223,10 +223,6 @@ zip64_creation.test \ zip64_stored_creation.test -XFAIL_TESTS= \ - open_filename_duplicate_consistency.test \ - open_filename_duplicate_empty_consistency.test - AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib -I${top_srcdir}/src LDADD=${top_builddir}/lib/libzip.la
diff --git a/regress/open_filename_duplicate.test b/regress/open_filename_duplicate.test index c8cc727..5204db6 100644 --- a/regress/open_filename_duplicate.test +++ b/regress/open_filename_duplicate.test
@@ -1,6 +1,6 @@ # zip_open: file opens fine even though same file name appears twice program tryopen -args filename_duplicate.zip +args filename_duplicate.zzip return 0 -file filename_duplicate.zip filename_duplicate.zip filename_duplicate.zip -stdout opening 'filename_duplicate.zip' succeeded, 2 entries +file filename_duplicate.zzip filename_duplicate.zip filename_duplicate.zip +stdout opening 'filename_duplicate.zzip' succeeded, 2 entries
diff --git a/regress/open_filename_duplicate_consistency.test b/regress/open_filename_duplicate_consistency.test index a997972..e82cf75 100644 --- a/regress/open_filename_duplicate_consistency.test +++ b/regress/open_filename_duplicate_consistency.test
@@ -1,6 +1,7 @@ # zip_open: file opens fine even though same file name appears twice program tryopen -args -c filename_duplicate.zip -return 0 -file filename_duplicate.zip filename_duplicate.zip filename_duplicate.zip -stderr some error about a duplicate file name +args -c filename_duplicate.zzip +return 1 +file filename_duplicate.zzip filename_duplicate.zip filename_duplicate.zip +stdout opening 'filename_duplicate.zzip' returned error 10 +stderr 1 errors
diff --git a/regress/open_filename_duplicate_empty.test b/regress/open_filename_duplicate_empty.test index 505916b..ea143b6 100644 --- a/regress/open_filename_duplicate_empty.test +++ b/regress/open_filename_duplicate_empty.test
@@ -1,6 +1,6 @@ # zip_open: file opens fine even though same file name (empty file name) appears twice program tryopen -args filename_duplicate_empty.zip +args filename_duplicate_empty.zzip return 0 -file filename_duplicate_empty.zip filename_duplicate_empty.zip filename_duplicate_empty.zip -stdout opening 'filename_duplicate_empty.zip' succeeded, 2 entries +file filename_duplicate_empty.zzip filename_duplicate_empty.zip filename_duplicate_empty.zip +stdout opening 'filename_duplicate_empty.zzip' succeeded, 2 entries
diff --git a/regress/open_filename_duplicate_empty_consistency.test b/regress/open_filename_duplicate_empty_consistency.test index 43bb43e..6570195 100644 --- a/regress/open_filename_duplicate_empty_consistency.test +++ b/regress/open_filename_duplicate_empty_consistency.test
@@ -1,6 +1,7 @@ # zip_open: file opens fine even though same file name (empty file name) appears twice program tryopen -args -c filename_duplicate_empty.zip +args -c filename_duplicate_empty.zzip return 1 -file filename_duplicate_empty.zip filename_duplicate_empty.zip filename_duplicate_empty.zip -stdout some error about duplicate file names +file filename_duplicate_empty.zzip filename_duplicate_empty.zip filename_duplicate_empty.zip +stdout opening 'filename_duplicate_empty.zzip' returned error 10 +stderr 1 errors