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