// Main.cpp

#include "StdAfx.h"

// FIXME #include <Psapi.h>

#if defined( _7ZIP_LARGE_PAGES)
#include "../../../../C/Alloc.h"
#endif

#include "../../../Common/MyInitGuid.h"

#include "../../../Common/CommandLineParser.h"
#include "../../../Common/IntToString.h"
#include "../../../Common/MyException.h"
#include "../../../Common/StringConvert.h"
#include "../../../Common/StringToInt.h"

#include "../../../Windows/ErrorMsg.h"
#ifdef _WIN32
#include "../../../Windows/MemoryLock.h"
#endif

#ifndef _7ZIP_ST
#include "../../../Windows/Synchronization.h"
#endif

#include "../../../Windows/TimeUtils.h"

#include "../Common/ArchiveCommandLine.h"
#include "../Common/ExitCode.h"
#include "../Common/Extract.h"
#ifdef EXTERNAL_CODECS
#include "../Common/LoadCodecs.h"
#endif

#include "BenchCon.h"
#include "ConsoleClose.h"
#include "ExtractCallbackConsole.h"
#include "List.h"
#include "OpenCallbackConsole.h"
#include "UpdateCallbackConsole.h"

#include "HashCon.h"

#ifdef PROG_VARIANT_R
#include "../../../../C/7zVersion.h"
#else
#include "../../MyVersion.h"
#endif

#include "myPrivate.h"
#include "Windows/System.h"

using namespace NWindows;
using namespace NFile;
using namespace NCommandLineParser;

#ifdef _WIN32
HINSTANCE g_hInstance = 0;
#endif
extern CStdOutStream *g_StdStream;

static const char *kCopyrightString = "\n7-Zip"
#ifndef EXTERNAL_CODECS
#ifdef PROG_VARIANT_R
" (r)"
#else
" (a)"
#endif
#endif

#ifdef _WIN64
" [64]"
#endif

" " MY_VERSION_COPYRIGHT_DATE "\n"
"p7zip Version " P7ZIP_VERSION ;

static const char *kHelpString =
    "\nUsage: 7z"
#ifndef EXTERNAL_CODECS
#ifdef PROG_VARIANT_R
    "r"
#else
    "a"
#endif
#endif
    " <command> [<switches>...] <archive_name> [<file_names>...]\n"
    "       [<@listfiles...>]\n"
    "\n"
    "<Commands>\n"
    "  a : Add files to archive\n"
    "  b : Benchmark\n"
    "  d : Delete files from archive\n"
    "  e : Extract files from archive (without using directory names)\n"
    "  h : Calculate hash values for files\n"
    "  l : List contents of archive\n"
//    "  l[a|t][f] : List contents of archive\n"
//    "    a - with Additional fields\n"
//    "    t - with all fields\n"
//    "    f - with Full pathnames\n"
    "  rn : Rename files in archive\n"
    "  t : Test integrity of archive\n"
    "  u : Update files to archive\n"
    "  x : eXtract files with full paths\n"
    "<Switches>\n"
    "  -- : Stop switches parsing\n"
    "  -ai[r[-|0]]{@listfile|!wildcard} : Include archives\n"
    "  -ax[r[-|0]]{@listfile|!wildcard} : eXclude archives\n"
    "  -bd : Disable percentage indicator\n"
    "  -i[r[-|0]]{@listfile|!wildcard} : Include filenames\n"
    "  -m{Parameters} : set compression Method\n"
    "  -o{Directory} : set Output directory\n"
    #ifndef _NO_CRYPTO
    "  -p{Password} : set Password\n"
    #endif
    "  -r[-|0] : Recurse subdirectories\n"
    "  -scs{UTF-8|UTF-16LE|UTF-16BE|WIN|DOS|{id}} : set charset for list files\n"
    "  -sfx[{name}] : Create SFX archive\n"
    "  -si[{name}] : read data from stdin\n"
    "  -slt : show technical information for l (List) command\n"
    "  -so : write data to stdout\n"
    "  -ssc[-] : set sensitive case mode\n"
    // "  -ssw : compress shared files\n"
    "  -t{Type} : Set type of archive\n"
    "  -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName] : Update options\n"
    "  -v{Size}[b|k|m|g] : Create volumes\n"
    "  -w[{path}] : assign Work directory. Empty path means a temporary directory\n"
    "  -x[r[-|0]]]{@listfile|!wildcard} : eXclude filenames\n"
    "  -y : assume Yes on all queries\n";

