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