1 /*
2    miniunz.c
3    Version 1.01h, December 28th, 2009
4 
5    Copyright (C) 1998-2009 Gilles Vollant
6 */
7 
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 
16 #ifdef unix
17 # include <unistd.h>
18 # include <utime.h>
19 #else
20 # include <direct.h>
21 # include <io.h>
22 #endif
23 
24 #include "unzip.h"
25 
26 #define CASESENSITIVITY (0)
27 #define WRITEBUFFERSIZE (8192)
28 #define MAXFILENAME (256)
29 
30 #ifdef WIN32
31 #define USEWIN32IOAPI
32 #include "iowin32.h"
33 #endif
34 /*
35   mini unzip, demo of unzip package
36 
37   usage :
38   Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir]
39 
40   list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
41     if it exists
42 */
43 
44 
45 /* change_file_date : change the date/time of a file
46     filename : the filename of the file where date/time must be modified
47     dosdate : the new date at the MSDos format (4 bytes)
48     tmu_date : the SAME new date at the tm_unz format */
change_file_date(filename,dosdate,tmu_date)49 void change_file_date(filename,dosdate,tmu_date)
50     const char *filename;
51     uLong dosdate;
52     tm_unz tmu_date;
53 {
54 #ifdef WIN32
55   HANDLE hFile;
56   FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
57 
58   hFile = CreateFile(filename,GENERIC_READ | GENERIC_WRITE,
59                       0,NULL,OPEN_EXISTING,0,NULL);
60   GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
61   DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
62   LocalFileTimeToFileTime(&ftLocal,&ftm);
63   SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
64   CloseHandle(hFile);
65 #else
66 #ifdef unix
67   struct utimbuf ut;
68   struct tm newdate;
69   newdate.tm_sec = tmu_date.tm_sec;
70   newdate.tm_min=tmu_date.tm_min;
71   newdate.tm_hour=tmu_date.tm_hour;
72   newdate.tm_mday=tmu_date.tm_mday;
73   newdate.tm_mon=tmu_date.tm_mon;
74   if (tmu_date.tm_year > 1900)
75       newdate.tm_year=tmu_date.tm_year - 1900;
76   else
77       newdate.tm_year=tmu_date.tm_year ;
78   newdate.tm_isdst=-1;
79 
80   ut.actime=ut.modtime=mktime(&newdate);
81   utime(filename,&ut);
82 #endif
83 #endif
84 }
85 
86 
87 /* mymkdir and change_file_date are not 100 % portable
88    As I don't know well Unix, I wait feedback for the unix portion */
89 
mymkdir(dirname)90 int mymkdir(dirname)
91     const char* dirname;
92 {
93     int ret=0;
94 #ifdef WIN32
95     ret = mkdir(dirname);
96 #else
97 #ifdef unix
98     ret = mkdir (dirname,0775);
99 #endif
100 #endif
101     return ret;
102 }
103 
makedir(newdir)104 int makedir (newdir)
105     char *newdir;
106 {
107   char *buffer ;
108   char *p;
109   int  len = (int)strlen(newdir);
110 
111   if (len <= 0)
112     return 0;
113 
114   buffer = (char*)malloc(len+1);
115   if (buffer==NULL)
116     {
117       printf("Error allocating memory\n");
118       return UNZ_INTERNALERROR;
119     }
120   strcpy(buffer,newdir);
121 
122   if (buffer[len-1] == '/') {
123     buffer[len-1] = '\0';
124   }
125   if (mymkdir(buffer) == 0)
126     {
127       free(buffer);
128       return 1;
129     }
130 
131   p = buffer+1;
132   while (1)
133     {
134       char hold;
135 
136       while(*p && *p != '\\' && *p != '/')
137         p++;
138       hold = *p;
139       *p = 0;
140       if ((mymkdir(buffer) == -1) && (errno == ENOENT))
141         {
142           printf("couldn't create directory %s\n",buffer);
143           free(buffer);
144           return 0;
145         }
146       if (hold == 0)
147         break;
148       *p++ = hold;
149     }
150   free(buffer);
151   return 1;
152 }
153 
do_banner()154 void do_banner()
155 {
156     printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n");
157     printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
158 }
159 
do_help()160 void do_help()
161 {
162     printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \
163            "  -e  Extract without pathname (junk paths)\n" \
164            "  -x  Extract with pathname\n" \
165            "  -v  list files\n" \
166            "  -l  list files\n" \
167            "  -d  directory to extract into\n" \
168            "  -o  overwrite files without prompting\n" \
169            "  -p  extract crypted file using password\n\n");
170 }
171 
172 
do_list(uf)173 int do_list(uf)
174     unzFile uf;
175 {
176     uLong i;
177     unz_global_info gi;
178     int err;
179 
180     err = unzGetGlobalInfo (uf,&gi);
181     if (err!=UNZ_OK)
182         printf("error %d with zipfile in unzGetGlobalInfo \n",err);
183     printf(" Length  Method   Size  Ratio   Date    Time   CRC-32     Name\n");
184     printf(" ------  ------   ----  -----   ----    ----   ------     ----\n");
185     for (i=0;i<gi.number_entry;i++)
186     {
187         char filename_inzip[256];
188         unz_file_info file_info;
189         uLong ratio=0;
190         const char *string_method;
191         char charCrypt=' ';
192         err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
193         if (err!=UNZ_OK)
194         {
195             printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
196             break;
197         }
198         if (file_info.uncompressed_size>0)
199             ratio = (file_info.compressed_size*100)/file_info.uncompressed_size;
200 
201         /* display a '*' if the file is crypted */
202         if ((file_info.flag & 1) != 0)
203             charCrypt='*';
204 
205         if (file_info.compression_method==0)
206             string_method="Stored";
207         else
208         if (file_info.compression_method==Z_DEFLATED)
209         {
210             uInt iLevel=(uInt)((file_info.flag & 0x6)/2);
211             if (iLevel==0)
212               string_method="Defl:N";
213             else if (iLevel==1)
214               string_method="Defl:X";
215             else if ((iLevel==2) || (iLevel==3))
216               string_method="Defl:F"; /* 2:fast , 3 : extra fast*/
217         }
218 #ifdef HAVE_BZIP2
219         else
220         if (file_info.compression_method==Z_BZIP2ED)
221         {
222               string_method="BZip2 ";
223         }
224 #endif
225         else
226             string_method="Unkn. ";
227 
228         printf("%7lu  %6s%c%7lu %3lu%%  %2.2lu-%2.2lu-%2.2lu  %2.2lu:%2.2lu  %8.8lx   %s\n",
229                 file_info.uncompressed_size,string_method,
230                 charCrypt,
231                 file_info.compressed_size,
232                 ratio,
233                 (uLong)file_info.tmu_date.tm_mon + 1,
234                 (uLong)file_info.tmu_date.tm_mday,
235                 (uLong)file_info.tmu_date.tm_year % 100,
236                 (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
237                 (uLong)file_info.crc,filename_inzip);
238         if ((i+1)<gi.number_entry)
239         {
240             err = unzGoToNextFile(uf);
241             if (err!=UNZ_OK)
242             {
243                 printf("error %d with zipfile in unzGoToNextFile\n",err);
244                 break;
245             }
246         }
247     }
248 
249     return 0;
250 }
251 
252 
do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)253 int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)
254     unzFile uf;
255     const int* popt_extract_without_path;
256     int* popt_overwrite;
257     const char* password;
258 {
259     char filename_inzip[256];
260     char* filename_withoutpath;
261     char* p;
262     int err=UNZ_OK;
263     FILE *fout=NULL;
264     void* buf;
265     uInt size_buf;
266 
267     unz_file_info file_info;
268     uLong ratio=0;
269     err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
270 
271     if (err!=UNZ_OK)
272     {
273         printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
274         return err;
275     }
276 
277     size_buf = WRITEBUFFERSIZE;
278     buf = (void*)malloc(size_buf);
279     if (buf==NULL)
280     {
281         printf("Error allocating memory\n");
282         return UNZ_INTERNALERROR;
283     }
284 
285     p = filename_withoutpath = filename_inzip;
286     while ((*p) != '\0')
287     {
288         if (((*p)=='/') || ((*p)=='\\'))
289             filename_withoutpath = p+1;
290         p++;
291     }
292 
293     if ((*filename_withoutpath)=='\0')
294     {
295         if ((*popt_extract_without_path)==0)
296         {
297             printf("creating directory: %s\n",filename_inzip);
298             mymkdir(filename_inzip);
299         }
300     }
301     else
302     {
303         const char* write_filename;
304         int skip=0;
305 
306         if ((*popt_extract_without_path)==0)
307             write_filename = filename_inzip;
308         else
309             write_filename = filename_withoutpath;
310 
311         err = unzOpenCurrentFilePassword(uf,password);
312         if (err!=UNZ_OK)
313         {
314             printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
315         }
316 
317         if (((*popt_overwrite)==0) && (err==UNZ_OK))
318         {
319             char rep=0;
320             FILE* ftestexist;
321             ftestexist = fopen(write_filename,"rb");
322             if (ftestexist!=NULL)
323             {
324                 fclose(ftestexist);
325                 do
326                 {
327                     char answer[128];
328                     int ret;
329 
330                     printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
331                     ret = scanf("%1s",answer);
332                     if (ret != 1)
333                     {
334                        exit(EXIT_FAILURE);
335                     }
336                     rep = answer[0] ;
337                     if ((rep>='a') && (rep<='z'))
338                         rep -= 0x20;
339                 }
340                 while ((rep!='Y') && (rep!='N') && (rep!='A'));
341             }
342 
343             if (rep == 'N')
344                 skip = 1;
345 
346             if (rep == 'A')
347                 *popt_overwrite=1;
348         }
349 
350         if ((skip==0) && (err==UNZ_OK))
351         {
352             fout=fopen(write_filename,"wb");
353 
354             /* some zipfile don't contain directory alone before file */
355             if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
356                                 (filename_withoutpath!=(char*)filename_inzip))
357             {
358                 char c=*(filename_withoutpath-1);
359                 *(filename_withoutpath-1)='\0';
360                 makedir(write_filename);
361                 *(filename_withoutpath-1)=c;
362                 fout=fopen(write_filename,"wb");
363             }
364 
365             if (fout==NULL)
366             {
367                 printf("error opening %s\n",write_filename);
368             }
369         }
370 
371         if (fout!=NULL)
372         {
373             printf(" extracting: %s\n",write_filename);
374 
375             do
376             {
377                 err = unzReadCurrentFile(uf,buf,size_buf);
378                 if (err<0)
379                 {
380                     printf("error %d with zipfile in unzReadCurrentFile\n",err);
381                     break;
382                 }
383                 if (err>0)
384                     if (fwrite(buf,err,1,fout)!=1)
385                     {
386                         printf("error in writing extracted file\n");
387                         err=UNZ_ERRNO;
388                         break;
389                     }
390             }
391             while (err>0);
392             if (fout)
393                     fclose(fout);
394 
395             if (err==0)
396                 change_file_date(write_filename,file_info.dosDate,
397                                  file_info.tmu_date);
398         }
399 
400         if (err==UNZ_OK)
401         {
402             err = unzCloseCurrentFile (uf);
403             if (err!=UNZ_OK)
404             {
405                 printf("error %d with zipfile in unzCloseCurrentFile\n",err);
406             }
407         }
408         else
409             unzCloseCurrentFile(uf); /* don't lose the error */
410     }
411 
412     free(buf);
413     return err;
414 }
415 
416 
do_extract(uf,opt_extract_without_path,opt_overwrite,password)417 int do_extract(uf,opt_extract_without_path,opt_overwrite,password)
418     unzFile uf;
419     int opt_extract_without_path;
420     int opt_overwrite;
421     const char* password;
422 {
423     uLong i;
424     unz_global_info gi;
425     int err;
426     FILE* fout=NULL;
427 
428     err = unzGetGlobalInfo (uf,&gi);
429     if (err!=UNZ_OK)
430         printf("error %d with zipfile in unzGetGlobalInfo \n",err);
431 
432     for (i=0;i<gi.number_entry;i++)
433     {
434         if (do_extract_currentfile(uf,&opt_extract_without_path,
435                                       &opt_overwrite,
436                                       password) != UNZ_OK)
437             break;
438 
439         if ((i+1)<gi.number_entry)
440         {
441             err = unzGoToNextFile(uf);
442             if (err!=UNZ_OK)
443             {
444                 printf("error %d with zipfile in unzGoToNextFile\n",err);
445                 break;
446             }
447         }
448     }
449 
450     return 0;
451 }
452 
do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)453 int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)
454     unzFile uf;
455     const char* filename;
456     int opt_extract_without_path;
457     int opt_overwrite;
458     const char* password;
459 {
460     int err = UNZ_OK;
461     if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
462     {
463         printf("file %s not found in the zipfile\n",filename);
464         return 2;
465     }
466 
467     if (do_extract_currentfile(uf,&opt_extract_without_path,
468                                       &opt_overwrite,
469                                       password) == UNZ_OK)
470         return 0;
471     else
472         return 1;
473 }
474 
475 
main(argc,argv)476 int main(argc,argv)
477     int argc;
478     char *argv[];
479 {
480     const char *zipfilename=NULL;
481     const char *filename_to_extract=NULL;
482     const char *password=NULL;
483     char filename_try[MAXFILENAME+16] = "";
484     int i;
485     int ret_value=0;
486     int opt_do_list=0;
487     int opt_do_extract=1;
488     int opt_do_extract_withoutpath=0;
489     int opt_overwrite=0;
490     int opt_extractdir=0;
491     const char *dirname=NULL;
492     unzFile uf=NULL;
493 
494     do_banner();
495     if (argc==1)
496     {
497         do_help();
498         return 0;
499     }
500     else
501     {
502         for (i=1;i<argc;i++)
503         {
504             if ((*argv[i])=='-')
505             {
506                 const char *p=argv[i]+1;
507 
508                 while ((*p)!='\0')
509                 {
510                     char c=*(p++);;
511                     if ((c=='l') || (c=='L'))
512                         opt_do_list = 1;
513                     if ((c=='v') || (c=='V'))
514                         opt_do_list = 1;
515                     if ((c=='x') || (c=='X'))
516                         opt_do_extract = 1;
517                     if ((c=='e') || (c=='E'))
518                         opt_do_extract = opt_do_extract_withoutpath = 1;
519                     if ((c=='o') || (c=='O'))
520                         opt_overwrite=1;
521                     if ((c=='d') || (c=='D'))
522                     {
523                         opt_extractdir=1;
524                         dirname=argv[i+1];
525                     }
526 
527                     if (((c=='p') || (c=='P')) && (i+1<argc))
528                     {
529                         password=argv[i+1];
530                         i++;
531                     }
532                 }
533             }
534             else
535             {
536                 if (zipfilename == NULL)
537                     zipfilename = argv[i];
538                 else if ((filename_to_extract==NULL) && (!opt_extractdir))
539                         filename_to_extract = argv[i] ;
540             }
541         }
542     }
543 
544     if (zipfilename!=NULL)
545     {
546 
547 #        ifdef USEWIN32IOAPI
548         zlib_filefunc_def ffunc;
549 #        endif
550 
551         strncpy(filename_try, zipfilename,MAXFILENAME-1);
552         /* strncpy doesnt append the trailing NULL, of the string is too long. */
553         filename_try[ MAXFILENAME ] = '\0';
554 
555 #        ifdef USEWIN32IOAPI
556         fill_win32_filefunc(&ffunc);
557         uf = unzOpen2(zipfilename,&ffunc);
558 #        else
559         uf = unzOpen(zipfilename);
560 #        endif
561         if (uf==NULL)
562         {
563             strcat(filename_try,".zip");
564 #            ifdef USEWIN32IOAPI
565             uf = unzOpen2(filename_try,&ffunc);
566 #            else
567             uf = unzOpen(filename_try);
568 #            endif
569         }
570     }
571 
572     if (uf==NULL)
573     {
574         printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename);
575         return 1;
576     }
577     printf("%s opened\n",filename_try);
578 
579     if (opt_do_list==1)
580         ret_value = do_list(uf);
581     else if (opt_do_extract==1)
582     {
583         if (opt_extractdir && chdir(dirname))
584         {
585           printf("Error changing into %s, aborting\n", dirname);
586           exit(-1);
587         }
588 
589         if (filename_to_extract == NULL)
590             ret_value = do_extract(uf,opt_do_extract_withoutpath,opt_overwrite,password);
591         else
592             ret_value = do_extract_onefile(uf,filename_to_extract,
593                                       opt_do_extract_withoutpath,opt_overwrite,password);
594     }
595 
596     unzClose(uf);
597 
598     return ret_value ;
599 }
600