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