1 /* 7zMain.c - Test application for 7z Decoder 2 2015-01-02 : Igor Pavlov : Public domain */ 3 4 #include "Precomp.h" 5 6 #include <stdio.h> 7 #include <string.h> 8 9 #include "../../7z.h" 10 #include "../../7zAlloc.h" 11 #include "../../7zBuf.h" 12 #include "../../7zCrc.h" 13 #include "../../7zFile.h" 14 #include "../../7zVersion.h" 15 16 #ifndef USE_WINDOWS_FILE 17 /* for mkdir */ 18 #ifdef _WIN32 19 #include <direct.h> 20 #else 21 #include <sys/stat.h> 22 #include <errno.h> 23 #endif 24 #endif 25 26 static ISzAlloc g_Alloc = { SzAlloc, SzFree }; 27 28 static int Buf_EnsureSize(CBuf *dest, size_t size) 29 { 30 if (dest->size >= size) 31 return 1; 32 Buf_Free(dest, &g_Alloc); 33 return Buf_Create(dest, size, &g_Alloc); 34 } 35 36 #ifndef _WIN32 37 38 static Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 39 40 static Bool Utf16_To_Utf8(Byte *dest, size_t *destLen, const UInt16 *src, size_t srcLen) 41 { 42 size_t destPos = 0, srcPos = 0; 43 for (;;) 44 { 45 unsigned numAdds; 46 UInt32 value; 47 if (srcPos == srcLen) 48 { 49 *destLen = destPos; 50 return True; 51 } 52 value = src[srcPos++]; 53 if (value < 0x80) 54 { 55 if (dest) 56 dest[destPos] = (char)value; 57 destPos++; 58 continue; 59 } 60 if (value >= 0xD800 && value < 0xE000) 61 { 62 UInt32 c2; 63 if (value >= 0xDC00 || srcPos == srcLen) 64 break; 65 c2 = src[srcPos++]; 66 if (c2 < 0xDC00 || c2 >= 0xE000) 67 break; 68 value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; 69 } 70 for (numAdds = 1; numAdds < 5; numAdds++) 71 if (value < (((UInt32)1) << (numAdds * 5 + 6))) 72 break; 73 if (dest) 74 dest[destPos] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); 75 destPos++; 76 do 77 { 78 numAdds--; 79 if (dest) 80 dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); 81 destPos++; 82 } 83 while (numAdds != 0); 84 } 85 *destLen = destPos; 86 return False; 87 } 88 89 static SRes Utf16_To_Utf8Buf(CBuf *dest, const UInt16 *src, size_t srcLen) 90 { 91 size_t destLen = 0; 92 Bool res; 93 Utf16_To_Utf8(NULL, &destLen, src, srcLen); 94 destLen += 1; 95 if (!Buf_EnsureSize(dest, destLen)) 96 return SZ_ERROR_MEM; 97 res = Utf16_To_Utf8(dest->data, &destLen, src, srcLen); 98 dest->data[destLen] = 0; 99 return res ? SZ_OK : SZ_ERROR_FAIL; 100 } 101 102 #endif 103 104 static SRes Utf16_To_Char(CBuf *buf, const UInt16 *s 105 #ifdef _WIN32 106 , UINT codePage 107 #endif 108 ) 109 { 110 unsigned len = 0; 111 for (len = 0; s[len] != 0; len++); 112 113 #ifdef _WIN32 114 { 115 unsigned size = len * 3 + 100; 116 if (!Buf_EnsureSize(buf, size)) 117 return SZ_ERROR_MEM; 118 { 119 buf->data[0] = 0; 120 if (len != 0) 121 { 122 char defaultChar = '_'; 123 BOOL defUsed; 124 unsigned numChars = 0; 125 numChars = WideCharToMultiByte(codePage, 0, s, len, (char *)buf->data, size, &defaultChar, &defUsed); 126 if (numChars == 0 || numChars >= size) 127 return SZ_ERROR_FAIL; 128 buf->data[numChars] = 0; 129 } 130 return SZ_OK; 131 } 132 } 133 #else 134 return Utf16_To_Utf8Buf(buf, s, len); 135 #endif 136 } 137 138 #ifdef _WIN32 139 #ifndef USE_WINDOWS_FILE 140 static UINT g_FileCodePage = CP_ACP; 141 #endif 142 #define MY_FILE_CODE_PAGE_PARAM ,g_FileCodePage 143 #else 144 #define MY_FILE_CODE_PAGE_PARAM 145 #endif 146 147 static WRes MyCreateDir(const UInt16 *name) 148 { 149 #ifdef USE_WINDOWS_FILE 150 151 return CreateDirectoryW(name, NULL) ? 0 : GetLastError(); 152 153 #else 154 155 CBuf buf; 156 WRes res; 157 Buf_Init(&buf); 158 RINOK(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM)); 159 160 res = 161 #ifdef _WIN32 162 _mkdir((const char *)buf.data) 163 #else 164 mkdir((const char *)buf.data, 0777) 165 #endif 166 == 0 ? 0 : errno; 167 Buf_Free(&buf, &g_Alloc); 168 return res; 169 170 #endif 171 } 172 173 static WRes OutFile_OpenUtf16(CSzFile *p, const UInt16 *name) 174 { 175 #ifdef USE_WINDOWS_FILE 176 return OutFile_OpenW(p, name); 177 #else 178 CBuf buf; 179 WRes res; 180 Buf_Init(&buf); 181 RINOK(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM)); 182 res = OutFile_Open(p, (const char *)buf.data); 183 Buf_Free(&buf, &g_Alloc); 184 return res; 185 #endif 186 } 187 188 static SRes PrintString(const UInt16 *s) 189 { 190 CBuf buf; 191 SRes res; 192 Buf_Init(&buf); 193 res = Utf16_To_Char(&buf, s 194 #ifdef _WIN32 195 , CP_OEMCP 196 #endif 197 ); 198 if (res == SZ_OK) 199 fputs((const char *)buf.data, stdout); 200 Buf_Free(&buf, &g_Alloc); 201 return res; 202 } 203 204 static void UInt64ToStr(UInt64 value, char *s) 205 { 206 char temp[32]; 207 int pos = 0; 208 do 209 { 210 temp[pos++] = (char)('0' + (unsigned)(value % 10)); 211 value /= 10; 212 } 213 while (value != 0); 214 do 215 *s++ = temp[--pos]; 216 while (pos); 217 *s = '\0'; 218 } 219 220 static char *UIntToStr(char *s, unsigned value, int numDigits) 221 { 222 char temp[16]; 223 int pos = 0; 224 do 225 temp[pos++] = (char)('0' + (value % 10)); 226 while (value /= 10); 227 for (numDigits -= pos; numDigits > 0; numDigits--) 228 *s++ = '0'; 229 do 230 *s++ = temp[--pos]; 231 while (pos); 232 *s = '\0'; 233 return s; 234 } 235 236 static void UIntToStr_2(char *s, unsigned value) 237 { 238 s[0] = (char)('0' + (value / 10)); 239 s[1] = (char)('0' + (value % 10)); 240 } 241 242 #define PERIOD_4 (4 * 365 + 1) 243 #define PERIOD_100 (PERIOD_4 * 25 - 1) 244 #define PERIOD_400 (PERIOD_100 * 4 + 1) 245 246 static void ConvertFileTimeToString(const CNtfsFileTime *nt, char *s) 247 { 248 unsigned year, mon, hour, min, sec; 249 Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 250 unsigned t; 251 UInt32 v; 252 UInt64 v64 = nt->Low | ((UInt64)nt->High << 32); 253 v64 /= 10000000; 254 sec = (unsigned)(v64 % 60); v64 /= 60; 255 min = (unsigned)(v64 % 60); v64 /= 60; 256 hour = (unsigned)(v64 % 24); v64 /= 24; 257 258 v = (UInt32)v64; 259 260 year = (unsigned)(1601 + v / PERIOD_400 * 400); 261 v %= PERIOD_400; 262 263 t = v / PERIOD_100; if (t == 4) t = 3; year += t * 100; v -= t * PERIOD_100; 264 t = v / PERIOD_4; if (t == 25) t = 24; year += t * 4; v -= t * PERIOD_4; 265 t = v / 365; if (t == 4) t = 3; year += t; v -= t * 365; 266 267 if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) 268 ms[1] = 29; 269 for (mon = 0;; mon++) 270 { 271 unsigned s = ms[mon]; 272 if (v < s) 273 break; 274 v -= s; 275 } 276 s = UIntToStr(s, year, 4); *s++ = '-'; 277 UIntToStr_2(s, mon + 1); s[2] = '-'; s += 3; 278 UIntToStr_2(s, (unsigned)v + 1); s[2] = ' '; s += 3; 279 UIntToStr_2(s, hour); s[2] = ':'; s += 3; 280 UIntToStr_2(s, min); s[2] = ':'; s += 3; 281 UIntToStr_2(s, sec); s[2] = 0; 282 } 283 284 void PrintError(const char *sz) 285 { 286 printf("\nERROR: %s\n", sz); 287 } 288 289 #ifdef USE_WINDOWS_FILE 290 static void GetAttribString(UInt32 wa, Bool isDir, char *s) 291 { 292 s[0] = (char)(((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : '.'); 293 s[1] = (char)(((wa & FILE_ATTRIBUTE_READONLY ) != 0) ? 'R': '.'); 294 s[2] = (char)(((wa & FILE_ATTRIBUTE_HIDDEN ) != 0) ? 'H': '.'); 295 s[3] = (char)(((wa & FILE_ATTRIBUTE_SYSTEM ) != 0) ? 'S': '.'); 296 s[4] = (char)(((wa & FILE_ATTRIBUTE_ARCHIVE ) != 0) ? 'A': '.'); 297 s[5] = '\0'; 298 } 299 #else 300 static void GetAttribString(UInt32, Bool, char *s) 301 { 302 s[0] = '\0'; 303 } 304 #endif 305 306 // #define NUM_PARENTS_MAX 128 307 308 int MY_CDECL main(int numargs, char *args[]) 309 { 310 CFileInStream archiveStream; 311 CLookToRead lookStream; 312 CSzArEx db; 313 SRes res; 314 ISzAlloc allocImp; 315 ISzAlloc allocTempImp; 316 UInt16 *temp = NULL; 317 size_t tempSize = 0; 318 // UInt32 parents[NUM_PARENTS_MAX]; 319 320 printf("\n7z ANSI-C Decoder " MY_VERSION_COPYRIGHT_DATE "\n\n"); 321 if (numargs == 1) 322 { 323 printf( 324 "Usage: 7zDec <command> <archive_name>\n\n" 325 "<Commands>\n" 326 " e: Extract files from archive (without using directory names)\n" 327 " l: List contents of archive\n" 328 " t: Test integrity of archive\n" 329 " x: eXtract files with full paths\n"); 330 return 0; 331 } 332 if (numargs < 3) 333 { 334 PrintError("incorrect command"); 335 return 1; 336 } 337 338 #if defined(_WIN32) && !defined(USE_WINDOWS_FILE) && !defined(UNDER_CE) 339 g_FileCodePage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; 340 #endif 341 342 allocImp.Alloc = SzAlloc; 343 allocImp.Free = SzFree; 344 345 allocTempImp.Alloc = SzAllocTemp; 346 allocTempImp.Free = SzFreeTemp; 347 348 #ifdef UNDER_CE 349 if (InFile_OpenW(&archiveStream.file, L"\test.7z")) 350 #else 351 if (InFile_Open(&archiveStream.file, args[2])) 352 #endif 353 { 354 PrintError("can not open input file"); 355 return 1; 356 } 357 358 FileInStream_CreateVTable(&archiveStream); 359 LookToRead_CreateVTable(&lookStream, False); 360 361 lookStream.realStream = &archiveStream.s; 362 LookToRead_Init(&lookStream); 363 364 CrcGenerateTable(); 365 366 SzArEx_Init(&db); 367 res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); 368 if (res == SZ_OK) 369 { 370 char *command = args[1]; 371 int listCommand = 0, testCommand = 0, fullPaths = 0; 372 if (strcmp(command, "l") == 0) listCommand = 1; 373 else if (strcmp(command, "t") == 0) testCommand = 1; 374 else if (strcmp(command, "e") == 0) { } 375 else if (strcmp(command, "x") == 0) { fullPaths = 1; } 376 else 377 { 378 PrintError("incorrect command"); 379 res = SZ_ERROR_FAIL; 380 } 381 382 if (res == SZ_OK) 383 { 384 UInt32 i; 385 386 /* 387 if you need cache, use these 3 variables. 388 if you use external function, you can make these variable as static. 389 */ 390 UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ 391 Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */ 392 size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ 393 394 for (i = 0; i < db.NumFiles; i++) 395 { 396 size_t offset = 0; 397 size_t outSizeProcessed = 0; 398 // const CSzFileItem *f = db.Files + i; 399 size_t len; 400 int isDir = SzArEx_IsDir(&db, i); 401 if (listCommand == 0 && isDir && !fullPaths) 402 continue; 403 len = SzArEx_GetFileNameUtf16(&db, i, NULL); 404 // len = SzArEx_GetFullNameLen(&db, i); 405 406 if (len > tempSize) 407 { 408 SzFree(NULL, temp); 409 tempSize = len; 410 temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(temp[0])); 411 if (!temp) 412 { 413 res = SZ_ERROR_MEM; 414 break; 415 } 416 } 417 418 SzArEx_GetFileNameUtf16(&db, i, temp); 419 /* 420 if (SzArEx_GetFullNameUtf16_Back(&db, i, temp + len) != temp) 421 { 422 res = SZ_ERROR_FAIL; 423 break; 424 } 425 */ 426 427 if (listCommand) 428 { 429 char attr[8], s[32], t[32]; 430 UInt64 fileSize; 431 432 GetAttribString(SzBitWithVals_Check(&db.Attribs, i) ? db.Attribs.Vals[i] : 0, isDir, attr); 433 434 fileSize = SzArEx_GetFileSize(&db, i); 435 UInt64ToStr(fileSize, s); 436 if (SzBitWithVals_Check(&db.MTime, i)) 437 ConvertFileTimeToString(&db.MTime.Vals[i], t); 438 else 439 { 440 size_t j; 441 for (j = 0; j < 19; j++) 442 t[j] = ' '; 443 t[j] = '\0'; 444 } 445 446 printf("%s %s %10s ", t, attr, s); 447 res = PrintString(temp); 448 if (res != SZ_OK) 449 break; 450 if (isDir) 451 printf("/"); 452 printf("\n"); 453 continue; 454 } 455 fputs(testCommand ? 456 "Testing ": 457 "Extracting ", 458 stdout); 459 res = PrintString(temp); 460 if (res != SZ_OK) 461 break; 462 if (isDir) 463 printf("/"); 464 else 465 { 466 res = SzArEx_Extract(&db, &lookStream.s, i, 467 &blockIndex, &outBuffer, &outBufferSize, 468 &offset, &outSizeProcessed, 469 &allocImp, &allocTempImp); 470 if (res != SZ_OK) 471 break; 472 } 473 if (!testCommand) 474 { 475 CSzFile outFile; 476 size_t processedSize; 477 size_t j; 478 UInt16 *name = (UInt16 *)temp; 479 const UInt16 *destPath = (const UInt16 *)name; 480 for (j = 0; name[j] != 0; j++) 481 if (name[j] == '/') 482 { 483 if (fullPaths) 484 { 485 name[j] = 0; 486 MyCreateDir(name); 487 name[j] = CHAR_PATH_SEPARATOR; 488 } 489 else 490 destPath = name + j + 1; 491 } 492 493 if (isDir) 494 { 495 MyCreateDir(destPath); 496 printf("\n"); 497 continue; 498 } 499 else if (OutFile_OpenUtf16(&outFile, destPath)) 500 { 501 PrintError("can not open output file"); 502 res = SZ_ERROR_FAIL; 503 break; 504 } 505 processedSize = outSizeProcessed; 506 if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed) 507 { 508 PrintError("can not write output file"); 509 res = SZ_ERROR_FAIL; 510 break; 511 } 512 if (File_Close(&outFile)) 513 { 514 PrintError("can not close output file"); 515 res = SZ_ERROR_FAIL; 516 break; 517 } 518 #ifdef USE_WINDOWS_FILE 519 if (SzBitWithVals_Check(&db.Attribs, i)) 520 SetFileAttributesW(destPath, db.Attribs.Vals[i]); 521 #endif 522 } 523 printf("\n"); 524 } 525 IAlloc_Free(&allocImp, outBuffer); 526 } 527 } 528 SzArEx_Free(&db, &allocImp); 529 SzFree(NULL, temp); 530 531 File_Close(&archiveStream.file); 532 if (res == SZ_OK) 533 { 534 printf("\nEverything is Ok\n"); 535 return 0; 536 } 537 if (res == SZ_ERROR_UNSUPPORTED) 538 PrintError("decoder doesn't support this archive"); 539 else if (res == SZ_ERROR_MEM) 540 PrintError("can not allocate memory"); 541 else if (res == SZ_ERROR_CRC) 542 PrintError("CRC error"); 543 else 544 printf("\nERROR #%d\n", res); 545 return 1; 546 } 547