xref: /third_party/zlib/contrib/minizip/miniunz.c (revision 275793ea)
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 */
84static 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
131static 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
146static 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
201static 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
207static 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
219static 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
252static 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
343static 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
540static 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
573static 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
598int 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