text: // UpdateCallbackConsole.cpp
  
  #include "StdAfx.h"
  
  #include "../../../Common/IntToString.h"
  
  #include "../../../Windows/ErrorMsg.h"
  
  #ifndef _7ZIP_ST
  #include "../../../Windows/Synchronization.h"
  #endif
  
  #include "ConsoleClose.h"
  #include "UserInputUtils.h"
  #include "UpdateCallbackConsole.h"
  
  using namespace NWindows;
  
  #ifndef _7ZIP_ST
  static NSynchronization::CCriticalSection g_CriticalSection;
  #define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
  #else
  #define MT_LOCK
  #endif
  
  static const wchar_t *kEmptyFileAlias = L"[Content]";
  
  static const char *kOpenArchiveMessage = "Open archive: ";
  static const char *kCreatingArchiveMessage = "Creating archive: ";
  static const char *kUpdatingArchiveMessage = "Updating archive: ";
  static const char *kScanningMessage = "Scanning the drive:";
  
  static const char *kError = "ERROR: ";
  static const char *kWarning = "WARNING: ";
  
  static HRESULT CheckBreak2()
  {
    return NConsoleClose::TestBreakSignal() ? E_ABORT : S_OK;
  }
  
  HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
  HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
  
  void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags);
  
  void Print_ErrorFormatIndex_Warning(CStdOutStream *_so, const CCodecs *codecs, const CArc &arc);
  
  HRESULT CUpdateCallbackConsole::OpenResult(
      const CCodecs *codecs, const CArchiveLink &arcLink,
      const wchar_t *name, HRESULT result)
  {
    ClosePercents2();
  
    FOR_VECTOR (level, arcLink.Arcs)
    {
      const CArc &arc = arcLink.Arcs[level];
      const CArcErrorInfo &er = arc.ErrorInfo;
      
      UInt32 errorFlags = er.GetErrorFlags();
  
      if (errorFlags != 0 || !er.ErrorMessage.IsEmpty())
      {
        if (_se)
        {
          *_se << endl;
          if (level != 0)
            *_se << arc.Path << endl;
        }
        
        if (errorFlags != 0)
        {
          if (_se)
            PrintErrorFlags(*_se, "ERRORS:", errorFlags);
        }
        
        if (!er.ErrorMessage.IsEmpty())
        {
          if (_se)
            *_se << "ERRORS:" << endl << er.ErrorMessage << endl;
        }
        
        if (_se)
        {
          *_se << endl;
          _se->Flush();
        }
      }
      
      UInt32 warningFlags = er.GetWarningFlags();
  
      if (warningFlags != 0 || !er.WarningMessage.IsEmpty())
      {
        if (_so)
        {
          *_so << endl;
          if (level != 0)
            *_so << arc.Path << endl;
        }
        
        if (warningFlags != 0)
        {
          if (_so)
            PrintErrorFlags(*_so, "WARNINGS:", warningFlags);
        }
        
        if (!er.WarningMessage.IsEmpty())
        {
          if (_so)
            *_so << "WARNINGS:" << endl << er.WarningMessage << endl;
        }
        
        if (_so)
        {
          *_so << endl;
          if (NeedFlush)
            _so->Flush();
        }
      }
  
    
      if (er.ErrorFormatIndex >= 0)
      {
        if (_so)
        {
          Print_ErrorFormatIndex_Warning(_so, codecs, arc);
          if (NeedFlush)
            _so->Flush();
        }
      }
    }
  
    if (result == S_OK)
    {
      if (_so)
      {
        RINOK(Print_OpenArchive_Props(*_so, codecs, arcLink));
        *_so << endl;
      }
    }
    else
    {
      if (_so)
        _so->Flush();
      if (_se)
      {
        *_se << kError << name << endl;
        HRESULT res = Print_OpenArchive_Error(*_se, codecs, arcLink);
        RINOK(res);
        _se->Flush();
      }
    }
  
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::StartScanning()
  {
    if (_so)
      *_so << kScanningMessage << endl;
    _percent.Command = "Scan ";
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::ScanProgress(const CDirItemsStat &st, const FString &path, bool /* isDir */)
  {
    if (NeedPercents())
    {
      _percent.Files = st.NumDirs + st.NumFiles + st.NumAltStreams;
      _percent.Completed = st.GetTotalBytes();
      _percent.FileName = fs2us(path);
      _percent.Print();
    }
  
    return CheckBreak();
  }
  
  void CCallbackConsoleBase::CommonError(const FString &path, DWORD systemError, bool isWarning)
  {
    ClosePercents2();
    
    if (_se)
    {
      if (_so)
        _so->Flush();
  
      *_se << endl << (isWarning ? kWarning : kError)
          << NError::MyFormatMessage(systemError)
          << endl << fs2us(path) << endl << endl;
      _se->Flush();
    }
  }
  
  
  HRESULT CCallbackConsoleBase::ScanError_Base(const FString &path, DWORD systemError)
  {
    MT_LOCK
  
    ScanErrors.AddError(path, systemError);
    CommonError(path, systemError, true);
  
    return S_OK;
  }
  
  HRESULT CCallbackConsoleBase::OpenFileError_Base(const FString &path, DWORD systemError)
  {
    MT_LOCK
    FailedFiles.AddError(path, systemError);
    /*
    if (systemError == ERROR_SHARING_VIOLATION)
    {
    */
      CommonError(path, systemError, true);
      return S_FALSE;
    /*
    }
    return systemError;
    */
  }
  
  HRESULT CCallbackConsoleBase::ReadingFileError_Base(const FString &path, DWORD systemError)
  {
    MT_LOCK
    CommonError(path, systemError, false);
    return HRESULT_FROM_WIN32(systemError);
  }
  
  HRESULT CUpdateCallbackConsole::ScanError(const FString &path, DWORD systemError)
  {
    return ScanError_Base(path, systemError);
  }
  
  
  static void PrintPropPair(AString &s, const char *name, UInt64 val)
  {
    char temp[32];
    ConvertUInt64ToString(val, temp);
    s += name;
    s += ": ";
    s += temp;
  }
  
  void PrintSize_bytes_Smart(AString &s, UInt64 val);
  void Print_DirItemsStat(AString &s, const CDirItemsStat &st);
  
  HRESULT CUpdateCallbackConsole::FinishScanning(const CDirItemsStat &st)
  {
    if (NeedPercents())
    {
      _percent.ClosePrint(true);
      _percent.ClearCurState();
    }
  
    if (_so)
    {
      AString s;
      Print_DirItemsStat(s, st);
      *_so << s << endl << endl;
    }
    return S_OK;
  }
  
  static const char *k_StdOut_ArcName = "StdOut";
  
  HRESULT CUpdateCallbackConsole::StartOpenArchive(const wchar_t *name)
  {
    if (_so)
    {
      *_so << kOpenArchiveMessage;
      if (name)
        *_so << name;
      else
        *_so << k_StdOut_ArcName;
      *_so << endl;
    }
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::StartArchive(const wchar_t *name, bool updating)
  {
    if (_so)
    {
      *_so << (updating ? kUpdatingArchiveMessage : kCreatingArchiveMessage);
      if (name != 0)
        *_so << name;
      else
        *_so << k_StdOut_ArcName;
     *_so << endl << endl;
    }
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::FinishArchive(const CFinishArchiveStat &st)
  {
    ClosePercents2();
  
    if (_so)
    {
      AString s;
      // Print_UInt64_and_String(s, _percent.Files == 1 ? "file" : "files", _percent.Files);
      PrintPropPair(s, "Files read from disk", _percent.Files);
      s.Add_LF();
      s += "Archive size: ";
      PrintSize_bytes_Smart(s, st.OutArcFileSize);
      s.Add_LF();
      *_so << endl;
      *_so << s;
      // *_so << endl;
    }
  
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::WriteSfx(const wchar_t *name, UInt64 size)
  {
    if (_so)
    {
      *_so << "Write SFX: ";
      *_so << name;
      AString s = " : ";
      PrintSize_bytes_Smart(s, size);
      *_so << s << endl;
    }
    return S_OK;
  }
  
  
  HRESULT CUpdateCallbackConsole::DeletingAfterArchiving(const FString &path, bool /* isDir */)
  {
    if (LogLevel > 0 && _so)
    {
      ClosePercents_for_so();
        
      if (!DeleteMessageWasShown)
      {
        if (_so)
          *_so << endl << ": Removing files after including to archive" << endl;
      }
     
      {
        {
          _tempA = "Removing";
          _tempA.Add_Space();
          *_so << _tempA;
          _tempU = fs2us(path);
          _so->PrintUString(_tempU, _tempA);
          *_so << endl;
          if (NeedFlush)
            _so->Flush();
        }
      }
    }
  
    if (!DeleteMessageWasShown)
    {
      if (NeedPercents())
      {
        _percent.ClearCurState();
      }
      DeleteMessageWasShown = true;
    }
    else
    {
      _percent.Files++;
    }
  
    if (NeedPercents())
    {
      // if (!FullLog)
      {
        _percent.Command = "Removing";
        _percent.FileName = fs2us(path);
      }
      _percent.Print();
    }
  
    return S_OK;
  }
  
  
  HRESULT CUpdateCallbackConsole::FinishDeletingAfterArchiving()
  {
    ClosePercents2();
    if (_so && DeleteMessageWasShown)
      *_so << endl;
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::CheckBreak()
  {
    return CheckBreak2();
  }
  
  /*
  HRESULT CUpdateCallbackConsole::Finalize()
  {
    // MT_LOCK
    return S_OK;
  }
  */
  
  HRESULT CUpdateCallbackConsole::SetNumItems(UInt64 numItems)
  {
    if (_so)
    {
      ClosePercents_for_so();
      AString s;
      PrintPropPair(s, "Items to compress", numItems);
      *_so << s << endl << endl;
    }
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::SetTotal(UInt64 size)
  {
    MT_LOCK
    if (NeedPercents())
    {
      _percent.Total = size;
      _percent.Print();
    }
    return S_OK;
  }
  
  HRESULT CUpdateCallbackConsole::SetCompleted(const UInt64 *completeValue)
  {
    MT_LOCK
    if (completeValue)
    {
      if (NeedPercents())
      {
        _percent.Completed = *completeValue;
        _percent.Print();
      }
    }
    return CheckBreak2();
  }
  
  HRESULT CUpdateCallbackConsole::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 * /* outSize */)
  {
    return CheckBreak2();
  }
  
  HRESULT CCallbackConsoleBase::PrintProgress(const wchar_t *name, const char *command, bool showInLog)
  {
    MT_LOCK
    
    bool show2 = (showInLog && _so);
  
    if (show2)
    {
      ClosePercents_for_so();
      
      _tempA = command;
      if (name)
        _tempA.Add_Space();
      *_so << _tempA;
  
      _tempU.Empty();
      if (name)
        _tempU = name;
      _so->PrintUString(_tempU, _tempA);
      *_so << endl;
      if (NeedFlush)
        _so->Flush();
    }
  
    if (NeedPercents())
    {
      if (PercentsNameLevel >= 1)
      {
        _percent.FileName.Empty();
        _percent.Command.Empty();
        if (PercentsNameLevel > 1 || !show2)
        {
          _percent.Command = command;
          if (name)
            _percent.FileName = name;
        }
      }
      _percent.Print();
    }
    
    return CheckBreak2();
  }
  
  HRESULT CUpdateCallbackConsole::GetStream(const wchar_t *name, bool /* isDir */, bool isAnti, UInt32 mode)
  {
    if (StdOutMode)
      return S_OK;
    
    if (!name || name[0] == 0)
      name = kEmptyFileAlias;
  
    unsigned requiredLevel = 1;
    
    const char *s;
    if (mode == NUpdateNotifyOp::kAdd ||
        mode == NUpdateNotifyOp::kUpdate)
    {
      if (isAnti)
        s = "Anti";
      else if (mode == NUpdateNotifyOp::kAdd)
        s = "+";
      else
        s = "U";
    }
    else
    {
      requiredLevel = 3;
      if (mode == NUpdateNotifyOp::kAnalyze)
        s = "A";
      else
        s = "Reading";
    }
    
    return PrintProgress(name, s, LogLevel >= requiredLevel);
  }
  
  HRESULT CUpdateCallbackConsole::OpenFileError(const FString &path, DWORD systemError)
  {
    return OpenFileError_Base(path, systemError);
  }
  
  HRESULT CUpdateCallbackConsole::ReadingFileError(const FString &path, DWORD systemError)
  {
    return ReadingFileError_Base(path, systemError);
  }
  
  HRESULT CUpdateCallbackConsole::SetOperationResult(Int32)
  {
    MT_LOCK
    _percent.Files++;
    return S_OK;
  }
  
  void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, AString &dest);
  
  HRESULT CUpdateCallbackConsole::ReportExtractResult(Int32 opRes, Int32 isEncrypted, const wchar_t *name)
  {
    // if (StdOutMode) return S_OK;
  
    if (opRes != NArchive::NExtract::NOperationResult::kOK)
    {
      ClosePercents2();
      
      if (_se)
      {
        if (_so)
          _so->Flush();
  
        AString s;
        SetExtractErrorMessage(opRes, isEncrypted, s);
        *_se << s << " : " << endl << name << endl << endl;
        _se->Flush();
      }
      return S_OK;
    }
    return S_OK;
  }
  
  
  HRESULT CUpdateCallbackConsole::ReportUpdateOpeartion(UInt32 op, const wchar_t *name, bool /* isDir */)
  {
    // if (StdOutMode) return S_OK;
  
    char temp[16];
    const char *s;
    
    unsigned requiredLevel = 1;
    
    switch (op)
    {
      case NUpdateNotifyOp::kAdd:       s = "+"; break;
      case NUpdateNotifyOp::kUpdate:    s = "U"; break;
      case NUpdateNotifyOp::kAnalyze:   s = "A"; requiredLevel = 3; break;
      case NUpdateNotifyOp::kReplicate: s = "="; requiredLevel = 3; break;
      case NUpdateNotifyOp::kRepack:    s = "R"; requiredLevel = 2; break;
      case NUpdateNotifyOp::kSkip:      s = "."; requiredLevel = 2; break;
      case NUpdateNotifyOp::kDelete:    s = "D"; requiredLevel = 3; break;
      case NUpdateNotifyOp::kHeader:    s = "Header creation"; requiredLevel = 100; break;
      default:
      {
        temp[0] = 'o';
        temp[1] = 'p';
        ConvertUInt64ToString(op, temp + 2);
        s = temp;
      }
    }
  
    return PrintProgress(name, s, LogLevel >= requiredLevel);
  }
  
  /*
  HRESULT CUpdateCallbackConsole::SetPassword(const UString &
      #ifndef _NO_CRYPTO
      password
      #endif
      )
  {
    #ifndef _NO_CRYPTO
    PasswordIsDefined = true;
    Password = password;
    #endif
    return S_OK;
  }
  */
  
  HRESULT CUpdateCallbackConsole::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
  {
    COM_TRY_BEGIN
  
    *password = NULL;
  
    #ifdef _NO_CRYPTO
  
    *passwordIsDefined = false;
    return S_OK;
    
    #else
    
    if (!PasswordIsDefined)
    {
      if (AskPassword)
      {
        Password = GetPassword(_so);
        PasswordIsDefined = true;
      }
    }
    *passwordIsDefined = BoolToInt(PasswordIsDefined);
    return StringToBstr(Password, password);
    
    #endif
  
    COM_TRY_END
  }
  
  HRESULT CUpdateCallbackConsole::CryptoGetTextPassword(BSTR *password)
  {
    COM_TRY_BEGIN
    
    *password = NULL;
  
    #ifdef _NO_CRYPTO
  
    return E_NOTIMPL;
    
    #else
    
    if (!PasswordIsDefined)
    {
      {
        Password = GetPassword(_so);
        PasswordIsDefined = true;
      }
    }
    return StringToBstr(Password, password);
    
    #endif
    COM_TRY_END
  }
  
  HRESULT CUpdateCallbackConsole::ShowDeleteFile(const wchar_t *name, bool /* isDir */)
  {
    if (StdOutMode)
      return S_OK;
    
    if (LogLevel > 7)
    {
      if (!name || name[0] == 0)
        name = kEmptyFileAlias;
      return PrintProgress(name, "D", true);
    }
    return S_OK;
  }
  