// ---------------------------
// exception messages

static const char *kEverythingIsOk = "Everything is Ok";
static const char *kUserErrorMessage = "Incorrect command line";
static const char *kNoFormats = "7-Zip cannot find the code that works with archives.";
static const char *kUnsupportedArcTypeMessage = "Unsupported archive type";
// static const char *kUnsupportedUpdateArcType = "Can't create archive for that type";

static CFSTR kDefaultSfxModule = FTEXT("7zCon.sfx");

static void ShowMessageAndThrowException(CStdOutStream &s, LPCSTR message, NExitCode::EEnum code)
{
  s << endl << "Error: " << message << endl;
  throw code;
}

#ifndef _WIN32
static void GetArguments(int numArgs, const char *args[], UStringVector &parts)
{
  parts.Clear();
  for (int i = 0; i < numArgs; i++)
  {
    UString s = MultiByteToUnicodeString(args[i]);
    parts.Add(s);
  }
}
#endif

static void ShowCopyrightAndHelp(CStdOutStream &s, bool needHelp)
{
  s << kCopyrightString << " (locale=" << my_getlocale() <<",Utf16=";
  if (global_use_utf16_conversion) s << "on";
  else                             s << "off";
  s << ",HugeFiles=";
  if (sizeof(off_t) >= 8) s << "on,";
  else                    s << "off,";
  int nbcpu = NWindows::NSystem::GetNumberOfProcessors();
  if (nbcpu > 1) s << nbcpu << " CPUs";
  else           s << nbcpu << " CPU";

#ifdef P7ZIP_USE_ASM
{
  const char * txt =",ASM";
  #ifdef MY_CPU_X86_OR_AMD64
  if (CPU_Is_Aes_Supported()) { txt =",ASM,AES-NI"; }
  #endif
  s << txt;
}
#endif
    s << ")\n";

  if (needHelp)
    s << kHelpString;
}

#ifdef EXTERNAL_CODECS

static void PrintString(CStdOutStream &stdStream, const AString &s, int size)
{
  int len = s.Len();
  for (int i = len; i < size; i++)
    stdStream << ' ';
  stdStream << s;
}

static void PrintUInt32(CStdOutStream &stdStream, UInt32 val, int size)
{
  char s[16];
  ConvertUInt32ToString(val, s);
  PrintString(stdStream, s, size);
}

static void PrintLibIndex(CStdOutStream &stdStream, int libIndex)
{
  if (libIndex >= 0)
    PrintUInt32(stdStream, libIndex, 2);
  else
    stdStream << "  ";
  stdStream << ' ';
}

#endif

static void PrintString(CStdOutStream &stdStream, const UString &s, int size)
{
  int len = s.Len();
  stdStream << s;
  for (int i = len; i < size; i++)
    stdStream << ' ';
}

static inline char GetHex(unsigned val)
{
  return (char)((val < 10) ? ('0' + val) : ('A' + (val - 10)));
}

