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.
28 #include <sys/types.h>
44 # define USEWIN32IOAPI
51 # define MKDIR(d) _mkdir(d)
52 # define CHDIR(d) _chdir(d)
54 # define MKDIR(d) mkdir(d, 0775)
55 # define CHDIR(d) chdir(d)
59 #define DEBUG_PRINTF 0
60 #define debug_printf(...) \
61 do { if (DEBUG_PRINTF) fprintf(stderr, __VA_ARGS__); } while (0)
64 static char **zip_entries = NULL;
65 static int num_zip_entries = 0;
66 static int max_zip_entries = 0;
67 static const int max_alloc_zip_entries = 256;
69 static void add_zip_entry(const char *s)
71 if (zip_entries == NULL)
73 max_zip_entries = max_alloc_zip_entries;
75 zip_entries = malloc(max_zip_entries * sizeof(char *));
77 if (zip_entries == NULL)
83 if (num_zip_entries >= max_zip_entries - 1)
85 max_zip_entries += max_alloc_zip_entries;
87 zip_entries = realloc(zip_entries, max_zip_entries * sizeof(char *));
89 if (zip_entries == NULL)
93 zip_entries[num_zip_entries++] = strdup(s);
94 zip_entries[num_zip_entries] = NULL; // ensure NULL terminated list
97 static void free_zip_entries(void)
99 if (zip_entries == NULL)
104 for (i = 0; i < num_zip_entries; i++)
105 free(zip_entries[i]);
115 static int invalid_date(const struct tm *ptm)
117 #define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
118 return (!datevalue_in_range(0, 207, ptm->tm_year) ||
119 !datevalue_in_range(0, 11, ptm->tm_mon) ||
120 !datevalue_in_range(1, 31, ptm->tm_mday) ||
121 !datevalue_in_range(0, 23, ptm->tm_hour) ||
122 !datevalue_in_range(0, 59, ptm->tm_min) ||
123 !datevalue_in_range(0, 59, ptm->tm_sec));
124 #undef datevalue_in_range
127 // Conversion without validation
128 static void dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
130 uint64_t date = (uint64_t)(dos_date >> 16);
132 ptm->tm_mday = (uint16_t)(date & 0x1f);
133 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
134 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
135 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
136 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
137 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
141 static int dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
143 dosdate_to_raw_tm(dos_date, ptm);
145 if (invalid_date(ptm))
147 // Invalid date stored, so don't return it.
148 memset(ptm, 0, sizeof(struct tm));
155 static time_t dosdate_to_time_t(uint64_t dos_date)
158 dosdate_to_raw_tm(dos_date, &ptm);
163 static void change_file_date(const char *path, uint32_t dos_date)
166 HANDLE handle = NULL;
167 FILETIME ftm, ftm_local, ftm_create, ftm_access, ftm_modified;
169 handle = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
170 if (handle != INVALID_HANDLE_VALUE)
172 GetFileTime(handle, &ftm_create, &ftm_access, &ftm_modified);
173 DosDateTimeToFileTime((WORD)(dos_date >> 16), (WORD)dos_date, &ftm_local);
174 LocalFileTimeToFileTime(&ftm_local, &ftm);
175 SetFileTime(handle, &ftm, &ftm_access, &ftm);
180 ut.actime = ut.modtime = dosdate_to_time_t(dos_date);
185 static int makedir(const char *newdir)
189 int len = (int)strlen(newdir);
194 buffer = (char*)malloc(len + 1);
197 debug_printf("Error allocating memory\n");
201 strcpy(buffer, newdir);
203 if (buffer[len - 1] == '/')
206 if (MKDIR(buffer) == 0)
216 while (*p && *p != '\\' && *p != '/')
221 if ((MKDIR(buffer) == -1) && (errno == ENOENT))
223 debug_printf("couldn't create directory %s (%d)\n", buffer, errno);
238 static FILE *get_file_handle(const char *path)
242 wchar_t *pathWide = NULL;
245 pathLength = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) + 1;
246 pathWide = (wchar_t*)calloc(pathLength, sizeof(wchar_t));
247 MultiByteToWideChar(CP_UTF8, 0, path, -1, pathWide, pathLength);
248 handle = _wfopen((const wchar_t*)pathWide, L"rb");
251 handle = fopen64(path, "rb");
257 static int check_file_exists(const char *path)
259 FILE *handle = get_file_handle(path);
266 static void display_zpos64(uint64_t n, int size_char)
268 /* To avoid compatibility problem we do here the conversion */
269 char number[21] = { 0 };
272 int size_display_string = 19;
276 number[offset] = (char)((n % 10) + '0');
277 if (number[offset] != '0')
285 size_display_string -= pos_string;
286 while (size_char-- > size_display_string)
288 debug_printf("%s", &number[pos_string]);
291 static int miniunz_list(unzFile uf)
295 int err = unzGoToFirstFile(uf);
298 debug_printf("error %d with zipfile in unzGoToFirstFile\n", err);
302 debug_printf(" Length Method Size Ratio Date Time CRC-32 Name\n");
303 debug_printf(" ------ ------ ---- ----- ---- ---- ------ ----\n");
307 char filename_inzip[256] = {0};
308 unz_file_info64 file_info = {0};
310 struct tm tmu_date = { 0 };
311 const char *string_method = NULL;
312 char char_crypt = ' ';
314 err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
317 debug_printf("error %d with zipfile in unzGetCurrentFileInfo\n", err);
321 add_zip_entry(filename_inzip);
323 if (file_info.uncompressed_size > 0)
324 ratio = (uint32_t)((file_info.compressed_size * 100) / file_info.uncompressed_size);
326 /* Display a '*' if the file is encrypted */
327 if ((file_info.flag & 1) != 0)
330 if (file_info.compression_method == 0)
331 string_method = "Stored";
332 else if (file_info.compression_method == Z_DEFLATED)
334 uint16_t level = (uint16_t)((file_info.flag & 0x6) / 2);
336 string_method = "Defl:N";
338 string_method = "Defl:X";
339 else if ((level == 2) || (level == 3))
340 string_method = "Defl:F"; /* 2:fast , 3 : extra fast*/
342 string_method = "Unkn. ";
344 else if (file_info.compression_method == Z_BZIP2ED)
346 string_method = "BZip2 ";
349 string_method = "Unkn. ";
351 display_zpos64(file_info.uncompressed_size, 7);
352 debug_printf(" %6s%c", string_method, char_crypt);
353 display_zpos64(file_info.compressed_size, 7);
355 dosdate_to_tm(file_info.dos_date, &tmu_date);
356 debug_printf(" %3u%% %2.2u-%2.2u-%2.2u %2.2u:%2.2u %8.8x %s\n", ratio,
357 (uint32_t)tmu_date.tm_mon + 1, (uint32_t)tmu_date.tm_mday,
358 (uint32_t)tmu_date.tm_year % 100,
359 (uint32_t)tmu_date.tm_hour, (uint32_t)tmu_date.tm_min,
360 file_info.crc, filename_inzip);
362 err = unzGoToNextFile(uf);
364 while (err == UNZ_OK);
366 if (err != UNZ_END_OF_LIST_OF_FILE && err != UNZ_OK)
368 debug_printf("error %d with zipfile in unzGoToNextFile\n", err);
375 static int miniunz_extract_currentfile(unzFile uf, int opt_extract_without_path, int *popt_overwrite, const char *password)
377 unz_file_info64 file_info = {0};
380 uint16_t size_buf = 8192;
382 int errclose = UNZ_OK;
384 char filename_inzip[256] = {0};
385 char *filename_withoutpath = NULL;
386 const char *write_filename = NULL;
389 err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
392 debug_printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
396 add_zip_entry(filename_inzip);
398 p = filename_withoutpath = filename_inzip;
401 if ((*p == '/') || (*p == '\\'))
402 filename_withoutpath = p+1;
406 /* If zip entry is a directory then create it on disk */
407 if (*filename_withoutpath == 0)
409 if (opt_extract_without_path == 0)
411 debug_printf("creating directory: %s\n", filename_inzip);
412 MKDIR(filename_inzip);
417 buf = (void*)malloc(size_buf);
420 debug_printf("Error allocating memory\n");
421 return UNZ_INTERNALERROR;
424 err = unzOpenCurrentFilePassword(uf, password);
426 debug_printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err);
428 if (opt_extract_without_path)
429 write_filename = filename_withoutpath;
431 write_filename = filename_inzip;
433 /* Determine if the file should be overwritten or not and ask the user if needed */
434 if ((err == UNZ_OK) && (*popt_overwrite == 0) && (check_file_exists(write_filename)))
440 debug_printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ", write_filename);
441 if (scanf("%1s", answer) != 1)
444 if ((rep >= 'a') && (rep <= 'z'))
447 while ((rep != 'Y') && (rep != 'N') && (rep != 'A'));
455 /* Create the file on disk so we can unzip to it */
456 if ((skip == 0) && (err == UNZ_OK))
458 fout = fopen64(write_filename, "wb");
459 /* Some zips don't contain directory alone before file */
460 if ((fout == NULL) && (opt_extract_without_path == 0) &&
461 (filename_withoutpath != (char*)filename_inzip))
463 char c = *(filename_withoutpath-1);
464 *(filename_withoutpath-1) = 0;
465 makedir(write_filename);
466 *(filename_withoutpath-1) = c;
467 fout = fopen64(write_filename, "wb");
470 debug_printf("error opening %s\n", write_filename);
473 /* Read from the zip, unzip to buffer, and write to disk */
476 debug_printf(" extracting: %s\n", write_filename);
480 err = unzReadCurrentFile(uf, buf, size_buf);
483 debug_printf("error %d with zipfile in unzReadCurrentFile\n", err);
488 if (fwrite(buf, err, 1, fout) != 1)
490 debug_printf("error %d in writing extracted file\n", errno);
500 /* Set the time of the file that has been unzipped */
502 change_file_date(write_filename, file_info.dos_date);
505 errclose = unzCloseCurrentFile(uf);
506 if (errclose != UNZ_OK)
507 debug_printf("error %d with zipfile in unzCloseCurrentFile\n", errclose);
513 static int miniunz_extract_all(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char *password)
517 int err = unzGoToFirstFile(uf);
520 debug_printf("error %d with zipfile in unzGoToFirstFile\n", err);
526 err = miniunz_extract_currentfile(uf, opt_extract_without_path, &opt_overwrite, password);
529 err = unzGoToNextFile(uf);
531 while (err == UNZ_OK);
533 if (err != UNZ_END_OF_LIST_OF_FILE)
535 debug_printf("error %d with zipfile in unzGoToNextFile\n", err);
541 char **zip_list(char *filename)
543 if (filename == NULL)
545 debug_printf("No filename specified!\n");
551 zlib_filefunc64_def ffunc;
552 fill_win32_filefunc64A(&ffunc);
553 unzFile uf = unzOpen2_64(filename, &ffunc);
555 unzFile uf = unzOpen64(filename);
560 debug_printf("Cannot open file '%s'!\n", filename);
565 debug_printf("File '%s' opened.\n", filename);
567 int success = (miniunz_list(uf) == UNZ_OK);
577 char **zip_extract(char *filename, char *directory)
579 if (filename == NULL)
581 debug_printf("No zip filename specified!\n");
586 if (directory == NULL)
588 debug_printf("No target directory specified!\n");
593 struct stat file_status;
595 if (stat(directory, &file_status) != 0 || !S_ISDIR(file_status.st_mode))
597 debug_printf("Directory '%s' does not exist!\n", directory);
603 zlib_filefunc64_def ffunc;
604 fill_win32_filefunc64A(&ffunc);
605 unzFile uf = unzOpen2_64(filename, &ffunc);
607 unzFile uf = unzOpen64(filename);
612 debug_printf("Cannot open file '%s'!\n", filename);
617 debug_printf("File '%s' opened.\n", filename);
619 int max_directory_size = 1024;
620 char last_directory[max_directory_size];
622 if (getcwd(last_directory, max_directory_size) == NULL)
624 debug_printf("Cannot get current directory!\n");
631 if (CHDIR(directory) != 0) // change to target directory
633 debug_printf("Cannot change to directory '%s'!\n", directory);
640 int success = (miniunz_extract_all(uf, 0, 1, NULL) == UNZ_OK);
642 if (CHDIR(last_directory) != 0) // change back to previous directory
644 debug_printf("Cannot change to directory '%s'!\n", last_directory);