OSDN Git Service

FileTransform: Remove m_subcode from PackingInfo class
[winmerge-jp/winmerge-jp.git] / Src / DiffTextBuffer.cpp
index dae81b7..019b950 100644 (file)
@@ -4,35 +4,31 @@
  * @brief Implementation file for CDiffTextBuffer
  *
  */
-// ID line follows -- this is updated by SVN
-// $Id$
 
 #include "StdAfx.h"
+#include "DiffTextBuffer.h"
+#include <Poco/Exception.h>
 #include "UniFile.h"
 #include "files.h"
-#include "cs2cs.h"
 #include "locality.h"
-#include "coretools.h"
-#include "Merge.h"
+#include "paths.h"
 #include "OptionsDef.h"
+#include "OptionsMgr.h"
 #include "Environment.h"
 #include "MergeLineFlags.h"
 #include "MergeDoc.h"
 #include "FileTransform.h"
 #include "FileTextEncoding.h"
-#include "DiffTextBuffer.h"
+#include "codepage_detect.h"
+#include "TFile.h"
+
+using Poco::Exception;
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
-#undef THIS_FILE
-static char THIS_FILE[] = __FILE__;
 #endif
 
 static bool IsTextFileStylePure(const UniMemFile::txtstats & stats);
-static CString GetLineByteTimeReport(UINT lines, __int64 bytes,
-       const COleDateTime & start);
-static void EscapeControlChars(CString &s);
-static LPCTSTR GetEol(const CString &str);
 static CRLFSTYLE GetTextFileStyle(const UniMemFile::txtstats & stats);
 
 /**
@@ -43,115 +39,38 @@ static CRLFSTYLE GetTextFileStyle(const UniMemFile::txtstats & stats);
 static bool IsTextFileStylePure(const UniMemFile::txtstats & stats)
 {
        int nType = 0;
-       nType += (stats.ncrlfs > 0);
-       nType += (stats.ncrs > 0);
-       nType += (stats.nlfs > 0);
+       if (stats.ncrlfs > 0)
+               nType++;
+       if ( stats.ncrs > 0)
+               nType++;
+       if (stats.nlfs > 0)
+               nType++;
        return (nType <= 1);
 }
 
 /**
- * @brief Return a string giving #lines and #bytes and how much time elapsed.
- * @param [in] lines Count of lines.
- * @param [in] bytes Count of bytes.
- * @param [in] start Time used.
- * @return Formatted string.
- */
-static CString GetLineByteTimeReport(UINT lines, __int64 bytes,
-       const COleDateTime & start)
-{
-       String sLines = locality::NumToLocaleStr((int)lines);
-       String sBytes = locality::NumToLocaleStr(bytes);
-       COleDateTimeSpan duration = COleDateTime::GetCurrentTime() - start;
-       String sMinutes = locality::NumToLocaleStr((int)duration.GetTotalMinutes());
-       CString str;
-       str.Format(_T("%s lines (%s byte) saved in %sm%02ds")
-               , sLines.c_str(), sBytes.c_str(), sMinutes.c_str()
-               , duration.GetSeconds()
-               );
-       return str;
-}
-
-/**
- * @brief Escape control characters.
- * @param [in,out] s Line of text excluding eol chars.
- *
- * @note Escape sequences follow the pattern
- * (leadin character, high nibble, low nibble, leadout character).
- * The leadin character is '\x0F'. The leadout character is a backslash.
- */
-static void EscapeControlChars(CString &s)
-{
-       // Compute buffer length required for escaping
-       int n = s.GetLength();
-       LPCTSTR q = s;
-       int i = n;
-       while (i)
-       {
-               TCHAR c = q[--i];
-               // Is it a control character in the range 0..31 except TAB?
-               if (!(c & ~_T('\x1F')) && c != _T('\t'))
-               {
-                       n += 3; // Need 3 extra characters to escape
-               }
-       }
-       // Reallocate accordingly
-       i = s.GetLength();
-       LPTSTR p = s.GetBufferSetLength(n);
-       // Copy/translate characters starting at end of string
-       while (i)
-       {
-               TCHAR c = p[--i];
-               // Is it a control character in the range 0..31 except TAB?
-               if (!(c & ~_T('\x1F')) && c != _T('\t'))
-               {
-                       // Bitwise OR with 0x100 so _itot() will output 3 hex digits
-                       _itot(0x100 | c, p + n - 4, 16);
-                       // Replace terminating zero with leadout character
-                       p[n - 1] = _T('\\');
-                       // Prepare to replace 1st hex digit with leadin character
-                       c = _T('\x0F');
-                       n -= 3;
-               }
-               p[--n] = c;
-       }
-}
-
-/**
- * @brief Get EOL of the string.
- * This function returns a pointer to the EOL chars in the given string.
- * Behavior is similar to CCrystalTextBuffer::GetLineEol().
- * @param [in] str String whose EOL chars are returned.
- * @return Pointer to string's EOL chars, or empty string if no EOL found.
- */
-static LPCTSTR GetEol(const CString &str)
-{
-       if (str.GetLength()>1 && str[str.GetLength()-2]=='\r' && str[str.GetLength()-1]=='\n')
-               return (LPCTSTR)str + str.GetLength()-2;
-       if (str.GetLength()>0 && (str[str.GetLength()-1]=='\r' || str[str.GetLength()-1]=='\n'))
-               return (LPCTSTR)str + str.GetLength()-1;
-       return _T("");
-}
-
-/**
  * @brief Get file's EOL type.
  * @param [in] stats File's text stats.
  * @return EOL type.
  */
 static CRLFSTYLE GetTextFileStyle(const UniMemFile::txtstats & stats)
 {
-       if (stats.ncrlfs >= stats.nlfs)
+       // Check if file has more than one EOL type.
+       if (!IsTextFileStylePure(stats))
+                       return CRLFSTYLE::MIXED;
+       else if (stats.ncrlfs >= stats.nlfs)
        {
                if (stats.ncrlfs >= stats.ncrs)
-                       return CRLF_STYLE_DOS;
+                       return CRLFSTYLE::DOS;
                else
-                       return CRLF_STYLE_MAC;
+                       return CRLFSTYLE::MAC;
        }
        else
        {
                if (stats.nlfs >= stats.ncrs)
-                       return CRLF_STYLE_UNIX;
+                       return CRLFSTYLE::UNIX;
                else
-                       return CRLF_STYLE_MAC;
+                       return CRLFSTYLE::MAC;
        }
 }
 
