1// ExtractCallbackConsole.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/IntToString.h"
6#include "../../../Common/Wildcard.h"
7
8#include "../../../Windows/FileDir.h"
9#include "../../../Windows/FileFind.h"
10#include "../../../Windows/TimeUtils.h"
11#include "../../../Windows/ErrorMsg.h"
12#include "../../../Windows/PropVariantConv.h"
13
14#ifndef Z7_ST
15#include "../../../Windows/Synchronization.h"
16#endif
17
18#include "../../Common/FilePathAutoRename.h"
19
20#include "../Common/ExtractingFilePath.h"
21
22#include "ConsoleClose.h"
23#include "ExtractCallbackConsole.h"
24#include "UserInputUtils.h"
25
26using namespace NWindows;
27using namespace NFile;
28using namespace NDir;
29
30static HRESULT CheckBreak2()
31{
32  return NConsoleClose::TestBreakSignal() ? E_ABORT : S_OK;
33}
34
35static const char * const kError = "ERROR: ";
36
37
38void CExtractScanConsole::StartScanning()
39{
40  if (NeedPercents())
41    _percent.Command = "Scan";
42}
43
44HRESULT CExtractScanConsole::ScanProgress(const CDirItemsStat &st, const FString &path, bool /* isDir */)
45{
46  if (NeedPercents())
47  {
48    _percent.Files = st.NumDirs + st.NumFiles;
49    _percent.Completed = st.GetTotalBytes();
50    _percent.FileName = fs2us(path);
51    _percent.Print();
52  }
53
54  return CheckBreak2();
55}
56
57HRESULT CExtractScanConsole::ScanError(const FString &path, DWORD systemError)
58{
59  // 22.00:
60  // ScanErrors.AddError(path, systemError);
61
62  ClosePercentsAndFlush();
63
64  if (_se)
65  {
66    *_se << endl << kError << NError::MyFormatMessage(systemError) << endl;
67    _se->NormalizePrint_UString(fs2us(path));
68    *_se << endl << endl;
69    _se->Flush();
70  }
71  return HRESULT_FROM_WIN32(systemError);
72
73  // 22.00: commented
74  // CommonError(path, systemError, true);
75  // return S_OK;
76}
77
78
79void Print_UInt64_and_String(AString &s, UInt64 val, const char *name);
80void Print_UInt64_and_String(AString &s, UInt64 val, const char *name)
81{
82  char temp[32];
83  ConvertUInt64ToString(val, temp);
84  s += temp;
85  s.Add_Space();
86  s += name;
87}
88
89void PrintSize_bytes_Smart(AString &s, UInt64 val);
90void PrintSize_bytes_Smart(AString &s, UInt64 val)
91{
92  Print_UInt64_and_String(s, val, "bytes");
93
94  if (val == 0)
95    return;
96
97  unsigned numBits = 10;
98  char c = 'K';
99  char temp[4] = { 'K', 'i', 'B', 0 };
100       if (val >= ((UInt64)10 << 30)) { numBits = 30; c = 'G'; }
101  else if (val >= ((UInt64)10 << 20)) { numBits = 20; c = 'M'; }
102  temp[0] = c;
103  s += " (";
104  Print_UInt64_and_String(s, ((val + ((UInt64)1 << numBits) - 1) >> numBits), temp);
105  s += ')';
106}
107
108static void PrintSize_bytes_Smart_comma(AString &s, UInt64 val)
109{
110  if (val == (UInt64)(Int64)-1)
111    return;
112  s += ", ";
113  PrintSize_bytes_Smart(s, val);
114}
115
116
117
118void Print_DirItemsStat(AString &s, const CDirItemsStat &st);
119void Print_DirItemsStat(AString &s, const CDirItemsStat &st)
120{
121  if (st.NumDirs != 0)
122  {
123    Print_UInt64_and_String(s, st.NumDirs, st.NumDirs == 1 ? "folder" : "folders");
124    s += ", ";
125  }
126  Print_UInt64_and_String(s, st.NumFiles, st.NumFiles == 1 ? "file" : "files");
127  PrintSize_bytes_Smart_comma(s, st.FilesSize);
128  if (st.NumAltStreams != 0)
129  {
130    s.Add_LF();
131    Print_UInt64_and_String(s, st.NumAltStreams, "alternate streams");
132    PrintSize_bytes_Smart_comma(s, st.AltStreamsSize);
133  }
134}
135
136
137void Print_DirItemsStat2(AString &s, const CDirItemsStat2 &st);
138void Print_DirItemsStat2(AString &s, const CDirItemsStat2 &st)
139{
140  Print_DirItemsStat(s, (CDirItemsStat &)st);
141  bool needLF = true;
142  if (st.Anti_NumDirs != 0)
143  {
144    if (needLF)
145      s.Add_LF();
146    needLF = false;
147    Print_UInt64_and_String(s, st.Anti_NumDirs, st.Anti_NumDirs == 1 ? "anti-folder" : "anti-folders");
148  }
149  if (st.Anti_NumFiles != 0)
150  {
151    if (needLF)
152      s.Add_LF();
153    else
154      s += ", ";
155    needLF = false;
156    Print_UInt64_and_String(s, st.Anti_NumFiles, st.Anti_NumFiles == 1 ? "anti-file" : "anti-files");
157  }
158  if (st.Anti_NumAltStreams != 0)
159  {
160    if (needLF)
161      s.Add_LF();
162    else
163      s += ", ";
164    needLF = false;
165    Print_UInt64_and_String(s, st.Anti_NumAltStreams, "anti-alternate-streams");
166  }
167}
168
169
170void CExtractScanConsole::PrintStat(const CDirItemsStat &st)
171{
172  if (_so)
173  {
174    AString s;
175    Print_DirItemsStat(s, st);
176    *_so << s << endl;
177  }
178}
179
180
181
182
183
184
185
186#ifndef Z7_ST
187static NSynchronization::CCriticalSection g_CriticalSection;
188#define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
189#else
190#define MT_LOCK
191#endif
192
193
194static const char * const kTestString    =  "T";
195static const char * const kExtractString =  "-";
196static const char * const kSkipString    =  ".";
197static const char * const kReadString    =  "H";
198
199// static const char * const kCantAutoRename = "cannot create file with auto name\n";
200// static const char * const kCantRenameFile = "cannot rename existing file\n";
201// static const char * const kCantDeleteOutputFile = "cannot delete output file ";
202
203static const char * const kMemoryExceptionMessage = "Can't allocate required memory!";
204
205static const char * const kExtracting = "Extracting archive: ";
206static const char * const kTesting = "Testing archive: ";
207
208static const char * const kEverythingIsOk = "Everything is Ok";
209static const char * const kNoFiles = "No files to process";
210
211static const char * const kUnsupportedMethod = "Unsupported Method";
212static const char * const kCrcFailed = "CRC Failed";
213static const char * const kCrcFailedEncrypted = "CRC Failed in encrypted file. Wrong password?";
214static const char * const kDataError = "Data Error";
215static const char * const kDataErrorEncrypted = "Data Error in encrypted file. Wrong password?";
216static const char * const kUnavailableData = "Unavailable data";
217static const char * const kUnexpectedEnd = "Unexpected end of data";
218static const char * const kDataAfterEnd = "There are some data after the end of the payload data";
219static const char * const kIsNotArc = "Is not archive";
220static const char * const kHeadersError = "Headers Error";
221static const char * const kWrongPassword = "Wrong password";
222
223static const char * const k_ErrorFlagsMessages[] =
224{
225    "Is not archive"
226  , "Headers Error"
227  , "Headers Error in encrypted archive. Wrong password?"
228  , "Unavailable start of archive"
229  , "Unconfirmed start of archive"
230  , "Unexpected end of archive"
231  , "There are data after the end of archive"
232  , "Unsupported method"
233  , "Unsupported feature"
234  , "Data Error"
235  , "CRC Error"
236};
237
238Z7_COM7F_IMF(CExtractCallbackConsole::SetTotal(UInt64 size))
239{
240  MT_LOCK
241
242  if (NeedPercents())
243  {
244    _percent.Total = size;
245    _percent.Print();
246  }
247  return CheckBreak2();
248}
249
250Z7_COM7F_IMF(CExtractCallbackConsole::SetCompleted(const UInt64 *completeValue))
251{
252  MT_LOCK
253
254  if (NeedPercents())
255  {
256    if (completeValue)
257      _percent.Completed = *completeValue;
258    _percent.Print();
259  }
260  return CheckBreak2();
261}
262
263static const char * const kTab = "  ";
264
265static void PrintFileInfo(CStdOutStream *_so, const wchar_t *path, const FILETIME *ft, const UInt64 *size)
266{
267  *_so << kTab << "Path:     ";
268  _so->NormalizePrint_wstr(path);
269  *_so << endl;
270  if (size && *size != (UInt64)(Int64)-1)
271  {
272    AString s;
273    PrintSize_bytes_Smart(s, *size);
274    *_so << kTab << "Size:     " << s << endl;
275  }
276  if (ft)
277  {
278    char temp[64];
279    if (ConvertUtcFileTimeToString(*ft, temp, kTimestampPrintLevel_SEC))
280      *_so << kTab << "Modified: " << temp << endl;
281  }
282}
283
284Z7_COM7F_IMF(CExtractCallbackConsole::AskOverwrite(
285    const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize,
286    const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize,
287    Int32 *answer))
288{
289  MT_LOCK
290
291  RINOK(CheckBreak2())
292
293  ClosePercentsAndFlush();
294
295  if (_so)
296  {
297    *_so << endl << "Would you like to replace the existing file:\n";
298    PrintFileInfo(_so, existName, existTime, existSize);
299    *_so << "with the file from archive:\n";
300    PrintFileInfo(_so, newName, newTime, newSize);
301  }
302
303  NUserAnswerMode::EEnum overwriteAnswer = ScanUserYesNoAllQuit(_so);
304
305  switch ((int)overwriteAnswer)
306  {
307    case NUserAnswerMode::kQuit:  return E_ABORT;
308    case NUserAnswerMode::kNo:     *answer = NOverwriteAnswer::kNo; break;
309    case NUserAnswerMode::kNoAll:  *answer = NOverwriteAnswer::kNoToAll; break;
310    case NUserAnswerMode::kYesAll: *answer = NOverwriteAnswer::kYesToAll; break;
311    case NUserAnswerMode::kYes:    *answer = NOverwriteAnswer::kYes; break;
312    case NUserAnswerMode::kAutoRenameAll: *answer = NOverwriteAnswer::kAutoRename; break;
313    case NUserAnswerMode::kEof:  return E_ABORT;
314    case NUserAnswerMode::kError:  return E_FAIL;
315    default: return E_FAIL;
316  }
317
318  if (_so)
319  {
320    *_so << endl;
321    if (NeedFlush)
322      _so->Flush();
323  }
324
325  return CheckBreak2();
326}
327
328Z7_COM7F_IMF(CExtractCallbackConsole::PrepareOperation(const wchar_t *name, Int32 isFolder, Int32 askExtractMode, const UInt64 *position))
329{
330  MT_LOCK
331
332  _currentName = name;
333
334  const char *s;
335  unsigned requiredLevel = 1;
336
337  switch (askExtractMode)
338  {
339    case NArchive::NExtract::NAskMode::kExtract: s = kExtractString; break;
340    case NArchive::NExtract::NAskMode::kTest:    s = kTestString; break;
341    case NArchive::NExtract::NAskMode::kSkip:    s = kSkipString; requiredLevel = 2; break;
342    case NArchive::NExtract::NAskMode::kReadExternal: s = kReadString; requiredLevel = 0; break;
343    default: s = "???"; requiredLevel = 2;
344  }
345
346  bool show2 = (LogLevel >= requiredLevel && _so);
347
348  if (show2)
349  {
350    ClosePercents_for_so();
351
352    _tempA = s;
353    if (name)
354      _tempA.Add_Space();
355    *_so << _tempA;
356
357    _tempU.Empty();
358    if (name)
359    {
360      _tempU = name;
361      _so->Normalize_UString(_tempU);
362      // 21.04
363      if (isFolder)
364      {
365        if (!_tempU.IsEmpty() && _tempU.Back() != WCHAR_PATH_SEPARATOR)
366          _tempU.Add_PathSepar();
367      }
368    }
369    _so->PrintUString(_tempU, _tempA);
370    if (position)
371      *_so << " <" << *position << ">";
372    *_so << endl;
373
374    if (NeedFlush)
375      _so->Flush();
376  }
377
378  if (NeedPercents())
379  {
380    if (PercentsNameLevel >= 1)
381    {
382      _percent.FileName.Empty();
383      _percent.Command.Empty();
384      if (PercentsNameLevel > 1 || !show2)
385      {
386        _percent.Command = s;
387        if (name)
388          _percent.FileName = name;
389      }
390    }
391    _percent.Print();
392  }
393
394  return CheckBreak2();
395}
396
397Z7_COM7F_IMF(CExtractCallbackConsole::MessageError(const wchar_t *message))
398{
399  MT_LOCK
400
401  RINOK(CheckBreak2())
402
403  NumFileErrors_in_Current++;
404  NumFileErrors++;
405
406  ClosePercentsAndFlush();
407  if (_se)
408  {
409    *_se << kError << message << endl;
410    _se->Flush();
411  }
412
413  return CheckBreak2();
414}
415
416void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, AString &dest);
417void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, AString &dest)
418{
419  dest.Empty();
420    const char *s = NULL;
421
422    switch (opRes)
423    {
424      case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
425        s = kUnsupportedMethod;
426        break;
427      case NArchive::NExtract::NOperationResult::kCRCError:
428        s = (encrypted ? kCrcFailedEncrypted : kCrcFailed);
429        break;
430      case NArchive::NExtract::NOperationResult::kDataError:
431        s = (encrypted ? kDataErrorEncrypted : kDataError);
432        break;
433      case NArchive::NExtract::NOperationResult::kUnavailable:
434        s = kUnavailableData;
435        break;
436      case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
437        s = kUnexpectedEnd;
438        break;
439      case NArchive::NExtract::NOperationResult::kDataAfterEnd:
440        s = kDataAfterEnd;
441        break;
442      case NArchive::NExtract::NOperationResult::kIsNotArc:
443        s = kIsNotArc;
444        break;
445      case NArchive::NExtract::NOperationResult::kHeadersError:
446        s = kHeadersError;
447        break;
448      case NArchive::NExtract::NOperationResult::kWrongPassword:
449        s = kWrongPassword;
450        break;
451    }
452
453    dest += kError;
454    if (s)
455      dest += s;
456    else
457    {
458      dest += "Error #";
459      dest.Add_UInt32((UInt32)opRes);
460    }
461}
462
463Z7_COM7F_IMF(CExtractCallbackConsole::SetOperationResult(Int32 opRes, Int32 encrypted))
464{
465  MT_LOCK
466
467  if (opRes == NArchive::NExtract::NOperationResult::kOK)
468  {
469    if (NeedPercents())
470    {
471      _percent.Command.Empty();
472      _percent.FileName.Empty();
473      _percent.Files++;
474    }
475  }
476  else
477  {
478    NumFileErrors_in_Current++;
479    NumFileErrors++;
480
481    if (_se)
482    {
483      ClosePercentsAndFlush();
484
485      AString s;
486      SetExtractErrorMessage(opRes, encrypted, s);
487
488      *_se << s;
489      if (!_currentName.IsEmpty())
490      {
491        *_se << " : ";
492        _se->NormalizePrint_UString(_currentName);
493      }
494      *_se << endl;
495      _se->Flush();
496    }
497  }
498
499  return CheckBreak2();
500}
501
502Z7_COM7F_IMF(CExtractCallbackConsole::ReportExtractResult(Int32 opRes, Int32 encrypted, const wchar_t *name))
503{
504  if (opRes != NArchive::NExtract::NOperationResult::kOK)
505  {
506    _currentName = name;
507    return SetOperationResult(opRes, encrypted);
508  }
509
510  return CheckBreak2();
511}
512
513
514
515#ifndef Z7_NO_CRYPTO
516
517HRESULT CExtractCallbackConsole::SetPassword(const UString &password)
518{
519  PasswordIsDefined = true;
520  Password = password;
521  return S_OK;
522}
523
524Z7_COM7F_IMF(CExtractCallbackConsole::CryptoGetTextPassword(BSTR *password))
525{
526  COM_TRY_BEGIN
527  MT_LOCK
528  return Open_CryptoGetTextPassword(password);
529  COM_TRY_END
530}
531
532#endif
533
534HRESULT CExtractCallbackConsole::BeforeOpen(const wchar_t *name, bool testMode)
535{
536  RINOK(CheckBreak2())
537
538  NumTryArcs++;
539  ThereIsError_in_Current = false;
540  ThereIsWarning_in_Current = false;
541  NumFileErrors_in_Current = 0;
542
543  ClosePercents_for_so();
544  if (_so)
545  {
546    *_so << endl << (testMode ? kTesting : kExtracting);
547    _so->NormalizePrint_wstr(name);
548    *_so << endl;
549  }
550
551  if (NeedPercents())
552    _percent.Command = "Open";
553  return S_OK;
554}
555
556HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
557HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
558
559static AString GetOpenArcErrorMessage(UInt32 errorFlags)
560{
561  AString s;
562
563  for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_ErrorFlagsMessages); i++)
564  {
565    UInt32 f = (1 << i);
566    if ((errorFlags & f) == 0)
567      continue;
568    const char *m = k_ErrorFlagsMessages[i];
569    if (!s.IsEmpty())
570      s.Add_LF();
571    s += m;
572    errorFlags &= ~f;
573  }
574
575  if (errorFlags != 0)
576  {
577    char sz[16];
578    sz[0] = '0';
579    sz[1] = 'x';
580    ConvertUInt32ToHex(errorFlags, sz + 2);
581    if (!s.IsEmpty())
582      s.Add_LF();
583    s += sz;
584  }
585
586  return s;
587}
588
589void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags);
590void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags)
591{
592  if (errorFlags == 0)
593    return;
594  so << s << endl << GetOpenArcErrorMessage(errorFlags) << endl;
595}
596
597static void Add_Messsage_Pre_ArcType(UString &s, const char *pre, const wchar_t *arcType)
598{
599  s.Add_LF();
600  s += pre;
601  s += " as [";
602  s += arcType;
603  s += "] archive";
604}
605
606void Print_ErrorFormatIndex_Warning(CStdOutStream *_so, const CCodecs *codecs, const CArc &arc);
607void Print_ErrorFormatIndex_Warning(CStdOutStream *_so, const CCodecs *codecs, const CArc &arc)
608{
609  const CArcErrorInfo &er = arc.ErrorInfo;
610
611  *_so << "WARNING:\n";
612  _so->NormalizePrint_UString(arc.Path);
613  UString s;
614  if (arc.FormatIndex == er.ErrorFormatIndex)
615  {
616    s.Add_LF();
617    s += "The archive is open with offset";
618  }
619  else
620  {
621    Add_Messsage_Pre_ArcType(s, "Cannot open the file", codecs->GetFormatNamePtr(er.ErrorFormatIndex));
622    Add_Messsage_Pre_ArcType(s, "The file is open", codecs->GetFormatNamePtr(arc.FormatIndex));
623  }
624
625  *_so << s << endl << endl;
626}
627
628
629HRESULT CExtractCallbackConsole::OpenResult(
630    const CCodecs *codecs, const CArchiveLink &arcLink,
631    const wchar_t *name, HRESULT result)
632{
633  ClosePercents();
634
635  if (NeedPercents())
636  {
637    _percent.Files = 0;
638    _percent.Command.Empty();
639    _percent.FileName.Empty();
640  }
641
642
643  ClosePercentsAndFlush();
644
645  FOR_VECTOR (level, arcLink.Arcs)
646  {
647    const CArc &arc = arcLink.Arcs[level];
648    const CArcErrorInfo &er = arc.ErrorInfo;
649
650    UInt32 errorFlags = er.GetErrorFlags();
651
652    if (errorFlags != 0 || !er.ErrorMessage.IsEmpty())
653    {
654      if (_se)
655      {
656        *_se << endl;
657        if (level != 0)
658        {
659          _se->NormalizePrint_UString(arc.Path);
660          *_se << endl;
661        }
662      }
663
664      if (errorFlags != 0)
665      {
666        if (_se)
667          PrintErrorFlags(*_se, "ERRORS:", errorFlags);
668        NumOpenArcErrors++;
669        ThereIsError_in_Current = true;
670      }
671
672      if (!er.ErrorMessage.IsEmpty())
673      {
674        if (_se)
675          *_se << "ERRORS:" << endl << er.ErrorMessage << endl;
676        NumOpenArcErrors++;
677        ThereIsError_in_Current = true;
678      }
679
680      if (_se)
681      {
682        *_se << endl;
683        _se->Flush();
684      }
685    }
686
687    UInt32 warningFlags = er.GetWarningFlags();
688
689    if (warningFlags != 0 || !er.WarningMessage.IsEmpty())
690    {
691      if (_so)
692      {
693        *_so << endl;
694        if (level != 0)
695        {
696          _so->NormalizePrint_UString(arc.Path);
697          *_so << endl;
698        }
699      }
700
701      if (warningFlags != 0)
702      {
703        if (_so)
704          PrintErrorFlags(*_so, "WARNINGS:", warningFlags);
705        NumOpenArcWarnings++;
706        ThereIsWarning_in_Current = true;
707      }
708
709      if (!er.WarningMessage.IsEmpty())
710      {
711        if (_so)
712          *_so << "WARNINGS:" << endl << er.WarningMessage << endl;
713        NumOpenArcWarnings++;
714        ThereIsWarning_in_Current = true;
715      }
716
717      if (_so)
718      {
719        *_so << endl;
720        if (NeedFlush)
721          _so->Flush();
722      }
723    }
724
725
726    if (er.ErrorFormatIndex >= 0)
727    {
728      if (_so)
729      {
730        Print_ErrorFormatIndex_Warning(_so, codecs, arc);
731        if (NeedFlush)
732          _so->Flush();
733      }
734      ThereIsWarning_in_Current = true;
735    }
736  }
737
738  if (result == S_OK)
739  {
740    if (_so)
741    {
742      RINOK(Print_OpenArchive_Props(*_so, codecs, arcLink))
743      *_so << endl;
744    }
745  }
746  else
747  {
748    NumCantOpenArcs++;
749    if (_so)
750      _so->Flush();
751    if (_se)
752    {
753      *_se << kError;
754      _se->NormalizePrint_wstr(name);
755      *_se << endl;
756      const HRESULT res = Print_OpenArchive_Error(*_se, codecs, arcLink);
757      RINOK(res)
758      if (result == S_FALSE)
759      {
760      }
761      else
762      {
763        if (result == E_OUTOFMEMORY)
764          *_se << "Can't allocate required memory";
765        else
766          *_se << NError::MyFormatMessage(result);
767        *_se << endl;
768      }
769      _se->Flush();
770    }
771  }
772
773
774  return CheckBreak2();
775}
776
777HRESULT CExtractCallbackConsole::ThereAreNoFiles()
778{
779  ClosePercents_for_so();
780
781  if (_so)
782  {
783    *_so << endl << kNoFiles << endl;
784    if (NeedFlush)
785      _so->Flush();
786  }
787  return CheckBreak2();
788}
789
790HRESULT CExtractCallbackConsole::ExtractResult(HRESULT result)
791{
792  MT_LOCK
793
794  if (NeedPercents())
795  {
796    _percent.ClosePrint(true);
797    _percent.Command.Empty();
798    _percent.FileName.Empty();
799  }
800
801  if (_so)
802    _so->Flush();
803
804  if (result == S_OK)
805  {
806    if (NumFileErrors_in_Current == 0 && !ThereIsError_in_Current)
807    {
808      if (ThereIsWarning_in_Current)
809        NumArcsWithWarnings++;
810      else
811        NumOkArcs++;
812      if (_so)
813        *_so << kEverythingIsOk << endl;
814    }
815    else
816    {
817      NumArcsWithError++;
818      if (_so)
819      {
820        *_so << endl;
821        if (NumFileErrors_in_Current != 0)
822          *_so << "Sub items Errors: " << NumFileErrors_in_Current << endl;
823      }
824    }
825    if (_so && NeedFlush)
826      _so->Flush();
827  }
828  else
829  {
830    NumArcsWithError++;
831    if (result == E_ABORT
832        || result == HRESULT_FROM_WIN32(ERROR_DISK_FULL)
833        )
834      return result;
835
836    if (_se)
837    {
838      *_se << endl << kError;
839      if (result == E_OUTOFMEMORY)
840        *_se << kMemoryExceptionMessage;
841      else
842        *_se << NError::MyFormatMessage(result);
843      *_se << endl;
844      _se->Flush();
845    }
846  }
847
848  return CheckBreak2();
849}
850