misc/libphysfs/lzma/CPP/7zip/UI/Console/Main.cpp
author nemo
Mon, 10 Apr 2017 12:06:43 -0400
changeset 12213 bb5522e88ab2
permissions -rw-r--r--
bulk copy of latest physfs to our misc/libphysfs since this seems to fix an off-by-1 error reliably hit in readln read of 1 byte probably introduced in the addition of the buffered read. Whether this is excessive or whether libphysfs should even be maintained by us is another matter. But at least we shouldn't crash

// Main.cpp

#include "StdAfx.h"

#include "Common/MyInitGuid.h"

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

#include "Windows/FileDir.h"
#include "Windows/FileName.h"
#include "Windows/Defs.h"
#include "Windows/Error.h"
#ifdef _WIN32
#include "Windows/MemoryLock.h"
#endif

#include "../../IPassword.h"
#include "../../ICoder.h"
#include "../Common/UpdateAction.h"
#include "../Common/Update.h"
#include "../Common/Extract.h"
#include "../Common/ArchiveCommandLine.h"
#include "../Common/ExitCode.h"
#ifdef EXTERNAL_CODECS
#include "../Common/LoadCodecs.h"
#endif

#include "../../Compress/LZMA_Alone/LzmaBenchCon.h"

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

#include "../../MyVersion.h"

#if defined( _WIN32) && defined( _7ZIP_LARGE_PAGES)
extern "C" 
{ 
#include "../../../../C/Alloc.h"
}
#endif

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

HINSTANCE g_hInstance = 0;
extern CStdOutStream *g_StdStream;

static const char *kCopyrightString = "\n7-Zip"
#ifndef EXTERNAL_CODECS
" (A)"
#endif

#ifdef _WIN64
" [64]"
#endif

" " MY_VERSION_COPYRIGHT_DATE "\n";

static const char *kHelpString = 
    "\nUsage: 7z"
#ifdef _NO_CRYPTO
    "r"
#else
#ifndef EXTERNAL_CODECS
    "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"
    "  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"
    "  t: Test integrity of archive\n"
    "  u: Update files to archive\n"
    "  x: eXtract files with full paths\n"
    "<Switches>\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"
    "  -p{Password}: set Password\n"
    "  -r[-|0]: Recurse subdirectories\n"
    "  -scs{UTF-8 | WIN | DOS}: 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"
    "  -v{Size}[b|k|m|g]: Create volumes\n"
    "  -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]: Update options\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"; // NExitCode::kUserError

static const wchar_t *kDefaultSfxModule = L"7zCon.sfx";

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

static void PrintHelpAndExit(CStdOutStream &s) // yyy
{
  s << kHelpString;
  ShowMessageAndThrowException(s, kUserErrorMessage, NExitCode::kUserError);
}

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

static void ShowCopyrightAndHelp(CStdOutStream &s, bool needHelp)
{
  s << kCopyrightString;
  // s << "# CPUs: " << (UInt64)NWindows::NSystem::GetNumberOfProcessors() << "\n";
  if (needHelp) 
    s << kHelpString;
}

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

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

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