@@ -164,6 +83,7 @@ CDiffTextBuffer::CDiffTextBuffer(CMergeDoc * pDoc, int pane)
 : m_pOwnerDoc(pDoc)
 , m_nThisPane(pane)
 , m_unpackerSubcode(0)
+, m_bMixedEOL(false)
 {
 }
 
@@ -172,28 +92,29 @@ CDiffTextBuffer::CDiffTextBuffer(CMergeDoc * pDoc, int pane)
  * @param [in] nLineIndex Index of the line to get.
  * @param [out] strLine Returns line text in the index.
  */
-BOOL CDiffTextBuffer::GetLine(int nLineIndex, CString &strLine)
+bool CDiffTextBuffer::GetLine(int nLineIndex, CString &strLine) const
 {
        int nLineLength = CCrystalTextBuffer::GetLineLength(nLineIndex);
        if (nLineLength < 0)
-               return FALSE;
+               return false;
        else if (nLineLength == 0)
                strLine.Empty();
        else
        {
-               _tcsncpy(strLine.GetBuffer(nLineLength + 1),
+               _tcsncpy_s(strLine.GetBuffer(nLineLength + 1), nLineLength + 1,
                        CCrystalTextBuffer::GetLineChars(nLineIndex), nLineLength);
                strLine.ReleaseBuffer(nLineLength);
        }
-       return TRUE;
+       return true;
 }
 
 /**
  * @brief Set the buffer modified status.
- * @param [in] bModified New modified status, TRUE if buffer has been
+ * @param [in] bModified New modified status, true if buffer has been
  *   modified since last saving.
  */
-void CDiffTextBuffer::SetModified(BOOL bModified /*= TRUE*/)
+void CDiffTextBuffer::                 /* virtual override */
+SetModified(bool bModified /*= true*/) 
 {
        CCrystalTextBuffer::SetModified (bModified);
        m_pOwnerDoc->SetModifiedFlag (bModified);
@@ -207,40 +128,42 @@ void CDiffTextBuffer::SetModified(BOOL bModified /*= TRUE*/)
  * @param [out] strLine Returns line text in the index. Existing content
  * of this string is overwritten.
  */
-BOOL CDiffTextBuffer::GetFullLine(int nLineIndex, CString &strLine)
+bool CDiffTextBuffer::GetFullLine(int nLineIndex, CString &strLine) const
 {
        int cchText = GetFullLineLength(nLineIndex);
        if (cchText == 0)
        {
                strLine.Empty();
-               return FALSE;
+               return false;
        }
        LPTSTR pchText = strLine.GetBufferSetLength(cchText);
        memcpy(pchText, GetLineChars(nLineIndex), cchText * sizeof(TCHAR));
-       return TRUE;
+       return true;
 }
 
-void CDiffTextBuffer::AddUndoRecord(BOOL bInsert, const CPoint & ptStartPos,
-               const CPoint & ptEndPos, LPCTSTR pszText, int cchText,
-               int nLinesToValidate, int nActionType /*= CE_ACTION_UNKNOWN*/,
-               CDWordArray *paSavedRevisonNumbers)
+void CDiffTextBuffer::                 /* virtual override */
+AddUndoRecord(bool bInsert, const CPoint & ptStartPos,
+               const CPoint & ptEndPos, LPCTSTR pszText, size_t cchText,
+               int nActionType /*= CE_ACTION_UNKNOWN*/,
+               CDWordArray *paSavedRevisionNumbers /*= nullptr*/)
 {
        CGhostTextBuffer::AddUndoRecord(bInsert, ptStartPos, ptEndPos, pszText,
-               cchText, nLinesToValidate, nActionType, paSavedRevisonNumbers);
+               cchText, nActionType, paSavedRevisionNumbers);
        if (m_aUndoBuf[m_nUndoPosition - 1].m_dwFlags & UNDO_BEGINGROUP)
        {
                m_pOwnerDoc->undoTgt.erase(m_pOwnerDoc->curUndo, m_pOwnerDoc->undoTgt.end());
-               m_pOwnerDoc->undoTgt.push_back(m_pOwnerDoc->GetView(m_nThisPane));
+               m_pOwnerDoc->undoTgt.push_back(m_nThisPane);
                m_pOwnerDoc->curUndo = m_pOwnerDoc->undoTgt.end();
        }
 }
+
 /**
  * @brief Checks if a flag is set for line.
  * @param [in] line Index (0-based) for line.
  * @param [in] flag Flag to check.
- * @return TRUE if flag is set, FALSE otherwise.
+ * @return true if flag is set, false otherwise.
  */
-BOOL CDiffTextBuffer::FlagIsSet(UINT line, DWORD flag)
+bool CDiffTextBuffer::FlagIsSet(UINT line, DWORD flag) const
 {
        return ((m_aLines[line].m_dwFlags & flag) == flag);
 }
@@ -257,47 +180,31 @@ void CDiffTextBuffer::prepareForRescan()
        RemoveAllGhostLines();
        for (int ct = GetLineCount() - 1; ct >= 0; --ct)
        {
-               SetLineFlag(ct, LF_DIFF, FALSE, FALSE, FALSE);
-               SetLineFlag(ct, LF_TRIVIAL, FALSE, FALSE, FALSE);
-               SetLineFlag(ct, LF_MOVED, FALSE, FALSE, FALSE);
+               SetLineFlag(ct, 
+                       LF_INVISIBLE | LF_DIFF | LF_TRIVIAL | LF_MOVED | LF_SNP,
+                       false, false, false);
        }
 }
 
 /** 
  * @brief Called when line has been edited.
  * After editing a line, we don't know if there is a diff or not.
- * So we clear the LF_DIFF flag (and it is more easy to read during edition).
+ * So we clear the LF_DIFF flag (and it is more easy to read during editing).
  * Rescan will set the proper color.
  * @param [in] nLine Line that has been edited.
  */
 
