1From f78446b14aff46db2ef27d062a275b6a01fd68b1 Mon Sep 17 00:00:00 2001
2From: Kim Kulling <kim.kulling@googlemail.com>
3Date: Tue, 19 Nov 2019 20:30:40 +0100
4Subject: [PATCH] closes https://github.com/assimp/assimp/issues/2733: update
5 of zlip to fix gcc build for v9.2.0 32 bit
6
7[Retrieved (and updated to remove .gitignore and appveyor.yml) from:
8https://github.com/assimp/assimp/commit/f78446b14aff46db2ef27d062a275b6a01fd68b1]
9Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
10---
11 contrib/zip/.gitignore          |   2 +
12 contrib/zip/CMakeLists.txt      |  83 +++++-
13 contrib/zip/README.md           |  12 +-
14 contrib/zip/appveyor.yml        |   2 +-
15 contrib/zip/src/miniz.h         | 457 ++++++++++++++++++++++++++++----
16 contrib/zip/src/zip.c           |  62 +++--
17 contrib/zip/src/zip.h           | 457 ++++++++++++++++----------------
18 contrib/zip/test/CMakeLists.txt |  27 +-
19 contrib/zip/test/test.c         |  38 ++-
20 contrib/zip/test/test_miniz.c   |  25 +-
21 10 files changed, 821 insertions(+), 344 deletions(-)
22
23diff --git a/contrib/zip/CMakeLists.txt b/contrib/zip/CMakeLists.txt
24index b46dbb1db0..77916d2e14 100644
25--- a/contrib/zip/CMakeLists.txt
26+++ b/contrib/zip/CMakeLists.txt
27@@ -1,10 +1,14 @@
28-cmake_minimum_required(VERSION 2.8)
29-project(zip)
30-enable_language(C)
31+cmake_minimum_required(VERSION 3.0)
32+
33+project(zip
34+  LANGUAGES C
35+  VERSION "0.1.15")
36 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
37
38+option(CMAKE_DISABLE_TESTING "Disable test creation" OFF)
39+
40 if (MSVC)
41-  # Use secure functions by defaualt and suppress warnings about "deprecated" functions
42+  # Use secure functions by default and suppress warnings about "deprecated" functions
43   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1")
44   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
45   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
46@@ -12,28 +16,80 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR
47         "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
48         "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
49   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic")
50+  if(ENABLE_COVERAGE)
51+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
52+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
53+  endif()
54 endif (MSVC)
55
56 # zip
57 set(SRC src/miniz.h src/zip.h src/zip.c)
58 add_library(${PROJECT_NAME} ${SRC})
59-target_include_directories(${PROJECT_NAME} INTERFACE src)
60+target_include_directories(${PROJECT_NAME} PUBLIC
61+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
62+  $<INSTALL_INTERFACE:include>
63+)
64
65 # test
66 if (NOT CMAKE_DISABLE_TESTING)
67   enable_testing()
68   add_subdirectory(test)
69   find_package(Sanitizers)
70-  add_sanitizers(${PROJECT_NAME} test.exe)
71-  add_sanitizers(${PROJECT_NAME} test_miniz.exe)
72+  add_sanitizers(${PROJECT_NAME} ${test_out} ${test_miniz_out})
73 endif()
74
75+####
76+# Installation (https://github.com/forexample/package-example) {
77+
78+set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
79+set(INCLUDE_INSTALL_DIR "include")
80+
81+set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
82+
83+# Configuration
84+set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
85+set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
86+set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
87+set(NAMESPACE "${PROJECT_NAME}::")
88+
89+# Include module with fuction 'write_basic_package_version_file'
90+include(CMakePackageConfigHelpers)
91+
92+# Note: PROJECT_VERSION is used as a VERSION
93+write_basic_package_version_file(
94+    "${VERSION_CONFIG}" COMPATIBILITY SameMajorVersion
95+)
96+
97+# Use variables:
98+#   * TARGETS_EXPORT_NAME
99+#   * PROJECT_NAME
100+configure_package_config_file(
101+    "cmake/Config.cmake.in"
102+    "${PROJECT_CONFIG}"
103+    INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
104+)
105+
106+install(
107+    FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
108+    DESTINATION "${CONFIG_INSTALL_DIR}"
109+)
110+
111+install(
112+    EXPORT "${TARGETS_EXPORT_NAME}"
113+    NAMESPACE "${NAMESPACE}"
114+    DESTINATION "${CONFIG_INSTALL_DIR}"
115+)
116+
117+# }
118+
119 install(TARGETS ${PROJECT_NAME}
120+        EXPORT ${TARGETS_EXPORT_NAME}
121         RUNTIME DESTINATION bin
122         ARCHIVE DESTINATION lib
123         LIBRARY DESTINATION lib
124-        COMPONENT library)
125-install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION include)
126+        INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR}
127+)
128+install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION ${INCLUDE_INSTALL_DIR}/zip)
129
130 # uninstall target (https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake)
131 if(NOT TARGET uninstall)
132@@ -45,3 +101,12 @@ if(NOT TARGET uninstall)
133     add_custom_target(uninstall
134         COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
135 endif()
136+
137+find_package(Doxygen)
138+if(DOXYGEN_FOUND)
139+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
140+    add_custom_target(doc
141+        ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
142+        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
143+        COMMENT "Generating API documentation with Doxygen" VERBATIM)
144+endif()
145diff --git a/contrib/zip/README.md b/contrib/zip/README.md
146index d5fb8cd203..14eb9a34c8 100644
147--- a/contrib/zip/README.md
148+++ b/contrib/zip/README.md
149@@ -71,7 +71,7 @@ int arg = 2;
150 zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);
151 ```
152
153-*   Extract a zip entry into memory.
154+* Extract a zip entry into memory.
155 ```c
156 void *buf = NULL;
157 size_t bufsize;
158@@ -89,7 +89,7 @@ zip_close(zip);
159 free(buf);
160 ```
161
162-*   Extract a zip entry into memory (no internal allocation).
163+* Extract a zip entry into memory (no internal allocation).
164 ```c
165 unsigned char *buf;
166 size_t bufsize;
167@@ -110,7 +110,7 @@ zip_close(zip);
168 free(buf);
169 ```
170
171-*   Extract a zip entry into memory using callback.
172+* Extract a zip entry into memory using callback.
173 ```c
174 struct buffer_t {
175     char *data;
176@@ -144,7 +144,7 @@ free(buf.data);
177 ```
178
179
180-*   Extract a zip entry into a file.
181+* Extract a zip entry into a file.
182 ```c
183 struct zip_t *zip = zip_open("foo.zip", 0, 'r');
184 {
185@@ -157,7 +157,7 @@ struct zip_t *zip = zip_open("foo.zip", 0, 'r');
186 zip_close(zip);
187 ```
188
189-*   List of all zip entries
190+* List of all zip entries
191 ```c
192 struct zip_t *zip = zip_open("foo.zip", 0, 'r');
193 int i, n = zip_total_entries(zip);
194@@ -174,7 +174,7 @@ for (i = 0; i < n; ++i) {
195 zip_close(zip);
196 ```
197
198-## Bindings
199+# Bindings
200 Compile zip library as a dynamic library.
201 ```shell
202 $ mkdir build
203diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h
204index 2c27a94d8d..c4fcfb83e6 100644
205--- a/contrib/zip/src/miniz.h
206+++ b/contrib/zip/src/miniz.h
207@@ -221,6 +221,7 @@
208 #ifndef MINIZ_HEADER_INCLUDED
209 #define MINIZ_HEADER_INCLUDED
210
211+#include <stdint.h>
212 #include <stdlib.h>
213
214 // Defines to completely disable specific portions of miniz.c:
215@@ -284,7 +285,8 @@
216 /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
217 #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
218 #if MINIZ_X86_OR_X64_CPU
219-/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */
220+/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient
221+ * integer loads and stores from unaligned addresses. */
222 #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
223 #define MINIZ_UNALIGNED_USE_MEMCPY
224 #else
225@@ -354,6 +356,44 @@ enum {
226   MZ_FIXED = 4
227 };
228
229+/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or
230+ * modify this enum. */
231+typedef enum {
232+  MZ_ZIP_NO_ERROR = 0,
233+  MZ_ZIP_UNDEFINED_ERROR,
234+  MZ_ZIP_TOO_MANY_FILES,
235+  MZ_ZIP_FILE_TOO_LARGE,
236+  MZ_ZIP_UNSUPPORTED_METHOD,
237+  MZ_ZIP_UNSUPPORTED_ENCRYPTION,
238+  MZ_ZIP_UNSUPPORTED_FEATURE,
239+  MZ_ZIP_FAILED_FINDING_CENTRAL_DIR,
240+  MZ_ZIP_NOT_AN_ARCHIVE,
241+  MZ_ZIP_INVALID_HEADER_OR_CORRUPTED,
242+  MZ_ZIP_UNSUPPORTED_MULTIDISK,
243+  MZ_ZIP_DECOMPRESSION_FAILED,
244+  MZ_ZIP_COMPRESSION_FAILED,
245+  MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE,
246+  MZ_ZIP_CRC_CHECK_FAILED,
247+  MZ_ZIP_UNSUPPORTED_CDIR_SIZE,
248+  MZ_ZIP_ALLOC_FAILED,
249+  MZ_ZIP_FILE_OPEN_FAILED,
250+  MZ_ZIP_FILE_CREATE_FAILED,
251+  MZ_ZIP_FILE_WRITE_FAILED,
252+  MZ_ZIP_FILE_READ_FAILED,
253+  MZ_ZIP_FILE_CLOSE_FAILED,
254+  MZ_ZIP_FILE_SEEK_FAILED,
255+  MZ_ZIP_FILE_STAT_FAILED,
256+  MZ_ZIP_INVALID_PARAMETER,
257+  MZ_ZIP_INVALID_FILENAME,
258+  MZ_ZIP_BUF_TOO_SMALL,
259+  MZ_ZIP_INTERNAL_ERROR,
260+  MZ_ZIP_FILE_NOT_FOUND,
261+  MZ_ZIP_ARCHIVE_TOO_LARGE,
262+  MZ_ZIP_VALIDATION_FAILED,
263+  MZ_ZIP_WRITE_CALLBACK_FAILED,
264+  MZ_ZIP_TOTAL_ERRORS
265+} mz_zip_error;
266+
267 // Method
268 #define MZ_DEFLATED 8
269
270@@ -696,6 +736,7 @@ typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs,
271                                     void *pBuf, size_t n);
272 typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs,
273                                      const void *pBuf, size_t n);
274+typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);
275
276 struct mz_zip_internal_state_tag;
277 typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
278@@ -707,13 +748,27 @@ typedef enum {
279   MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
280 } mz_zip_mode;
281
282-typedef struct mz_zip_archive_tag {
283+typedef enum {
284+  MZ_ZIP_TYPE_INVALID = 0,
285+  MZ_ZIP_TYPE_USER,
286+  MZ_ZIP_TYPE_MEMORY,
287+  MZ_ZIP_TYPE_HEAP,
288+  MZ_ZIP_TYPE_FILE,
289+  MZ_ZIP_TYPE_CFILE,
290+  MZ_ZIP_TOTAL_TYPES
291+} mz_zip_type;
292+
293+typedef struct {
294   mz_uint64 m_archive_size;
295   mz_uint64 m_central_directory_file_ofs;
296-  mz_uint m_total_files;
297+
298+  /* We only support up to UINT32_MAX files in zip64 mode. */
299+  mz_uint32 m_total_files;
300   mz_zip_mode m_zip_mode;
301+  mz_zip_type m_zip_type;
302+  mz_zip_error m_last_error;
303
304-  mz_uint m_file_offset_alignment;
305+  mz_uint64 m_file_offset_alignment;
306
307   mz_alloc_func m_pAlloc;
308   mz_free_func m_pFree;
309@@ -722,6 +777,7 @@ typedef struct mz_zip_archive_tag {
310
311   mz_file_read_func m_pRead;
312   mz_file_write_func m_pWrite;
313+  mz_file_needs_keepalive m_pNeeds_keepalive;
314   void *m_pIO_opaque;
315
316   mz_zip_internal_state *m_pState;
317@@ -1263,6 +1319,9 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits,
318                                                 int strategy);
319 #endif // #ifndef MINIZ_NO_ZLIB_APIS
320
321+#define MZ_UINT16_MAX (0xFFFFU)
322+#define MZ_UINT32_MAX (0xFFFFFFFFU)
323+
324 #ifdef __cplusplus
325 }
326 #endif
327@@ -1311,6 +1370,11 @@ typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
328    ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
329 #endif
330
331+#define MZ_READ_LE64(p)                                                        \
332+  (((mz_uint64)MZ_READ_LE32(p)) |                                              \
333+   (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32)))       \
334+    << 32U))
335+
336 #ifdef _MSC_VER
337 #define MZ_FORCEINLINE __forceinline
338 #elif defined(__GNUC__)
339@@ -4160,6 +4224,17 @@ enum {
340   MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,
341   MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,
342   MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
343+
344+  /* ZIP64 archive identifier and record sizes */
345+  MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50,
346+  MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50,
347+  MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56,
348+  MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20,
349+  MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001,
350+  MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50,
351+  MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24,
352+  MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16,
353+
354   // Central directory header record offsets
355   MZ_ZIP_CDH_SIG_OFS = 0,
356   MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,
357@@ -4199,6 +4274,31 @@ enum {
358   MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,
359   MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,
360   MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
361+
362+  /* ZIP64 End of central directory locator offsets */
363+  MZ_ZIP64_ECDL_SIG_OFS = 0,                    /* 4 bytes */
364+  MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4,          /* 4 bytes */
365+  MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8,  /* 8 bytes */
366+  MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */
367+
368+  /* ZIP64 End of central directory header offsets */
369+  MZ_ZIP64_ECDH_SIG_OFS = 0,                       /* 4 bytes */
370+  MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4,            /* 8 bytes */
371+  MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12,          /* 2 bytes */
372+  MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14,           /* 2 bytes */
373+  MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16,            /* 4 bytes */
374+  MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20,            /* 4 bytes */
375+  MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */
376+  MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32,       /* 8 bytes */
377+  MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40,                /* 8 bytes */
378+  MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48,                 /* 8 bytes */
379+  MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0,
380+  MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10,
381+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1,
382+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32,
383+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64,
384+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192,
385+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11
386 };
387
388 typedef struct {
389@@ -4211,7 +4311,24 @@ struct mz_zip_internal_state_tag {
390   mz_zip_array m_central_dir;
391   mz_zip_array m_central_dir_offsets;
392   mz_zip_array m_sorted_central_dir_offsets;
393+
394+  /* The flags passed in when the archive is initially opened. */
395+  uint32_t m_init_flags;
396+
397+  /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc.
398+   */
399+  mz_bool m_zip64;
400+
401+  /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64
402+   * will also be slammed to true too, even if we didn't find a zip64 end of
403+   * central dir header, etc.) */
404+  mz_bool m_zip64_has_extended_info_fields;
405+
406+  /* These fields are used by the file, FILE, memory, and memory/heap read/write
407+   * helpers. */
408   MZ_FILE *m_pFile;
409+  mz_uint64 m_file_archive_start_ofs;
410+
411   void *m_pMem;
412   size_t m_mem_size;
413   size_t m_mem_capacity;
414@@ -4363,6 +4480,13 @@ static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time,
415 #endif /* #ifndef MINIZ_NO_STDIO */
416 #endif /* #ifndef MINIZ_NO_TIME */
417
418+static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip,
419+                                               mz_zip_error err_num) {
420+  if (pZip)
421+    pZip->m_last_error = err_num;
422+  return MZ_FALSE;
423+}
424+
425 static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip,
426                                            mz_uint32 flags) {
427   (void)flags;
428@@ -4480,127 +4604,346 @@ mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) {
429   }
430 }
431
432-static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip,
433-                                              mz_uint32 flags) {
434-  mz_uint cdir_size, num_this_disk, cdir_disk_index;
435-  mz_uint64 cdir_ofs;
436+static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip,
437+                                               mz_uint32 record_sig,
438+                                               mz_uint32 record_size,
439+                                               mz_int64 *pOfs) {
440   mz_int64 cur_file_ofs;
441-  const mz_uint8 *p;
442   mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
443   mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
444-  mz_bool sort_central_dir =
445-      ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
446-  // Basic sanity checks - reject files which are too small, and check the first
447-  // 4 bytes of the file to make sure a local header is there.
448-  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
449+
450+  /* Basic sanity checks - reject files which are too small */
451+  if (pZip->m_archive_size < record_size)
452     return MZ_FALSE;
453-  // Find the end of central directory record by scanning the file from the end
454-  // towards the beginning.
455+
456+  /* Find the record by scanning the file from the end towards the beginning. */
457   cur_file_ofs =
458       MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
459   for (;;) {
460     int i,
461         n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
462+
463     if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
464       return MZ_FALSE;
465-    for (i = n - 4; i >= 0; --i)
466-      if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
467-        break;
468+
469+    for (i = n - 4; i >= 0; --i) {
470+      mz_uint s = MZ_READ_LE32(pBuf + i);
471+      if (s == record_sig) {
472+        if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
473+          break;
474+      }
475+    }
476+
477     if (i >= 0) {
478       cur_file_ofs += i;
479       break;
480     }
481+
482+    /* Give up if we've searched the entire file, or we've gone back "too far"
483+     * (~64kb) */
484     if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >=
485-                            (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
486+                            (MZ_UINT16_MAX + record_size)))
487       return MZ_FALSE;
488+
489     cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
490   }
491-  // Read and verify the end of central directory record.
492+
493+  *pOfs = cur_file_ofs;
494+  return MZ_TRUE;
495+}
496+
497+static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip,
498+                                              mz_uint flags) {
499+  mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0,
500+          cdir_disk_index = 0;
501+  mz_uint64 cdir_ofs = 0;
502+  mz_int64 cur_file_ofs = 0;
503+  const mz_uint8 *p;
504+
505+  mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
506+  mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
507+  mz_bool sort_central_dir =
508+      ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
509+  mz_uint32 zip64_end_of_central_dir_locator_u32
510+      [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) /
511+       sizeof(mz_uint32)];
512+  mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32;
513+
514+  mz_uint32 zip64_end_of_central_dir_header_u32
515+      [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) /
516+       sizeof(mz_uint32)];
517+  mz_uint8 *pZip64_end_of_central_dir =
518+      (mz_uint8 *)zip64_end_of_central_dir_header_u32;
519+
520+  mz_uint64 zip64_end_of_central_dir_ofs = 0;
521+
522+  /* Basic sanity checks - reject files which are too small, and check the first
523+   * 4 bytes of the file to make sure a local header is there. */
524+  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
525+    return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
526+
527+  if (!mz_zip_reader_locate_header_sig(
528+          pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG,
529+          MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs))
530+    return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR);
531+
532+  /* Read and verify the end of central directory record. */
533   if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf,
534                     MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) !=
535       MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
536-    return MZ_FALSE;
537-  if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) !=
538-       MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
539-      ((pZip->m_total_files =
540-            MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) !=
541-       MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
542-    return MZ_FALSE;
543+    return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
544+
545+  if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) !=
546+      MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
547+    return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
548+
549+  if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE +
550+                       MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) {
551+    if (pZip->m_pRead(pZip->m_pIO_opaque,
552+                      cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE,
553+                      pZip64_locator,
554+                      MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) ==
555+        MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) {
556+      if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) ==
557+          MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) {
558+        zip64_end_of_central_dir_ofs = MZ_READ_LE64(
559+            pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS);
560+        if (zip64_end_of_central_dir_ofs >
561+            (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
562+          return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
563+
564+        if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs,
565+                          pZip64_end_of_central_dir,
566+                          MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) ==
567+            MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) {
568+          if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) ==
569+              MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) {
570+            pZip->m_pState->m_zip64 = MZ_TRUE;
571+          }
572+        }
573+      }
574+    }
575+  }
576
577+  pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS);
578+  cdir_entries_on_this_disk =
579+      MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
580   num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
581   cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
582+  cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS);
583+  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
584+
585+  if (pZip->m_pState->m_zip64) {
586+    mz_uint32 zip64_total_num_of_disks =
587+        MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS);
588+    mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(
589+        pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS);
590+    mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(
591+        pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
592+    mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(
593+        pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS);
594+    mz_uint64 zip64_size_of_central_directory =
595+        MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS);
596+
597+    if (zip64_size_of_end_of_central_dir_record <
598+        (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12))
599+      return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
600+
601+    if (zip64_total_num_of_disks != 1U)
602+      return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
603+
604+    /* Check for miniz's practical limits */
605+    if (zip64_cdir_total_entries > MZ_UINT32_MAX)
606+      return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
607+
608+    pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries;
609+
610+    if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX)
611+      return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
612+
613+    cdir_entries_on_this_disk =
614+        (mz_uint32)zip64_cdir_total_entries_on_this_disk;
615+
616+    /* Check for miniz's current practical limits (sorry, this should be enough
617+     * for millions of files) */
618+    if (zip64_size_of_central_directory > MZ_UINT32_MAX)
619+      return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
620+
621+    cdir_size = (mz_uint32)zip64_size_of_central_directory;
622+
623+    num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir +
624+                                 MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS);
625+
626+    cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir +
627+                                   MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS);
628+
629+    cdir_ofs =
630+        MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS);
631+  }
632+
633+  if (pZip->m_total_files != cdir_entries_on_this_disk)
634+    return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
635+
636   if (((num_this_disk | cdir_disk_index) != 0) &&
637       ((num_this_disk != 1) || (cdir_disk_index != 1)))
638-    return MZ_FALSE;
639+    return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
640
641-  if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) <
642-      pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
643-    return MZ_FALSE;
644+  if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
645+    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
646
647-  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
648   if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
649-    return MZ_FALSE;
650+    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
651
652   pZip->m_central_directory_file_ofs = cdir_ofs;
653
654   if (pZip->m_total_files) {
655     mz_uint i, n;
656-
657-    // Read the entire central directory into a heap block, and allocate another
658-    // heap block to hold the unsorted central dir file record offsets, and
659-    // another to hold the sorted indices.
660+    /* Read the entire central directory into a heap block, and allocate another
661+     * heap block to hold the unsorted central dir file record offsets, and
662+     * possibly another to hold the sorted indices. */
663     if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size,
664                               MZ_FALSE)) ||
665         (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets,
666                               pZip->m_total_files, MZ_FALSE)))
667-      return MZ_FALSE;
668+      return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
669
670     if (sort_central_dir) {
671       if (!mz_zip_array_resize(pZip,
672                                &pZip->m_pState->m_sorted_central_dir_offsets,
673                                pZip->m_total_files, MZ_FALSE))
674-        return MZ_FALSE;
675+        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
676     }
677
678     if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs,
679                       pZip->m_pState->m_central_dir.m_p,
680                       cdir_size) != cdir_size)
681-      return MZ_FALSE;
682+      return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
683
684-    // Now create an index into the central directory file records, do some
685-    // basic sanity checking on each record, and check for zip64 entries (which
686-    // are not yet supported).
687+    /* Now create an index into the central directory file records, do some
688+     * basic sanity checking on each record */
689     p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
690     for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) {
691-      mz_uint total_header_size, comp_size, decomp_size, disk_index;
692+      mz_uint total_header_size, disk_index, bit_flags, filename_size,
693+          ext_data_size;
694+      mz_uint64 comp_size, decomp_size, local_header_ofs;
695+
696       if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) ||
697           (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
698-        return MZ_FALSE;
699+        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
700+
701       MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32,
702                            i) =
703           (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
704+
705       if (sort_central_dir)
706         MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets,
707                              mz_uint32, i) = i;
708+
709       comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
710       decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
711-      if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) &&
712-           (decomp_size != comp_size)) ||
713-          (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) ||
714-          (comp_size == 0xFFFFFFFF))
715-        return MZ_FALSE;
716+      local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
717+      filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
718+      ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
719+
720+      if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&
721+          (ext_data_size) &&
722+          (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) ==
723+           MZ_UINT32_MAX)) {
724+        /* Attempt to find zip64 extended information field in the entry's extra
725+         * data */
726+        mz_uint32 extra_size_remaining = ext_data_size;
727+
728+        if (extra_size_remaining) {
729+          const mz_uint8 *pExtra_data;
730+          void *buf = NULL;
731+
732+          if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size >
733+              n) {
734+            buf = MZ_MALLOC(ext_data_size);
735+            if (buf == NULL)
736+              return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
737+
738+            if (pZip->m_pRead(pZip->m_pIO_opaque,
739+                              cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
740+                                  filename_size,
741+                              buf, ext_data_size) != ext_data_size) {
742+              MZ_FREE(buf);
743+              return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
744+            }
745+
746+            pExtra_data = (mz_uint8 *)buf;
747+          } else {
748+            pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;
749+          }
750+
751+          do {
752+            mz_uint32 field_id;
753+            mz_uint32 field_data_size;
754+
755+            if (extra_size_remaining < (sizeof(mz_uint16) * 2)) {
756+              MZ_FREE(buf);
757+              return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
758+            }
759+
760+            field_id = MZ_READ_LE16(pExtra_data);
761+            field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
762+
763+            if ((field_data_size + sizeof(mz_uint16) * 2) >
764+                extra_size_remaining) {
765+              MZ_FREE(buf);
766+              return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
767+            }
768+
769+            if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) {
770+              /* Ok, the archive didn't have any zip64 headers but it uses a
771+               * zip64 extended information field so mark it as zip64 anyway
772+               * (this can occur with infozip's zip util when it reads
773+               * compresses files from stdin). */
774+              pZip->m_pState->m_zip64 = MZ_TRUE;
775+              pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE;
776+              break;
777+            }
778+
779+            pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
780+            extra_size_remaining =
781+                extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
782+          } while (extra_size_remaining);
783+
784+          MZ_FREE(buf);
785+        }
786+      }
787+
788+      /* I've seen archives that aren't marked as zip64 that uses zip64 ext
789+       * data, argh */
790+      if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) {
791+        if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) &&
792+             (decomp_size != comp_size)) ||
793+            (decomp_size && !comp_size))
794+          return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
795+      }
796+
797       disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
798-      if ((disk_index != num_this_disk) && (disk_index != 1))
799-        return MZ_FALSE;
800-      if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) +
801-           MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
802-        return MZ_FALSE;
803+      if ((disk_index == MZ_UINT16_MAX) ||
804+          ((disk_index != num_this_disk) && (disk_index != 1)))
805+        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
806+
807+      if (comp_size != MZ_UINT32_MAX) {
808+        if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) +
809+             MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
810+          return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
811+      }
812+
813+      bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
814+      if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED)
815+        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
816+
817       if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
818                                MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) +
819                                MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) +
820                                MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) >
821           n)
822-        return MZ_FALSE;
823+        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
824+
825       n -= total_header_size;
826       p += total_header_size;
827     }
828diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c
829index ff3a8fe1e6..1abcfd8fd1 100644
830--- a/contrib/zip/src/zip.c
831+++ b/contrib/zip/src/zip.c
832@@ -24,7 +24,6 @@
833   ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) &&   \
834    (P)[1] == ':')
835 #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
836-#define ISSLASH(C) ((C) == '/' || (C) == '\\')
837
838 #else
839
840@@ -48,7 +47,7 @@ int symlink(const char *target, const char *linkpath); // needed on Linux
841 #endif
842
843 #ifndef ISSLASH
844-#define ISSLASH(C) ((C) == '/')
845+#define ISSLASH(C) ((C) == '/' || (C) == '\\')
846 #endif
847
848 #define CLEANUP(ptr)                                                           \
849@@ -78,26 +77,34 @@ static const char *base_name(const char *name) {
850   return base;
851 }
852
853-static int mkpath(const char *path) {
854-  char const *p;
855+static int mkpath(char *path) {
856+  char *p;
857   char npath[MAX_PATH + 1];
858   int len = 0;
859   int has_device = HAS_DEVICE(path);
860
861   memset(npath, 0, MAX_PATH + 1);
862-
863-#ifdef _WIN32
864-  // only on windows fix the path
865-  npath[0] = path[0];
866-  npath[1] = path[1];
867-  len = 2;
868-#endif // _WIN32
869-
870+  if (has_device) {
871+    // only on windows
872+    npath[0] = path[0];
873+    npath[1] = path[1];
874+    len = 2;
875+  }
876   for (p = path + len; *p && len < MAX_PATH; p++) {
877     if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) {
878-      if (MKDIR(npath) == -1)
879-        if (errno != EEXIST)
880+#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
881+    defined(__MINGW32__)
882+#else
883+      if ('\\' == *p) {
884+        *p = '/';
885+      }
886+#endif
887+
888+      if (MKDIR(npath) == -1) {
889+        if (errno != EEXIST) {
890           return -1;
891+        }
892+      }
893     }
894     npath[len++] = *p;
895   }
896@@ -279,7 +286,14 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) {
897   zip->entry.header_offset = zip->archive.m_archive_size;
898   memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
899   zip->entry.method = 0;
900+
901+  // UNIX or APPLE
902+#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19
903+  // regular file with rw-r--r-- persmissions
904+  zip->entry.external_attr = (mz_uint32)(0100644) << 16;
905+#else
906   zip->entry.external_attr = 0;
907+#endif
908
909   num_alignment_padding_bytes =
910       mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
911@@ -660,7 +674,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
912   }
913
914   if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
915-  buf, bufsize, 0, NULL,  0)) {
916+                                             buf, bufsize, 0, NULL, 0)) {
917     return -1;
918   }
919
920@@ -670,10 +684,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
921 int zip_entry_fread(struct zip_t *zip, const char *filename) {
922   mz_zip_archive *pzip = NULL;
923   mz_uint idx;
924-#if defined(_MSC_VER)
925-#else
926   mz_uint32 xattr = 0;
927-#endif
928   mz_zip_archive_file_stat info;
929
930   if (!zip) {
931@@ -875,12 +886,19 @@ int zip_extract(const char *zipname, const char *dir,
932       goto out;
933     }
934
935-    if ((((info.m_version_made_by >> 8) == 3) || ((info.m_version_made_by >> 8) == 19)) // if zip is produced on Unix or macOS (3 and 19 from section 4.4.2.2 of zip standard)
936-        && info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 is directory)
937+    if ((((info.m_version_made_by >> 8) == 3) ||
938+         ((info.m_version_made_by >> 8) ==
939+          19)) // if zip is produced on Unix or macOS (3 and 19 from
940+               // section 4.4.2.2 of zip standard)
941+        && info.m_external_attr &
942+               (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40
943+                               // is directory)
944 #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
945     defined(__MINGW32__)
946-#else
947-      if (info.m_uncomp_size > MAX_PATH || !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, MAX_PATH, 0, NULL, 0)) {
948+#else
949+      if (info.m_uncomp_size > MAX_PATH ||
950+          !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to,
951+                                                 MAX_PATH, 0, NULL, 0)) {
952         goto out;
953       }
954       symlink_to[info.m_uncomp_size] = '\0';
955diff --git a/contrib/zip/src/zip.h b/contrib/zip/src/zip.h
956index 5f39df50ad..a48d64d6de 100644
957--- a/contrib/zip/src/zip.h
958+++ b/contrib/zip/src/zip.h
959@@ -20,241 +20,240 @@ extern "C" {
960 #endif
961
962 #if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) &&               \
963-    !defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(__ssize_t_defined)
964-#define _SSIZE_T
965+    !defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) &&              \
966+    !defined(_SSIZE_T) && !defined(_SSIZE_T_)
967+
968 // 64-bit Windows is the only mainstream platform
969 // where sizeof(long) != sizeof(void*)
970 #ifdef _WIN64
971-typedef long long  ssize_t;  /* byte count or error */
972+typedef long long ssize_t; /* byte count or error */
973 #else
974-typedef long  ssize_t;  /* byte count or error */
975+typedef long ssize_t; /* byte count or error */
976 #endif
977+
978+#define _SSIZE_T_DEFINED
979+#define _SSIZE_T_DEFINED_
980+#define __DEFINED_ssize_t
981+#define __ssize_t_defined
982+#define _SSIZE_T
983+#define _SSIZE_T_
984+
985 #endif
986
987 #ifndef MAX_PATH
988 #define MAX_PATH 32767 /* # chars in a path name including NULL */
989 #endif
990
991+/**
992+ * @mainpage
993+ *
994+ * Documenation for @ref zip.
995+ */
996+
997+/**
998+ * @addtogroup zip
999+ * @{
1000+ */
1001+
1002+/**
1003+ * Default zip compression level.
1004+ */
1005+
1006 #define ZIP_DEFAULT_COMPRESSION_LEVEL 6
1007
1008-/*
1009-  This data structure is used throughout the library to represent zip archive
1010-  - forward declaration.
1011-*/
1012+/**
1013+ * @struct zip_t
1014+ *
1015+ * This data structure is used throughout the library to represent zip archive -
1016+ * forward declaration.
1017+ */
1018 struct zip_t;
1019
1020-/*
1021-  Opens zip archive with compression level using the given mode.
1022-
1023-  Args:
1024-    zipname: zip archive file name.
1025-    level: compression level (0-9 are the standard zlib-style levels).
1026-    mode: file access mode.
1027-        'r': opens a file for reading/extracting (the file must exists).
1028-        'w': creates an empty file for writing.
1029-        'a': appends to an existing archive.
1030-
1031-  Returns:
1032-    The zip archive handler or NULL on error
1033-*/
1034+/**
1035+ * Opens zip archive with compression level using the given mode.
1036+ *
1037+ * @param zipname zip archive file name.
1038+ * @param level compression level (0-9 are the standard zlib-style levels).
1039+ * @param mode file access mode.
1040+ *        - 'r': opens a file for reading/extracting (the file must exists).
1041+ *        - 'w': creates an empty file for writing.
1042+ *        - 'a': appends to an existing archive.
1043+ *
1044+ * @return the zip archive handler or NULL on error
1045+ */
1046 extern struct zip_t *zip_open(const char *zipname, int level, char mode);
1047
1048-/*
1049-  Closes the zip archive, releases resources - always finalize.
1050-
1051-  Args:
1052-    zip: zip archive handler.
1053-*/
1054+/**
1055+ * Closes the zip archive, releases resources - always finalize.
1056+ *
1057+ * @param zip zip archive handler.
1058+ */
1059 extern void zip_close(struct zip_t *zip);
1060
1061-/*
1062-  Opens an entry by name in the zip archive.
1063-  For zip archive opened in 'w' or 'a' mode the function will append
1064-  a new entry. In readonly mode the function tries to locate the entry
1065-  in global dictionary.
1066-
1067-  Args:
1068-    zip: zip archive handler.
1069-    entryname: an entry name in local dictionary.
1070-
1071-  Returns:
1072-    The return code - 0 on success, negative number (< 0) on error.
1073-*/
1074+/**
1075+ * Opens an entry by name in the zip archive.
1076+ *
1077+ * For zip archive opened in 'w' or 'a' mode the function will append
1078+ * a new entry. In readonly mode the function tries to locate the entry
1079+ * in global dictionary.
1080+ *
1081+ * @param zip zip archive handler.
1082+ * @param entryname an entry name in local dictionary.
1083+ *
1084+ * @return the return code - 0 on success, negative number (< 0) on error.
1085+ */
1086 extern int zip_entry_open(struct zip_t *zip, const char *entryname);
1087
1088-/*
1089-  Opens a new entry by index in the zip archive.
1090-  This function is only valid if zip archive was opened in 'r' (readonly) mode.
1091-
1092-  Args:
1093-    zip: zip archive handler.
1094-    index: index in local dictionary.
1095-
1096-  Returns:
1097-    The return code - 0 on success, negative number (< 0) on error.
1098-*/
1099+/**
1100+ * Opens a new entry by index in the zip archive.
1101+ *
1102+ * This function is only valid if zip archive was opened in 'r' (readonly) mode.
1103+ *
1104+ * @param zip zip archive handler.
1105+ * @param index index in local dictionary.
1106+ *
1107+ * @return the return code - 0 on success, negative number (< 0) on error.
1108+ */
1109 extern int zip_entry_openbyindex(struct zip_t *zip, int index);
1110
1111-/*
1112-  Closes a zip entry, flushes buffer and releases resources.
1113-
1114-  Args:
1115-    zip: zip archive handler.
1116-
1117-  Returns:
1118-    The return code - 0 on success, negative number (< 0) on error.
1119-*/
1120+/**
1121+ * Closes a zip entry, flushes buffer and releases resources.
1122+ *
1123+ * @param zip zip archive handler.
1124+ *
1125+ * @return the return code - 0 on success, negative number (< 0) on error.
1126+ */
1127 extern int zip_entry_close(struct zip_t *zip);
1128
1129-/*
1130-  Returns a local name of the current zip entry.
1131-  The main difference between user's entry name and local entry name
1132-  is optional relative path.
1133-  Following .ZIP File Format Specification - the path stored MUST not contain
1134-  a drive or device letter, or a leading slash.
1135-  All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
1136-  for compatibility with Amiga and UNIX file systems etc.
1137-
1138-  Args:
1139-    zip: zip archive handler.
1140-
1141-  Returns:
1142-    The pointer to the current zip entry name, or NULL on error.
1143-*/
1144+/**
1145+ * Returns a local name of the current zip entry.
1146+ *
1147+ * The main difference between user's entry name and local entry name
1148+ * is optional relative path.
1149+ * Following .ZIP File Format Specification - the path stored MUST not contain
1150+ * a drive or device letter, or a leading slash.
1151+ * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
1152+ * for compatibility with Amiga and UNIX file systems etc.
1153+ *
1154+ * @param zip: zip archive handler.
1155+ *
1156+ * @return the pointer to the current zip entry name, or NULL on error.
1157+ */
1158 extern const char *zip_entry_name(struct zip_t *zip);
1159
1160-/*
1161-  Returns an index of the current zip entry.
1162-
1163-  Args:
1164-    zip: zip archive handler.
1165-
1166-  Returns:
1167-    The index on success, negative number (< 0) on error.
1168-*/
1169+/**
1170+ * Returns an index of the current zip entry.
1171+ *
1172+ * @param zip zip archive handler.
1173+ *
1174+ * @return the index on success, negative number (< 0) on error.
1175+ */
1176 extern int zip_entry_index(struct zip_t *zip);
1177
1178-/*
1179-  Determines if the current zip entry is a directory entry.
1180-
1181-  Args:
1182-    zip: zip archive handler.
1183-
1184-  Returns:
1185-    The return code - 1 (true), 0 (false), negative number (< 0) on error.
1186-*/
1187+/**
1188+ * Determines if the current zip entry is a directory entry.
1189+ *
1190+ * @param zip zip archive handler.
1191+ *
1192+ * @return the return code - 1 (true), 0 (false), negative number (< 0) on
1193+ *         error.
1194+ */
1195 extern int zip_entry_isdir(struct zip_t *zip);
1196
1197-/*
1198-  Returns an uncompressed size of the current zip entry.
1199-
1200-  Args:
1201-    zip: zip archive handler.
1202-
1203-  Returns:
1204-    The uncompressed size in bytes.
1205-*/
1206+/**
1207+ * Returns an uncompressed size of the current zip entry.
1208+ *
1209+ * @param zip zip archive handler.
1210+ *
1211+ * @return the uncompressed size in bytes.
1212+ */
1213 extern unsigned long long zip_entry_size(struct zip_t *zip);
1214
1215-/*
1216-  Returns CRC-32 checksum of the current zip entry.
1217-
1218-  Args:
1219-    zip: zip archive handler.
1220-
1221-  Returns:
1222-    The CRC-32 checksum.
1223-*/
1224+/**
1225+ * Returns CRC-32 checksum of the current zip entry.
1226+ *
1227+ * @param zip zip archive handler.
1228+ *
1229+ * @return the CRC-32 checksum.
1230+ */
1231 extern unsigned int zip_entry_crc32(struct zip_t *zip);
1232
1233-/*
1234-  Compresses an input buffer for the current zip entry.
1235-
1236-  Args:
1237-    zip: zip archive handler.
1238-    buf: input buffer.
1239-    bufsize: input buffer size (in bytes).
1240-
1241-  Returns:
1242-    The return code - 0 on success, negative number (< 0) on error.
1243-*/
1244+/**
1245+ * Compresses an input buffer for the current zip entry.
1246+ *
1247+ * @param zip zip archive handler.
1248+ * @param buf input buffer.
1249+ * @param bufsize input buffer size (in bytes).
1250+ *
1251+ * @return the return code - 0 on success, negative number (< 0) on error.
1252+ */
1253 extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
1254
1255-/*
1256-  Compresses a file for the current zip entry.
1257-
1258-  Args:
1259-    zip: zip archive handler.
1260-    filename: input file.
1261-
1262-  Returns:
1263-    The return code - 0 on success, negative number (< 0) on error.
1264-*/
1265+/**
1266+ * Compresses a file for the current zip entry.
1267+ *
1268+ * @param zip zip archive handler.
1269+ * @param filename input file.
1270+ *
1271+ * @return the return code - 0 on success, negative number (< 0) on error.
1272+ */
1273 extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
1274
1275-/*
1276-  Extracts the current zip entry into output buffer.
1277-  The function allocates sufficient memory for a output buffer.
1278-
1279-  Args:
1280-    zip: zip archive handler.
1281-    buf: output buffer.
1282-    bufsize: output buffer size (in bytes).
1283-
1284-  Note:
1285-    - remember to release memory allocated for a output buffer.
1286-    - for large entries, please take a look at zip_entry_extract function.
1287-
1288-  Returns:
1289-    The return code - the number of bytes actually read on success.
1290-    Otherwise a -1 on error.
1291-*/
1292+/**
1293+ * Extracts the current zip entry into output buffer.
1294+ *
1295+ * The function allocates sufficient memory for a output buffer.
1296+ *
1297+ * @param zip zip archive handler.
1298+ * @param buf output buffer.
1299+ * @param bufsize output buffer size (in bytes).
1300+ *
1301+ * @note remember to release memory allocated for a output buffer.
1302+ *       for large entries, please take a look at zip_entry_extract function.
1303+ *
1304+ * @return the return code - the number of bytes actually read on success.
1305+ *         Otherwise a -1 on error.
1306+ */
1307 extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
1308
1309-/*
1310-  Extracts the current zip entry into a memory buffer using no memory
1311-  allocation.
1312-
1313-  Args:
1314-    zip: zip archive handler.
1315-    buf: preallocated output buffer.
1316-    bufsize: output buffer size (in bytes).
1317-
1318-  Note:
1319-    - ensure supplied output buffer is large enough.
1320-    - zip_entry_size function (returns uncompressed size for the current entry)
1321-      can be handy to estimate how big buffer is needed.
1322-    - for large entries, please take a look at zip_entry_extract function.
1323-
1324-  Returns:
1325-    The return code - the number of bytes actually read on success.
1326-    Otherwise a -1 on error (e.g. bufsize is not large enough).
1327-*/
1328-extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize);
1329-
1330-/*
1331-  Extracts the current zip entry into output file.
1332-
1333-  Args:
1334-    zip: zip archive handler.
1335-    filename: output file.
1336-
1337-  Returns:
1338-    The return code - 0 on success, negative number (< 0) on error.
1339-*/
1340+/**
1341+ * Extracts the current zip entry into a memory buffer using no memory
1342+ * allocation.
1343+ *
1344+ * @param zip zip archive handler.
1345+ * @param buf preallocated output buffer.
1346+ * @param bufsize output buffer size (in bytes).
1347+ *
1348+ * @note ensure supplied output buffer is large enough.
1349+ *       zip_entry_size function (returns uncompressed size for the current
1350+ *       entry) can be handy to estimate how big buffer is needed. for large
1351+ * entries, please take a look at zip_entry_extract function.
1352+ *
1353+ * @return the return code - the number of bytes actually read on success.
1354+ *         Otherwise a -1 on error (e.g. bufsize is not large enough).
1355+ */
1356+extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
1357+                                     size_t bufsize);
1358+
1359+/**
1360+ * Extracts the current zip entry into output file.
1361+ *
1362+ * @param zip zip archive handler.
1363+ * @param filename output file.
1364+ *
1365+ * @return the return code - 0 on success, negative number (< 0) on error.
1366+ */
1367 extern int zip_entry_fread(struct zip_t *zip, const char *filename);
1368
1369-/*
1370-  Extracts the current zip entry using a callback function (on_extract).
1371-
1372-  Args:
1373-    zip: zip archive handler.
1374-    on_extract: callback function.
1375-    arg: opaque pointer (optional argument,
1376-                         which you can pass to the on_extract callback)
1377-
1378-   Returns:
1379-    The return code - 0 on success, negative number (< 0) on error.
1380+/**
1381+ * Extracts the current zip entry using a callback function (on_extract).
1382+ *
1383+ * @param zip zip archive handler.
1384+ * @param on_extract callback function.
1385+ * @param arg opaque pointer (optional argument, which you can pass to the
1386+ *        on_extract callback)
1387+ *
1388+ * @return the return code - 0 on success, negative number (< 0) on error.
1389  */
1390 extern int
1391 zip_entry_extract(struct zip_t *zip,
1392@@ -262,53 +261,49 @@ zip_entry_extract(struct zip_t *zip,
1393                                        const void *data, size_t size),
1394                   void *arg);
1395
1396-/*
1397-  Returns the number of all entries (files and directories) in the zip archive.
1398-
1399-  Args:
1400-    zip: zip archive handler.
1401-
1402-  Returns:
1403-    The return code - the number of entries on success,
1404-    negative number (< 0) on error.
1405-*/
1406+/**
1407+ * Returns the number of all entries (files and directories) in the zip archive.
1408+ *
1409+ * @param zip zip archive handler.
1410+ *
1411+ * @return the return code - the number of entries on success, negative number
1412+ *         (< 0) on error.
1413+ */
1414 extern int zip_total_entries(struct zip_t *zip);
1415
1416-/*
1417-  Creates a new archive and puts files into a single zip archive.
1418-
1419-  Args:
1420-    zipname: zip archive file.
1421-    filenames: input files.
1422-    len: number of input files.
1423-
1424-  Returns:
1425-    The return code - 0 on success, negative number (< 0) on error.
1426-*/
1427+/**
1428+ * Creates a new archive and puts files into a single zip archive.
1429+ *
1430+ * @param zipname zip archive file.
1431+ * @param filenames input files.
1432+ * @param len: number of input files.
1433+ *
1434+ * @return the return code - 0 on success, negative number (< 0) on error.
1435+ */
1436 extern int zip_create(const char *zipname, const char *filenames[], size_t len);
1437
1438-/*
1439-  Extracts a zip archive file into directory.
1440-
1441-  If on_extract_entry is not NULL, the callback will be called after
1442-  successfully extracted each zip entry.
1443-  Returning a negative value from the callback will cause abort and return an
1444-  error. The last argument (void *arg) is optional, which you can use to pass
1445-  data to the on_extract_entry callback.
1446-
1447-  Args:
1448-    zipname: zip archive file.
1449-    dir: output directory.
1450-    on_extract_entry: on extract callback.
1451-    arg: opaque pointer.
1452-
1453-  Returns:
1454-    The return code - 0 on success, negative number (< 0) on error.
1455-*/
1456+/**
1457+ * Extracts a zip archive file into directory.
1458+ *
1459+ * If on_extract_entry is not NULL, the callback will be called after
1460+ * successfully extracted each zip entry.
1461+ * Returning a negative value from the callback will cause abort and return an
1462+ * error. The last argument (void *arg) is optional, which you can use to pass
1463+ * data to the on_extract_entry callback.
1464+ *
1465+ * @param zipname zip archive file.
1466+ * @param dir output directory.
1467+ * @param on_extract_entry on extract callback.
1468+ * @param arg opaque pointer.
1469+ *
1470+ * @return the return code - 0 on success, negative number (< 0) on error.
1471+ */
1472 extern int zip_extract(const char *zipname, const char *dir,
1473                        int (*on_extract_entry)(const char *filename, void *arg),
1474                        void *arg);
1475
1476+/** @} */
1477+
1478 #ifdef __cplusplus
1479 }
1480 #endif
1481diff --git a/contrib/zip/test/CMakeLists.txt b/contrib/zip/test/CMakeLists.txt
1482index 9b2a8db106..cc060b00fe 100644
1483--- a/contrib/zip/test/CMakeLists.txt
1484+++ b/contrib/zip/test/CMakeLists.txt
1485@@ -1,19 +1,16 @@
1486 cmake_minimum_required(VERSION 2.8)
1487
1488-if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
1489-  if(ENABLE_COVERAGE)
1490-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ")
1491-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
1492-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs")
1493-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage")
1494-    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
1495-  endif()
1496-endif ()
1497-
1498 # test
1499-include_directories(../src)
1500-add_executable(test.exe test.c ../src/zip.c)
1501-add_executable(test_miniz.exe test_miniz.c)
1502+set(test_out test.out)
1503+set(test_miniz_out test_miniz.out)
1504+
1505+add_executable(${test_out} test.c)
1506+target_link_libraries(${test_out} zip)
1507+add_executable(${test_miniz_out} test_miniz.c)
1508+target_link_libraries(${test_miniz_out} zip)
1509+
1510+add_test(NAME ${test_out} COMMAND ${test_out})
1511+add_test(NAME ${test_miniz_out} COMMAND ${test_miniz_out})
1512
1513-add_test(NAME test COMMAND test.exe)
1514-add_test(NAME test_miniz COMMAND test_miniz.exe)
1515+set(test_out ${test_out} PARENT_SCOPE)
1516+set(test_miniz_out ${test_miniz_out} PARENT_SCOPE)
1517diff --git a/contrib/zip/test/test.c b/contrib/zip/test/test.c
1518index 454430533a..a9b2ddab1e 100644
1519--- a/contrib/zip/test/test.c
1520+++ b/contrib/zip/test/test.c
1521@@ -29,6 +29,8 @@
1522 #define XFILE "7.txt\0"
1523 #define XMODE 0100777
1524
1525+#define UNIXMODE 0100644
1526+
1527 #define UNUSED(x) (void)x
1528
1529 static int total_entries = 0;
1530@@ -102,7 +104,8 @@ static void test_read(void) {
1531   assert(0 == zip_entry_close(zip));
1532   free(buf);
1533   buf = NULL;
1534-
1535+  bufsize = 0;
1536+
1537   assert(0 == zip_entry_open(zip, "test/test-2.txt"));
1538   assert(strlen(TESTDATA2) == zip_entry_size(zip));
1539   assert(CRC32DATA2 == zip_entry_crc32(zip));
1540@@ -131,7 +134,8 @@ static void test_read(void) {
1541   assert(0 == zip_entry_close(zip));
1542   free(buf);
1543   buf = NULL;
1544-
1545+  bufsize = 0;
1546+
1547   buftmp = strlen(TESTDATA1);
1548   buf = calloc(buftmp, sizeof(char));
1549   assert(0 == zip_entry_open(zip, "test/test-1.txt"));
1550@@ -433,6 +437,35 @@ static void test_mtime(void) {
1551   remove(ZIPNAME);
1552 }
1553
1554+static void test_unix_permissions(void) {
1555+#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__)
1556+#else
1557+  // UNIX or APPLE
1558+  struct MZ_FILE_STAT_STRUCT file_stats;
1559+
1560+  remove(ZIPNAME);
1561+
1562+  struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
1563+  assert(zip != NULL);
1564+
1565+  assert(0 == zip_entry_open(zip, RFILE));
1566+  assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
1567+  assert(0 == zip_entry_close(zip));
1568+
1569+  zip_close(zip);
1570+
1571+  remove(RFILE);
1572+
1573+  assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
1574+
1575+  assert(0 == MZ_FILE_STAT(RFILE, &file_stats));
1576+  assert(UNIXMODE == file_stats.st_mode);
1577+
1578+  remove(RFILE);
1579+  remove(ZIPNAME);
1580+#endif
1581+}
1582+
1583 int main(int argc, char *argv[]) {
1584   UNUSED(argc);
1585   UNUSED(argv);
1586@@ -453,6 +486,7 @@ int main(int argc, char *argv[]) {
1587   test_write_permissions();
1588   test_exe_permissions();
1589   test_mtime();
1590+  test_unix_permissions();
1591
1592   remove(ZIPNAME);
1593   return 0;
1594diff --git a/contrib/zip/test/test_miniz.c b/contrib/zip/test/test_miniz.c
1595index ebc0564dc3..babcaecdb6 100644
1596--- a/contrib/zip/test/test_miniz.c
1597+++ b/contrib/zip/test/test_miniz.c
1598@@ -23,16 +23,39 @@ int main(int argc, char *argv[]) {
1599   uint step = 0;
1600   int cmp_status;
1601   uLong src_len = (uLong)strlen(s_pStr);
1602-  uLong cmp_len = compressBound(src_len);
1603   uLong uncomp_len = src_len;
1604+  uLong cmp_len;
1605   uint8 *pCmp, *pUncomp;
1606+  size_t sz;
1607   uint total_succeeded = 0;
1608   (void)argc, (void)argv;
1609
1610   printf("miniz.c version: %s\n", MZ_VERSION);
1611
1612   do {
1613+    pCmp = (uint8 *)tdefl_compress_mem_to_heap(s_pStr, src_len, &cmp_len, 0);
1614+    if (!pCmp) {
1615+      printf("tdefl_compress_mem_to_heap failed\n");
1616+      return EXIT_FAILURE;
1617+    }
1618+    if (src_len <= cmp_len) {
1619+      printf("tdefl_compress_mem_to_heap failed: from %u to %u bytes\n",
1620+             (mz_uint32)uncomp_len, (mz_uint32)cmp_len);
1621+      free(pCmp);
1622+      return EXIT_FAILURE;
1623+    }
1624+
1625+    sz = tdefl_compress_mem_to_mem(pCmp, cmp_len, s_pStr, src_len, 0);
1626+    if (sz != cmp_len) {
1627+      printf("tdefl_compress_mem_to_mem failed: expected %u, got %u\n",
1628+             (mz_uint32)cmp_len, (mz_uint32)sz);
1629+      free(pCmp);
1630+      return EXIT_FAILURE;
1631+    }
1632+
1633     // Allocate buffers to hold compressed and uncompressed data.
1634+    free(pCmp);
1635+    cmp_len = compressBound(src_len);
1636     pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
1637     pUncomp = (mz_uint8 *)malloc((size_t)src_len);
1638     if ((!pCmp) || (!pUncomp)) {
1639