static int WarningsCheck(HRESULT result, const CCallbackConsoleBase &callback,
    const CErrorInfo &errorInfo, CStdOutStream &stdStream)
{
  int exitCode = NExitCode::kSuccess;
  
  if (callback.CantFindFiles.Size() > 0)
  {
    stdStream << endl;
    stdStream << "WARNINGS for files:" << endl << endl;
    unsigned numErrors = callback.CantFindFiles.Size();
    for (unsigned i = 0; i < numErrors; i++)
    {
      stdStream << callback.CantFindFiles[i] << " : ";
      stdStream << NError::MyFormatMessage(callback.CantFindCodes[i]) << endl;
    }
    stdStream << "----------------" << endl;
    stdStream << "WARNING: Cannot find " << numErrors << " file";
    if (numErrors > 1)
      stdStream << "s";
    stdStream << endl;
    exitCode = NExitCode::kWarning;
  }
  
  if (result != S_OK)
  {
    UString message;
    if (!errorInfo.Message.IsEmpty())
    {
      message += errorInfo.Message;
      message += L"\n";
    }
    if (!errorInfo.FileName.IsEmpty())
    {
      message += fs2us(errorInfo.FileName);
      message += L"\n";
    }
    if (!errorInfo.FileName2.IsEmpty())
    {
      message += fs2us(errorInfo.FileName2);
      message += L"\n";
    }
    if (errorInfo.SystemError != 0)
    {
      message += NError::MyFormatMessage(errorInfo.SystemError);
      message += L"\n";
    }
    if (!message.IsEmpty())
      stdStream << L"\nError:\n" << message;

    // we will work with (result) later
    // throw CSystemException(result);
    return NExitCode::kFatalError;
  }

  unsigned numErrors = callback.FailedFiles.Size();
  if (numErrors == 0)
  {
    if (callback.CantFindFiles.Size() == 0)
      stdStream << kEverythingIsOk << endl;
  }
  else
  {
    stdStream << endl;
    stdStream << "WARNINGS for files:" << endl << endl;
    for (unsigned i = 0; i < numErrors; i++)
    {
      stdStream << callback.FailedFiles[i] << " : ";
      stdStream << NError::MyFormatMessage(callback.FailedCodes[i]) << endl;
    }
    stdStream << "----------------" << endl;
    stdStream << "WARNING: Cannot open " << numErrors << " file";
    if (numErrors > 1)
      stdStream << "s";
    stdStream << endl;
    exitCode = NExitCode::kWarning;
  }
  return exitCode;
}

static void ThrowException_if_Error(HRESULT res)
{
  if (res != S_OK)
    throw CSystemException(res);
}


static void PrintNum(UInt64 val, unsigned numDigits, char c = ' ')
{
  char temp[64];
  char *p = temp + 32;
  ConvertUInt64ToString(val, p);
  unsigned len = MyStringLen(p);
  for (; len < numDigits; len++)
    *--p = c;
  *g_StdStream << p;
}

static void PrintTime(const char *s, UInt64 val, UInt64 total)
{
  *g_StdStream << endl << s << " Time =";
  const UInt32 kFreq = 10000000;
  UInt64 sec = val / kFreq;
  PrintNum(sec, 6);
  *g_StdStream << '.';
  UInt32 ms = (UInt32)(val - (sec * kFreq)) / (kFreq / 1000);
  PrintNum(ms, 3, '0');
  
  while (val > ((UInt64)1 << 56))
  {
    val >>= 1;
    total >>= 1;
  }

  UInt64 percent = 0;
  if (total != 0)
    percent = val * 100 / total;
  *g_StdStream << " =";
  PrintNum(percent, 5);
  *g_StdStream << '%';
}

#if 0 // #ifndef UNDER_CE

#define SHIFT_SIZE_VALUE(x, num) (((x) + (1 << (num)) - 1) >> (num))

static void PrintMemUsage(const char *s, UInt64 val)
{
  *g_StdStream << "    " << s << " Memory =";
  PrintNum(SHIFT_SIZE_VALUE(val, 20), 7);
  *g_StdStream << " MB";
}

EXTERN_C_BEGIN
typedef BOOL (WINAPI *Func_GetProcessMemoryInfo)(HANDLE Process,
    PPROCESS_MEMORY_COUNTERS ppsmemCounters, DWORD cb);