-void CDiffTextBuffer::OnNotifyLineHasBeenEdited(int nLine)
+void CDiffTextBuffer::                 /* virtual override */
+OnNotifyLineHasBeenEdited(int nLine)
 {
-       SetLineFlag(nLine, LF_DIFF, FALSE, FALSE, FALSE);
-       SetLineFlag(nLine, LF_TRIVIAL, FALSE, FALSE, FALSE);
-       SetLineFlag(nLine, LF_MOVED, FALSE, FALSE, FALSE);
+       SetLineFlag(nLine, LF_DIFF, false, false, false);
+       SetLineFlag(nLine, LF_TRIVIAL, false, false, false);
+       SetLineFlag(nLine, LF_MOVED, false, false, false);
+       SetLineFlag(nLine, LF_SNP, false, false, false);
        CGhostTextBuffer::OnNotifyLineHasBeenEdited(nLine);
 }
 
 /**
- * @brief Set the folder for temp files.
- * @param [in] path Temp files folder.
- */
-void CDiffTextBuffer::SetTempPath(const String &path)
-{
-       m_strTempPath = path;
-}
-
-/**
- * @brief Is the buffer initialized?
- * @return TRUE if the buffer is initialized, FALSE otherwise.
- */
-bool CDiffTextBuffer::IsInitialized() const
-{
-       return !!m_bInit;
-}
-
-/**
  * @brief Load file from disk into buffer
  *
  * @param [in] pszFileNameInit File to load
@@ -315,48 +222,37 @@ bool CDiffTextBuffer::IsInitialized() const
  * @note If this method fails, it calls InitNew so the CDiffTextBuffer is in a valid state
  */
 int CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit,
