Home | History | Annotate | Download | only in LzmaCon
      1 // LzmaAlone.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include <stdio.h>
      6 
      7 #if (defined(_WIN32) || defined(OS2) || defined(MSDOS)) && !defined(UNDER_CE)
      8 #include <fcntl.h>
      9 #include <io.h>
     10 #define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY)
     11 #else
     12 #define MY_SET_BINARY_MODE(file)
     13 #endif
     14 
     15 // #include "../../../Common/MyWindows.h"
     16 #include "../../../Common/MyInitGuid.h"
     17 
     18 #include "../../../../C/7zVersion.h"
     19 #include "../../../../C/Alloc.h"
     20 #include "../../../../C/Lzma86.h"
     21 
     22 #include "../../../Windows/NtCheck.h"
     23 
     24 #ifndef _7ZIP_ST
     25 #include "../../../Windows/System.h"
     26 #endif
     27 
     28 #include "../../../Common/CommandLineParser.h"
     29 #include "../../../Common/StringConvert.h"
     30 #include "../../../Common/StringToInt.h"
     31 
     32 #include "../../Common/FileStreams.h"
     33 #include "../../Common/StreamUtils.h"
     34 
     35 #include "../../Compress/LzmaDecoder.h"
     36 #include "../../Compress/LzmaEncoder.h"
     37 
     38 #include "../../UI/Console/BenchCon.h"
     39 
     40 
     41 using namespace NCommandLineParser;
     42 
     43 static const char *kCantAllocate = "Can not allocate memory";
     44 static const char *kReadError = "Read error";
     45 static const char *kWriteError = "Write error";
     46 
     47 namespace NKey {
     48 enum Enum
     49 {
     50   kHelp1 = 0,
     51   kHelp2,
     52   kAlgo,
     53   kDict,
     54   kFb,
     55   kMc,
     56   kLc,
     57   kLp,
     58   kPb,
     59   kMatchFinder,
     60   kMultiThread,
     61   kEOS,
     62   kStdIn,
     63   kStdOut,
     64   kFilter86
     65 };
     66 }
     67 
     68 static const CSwitchForm kSwitchForms[] =
     69 {
     70   { L"?",  NSwitchType::kSimple, false },
     71   { L"H",  NSwitchType::kSimple, false },
     72   { L"A", NSwitchType::kUnLimitedPostString, false, 1 },
     73   { L"D", NSwitchType::kUnLimitedPostString, false, 1 },
     74   { L"FB", NSwitchType::kUnLimitedPostString, false, 1 },
     75   { L"MC", NSwitchType::kUnLimitedPostString, false, 1 },
     76   { L"LC", NSwitchType::kUnLimitedPostString, false, 1 },
     77   { L"LP", NSwitchType::kUnLimitedPostString, false, 1 },
     78   { L"PB", NSwitchType::kUnLimitedPostString, false, 1 },
     79   { L"MF", NSwitchType::kUnLimitedPostString, false, 1 },
     80   { L"MT", NSwitchType::kUnLimitedPostString, false, 0 },
     81   { L"EOS", NSwitchType::kSimple, false },
     82   { L"SI",  NSwitchType::kSimple, false },
     83   { L"SO",  NSwitchType::kSimple, false },
     84   { L"F86",  NSwitchType::kPostChar, false, 0, 0, L"+" }
     85 };
     86 
     87 static const int kNumSwitches = sizeof(kSwitchForms) / sizeof(kSwitchForms[0]);
     88 
     89 static void PrintMessage(const char *s)
     90 {
     91   fputs(s, stderr);
     92 }
     93 
     94 static void PrintHelp()
     95 {
     96   PrintMessage("\nUsage:  LZMA <e|d> inputFile outputFile [<switches>...]\n"
     97              "  e: encode file\n"
     98              "  d: decode file\n"
     99              "  b: Benchmark\n"
    100     "<Switches>\n"
    101     "  -a{N}:  set compression mode - [0, 1], default: 1 (max)\n"
    102     "  -d{N}:  set dictionary size - [12, 30], default: 23 (8MB)\n"
    103     "  -fb{N}: set number of fast bytes - [5, 273], default: 128\n"
    104     "  -mc{N}: set number of cycles for match finder\n"
    105     "  -lc{N}: set number of literal context bits - [0, 8], default: 3\n"
    106     "  -lp{N}: set number of literal pos bits - [0, 4], default: 0\n"
    107     "  -pb{N}: set number of pos bits - [0, 4], default: 2\n"
    108     "  -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4\n"
    109     "  -mt{N}: set number of CPU threads\n"
    110     "  -eos:   write End Of Stream marker\n"
    111     "  -si:    read data from stdin\n"
    112     "  -so:    write data to stdout\n"
    113     );
    114 }
    115 
    116 static void PrintHelpAndExit(const char *s)
    117 {
    118   fprintf(stderr, "\nError: %s\n\n", s);
    119   PrintHelp();
    120   throw -1;
    121 }
    122 
    123 static void IncorrectCommand()
    124 {
    125   PrintHelpAndExit("Incorrect command");
    126 }
    127 
    128 static void WriteArgumentsToStringList(int numArgs, const char *args[], UStringVector &strings)
    129 {
    130   for (int i = 1; i < numArgs; i++)
    131     strings.Add(MultiByteToUnicodeString(args[i]));
    132 }
    133 
    134 static bool GetNumber(const wchar_t *s, UInt32 &value)
    135 {
    136   value = 0;
    137   if (MyStringLen(s) == 0)
    138     return false;
    139   const wchar_t *end;
    140   UInt64 res = ConvertStringToUInt64(s, &end);
    141   if (*end != L'\0')
    142     return false;
    143   if (res > 0xFFFFFFFF)
    144     return false;
    145   value = UInt32(res);
    146   return true;
    147 }
    148 
    149 static void ParseUInt32(const CParser &parser, int index, UInt32 &res)
    150 {
    151   if (parser[index].ThereIs)
    152     if (!GetNumber(parser[index].PostStrings[0], res))
    153       IncorrectCommand();
    154 }
    155 
    156 #define NT_CHECK_FAIL_ACTION PrintMessage("Unsupported Windows version"); return 1;
    157 
    158 int main2(int numArgs, const char *args[])
    159 {
    160   NT_CHECK
    161 
    162   PrintMessage("\nLZMA " MY_VERSION_COPYRIGHT_DATE "\n");
    163 
    164   if (numArgs == 1)
    165   {
    166     PrintHelp();
    167     return 0;
    168   }
    169 
    170   bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 4);
    171   if (unsupportedTypes)
    172   {
    173     PrintMessage("Unsupported base types. Edit Common/Types.h and recompile");
    174     return 1;
    175   }
    176 
    177   UStringVector commandStrings;
    178   WriteArgumentsToStringList(numArgs, args, commandStrings);
    179   CParser parser(kNumSwitches);
    180   try
    181   {
    182     parser.ParseStrings(kSwitchForms, commandStrings);
    183   }
    184   catch(...)
    185   {
    186     IncorrectCommand();
    187   }
    188 
    189   if(parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs)
    190   {
    191     PrintHelp();
    192     return 0;
    193   }
    194   const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
    195 
    196   int paramIndex = 0;
    197   if (paramIndex >= nonSwitchStrings.Size())
    198     IncorrectCommand();
    199   const UString &command = nonSwitchStrings[paramIndex++];
    200 
    201   bool dictDefined = false;
    202   UInt32 dict = (UInt32)-1;
    203   if(parser[NKey::kDict].ThereIs)
    204   {
    205     UInt32 dicLog;
    206     if (!GetNumber(parser[NKey::kDict].PostStrings[0], dicLog))
    207       IncorrectCommand();
    208     dict = 1 << dicLog;
    209     dictDefined = true;
    210   }
    211   UString mf = L"BT4";
    212   if (parser[NKey::kMatchFinder].ThereIs)
    213     mf = parser[NKey::kMatchFinder].PostStrings[0];
    214 
    215   UInt32 numThreads = (UInt32)-1;
    216 
    217   #ifndef _7ZIP_ST
    218   if (parser[NKey::kMultiThread].ThereIs)
    219   {
    220     UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors();
    221     const UString &s = parser[NKey::kMultiThread].PostStrings[0];
    222     if (s.IsEmpty())
    223       numThreads = numCPUs;
    224     else
    225       if (!GetNumber(s, numThreads))
    226         IncorrectCommand();
    227   }
    228   #endif
    229 
    230   if (command.CompareNoCase(L"b") == 0)
    231   {
    232     const UInt32 kNumDefaultItereations = 1;
    233     UInt32 numIterations = kNumDefaultItereations;
    234     {
    235       if (paramIndex < nonSwitchStrings.Size())
    236         if (!GetNumber(nonSwitchStrings[paramIndex++], numIterations))
    237           numIterations = kNumDefaultItereations;
    238     }
    239     return LzmaBenchCon(stderr, numIterations, numThreads, dict);
    240   }
    241 
    242   if (numThreads == (UInt32)-1)
    243     numThreads = 1;
    244 
    245   bool encodeMode = false;
    246   if (command.CompareNoCase(L"e") == 0)
    247     encodeMode = true;
    248   else if (command.CompareNoCase(L"d") == 0)
    249     encodeMode = false;
    250   else
    251     IncorrectCommand();
    252 
    253   bool stdInMode = parser[NKey::kStdIn].ThereIs;
    254   bool stdOutMode = parser[NKey::kStdOut].ThereIs;
    255 
    256   CMyComPtr<ISequentialInStream> inStream;
    257   CInFileStream *inStreamSpec = 0;
    258   if (stdInMode)
    259   {
    260     inStream = new CStdInFileStream;
    261     MY_SET_BINARY_MODE(stdin);
    262   }
    263   else
    264   {
    265     if (paramIndex >= nonSwitchStrings.Size())
    266       IncorrectCommand();
    267     const UString &inputName = nonSwitchStrings[paramIndex++];
    268     inStreamSpec = new CInFileStream;
    269     inStream = inStreamSpec;
    270     if (!inStreamSpec->Open(GetSystemString(inputName)))
    271     {
    272       fprintf(stderr, "\nError: can not open input file %s\n",
    273           (const char *)GetOemString(inputName));
    274       return 1;
    275     }
    276   }
    277 
    278   CMyComPtr<ISequentialOutStream> outStream;
    279   COutFileStream *outStreamSpec = NULL;
    280   if (stdOutMode)
    281   {
    282     outStream = new CStdOutFileStream;
    283     MY_SET_BINARY_MODE(stdout);
    284   }
    285   else
    286   {
    287     if (paramIndex >= nonSwitchStrings.Size())
    288       IncorrectCommand();
    289     const UString &outputName = nonSwitchStrings[paramIndex++];
    290     outStreamSpec = new COutFileStream;
    291     outStream = outStreamSpec;
    292     if (!outStreamSpec->Create(GetSystemString(outputName), true))
    293     {
    294       fprintf(stderr, "\nError: can not open output file %s\n",
    295         (const char *)GetOemString(outputName));
    296       return 1;
    297     }
    298   }
    299 
    300   if (parser[NKey::kFilter86].ThereIs)
    301   {
    302     // -f86 switch is for x86 filtered mode: BCJ + LZMA.
    303     if (parser[NKey::kEOS].ThereIs || stdInMode)
    304       throw "Can not use stdin in this mode";
    305     UInt64 fileSize;
    306     inStreamSpec->File.GetLength(fileSize);
    307     if (fileSize > 0xF0000000)
    308       throw "File is too big";
    309     size_t inSize = (size_t)fileSize;
    310     Byte *inBuffer = 0;
    311     if (inSize != 0)
    312     {
    313       inBuffer = (Byte *)MyAlloc((size_t)inSize);
    314       if (inBuffer == 0)
    315         throw kCantAllocate;
    316     }
    317 
    318     if (ReadStream_FAIL(inStream, inBuffer, inSize) != S_OK)
    319       throw "Can not read";
    320 
    321     Byte *outBuffer = 0;
    322     size_t outSize;
    323     if (encodeMode)
    324     {
    325       // we allocate 105% of original size for output buffer
    326       outSize = (size_t)fileSize / 20 * 21 + (1 << 16);
    327       if (outSize != 0)
    328       {
    329         outBuffer = (Byte *)MyAlloc((size_t)outSize);
    330         if (outBuffer == 0)
    331           throw kCantAllocate;
    332       }
    333       if (!dictDefined)
    334         dict = 1 << 23;
    335       int res = Lzma86_Encode(outBuffer, &outSize, inBuffer, inSize,
    336           5, dict, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO);
    337       if (res != 0)
    338       {
    339         fprintf(stderr, "\nEncoder error = %d\n", (int)res);
    340         return 1;
    341       }
    342     }
    343     else
    344     {
    345       UInt64 outSize64;
    346       if (Lzma86_GetUnpackSize(inBuffer, inSize, &outSize64) != 0)
    347         throw "data error";
    348       outSize = (size_t)outSize64;
    349       if (outSize != outSize64)
    350         throw "too big";
    351       if (outSize != 0)
    352       {
    353         outBuffer = (Byte *)MyAlloc(outSize);
    354         if (outBuffer == 0)
    355           throw kCantAllocate;
    356       }
    357       int res = Lzma86_Decode(outBuffer, &outSize, inBuffer, &inSize);
    358       if (inSize != (size_t)fileSize)
    359         throw "incorrect processed size";
    360       if (res != 0)
    361         throw "LzmaDecoder error";
    362     }
    363     if (WriteStream(outStream, outBuffer, outSize) != S_OK)
    364       throw kWriteError;
    365     MyFree(outBuffer);
    366     MyFree(inBuffer);
    367     return 0;
    368   }
    369 
    370 
    371   UInt64 fileSize;
    372   if (encodeMode)
    373   {
    374     NCompress::NLzma::CEncoder *encoderSpec = new NCompress::NLzma::CEncoder;
    375     CMyComPtr<ICompressCoder> encoder = encoderSpec;
    376 
    377     if (!dictDefined)
    378       dict = 1 << 23;
    379 
    380     UInt32 pb = 2;
    381     UInt32 lc = 3; // = 0; for 32-bit data
    382     UInt32 lp = 0; // = 2; for 32-bit data
    383     UInt32 algo = 1;
    384     UInt32 fb = 128;
    385     UInt32 mc = 16 + fb / 2;
    386     bool mcDefined = false;
    387 
    388     bool eos = parser[NKey::kEOS].ThereIs || stdInMode;
    389 
    390     ParseUInt32(parser, NKey::kAlgo, algo);
    391     ParseUInt32(parser, NKey::kFb, fb);
    392     ParseUInt32(parser, NKey::kLc, lc);
    393     ParseUInt32(parser, NKey::kLp, lp);
    394     ParseUInt32(parser, NKey::kPb, pb);
    395 
    396     mcDefined = parser[NKey::kMc].ThereIs;
    397     if (mcDefined)
    398       if (!GetNumber(parser[NKey::kMc].PostStrings[0], mc))
    399         IncorrectCommand();
    400 
    401     PROPID propIDs[] =
    402     {
    403       NCoderPropID::kDictionarySize,
    404       NCoderPropID::kPosStateBits,
    405       NCoderPropID::kLitContextBits,
    406       NCoderPropID::kLitPosBits,
    407       NCoderPropID::kAlgorithm,
    408       NCoderPropID::kNumFastBytes,
    409       NCoderPropID::kMatchFinder,
    410       NCoderPropID::kEndMarker,
    411       NCoderPropID::kNumThreads,
    412       NCoderPropID::kMatchFinderCycles,
    413     };
    414     const int kNumPropsMax = sizeof(propIDs) / sizeof(propIDs[0]);
    415 
    416     PROPVARIANT props[kNumPropsMax];
    417     for (int p = 0; p < 6; p++)
    418       props[p].vt = VT_UI4;
    419 
    420     props[0].ulVal = (UInt32)dict;
    421     props[1].ulVal = (UInt32)pb;
    422     props[2].ulVal = (UInt32)lc;
    423     props[3].ulVal = (UInt32)lp;
    424     props[4].ulVal = (UInt32)algo;
    425     props[5].ulVal = (UInt32)fb;
    426 
    427     props[6].vt = VT_BSTR;
    428     props[6].bstrVal = const_cast<BSTR>((const wchar_t *)mf);
    429 
    430     props[7].vt = VT_BOOL;
    431     props[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE;
    432 
    433     props[8].vt = VT_UI4;
    434     props[8].ulVal = (UInt32)numThreads;
    435 
    436     // it must be last in property list
    437     props[9].vt = VT_UI4;
    438     props[9].ulVal = (UInt32)mc;
    439 
    440     int numProps = kNumPropsMax;
    441     if (!mcDefined)
    442       numProps--;
    443 
    444     if (encoderSpec->SetCoderProperties(propIDs, props, numProps) != S_OK)
    445       IncorrectCommand();
    446     encoderSpec->WriteCoderProperties(outStream);
    447 
    448     if (eos || stdInMode)
    449       fileSize = (UInt64)(Int64)-1;
    450     else
    451       inStreamSpec->File.GetLength(fileSize);
    452 
    453     for (int i = 0; i < 8; i++)
    454     {
    455       Byte b = Byte(fileSize >> (8 * i));
    456       if (outStream->Write(&b, 1, 0) != S_OK)
    457       {
    458         PrintMessage(kWriteError);
    459         return 1;
    460       }
    461     }
    462     HRESULT result = encoder->Code(inStream, outStream, 0, 0, 0);
    463     if (result == E_OUTOFMEMORY)
    464     {
    465       PrintMessage("\nError: Can not allocate memory\n");
    466       return 1;
    467     }
    468     else if (result != S_OK)
    469     {
    470       fprintf(stderr, "\nEncoder error = %X\n", (unsigned int)result);
    471       return 1;
    472     }
    473   }
    474   else
    475   {
    476     NCompress::NLzma::CDecoder *decoderSpec = new NCompress::NLzma::CDecoder;
    477     CMyComPtr<ICompressCoder> decoder = decoderSpec;
    478     decoderSpec->FinishStream = true;
    479     const UInt32 kPropertiesSize = 5;
    480     Byte header[kPropertiesSize + 8];
    481     if (ReadStream_FALSE(inStream, header, kPropertiesSize + 8) != S_OK)
    482     {
    483       PrintMessage(kReadError);
    484       return 1;
    485     }
    486     if (decoderSpec->SetDecoderProperties2(header, kPropertiesSize) != S_OK)
    487     {
    488       PrintMessage("SetDecoderProperties error");
    489       return 1;
    490     }
    491     fileSize = 0;
    492     for (int i = 0; i < 8; i++)
    493       fileSize |= ((UInt64)header[kPropertiesSize + i]) << (8 * i);
    494 
    495     if (decoder->Code(inStream, outStream, 0, (fileSize == (UInt64)(Int64)-1) ? 0 : &fileSize, 0) != S_OK)
    496     {
    497       PrintMessage("Decoder error");
    498       return 1;
    499     }
    500   }
    501   if (outStreamSpec != NULL)
    502   {
    503     if (outStreamSpec->Close() != S_OK)
    504     {
    505       PrintMessage("File closing error");
    506       return 1;
    507     }
    508   }
    509   return 0;
    510 }
    511 
    512 int MY_CDECL main(int numArgs, const char *args[])
    513 {
    514   try { return main2(numArgs, args); }
    515   catch(const char *s)
    516   {
    517     fprintf(stderr, "\nError: %s\n", s);
    518     return 1;
    519   }
    520   catch(...)
    521   {
    522     PrintMessage("\nError\n");
    523     return 1;
    524   }
    525 }
    526