1/*
2   miniunz.c
3   Version 1.01e, February 12th, 2005
4
5   Copyright (C) 1998-2005 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 */
49void 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
90int 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
104int 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  strcpy(buffer,newdir);
116
117  if (buffer[len-1] == '/') {
118    buffer[len-1] = '\0';
119  }
120  if (mymkdir(buffer) == 0)
121    {
122      free(buffer);
123      return 1;
124    }
125
126  p = buffer+1;
127  while (1)
128    {
129      char hold;
130
131      while(*p && *p != '\\' && *p != '/')
132        p++;
133      hold = *p;
134      *p = 0;
135      if ((mymkdir(buffer) == -1) && (errno == ENOENT))
136        {
137          printf("couldn't create directory %s\n",buffer);
138          free(buffer);
139          return 0;
140        }
141      if (hold == 0)
142        break;
143      *p++ = hold;
144    }
145  free(buffer);
146  return 1;
147}
148
149void do_banner()
150{
151    printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n");
152    printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
153}
154
155void do_help()
156{
157    printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \
158           "  -e  Extract without pathname (junk paths)\n" \
159           "  -x  Extract with pathname\n" \
160           "  -v  list files\n" \
161           "  -l  list files\n" \
162           "  -d  directory to extract into\n" \
163           "  -o  overwrite files without prompting\n" \
164           "  -p  extract crypted file using password\n\n");
165}
166
167
168int do_list(uf)
169    unzFile uf;
170{
171    uLong i;
172    unz_global_info gi;
173    int err;
174
175    err = unzGetGlobalInfo (uf,&gi);
176    if (err!=UNZ_OK)
177        printf("error %d with zipfile in unzGetGlobalInfo \n",err);
178    printf(" Length  Method   Size  Ratio   Date    Time   CRC-32     Name\n");
179    printf(" ------  ------   ----  -----   ----    ----   ------     ----\n");
180    for (i=0;i<gi.number_entry;i++)
181    {
182        char filename_inzip[256];
183        unz_file_info file_info;
184        uLong ratio=0;
185        const char *string_method;
186        char charCrypt=' ';
187        err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
188        if (err!=UNZ_OK)
189        {
190            printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
191            break;
192        }
193        if (file_info.uncompressed_size>0)
194            ratio = (file_info.compressed_size*100)/file_info.uncompressed_size;
195
196        /* display a '*' if the file is crypted */
197        if ((file_info.flag & 1) != 0)
198            charCrypt='*';
199
200        if (file_info.compression_method==0)
201            string_method="Stored";
202        else
203        if (file_info.compression_method==Z_DEFLATED)
204        {
205            uInt iLevel=(uInt)((file_info.flag & 0x6)/2);
206            if (iLevel==0)
207              string_method="Defl:N";
208            else if (iLevel==1)
209              string_method="Defl:X";
210            else if ((iLevel==2) || (iLevel==3))
211              string_method="Defl:F"; /* 2:fast , 3 : extra fast*/
212        }
213        else
214            string_method="Unkn. ";
215
216        printf("%7lu  %6s%c%7lu %3lu%%  %2.2lu-%2.2lu-%2.2lu  %2.2lu:%2.2lu  %8.8lx   %s\n",
217                file_info.uncompressed_size,string_method,
218                charCrypt,
219                file_info.compressed_size,
220                ratio,
221                (uLong)file_info.tmu_date.tm_mon + 1,
222                (uLong)file_info.tmu_date.tm_mday,
223                (uLong)file_info.tmu_date.tm_year % 100,
224                (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
225                (uLong)file_info.crc,filename_inzip);
226        if ((i+1)<gi.number_entry)
227        {
228            err = unzGoToNextFile(uf);
229            if (err!=UNZ_OK)
230            {
231                printf("error %d with zipfile in unzGoToNextFile\n",err);
232                break;
233            }
234        }
235    }
236
237    return 0;
238}
239
240
241int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)
242    unzFile uf;
243    const int* popt_extract_without_path;
244    int* popt_overwrite;
245    const char* password;
246{
247    char filename_inzip[256];
248    char* filename_withoutpath;
249    char* p;
250    int err=UNZ_OK;
251    FILE *fout=NULL;
252    void* buf;
253    uInt size_buf;
254
255    unz_file_info file_info;
256    uLong ratio=0;
257    err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
258
259    if (err!=UNZ_OK)
260    {
261        printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
262        return err;
263    }
264
265    size_buf = WRITEBUFFERSIZE;
266    buf = (void*)malloc(size_buf);
267    if (buf==NULL)
268    {
269        printf("Error allocating memory\n");
270        return UNZ_INTERNALERROR;
271    }
272
273    p = filename_withoutpath = filename_inzip;
274    while ((*p) != '\0')
275    {
276        if (((*p)=='/') || ((*p)=='\\'))
277            filename_withoutpath = p+1;
278        p++;
279    }
280
281    if ((*filename_withoutpath)=='\0')
282    {
283        if ((*popt_extract_without_path)==0)
284        {
285            printf("creating directory: %s\n",filename_inzip);
286            mymkdir(filename_inzip);
287        }
288    }
289    else
290    {
291        const char* write_filename;
292        int skip=0;
293
294        if ((*popt_extract_without_path)==0)
295            write_filename = filename_inzip;
296        else
297            write_filename = filename_withoutpath;
298
299        err = unzOpenCurrentFilePassword(uf,password);
300        if (err!=UNZ_OK)
301        {
302            printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
303        }
304
305        if (((*popt_overwrite)==0) && (err==UNZ_OK))
306        {
307            char rep=0;
308            FILE* ftestexist;
309            ftestexist = fopen(write_filename,"rb");
310            if (ftestexist!=NULL)
311            {
312                fclose(ftestexist);
313                do
314                {
315                    char answer[128];
316                    int ret;
317
318                    printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
319                    ret = scanf("%1s",answer);
320                    if (ret != 1)
321                    {
322                       exit(EXIT_FAILURE);
323                    }
324                    rep = answer[0] ;
325                    if ((rep>='a') && (rep<='z'))
326                        rep -= 0x20;
327                }
328                while ((rep!='Y') && (rep!='N') && (rep!='A'));
329            }
330
331            if (rep == 'N')
332                skip = 1;
333
334            if (rep == 'A')
335                *popt_overwrite=1;
336        }
337
338        if ((skip==0) && (err==UNZ_OK))
339        {
340            fout=fopen(write_filename,"wb");
341
342            /* some zipfile don't contain directory alone before file */
343            if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
344                                (filename_withoutpath!=(char*)filename_inzip))
345            {
346                char c=*(filename_withoutpath-1);
347                *(filename_withoutpath-1)='\0';
348                makedir(write_filename);
349                *(filename_withoutpath-1)=c;
350                fout=fopen(write_filename,"wb");
351            }
352
353            if (fout==NULL)
354            {
355                printf("error opening %s\n",write_filename);
356            }
357        }
358
359        if (fout!=NULL)
360        {
361            printf(" extracting: %s\n",write_filename);
362
363            do
364            {
365                err = unzReadCurrentFile(uf,buf,size_buf);
366                if (err<0)
367                {
368                    printf("error %d with zipfile in unzReadCurrentFile\n",err);
369                    break;
370                }
371                if (err>0)
372                    if (fwrite(buf,err,1,fout)!=1)
373                    {
374                        printf("error in writing extracted file\n");
375                        err=UNZ_ERRNO;
376                        break;
377                    }
378            }
379            while (err>0);
380            if (fout)
381                    fclose(fout);
382
383            if (err==0)
384                change_file_date(write_filename,file_info.dosDate,
385                                 file_info.tmu_date);
386        }
387
388        if (err==UNZ_OK)
389        {
390            err = unzCloseCurrentFile (uf);
391            if (err!=UNZ_OK)
392            {
393                printf("error %d with zipfile in unzCloseCurrentFile\n",err);
394            }
395        }
396        else
397            unzCloseCurrentFile(uf); /* don't lose the error */
398    }
399
400    free(buf);
401    return err;
402}
403
404
405int do_extract(uf,opt_extract_without_path,opt_overwrite,password)
406    unzFile uf;
407    int opt_extract_without_path;
408    int opt_overwrite;
409    const char* password;
410{
411    uLong i;
412    unz_global_info gi;
413    int err;
414    FILE* fout=NULL;
415
416    err = unzGetGlobalInfo (uf,&gi);
417    if (err!=UNZ_OK)
418        printf("error %d with zipfile in unzGetGlobalInfo \n",err);
419
420    for (i=0;i<gi.number_entry;i++)
421    {
422        if (do_extract_currentfile(uf,&opt_extract_without_path,
423                                      &opt_overwrite,
424                                      password) != UNZ_OK)
425            break;
426
427        if ((i+1)<gi.number_entry)
428        {
429            err = unzGoToNextFile(uf);
430            if (err!=UNZ_OK)
431            {
432                printf("error %d with zipfile in unzGoToNextFile\n",err);
433                break;
434            }
435        }
436    }
437
438    return 0;
439}
440
441int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)
442    unzFile uf;
443    const char* filename;
444    int opt_extract_without_path;
445    int opt_overwrite;
446    const char* password;
447{
448    int err = UNZ_OK;
449    if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
450    {
451        printf("file %s not found in the zipfile\n",filename);
452        return 2;
453    }
454
455    if (do_extract_currentfile(uf,&opt_extract_without_path,
456                                      &opt_overwrite,
457                                      password) == UNZ_OK)
458        return 0;
459    else
460        return 1;
461}
462
463
464int main(argc,argv)
465    int argc;
466    char *argv[];
467{
468    const char *zipfilename=NULL;
469    const char *filename_to_extract=NULL;
470    const char *password=NULL;
471    char filename_try[MAXFILENAME+16] = "";
472    int i;
473    int opt_do_list=0;
474    int opt_do_extract=1;
475    int opt_do_extract_withoutpath=0;
476    int opt_overwrite=0;
477    int opt_extractdir=0;
478    const char *dirname=NULL;
479    unzFile uf=NULL;
480
481    do_banner();
482    if (argc==1)
483    {
484        do_help();
485        return 0;
486    }
487    else
488    {
489        for (i=1;i<argc;i++)
490        {
491            if ((*argv[i])=='-')
492            {
493                const char *p=argv[i]+1;
494
495                while ((*p)!='\0')
496                {
497                    char c=*(p++);;
498                    if ((c=='l') || (c=='L'))
499                        opt_do_list = 1;
500                    if ((c=='v') || (c=='V'))
501                        opt_do_list = 1;
502                    if ((c=='x') || (c=='X'))
503                        opt_do_extract = 1;
504                    if ((c=='e') || (c=='E'))
505                        opt_do_extract = opt_do_extract_withoutpath = 1;
506                    if ((c=='o') || (c=='O'))
507                        opt_overwrite=1;
508                    if ((c=='d') || (c=='D'))
509                    {
510                        opt_extractdir=1;
511                        dirname=argv[i+1];
512                    }
513
514                    if (((c=='p') || (c=='P')) && (i+1<argc))
515                    {
516                        password=argv[i+1];
517                        i++;
518                    }
519                }
520            }
521            else
522            {
523                if (zipfilename == NULL)
524                    zipfilename = argv[i];
525                else if ((filename_to_extract==NULL) && (!opt_extractdir))
526                        filename_to_extract = argv[i] ;
527            }
528        }
529    }
530
531    if (zipfilename!=NULL)
532    {
533
534#        ifdef USEWIN32IOAPI
535        zlib_filefunc_def ffunc;
536#        endif
537
538        strncpy(filename_try, zipfilename,MAXFILENAME-1);
539        /* strncpy doesnt append the trailing NULL, of the string is too long. */
540        filename_try[ MAXFILENAME ] = '\0';
541
542#        ifdef USEWIN32IOAPI
543        fill_win32_filefunc(&ffunc);
544        uf = unzOpen2(zipfilename,&ffunc);
545#        else
546        uf = unzOpen(zipfilename);
547#        endif
548        if (uf==NULL)
549        {
550            strcat(filename_try,".zip");
551#            ifdef USEWIN32IOAPI
552            uf = unzOpen2(filename_try,&ffunc);
553#            else
554            uf = unzOpen(filename_try);
555#            endif
556        }
557    }
558
559    if (uf==NULL)
560    {
561        printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename);
562        return 1;
563    }
564    printf("%s opened\n",filename_try);
565
566    if (opt_do_list==1)
567        return do_list(uf);
568    else if (opt_do_extract==1)
569    {
570        if (opt_extractdir && chdir(dirname))
571        {
572          printf("Error changing into %s, aborting\n", dirname);
573          exit(-1);
574        }
575
576        if (filename_to_extract == NULL)
577            return do_extract(uf,opt_do_extract_withoutpath,opt_overwrite,password);
578        else
579            return do_extract_onefile(uf,filename_to_extract,
580                                      opt_do_extract_withoutpath,opt_overwrite,password);
581    }
582    unzCloseCurrentFile(uf);
583
584    return 0;
585}
586