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