-               PackingInfo * infoUnpacker, LPCTSTR sToFindUnpacker, BOOL & readOnly,
+               PackingInfo * infoUnpacker, LPCTSTR sToFindUnpacker, bool & readOnly,
                CRLFSTYLE nCrlfStyle, const FileTextEncoding & encoding, CString &sError)
 {
        ASSERT(!m_bInit);
-       ASSERT(m_aLines.GetSize() == 0);
+       ASSERT(m_aLines.size() == 0);
 
        // Unpacking the file here, save the result in a temporary file
-       String sFileName(pszFileNameInit);
-       if (infoUnpacker->bToBeScanned)
+       m_strTempFileName = pszFileNameInit;
+       if (!FileTransform::Unpacking(infoUnpacker, &m_unpackerSubcode, m_strTempFileName, sToFindUnpacker))
        {
-               if (!FileTransform_Unpacking(sFileName, sToFindUnpacker, infoUnpacker,
-                       &m_unpackerSubcode))
-               {
-                       InitNew(); // leave crystal editor in valid, empty state
-                       return FileLoadResult::FRESULT_ERROR_UNPACK;
-               }
-       }
-       else
-       {
-               if (!FileTransform_Unpacking(sFileName, infoUnpacker, &m_unpackerSubcode))
-               {
-                       InitNew(); // leave crystal editor in valid, empty state
-                       return FileLoadResult::FRESULT_ERROR_UNPACK;
-               }
+               InitNew(); // leave crystal editor in valid, empty state
+               return FileLoadResult::FRESULT_ERROR_UNPACK;
        }
+
        // we use the same unpacker for both files, so it must be defined after first file
-       ASSERT(infoUnpacker->bToBeScanned != PLUGIN_AUTO);
+       ASSERT(infoUnpacker->m_PluginOrPredifferMode != PLUGIN_MODE::PLUGIN_AUTO);
        // we will load the transformed file
-       LPCTSTR pszFileName = sFileName.c_str();
+       LPCTSTR pszFileName = m_strTempFileName.c_str();
 
        String sExt;
        DWORD nRetVal = FileLoadResult::FRESULT_OK;
 
        // Set encoding based on extension, if we know one
-       SplitFilename(pszFileName, NULL, NULL, &sExt);
-       CCrystalTextView::TextDefinition *def = 
-               CCrystalTextView::GetTextType(sExt.c_str());
+       paths::SplitFilename(pszFileName, nullptr, nullptr, &sExt);
+       CrystalLineParser::TextDefinition *def = 
+               CrystalLineParser::GetTextType(sExt.c_str());
        if (def && def->encoding != -1)
                m_nSourceEncoding = def->encoding;
        
-       UniFile *pufile = infoUnpacker->pufile;
-       if (pufile == 0)
+       UniFile *pufile = infoUnpacker->m_pufile;
+       if (pufile == nullptr)
                pufile = new UniMemFile;
 
        // Now we only use the UniFile interface
@@ -371,26 +267,37 @@ int CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit,
                        sError = uniErr.GetError().c_str();
                }
                InitNew(); // leave crystal editor in valid, empty state
-               goto LoadFromFileExit;
        }
        else
        {
-               // If the file is not unicode file, use the codepage we were given to
-               // interpret the 8-bit characters. If the file is unicode file,
-               // determine its type (IsUnicode() does that).
-               if (encoding.m_unicoding == ucr::NONE || !pufile->IsUnicode())
-                       pufile->SetCodepage(encoding.m_codepage);
+               if (infoUnpacker->m_PluginName.length() > 0)
+               {
+                       // re-detect codepage
+                       int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
+                       FileTextEncoding encoding2 = codepage_detect::Guess(pszFileName, iGuessEncodingType);
+                       pufile->SetUnicoding(encoding2.m_unicoding);
+                       pufile->SetCodepage(encoding2.m_codepage);
+                       pufile->SetBom(encoding2.m_bom);
+                       if (encoding2.m_bom)
+                               pufile->ReadBom();
+               }
+               else
+               {
+                       // If the file is not unicode file, use the codepage we were given to
+                       // interpret the 8-bit characters. If the file is unicode file,
+                       // determine its type (IsUnicode() does that).
+                       if (encoding.m_unicoding == ucr::NONE  || !pufile->IsUnicode())
+                               pufile->SetCodepage(encoding.m_codepage);
+               }
                UINT lineno = 0;
                String eol, preveol;
                String sline;
                bool done = false;
-               UINT next_line_report = 100; // for trace messages
-               UINT next_line_multiple = 5; // for trace messages
                COleDateTime start = COleDateTime::GetCurrentTime(); // for trace messages
 
                // Manually grow line array exponentially
                UINT arraysize = 500;
-               m_aLines.SetSize(arraysize);
+               m_aLines.resize(arraysize);
                
                // preveol must be initialized for empty files
                preveol = _T("\n");
@@ -414,7 +321,7 @@ int CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit,
                                        arraysize *= 2;
                                else
                                        arraysize += 100 * 1024;
-                               m_aLines.SetSize(arraysize);
+                               m_aLines.resize(arraysize);
                        }
 
                        sline += eol; // TODO: opportunity for optimization, as CString append is terrible
@@ -422,59 +329,31 @@ int CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit,
                        {
                                // TODO: Should record lossy status of line
                        }
-                       AppendLine(lineno, sline.c_str(), sline.length());
+                       AppendLine(lineno, sline.c_str(), static_cast<int>(sline.length()));
                        ++lineno;
                        preveol = eol;
 
