1 /*
2 miniunz.c
3 Version 1.1, February 14h, 2010
4 sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
5
6 Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
7
8 Modifications of Unzip for Zip64
9 Copyright (C) 2007-2008 Even Rouault
10
11 Modifications for Zip64 support on both zip and unzip
12 Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
13 */
14
15 #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
16 #ifndef __USE_FILE_OFFSET64
17 #define __USE_FILE_OFFSET64
18 #endif
19 #ifndef __USE_LARGEFILE64
20 #define __USE_LARGEFILE64
21 #endif
22 #ifndef _LARGEFILE64_SOURCE
23 #define _LARGEFILE64_SOURCE
24 #endif
25 #ifndef _FILE_OFFSET_BIT
26 #define _FILE_OFFSET_BIT 64
27 #endif
28 #endif
29
30 #if defined(__APPLE__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64)
31 // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
32 #define FOPEN_FUNC(filename, mode) fopen(filename, mode)
33 #define FTELLO_FUNC(stream) ftello(stream)
34 #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
35 #else
36 #define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
37 #define FTELLO_FUNC(stream) ftello64(stream)
38 #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
39 #endif
40
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <sys/stat.h>
49
50 #ifdef _WIN32
51 # include <direct.h>
52 # include <io.h>
53 #else
54 # include <unistd.h>
55 # include <utime.h>
56 #endif
57
58
59 #include "unzip.h"
60
61 #define CASESENSITIVITY (0)
62 #define WRITEBUFFERSIZE (8192)
63 #define MAXFILENAME (256)
64
65 #ifdef _WIN32
66 #define USEWIN32IOAPI
67 #include "iowin32.h"
68 #endif
69 /*
70 mini unzip, demo of unzip package
71
72 usage :
73 Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir]
74
75 list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
76 if it exists
77 */
78
79
80 /* change_file_date : change the date/time of a file
81 filename : the filename of the file where date/time must be modified
82 dosdate : the new date at the MSDOS format (4 bytes)
83 tmu_date : the SAME new date at the tm_unz format */
change_file_date(const char * filename,uLong dosdate,tm_unz tmu_date)84 static void change_file_date(const char *filename, uLong dosdate, tm_unz tmu_date)
85 {
86 #ifdef _WIN32
87 HANDLE hFile;
88 FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
89
90 hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
91 0,NULL,OPEN_EXISTING,0,NULL);
92 GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
93 DosDateTimeToFileTime((WORD)(dosdate >> 16),(WORD)dosdate,&ftLocal);
94 LocalFileTimeToFileTime(&ftLocal,&ftm);
95 SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
96 CloseHandle(hFile);
97 #else
98 #if defined(unix) || defined(__APPLE__)
99 (void)dosdate;
100 struct utimbuf ut;
101 struct tm newdate;
102 newdate.tm_sec = tmu_date.tm_sec;
103 newdate.tm_min = tmu_date.tm_min;
104 newdate.tm_hour = tmu_date.tm_hour;
105 newdate.tm_mday = tmu_date.tm_mday;
106 newdate.tm_mon = tmu_date.tm_mon;
107 if (tmu_date.tm_year > 1900)
108 {
109 newdate.tm_year = tmu_date.tm_year - 1900;
110 }
111 else
112 {
113 newdate.tm_year = tmu_date.tm_year ;
114 }
115 newdate.tm_isdst = -1;
116
117 ut.actime = ut.modtime = mktime(&newdate);
118 utime(filename,&ut);
119 #else
120 (void)filename;
121 (void)dosdate;
122 (void)tmu_date;
123 #endif
124 #endif
125 }
126
127
128 /* mymkdir and change_file_date are not 100 % portable
129 As I don't know well Unix, I wait feedback for the unix portion */
130
mymkdir(const char * dirname)131 static int mymkdir(const char* dirname)
132 {
133 int ret = 0;
134 #ifdef _WIN32
135 ret = _mkdir(dirname);
136 #elif unix
137 ret = mkdir (dirname,0775);
138 #elif __APPLE__
139 ret = mkdir (dirname,0775);
140 #else
141 (void)dirname;
142 #endif
143 return ret;
144 }
145
makedir(const char * newdir)146 static int makedir(const char *newdir)
147 {
148 char *buffer ;
149 char *p;
150 size_t len = strlen(newdir);
151
152 if (len == 0)
153 {
154 return 0;
155 }
156
157 buffer = (char*)malloc(len+1);
158 if (buffer==NULL)
159 {
160 printf("Error allocating memory\n");
161 return UNZ_INTERNALERROR;
162 }
163 strcpy(buffer,newdir);
164
165 if (buffer[len-1] == '/') {
166 buffer[len-1] = '\0';
167 }
168 if (mymkdir(buffer) == 0)
169 {
170 free(buffer);
171 return 1;
172 }
173
174 p = buffer+1;
175 while (1)
176 {
177 char hold;
178
179 while(*p && *p != '\\' && *p != '/')
180 {
181 p++;
182 }
183 hold = *p;
184 *p = 0;
185 if ((mymkdir(buffer) == -1) && (errno == ENOENT))
186 {
187 printf("couldn't create directory %s\n",buffer);
188 free(buffer);
189 return 0;
190 }
191 if (hold == 0)
192 {
193 break;
194 }
195 *p++ = hold;
196 }
197 free(buffer);
198 return 1;
199 }
200
do_banner(void)201 static void do_banner(void)
202 {
203 printf("MiniUnz 1.1, demo of zLib + Unz package written by Gilles Vollant\n");
204 printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
205 }
206
do_help(void)207 static void do_help(void)
208 {
209 printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \
210 " -e Extract without pathname (junk paths)\n" \
211 " -x Extract with pathname\n" \
212 " -v list files\n" \
213 " -l list files\n" \
214 " -d directory to extract into\n" \
215 " -o overwrite files without prompting\n" \
216 " -p extract encrypted file using password\n\n");
217 }
218
Display64BitsSize(ZPOS64_T n,int size_char)219 static void Display64BitsSize(ZPOS64_T n, int size_char)
220 {
221 /* to avoid compatibility problem , we do here the conversion */
222 char number[21];
223 int offset=19;
224 int pos_string = 19;
225 number[20]=0;
226 for (;;)
227 {
228 number[offset]=(char)((n%10)+'0');
229 if (number[offset] != '0')
230 {
231 pos_string=offset;
232 }
233 n/=10;
234 if (offset==0)
235 {
236 break;
237 }
238 offset--;
239 }
240 {
241 int size_display_string = 19-pos_string;
242 while (size_char > size_display_string)
243 {
244 size_char--;
245 printf(" ");
246 }
247 }
248
249 printf("%s",&number[pos_string]);
250 }
251
do_list(unzFile uf)252 static int do_list(unzFile uf)
253 {
254 uLong i;
255 unz_global_info64 gi;
256 int err;
257
258 err = unzGetGlobalInfo64(uf,&gi);
259 if (err != UNZ_OK)
260 {
261 printf("error %d with zipfile in unzGetGlobalInfo \n",err);
262 }
263 printf(" Length Method Size Ratio Date Time CRC-32 Name\n");
264 printf(" ------ ------ ---- ----- ---- ---- ------ ----\n");
265 for (i = 0;i < gi.number_entry; i++)
266 {
267 char filename_inzip[256];
268 unz_file_info64 file_info;
269 uLong ratio = 0;
270 const char *string_method = "";
271 char charCrypt = ' ';
272 err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
273 if (err != UNZ_OK)
274 {
275 printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
276 break;
277 }
278 if (file_info.uncompressed_size > 0)
279 ratio = (uLong)((file_info.compressed_size*100) / file_info.uncompressed_size);
280
281 /* display a '*' if the file is encrypted */
282 if ((file_info.flag & 1) != 0)
283 {
284 charCrypt = '*';
285 }
286
287 if (file_info.compression_method == 0)
288 {
289 string_method = "Stored";
290 }
291 else
292 if (file_info.compression_method == Z_DEFLATED)
293 {
294 uInt iLevel=(uInt)((file_info.flag & 0x6) / 2);
295 if (iLevel == 0)
296 {
297 string_method = "Defl:N";
298 }
299 else if (iLevel == 1)
300 {
301 string_method = "Defl:X";
302 }
303 else if ((iLevel == 2) || (iLevel == 3))
304 {
305 string_method = "Defl:F"; /* 2:fast , 3 : extra fast*/
306 }
307 }
308 else
309 if (file_info.compression_method == Z_BZIP2ED)
310 {
311 string_method = "BZip2 ";
312 }
313 else
314 {
315 string_method = "Unkn. ";
316 }
317
318 Display64BitsSize(file_info.uncompressed_size,7);
319 printf(" %6s%c",string_method,charCrypt);
320 Display64BitsSize(file_info.compressed_size,7);
321 printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n",
322 ratio,
323 (uLong)file_info.tmu_date.tm_mon + 1,
324 (uLong)file_info.tmu_date.tm_mday,
325 (uLong)file_info.tmu_date.tm_year % 100,
326 (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
327 (uLong)file_info.crc,filename_inzip);
328 if (gi.number_entry > (i+1))
329 {
330 err = unzGoToNextFile(uf);
331 if (err != UNZ_OK)
332 {
333 printf("error %d with zipfile in unzGoToNextFile\n",err);
334 break;
335 }
336 }
337 }
338
339 return 0;
340 }
341
342
do_extract_currentfile(unzFile uf,const int * popt_extract_without_path,int * popt_overwrite,const char * password)343 static int do_extract_currentfile(unzFile uf,
344 const int* popt_extract_without_path,
345 int* popt_overwrite,
346 const char* password)
347 {
348 char filename_inzip[256];
349 char* filename_withoutpath;
350 char* p;
351 int err = UNZ_OK;
352 FILE *fout = NULL;
353 void* buf;
354 uInt size_buf;
355
356 unz_file_info64 file_info;
357 err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
358
359 if (err != UNZ_OK)
360 {
361 printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
362 return err;
363 }
364
365 size_buf = WRITEBUFFERSIZE;
366 buf = (void*)malloc(size_buf);
367 if (buf == NULL)
368 {
369 printf("Error allocating memory\n");
370 return UNZ_INTERNALERROR;
371 }
372
373 p = filename_withoutpath = filename_inzip;
374 while ((*p) != '\0')
375 {
376 if (((*p) == '/') || ((*p) == '\\'))
377 {
378 filename_withoutpath = p+1;
379 }
380 p++;
381 }
382
383 if ((*filename_withoutpath) == '\0')
384 {
385 if ((*popt_extract_without_path) == 0)
386 {
387 printf("creating directory: %s\n",filename_inzip);
388 mymkdir(filename_inzip);
389 }
390 }
391 else
392 {
393 const char* write_filename;
394 int skip = 0;
395
396 if ((*popt_extract_without_path) == 0)
397 {
398 write_filename = filename_inzip;
399 }
400 else
401 {
402 write_filename = filename_withoutpath;
403 }
404
405 if (write_filename[0] != '\0')
406 {
407 const char* relative_check = write_filename;
408 while (relative_check[1] != '\0')
409 {
410 if (relative_check[0] == '.' && relative_check[1] == '.')
411 {
412 write_filename = relative_check;
413 }
414 relative_check++;
415 }
416 }
417
418 while (write_filename[0] == '/' || write_filename[0] == '.')
419 {
420 write_filename++;
421 }
422
423 err = unzOpenCurrentFilePassword(uf,password);
424 if (err != UNZ_OK)
425 {
426 printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
427 }
428
429 if (((*popt_overwrite) == 0) && (err == UNZ_OK))
430 {
431 char rep = 0;
432 FILE* ftestexist;
433 ftestexist = FOPEN_FUNC(write_filename,"rb");
434 if (ftestexist != NULL)
435 {
436 fclose(ftestexist);
437 do
438 {
439 char answer[128];
440 int ret;
441
442 printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
443 ret = scanf("%1s",answer);
444 if (ret != 1)
445 {
446 exit(EXIT_FAILURE);
447 }
448 rep = answer[0] ;
449 if ((rep >= 'a') && (rep <= 'z'))
450 {
451 rep -= 0x20;
452 }
453 }
454 while ((rep != 'Y') && (rep != 'N') && (rep != 'A'));
455 }
456
457 if (rep == 'N')
458 {
459 skip = 1;
460 }
461
462 if (rep == 'A')
463 {
464 *popt_overwrite=1;
465 }
466 }
467
468 if ((skip == 0) && (err == UNZ_OK))
469 {
470 fout=FOPEN_FUNC(write_filename,"wb");
471 /* some zipfile don't contain directory alone before file */
472 if ((fout == NULL) && ((*popt_extract_without_path) == 0) &&
473 (filename_withoutpath!=(char*)filename_inzip))
474 {
475 char c=*(filename_withoutpath - 1);
476 *(filename_withoutpath-1) = '\0';
477 makedir(write_filename);
478 *(filename_withoutpath-1) = c;
479 fout=FOPEN_FUNC(write_filename,"wb");
480 }
481
482 if (fout == NULL)
483 {
484 printf("error opening %s\n",write_filename);
485 }
486 }
487
488 if (fout != NULL)
489 {
490 printf(" extracting: %s\n",write_filename);
491
492 do
493 {
494 err = unzReadCurrentFile(uf,buf,size_buf);
495 if (err < 0)
496 {
497 printf("error %d with zipfile in unzReadCurrentFile\n",err);
498 break;
499 }
500 if (err > 0)
501 if (fwrite(buf,(unsigned)err,1,fout) != 1)
502 {
503 printf("error in writing extracted file\n");
504 err=UNZ_ERRNO;
505 break;
506 }
507 }
508 while (err > 0);
509 if (fout)
510 {
511 fclose(fout);
512 }
513
514 if (err == 0)
515 {
516 change_file_date(write_filename,file_info.dosDate,
517 file_info.tmu_date);
518 }
519 }
520
521 if (err == UNZ_OK)
522 {
523 err = unzCloseCurrentFile (uf);
524 if (err != UNZ_OK)
525 {
526 printf("error %d with zipfile in unzCloseCurrentFile\n",err);
527 }
528 }
529 else
530 {
531 unzCloseCurrentFile(uf); /* don't lose the error */
532 }
533 }
534
535 free(buf);
536 return err;
537 }
538
539
do_extract(unzFile uf,int opt_extract_without_path,int opt_overwrite,const char * password)540 static int do_extract(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char* password)
541 {
542 uLong i;
543 unz_global_info64 gi;
544 int err;
545
546 err = unzGetGlobalInfo64(uf,&gi);
547 if (err != UNZ_OK)
548 {
549 printf("error %d with zipfile in unzGetGlobalInfo \n",err);
550 }
551
552 for (i = 0;i < gi.number_entry; i++)
553 {
554 if (do_extract_currentfile(uf,&opt_extract_without_path,
555 &opt_overwrite,
556 password) != UNZ_OK)
557 break;
558
559 if (gi.number_entry > (i+1))
560 {
561 err = unzGoToNextFile(uf);
562 if (err != UNZ_OK)
563 {
564 printf("error %d with zipfile in unzGoToNextFile\n",err);
565 break;
566 }
567 }
568 }
569
570 return 0;
571 }
572
do_extract_onefile(unzFile uf,const char * filename,int opt_extract_without_path,int opt_overwrite,const char * password)573 static int do_extract_onefile(unzFile uf,
574 const char* filename,
575 int opt_extract_without_path,
576 int opt_overwrite,
577 const char* password)
578 {
579 if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
580 {
581 printf("file %s not found in the zipfile\n",filename);
582 return 2;
583 }
584
585 if (do_extract_currentfile(uf,&opt_extract_without_path,
586 &opt_overwrite,
587 password) == UNZ_OK)
588 {
589 return 0;
590 }
591 else
592 {
593 return 1;
594 }
595 }
596
597
main(int argc,char * argv[])598 int main(int argc, char *argv[])
599 {
600 const char *zipfilename = NULL;
601 const char *filename_to_extract = NULL;
602 const char *password = NULL;
603 char filename_try[MAXFILENAME+16] = "";
604 int i;
605 int ret_value = 0;
606 int opt_do_list = 0;
607 int opt_do_extract = 1;
608 int opt_do_extract_withoutpath = 0;
609 int opt_overwrite = 0;
610 int opt_extractdir = 0;
611 const char *dirname = NULL;
612 unzFile uf = NULL;
613
614 do_banner();
615 if (argc == 1)
616 {
617 do_help();
618 return 0;
619 }
620 else
621 {
622 for (i = 1;i < argc; i++)
623 {
624 if ((*argv[i]) == '-')
625 {
626 const char *p = argv[i]+1;
627
628 while ((*p) != '\0')
629 {
630 char c = *(p++);
631 if ((c == 'l') || (c == 'L'))
632 {
633 opt_do_list = 1;
634 }
635 if ((c == 'v') || (c == 'V'))
636 {
637 opt_do_list = 1;
638 }
639 if ((c == 'x') || (c == 'X'))
640 {
641 opt_do_extract = 1;
642 }
643 if ((c == 'e') || (c == 'E'))
644 {
645 opt_do_extract = opt_do_extract_withoutpath = 1;
646 }
647 if ((c == 'o') || (c == 'O'))
648 {
649 opt_overwrite=1;
650 }
651 if ((c == 'd') || (c == 'D'))
652 {
653 opt_extractdir = 1;
654 dirname = argv[i+1];
655 }
656
657 if (((c == 'p') || (c == 'P')) && (i+1 < argc))
658 {
659 password = argv[i+1];
660 i++;
661 }
662 }
663 }
664 else
665 {
666 if (zipfilename == NULL)
667 {
668 zipfilename = argv[i];
669 }
670 else if ((filename_to_extract == NULL) && (!opt_extractdir))
671 {
672 filename_to_extract = argv[i];
673 }
674 }
675 }
676 }
677
678 if (zipfilename != NULL)
679 {
680
681 # ifdef USEWIN32IOAPI
682 zlib_filefunc64_def ffunc;
683 # endif
684
685 strncpy(filename_try, zipfilename,MAXFILENAME-1);
686 /* strncpy doesn't append the trailing NULL, of the string is too long. */
687 filename_try[ MAXFILENAME ] = '\0';
688
689 # ifdef USEWIN32IOAPI
690 fill_win32_filefunc64A(&ffunc);
691 uf = unzOpen2_64(zipfilename,&ffunc);
692 # else
693 uf = unzOpen64(zipfilename);
694 # endif
695 if (uf == NULL)
696 {
697 strcat(filename_try,".zip");
698 # ifdef USEWIN32IOAPI
699 uf = unzOpen2_64(filename_try,&ffunc);
700 # else
701 uf = unzOpen64(filename_try);
702 # endif
703 }
704 }
705
706 if (uf == NULL)
707 {
708 printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename);
709 return 1;
710 }
711 printf("%s opened\n",filename_try);
712
713 if (opt_do_list == 1)
714 ret_value = do_list(uf);
715 else if (opt_do_extract == 1)
716 {
717 #ifdef _WIN32
718 if (opt_extractdir && _chdir(dirname))
719 #else
720 if (opt_extractdir && chdir(dirname))
721 #endif
722 {
723 printf("Error changing into %s, aborting\n", dirname);
724 exit(-1);
725 }
726
727 if (filename_to_extract == NULL)
728 {
729 ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
730 }
731 else
732 {
733 ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password);
734 }
735 }
736
737 unzClose(uf);
738
739 return ret_value;
740 }
741