7zMain.c (16494B)
1 /* 7zMain.c - Test application for 7z Decoder 2 2018-04-19 : Igor Pavlov : Public domain */ 3 4 #include "Precomp.h" 5 6 #include <stdio.h> 7 #include <string.h> 8 9 #include "../../CpuArch.h" 10 11 #include "../../7z.h" 12 #include "../../7zAlloc.h" 13 #include "../../7zBuf.h" 14 #include "../../7zCrc.h" 15 #include "../../7zFile.h" 16 #include "../../7zVersion.h" 17 18 #ifndef USE_WINDOWS_FILE 19 /* for mkdir */ 20 #ifdef _WIN32 21 #include <direct.h> 22 #else 23 #include <sys/stat.h> 24 #include <errno.h> 25 #endif 26 #endif 27 28 29 #define kInputBufSize ((size_t)1 << 18) 30 31 static const ISzAlloc g_Alloc = { SzAlloc, SzFree }; 32 33 34 static void Print(const char *s) 35 { 36 fputs(s, stdout); 37 } 38 39 40 static int Buf_EnsureSize(CBuf *dest, size_t size) 41 { 42 if (dest->size >= size) 43 return 1; 44 Buf_Free(dest, &g_Alloc); 45 return Buf_Create(dest, size, &g_Alloc); 46 } 47 48 #ifndef _WIN32 49 #define _USE_UTF8 50 #endif 51 52 /* #define _USE_UTF8 */ 53 54 #ifdef _USE_UTF8 55 56 #define _UTF8_START(n) (0x100 - (1 << (7 - (n)))) 57 58 #define _UTF8_RANGE(n) (((UInt32)1) << ((n) * 5 + 6)) 59 60 #define _UTF8_HEAD(n, val) ((Byte)(_UTF8_START(n) + (val >> (6 * (n))))) 61 #define _UTF8_CHAR(n, val) ((Byte)(0x80 + (((val) >> (6 * (n))) & 0x3F))) 62 63 static size_t Utf16_To_Utf8_Calc(const UInt16 *src, const UInt16 *srcLim) 64 { 65 size_t size = 0; 66 for (;;) 67 { 68 UInt32 val; 69 if (src == srcLim) 70 return size; 71 72 size++; 73 val = *src++; 74 75 if (val < 0x80) 76 continue; 77 78 if (val < _UTF8_RANGE(1)) 79 { 80 size++; 81 continue; 82 } 83 84 if (val >= 0xD800 && val < 0xDC00 && src != srcLim) 85 { 86 UInt32 c2 = *src; 87 if (c2 >= 0xDC00 && c2 < 0xE000) 88 { 89 src++; 90 size += 3; 91 continue; 92 } 93 } 94 95 size += 2; 96 } 97 } 98 99 static Byte *Utf16_To_Utf8(Byte *dest, const UInt16 *src, const UInt16 *srcLim) 100 { 101 for (;;) 102 { 103 UInt32 val; 104 if (src == srcLim) 105 return dest; 106 107 val = *src++; 108 109 if (val < 0x80) 110 { 111 *dest++ = (char)val; 112 continue; 113 } 114 115 if (val < _UTF8_RANGE(1)) 116 { 117 dest[0] = _UTF8_HEAD(1, val); 118 dest[1] = _UTF8_CHAR(0, val); 119 dest += 2; 120 continue; 121 } 122 123 if (val >= 0xD800 && val < 0xDC00 && src != srcLim) 124 { 125 UInt32 c2 = *src; 126 if (c2 >= 0xDC00 && c2 < 0xE000) 127 { 128 src++; 129 val = (((val - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; 130 dest[0] = _UTF8_HEAD(3, val); 131 dest[1] = _UTF8_CHAR(2, val); 132 dest[2] = _UTF8_CHAR(1, val); 133 dest[3] = _UTF8_CHAR(0, val); 134 dest += 4; 135 continue; 136 } 137 } 138 139 dest[0] = _UTF8_HEAD(2, val); 140 dest[1] = _UTF8_CHAR(1, val); 141 dest[2] = _UTF8_CHAR(0, val); 142 dest += 3; 143 } 144 } 145 146 static SRes Utf16_To_Utf8Buf(CBuf *dest, const UInt16 *src, size_t srcLen) 147 { 148 size_t destLen = Utf16_To_Utf8_Calc(src, src + srcLen); 149 destLen += 1; 150 if (!Buf_EnsureSize(dest, destLen)) 151 return SZ_ERROR_MEM; 152 *Utf16_To_Utf8(dest->data, src, src + srcLen) = 0; 153 return SZ_OK; 154 } 155 156 #endif 157 158 static SRes Utf16_To_Char(CBuf *buf, const UInt16 *s 159 #ifndef _USE_UTF8 160 , UINT codePage 161 #endif 162 ) 163 { 164 unsigned len = 0; 165 for (len = 0; s[len] != 0; len++); 166 167 #ifndef _USE_UTF8 168 { 169 unsigned size = len * 3 + 100; 170 if (!Buf_EnsureSize(buf, size)) 171 return SZ_ERROR_MEM; 172 { 173 buf->data[0] = 0; 174 if (len != 0) 175 { 176 char defaultChar = '_'; 177 BOOL defUsed; 178 unsigned numChars = 0; 179 numChars = WideCharToMultiByte(codePage, 0, s, len, (char *)buf->data, size, &defaultChar, &defUsed); 180 if (numChars == 0 || numChars >= size) 181 return SZ_ERROR_FAIL; 182 buf->data[numChars] = 0; 183 } 184 return SZ_OK; 185 } 186 } 187 #else 188 return Utf16_To_Utf8Buf(buf, s, len); 189 #endif 190 } 191 192 #ifdef _WIN32 193 #ifndef USE_WINDOWS_FILE 194 static UINT g_FileCodePage = CP_ACP; 195 #endif 196 #define MY_FILE_CODE_PAGE_PARAM ,g_FileCodePage 197 #else 198 #define MY_FILE_CODE_PAGE_PARAM 199 #endif 200 201 static WRes MyCreateDir(const UInt16 *name) 202 { 203 #ifdef USE_WINDOWS_FILE 204 205 return CreateDirectoryW(name, NULL) ? 0 : GetLastError(); 206 207 #else 208 209 CBuf buf; 210 WRes res; 211 Buf_Init(&buf); 212 RINOK(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM)); 213 214 res = 215 #ifdef _WIN32 216 _mkdir((const char *)buf.data) 217 #else 218 mkdir((const char *)buf.data, 0777) 219 #endif 220 == 0 ? 0 : errno; 221 Buf_Free(&buf, &g_Alloc); 222 return res; 223 224 #endif 225 } 226 227 static WRes OutFile_OpenUtf16(CSzFile *p, const UInt16 *name) 228 { 229 #ifdef USE_WINDOWS_FILE 230 return OutFile_OpenW(p, name); 231 #else 232 CBuf buf; 233 WRes res; 234 Buf_Init(&buf); 235 RINOK(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM)); 236 res = OutFile_Open(p, (const char *)buf.data); 237 Buf_Free(&buf, &g_Alloc); 238 return res; 239 #endif 240 } 241 242 243 static SRes PrintString(const UInt16 *s) 244 { 245 CBuf buf; 246 SRes res; 247 Buf_Init(&buf); 248 res = Utf16_To_Char(&buf, s 249 #ifndef _USE_UTF8 250 , CP_OEMCP 251 #endif 252 ); 253 if (res == SZ_OK) 254 Print((const char *)buf.data); 255 Buf_Free(&buf, &g_Alloc); 256 return res; 257 } 258 259 static void UInt64ToStr(UInt64 value, char *s, int numDigits) 260 { 261 char temp[32]; 262 int pos = 0; 263 do 264 { 265 temp[pos++] = (char)('0' + (unsigned)(value % 10)); 266 value /= 10; 267 } 268 while (value != 0); 269 270 for (numDigits -= pos; numDigits > 0; numDigits--) 271 *s++ = ' '; 272 273 do 274 *s++ = temp[--pos]; 275 while (pos); 276 *s = '\0'; 277 } 278 279 static char *UIntToStr(char *s, unsigned value, int numDigits) 280 { 281 char temp[16]; 282 int pos = 0; 283 do 284 temp[pos++] = (char)('0' + (value % 10)); 285 while (value /= 10); 286 287 for (numDigits -= pos; numDigits > 0; numDigits--) 288 *s++ = '0'; 289 290 do 291 *s++ = temp[--pos]; 292 while (pos); 293 *s = '\0'; 294 return s; 295 } 296 297 static void UIntToStr_2(char *s, unsigned value) 298 { 299 s[0] = (char)('0' + (value / 10)); 300 s[1] = (char)('0' + (value % 10)); 301 } 302 303 #define PERIOD_4 (4 * 365 + 1) 304 #define PERIOD_100 (PERIOD_4 * 25 - 1) 305 #define PERIOD_400 (PERIOD_100 * 4 + 1) 306 307 static void ConvertFileTimeToString(const CNtfsFileTime *nt, char *s) 308 { 309 unsigned year, mon, hour, min, sec; 310 Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 311 unsigned t; 312 UInt32 v; 313 UInt64 v64 = nt->Low | ((UInt64)nt->High << 32); 314 v64 /= 10000000; 315 sec = (unsigned)(v64 % 60); v64 /= 60; 316 min = (unsigned)(v64 % 60); v64 /= 60; 317 hour = (unsigned)(v64 % 24); v64 /= 24; 318 319 v = (UInt32)v64; 320 321 year = (unsigned)(1601 + v / PERIOD_400 * 400); 322 v %= PERIOD_400; 323 324 t = v / PERIOD_100; if (t == 4) t = 3; year += t * 100; v -= t * PERIOD_100; 325 t = v / PERIOD_4; if (t == 25) t = 24; year += t * 4; v -= t * PERIOD_4; 326 t = v / 365; if (t == 4) t = 3; year += t; v -= t * 365; 327 328 if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) 329 ms[1] = 29; 330 for (mon = 0;; mon++) 331 { 332 unsigned d = ms[mon]; 333 if (v < d) 334 break; 335 v -= d; 336 } 337 s = UIntToStr(s, year, 4); *s++ = '-'; 338 UIntToStr_2(s, mon + 1); s[2] = '-'; s += 3; 339 UIntToStr_2(s, (unsigned)v + 1); s[2] = ' '; s += 3; 340 UIntToStr_2(s, hour); s[2] = ':'; s += 3; 341 UIntToStr_2(s, min); s[2] = ':'; s += 3; 342 UIntToStr_2(s, sec); s[2] = 0; 343 } 344 345 static void PrintLF() 346 { 347 Print("\n"); 348 } 349 350 static void PrintError(char *s) 351 { 352 Print("\nERROR: "); 353 Print(s); 354 PrintLF(); 355 } 356 357 static void GetAttribString(UInt32 wa, Bool isDir, char *s) 358 { 359 #ifdef USE_WINDOWS_FILE 360 s[0] = (char)(((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : '.'); 361 s[1] = (char)(((wa & FILE_ATTRIBUTE_READONLY ) != 0) ? 'R': '.'); 362 s[2] = (char)(((wa & FILE_ATTRIBUTE_HIDDEN ) != 0) ? 'H': '.'); 363 s[3] = (char)(((wa & FILE_ATTRIBUTE_SYSTEM ) != 0) ? 'S': '.'); 364 s[4] = (char)(((wa & FILE_ATTRIBUTE_ARCHIVE ) != 0) ? 'A': '.'); 365 s[5] = 0; 366 #else 367 s[0] = (char)(((wa & (1 << 4)) != 0 || isDir) ? 'D' : '.'); 368 s[1] = 0; 369 #endif 370 } 371 372 373 // #define NUM_PARENTS_MAX 128 374 375 int MY_CDECL main(int numargs, char *args[]) 376 { 377 ISzAlloc allocImp; 378 ISzAlloc allocTempImp; 379 380 CFileInStream archiveStream; 381 CLookToRead2 lookStream; 382 CSzArEx db; 383 SRes res; 384 UInt16 *temp = NULL; 385 size_t tempSize = 0; 386 // UInt32 parents[NUM_PARENTS_MAX]; 387 388 Print("\n7z Decoder " MY_VERSION_CPU " : " MY_COPYRIGHT_DATE "\n\n"); 389 390 if (numargs == 1) 391 { 392 Print( 393 "Usage: 7zDec <command> <archive_name>\n\n" 394 "<Commands>\n" 395 " e: Extract files from archive (without using directory names)\n" 396 " l: List contents of archive\n" 397 " t: Test integrity of archive\n" 398 " x: eXtract files with full paths\n"); 399 return 0; 400 } 401 402 if (numargs < 3) 403 { 404 PrintError("incorrect command"); 405 return 1; 406 } 407 408 #if defined(_WIN32) && !defined(USE_WINDOWS_FILE) && !defined(UNDER_CE) 409 g_FileCodePage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; 410 #endif 411 412 413 allocImp = g_Alloc; 414 allocTempImp = g_Alloc; 415 416 #ifdef UNDER_CE 417 if (InFile_OpenW(&archiveStream.file, L"\test.7z")) 418 #else 419 if (InFile_Open(&archiveStream.file, args[2])) 420 #endif 421 { 422 PrintError("can not open input file"); 423 return 1; 424 } 425 426 FileInStream_CreateVTable(&archiveStream); 427 LookToRead2_CreateVTable(&lookStream, False); 428 lookStream.buf = NULL; 429 430 res = SZ_OK; 431 432 { 433 lookStream.buf = ISzAlloc_Alloc(&allocImp, kInputBufSize); 434 if (!lookStream.buf) 435 res = SZ_ERROR_MEM; 436 else 437 { 438 lookStream.bufSize = kInputBufSize; 439 lookStream.realStream = &archiveStream.vt; 440 LookToRead2_Init(&lookStream); 441 } 442 } 443 444 CrcGenerateTable(); 445 446 SzArEx_Init(&db); 447 448 if (res == SZ_OK) 449 { 450 res = SzArEx_Open(&db, &lookStream.vt, &allocImp, &allocTempImp); 451 } 452 453 if (res == SZ_OK) 454 { 455 char *command = args[1]; 456 int listCommand = 0, testCommand = 0, fullPaths = 0; 457 458 if (strcmp(command, "l") == 0) listCommand = 1; 459 else if (strcmp(command, "t") == 0) testCommand = 1; 460 else if (strcmp(command, "e") == 0) { } 461 else if (strcmp(command, "x") == 0) { fullPaths = 1; } 462 else 463 { 464 PrintError("incorrect command"); 465 res = SZ_ERROR_FAIL; 466 } 467 468 if (res == SZ_OK) 469 { 470 UInt32 i; 471 472 /* 473 if you need cache, use these 3 variables. 474 if you use external function, you can make these variable as static. 475 */ 476 UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ 477 Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */ 478 size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ 479 480 for (i = 0; i < db.NumFiles; i++) 481 { 482 size_t offset = 0; 483 size_t outSizeProcessed = 0; 484 // const CSzFileItem *f = db.Files + i; 485 size_t len; 486 unsigned isDir = SzArEx_IsDir(&db, i); 487 if (listCommand == 0 && isDir && !fullPaths) 488 continue; 489 len = SzArEx_GetFileNameUtf16(&db, i, NULL); 490 // len = SzArEx_GetFullNameLen(&db, i); 491 492 if (len > tempSize) 493 { 494 SzFree(NULL, temp); 495 tempSize = len; 496 temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(temp[0])); 497 if (!temp) 498 { 499 res = SZ_ERROR_MEM; 500 break; 501 } 502 } 503 504 SzArEx_GetFileNameUtf16(&db, i, temp); 505 /* 506 if (SzArEx_GetFullNameUtf16_Back(&db, i, temp + len) != temp) 507 { 508 res = SZ_ERROR_FAIL; 509 break; 510 } 511 */ 512 513 if (listCommand) 514 { 515 char attr[8], s[32], t[32]; 516 UInt64 fileSize; 517 518 GetAttribString(SzBitWithVals_Check(&db.Attribs, i) ? db.Attribs.Vals[i] : 0, isDir, attr); 519 520 fileSize = SzArEx_GetFileSize(&db, i); 521 UInt64ToStr(fileSize, s, 10); 522 523 if (SzBitWithVals_Check(&db.MTime, i)) 524 ConvertFileTimeToString(&db.MTime.Vals[i], t); 525 else 526 { 527 size_t j; 528 for (j = 0; j < 19; j++) 529 t[j] = ' '; 530 t[j] = '\0'; 531 } 532 533 Print(t); 534 Print(" "); 535 Print(attr); 536 Print(" "); 537 Print(s); 538 Print(" "); 539 res = PrintString(temp); 540 if (res != SZ_OK) 541 break; 542 if (isDir) 543 Print("/"); 544 PrintLF(); 545 continue; 546 } 547 548 Print(testCommand ? 549 "Testing ": 550 "Extracting "); 551 res = PrintString(temp); 552 if (res != SZ_OK) 553 break; 554 555 if (isDir) 556 Print("/"); 557 else 558 { 559 res = SzArEx_Extract(&db, &lookStream.vt, i, 560 &blockIndex, &outBuffer, &outBufferSize, 561 &offset, &outSizeProcessed, 562 &allocImp, &allocTempImp); 563 if (res != SZ_OK) 564 break; 565 } 566 567 if (!testCommand) 568 { 569 CSzFile outFile; 570 size_t processedSize; 571 size_t j; 572 UInt16 *name = (UInt16 *)temp; 573 const UInt16 *destPath = (const UInt16 *)name; 574 575 for (j = 0; name[j] != 0; j++) 576 if (name[j] == '/') 577 { 578 if (fullPaths) 579 { 580 name[j] = 0; 581 MyCreateDir(name); 582 name[j] = CHAR_PATH_SEPARATOR; 583 } 584 else 585 destPath = name + j + 1; 586 } 587 588 if (isDir) 589 { 590 MyCreateDir(destPath); 591 PrintLF(); 592 continue; 593 } 594 else if (OutFile_OpenUtf16(&outFile, destPath)) 595 { 596 PrintError("can not open output file"); 597 res = SZ_ERROR_FAIL; 598 break; 599 } 600 601 processedSize = outSizeProcessed; 602 603 if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed) 604 { 605 PrintError("can not write output file"); 606 res = SZ_ERROR_FAIL; 607 break; 608 } 609 610 #ifdef USE_WINDOWS_FILE 611 { 612 FILETIME mtime, ctime; 613 FILETIME *mtimePtr = NULL; 614 FILETIME *ctimePtr = NULL; 615 616 if (SzBitWithVals_Check(&db.MTime, i)) 617 { 618 const CNtfsFileTime *t = &db.MTime.Vals[i]; 619 mtime.dwLowDateTime = (DWORD)(t->Low); 620 mtime.dwHighDateTime = (DWORD)(t->High); 621 mtimePtr = &mtime; 622 } 623 if (SzBitWithVals_Check(&db.CTime, i)) 624 { 625 const CNtfsFileTime *t = &db.CTime.Vals[i]; 626 ctime.dwLowDateTime = (DWORD)(t->Low); 627 ctime.dwHighDateTime = (DWORD)(t->High); 628 ctimePtr = &ctime; 629 } 630 if (mtimePtr || ctimePtr) 631 SetFileTime(outFile.handle, ctimePtr, NULL, mtimePtr); 632 } 633 #endif 634 635 if (File_Close(&outFile)) 636 { 637 PrintError("can not close output file"); 638 res = SZ_ERROR_FAIL; 639 break; 640 } 641 642 #ifdef USE_WINDOWS_FILE 643 if (SzBitWithVals_Check(&db.Attribs, i)) 644 { 645 UInt32 attrib = db.Attribs.Vals[i]; 646 /* p7zip stores posix attributes in high 16 bits and adds 0x8000 as marker. 647 We remove posix bits, if we detect posix mode field */ 648 if ((attrib & 0xF0000000) != 0) 649 attrib &= 0x7FFF; 650 SetFileAttributesW(destPath, attrib); 651 } 652 #endif 653 } 654 PrintLF(); 655 } 656 ISzAlloc_Free(&allocImp, outBuffer); 657 } 658 } 659 660 SzFree(NULL, temp); 661 SzArEx_Free(&db, &allocImp); 662 ISzAlloc_Free(&allocImp, lookStream.buf); 663 664 File_Close(&archiveStream.file); 665 666 if (res == SZ_OK) 667 { 668 Print("\nEverything is Ok\n"); 669 return 0; 670 } 671 672 if (res == SZ_ERROR_UNSUPPORTED) 673 PrintError("decoder doesn't support this archive"); 674 else if (res == SZ_ERROR_MEM) 675 PrintError("can not allocate memory"); 676 else if (res == SZ_ERROR_CRC) 677 PrintError("CRC error"); 678 else 679 { 680 char s[32]; 681 UInt64ToStr(res, s, 0); 682 PrintError(s); 683 } 684 685 return 1; 686 }