EXTERN_C_END

#endif

static inline UInt64 GetTime64(const FILETIME &t) { return ((UInt64)t.dwHighDateTime << 32) | t.dwLowDateTime; }

static void PrintStat()
{
#if 0 // FIXME
  FILETIME creationTimeFT, exitTimeFT, kernelTimeFT, userTimeFT;
  if (!
      #ifdef UNDER_CE
        ::GetThreadTimes(::GetCurrentThread()
      #else
        // NT 3.5
        ::GetProcessTimes(::GetCurrentProcess()
      #endif
      , &creationTimeFT, &exitTimeFT, &kernelTimeFT, &userTimeFT))
    return;
  FILETIME curTimeFT;
  NTime::GetCurUtcFileTime(curTimeFT);

  #ifndef UNDER_CE
  
  PROCESS_MEMORY_COUNTERS m;
  memset(&m, 0, sizeof(m));
  BOOL memDefined = FALSE;
  {
    /* NT 4.0: GetProcessMemoryInfo() in Psapi.dll
       Win7: new function K32GetProcessMemoryInfo() in kernel32.dll
       It's faster to call kernel32.dll code than Psapi.dll code
       GetProcessMemoryInfo() requires Psapi.lib
       Psapi.lib in SDK7+ can link to K32GetProcessMemoryInfo in kernel32.dll
       The program with K32GetProcessMemoryInfo will not work on systems before Win7
       // memDefined = GetProcessMemoryInfo(GetCurrentProcess(), &m, sizeof(m));
    */

    Func_GetProcessMemoryInfo my_GetProcessMemoryInfo = (Func_GetProcessMemoryInfo)
        ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "K32GetProcessMemoryInfo");
    if (!my_GetProcessMemoryInfo)
    {
      HMODULE lib = LoadLibraryW(L"Psapi.dll");
      if (lib)
        my_GetProcessMemoryInfo = (Func_GetProcessMemoryInfo)::GetProcAddress(lib, "GetProcessMemoryInfo");
    }
    if (my_GetProcessMemoryInfo)
      memDefined = my_GetProcessMemoryInfo(GetCurrentProcess(), &m, sizeof(m));
    // FreeLibrary(lib);
  }

  #endif

  UInt64 curTime = GetTime64(curTimeFT);
  UInt64 creationTime = GetTime64(creationTimeFT);
  UInt64 kernelTime = GetTime64(kernelTimeFT);
  UInt64 userTime = GetTime64(userTimeFT);

  UInt64 totalTime = curTime - creationTime;
  
  PrintTime("Kernel ", kernelTime, totalTime);
  PrintTime("User   ", userTime, totalTime);
  
  PrintTime("Process", kernelTime + userTime, totalTime);
  #ifndef UNDER_CE
  if (memDefined) PrintMemUsage("Virtual ", m.PeakPagefileUsage);
  #endif
  
  PrintTime("Global ", totalTime, totalTime);
  #ifndef UNDER_CE
  if (memDefined) PrintMemUsage("Physical", m.PeakWorkingSetSize);
  #endif
  
  *g_StdStream << endl;
#endif // FIXME
}