int Main2(
  #ifndef _WIN32  
  int numArguments, const char *arguments[]
  #endif
)
{
  #ifdef _WIN32  
  SetFileApisToOEM();
  #endif
  
  UStringVector commandStrings;
  #ifdef _WIN32  
  NCommandLineParser::SplitCommandLine(GetCommandLineW(), commandStrings);
  #else
  GetArguments(numArguments, arguments, commandStrings);
  #endif

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

  CArchiveCommandLineOptions options;

  CArchiveCommandLineParser parser;

  parser.Parse1(commandStrings, options);

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

  #if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES)
  if (options.LargePages)
  {
    SetLargePageSize();
    NSecurity::EnableLockMemoryPrivilege();
  }
  #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;
  CMyComPtr<
    #ifdef EXTERNAL_CODECS
    ICompressCodecsInfo
    #else
    IUnknown
    #endif
    > compressCodecsInfo = codecs;
  HRESULT result = codecs->Load();
  if (result != S_OK)
    throw CSystemException(result);

  bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
  if (options.Command.CommandType == NCommandType::kInfo)
  {
    stdStream << endl << "Formats:" << endl;
    int i;
    for (i = 0; i < codecs->Formats.Size(); i++)
    {
      const CArcInfoEx &arc = codecs->Formats[i];
      #ifdef EXTERNAL_CODECS
      if (arc.LibIndex >= 0)
      {
        char s[32];
        ConvertUInt64ToString(arc.LibIndex, s);
        PrintString(stdStream, s, 2);
      }
      else
      #endif
        stdStream << "  ";
      stdStream << ' ';
      stdStream << (char)(arc.UpdateEnabled ? 'C' : ' ');
      stdStream << (char)(arc.KeepName ? 'K' : ' ');
      stdStream << "  ";
      PrintString(stdStream, arc.Name, 6);
      stdStream << "  ";
      UString s;
      for (int t = 0; t < arc.Exts.Size(); t++)
      {
        const CArcExtInfo &ext = arc.Exts[t];
        s += ext.Ext;
        if (!ext.AddExt.IsEmpty())
        {
          s += L" (";
          s += ext.AddExt;
          s += L')';
        }
        s += L' ';
      }
      PrintString(stdStream, s, 14);
      stdStream << "  ";
      const CByteBuffer &sig = arc.StartSignature;
      for (size_t j = 0; j < sig.GetCapacity(); j++)
      {
        Byte b = sig[j];
        if (b > 0x20 && b < 0x80)
        {
          stdStream << (char)b;
        }
        else
        {
          stdStream << GetHex((Byte)((b >> 4) & 0xF));
          stdStream << GetHex((Byte)(b & 0xF));
        }
        stdStream << ' ';
      }
      stdStream << endl;
    }
    stdStream << endl << "Codecs:" << endl;

    #ifdef EXTERNAL_CODECS
    UINT32 numMethods;
    if (codecs->GetNumberOfMethods(&numMethods) == S_OK)
    for (UInt32 j = 0; j < numMethods; j++)
    {
      int libIndex = codecs->GetCodecLibIndex(j);
      if (libIndex >= 0)
      {
        char s[32];
        ConvertUInt64ToString(libIndex, s);
        PrintString(stdStream, s, 2);
      }
      else
        stdStream << "  ";
      stdStream << ' ';
      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];
      ConvertUInt64ToString(id, s, 16);
      PrintString(stdStream, s, 8);
      stdStream << "  ";
      PrintString(stdStream, codecs->GetCodecName(j), 11);
      stdStream << endl;
      /*
      if (res != S_OK)
        throw "incorrect Codec ID";
      */
    }
    #endif
    return S_OK;
  }
  else if (options.Command.CommandType == NCommandType::kBenchmark)
  {
    if (options.Method.CompareNoCase(L"CRC") == 0)
    {
      HRESULT res = CrcBenchCon((FILE *)stdStream, options.NumIterations, options.NumThreads, options.DictionarySize);
      if (res != S_OK)
      {
        if (res == S_FALSE)
        {
          stdStream << "\nCRC Error\n";
          return NExitCode::kFatalError;
        }
        throw CSystemException(res);
      }
    }
    else
    {
      HRESULT res = LzmaBenchCon(
        #ifdef EXTERNAL_LZMA
        codecs,
        #endif
        (FILE *)stdStream, options.NumIterations, options.NumThreads, options.DictionarySize);
      if (res != S_OK)
      {
        if (res == S_FALSE)
        {
          stdStream << "\nDecoding Error\n";
          return NExitCode::kFatalError;
        }
        throw CSystemException(res);
      }
    }
  }
  else if (isExtractGroupCommand || options.Command.CommandType == NCommandType::kList)
  {
    if(isExtractGroupCommand)
    {
      CExtractCallbackConsole *ecs = new CExtractCallbackConsole;
      CMyComPtr<IFolderArchiveExtractCallback> extractCallback = ecs;

      ecs->OutStream = &stdStream;
      ecs->PasswordIsDefined = options.PasswordEnabled;
      ecs->Password = options.Password;
      ecs->Init();

      COpenCallbackConsole openCallback;
      openCallback.OutStream = &stdStream;
      openCallback.PasswordIsDefined = options.PasswordEnabled;
      openCallback.Password = options.Password;

      CExtractOptions eo;
      eo.StdOutMode = options.StdOutMode;
      eo.PathMode = options.Command.GetPathMode();
      eo.TestMode = options.Command.IsTestMode();
      eo.OverwriteMode = options.OverwriteMode;
      eo.OutputDir = options.OutputDir;
      eo.YesToAll = options.YesToAll;
      #ifdef COMPRESS_MT
      eo.Properties = options.ExtractProperties;
      #endif
      UString errorMessage;
      CDecompressStat stat;
      HRESULT result = DecompressArchives(
          codecs,
          options.ArchivePathsSorted, 
          options.ArchivePathsFullSorted,
          options.WildcardCensor.Pairs.Front().Head, 
          eo, &openCallback, ecs, errorMessage, stat);
      if (!errorMessage.IsEmpty())
      {
        stdStream << endl << "Error: " << errorMessage;
        if (result == S_OK)
          result = E_FAIL;
      }

      stdStream << endl;
      if (ecs->NumArchives > 1)
        stdStream << "Archives: " << ecs->NumArchives << endl;
      if (ecs->NumArchiveErrors != 0 || ecs->NumFileErrors != 0)
      {
        if (ecs->NumArchives > 1)
        {
          stdStream << endl;
          if (ecs->NumArchiveErrors != 0)
            stdStream << "Archive Errors: " << ecs->NumArchiveErrors << endl;
          if (ecs->NumFileErrors != 0)
            stdStream << "Sub items Errors: " << ecs->NumFileErrors << endl;
        }
        if (result != S_OK)
          throw CSystemException(result);
        return NExitCode::kFatalError;
      }
      if (result != S_OK)
        throw CSystemException(result);
      if (stat.NumFolders != 0)
        stdStream << "Folders: " << stat.NumFolders << endl;
      if (stat.NumFiles != 1 || stat.NumFolders != 0)
          stdStream << "Files: " << stat.NumFiles << endl;
      stdStream 
           << "Size:       " << stat.UnpackSize << endl
           << "Compressed: " << stat.PackSize << endl;
    }
    else
    {
      UInt64 numErrors = 0;
      HRESULT result = ListArchives(
          codecs,
          options.ArchivePathsSorted, 
          options.ArchivePathsFullSorted,
          options.WildcardCensor.Pairs.Front().Head, 
          options.EnableHeaders, 
          options.TechMode,
          options.PasswordEnabled, 
          options.Password, numErrors);
      if (numErrors > 0)
      {
        g_StdOut << endl << "Errors: " << numErrors;
        return NExitCode::kFatalError;
      }
      if (result != S_OK)
        throw CSystemException(result);
    }
  }
  else if(options.Command.IsFromUpdateGroup())
  {
    UString workingDir;

    CUpdateOptions &uo = options.UpdateOptions;
    if (uo.SfxMode && uo.SfxModule.IsEmpty())
      uo.SfxModule = kDefaultSfxModule;

    bool passwordIsDefined = 
        options.PasswordEnabled && !options.Password.IsEmpty();

    COpenCallbackConsole openCallback;
    openCallback.OutStream = &stdStream;
    openCallback.PasswordIsDefined = passwordIsDefined;
    openCallback.Password = options.Password;

    CUpdateCallbackConsole callback;
    callback.EnablePercents = options.EnablePercents;
    callback.PasswordIsDefined = passwordIsDefined;
    callback.AskPassword = options.PasswordEnabled && options.Password.IsEmpty();
    callback.Password = options.Password;
    callback.StdOutMode = uo.StdOutMode;
    callback.Init(&stdStream);

    CUpdateErrorInfo errorInfo;

    if (!uo.Init(codecs, options.ArchiveName, options.ArcType))
      throw "Unsupported archive type";
    HRESULT result = UpdateArchive(codecs, 
        options.WildcardCensor, uo, 
        errorInfo, &openCallback, &callback);

    int exitCode = NExitCode::kSuccess;
    if (callback.CantFindFiles.Size() > 0)
    {
      stdStream << endl;
      stdStream << "WARNINGS for files:" << endl << endl;
      int numErrors = callback.CantFindFiles.Size();
      for (int i = 0; i < numErrors; i++)
      {
        stdStream << callback.CantFindFiles[i] << " : ";
        stdStream << NError::MyFormatMessageW(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 += errorInfo.FileName;
        message += L"\n";
      }
      if (!errorInfo.FileName2.IsEmpty())
      {
        message += errorInfo.FileName2;
        message += L"\n";
      }
      if (errorInfo.SystemError != 0)
      {
        message += NError::MyFormatMessageW(errorInfo.SystemError);
        message += L"\n";
      }
      if (!message.IsEmpty())
        stdStream << L"\nError:\n" << message;
      throw CSystemException(result);
    }
    int 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 (int i = 0; i < numErrors; i++)
      {
        stdStream << callback.FailedFiles[i] << " : ";
        stdStream << NError::MyFormatMessageW(callback.FailedCodes[i]) << endl;
      }
      stdStream << "----------------" << endl;
      stdStream << "WARNING: Cannot open " << numErrors << " file";
      if (numErrors > 1)
        stdStream << "s";
      stdStream << endl;
      exitCode = NExitCode::kWarning;
    }
    return exitCode;
  }
  else 
    PrintHelpAndExit(stdStream);
  return 0;
}