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#ifdef __APPLE__
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 */
84static void change_file_date(filename,dosdate,tmu_date)
85    const char *filename;
86    uLong dosdate;
87    tm_unz tmu_date;
88{
89#ifdef _WIN32
90  HANDLE hFile;
91  FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
92
93  hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
94                      0,NULL,OPEN_EXISTING,0,NULL);
95  GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
96  DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
97  LocalFileTimeToFileTime(&ftLocal,&ftm);
98  SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
99  CloseHandle(hFile);
100#else
101#if defined(unix) || defined(__APPLE__)
102  (void)dosdate;
103  struct utimbuf ut;
104  struct tm newdate;
105  newdate.tm_sec = tmu_date.tm_sec;
106  newdate.tm_min=tmu_date.tm_min;
107  newdate.tm_hour=tmu_date.tm_hour;
108  newdate.tm_mday=tmu_date.tm_mday;
109  newdate.tm_mon=tmu_date.tm_mon;
110  if (tmu_date.tm_year > 1900)
111      newdate.tm_year=tmu_date.tm_year - 1900;
112  else
113      newdate.tm_year=tmu_date.tm_year ;
114  newdate.tm_isdst=-1;
115
116  ut.actime=ut.modtime=mktime(&newdate);
117  utime(filename,&ut);
118#endif
119#endif
120}
121
122
123/* mymkdir and change_file_date are not 100 % portable
124   As I don't know well Unix, I wait feedback for the unix portion */
125
126static int mymkdir(dirname)
127    const char* dirname;
128{
129    int ret=0;
130#ifdef _WIN32
131    ret = _mkdir(dirname);
132#elif unix
133    ret = mkdir (dirname,0775);
134#elif __APPLE__
135    ret = mkdir (dirname,0775);
136#endif
137    return ret;
138}
139
140static int makedir (newdir)
141    const char *newdir;
142{
143  char *buffer ;
144  char *p;
145  size_t len = strlen(newdir);
146
147  if (len == 0)
148    return 0;
149
150  buffer = (char*)malloc(len+1);
151        if (buffer==NULL)
152        {
153                printf("Error allocating memory\n");
154                return UNZ_INTERNALERROR;
155        }
156  strcpy(buffer,newdir);
157
158  if (buffer[len-1] == '/') {
159    buffer[len-1] = '\0';
160  }
161  if (mymkdir(buffer) == 0)
162    {
163      free(buffer);
164      return 1;
165    }
166
167  p = buffer+1;
168  while (1)
169    {
170      char hold;
171
172      while(*p && *p != '\\' && *p != '/')
173        p++;
174      hold = *p;
175      *p = 0;
176      if ((mymkdir(buffer) == -1) && (errno == ENOENT))
177        {
178          printf("couldn't create directory %s\n",buffer);
179          free(buffer);
180          return 0;
181        }
182      if (hold == 0)
183        break;
184      *p++ = hold;
185    }
186  free(buffer);
187  return 1;
188}
189
190static void do_banner()
191{
192    printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n");
193    printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
194}
195
196static void do_help()
197{
198    printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \
199           "  -e  Extract without pathname (junk paths)\n" \
200           "  -x  Extract with pathname\n" \
201           "  -v  list files\n" \
202           "  -l  list files\n" \
203           "  -d  directory to extract into\n" \
204           "  -o  overwrite files without prompting\n" \
205           "  -p  extract crypted file using password\n\n");
206}
207
208static void Display64BitsSize(ZPOS64_T n, int size_char)
209{
210  /* to avoid compatibility problem , we do here the conversion */
211  char number[21];
212  int offset=19;
213  int pos_string = 19;
214  number[20]=0;
215  for (;;) {
216      number[offset]=(char)((n%10)+'0');
217      if (number[offset] != '0')
218          pos_string=offset;
219      n/=10;
220      if (offset==0)
221          break;
222      offset--;
223  }
224  {
225      int size_display_string = 19-pos_string;
226      while (size_char > size_display_string)
227      {
228          size_char--;
229          printf(" ");
230      }
231  }
232
233  printf("%s",&number[pos_string]);
234}
235
236static int do_list(uf)
237    unzFile uf;
238{
239    uLong i;
240    unz_global_info64 gi;
241    int err;
242
243    err = unzGetGlobalInfo64(uf,&gi);
244    if (err!=UNZ_OK)
245        printf("error %d with zipfile in unzGetGlobalInfo \n",err);
246    printf("  Length  Method     Size Ratio   Date    Time   CRC-32     Name\n");
247    printf("  ------  ------     ---- -----   ----    ----   ------     ----\n");
248    for (i=0;i<gi.number_entry;i++)
249    {
250        char filename_inzip[256];
251        unz_file_info64 file_info;
252        uLong ratio=0;
253        const char *string_method;
254        char charCrypt=' ';
255        err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
256        if (err!=UNZ_OK)
257        {
258            printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
259            break;
260        }
261        if (file_info.uncompressed_size>0)
262            ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size);
263
264        /* display a '*' if the file is crypted */
265        if ((file_info.flag & 1) != 0)
266            charCrypt='*';
267
268        if (file_info.compression_method==0)
269            string_method="Stored";
270        else
271        if (file_info.compression_method==Z_DEFLATED)
272        {
273            uInt iLevel=(uInt)((file_info.flag & 0x6)/2);
274            if (iLevel==0)
275              string_method="Defl:N";
276            else if (iLevel==1)
277              string_method="Defl:X";
278            else if ((iLevel==2) || (iLevel==3))
279              string_method="Defl:F"; /* 2:fast , 3 : extra fast*/
280        }
281        else
282        if (file_info.compression_method==Z_BZIP2ED)
283        {
284              string_method="BZip2 ";
285        }
286        else
287            string_method="Unkn. ";
288
289        Display64BitsSize(file_info.uncompressed_size,7);
290        printf("  %6s%c",string_method,charCrypt);
291        Display64BitsSize(file_info.compressed_size,7);
292        printf(" %3lu%%  %2.2lu-%2.2lu-%2.2lu  %2.2lu:%2.2lu  %8.8lx   %s\n",
293                ratio,
294                (uLong)file_info.tmu_date.tm_mon + 1,
295                (uLong)file_info.tmu_date.tm_mday,
296                (uLong)file_info.tmu_date.tm_year % 100,
297                (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
298                (uLong)file_info.crc,filename_inzip);
299        if ((i+1)<gi.number_entry)
300        {
301            err = unzGoToNextFile(uf);
302            if (err!=UNZ_OK)
303            {
304                printf("error %d with zipfile in unzGoToNextFile\n",err);
305                break;
306            }
307        }
308    }
309
310    return 0;
311}
312
313
314static int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)
315    unzFile uf;
316    const int* popt_extract_without_path;
317    int* popt_overwrite;
318    const char* password;
319{
320    char filename_inzip[256];
321    char* filename_withoutpath;
322    char* p;
323    int err=UNZ_OK;
324    FILE *fout=NULL;
325    void* buf;
326    uInt size_buf;
327
328    unz_file_info64 file_info;
329    err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
330
331    if (err!=UNZ_OK)
332    {
333        printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
334        return err;
335    }
336
337    size_buf = WRITEBUFFERSIZE;
338    buf = (void*)malloc(size_buf);
339    if (buf==NULL)
340    {
341        printf("Error allocating memory\n");
342        return UNZ_INTERNALERROR;
343    }
344
345    p = filename_withoutpath = filename_inzip;
346    while ((*p) != '\0')
347    {
348        if (((*p)=='/') || ((*p)=='\\'))
349            filename_withoutpath = p+1;
350        p++;
351    }
352
353    if ((*filename_withoutpath)=='\0')
354    {
355        if ((*popt_extract_without_path)==0)
356        {
357            printf("creating directory: %s\n",filename_inzip);
358            mymkdir(filename_inzip);
359        }
360    }
361    else
362    {
363        const char* write_filename;
364        int skip=0;
365
366        if ((*popt_extract_without_path)==0)
367            write_filename = filename_inzip;
368        else
369            write_filename = filename_withoutpath;
370
371        err = unzOpenCurrentFilePassword(uf,password);
372        if (err!=UNZ_OK)
373        {
374            printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
375        }
376
377        if (((*popt_overwrite)==0) && (err==UNZ_OK))
378        {
379            char rep=0;
380            FILE* ftestexist;
381            ftestexist = FOPEN_FUNC(write_filename,"rb");
382            if (ftestexist!=NULL)
383            {
384                fclose(ftestexist);
385                do
386                {
387                    char answer[128];
388                    int ret;
389
390                    printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
391                    ret = scanf("%1s",answer);
392                    if (ret != 1)
393                    {
394                       exit(EXIT_FAILURE);
395                    }
396                    rep = answer[0] ;
397                    if ((rep>='a') && (rep<='z'))
398                        rep -= 0x20;
399                }
400                while ((rep!='Y') && (rep!='N') && (rep!='A'));
401            }
402
403            if (rep == 'N')
404                skip = 1;
405
406            if (rep == 'A')
407                *popt_overwrite=1;
408        }
409
410        if ((skip==0) && (err==UNZ_OK))
411        {
412            fout=FOPEN_FUNC(write_filename,"wb");
413            /* some zipfile don't contain directory alone before file */
414            if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
415                                (filename_withoutpath!=(char*)filename_inzip))
416            {
417                char c=*(filename_withoutpath-1);
418                *(filename_withoutpath-1)='\0';
419                makedir(write_filename);
420                *(filename_withoutpath-1)=c;
421                fout=FOPEN_FUNC(write_filename,"wb");
422            }
423
424            if (fout==NULL)
425            {
426                printf("error opening %s\n",write_filename);
427            }
428        }
429
430        if (fout!=NULL)
431        {
432            printf(" extracting: %s\n",write_filename);
433
434            do
435            {
436                err = unzReadCurrentFile(uf,buf,size_buf);
437                if (err<0)
438                {
439                    printf("error %d with zipfile in unzReadCurrentFile\n",err);
440                    break;
441                }
442                if (err>0)
443                    if (fwrite(buf,(unsigned)err,1,fout)!=1)
444                    {
445                        printf("error in writing extracted file\n");
446                        err=UNZ_ERRNO;
447                        break;
448                    }
449            }
450            while (err>0);
451            if (fout)
452                    fclose(fout);
453
454            if (err==0)
455                change_file_date(write_filename,file_info.dosDate,
456                                 file_info.tmu_date);
457        }
458
459        if (err==UNZ_OK)
460        {
461            err = unzCloseCurrentFile (uf);
462            if (err!=UNZ_OK)
463            {
464                printf("error %d with zipfile in unzCloseCurrentFile\n",err);
465            }
466        }
467        else
468            unzCloseCurrentFile(uf); /* don't lose the error */
469    }
470
471    free(buf);
472    return err;
473}
474
475
476static int do_extract(uf,opt_extract_without_path,opt_overwrite,password)
477    unzFile uf;
478    int opt_extract_without_path;
479    int opt_overwrite;
480    const char* password;
481{
482    uLong i;
483    unz_global_info64 gi;
484    int err;
485
486    err = unzGetGlobalInfo64(uf,&gi);
487    if (err!=UNZ_OK)
488        printf("error %d with zipfile in unzGetGlobalInfo \n",err);
489
490    for (i=0;i<gi.number_entry;i++)
491    {
492        if (do_extract_currentfile(uf,&opt_extract_without_path,
493                                      &opt_overwrite,
494                                      password) != UNZ_OK)
495            break;
496
497        if ((i+1)<gi.number_entry)
498        {
499            err = unzGoToNextFile(uf);
500            if (err!=UNZ_OK)
501            {
502                printf("error %d with zipfile in unzGoToNextFile\n",err);
503                break;
504            }
505        }
506    }
507
508    return 0;
509}
510
511static int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)
512    unzFile uf;
513    const char* filename;
514    int opt_extract_without_path;
515    int opt_overwrite;
516    const char* password;
517{
518    if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
519    {
520        printf("file %s not found in the zipfile\n",filename);
521        return 2;
522    }
523
524    if (do_extract_currentfile(uf,&opt_extract_without_path,
525                                      &opt_overwrite,
526                                      password) == UNZ_OK)
527        return 0;
528    else
529        return 1;
530}
531
532
533int main(argc,argv)
534    int argc;
535    char *argv[];
536{
537    const char *zipfilename=NULL;
538    const char *filename_to_extract=NULL;
539    const char *password=NULL;
540    char filename_try[MAXFILENAME+16] = "";
541    int i;
542    int ret_value=0;
543    int opt_do_list=0;
544    int opt_do_extract=1;
545    int opt_do_extract_withoutpath=0;
546    int opt_overwrite=0;
547    int opt_extractdir=0;
548    const char *dirname=NULL;
549    unzFile uf=NULL;
550
551    do_banner();
552    if (argc==1)
553    {
554        do_help();
555        return 0;
556    }
557    else
558    {
559        for (i=1;i<argc;i++)
560        {
561            if ((*argv[i])=='-')
562            {
563                const char *p=argv[i]+1;
564
565                while ((*p)!='\0')
566                {
567                    char c=*(p++);;
568                    if ((c=='l') || (c=='L'))
569                        opt_do_list = 1;
570                    if ((c=='v') || (c=='V'))
571                        opt_do_list = 1;
572                    if ((c=='x') || (c=='X'))
573                        opt_do_extract = 1;
574                    if ((c=='e') || (c=='E'))
575                        opt_do_extract = opt_do_extract_withoutpath = 1;
576                    if ((c=='o') || (c=='O'))
577                        opt_overwrite=1;
578                    if ((c=='d') || (c=='D'))
579                    {
580                        opt_extractdir=1;
581                        dirname=argv[i+1];
582                    }
583
584                    if (((c=='p') || (c=='P')) && (i+1<argc))
585                    {
586                        password=argv[i+1];
587                        i++;
588                    }
589                }
590            }
591            else
592            {
593                if (zipfilename == NULL)
594                    zipfilename = argv[i];
595                else if ((filename_to_extract==NULL) && (!opt_extractdir))
596                        filename_to_extract = argv[i] ;
597            }
598        }
599    }
600
601    if (zipfilename!=NULL)
602    {
603
604#        ifdef USEWIN32IOAPI
605        zlib_filefunc64_def ffunc;
606#        endif
607
608        strncpy(filename_try, zipfilename,MAXFILENAME-1);
609        /* strncpy doesnt append the trailing NULL, of the string is too long. */
610        filename_try[ MAXFILENAME ] = '\0';
611
612#        ifdef USEWIN32IOAPI
613        fill_win32_filefunc64A(&ffunc);
614        uf = unzOpen2_64(zipfilename,&ffunc);
615#        else
616        uf = unzOpen64(zipfilename);
617#        endif
618        if (uf==NULL)
619        {
620            strcat(filename_try,".zip");
621#            ifdef USEWIN32IOAPI
622            uf = unzOpen2_64(filename_try,&ffunc);
623#            else
624            uf = unzOpen64(filename_try);
625#            endif
626        }
627    }
628
629    if (uf==NULL)
630    {
631        printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename);
632        return 1;
633    }
634    printf("%s opened\n",filename_try);
635
636    if (opt_do_list==1)
637        ret_value = do_list(uf);
638    else if (opt_do_extract==1)
639    {
640#ifdef _WIN32
641        if (opt_extractdir && _chdir(dirname))
642#else
643        if (opt_extractdir && chdir(dirname))
644#endif
645        {
646          printf("Error changing into %s, aborting\n", dirname);
647          exit(-1);
648        }
649
650        if (filename_to_extract == NULL)
651            ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
652        else
653            ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password);
654    }
655
656    unzClose(uf);
657
658    return ret_value;
659}
660