v / thirdparty / zip
Raw file | 1780 loc (1523 sloc) | 47.21 KB | Latest commit hash dc78f1ba1
1/*
2 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
3 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
4 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
5 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
6 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
7 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
8 * OTHER DEALINGS IN THE SOFTWARE.
9 */
10#define __STDC_WANT_LIB_EXT1__ 1
11
12#include <errno.h>
13#include <sys/stat.h>
14#include <time.h>
15
16#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
17 defined(__MINGW32__)
18/* Win32, DOS, MSVC, MSVS */
19#include <direct.h>
20
21#define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL)
22#define HAS_DEVICE(P) \
23 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \
24 (P)[1] == ':')
25#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
26
27#else
28
29#include <unistd.h> // needed for symlink()
30#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)
31
32#endif
33
34#ifdef __MINGW32__
35#include <sys/types.h>
36#include <unistd.h>
37#endif
38
39#include "miniz.h"
40#include "zip.h"
41
42#ifdef _MSC_VER
43#include <io.h>
44
45#define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0))
46#define fileno _fileno
47#endif
48
49#if defined(__TINYC__) && (defined(_WIN32) || defined(_WIN64))
50#include <io.h>
51
52#define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0))
53#define fileno _fileno
54#endif
55
56#ifndef HAS_DEVICE
57#define HAS_DEVICE(P) 0
58#endif
59
60#ifndef FILESYSTEM_PREFIX_LEN
61#define FILESYSTEM_PREFIX_LEN(P) 0
62#endif
63
64#ifndef ISSLASH
65#define ISSLASH(C) ((C) == '/' || (C) == '\\')
66#endif
67
68#define CLEANUP(ptr) \
69 do { \
70 if (ptr) { \
71 free((void *)ptr); \
72 ptr = NULL; \
73 } \
74 } while (0)
75
76#define UNX_IFDIR 0040000 /* Unix directory */
77#define UNX_IFREG 0100000 /* Unix regular file */
78#define UNX_IFSOCK 0140000 /* Unix socket (BSD, not SysV or Amiga) */
79#define UNX_IFLNK 0120000 /* Unix symbolic link (not SysV, Amiga) */
80#define UNX_IFBLK 0060000 /* Unix block special (not Amiga) */
81#define UNX_IFCHR 0020000 /* Unix character special (not Amiga) */
82#define UNX_IFIFO 0010000 /* Unix fifo (BCC, not MSC or Amiga) */
83
84struct zip_entry_t {
85 ssize_t index;
86 char *name;
87 mz_uint64 uncomp_size;
88 mz_uint64 comp_size;
89 mz_uint32 uncomp_crc32;
90 mz_uint64 offset;
91 mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
92 mz_uint64 header_offset;
93 mz_uint16 method;
94 mz_zip_writer_add_state state;
95 tdefl_compressor comp;
96 mz_uint32 external_attr;
97 time_t m_time;
98};
99
100struct zip_t {
101 mz_zip_archive archive;
102 mz_uint level;
103 struct zip_entry_t entry;
104};
105
106enum zip_modify_t {
107 MZ_KEEP = 0,
108 MZ_DELETE = 1,
109 MZ_MOVE = 2,
110};
111
112struct zip_entry_mark_t {
113 ssize_t file_index;
114 enum zip_modify_t type;
115 mz_uint64 m_local_header_ofs;
116 size_t lf_length;
117};
118
119static const char *const zip_errlist[30] = {
120 NULL,
121 "not initialized\0",
122 "invalid entry name\0",
123 "entry not found\0",
124 "invalid zip mode\0",
125 "invalid compression level\0",
126 "no zip 64 support\0",
127 "memset error\0",
128 "cannot write data to entry\0",
129 "cannot initialize tdefl compressor\0",
130 "invalid index\0",
131 "header not found\0",
132 "cannot flush tdefl buffer\0",
133 "cannot write entry header\0",
134 "cannot create entry header\0",
135 "cannot write to central dir\0",
136 "cannot open file\0",
137 "invalid entry type\0",
138 "extracting data using no memory allocation\0",
139 "file not found\0",
140 "no permission\0",
141 "out of memory\0",
142 "invalid zip archive name\0",
143 "make dir error\0",
144 "symlink error\0",
145 "close archive error\0",
146 "capacity size too small\0",
147 "fseek error\0",
148 "fread error\0",
149 "fwrite error\0",
150};
151
152const char *zip_strerror(int errnum) {
153 errnum = -errnum;
154 if (errnum <= 0 || errnum >= 30) {
155 return NULL;
156 }
157
158 return zip_errlist[errnum];
159}
160
161static const char *zip_basename(const char *name) {
162 char const *p;
163 char const *base = name += FILESYSTEM_PREFIX_LEN(name);
164 int all_slashes = 1;
165
166 for (p = name; *p; p++) {
167 if (ISSLASH(*p))
168 base = p + 1;
169 else
170 all_slashes = 0;
171 }
172
173 /* If NAME is all slashes, arrange to return `/'. */
174 if (*base == '\0' && ISSLASH(*name) && all_slashes)
175 --base;
176
177 return base;
178}
179
180static int zip_mkpath(char *path) {
181 char *p;
182 char npath[MAX_PATH + 1];
183 int len = 0;
184 int has_device = HAS_DEVICE(path);
185
186 memset(npath, 0, MAX_PATH + 1);
187 if (has_device) {
188 // only on windows
189 npath[0] = path[0];
190 npath[1] = path[1];
191 len = 2;
192 }
193 for (p = path + len; *p && len < MAX_PATH; p++) {
194 if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) {
195#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
196 defined(__MINGW32__)
197#else
198 if ('\\' == *p) {
199 *p = '/';
200 }
201#endif
202
203 if (MZ_MKDIR(npath) == -1) {
204 if (errno != EEXIST) {
205 return ZIP_EMKDIR;
206 }
207 }
208 }
209 npath[len++] = *p;
210 }
211
212 return 0;
213}
214
215static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) {
216 char c;
217 size_t i;
218 char *rpl = (char *)calloc((1 + n), sizeof(char));
219 char *begin = rpl;
220 if (!rpl) {
221 return NULL;
222 }
223
224 for (i = 0; (i < n) && (c = *str++); ++i) {
225 if (c == oldchar) {
226 c = newchar;
227 }
228 *rpl++ = c;
229 }
230
231 return begin;
232}
233
234static char *zip_name_normalize(char *name, char *const nname, size_t len) {
235 size_t offn = 0;
236 size_t offnn = 0, ncpy = 0;
237
238 if (name == NULL || nname == NULL || len <= 0) {
239 return NULL;
240 }
241 // skip trailing '/'
242 while (ISSLASH(*name))
243 name++;
244
245 for (; offn < len; offn++) {
246 if (ISSLASH(name[offn])) {
247 if (ncpy > 0 && strcmp(&nname[offnn], ".\0") &&
248 strcmp(&nname[offnn], "..\0")) {
249 offnn += ncpy;
250 nname[offnn++] = name[offn]; // append '/'
251 }
252 ncpy = 0;
253 } else {
254 nname[offnn + ncpy] = name[offn];
255 ncpy++;
256 }
257 }
258
259 // at the end, extra check what we've already copied
260 if (ncpy == 0 || !strcmp(&nname[offnn], ".\0") ||
261 !strcmp(&nname[offnn], "..\0")) {
262 nname[offnn] = 0;
263 }
264 return nname;
265}
266
267static mz_bool zip_name_match(const char *name1, const char *name2) {
268 char *nname2 = NULL;
269
270#ifdef ZIP_RAW_ENTRYNAME
271 nname2 = STRCLONE(name2);
272#else
273 nname2 = zip_strrpl(name2, strlen(name2), '\\', '/');
274#endif
275
276 if (!nname2) {
277 return MZ_FALSE;
278 }
279
280 mz_bool res = (strcmp(name1, nname2) == 0) ? MZ_TRUE : MZ_FALSE;
281 CLEANUP(nname2);
282 return res;
283}
284
285static int zip_archive_truncate(mz_zip_archive *pzip) {
286 mz_zip_internal_state *pState = pzip->m_pState;
287 mz_uint64 file_size = pzip->m_archive_size;
288 if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) {
289 return 0;
290 }
291 if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) {
292 if (pState->m_pFile) {
293 int fd = fileno(pState->m_pFile);
294 return ftruncate(fd, file_size);
295 }
296 }
297 return 0;
298}
299
300static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir,
301 int (*on_extract)(const char *filename,
302 void *arg),
303 void *arg) {
304 int err = 0;
305 mz_uint i, n;
306 char path[MAX_PATH + 1];
307 char symlink_to[MAX_PATH + 1];
308 mz_zip_archive_file_stat info;
309 size_t dirlen = 0;
310 mz_uint32 xattr = 0;
311
312 memset(path, 0, sizeof(path));
313 memset(symlink_to, 0, sizeof(symlink_to));
314
315 dirlen = strlen(dir);
316 if (dirlen + 1 > MAX_PATH) {
317 return ZIP_EINVENTNAME;
318 }
319
320 memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
321
322#if defined(_MSC_VER)
323 strcpy_s(path, MAX_PATH, dir);
324#else
325 strcpy(path, dir);
326#endif
327
328 if (!ISSLASH(path[dirlen - 1])) {
329#if defined(_WIN32) || defined(__WIN32__)
330 path[dirlen] = '\\';
331#else
332 path[dirlen] = '/';
333#endif
334 ++dirlen;
335 }
336
337 // Get and print information about each file in the archive.
338 n = mz_zip_reader_get_num_files(zip_archive);
339 for (i = 0; i < n; ++i) {
340 if (!mz_zip_reader_file_stat(zip_archive, i, &info)) {
341 // Cannot get information about zip archive;
342 err = ZIP_ENOENT;
343 goto out;
344 }
345
346 if (!zip_name_normalize(info.m_filename, info.m_filename,
347 strlen(info.m_filename))) {
348 // Cannot normalize file name;
349 err = ZIP_EINVENTNAME;
350 goto out;
351 }
352#if defined(_MSC_VER)
353 strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename,
354 MAX_PATH - dirlen);
355#else
356 strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
357#endif
358 err = zip_mkpath(path);
359 if (err < 0) {
360 // Cannot make a path
361 goto out;
362 }
363
364 if ((((info.m_version_made_by >> 8) == 3) ||
365 ((info.m_version_made_by >> 8) ==
366 19)) // if zip is produced on Unix or macOS (3 and 19 from
367 // section 4.4.2.2 of zip standard)
368 && info.m_external_attr &
369 (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40
370 // is directory)
371#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
372 defined(__MINGW32__)
373#else
374 if (info.m_uncomp_size > MAX_PATH ||
375 !mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to,
376 MAX_PATH, 0, NULL, 0)) {
377 err = ZIP_EMEMNOALLOC;
378 goto out;
379 }
380 symlink_to[info.m_uncomp_size] = '\0';
381 if (symlink(symlink_to, path) != 0) {
382 err = ZIP_ESYMLINK;
383 goto out;
384 }
385#endif
386 } else {
387 if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) {
388 if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) {
389 // Cannot extract zip archive to file
390 err = ZIP_ENOFILE;
391 goto out;
392 }
393 }
394
395#if defined(_MSC_VER) || defined(PS4)
396 (void)xattr; // unused
397#else
398 xattr = (info.m_external_attr >> 16) & 0xFFFF;
399 if (xattr > 0 && xattr <= MZ_UINT16_MAX) {
400 if (CHMOD(path, (mode_t)xattr) < 0) {
401 err = ZIP_ENOPERM;
402 goto out;
403 }
404 }
405#endif
406 }
407
408 if (on_extract) {
409 if (on_extract(path, arg) < 0) {
410 goto out;
411 }
412 }
413 }
414
415out:
416 // Close the archive, freeing any resources it was using
417 if (!mz_zip_reader_end(zip_archive)) {
418 // Cannot end zip reader
419 err = ZIP_ECLSZIP;
420 }
421 return err;
422}
423
424static inline void zip_archive_finalize(mz_zip_archive *pzip) {
425 mz_zip_writer_finalize_archive(pzip);
426 zip_archive_truncate(pzip);
427}
428
429static ssize_t zip_entry_mark(struct zip_t *zip,
430 struct zip_entry_mark_t *entry_mark,
431 const ssize_t n, char *const entries[],
432 const size_t len) {
433 ssize_t i = 0;
434 ssize_t err = 0;
435 if (!zip || !entry_mark || !entries) {
436 return ZIP_ENOINIT;
437 }
438
439 mz_zip_archive_file_stat file_stat;
440 mz_uint64 d_pos = ~0UL;
441 for (i = 0; i < n; ++i) {
442 if ((err = zip_entry_openbyindex(zip, i))) {
443 return (ssize_t)err;
444 }
445
446 mz_bool name_matches = MZ_FALSE;
447 {
448 size_t j;
449 for (j = 0; j < len; ++j) {
450 if (zip_name_match(zip->entry.name, entries[j])) {
451 name_matches = MZ_TRUE;
452 break;
453 }
454 }
455 }
456 if (name_matches) {
457 entry_mark[i].type = MZ_DELETE;
458 } else {
459 entry_mark[i].type = MZ_KEEP;
460 }
461
462 if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) {
463 return ZIP_ENOENT;
464 }
465
466 zip_entry_close(zip);
467
468 entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs;
469 entry_mark[i].file_index = (ssize_t)-1;
470 entry_mark[i].lf_length = 0;
471 if ((entry_mark[i].type) == MZ_DELETE &&
472 (d_pos > entry_mark[i].m_local_header_ofs)) {
473 d_pos = entry_mark[i].m_local_header_ofs;
474 }
475 }
476
477 for (i = 0; i < n; ++i) {
478 if ((entry_mark[i].m_local_header_ofs > d_pos) &&
479 (entry_mark[i].type != MZ_DELETE)) {
480 entry_mark[i].type = MZ_MOVE;
481 }
482 }
483 return err;
484}
485
486static ssize_t zip_index_next(mz_uint64 *local_header_ofs_array,
487 ssize_t cur_index) {
488 ssize_t new_index = 0, i;
489 for (i = cur_index - 1; i >= 0; --i) {
490 if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) {
491 new_index = i + 1;
492 return new_index;
493 }
494 }
495 return new_index;
496}
497
498static ssize_t zip_sort(mz_uint64 *local_header_ofs_array, ssize_t cur_index) {
499 ssize_t nxt_index = zip_index_next(local_header_ofs_array, cur_index);
500
501 if (nxt_index != cur_index) {
502 mz_uint64 temp = local_header_ofs_array[cur_index];
503 ssize_t i;
504 for (i = cur_index; i > nxt_index; i--) {
505 local_header_ofs_array[i] = local_header_ofs_array[i - 1];
506 }
507 local_header_ofs_array[nxt_index] = temp;
508 }
509 return nxt_index;
510}
511
512static int zip_index_update(struct zip_entry_mark_t *entry_mark,
513 ssize_t last_index, ssize_t nxt_index) {
514 ssize_t j;
515 for (j = 0; j < last_index; j++) {
516 if (entry_mark[j].file_index >= nxt_index) {
517 entry_mark[j].file_index += 1;
518 }
519 }
520 entry_mark[nxt_index].file_index = last_index;
521 return 0;
522}
523
524static int zip_entry_finalize(struct zip_t *zip,
525 struct zip_entry_mark_t *entry_mark,
526 const ssize_t n) {
527
528 ssize_t i = 0;
529 mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64));
530 if (!local_header_ofs_array) {
531 return ZIP_EOOMEM;
532 }
533
534 for (i = 0; i < n; ++i) {
535 local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs;
536 ssize_t index = zip_sort(local_header_ofs_array, i);
537
538 if (index != i) {
539 zip_index_update(entry_mark, i, index);
540 }
541 entry_mark[i].file_index = index;
542 }
543
544 size_t *length = (size_t *)calloc(n, sizeof(size_t));
545 if (!length) {
546 CLEANUP(local_header_ofs_array);
547 return ZIP_EOOMEM;
548 }
549 for (i = 0; i < n - 1; i++) {
550 length[i] =
551 (size_t)(local_header_ofs_array[i + 1] - local_header_ofs_array[i]);
552 }
553 length[n - 1] =
554 (size_t)(zip->archive.m_archive_size - local_header_ofs_array[n - 1]);
555
556 for (i = 0; i < n; i++) {
557 entry_mark[i].lf_length = length[entry_mark[i].file_index];
558 }
559
560 CLEANUP(length);
561 CLEANUP(local_header_ofs_array);
562 return 0;
563}
564
565static ssize_t zip_entry_set(struct zip_t *zip,
566 struct zip_entry_mark_t *entry_mark, ssize_t n,
567 char *const entries[], const size_t len) {
568 ssize_t err = 0;
569
570 if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) {
571 return err;
572 }
573 if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) {
574 return err;
575 }
576 return 0;
577}
578
579static ssize_t zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to,
580 const mz_uint64 from, const size_t length,
581 mz_uint8 *move_buf, const size_t capacity_size) {
582 if (length > capacity_size) {
583 return ZIP_ECAPSIZE;
584 }
585 if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) {
586 return ZIP_EFSEEK;
587 }
588 if (fread(move_buf, 1, length, m_pFile) != length) {
589 return ZIP_EFREAD;
590 }
591 if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) {
592 return ZIP_EFSEEK;
593 }
594 if (fwrite(move_buf, 1, length, m_pFile) != length) {
595 return ZIP_EFWRITE;
596 }
597 return (ssize_t)length;
598}
599
600static ssize_t zip_files_move(MZ_FILE *m_pFile, mz_uint64 writen_num,
601 mz_uint64 read_num, size_t length) {
602 ssize_t n = 0;
603 const size_t page_size = 1 << 12; // 4K
604 mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size);
605 if (!move_buf) {
606 return ZIP_EOOMEM;
607 }
608
609 ssize_t moved_length = 0;
610 ssize_t move_count = 0;
611 while ((mz_int64)length > 0) {
612 move_count = (length >= page_size) ? page_size : length;
613 n = zip_file_move(m_pFile, writen_num, read_num, move_count, move_buf,
614 page_size);
615 if (n < 0) {
616 moved_length = n;
617 goto cleanup;
618 }
619
620 if (n != move_count) {
621 goto cleanup;
622 }
623
624 writen_num += move_count;
625 read_num += move_count;
626 length -= move_count;
627 moved_length += move_count;
628 }
629
630cleanup:
631 CLEANUP(move_buf);
632 return moved_length;
633}
634
635static int zip_central_dir_move(mz_zip_internal_state *pState, int begin,
636 int end, int entry_num) {
637 if (begin == entry_num) {
638 return 0;
639 }
640
641 size_t l_size = 0;
642 size_t r_size = 0;
643 mz_uint32 d_size = 0;
644 mz_uint8 *next = NULL;
645 mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT(
646 &pState->m_central_dir, mz_uint8,
647 MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin));
648 l_size = (size_t)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p));
649 if (end == entry_num) {
650 r_size = 0;
651 } else {
652 next = &MZ_ZIP_ARRAY_ELEMENT(
653 &pState->m_central_dir, mz_uint8,
654 MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end));
655 r_size = pState->m_central_dir.m_size -
656 (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p));
657 d_size = (mz_uint32)(next - deleted);
658 }
659
660 if (next && l_size == 0) {
661 memmove(pState->m_central_dir.m_p, next, r_size);
662 pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size);
663 {
664 int i;
665 for (i = end; i < entry_num; i++) {
666 MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -=
667 d_size;
668 }
669 }
670 }
671
672 if (next && l_size * r_size != 0) {
673 memmove(deleted, next, r_size);
674 {
675 int i;
676 for (i = end; i < entry_num; i++) {
677 MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -=
678 d_size;
679 }
680 }
681 }
682
683 pState->m_central_dir.m_size = l_size + r_size;
684 return 0;
685}
686
687static int zip_central_dir_delete(mz_zip_internal_state *pState,
688 int *deleted_entry_index_array,
689 int entry_num) {
690 int i = 0;
691 int begin = 0;
692 int end = 0;
693 int d_num = 0;
694 while (i < entry_num) {
695 while ((i < entry_num) && (!deleted_entry_index_array[i])) {
696 i++;
697 }
698 begin = i;
699
700 while ((i < entry_num) && (deleted_entry_index_array[i])) {
701 i++;
702 }
703 end = i;
704 zip_central_dir_move(pState, begin, end, entry_num);
705 }
706
707 i = 0;
708 while (i < entry_num) {
709 while ((i < entry_num) && (!deleted_entry_index_array[i])) {
710 i++;
711 }
712 begin = i;
713 if (begin == entry_num) {
714 break;
715 }
716 while ((i < entry_num) && (deleted_entry_index_array[i])) {
717 i++;
718 }
719 end = i;
720 int k = 0, j;
721 for (j = end; j < entry_num; j++) {
722 MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32,
723 begin + k) =
724 (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets,
725 mz_uint32, j);
726 k++;
727 }
728 d_num += end - begin;
729 }
730
731 pState->m_central_dir_offsets.m_size =
732 sizeof(mz_uint32) * (entry_num - d_num);
733 return 0;
734}
735
736static ssize_t zip_entries_delete_mark(struct zip_t *zip,
737 struct zip_entry_mark_t *entry_mark,
738 int entry_num) {
739 mz_uint64 writen_num = 0;
740 mz_uint64 read_num = 0;
741 size_t deleted_length = 0;
742 size_t move_length = 0;
743 int i = 0;
744 size_t deleted_entry_num = 0;
745 ssize_t n = 0;
746
747 mz_bool *deleted_entry_flag_array =
748 (mz_bool *)calloc(entry_num, sizeof(mz_bool));
749 if (deleted_entry_flag_array == NULL) {
750 return ZIP_EOOMEM;
751 }
752
753 mz_zip_internal_state *pState = zip->archive.m_pState;
754 zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING;
755
756 if ((!pState->m_pFile) || MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) {
757 CLEANUP(deleted_entry_flag_array);
758 return ZIP_ENOENT;
759 }
760
761 while (i < entry_num) {
762 while ((i < entry_num) && (entry_mark[i].type == MZ_KEEP)) {
763 writen_num += entry_mark[i].lf_length;
764 read_num = writen_num;
765 i++;
766 }
767
768 while ((i < entry_num) && (entry_mark[i].type == MZ_DELETE)) {
769 deleted_entry_flag_array[i] = MZ_TRUE;
770 read_num += entry_mark[i].lf_length;
771 deleted_length += entry_mark[i].lf_length;
772 i++;
773 deleted_entry_num++;
774 }
775
776 while ((i < entry_num) && (entry_mark[i].type == MZ_MOVE)) {
777 move_length += entry_mark[i].lf_length;
778 mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT(
779 &pState->m_central_dir, mz_uint8,
780 MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i));
781 if (!p) {
782 CLEANUP(deleted_entry_flag_array);
783 return ZIP_ENOENT;
784 }
785 mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
786 offset -= (mz_uint32)deleted_length;
787 MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset);
788 i++;
789 }
790
791 n = zip_files_move(pState->m_pFile, writen_num, read_num, move_length);
792 if (n != (ssize_t)move_length) {
793 CLEANUP(deleted_entry_flag_array);
794 return n;
795 }
796 writen_num += move_length;
797 read_num += move_length;
798 }
799
800 zip->archive.m_archive_size -= (mz_uint64)deleted_length;
801 zip->archive.m_total_files =
802 (mz_uint32)entry_num - (mz_uint32)deleted_entry_num;
803
804 zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num);
805 CLEANUP(deleted_entry_flag_array);
806
807 return (ssize_t)deleted_entry_num;
808}
809
810struct zip_t *zip_open(const char *zipname, int level, char mode) {
811 struct zip_t *zip = NULL;
812
813 if (!zipname || strlen(zipname) < 1) {
814 // zip_t archive name is empty or NULL
815 goto cleanup;
816 }
817
818 if (level < 0)
819 level = MZ_DEFAULT_LEVEL;
820 if ((level & 0xF) > MZ_UBER_COMPRESSION) {
821 // Wrong compression level
822 goto cleanup;
823 }
824
825 zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
826 if (!zip)
827 goto cleanup;
828
829 zip->level = (mz_uint)level;
830 switch (mode) {
831 case 'w':
832 // Create a new archive.
833 if (!mz_zip_writer_init_file_v2(&(zip->archive), zipname, 0,
834 MZ_ZIP_FLAG_WRITE_ZIP64)) {
835 // Cannot initialize zip_archive writer
836 goto cleanup;
837 }
838 break;
839
840 case 'r':
841 case 'a':
842 case 'd':
843 if (!mz_zip_reader_init_file_v2_rpb(
844 &(zip->archive), zipname,
845 zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) {
846 // An archive file does not exist or cannot initialize
847 // zip_archive reader
848 goto cleanup;
849 }
850 if ((mode == 'a' || mode == 'd')) {
851 if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), zipname,
852 0)) {
853 mz_zip_reader_end(&(zip->archive));
854 goto cleanup;
855 }
856 }
857 break;
858
859 default:
860 goto cleanup;
861 }
862
863 return zip;
864
865cleanup:
866 CLEANUP(zip);
867 return NULL;
868}
869
870void zip_close(struct zip_t *zip) {
871 if (zip) {
872 // Always finalize, even if adding failed for some reason, so we have a
873 // valid central directory.
874 mz_zip_writer_finalize_archive(&(zip->archive));
875 zip_archive_truncate(&(zip->archive));
876 mz_zip_writer_end(&(zip->archive));
877 mz_zip_reader_end(&(zip->archive));
878
879 CLEANUP(zip);
880 }
881}
882
883int zip_is64(struct zip_t *zip) {
884 if (!zip || !zip->archive.m_pState) {
885 // zip_t handler or zip state is not initialized
886 return ZIP_ENOINIT;
887 }
888
889 return (int)zip->archive.m_pState->m_zip64;
890}
891
892static int _zip_entry_open(struct zip_t *zip, const char *entryname,
893 int case_sensitive) {
894 size_t entrylen = 0;
895 mz_zip_archive *pzip = NULL;
896 mz_uint num_alignment_padding_bytes, level;
897 mz_zip_archive_file_stat stats;
898 int err = 0;
899 mz_uint16 dos_time, dos_date;
900 mz_uint32 extra_size = 0;
901 mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
902 mz_uint64 local_dir_header_ofs = 0;
903
904 if (!zip) {
905 return ZIP_ENOINIT;
906 }
907
908 local_dir_header_ofs = zip->archive.m_archive_size;
909
910 if (!entryname) {
911 return ZIP_EINVENTNAME;
912 }
913
914 entrylen = strlen(entryname);
915 if (entrylen == 0) {
916 return ZIP_EINVENTNAME;
917 }
918
919 /*
920 .ZIP File Format Specification Version: 6.3.3
921
922 4.4.17.1 The name of the file, with optional relative path.
923 The path stored MUST not contain a drive or
924 device letter, or a leading slash. All slashes
925 MUST be forward slashes '/' as opposed to
926 backwards slashes '\' for compatibility with Amiga
927 and UNIX file systems etc. If input came from standard
928 input, there is no file name field.
929 */
930 if (zip->entry.name) {
931 CLEANUP(zip->entry.name);
932 }
933#ifdef ZIP_RAW_ENTRYNAME
934 zip->entry.name = STRCLONE(entryname);
935#else
936 zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/');
937#endif
938
939 if (!zip->entry.name) {
940 // Cannot parse zip entry name
941 return ZIP_EINVENTNAME;
942 }
943
944 pzip = &(zip->archive);
945 if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
946 zip->entry.index = (ssize_t)mz_zip_reader_locate_file(
947 pzip, zip->entry.name, NULL,
948 case_sensitive ? MZ_ZIP_FLAG_CASE_SENSITIVE : 0);
949 if (zip->entry.index < (ssize_t)0) {
950 err = ZIP_ENOENT;
951 goto cleanup;
952 }
953
954 if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) {
955 err = ZIP_ENOENT;
956 goto cleanup;
957 }
958
959 zip->entry.comp_size = stats.m_comp_size;
960 zip->entry.uncomp_size = stats.m_uncomp_size;
961 zip->entry.uncomp_crc32 = stats.m_crc32;
962 zip->entry.offset = stats.m_central_dir_ofs;
963 zip->entry.header_offset = stats.m_local_header_ofs;
964 zip->entry.method = stats.m_method;
965 zip->entry.external_attr = stats.m_external_attr;
966#ifndef MINIZ_NO_TIME
967 zip->entry.m_time = stats.m_time;
968#endif
969
970 return 0;
971 }
972
973 level = zip->level & 0xF;
974
975 zip->entry.index = (ssize_t)zip->archive.m_total_files;
976 zip->entry.comp_size = 0;
977 zip->entry.uncomp_size = 0;
978 zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
979 zip->entry.offset = zip->archive.m_archive_size;
980 zip->entry.header_offset = zip->archive.m_archive_size;
981 memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
982 zip->entry.method = level ? MZ_DEFLATED : 0;
983
984 // UNIX or APPLE
985#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19
986 // regular file with rw-r--r-- permissions
987 zip->entry.external_attr = (mz_uint32)(0100644) << 16;
988#else
989 zip->entry.external_attr = 0;
990#endif
991
992 num_alignment_padding_bytes =
993 mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
994
995 if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
996 // Invalid zip mode
997 err = ZIP_EINVMODE;
998 goto cleanup;
999 }
1000 if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
1001 // Invalid zip compression level
1002 err = ZIP_EINVLVL;
1003 goto cleanup;
1004 }
1005
1006 if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset,
1007 num_alignment_padding_bytes)) {
1008 // Cannot memset zip entry header
1009 err = ZIP_EMEMSET;
1010 goto cleanup;
1011 }
1012 local_dir_header_ofs += num_alignment_padding_bytes;
1013
1014 zip->entry.m_time = time(NULL);
1015#ifndef MINIZ_NO_TIME
1016 mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date);
1017#endif
1018
1019 // ZIP64 header with NULL sizes (sizes will be in the data descriptor, just
1020 // after file data)
1021 extra_size = mz_zip_writer_create_zip64_extra_data(
1022 extra_data, NULL, NULL,
1023 (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
1024
1025 if (!mz_zip_writer_create_local_dir_header(
1026 pzip, zip->entry.header, entrylen, (mz_uint16)extra_size, 0, 0, 0,
1027 zip->entry.method,
1028 MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 |
1029 MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR,
1030 dos_time, dos_date)) {
1031 // Cannot create zip entry header
1032 err = ZIP_EMEMSET;
1033 goto cleanup;
1034 }
1035
1036 zip->entry.header_offset = zip->entry.offset + num_alignment_padding_bytes;
1037
1038 if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset,
1039 zip->entry.header,
1040 sizeof(zip->entry.header)) != sizeof(zip->entry.header)) {
1041 // Cannot write zip entry header
1042 err = ZIP_EMEMSET;
1043 goto cleanup;
1044 }
1045
1046 if (pzip->m_file_offset_alignment) {
1047 MZ_ASSERT(
1048 (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0);
1049 }
1050 zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header);
1051
1052 if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name,
1053 entrylen) != entrylen) {
1054 // Cannot write data to zip entry
1055 err = ZIP_EWRTENT;
1056 goto cleanup;
1057 }
1058
1059 zip->entry.offset += entrylen;
1060
1061 if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, extra_data,
1062 extra_size) != extra_size) {
1063 // Cannot write ZIP64 data to zip entry
1064 err = ZIP_EWRTENT;
1065 goto cleanup;
1066 }
1067 zip->entry.offset += extra_size;
1068
1069 if (level) {
1070 zip->entry.state.m_pZip = pzip;
1071 zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset;
1072 zip->entry.state.m_comp_size = 0;
1073
1074 if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback,
1075 &(zip->entry.state),
1076 (int)tdefl_create_comp_flags_from_zip_params(
1077 (int)level, -15, MZ_DEFAULT_STRATEGY)) !=
1078 TDEFL_STATUS_OKAY) {
1079 // Cannot initialize the zip compressor
1080 err = ZIP_ETDEFLINIT;
1081 goto cleanup;
1082 }
1083 }
1084
1085 return 0;
1086
1087cleanup:
1088 CLEANUP(zip->entry.name);
1089 return err;
1090}
1091
1092int zip_entry_open(struct zip_t *zip, const char *entryname) {
1093 return _zip_entry_open(zip, entryname, 0);
1094}
1095
1096int zip_entry_opencasesensitive(struct zip_t *zip, const char *entryname) {
1097 return _zip_entry_open(zip, entryname, 1);
1098}
1099
1100int zip_entry_openbyindex(struct zip_t *zip, size_t index) {
1101 mz_zip_archive *pZip = NULL;
1102 mz_zip_archive_file_stat stats;
1103 mz_uint namelen;
1104 const mz_uint8 *pHeader;
1105 const char *pFilename;
1106
1107 if (!zip) {
1108 // zip_t handler is not initialized
1109 return ZIP_ENOINIT;
1110 }
1111
1112 pZip = &(zip->archive);
1113 if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) {
1114 // open by index requires readonly mode
1115 return ZIP_EINVMODE;
1116 }
1117
1118 if (index >= (size_t)pZip->m_total_files) {
1119 // index out of range
1120 return ZIP_EINVIDX;
1121 }
1122
1123 if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT(
1124 &pZip->m_pState->m_central_dir, mz_uint8,
1125 MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets,
1126 mz_uint32, index)))) {
1127 // cannot find header in central directory
1128 return ZIP_ENOHDR;
1129 }
1130
1131 namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1132 pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
1133
1134 /*
1135 .ZIP File Format Specification Version: 6.3.3
1136
1137 4.4.17.1 The name of the file, with optional relative path.
1138 The path stored MUST not contain a drive or
1139 device letter, or a leading slash. All slashes
1140 MUST be forward slashes '/' as opposed to
1141 backwards slashes '\' for compatibility with Amiga
1142 and UNIX file systems etc. If input came from standard
1143 input, there is no file name field.
1144 */
1145 if (zip->entry.name) {
1146 CLEANUP(zip->entry.name);
1147 }
1148#ifdef ZIP_RAW_ENTRYNAME
1149 zip->entry.name = STRCLONE(pFilename);
1150#else
1151 zip->entry.name = zip_strrpl(pFilename, namelen, '\\', '/');
1152#endif
1153
1154 if (!zip->entry.name) {
1155 // local entry name is NULL
1156 return ZIP_EINVENTNAME;
1157 }
1158
1159 if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) {
1160 return ZIP_ENOENT;
1161 }
1162
1163 zip->entry.index = (ssize_t)index;
1164 zip->entry.comp_size = stats.m_comp_size;
1165 zip->entry.uncomp_size = stats.m_uncomp_size;
1166 zip->entry.uncomp_crc32 = stats.m_crc32;
1167 zip->entry.offset = stats.m_central_dir_ofs;
1168 zip->entry.header_offset = stats.m_local_header_ofs;
1169 zip->entry.method = stats.m_method;
1170 zip->entry.external_attr = stats.m_external_attr;
1171#ifndef MINIZ_NO_TIME
1172 zip->entry.m_time = stats.m_time;
1173#endif
1174
1175 return 0;
1176}
1177
1178int zip_entry_close(struct zip_t *zip) {
1179 mz_zip_archive *pzip = NULL;
1180 mz_uint level;
1181 tdefl_status done;
1182 mz_uint16 entrylen;
1183 mz_uint16 dos_time = 0, dos_date = 0;
1184 int err = 0;
1185 mz_uint8 *pExtra_data = NULL;
1186 mz_uint32 extra_size = 0;
1187 mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
1188 mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];
1189 mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64;
1190
1191 if (!zip) {
1192 // zip_t handler is not initialized
1193 err = ZIP_ENOINIT;
1194 goto cleanup;
1195 }
1196
1197 pzip = &(zip->archive);
1198 if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
1199 goto cleanup;
1200 }
1201
1202 level = zip->level & 0xF;
1203 if (level) {
1204 done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
1205 if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
1206 // Cannot flush compressed buffer
1207 err = ZIP_ETDEFLBUF;
1208 goto cleanup;
1209 }
1210 zip->entry.comp_size = zip->entry.state.m_comp_size;
1211 zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs;
1212 zip->entry.method = MZ_DEFLATED;
1213 }
1214
1215 entrylen = (mz_uint16)strlen(zip->entry.name);
1216#ifndef MINIZ_NO_TIME
1217 mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date);
1218#endif
1219
1220 MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);
1221 MZ_WRITE_LE32(local_dir_footer + 4, zip->entry.uncomp_crc32);
1222 MZ_WRITE_LE64(local_dir_footer + 8, zip->entry.comp_size);
1223 MZ_WRITE_LE64(local_dir_footer + 16, zip->entry.uncomp_size);
1224
1225 if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, local_dir_footer,
1226 local_dir_footer_size) != local_dir_footer_size) {
1227 // Cannot write zip entry header
1228 err = ZIP_EWRTHDR;
1229 goto cleanup;
1230 }
1231 zip->entry.offset += local_dir_footer_size;
1232
1233 pExtra_data = extra_data;
1234 extra_size = mz_zip_writer_create_zip64_extra_data(
1235 extra_data,
1236 (zip->entry.uncomp_size >= MZ_UINT32_MAX) ? &zip->entry.uncomp_size
1237 : NULL,
1238 (zip->entry.comp_size >= MZ_UINT32_MAX) ? &zip->entry.comp_size : NULL,
1239 (zip->entry.header_offset >= MZ_UINT32_MAX) ? &zip->entry.header_offset
1240 : NULL);
1241
1242 if ((entrylen) && (zip->entry.name[entrylen - 1] == '/') &&
1243 !zip->entry.uncomp_size) {
1244 /* Set DOS Subdirectory attribute bit. */
1245 zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
1246 }
1247
1248 if (!mz_zip_writer_add_to_central_dir(
1249 pzip, zip->entry.name, entrylen, pExtra_data, (mz_uint16)extra_size,
1250 "", 0, zip->entry.uncomp_size, zip->entry.comp_size,
1251 zip->entry.uncomp_crc32, zip->entry.method,
1252 MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 |
1253 MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR,
1254 dos_time, dos_date, zip->entry.header_offset,
1255 zip->entry.external_attr, NULL, 0)) {
1256 // Cannot write to zip central dir
1257 err = ZIP_EWRTDIR;
1258 goto cleanup;
1259 }
1260
1261 pzip->m_total_files++;
1262 pzip->m_archive_size = zip->entry.offset;
1263
1264cleanup:
1265 if (zip) {
1266 zip->entry.m_time = 0;
1267 CLEANUP(zip->entry.name);
1268 }
1269 return err;
1270}
1271
1272const char *zip_entry_name(struct zip_t *zip) {
1273 if (!zip) {
1274 // zip_t handler is not initialized
1275 return NULL;
1276 }
1277
1278 return zip->entry.name;
1279}
1280
1281ssize_t zip_entry_index(struct zip_t *zip) {
1282 if (!zip) {
1283 // zip_t handler is not initialized
1284 return (ssize_t)ZIP_ENOINIT;
1285 }
1286
1287 return zip->entry.index;
1288}
1289
1290int zip_entry_isdir(struct zip_t *zip) {
1291 if (!zip) {
1292 // zip_t handler is not initialized
1293 return ZIP_ENOINIT;
1294 }
1295
1296 if (zip->entry.index < (ssize_t)0) {
1297 // zip entry is not opened
1298 return ZIP_EINVIDX;
1299 }
1300
1301 return (int)mz_zip_reader_is_file_a_directory(&zip->archive,
1302 (mz_uint)zip->entry.index);
1303}
1304
1305unsigned long long zip_entry_size(struct zip_t *zip) {
1306 return zip_entry_uncomp_size(zip);
1307}
1308
1309unsigned long long zip_entry_uncomp_size(struct zip_t *zip) {
1310 return zip ? zip->entry.uncomp_size : 0;
1311}
1312
1313unsigned long long zip_entry_comp_size(struct zip_t *zip) {
1314 return zip ? zip->entry.comp_size : 0;
1315}
1316
1317unsigned int zip_entry_crc32(struct zip_t *zip) {
1318 return zip ? zip->entry.uncomp_crc32 : 0;
1319}
1320
1321int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) {
1322 mz_uint level;
1323 mz_zip_archive *pzip = NULL;
1324 tdefl_status status;
1325
1326 if (!zip) {
1327 // zip_t handler is not initialized
1328 return ZIP_ENOINIT;
1329 }
1330
1331 pzip = &(zip->archive);
1332 if (buf && bufsize > 0) {
1333 zip->entry.uncomp_size += bufsize;
1334 zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(
1335 zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
1336
1337 level = zip->level & 0xF;
1338 if (!level) {
1339 if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf,
1340 bufsize) != bufsize)) {
1341 // Cannot write buffer
1342 return ZIP_EWRTENT;
1343 }
1344 zip->entry.offset += bufsize;
1345 zip->entry.comp_size += bufsize;
1346 } else {
1347 status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize,
1348 TDEFL_NO_FLUSH);
1349 if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
1350 // Cannot compress buffer
1351 return ZIP_ETDEFLBUF;
1352 }
1353 }
1354 }
1355
1356 return 0;
1357}
1358
1359int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
1360 int err = 0;
1361 size_t n = 0;
1362 MZ_FILE *stream = NULL;
1363 mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE];
1364 struct MZ_FILE_STAT_STRUCT file_stat;
1365 mz_uint16 modes;
1366
1367 if (!zip) {
1368 // zip_t handler is not initialized
1369 return ZIP_ENOINIT;
1370 }
1371
1372 memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE);
1373 memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT));
1374 if (MZ_FILE_STAT(filename, &file_stat) != 0) {
1375 // problem getting information - check errno
1376 return ZIP_ENOENT;
1377 }
1378
1379#if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP)
1380 (void)modes; // unused
1381#else
1382 /* Initialize with permission bits--which are not implementation-optional */
1383 modes = file_stat.st_mode &
1384 (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
1385 if (S_ISDIR(file_stat.st_mode))
1386 modes |= UNX_IFDIR;
1387 if (S_ISREG(file_stat.st_mode))
1388 modes |= UNX_IFREG;
1389 if (S_ISLNK(file_stat.st_mode))
1390 modes |= UNX_IFLNK;
1391 if (S_ISBLK(file_stat.st_mode))
1392 modes |= UNX_IFBLK;
1393 if (S_ISCHR(file_stat.st_mode))
1394 modes |= UNX_IFCHR;
1395 if (S_ISFIFO(file_stat.st_mode))
1396 modes |= UNX_IFIFO;
1397 if (S_ISSOCK(file_stat.st_mode))
1398 modes |= UNX_IFSOCK;
1399 zip->entry.external_attr = (modes << 16) | !(file_stat.st_mode & S_IWUSR);
1400 if ((file_stat.st_mode & S_IFMT) == S_IFDIR) {
1401 zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
1402 }
1403#endif
1404
1405 zip->entry.m_time = file_stat.st_mtime;
1406
1407 if (!(stream = MZ_FOPEN(filename, "rb"))) {
1408 // Cannot open filename
1409 return ZIP_EOPNFILE;
1410 }
1411
1412 while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) >
1413 0) {
1414 if (zip_entry_write(zip, buf, n) < 0) {
1415 err = ZIP_EWRTENT;
1416 break;
1417 }
1418 }
1419 fclose(stream);
1420
1421 return err;
1422}
1423
1424ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) {
1425 mz_zip_archive *pzip = NULL;
1426 mz_uint idx;
1427 size_t size = 0;
1428
1429 if (!zip) {
1430 // zip_t handler is not initialized
1431 return (ssize_t)ZIP_ENOINIT;
1432 }
1433
1434 pzip = &(zip->archive);
1435 if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1436 zip->entry.index < (ssize_t)0) {
1437 // the entry is not found or we do not have read access
1438 return (ssize_t)ZIP_ENOENT;
1439 }
1440
1441 idx = (mz_uint)zip->entry.index;
1442 if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
1443 // the entry is a directory
1444 return (ssize_t)ZIP_EINVENTTYPE;
1445 }
1446
1447 *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0);
1448 if (*buf && bufsize) {
1449 *bufsize = size;
1450 }
1451 return (ssize_t)size;
1452}
1453
1454ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
1455 mz_zip_archive *pzip = NULL;
1456
1457 if (!zip) {
1458 // zip_t handler is not initialized
1459 return (ssize_t)ZIP_ENOINIT;
1460 }
1461
1462 pzip = &(zip->archive);
1463 if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1464 zip->entry.index < (ssize_t)0) {
1465 // the entry is not found or we do not have read access
1466 return (ssize_t)ZIP_ENOENT;
1467 }
1468
1469 if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
1470 buf, bufsize, 0, NULL, 0)) {
1471 return (ssize_t)ZIP_EMEMNOALLOC;
1472 }
1473
1474 return (ssize_t)zip->entry.uncomp_size;
1475}
1476
1477int zip_entry_fread(struct zip_t *zip, const char *filename) {
1478 mz_zip_archive *pzip = NULL;
1479 mz_uint idx;
1480 mz_uint32 xattr = 0;
1481 mz_zip_archive_file_stat info;
1482
1483 if (!zip) {
1484 // zip_t handler is not initialized
1485 return ZIP_ENOINIT;
1486 }
1487
1488 memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
1489 pzip = &(zip->archive);
1490 if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1491 zip->entry.index < (ssize_t)0) {
1492 // the entry is not found or we do not have read access
1493 return ZIP_ENOENT;
1494 }
1495
1496 idx = (mz_uint)zip->entry.index;
1497 if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
1498 // the entry is a directory
1499 return ZIP_EINVENTTYPE;
1500 }
1501
1502 if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) {
1503 return ZIP_ENOFILE;
1504 }
1505
1506#if defined(_MSC_VER) || defined(PS4)
1507 (void)xattr; // unused
1508#else
1509 if (!mz_zip_reader_file_stat(pzip, idx, &info)) {
1510 // Cannot get information about zip archive;
1511 return ZIP_ENOFILE;
1512 }
1513
1514 xattr = (info.m_external_attr >> 16) & 0xFFFF;
1515 if (xattr > 0 && xattr <= MZ_UINT16_MAX) {
1516 if (CHMOD(filename, (mode_t)xattr) < 0) {
1517 return ZIP_ENOPERM;
1518 }
1519 }
1520#endif
1521
1522 return 0;
1523}
1524
1525int zip_entry_extract(struct zip_t *zip,
1526 size_t (*on_extract)(void *arg, uint64_t offset,
1527 const void *buf, size_t bufsize),
1528 void *arg) {
1529 mz_zip_archive *pzip = NULL;
1530 mz_uint idx;
1531
1532 if (!zip) {
1533 // zip_t handler is not initialized
1534 return ZIP_ENOINIT;
1535 }
1536
1537 pzip = &(zip->archive);
1538 if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1539 zip->entry.index < (ssize_t)0) {
1540 // the entry is not found or we do not have read access
1541 return ZIP_ENOENT;
1542 }
1543
1544 idx = (mz_uint)zip->entry.index;
1545 return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0))
1546 ? 0
1547 : ZIP_EINVIDX;
1548}
1549
1550ssize_t zip_entries_total(struct zip_t *zip) {
1551 if (!zip) {
1552 // zip_t handler is not initialized
1553 return ZIP_ENOINIT;
1554 }
1555
1556 return (ssize_t)zip->archive.m_total_files;
1557}
1558
1559ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[],
1560 size_t len) {
1561 ssize_t n = 0;
1562 ssize_t err = 0;
1563 struct zip_entry_mark_t *entry_mark = NULL;
1564
1565 if (zip == NULL || (entries == NULL && len != 0)) {
1566 return ZIP_ENOINIT;
1567 }
1568
1569 if (entries == NULL && len == 0) {
1570 return 0;
1571 }
1572
1573 n = zip_entries_total(zip);
1574
1575 entry_mark = (struct zip_entry_mark_t *)calloc(
1576 (size_t)n, sizeof(struct zip_entry_mark_t));
1577 if (!entry_mark) {
1578 return ZIP_EOOMEM;
1579 }
1580
1581 zip->archive.m_zip_mode = MZ_ZIP_MODE_READING;
1582
1583 err = zip_entry_set(zip, entry_mark, n, entries, len);
1584 if (err < 0) {
1585 CLEANUP(entry_mark);
1586 return err;
1587 }
1588
1589 err = zip_entries_delete_mark(zip, entry_mark, (int)n);
1590 CLEANUP(entry_mark);
1591 return err;
1592}
1593
1594int zip_stream_extract(const char *stream, size_t size, const char *dir,
1595 int (*on_extract)(const char *filename, void *arg),
1596 void *arg) {
1597 mz_zip_archive zip_archive;
1598 if (!stream || !dir) {
1599 // Cannot parse zip archive stream
1600 return ZIP_ENOINIT;
1601 }
1602 if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) {
1603 // Cannot memset zip archive
1604 return ZIP_EMEMSET;
1605 }
1606 if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) {
1607 // Cannot initialize zip_archive reader
1608 return ZIP_ENOINIT;
1609 }
1610
1611 return zip_archive_extract(&zip_archive, dir, on_extract, arg);
1612}
1613
1614struct zip_t *zip_stream_open(const char *stream, size_t size, int level,
1615 char mode) {
1616 struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
1617 if (!zip) {
1618 return NULL;
1619 }
1620
1621 if (level < 0) {
1622 level = MZ_DEFAULT_LEVEL;
1623 }
1624 if ((level & 0xF) > MZ_UBER_COMPRESSION) {
1625 // Wrong compression level
1626 goto cleanup;
1627 }
1628 zip->level = (mz_uint)level;
1629
1630 if ((stream != NULL) && (size > 0) && (mode == 'r')) {
1631 if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) {
1632 goto cleanup;
1633 }
1634 } else if ((stream == NULL) && (size == 0) && (mode == 'w')) {
1635 // Create a new archive.
1636 if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) {
1637 // Cannot initialize zip_archive writer
1638 goto cleanup;
1639 }
1640 } else {
1641 goto cleanup;
1642 }
1643 return zip;
1644
1645cleanup:
1646 CLEANUP(zip);
1647 return NULL;
1648}
1649
1650ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize) {
1651 size_t n;
1652
1653 if (!zip) {
1654 return (ssize_t)ZIP_ENOINIT;
1655 }
1656 zip_archive_finalize(&(zip->archive));
1657
1658 n = (size_t)zip->archive.m_archive_size;
1659 if (bufsize != NULL) {
1660 *bufsize = n;
1661 }
1662
1663 *buf = calloc(sizeof(unsigned char), n);
1664 memcpy(*buf, zip->archive.m_pState->m_pMem, n);
1665
1666 return (ssize_t)n;
1667}
1668
1669void zip_stream_close(struct zip_t *zip) {
1670 if (zip) {
1671 mz_zip_writer_end(&(zip->archive));
1672 mz_zip_reader_end(&(zip->archive));
1673 CLEANUP(zip);
1674 }
1675}
1676
1677int zip_create(const char *zipname, const char *filenames[], size_t len) {
1678 int err = 0;
1679 size_t i;
1680 mz_zip_archive zip_archive;
1681 struct MZ_FILE_STAT_STRUCT file_stat;
1682 mz_uint32 ext_attributes = 0;
1683 mz_uint16 modes;
1684
1685 if (!zipname || strlen(zipname) < 1) {
1686 // zip_t archive name is empty or NULL
1687 return ZIP_EINVZIPNAME;
1688 }
1689
1690 // Create a new archive.
1691 if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
1692 // Cannot memset zip archive
1693 return ZIP_EMEMSET;
1694 }
1695
1696 if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) {
1697 // Cannot initialize zip_archive writer
1698 return ZIP_ENOINIT;
1699 }
1700
1701 if (!memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT))) {
1702 return ZIP_EMEMSET;
1703 }
1704
1705 for (i = 0; i < len; ++i) {
1706 const char *name = filenames[i];
1707 if (!name) {
1708 err = ZIP_EINVENTNAME;
1709 break;
1710 }
1711
1712 if (MZ_FILE_STAT(name, &file_stat) != 0) {
1713 // problem getting information - check errno
1714 err = ZIP_ENOFILE;
1715 break;
1716 }
1717
1718#if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP)
1719 (void)modes; // unused
1720#else
1721
1722 /* Initialize with permission bits--which are not implementation-optional */
1723 modes = file_stat.st_mode &
1724 (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
1725 if (S_ISDIR(file_stat.st_mode))
1726 modes |= UNX_IFDIR;
1727 if (S_ISREG(file_stat.st_mode))
1728 modes |= UNX_IFREG;
1729 if (S_ISLNK(file_stat.st_mode))
1730 modes |= UNX_IFLNK;
1731 if (S_ISBLK(file_stat.st_mode))
1732 modes |= UNX_IFBLK;
1733 if (S_ISCHR(file_stat.st_mode))
1734 modes |= UNX_IFCHR;
1735 if (S_ISFIFO(file_stat.st_mode))
1736 modes |= UNX_IFIFO;
1737 if (S_ISSOCK(file_stat.st_mode))
1738 modes |= UNX_IFSOCK;
1739 ext_attributes = (modes << 16) | !(file_stat.st_mode & S_IWUSR);
1740 if ((file_stat.st_mode & S_IFMT) == S_IFDIR) {
1741 ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
1742 }
1743#endif
1744
1745 if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0,
1746 ZIP_DEFAULT_COMPRESSION_LEVEL,
1747 ext_attributes)) {
1748 // Cannot add file to zip_archive
1749 err = ZIP_ENOFILE;
1750 break;
1751 }
1752 }
1753
1754 mz_zip_writer_finalize_archive(&zip_archive);
1755 mz_zip_writer_end(&zip_archive);
1756 return err;
1757}
1758
1759int zip_extract(const char *zipname, const char *dir,
1760 int (*on_extract)(const char *filename, void *arg), void *arg) {
1761 mz_zip_archive zip_archive;
1762
1763 if (!zipname || !dir) {
1764 // Cannot parse zip archive name
1765 return ZIP_EINVZIPNAME;
1766 }
1767
1768 if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) {
1769 // Cannot memset zip archive
1770 return ZIP_EMEMSET;
1771 }
1772
1773 // Now try to open the archive.
1774 if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) {
1775 // Cannot initialize zip_archive reader
1776 return ZIP_ENOINIT;
1777 }
1778
1779 return zip_archive_extract(&zip_archive, dir, on_extract, arg);
1780}