int Main2(
  #ifndef _WIN32
  int numArgs, char *args[]
  #endif
)
{
  #if defined(_WIN32) && !defined(UNDER_CE)
  SetFileApisToOEM();
  #endif

  UStringVector commandStrings;
  #ifdef _WIN32
  NCommandLineParser::SplitCommandLine(GetCommandLineW(), commandStrings);
  #else
  // GetArguments(numArgs, args, commandStrings);
  extern void mySplitCommandLine(int numArgs, char *args[],UStringVector &parts);
  mySplitCommandLine(numArgs,args,commandStrings);  
  #endif

  if (commandStrings.Size() == 1)
  {
    ShowCopyrightAndHelp(g_StdOut, true);
    return 0;
  }

  commandStrings.Delete(0);

  CArcCmdLineOptions options;

  CArcCmdLineParser parser;

  parser.Parse1(commandStrings, options);

  if (options.HelpMode)
  {
    ShowCopyrightAndHelp(g_StdOut, true);
    return 0;
  }

  #if defined(_WIN32) && !defined(UNDER_CE)
  NSecurity::EnablePrivilege_SymLink();
  #endif
  #ifdef _7ZIP_LARGE_PAGES
  if (options.LargePages)
  {
    SetLargePageSize();
    #if defined(_WIN32) && !defined(UNDER_CE)
    NSecurity::EnablePrivilege_LockMemory();
    #endif
  }
  #endif

  CStdOutStream &stdStream = options.StdOutMode ? g_StdErr : g_StdOut;
  g_StdStream = &stdStream;

  if (options.EnableHeaders)
    ShowCopyrightAndHelp(stdStream, false);

  parser.Parse2(options);

  CCodecs *codecs = new CCodecs;
  #ifdef EXTERNAL_CODECS
  CExternalCodecs __externalCodecs;
  __externalCodecs.GetCodecs = codecs;
  __externalCodecs.GetHashers = codecs;
  #else
  CMyComPtr<IUnknown> compressCodecsInfo = codecs;
  #endif
  codecs->CaseSensitiveChange = options.CaseSensitiveChange;
  codecs->CaseSensitive = options.CaseSensitive;
  ThrowException_if_Error(codecs->Load());

  bool isExtractGroupCommand = options.Command.IsFromExtractGroup();

  if (codecs->Formats.Size() == 0 &&
        (isExtractGroupCommand
        || options.Command.CommandType == NCommandType::kList
        || options.Command.IsFromUpdateGroup()))
    throw kNoFormats;

  CObjectVector<COpenType> types;
  if (!ParseOpenTypes(*codecs, options.ArcType, types))
    throw kUnsupportedArcTypeMessage;

  CIntVector excludedFormats;
  FOR_VECTOR (k, options.ExcludedArcTypes)
  {
    CIntVector tempIndices;
    if (!codecs->FindFormatForArchiveType(options.ExcludedArcTypes[k], tempIndices)
        || tempIndices.Size() != 1)
      throw kUnsupportedArcTypeMessage;
    excludedFormats.AddToUniqueSorted(tempIndices[0]);
    // excludedFormats.Sort();
  }

  
  #ifdef EXTERNAL_CODECS
  if (isExtractGroupCommand
      || options.Command.CommandType == NCommandType::kHash
      || options.Command.CommandType == NCommandType::kBenchmark)
    ThrowException_if_Error(__externalCodecs.LoadCodecs());
  #endif

  int retCode = NExitCode::kSuccess;
  HRESULT hresultMain = S_OK;

  bool showStat = true;
  if (!options.EnableHeaders ||
      options.TechMode)
    showStat = false;
  

  if (options.Command.CommandType == NCommandType::kInfo)
  {
    unsigned i;

    #ifdef EXTERNAL_CODECS
    stdStream << endl << "Libs:" << endl;
    for (i = 0; i < codecs->Libs.Size(); i++)
    {
      PrintLibIndex(stdStream, i);
      stdStream << ' ' << codecs->Libs[i].Path << endl;
    }
    #endif

    stdStream << endl << "Formats:" << endl;
    
    const char *kArcFlags = "KSNFMGOPBELH";
    const unsigned kNumArcFlags = (unsigned)strlen(kArcFlags);
    
    for (i = 0; i < codecs->Formats.Size(); i++)
    {
      const CArcInfoEx &arc = codecs->Formats[i];
      #ifdef EXTERNAL_CODECS
      PrintLibIndex(stdStream, arc.LibIndex);
      #else
      stdStream << "  ";
      #endif
      stdStream << (char)(arc.UpdateEnabled ? 'C' : ' ');
      for (unsigned b = 0; b < kNumArcFlags; b++)
      {
        stdStream << (char)
          ((arc.Flags & ((UInt32)1 << b)) != 0 ? kArcFlags[b] : ' ');
      }
        
      stdStream << ' ';
      PrintString(stdStream, arc.Name, 8);
      stdStream << ' ';
      UString s;
      FOR_VECTOR (t, arc.Exts)
      {
        if (t != 0)
          s += L' ';
        const CArcExtInfo &ext = arc.Exts[t];
        s += ext.Ext;
        if (!ext.AddExt.IsEmpty())
        {
          s += L" (";
          s += ext.AddExt;
          s += L')';
        }
      }
      PrintString(stdStream, s, 13);
      stdStream << ' ';
      if (arc.SignatureOffset != 0)
        stdStream << "offset=" << arc.SignatureOffset << ' ';

      FOR_VECTOR(si, arc.Signatures)
      {
        if (si != 0)
          stdStream << "  ||  ";

        const CByteBuffer &sig = arc.Signatures[si];
        
        for (size_t j = 0; j < sig.Size(); j++)
        {
          if (j != 0)
            stdStream << ' ';
          Byte b = sig[j];
          if (b > 0x20 && b < 0x80)
          {
            stdStream << (char)b;
          }
          else
          {
            stdStream << GetHex((b >> 4) & 0xF);
            stdStream << GetHex(b & 0xF);
          }
        }
      }
      stdStream << endl;
    }

    #ifdef EXTERNAL_CODECS

    stdStream << endl << "Codecs:" << endl << "Lib         ID  Name" << endl;
    UInt32 numMethods;
    if (codecs->GetNumberOfMethods(&numMethods) == S_OK)
    for (UInt32 j = 0; j < numMethods; j++)
    {
      PrintLibIndex(stdStream, codecs->GetCodecLibIndex(j));
      stdStream << (char)(codecs->GetCodecEncoderIsAssigned(j) ? 'C' : ' ');
      UInt64 id;
      stdStream << "  ";
      HRESULT res = codecs->GetCodecId(j, id);
      if (res != S_OK)
        id = (UInt64)(Int64)-1;
      char s[32];
      ConvertUInt64ToHex(id, s);
      PrintString(stdStream, s, 8);
      stdStream << "  " << codecs->GetCodecName(j) << endl;
    }
    
    stdStream << endl << "Hashers:" << endl << " L Size     ID  Name" << endl;
    numMethods = codecs->GetNumHashers();
    for (UInt32 j = 0; j < numMethods; j++)
    {
      PrintLibIndex(stdStream, codecs->GetHasherLibIndex(j));
      PrintUInt32(stdStream, codecs->GetHasherDigestSize(j), 4);
      stdStream << ' ';
      char s[32];
      ConvertUInt64ToHex(codecs->GetHasherId(j), s);
      PrintString(stdStream, s, 6);
      stdStream << "  " << codecs->GetHasherName(j) << endl;
    }

    #endif
    
  }
  else if (options.Command.CommandType == NCommandType::kBenchmark)
  {
    hresultMain = BenchCon(EXTERNAL_CODECS_VARS
        options.Properties, options.NumIterations, (FILE *)stdStream);
    if (hresultMain == S_FALSE)
    {
      stdStream << "\nDecoding Error\n";
      retCode = NExitCode::kFatalError;
      hresultMain = S_OK;
    }
  }
  else if (isExtractGroupCommand || options.Command.CommandType == NCommandType::kList)
  {
    if (isExtractGroupCommand)
    {
      CExtractCallbackConsole *ecs = new CExtractCallbackConsole;
      CMyComPtr<IFolderArchiveExtractCallback> extractCallback = ecs;

      ecs->OutStream = &stdStream;

      #ifndef _NO_CRYPTO
      ecs->PasswordIsDefined = options.PasswordEnabled;
      ecs->Password = options.Password;
      #endif

      ecs->Init();

      COpenCallbackConsole openCallback;
      openCallback.OutStream = &stdStream;

      #ifndef _NO_CRYPTO
      openCallback.PasswordIsDefined = options.PasswordEnabled;
      openCallback.Password = options.Password;
      #endif

      CExtractOptions eo;
      (CExtractOptionsBase &)eo = options.ExtractOptions;
      eo.StdInMode = options.StdInMode;
      eo.StdOutMode = options.StdOutMode;
      eo.YesToAll = options.YesToAll;
      eo.TestMode = options.Command.IsTestCommand();
      
      #ifndef _SFX
      eo.Properties = options.Properties;
      #endif

      UString errorMessage;
      CDecompressStat stat;
      CHashBundle hb;
      IHashCalc *hashCalc = NULL;

      if (!options.HashMethods.IsEmpty())
      {
        hashCalc = &hb;
        ThrowException_if_Error(hb.SetMethods(EXTERNAL_CODECS_VARS options.HashMethods));
        hb.Init();
      }
      hresultMain = Extract(
          codecs,
          types,
          excludedFormats,
          options.ArchivePathsSorted,
          options.ArchivePathsFullSorted,
          options.Censor.Pairs.Front().Head,
          eo, &openCallback, ecs, hashCalc, errorMessage, stat);
      if (!errorMessage.IsEmpty())
      {
        stdStream << endl << "Error: " << errorMessage;
        if (hresultMain == S_OK)
          hresultMain = E_FAIL;
      }

      stdStream << endl;

      if (ecs->NumTryArcs > 1)
      {
        stdStream << "Archives: " << ecs->NumTryArcs << endl;
        stdStream << "OK archives: " << ecs->NumOkArcs << endl;
      }
      bool isError = false;
      if (ecs->NumCantOpenArcs != 0)
      {
        isError = true;
        stdStream << "Can't open as archive: " << ecs->NumCantOpenArcs << endl;
      }
      if (ecs->NumArcsWithError != 0)
      {
        isError = true;
        stdStream << "Archives with Errors: " << ecs->NumArcsWithError << endl;
      }
      if (ecs->NumArcsWithWarnings != 0)
        stdStream << "Archives with Warnings: " << ecs->NumArcsWithWarnings << endl;
      
      if (ecs->NumOpenArcWarnings != 0)
      {
        stdStream << endl;
        if (ecs->NumOpenArcWarnings != 0)
          stdStream << "Warnings: " << ecs->NumOpenArcWarnings << endl;
      }
      
      if (ecs->NumOpenArcErrors != 0)
      {
        isError = true;
        stdStream << endl;
        if (ecs->NumOpenArcErrors != 0)
          stdStream << "Open Errors: " << ecs->NumOpenArcErrors << endl;
      }

      if (isError)
        retCode = NExitCode::kFatalError;
      
      if (ecs->NumArcsWithError != 0 || ecs->NumFileErrors != 0)
      {
        // if (ecs->NumArchives > 1)
        {
          stdStream << endl;
          if (ecs->NumFileErrors != 0)
            stdStream << "Sub items Errors: " << ecs->NumFileErrors << endl;
        }
      }
      else if (hresultMain == S_OK)
      {
     
      if (stat.NumFolders != 0)
        stdStream << "Folders: " << stat.NumFolders << endl;
      if (stat.NumFiles != 1 || stat.NumFolders != 0 || stat.NumAltStreams != 0)
        stdStream << "Files: " << stat.NumFiles << endl;
      if (stat.NumAltStreams != 0)
      {
        stdStream << "Alternate Streams: " << stat.NumAltStreams << endl;
        stdStream << "Alternate Streams Size: " << stat.AltStreams_UnpackSize << endl;
      }

      stdStream
           << "Size:       " << stat.UnpackSize << endl
           << "Compressed: " << stat.PackSize << endl;
      if (hashCalc)
        PrintHashStat(stdStream, hb);
      }
    }
    else
    {
      UInt64 numErrors = 0;
      UInt64 numWarnings = 0;
      
      // options.ExtractNtOptions.StoreAltStreams = true, if -sns[-] is not definmed

      hresultMain = ListArchives(
          codecs,
          types,
          excludedFormats,
          options.StdInMode,
          options.ArchivePathsSorted,
          options.ArchivePathsFullSorted,
          options.ExtractOptions.NtOptions.AltStreams.Val,
          options.AltStreams.Val, // we don't want to show AltStreams by default
          options.Censor.Pairs.Front().Head,
          options.EnableHeaders,
          options.TechMode,
          #ifndef _NO_CRYPTO
          options.PasswordEnabled,
          options.Password,
          #endif
          &options.Properties,
          numErrors, numWarnings);

        if (options.EnableHeaders)
          if (numWarnings > 0)
            g_StdOut << endl << "Warnings: " << numWarnings << endl;
      if (numErrors > 0)
      {
        if (options.EnableHeaders)
          g_StdOut << endl << "Errors: " << numErrors << endl;
        retCode = NExitCode::kFatalError;
      }
    }
  }
  else if (options.Command.IsFromUpdateGroup())
  {
    CUpdateOptions &uo = options.UpdateOptions;
    if (uo.SfxMode && uo.SfxModule.IsEmpty())
      uo.SfxModule = kDefaultSfxModule;

    COpenCallbackConsole openCallback;
    openCallback.OutStream = &stdStream;

    #ifndef _NO_CRYPTO
    bool passwordIsDefined =
        options.PasswordEnabled && !options.Password.IsEmpty();
    openCallback.PasswordIsDefined = passwordIsDefined;
    openCallback.Password = options.Password;
    #endif

    CUpdateCallbackConsole callback;
    callback.EnablePercents = options.EnablePercents;

    #ifndef _NO_CRYPTO
    callback.PasswordIsDefined = passwordIsDefined;
    callback.AskPassword = options.PasswordEnabled && options.Password.IsEmpty();
    callback.Password = options.Password;
    #endif
    callback.StdOutMode = uo.StdOutMode;
    callback.Init(&stdStream);

    CUpdateErrorInfo errorInfo;

    /*
    if (!uo.Init(codecs, types, options.ArchiveName))
      throw kUnsupportedUpdateArcType;
    */
    hresultMain = UpdateArchive(codecs,
        types,
        options.ArchiveName,
        options.Censor,
        uo,
        errorInfo, &openCallback, &callback, true);
    retCode = WarningsCheck(hresultMain, callback, errorInfo, stdStream);
#ifdef ENV_UNIX
    if (uo.SfxMode)
    {
        void myAddExeFlag(const UString &name);
        for(int i = 0; i < uo.Commands.Size(); i++)
        {
            CUpdateArchiveCommand &command = uo.Commands[i];
            if (!uo.StdOutMode)
            {
                myAddExeFlag(command.ArchivePath.GetFinalPath());
            }
        }
    }
#endif
  }
  else if (options.Command.CommandType == NCommandType::kHash)
  {
    const CHashOptions &uo = options.HashOptions;

    CHashCallbackConsole callback;
    callback.EnablePercents = options.EnablePercents;

    callback.Init(&stdStream);

    UString errorInfoString;
    hresultMain = HashCalc(EXTERNAL_CODECS_VARS
        options.Censor, uo,
        errorInfoString, &callback);
    CErrorInfo errorInfo;
    errorInfo.Message = errorInfoString;
    retCode = WarningsCheck(hresultMain, callback, errorInfo, stdStream);
  }
  else
    ShowMessageAndThrowException(stdStream, kUserErrorMessage, NExitCode::kUserError);

  if (showStat)
    PrintStat();

  ThrowException_if_Error(hresultMain);

  return retCode;
}
