1 // LzmaAlone.cpp 2 3 #include "StdAfx.h" 4 5 #include <stdio.h> 6 7 #include "../../../../C/CpuArch.h" 8 9 #if (defined(_WIN32) || defined(OS2) || defined(MSDOS)) && !defined(UNDER_CE) 10 #include <fcntl.h> 11 #include <io.h> 12 #define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY) 13 #else 14 #define MY_SET_BINARY_MODE(file) 15 #endif 16 17 #include "../../../Common/MyWindows.h" 18 #include "../../../Common/MyInitGuid.h" 19 20 #include "../../../../C/7zVersion.h" 21 #include "../../../../C/Alloc.h" 22 #include "../../../../C/Lzma86.h" 23 24 #include "../../../Windows/NtCheck.h" 25 26 #ifndef _7ZIP_ST 27 #include "../../../Windows/System.h" 28 #endif 29 30 #include "../../../Common/IntToString.h" 31 #include "../../../Common/CommandLineParser.h" 32 #include "../../../Common/StringConvert.h" 33 #include "../../../Common/StringToInt.h" 34 35 #include "../../Common/FileStreams.h" 36 #include "../../Common/StreamUtils.h" 37 38 #include "../../Compress/LzmaDecoder.h" 39 #include "../../Compress/LzmaEncoder.h" 40 41 #include "../../UI/Console/BenchCon.h" 42 #include "../../UI/Console/ConsoleClose.h" 43 44 bool g_LargePagesMode = false; 45 46 using namespace NCommandLineParser; 47 48 static const unsigned kDictSizeLog = 24; 49 50 #define kCopyrightString "\nLZMA " MY_VERSION_CPU " : " MY_COPYRIGHT_DATE "\n\n" 51 52 static const char * const kHelpString = 53 "Usage: lzma <command> [inputFile] [outputFile] [<switches>...]\n" 54 "\n" 55 "<command>\n" 56 " e : Encode file\n" 57 " d : Decode file\n" 58 " b : Benchmark\n" 59 "<switches>\n" 60 " -a{N} : set compression mode : [0, 1] : default = 1 (max)\n" 61 " -d{N} : set dictionary size : [12, 30] : default = 24 (16 MiB)\n" 62 " -fb{N} : set number of fast bytes : [5, 273] : default = 128\n" 63 " -mc{N} : set number of cycles for match finder\n" 64 " -lc{N} : set number of literal context bits : [0, 8] : default = 3\n" 65 " -lp{N} : set number of literal pos bits : [0, 4] : default = 0\n" 66 " -pb{N} : set number of pos bits : [0, 4] : default = 2\n" 67 " -mf{M} : set match finder: [hc4, bt2, bt3, bt4] : default = bt4\n" 68 " -mt{N} : set number of CPU threads\n" 69 " -eos : write end of stream marker\n" 70 " -si : read data from stdin\n" 71 " -so : write data to stdout\n"; 72 73 74 static const char * const kCantAllocate = "Can not allocate memory"; 75 static const char * const kReadError = "Read error"; 76 static const char * const kWriteError = "Write error"; 77 78 79 namespace NKey { 80 enum Enum 81 { 82 kHelp1 = 0, 83 kHelp2, 84 kMethod, 85 kLevel, 86 kAlgo, 87 kDict, 88 kFb, 89 kMc, 90 kLc, 91 kLp, 92 kPb, 93 kMatchFinder, 94 kMultiThread, 95 kEOS, 96 kStdIn, 97 kStdOut, 98 kFilter86 99 }; 100 } 101 102 static const CSwitchForm kSwitchForms[] = 103 { 104 { "?", NSwitchType::kSimple, false }, 105 { "H", NSwitchType::kSimple, false }, 106 { "MM", NSwitchType::kString, false, 1 }, 107 { "X", NSwitchType::kString, false, 1 }, 108 { "A", NSwitchType::kString, false, 1 }, 109 { "D", NSwitchType::kString, false, 1 }, 110 { "FB", NSwitchType::kString, false, 1 }, 111 { "MC", NSwitchType::kString, false, 1 }, 112 { "LC", NSwitchType::kString, false, 1 }, 113 { "LP", NSwitchType::kString, false, 1 }, 114 { "PB", NSwitchType::kString, false, 1 }, 115 { "MF", NSwitchType::kString, false, 1 }, 116 { "MT", NSwitchType::kString, false, 0 }, 117 { "EOS", NSwitchType::kSimple, false }, 118 { "SI", NSwitchType::kSimple, false }, 119 { "SO", NSwitchType::kSimple, false }, 120 { "F86", NSwitchType::kChar, false, 0, "+" } 121 }; 122 123 124 static void Convert_UString_to_AString(const UString &s, AString &temp) 125 { 126 int codePage = CP_OEMCP; 127 /* 128 int g_CodePage = -1; 129 int codePage = g_CodePage; 130 if (codePage == -1) 131 codePage = CP_OEMCP; 132 if (codePage == CP_UTF8) 133 ConvertUnicodeToUTF8(s, temp); 134 else 135 */ 136 UnicodeStringToMultiByte2(temp, s, (UINT)codePage); 137 } 138 139 static void PrintErr(const char *s) 140 { 141 fputs(s, stderr); 142 } 143 144 static void PrintErr_LF(const char *s) 145 { 146 PrintErr(s); 147 fputc('\n', stderr); 148 } 149 150 151 static void PrintError(const char *s) 152 { 153 PrintErr("\nERROR: "); 154 PrintErr_LF(s); 155 } 156 157 static void PrintError2(const char *s1, const UString &s2) 158 { 159 PrintError(s1); 160 AString a; 161 Convert_UString_to_AString(s2, a); 162 PrintErr_LF(a); 163 } 164 165 static void PrintError_int(const char *s, int code) 166 { 167 PrintError(s); 168 char temp[32]; 169 ConvertInt64ToString(code, temp); 170 PrintErr("Error code = "); 171 PrintErr_LF(temp); 172 } 173 174 175 176 static void Print(const char *s) 177 { 178 fputs(s, stdout); 179 } 180 181 static void Print_UInt64(UInt64 v) 182 { 183 char temp[32]; 184 ConvertUInt64ToString(v, temp); 185 Print(temp); 186 } 187 188 static void Print_MB(UInt64 v) 189 { 190 Print_UInt64(v); 191 Print(" MiB"); 192 } 193 194 static void Print_Size(const char *s, UInt64 v) 195 { 196 Print(s); 197 Print_UInt64(v); 198 Print(" ("); 199 Print_MB(v >> 20); 200 Print(")\n"); 201 } 202 203 static void PrintTitle() 204 { 205 Print(kCopyrightString); 206 } 207 208 static void PrintHelp() 209 { 210 PrintTitle(); 211 Print(kHelpString); 212 } 213 214 class CProgressPrint: 215 public ICompressProgressInfo, 216 public CMyUnknownImp 217 { 218 UInt64 _size1; 219 UInt64 _size2; 220 public: 221 CProgressPrint(): _size1(0), _size2(0) {} 222 223 void ClosePrint(); 224 225 MY_UNKNOWN_IMP1(ICompressProgressInfo) 226 227 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); 228 }; 229 230 #define BACK_STR \ 231 "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" 232 static const char * const kBackSpaces = 233 BACK_STR 234 " " 235 BACK_STR; 236 237 238 void CProgressPrint::ClosePrint() 239 { 240 Print(kBackSpaces); 241 } 242 243 STDMETHODIMP CProgressPrint::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) 244 { 245 if (NConsoleClose::TestBreakSignal()) 246 return E_ABORT; 247 if (inSize) 248 { 249 UInt64 v1 = *inSize >> 20; 250 UInt64 v2 = _size2; 251 if (outSize) 252 v2 = *outSize >> 20; 253 if (v1 != _size1 || v2 != _size2) 254 { 255 _size1 = v1; 256 _size2 = v2; 257 ClosePrint(); 258 Print_MB(_size1); 259 Print(" -> "); 260 Print_MB(_size2); 261 } 262 } 263 return S_OK; 264 } 265 266 267 static void IncorrectCommand() 268 { 269 throw "Incorrect command"; 270 } 271 272 static UInt32 GetNumber(const wchar_t *s) 273 { 274 const wchar_t *end; 275 UInt32 v = ConvertStringToUInt32(s, &end); 276 if (*end != 0) 277 IncorrectCommand(); 278 return v; 279 } 280 281 static void ParseUInt32(const CParser &parser, unsigned index, UInt32 &res) 282 { 283 if (parser[index].ThereIs) 284 res = GetNumber(parser[index].PostStrings[0]); 285 } 286 287 288 static int Error_HRESULT(const char *s, HRESULT res) 289 { 290 if (res == E_ABORT) 291 { 292 Print("\n\nBreak signaled\n"); 293 return 255; 294 } 295 296 PrintError(s); 297 298 if (res == E_OUTOFMEMORY) 299 { 300 PrintErr_LF(kCantAllocate); 301 return 8; 302 } 303 if (res == E_INVALIDARG) 304 { 305 PrintErr_LF("Ununsupported parameter"); 306 } 307 else 308 { 309 char temp[32]; 310 ConvertUInt32ToHex(res, temp); 311 PrintErr("Error code = 0x"); 312 PrintErr_LF(temp); 313 } 314 return 1; 315 } 316 317 #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1; 318 319 static void AddProp(CObjectVector<CProperty> &props2, const char *name, const wchar_t *val) 320 { 321 CProperty &prop = props2.AddNew(); 322 prop.Name = name; 323 prop.Value = val; 324 } 325 326 static int main2(int numArgs, const char *args[]) 327 { 328 NT_CHECK 329 330 if (numArgs == 1) 331 { 332 PrintHelp(); 333 return 0; 334 } 335 336 /* 337 bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 8); 338 if (unsupportedTypes) 339 throw "Unsupported base types. Edit Common/Types.h and recompile"; 340 */ 341 342 UStringVector commandStrings; 343 for (int i = 1; i < numArgs; i++) 344 commandStrings.Add(MultiByteToUnicodeString(args[i])); 345 346 CParser parser; 347 try 348 { 349 if (!parser.ParseStrings(kSwitchForms, ARRAY_SIZE(kSwitchForms), commandStrings)) 350 { 351 PrintError2(parser.ErrorMessage, parser.ErrorLine); 352 return 1; 353 } 354 } 355 catch(...) 356 { 357 IncorrectCommand(); 358 } 359 360 if (parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs) 361 { 362 PrintHelp(); 363 return 0; 364 } 365 366 bool stdInMode = parser[NKey::kStdIn].ThereIs; 367 bool stdOutMode = parser[NKey::kStdOut].ThereIs; 368 369 if (!stdOutMode) 370 PrintTitle(); 371 372 const UStringVector ¶ms = parser.NonSwitchStrings; 373 374 unsigned paramIndex = 0; 375 if (paramIndex >= params.Size()) 376 IncorrectCommand(); 377 const UString &command = params[paramIndex++]; 378 379 CObjectVector<CProperty> props2; 380 bool dictDefined = false; 381 UInt32 dict = (UInt32)(Int32)-1; 382 383 if (parser[NKey::kDict].ThereIs) 384 { 385 UInt32 dictLog; 386 const UString &s = parser[NKey::kDict].PostStrings[0]; 387 dictLog = GetNumber(s); 388 dict = 1 << dictLog; 389 dictDefined = true; 390 AddProp(props2, "d", s); 391 } 392 393 if (parser[NKey::kLevel].ThereIs) 394 { 395 const UString &s = parser[NKey::kLevel].PostStrings[0]; 396 /* UInt32 level = */ GetNumber(s); 397 AddProp(props2, "x", s); 398 } 399 400 UString mf ("BT4"); 401 if (parser[NKey::kMatchFinder].ThereIs) 402 mf = parser[NKey::kMatchFinder].PostStrings[0]; 403 404 UInt32 numThreads = (UInt32)(Int32)-1; 405 406 #ifndef _7ZIP_ST 407 408 if (parser[NKey::kMultiThread].ThereIs) 409 { 410 const UString &s = parser[NKey::kMultiThread].PostStrings[0]; 411 if (s.IsEmpty()) 412 numThreads = NWindows::NSystem::GetNumberOfProcessors(); 413 else 414 numThreads = GetNumber(s); 415 AddProp(props2, "mt", s); 416 } 417 418 #endif 419 420 421 if (parser[NKey::kMethod].ThereIs) 422 { 423 const UString &s = parser[NKey::kMethod].PostStrings[0]; 424 if (s.IsEmpty() || s[0] != '=') 425 IncorrectCommand(); 426 AddProp(props2, "m", s.Ptr(1)); 427 } 428 429 if (StringsAreEqualNoCase_Ascii(command, "b")) 430 { 431 UInt32 numIterations = 1; 432 if (paramIndex < params.Size()) 433 numIterations = GetNumber(params[paramIndex++]); 434 if (params.Size() != paramIndex) 435 IncorrectCommand(); 436 437 HRESULT res = BenchCon(props2, numIterations, stdout); 438 439 if (res == S_OK) 440 return 0; 441 return Error_HRESULT("Benchmark error", res); 442 } 443 444 { 445 UInt32 needParams = 3; 446 if (stdInMode) needParams--; 447 if (stdOutMode) needParams--; 448 if (needParams != params.Size()) 449 IncorrectCommand(); 450 } 451 452 if (numThreads == (UInt32)(Int32)-1) 453 numThreads = 1; 454 455 bool encodeMode = false; 456 457 if (StringsAreEqualNoCase_Ascii(command, "e")) 458 encodeMode = true; 459 else if (!StringsAreEqualNoCase_Ascii(command, "d")) 460 IncorrectCommand(); 461 462 CMyComPtr<ISequentialInStream> inStream; 463 CInFileStream *inStreamSpec = NULL; 464 465 if (stdInMode) 466 { 467 inStream = new CStdInFileStream; 468 MY_SET_BINARY_MODE(stdin); 469 } 470 else 471 { 472 const UString &inputName = params[paramIndex++]; 473 inStreamSpec = new CInFileStream; 474 inStream = inStreamSpec; 475 if (!inStreamSpec->Open(us2fs(inputName))) 476 { 477 PrintError2("can not open input file", inputName); 478 return 1; 479 } 480 } 481 482 CMyComPtr<ISequentialOutStream> outStream; 483 COutFileStream *outStreamSpec = NULL; 484 485 if (stdOutMode) 486 { 487 outStream = new CStdOutFileStream; 488 MY_SET_BINARY_MODE(stdout); 489 } 490 else 491 { 492 const UString &outputName = params[paramIndex++]; 493 outStreamSpec = new COutFileStream; 494 outStream = outStreamSpec; 495 if (!outStreamSpec->Create(us2fs(outputName), true)) 496 { 497 PrintError2("can not open output file", outputName); 498 return 1; 499 } 500 } 501 502 bool fileSizeDefined = false; 503 UInt64 fileSize = 0; 504 505 if (inStreamSpec) 506 { 507 if (!inStreamSpec->File.GetLength(fileSize)) 508 throw "Can not get file length"; 509 fileSizeDefined = true; 510 if (!stdOutMode) 511 Print_Size("Input size: ", fileSize); 512 } 513 514 if (encodeMode && !dictDefined) 515 { 516 dict = 1 << kDictSizeLog; 517 if (fileSizeDefined) 518 { 519 unsigned i; 520 for (i = 16; i < kDictSizeLog; i++) 521 if ((UInt32)((UInt32)1 << i) >= fileSize) 522 break; 523 dict = (UInt32)1 << i; 524 } 525 } 526 527 if (parser[NKey::kFilter86].ThereIs) 528 { 529 /* -f86 switch is for x86 filtered mode: BCJ + LZMA. 530 It uses modified header format. 531 It's not recommended to use -f86 mode now. 532 You can use xz format instead, if you want to use filters */ 533 534 if (parser[NKey::kEOS].ThereIs || stdInMode) 535 throw "Can not use stdin in this mode"; 536 537 size_t inSize = (size_t)fileSize; 538 539 if (inSize != fileSize) 540 throw "File is too big"; 541 542 Byte *inBuffer = NULL; 543 544 if (inSize != 0) 545 { 546 inBuffer = (Byte *)MyAlloc((size_t)inSize); 547 if (!inBuffer) 548 throw kCantAllocate; 549 } 550 551 if (ReadStream_FAIL(inStream, inBuffer, inSize) != S_OK) 552 throw "Can not read"; 553 554 Byte *outBuffer = NULL; 555 size_t outSize; 556 557 if (encodeMode) 558 { 559 // we allocate 105% of original size for output buffer 560 UInt64 outSize64 = fileSize / 20 * 21 + (1 << 16); 561 562 outSize = (size_t)outSize64; 563 564 if (outSize != outSize64) 565 throw "File is too big"; 566 567 if (outSize != 0) 568 { 569 outBuffer = (Byte *)MyAlloc((size_t)outSize); 570 if (!outBuffer) 571 throw kCantAllocate; 572 } 573 574 int res = Lzma86_Encode(outBuffer, &outSize, inBuffer, inSize, 575 5, dict, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO); 576 577 if (res != 0) 578 { 579 PrintError_int("Encode error", (int)res); 580 return 1; 581 } 582 } 583 else 584 { 585 UInt64 outSize64; 586 587 if (Lzma86_GetUnpackSize(inBuffer, inSize, &outSize64) != 0) 588 throw "data error"; 589 590 outSize = (size_t)outSize64; 591 if (outSize != outSize64) 592 throw "Unpack size is too big"; 593 if (outSize != 0) 594 { 595 outBuffer = (Byte *)MyAlloc(outSize); 596 if (!outBuffer) 597 throw kCantAllocate; 598 } 599 600 int res = Lzma86_Decode(outBuffer, &outSize, inBuffer, &inSize); 601 602 if (inSize != (size_t)fileSize) 603 throw "incorrect processed size"; 604 if (res != 0) 605 { 606 PrintError_int("Decode error", (int)res); 607 return 1; 608 } 609 } 610 611 if (WriteStream(outStream, outBuffer, outSize) != S_OK) 612 throw kWriteError; 613 614 MyFree(outBuffer); 615 MyFree(inBuffer); 616 } 617 else 618 { 619 620 CProgressPrint *progressSpec = NULL; 621 CMyComPtr<ICompressProgressInfo> progress; 622 623 if (!stdOutMode) 624 { 625 progressSpec = new CProgressPrint; 626 progress = progressSpec; 627 } 628 629 if (encodeMode) 630 { 631 NCompress::NLzma::CEncoder *encoderSpec = new NCompress::NLzma::CEncoder; 632 CMyComPtr<ICompressCoder> encoder = encoderSpec; 633 634 UInt32 pb = 2; 635 UInt32 lc = 3; // = 0; for 32-bit data 636 UInt32 lp = 0; // = 2; for 32-bit data 637 UInt32 algo = 1; 638 UInt32 fb = 128; 639 UInt32 mc = 16 + fb / 2; 640 bool mcDefined = false; 641 642 bool eos = parser[NKey::kEOS].ThereIs || stdInMode; 643 644 ParseUInt32(parser, NKey::kAlgo, algo); 645 ParseUInt32(parser, NKey::kFb, fb); 646 ParseUInt32(parser, NKey::kLc, lc); 647 ParseUInt32(parser, NKey::kLp, lp); 648 ParseUInt32(parser, NKey::kPb, pb); 649 650 mcDefined = parser[NKey::kMc].ThereIs; 651 if (mcDefined) 652 mc = GetNumber(parser[NKey::kMc].PostStrings[0]); 653 654 const PROPID propIDs[] = 655 { 656 NCoderPropID::kDictionarySize, 657 NCoderPropID::kPosStateBits, 658 NCoderPropID::kLitContextBits, 659 NCoderPropID::kLitPosBits, 660 NCoderPropID::kAlgorithm, 661 NCoderPropID::kNumFastBytes, 662 NCoderPropID::kMatchFinder, 663 NCoderPropID::kEndMarker, 664 NCoderPropID::kNumThreads, 665 NCoderPropID::kMatchFinderCycles, 666 }; 667 668 const unsigned kNumPropsMax = ARRAY_SIZE(propIDs); 669 670 PROPVARIANT props[kNumPropsMax]; 671 for (int p = 0; p < 6; p++) 672 props[p].vt = VT_UI4; 673 674 props[0].ulVal = (UInt32)dict; 675 props[1].ulVal = (UInt32)pb; 676 props[2].ulVal = (UInt32)lc; 677 props[3].ulVal = (UInt32)lp; 678 props[4].ulVal = (UInt32)algo; 679 props[5].ulVal = (UInt32)fb; 680 681 props[6].vt = VT_BSTR; 682 props[6].bstrVal = const_cast<BSTR>((const wchar_t *)mf); 683 684 props[7].vt = VT_BOOL; 685 props[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE; 686 687 props[8].vt = VT_UI4; 688 props[8].ulVal = (UInt32)numThreads; 689 690 // it must be last in property list 691 props[9].vt = VT_UI4; 692 props[9].ulVal = (UInt32)mc; 693 694 unsigned numProps = kNumPropsMax; 695 if (!mcDefined) 696 numProps--; 697 698 HRESULT res = encoderSpec->SetCoderProperties(propIDs, props, numProps); 699 if (res != S_OK) 700 return Error_HRESULT("incorrect encoder properties", res); 701 702 if (encoderSpec->WriteCoderProperties(outStream) != S_OK) 703 throw kWriteError; 704 705 bool fileSizeWasUsed = true; 706 if (eos || stdInMode) 707 { 708 fileSize = (UInt64)(Int64)-1; 709 fileSizeWasUsed = false; 710 } 711 712 { 713 Byte temp[8]; 714 for (int i = 0; i < 8; i++) 715 temp[i]= (Byte)(fileSize >> (8 * i)); 716 if (WriteStream(outStream, temp, 8) != S_OK) 717 throw kWriteError; 718 } 719 720 res = encoder->Code(inStream, outStream, NULL, NULL, progress); 721 if (progressSpec) 722 progressSpec->ClosePrint(); 723 724 if (res != S_OK) 725 return Error_HRESULT("Encoding error", res); 726 727 UInt64 processedSize = encoderSpec->GetInputProcessedSize(); 728 729 if (fileSizeWasUsed && processedSize != fileSize) 730 throw "Incorrect size of processed data"; 731 } 732 else 733 { 734 NCompress::NLzma::CDecoder *decoderSpec = new NCompress::NLzma::CDecoder; 735 CMyComPtr<ICompressCoder> decoder = decoderSpec; 736 737 decoderSpec->FinishStream = true; 738 739 const unsigned kPropertiesSize = 5; 740 Byte header[kPropertiesSize + 8]; 741 742 if (ReadStream_FALSE(inStream, header, kPropertiesSize + 8) != S_OK) 743 throw kReadError; 744 745 if (decoderSpec->SetDecoderProperties2(header, kPropertiesSize) != S_OK) 746 throw "SetDecoderProperties error"; 747 748 UInt64 unpackSize = 0; 749 for (int i = 0; i < 8; i++) 750 unpackSize |= ((UInt64)header[kPropertiesSize + i]) << (8 * i); 751 752 bool unpackSizeDefined = (unpackSize != (UInt64)(Int64)-1); 753 754 HRESULT res = decoder->Code(inStream, outStream, NULL, unpackSizeDefined ? &unpackSize : NULL, progress); 755 if (progressSpec) 756 progressSpec->ClosePrint(); 757 758 if (res != S_OK) 759 { 760 if (res == S_FALSE) 761 { 762 PrintError("Decoding error"); 763 return 1; 764 } 765 return Error_HRESULT("Decoding error", res); 766 } 767 768 if (unpackSizeDefined && unpackSize != decoderSpec->GetOutputProcessedSize()) 769 throw "incorrect uncompressed size in header"; 770 } 771 } 772 773 if (outStreamSpec) 774 { 775 if (!stdOutMode) 776 Print_Size("Output size: ", outStreamSpec->ProcessedSize); 777 if (outStreamSpec->Close() != S_OK) 778 throw "File closing error"; 779 } 780 781 return 0; 782 } 783 784 int MY_CDECL main(int numArgs, const char *args[]) 785 { 786 NConsoleClose::CCtrlHandlerSetter ctrlHandlerSetter; 787 788 try { return main2(numArgs, args); } 789 catch (const char *s) 790 { 791 PrintError(s); 792 return 1; 793 } 794 catch(...) 795 { 796 PrintError("Unknown Error"); 797 return 1; 798 } 799 } 800