-#ifdef _DEBUG
-                       // send occasional line counts to trace
-                       // (at 100, 500, 1000, 5000, etc)
-                       if (lineno == next_line_report)
-                       {
-                               __int64 dwBytesRead = pufile->GetPosition();
-                               COleDateTimeSpan duration = COleDateTime::GetCurrentTime() - start;
-                               if (duration.GetTotalMinutes() > 0)
-                               {
-                                       CString strace = GetLineByteTimeReport(lineno, dwBytesRead, start);
-                                       TRACE(_T("%s\n"), (LPCTSTR)strace);
-                               }
-                               next_line_report = next_line_multiple * next_line_report;
-                               next_line_multiple = (next_line_multiple == 5) ? 2 : 5;
-                       }
-#endif // _DEBUG
                } while (!done);
 
-#ifdef _DEBUG
-               // Send report of duration to trace (if it took a while)
-               COleDateTime end = COleDateTime::GetCurrentTime();
-               COleDateTimeSpan duration = end - start;
-               if (duration.GetTotalMinutes() > 0)
-               {
-                       __int64 dwBytesRead = pufile->GetPosition();
-                       CString strace = GetLineByteTimeReport(lineno, dwBytesRead, start);
-                       TRACE(_T("%s\n"), (LPCTSTR)strace);
-               }
-#endif // _DEBUG
-
                // fix array size (due to our manual exponential growth
-               m_aLines.SetSize(lineno);
+               m_aLines.resize(lineno);
        
                
                //Try to determine current CRLF mode (most frequent)
-               if (nCrlfStyle == CRLF_STYLE_AUTOMATIC)
+               if (nCrlfStyle == CRLFSTYLE::AUTOMATIC)
                {
                        nCrlfStyle = GetTextFileStyle(pufile->GetTxtStats());
                }
-               ASSERT(nCrlfStyle >= 0 && nCrlfStyle <= 2);
+               ASSERT (nCrlfStyle != CRLFSTYLE::AUTOMATIC);
                SetCRLFMode(nCrlfStyle);
                
                //  At least one empty line must present
                // (view does not work for empty buffers)
-               ASSERT(m_aLines.GetSize() > 0);
+               ASSERT(m_aLines.size() > 0);
                
-               m_bInit = TRUE;
-               m_bModified = FALSE;
-               m_bUndoGroup = m_bUndoBeginGroup = FALSE;
+               m_bInit = true;
+               m_bModified = false;
+               m_bUndoGroup = m_bUndoBeginGroup = false;
                m_nSyncPosition = m_nUndoPosition = 0;
                ASSERT(m_aUndoBuf.size() == 0);
                m_ptLastChange.x = m_ptLastChange.y = -1;
@@ -500,74 +379,85 @@ int CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit,
                if (pufile->GetTxtStats().nlosses)
                {
                        FileLoadResult::AddModifier(nRetVal, FileLoadResult::FRESULT_LOSSY);
-                       readOnly = TRUE;
+                       readOnly = true;
                }
        }
        
-LoadFromFileExit:
        // close the file now to free the handle
        pufile->Close();
        delete pufile;
 
        // delete the file that unpacking may have created
        if (_tcscmp(pszFileNameInit, pszFileName) != 0)
-               if (!::DeleteFile(pszFileName))
+       {
+               try
                {
-                       LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                               pszFileName, GetSysError(GetLastError())));
+                       TFile(pszFileName).remove();
                }
-
+               catch (Exception& e)
+               {
+                       LogErrorStringUTF8(e.displayText());
+               }
+       }
        return nRetVal;
 }
 
 /**
  * @brief Saves file from buffer to disk
  *
- * @param bTempFile : FALSE if we are saving user files and
- * TRUE if we are saving workin-temp-files for diff-engine
+ * @param bTempFile : false if we are saving user files and
+ * true if we are saving working-temp-files for diff-engine
  *
  * @return SAVE_DONE or an error code (list in MergeDoc.h)
  */
