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