Coverage Report

Created: 2025-11-25 06:29

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