-int CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
-               BOOL bTempFile, String & sError, PackingInfo * infoUnpacker /*= NULL*/,
-               CRLFSTYLE nCrlfStyle /*= CRLF_STYLE_AUTOMATIC*/,
-               BOOL bClearModifiedFlag /*= TRUE*/ )
+int CDiffTextBuffer::SaveToFile (const String& pszFileName,
+               bool bTempFile, String & sError, PackingInfo * infoUnpacker /*= nullptr*/,
+               CRLFSTYLE nCrlfStyle /*= CRLFSTYLE::AUTOMATIC*/,
+               bool bClearModifiedFlag /*= true*/,
+               int nStartLine /*= 0*/, int nLines /*= -1*/)
 {
+       ASSERT (nCrlfStyle == CRLFSTYLE::AUTOMATIC || nCrlfStyle == CRLFSTYLE::DOS ||
+               nCrlfStyle == CRLFSTYLE::UNIX || nCrlfStyle == CRLFSTYLE::MAC);
        ASSERT (m_bInit);
 
-       if (!pszFileName || _tcslen(pszFileName) == 0)
+       if (nLines == -1)
+               nLines = static_cast<int>(m_aLines.size() - nStartLine);
+
+       if (pszFileName.empty())
                return SAVE_FAILED;     // No filename, cannot save...
 
-       if (nCrlfStyle == CRLF_STYLE_AUTOMATIC &&
+       if (nCrlfStyle == CRLFSTYLE::AUTOMATIC &&
                !GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
-               infoUnpacker && infoUnpacker->disallowMixedEOL)
+               infoUnpacker!=nullptr && infoUnpacker->m_bDisallowMixedEOL)
        {
                        // get the default nCrlfStyle of the CDiffTextBuffer
                nCrlfStyle = GetCRLFMode();
-               ASSERT(nCrlfStyle >= 0 && nCrlfStyle <= 2);
+               ASSERT(nCrlfStyle != CRLFSTYLE::AUTOMATIC);
        }
 
-       BOOL bOpenSuccess = TRUE;
-       BOOL bSaveSuccess = FALSE;
+       bool bOpenSuccess = true;
+       bool bSaveSuccess = false;
 
        UniStdioFile file;
-       file.SetUnicoding(m_encoding.m_unicoding);
-       file.SetBom(m_encoding.m_bom);
-       file.SetCodepage(m_encoding.m_codepage);
 
        String sIntermediateFilename; // used when !bTempFile
 
        if (bTempFile)
        {
+               file.SetUnicoding(ucr::UTF8);
+               file.SetBom(GetOptionsMgr()->GetInt(OPT_CMP_DIFF_ALGORITHM) == 0);
                bOpenSuccess = !!file.OpenCreate(pszFileName);
        }
        else
        {
-               sIntermediateFilename = env_GetTempFileName(m_strTempPath.c_str(),
-                       _T("MRG_"), NULL);
+               file.SetUnicoding(m_encoding.m_unicoding);
+               file.SetBom(m_encoding.m_bom);
+               file.SetCodepage(m_encoding.m_codepage);
+               sIntermediateFilename = env::GetTemporaryFileName(m_strTempPath,
+                       _T("MRG_"), nullptr);
                if (sIntermediateFilename.empty())
                        return SAVE_FAILED;  //Nothing to do if even tempfile name fails
-               bOpenSuccess = !!file.OpenCreate(sIntermediateFilename.c_str());
+               bOpenSuccess = !!file.OpenCreate(sIntermediateFilename);
        }
 
        if (!bOpenSuccess)
@@ -575,12 +465,12 @@ int CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
                UniFile::UniError uniErr = file.GetLastUniError();
                if (uniErr.HasError())
                {
-                       sError = uniErr.GetError().c_str();
+                       sError = uniErr.GetError();
                        if (bTempFile)
-                               LogErrorString(Fmt(_T("Opening file %s failed: %s"),
+                               LogErrorString(strutils::format(_T("Opening file %s failed: %s"),
                                        pszFileName, sError));
                        else
-                               LogErrorString(Fmt(_T("Opening file %s failed: %s"),
+                               LogErrorString(strutils::format(_T("Opening file %s failed: %s"),
                                        sIntermediateFilename, sError));
                }
                return SAVE_FAILED;
@@ -589,35 +479,46 @@ int CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
        file.WriteBom();
 
        // line loop : get each real line and write it in the file
-       CString sLine;
-       CString sEol = GetStringEol(nCrlfStyle);
-       int nLineCount = m_aLines.GetSize();
-       for (int line = 0; line < nLineCount; ++line)
+       String sLine;
+       String sEol = GetStringEol(nCrlfStyle);
+       int lastRealLine = ApparentLastRealLine();
+       for (int line = nStartLine; line < nStartLine + nLines; ++line)
        {
                if (GetLineFlags(line) & LF_GHOST)
                        continue;
 
                // get the characters of the line (excluding EOL)
                if (GetLineLength(line) > 0)
-                       GetText(line, 0, line, GetLineLength(line), sLine, 0);
+               {
+                       int nLineLength = GetLineLength(line);
+                       sLine.resize(0);
+                       sLine.reserve(nLineLength + 4);
+                       sLine.append(GetLineChars(line), nLineLength);
+               }
                else
                        sLine = _T("");
 
-               if (bTempFile)
-                       EscapeControlChars(sLine);
+               if (bTempFile && m_bTableEditing && m_bAllowNewlinesInQuotes)
+               {
+                       strutils::replace(sLine, _T("\x1b"), _T("\x1b\x1b"));
+                       strutils::replace(sLine, _T("\r"), _T("\x1br"));
+                       strutils::replace(sLine, _T("\n"), _T("\x1bn"));
+               }
+
                // last real line ?
-               if (line == ApparentLastRealLine())
+               if (line == lastRealLine || lastRealLine == -1 )
                {
-                       // last real line is never EOL terminated
-                       ASSERT (_tcslen(GetLineEol(line)) == 0);
-                       // write the line and exit loop
-                       String tmpLine(sLine);
-                       file.WriteString(tmpLine);
-                       break;
+                       // If original last line had no EOL, then we are done
+                       if( !m_aLines[line].HasEol() )
+                       {
+                               file.WriteString(sLine);
+                               break;
+                       }
+                       // Otherwise, add the appropriate EOL to the last line ...
                }
 
-               // normal real line : append an EOL
-               if (nCrlfStyle == CRLF_STYLE_AUTOMATIC)
+               // normal line : append an EOL
+               if (nCrlfStyle == CRLFSTYLE::AUTOMATIC || nCrlfStyle == CRLFSTYLE::MIXED)
                {
                        // either the EOL of the line (when preserve original EOL chars is on)
                        sLine += GetLineEol(line);
@@ -628,9 +529,14 @@ int CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
                        sLine += sEol;
                }
 
-               // write this line to the file (codeset or unicode conversions are done there)
-               String tmpLine(sLine);
-               file.WriteString(tmpLine);
+               // write this line to the file (codeset or unicode conversions are done there
+               file.WriteString(sLine);
+
+               if (line == lastRealLine || lastRealLine == -1)
+               {
+                       // Last line, so now done
+                       break;
+               }
        }
        file.Close();
 
@@ -638,67 +544,43 @@ int CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
        {
                // If we are saving user files
                // we need an unpacker/packer, at least a "do nothing" one
-               ASSERT(infoUnpacker != NULL);
+               ASSERT(infoUnpacker != nullptr);
                // repack the file here, overwrite the temporary file we did save in
-               String csTempFileName = sIntermediateFilename;
-               infoUnpacker->subcode = m_unpackerSubcode;
-               if (!FileTransform_Packing(csTempFileName, *infoUnpacker))
+               bSaveSuccess = FileTransform::Packing(sIntermediateFilename, pszFileName, *infoUnpacker, m_unpackerSubcode);
+               try
                {
-                       if (!::DeleteFile(sIntermediateFilename.c_str()))
-                       {
-                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
-                       }
-                       // returns now, don't overwrite the original file
-                       return SAVE_PACK_FAILED;
+                       TFile(sIntermediateFilename).remove();
                }
-               // the temp filename may have changed during packing
-               if (csTempFileName != sIntermediateFilename)
+               catch (Exception& e)
                {
-                       if (!::DeleteFile(sIntermediateFilename.c_str()))
-                       {
-                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
-                       }
-                       sIntermediateFilename = csTempFileName;
+                       LogErrorStringUTF8(e.displayText());
                }
-
-               // Write tempfile over original file
-               if (::CopyFile(sIntermediateFilename.c_str(), pszFileName, FALSE))
+               if (!bSaveSuccess)
                {
-                       if (!::DeleteFile(sIntermediateFilename.c_str()))
-                       {
-                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
-                       }
-                       if (bClearModifiedFlag)
-                       {
-                               SetModified(FALSE);
-                               m_nSyncPosition = m_nUndoPosition;
-                       }
-                       bSaveSuccess = TRUE;
-
-                       // remember revision number on save
-                       m_dwRevisionNumberOnSave = m_dwCurrentRevisionNumber;
-
-                       // redraw line revision marks
-                       UpdateViews (NULL, NULL, UPDATE_FLAGSONLY);     
+                       // returns now, don't overwrite the original file
+                       return SAVE_PACK_FAILED;
                }
-               else
+
+               if (bClearModifiedFlag)
                {
-                       sError = GetSysError(GetLastError());
-                       LogErrorString(Fmt(_T("CopyFile(%s, %s) failed: %s"),
-                               sIntermediateFilename.c_str(), pszFileName, sError));
+                       SetModified(false);
+                       m_nSyncPosition = m_nUndoPosition;
                }
+
+               // remember revision number on save
+               m_dwRevisionNumberOnSave = m_dwCurrentRevisionNumber;
+
+               // redraw line revision marks
+               UpdateViews (nullptr, nullptr, UPDATE_FLAGSONLY);       
        }
        else
        {
                if (bClearModifiedFlag)
                {
-                       SetModified(FALSE);
+                       SetModified(false);
                        m_nSyncPosition = m_nUndoPosition;
                }
-               bSaveSuccess = TRUE;
+               bSaveSuccess = true;
        }
 
        if (bSaveSuccess)
@@ -707,63 +589,45 @@ int CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
                return SAVE_FAILED;
 }
 
-/**
- * @brief Replace a line with new text.
- * This function replaces line's text without changing the EOL style/bytes
- * of the line.
- * @param [in] pSource Editor view where text is changed.
- * @param [in] nLine Index of the line to change.
- * @param [in] pchText New text of the line.
- * @param [in] cchText New length of the line (not inc. EOL bytes).
- * @param [in] nAction Edit action to use.
- */
-void CDiffTextBuffer::ReplaceLine(CCrystalTextView * pSource, int nLine,
-               LPCTSTR pchText, int cchText, int nAction /*=CE_ACTION_UNKNOWN*/)
-{
-       if (GetLineLength(nLine)>0)
-               DeleteText(pSource, nLine, 0, nLine, GetLineLength(nLine), nAction);
-       int endl, endc;
-       if (cchText)
-               InsertText(pSource, nLine, 0, pchText, cchText, endl, endc, nAction);
-}
-
 /// Replace line (removing any eol, and only including one if in strText)
-/**
- * @brief Replace a line with new text.
- * This function replaces line's text including EOL bytes. If the @p strText
- * does not include EOL bytes, the "line" does not get EOL bytes.
- * @param [in] pSource Editor view where text is changed.
- * @param [in] nLine Index of the line to change.
- * @param [in] pchText New text of the line.
- * @param [in] cchText New length of the line (not inc. EOL bytes).
- * @param [in] nAction Edit action to use.
- */
-void CDiffTextBuffer::ReplaceFullLine(CCrystalTextView * pSource, int nLine,
-               const CString &strText, int nAction /*=CE_ACTION_UNKNOWN*/)
+void CDiffTextBuffer::ReplaceFullLines(CDiffTextBuffer& dbuf, CDiffTextBuffer& sbuf, CCrystalTextView * pSource, int nLineBegin, int nLineEnd, int nAction /*=CE_ACTION_UNKNOWN*/)
 {
-       LPCTSTR eol = GetEol(strText);
-       if (_tcscmp(GetLineEol(nLine), eol) == 0)
+       CString strText;
+       if (nLineBegin != nLineEnd || sbuf.GetLineLength(nLineEnd) > 0)
+               sbuf.GetTextWithoutEmptys(nLineBegin, 0, nLineEnd, sbuf.GetLineLength(nLineEnd), strText);
+       strText += sbuf.GetLineEol(nLineEnd);
+
+       if (nLineBegin != nLineEnd || dbuf.GetFullLineLength(nLineEnd) > 0)
        {
-               // (optimization) eols are the same, so just replace text inside line
-               // we must clean strText from its eol...
-               int eolLength = _tcslen(eol);
-               ReplaceLine(pSource, nLine, strText, strText.GetLength() - eolLength, nAction);
-               return;
+               int nLineEndSource = nLineEnd < dbuf.GetLineCount() ? nLineEnd : dbuf.GetLineCount();
+               if (nLineEnd+1 < GetLineCount())
+                       dbuf.DeleteText(pSource, nLineBegin, 0, nLineEndSource + 1, 0, nAction);
+               else
+                       dbuf.DeleteText(pSource, nLineBegin, 0, nLineEndSource, dbuf.GetLineLength(nLineEndSource), nAction); 
        }
 
-       // we may need a last line as the DeleteText end is (x=0,y=line+1)
-       if (nLine + 1 == GetLineCount())
-               InsertGhostLine (pSource, GetLineCount());
-
-       if (GetFullLineLength(nLine))
-               DeleteText(pSource, nLine, 0, nLine + 1, 0, nAction); 
-       int endl, endc;
-       const int cchText = strText.GetLength();
-       if (cchText)
-               InsertText(pSource, nLine, 0, strText, cchText, endl, endc, nAction);
+       if (int cchText = strText.GetLength())
+       {
+               int endl,endc;
+               dbuf.InsertText(pSource, nLineBegin, 0, strText, cchText, endl,endc, nAction);
+       }
 }
 
 bool CDiffTextBuffer::curUndoGroup()
 {
        return (m_aUndoBuf.size() != 0 && m_aUndoBuf[0].m_dwFlags&UNDO_BEGINGROUP);
 }
+
+bool CDiffTextBuffer::                 /* virtual override */
+DeleteText2(CCrystalTextView * pSource, int nStartLine, int nStartChar,
+       int nEndLine, int nEndChar, int nAction /*= CE_ACTION_UNKNOWN*/, bool bHistory /*= true*/)
+{
+       for (auto syncpnt : m_pOwnerDoc->GetSyncPointList())
+       {
+               const int nLineSyncPoint = syncpnt[m_nThisPane];
+               if (((nStartChar == 0 && nStartLine == nLineSyncPoint) || nStartLine < nLineSyncPoint) &&
+                       nLineSyncPoint < nEndLine)
+                       m_pOwnerDoc->DeleteSyncPoint(m_nThisPane, nLineSyncPoint, false);
+       }
+       return CGhostTextBuffer::DeleteText2(pSource, nStartLine, nStartChar, nEndLine, nEndChar, nAction, bHistory);
+}