2 Version 1.2.0, September 16th, 2017
3 sample part of the MiniZip project
5 Copyright (C) 2012-2017 Nathan Moinvaziri
6 https://github.com/nmoinvaz/minizip
7 Copyright (C) 2009-2010 Mathias Svensson
8 Modifications for Zip64 support
10 Copyright (C) 2007-2008 Even Rouault
11 Modifications of Unzip for Zip64
12 Copyright (C) 1998-2010 Gilles Vollant
13 http://www.winimage.com/zLibDll/minizip.html
15 This program is distributed under the terms of the same license as zlib.
16 See the accompanying LICENSE file for the full text of the license.
19 #if defined(__linux__)
20 // Linux needs this to support file operation on files larger then 4+GB
21 # ifndef __USE_FILE_OFFSET64
22 # define __USE_FILE_OFFSET64
24 # ifndef __USE_LARGEFILE64
25 # define __USE_LARGEFILE64
27 # ifndef _LARGEFILE64_SOURCE
28 # define _LARGEFILE64_SOURCE
30 # ifndef _FILE_OFFSET_BIT
31 # define _FILE_OFFSET_BIT 64
44 #include <sys/types.h>
60 # define USEWIN32IOAPI
67 # define MKDIR(d) _mkdir(d)
68 # define CHDIR(d) _chdir(d)
70 # define MKDIR(d) mkdir(d, 0775)
71 # define CHDIR(d) chdir(d)
75 #define DEBUG_PRINTF 0
76 #define debug_printf(...) \
77 do { if (DEBUG_PRINTF) fprintf(stderr, __VA_ARGS__); } while (0)
80 static char **zip_entries = NULL;
81 static int num_zip_entries = 0;
82 static int max_zip_entries = 0;
83 static const int max_alloc_zip_entries = 256;
85 static void add_zip_entry(const char *s)
87 if (zip_entries == NULL)
89 max_zip_entries = max_alloc_zip_entries;
91 zip_entries = malloc(max_zip_entries * sizeof(char *));
93 if (zip_entries == NULL)
99 if (num_zip_entries >= max_zip_entries - 1)
101 max_zip_entries += max_alloc_zip_entries;
103 zip_entries = realloc(zip_entries, max_zip_entries * sizeof(char *));
105 if (zip_entries == NULL)
109 zip_entries[num_zip_entries++] = strdup(s);
110 zip_entries[num_zip_entries] = NULL; // ensure NULL terminated list
113 static void free_zip_entries(void)
115 if (zip_entries == NULL)
120 for (i = 0; i < num_zip_entries; i++)
121 free(zip_entries[i]);
131 static int invalid_date(const struct tm *ptm)
133 #define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
134 return (!datevalue_in_range(0, 207, ptm->tm_year) ||
135 !datevalue_in_range(0, 11, ptm->tm_mon) ||
136 !datevalue_in_range(1, 31, ptm->tm_mday) ||
137 !datevalue_in_range(0, 23, ptm->tm_hour) ||
138 !datevalue_in_range(0, 59, ptm->tm_min) ||
139 !datevalue_in_range(0, 59, ptm->tm_sec));
140 #undef datevalue_in_range
143 // Conversion without validation
144 static void dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
146 uint64_t date = (uint64_t)(dos_date >> 16);
148 ptm->tm_mday = (uint16_t)(date & 0x1f);
149 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
150 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
151 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
152 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
153 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
157 static int dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
159 dosdate_to_raw_tm(dos_date, ptm);
161 if (invalid_date(ptm))
163 // Invalid date stored, so don't return it.
164 memset(ptm, 0, sizeof(struct tm));
171 static time_t dosdate_to_time_t(uint64_t dos_date)
174 dosdate_to_raw_tm(dos_date, &ptm);
179 static void change_file_date(const char *path, uint32_t dos_date)
182 HANDLE handle = NULL;
183 FILETIME ftm, ftm_local, ftm_create, ftm_access, ftm_modified;
185 handle = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
186 if (handle != INVALID_HANDLE_VALUE)
188 GetFileTime(handle, &ftm_create, &ftm_access, &ftm_modified);
189 DosDateTimeToFileTime((WORD)(dos_date >> 16), (WORD)dos_date, &ftm_local);
190 LocalFileTimeToFileTime(&ftm_local, &ftm);
191 SetFileTime(handle, &ftm, &ftm_access, &ftm);
196 ut.actime = ut.modtime = dosdate_to_time_t(dos_date);
201 static int makedir(const char *newdir)
205 int len = (int)strlen(newdir);
210 buffer = (char*)malloc(len + 1);
213 debug_printf("Error allocating memory\n");
217 strcpy(buffer, newdir);
219 if (buffer[len - 1] == '/')
222 if (MKDIR(buffer) == 0)
232 while (*p && *p != '\\' && *p != '/')
237 if ((MKDIR(buffer) == -1) && (errno == ENOENT))
239 debug_printf("couldn't create directory %s (%d)\n", buffer, errno);
254 static FILE *get_file_handle(const char *path)
258 wchar_t *pathWide = NULL;
261 pathLength = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) + 1;
262 pathWide = (wchar_t*)calloc(pathLength, sizeof(wchar_t));
263 MultiByteToWideChar(CP_UTF8, 0, path, -1, pathWide, pathLength);
264 handle = _wfopen((const wchar_t*)pathWide, L"rb");
267 handle = fopen64(path, "rb");
273 static int check_file_exists(const char *path)
275 FILE *handle = get_file_handle(path);
282 static void display_zpos64(uint64_t n, int size_char)
284 /* To avoid compatibility problem we do here the conversion */
285 char number[21] = { 0 };
288 int size_display_string = 19;
292 number[offset] = (char)((n % 10) + '0');
293 if (number[offset] != '0')
301 size_display_string -= pos_string;
302 while (size_char-- > size_display_string)
304 debug_printf("%s", &number[pos_string]);
307 static int miniunz_list(unzFile uf)
311 int err = unzGoToFirstFile(uf);
314 debug_printf("error %d with zipfile in unzGoToFirstFile\n", err);
318 debug_printf(" Length Method Size Ratio Date Time CRC-32 Name\n");
319 debug_printf(" ------ ------ ---- ----- ---- ---- ------ ----\n");
323 char filename_inzip[256] = {0};
324 unz_file_info64 file_info = {0};
326 struct tm tmu_date = { 0 };
327 const char *string_method = NULL;
328 char char_crypt = ' ';
330 err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
333 debug_printf("error %d with zipfile in unzGetCurrentFileInfo\n", err);
337 add_zip_entry(filename_inzip);
339 if (file_info.uncompressed_size > 0)
340 ratio = (uint32_t)((file_info.compressed_size * 100) / file_info.uncompressed_size);
342 /* Display a '*' if the file is encrypted */
343 if ((file_info.flag & 1) != 0)
346 if (file_info.compression_method == 0)
347 string_method = "Stored";
348 else if (file_info.compression_method == Z_DEFLATED)
350 uint16_t level = (uint16_t)((file_info.flag & 0x6) / 2);
352 string_method = "Defl:N";
354 string_method = "Defl:X";
355 else if ((level == 2) || (level == 3))
356 string_method = "Defl:F"; /* 2:fast , 3 : extra fast*/
358 string_method = "Unkn. ";
360 else if (file_info.compression_method == Z_BZIP2ED)
362 string_method = "BZip2 ";
365 string_method = "Unkn. ";
367 display_zpos64(file_info.uncompressed_size, 7);
368 debug_printf(" %6s%c", string_method, char_crypt);
369 display_zpos64(file_info.compressed_size, 7);
371 dosdate_to_tm(file_info.dos_date, &tmu_date);
372 debug_printf(" %3u%% %2.2u-%2.2u-%2.2u %2.2u:%2.2u %8.8x %s\n", ratio,
373 (uint32_t)tmu_date.tm_mon + 1, (uint32_t)tmu_date.tm_mday,
374 (uint32_t)tmu_date.tm_year % 100,
375 (uint32_t)tmu_date.tm_hour, (uint32_t)tmu_date.tm_min,
376 file_info.crc, filename_inzip);
378 err = unzGoToNextFile(uf);
380 while (err == UNZ_OK);
382 if (err != UNZ_END_OF_LIST_OF_FILE && err != UNZ_OK)
384 debug_printf("error %d with zipfile in unzGoToNextFile\n", err);
391 static int miniunz_extract_currentfile(unzFile uf, int opt_extract_without_path, int *popt_overwrite, const char *password)
393 unz_file_info64 file_info = {0};
396 uint16_t size_buf = 8192;
398 int errclose = UNZ_OK;
400 char filename_inzip[256] = {0};
401 char *filename_withoutpath = NULL;
402 const char *write_filename = NULL;
405 err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
408 debug_printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
412 add_zip_entry(filename_inzip);
414 p = filename_withoutpath = filename_inzip;
417 if ((*p == '/') || (*p == '\\'))
418 filename_withoutpath = p+1;
422 /* If zip entry is a directory then create it on disk */
423 if (*filename_withoutpath == 0)
425 if (opt_extract_without_path == 0)
427 debug_printf("creating directory: %s\n", filename_inzip);
428 MKDIR(filename_inzip);
433 buf = (void*)malloc(size_buf);
436 debug_printf("Error allocating memory\n");
437 return UNZ_INTERNALERROR;
440 err = unzOpenCurrentFilePassword(uf, password);
442 debug_printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err);
444 if (opt_extract_without_path)
445 write_filename = filename_withoutpath;
447 write_filename = filename_inzip;
449 /* Determine if the file should be overwritten or not and ask the user if needed */
450 if ((err == UNZ_OK) && (*popt_overwrite == 0) && (check_file_exists(write_filename)))
456 debug_printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ", write_filename);
457 if (scanf("%1s", answer) != 1)
460 if ((rep >= 'a') && (rep <= 'z'))
463 while ((rep != 'Y') && (rep != 'N') && (rep != 'A'));
471 /* Create the file on disk so we can unzip to it */
472 if ((skip == 0) && (err == UNZ_OK))
474 fout = fopen64(write_filename, "wb");
475 /* Some zips don't contain directory alone before file */
476 if ((fout == NULL) && (opt_extract_without_path == 0) &&
477 (filename_withoutpath != (char*)filename_inzip))
479 char c = *(filename_withoutpath-1);
480 *(filename_withoutpath-1) = 0;
481 makedir(write_filename);
482 *(filename_withoutpath-1) = c;
483 fout = fopen64(write_filename, "wb");
486 debug_printf("error opening %s\n", write_filename);
489 /* Read from the zip, unzip to buffer, and write to disk */
492 debug_printf(" extracting: %s\n", write_filename);
496 err = unzReadCurrentFile(uf, buf, size_buf);
499 debug_printf("error %d with zipfile in unzReadCurrentFile\n", err);
504 if (fwrite(buf, err, 1, fout) != 1)
506 debug_printf("error %d in writing extracted file\n", errno);
516 /* Set the time of the file that has been unzipped */
518 change_file_date(write_filename, file_info.dos_date);
521 errclose = unzCloseCurrentFile(uf);
522 if (errclose != UNZ_OK)
523 debug_printf("error %d with zipfile in unzCloseCurrentFile\n", errclose);
529 static int miniunz_extract_all(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char *password)
533 int err = unzGoToFirstFile(uf);
536 debug_printf("error %d with zipfile in unzGoToFirstFile\n", err);
542 err = miniunz_extract_currentfile(uf, opt_extract_without_path, &opt_overwrite, password);
545 err = unzGoToNextFile(uf);
547 while (err == UNZ_OK);
549 if (err != UNZ_END_OF_LIST_OF_FILE)
551 debug_printf("error %d with zipfile in unzGoToNextFile\n", err);
557 char **zip_list(char *filename)
559 if (filename == NULL)
561 debug_printf("No filename specified!\n");
567 zlib_filefunc64_def ffunc;
568 fill_win32_filefunc64A(&ffunc);
569 unzFile uf = unzOpen2_64(filename, &ffunc);
571 unzFile uf = unzOpen64(filename);
576 debug_printf("Cannot open file '%s'!\n", filename);
581 debug_printf("File '%s' opened.\n", filename);
583 int success = (miniunz_list(uf) == UNZ_OK);
593 char **zip_extract(char *filename, char *directory)
595 if (filename == NULL)
597 debug_printf("No zip filename specified!\n");
602 if (directory == NULL)
604 debug_printf("No target directory specified!\n");
609 struct stat file_status;
611 if (stat(directory, &file_status) != 0 || !S_ISDIR(file_status.st_mode))
613 debug_printf("Directory '%s' does not exist!\n", directory);
619 zlib_filefunc64_def ffunc;
620 fill_win32_filefunc64A(&ffunc);
621 unzFile uf = unzOpen2_64(filename, &ffunc);
623 unzFile uf = unzOpen64(filename);
628 debug_printf("Cannot open file '%s'!\n", filename);
633 debug_printf("File '%s' opened.\n", filename);
635 int max_directory_size = 1024;
636 char last_directory[max_directory_size];
638 if (getcwd(last_directory, max_directory_size) == NULL)
640 debug_printf("Cannot get current directory!\n");
647 if (CHDIR(directory)) // change to target directory
649 debug_printf("Cannot change to directory '%s'!\n", directory);
656 int success = (miniunz_extract_all(uf, 0, 1, NULL) == UNZ_OK);
658 CHDIR(last_directory); // change back to previous directory