• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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