OSDN Git Service

crystaledit: Use GetProfile*()/WriteProfile*() to read and write the registry wheneve...
[winmerge-jp/winmerge-jp.git] / Src / DiffWrapper.cpp
index c736695..300bdbf 100644 (file)
@@ -1,19 +1,4 @@
-/////////////////////////////////////////////////////////////////////////////
-//    License (GPLv2+):
-//    This program is free software; you can redistribute it and/or modify
-//    it under the terms of the GNU General Public License as published by
-//    the Free Software Foundation; either version 2 of the License, or (at
-//    your option) any later version.
-//    
-//    This program is distributed in the hope that it will be useful, but
-//    WITHOUT ANY WARRANTY; without even the implied warranty of
-//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//    GNU General Public License for more details.
-//
-//    You should have received a copy of the GNU General Public License
-//    along with this program; if not, write to the Free Software
-//    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-/////////////////////////////////////////////////////////////////////////////
+// SPDX-License-Identifier: GPL-2.0-or-later
 /** 
  * @file  DiffWrapper.cpp
  *
  * @date  Created: 2003-08-22
  */
 
+#include "pch.h"
 #define NOMINMAX
 #include "DiffWrapper.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <algorithm>
 #include <string>
+#include <cctype>
+#include <cwctype>
 #include <map>
 #include <cassert>
 #include <exception>
 #include "FilterList.h"
 #include "diff.h"
 #include "Diff3.h"
+#include "xdiff_gnudiff_compat.h"
 #include "FileTransform.h"
 #include "paths.h"
 #include "CompareOptions.h"
 #include "FileTextStats.h"
 #include "FolderCmp.h"
-#include "FilterCommentsManager.h"
 #include "Environment.h"
 #include "PatchHTML.h"
 #include "UnicodeString.h"
 #include "unicoder.h"
 #include "TFile.h"
 #include "Exceptions.h"
+#include "parsers/crystallineparser.h"
+#include "SyntaxColors.h"
 #include "MergeApp.h"
+#include "SubstitutionList.h"
 
 using Poco::Debugger;
 using Poco::format;
@@ -65,31 +56,28 @@ using Poco::Exception;
 
 extern int recursive;
 
-static void FreeDiffUtilsScript(struct change * & script);
-static void FreeDiffUtilsScript3(struct change * & script10, struct change * & script12);
 static void CopyTextStats(const file_data * inf, FileTextStats * myTextStats);
 static void CopyDiffutilTextStats(file_data *inf, DiffFileData * diffData);
 
 /**
  * @brief Default constructor.
- * Initializes members and creates new FilterCommentsManager.
+ * Initializes members.
  */
 CDiffWrapper::CDiffWrapper()
-: m_pFilterCommentsManager(nullptr)
+: m_pFilterCommentsDef(nullptr)
 , m_bCreatePatchFile(false)
 , m_bUseDiffList(false)
 , m_bAddCmdLine(true)
 , m_bAppendFiles(false)
 , m_nDiffs(0)
-, m_codepage(GetACP())
 , m_infoPrediffer(nullptr)
 , m_pDiffList(nullptr)
 , m_bPathsAreTemp(false)
 , m_pFilterList(nullptr)
+, m_pSubstitutionList{nullptr}
 , m_bPluginsEnabled(false)
+, m_status()
 {
-       memset(&m_status, 0, sizeof(DIFFSTATUS));
-
        // character that ends a line.  Currently this is always `\n'
        line_end_char = '\n';
 }
@@ -102,15 +90,6 @@ CDiffWrapper::~CDiffWrapper()
 }
 
 /**
- * @brief Set plugins enabled/disabled.
- * @param [in] enable if true plugins are enabled.
- */
-void CDiffWrapper::EnablePlugins(bool enable)
-{
-       m_bPluginsEnabled = enable;
-}
-
-/**
  * @brief Enables/disables patch-file creation and sets filename.
  * This function enables or disables patch file creation. When
  * @p filename is empty, patch files are disabled.
@@ -127,23 +106,23 @@ void CDiffWrapper::SetCreatePatchFile(const String &filename)
        {
                m_bCreatePatchFile = true;
                m_sPatchFile = filename;
-               string_replace(m_sPatchFile, _T("/"), _T("\\"));
+               strutils::replace(m_sPatchFile, _T("/"), _T("\\"));
        }
 }
 
 /**
  * @brief Enables/disabled DiffList creation ands sets DiffList.
  * This function enables or disables DiffList creation. When
- * @p diffList is NULL difflist is not created. When valid DiffList
- * pointer is given, compare results are stored into it.
+ * @p diffList is `nullptr`, a difflist was not created. When valid 
+ * DiffList pointer is given, compare results are stored into it.
  * @param [in] diffList Pointer to DiffList getting compare results.
  */
 void CDiffWrapper::SetCreateDiffList(DiffList *diffList)
 {
-       if (diffList == NULL)
+       if (diffList == nullptr)
        {
                m_bUseDiffList = false;
-               m_pDiffList = NULL;
+               m_pDiffList = nullptr;
        }
        else
        {
@@ -160,7 +139,7 @@ void CDiffWrapper::SetCreateDiffList(DiffList *diffList)
  */
 void CDiffWrapper::GetOptions(DIFFOPTIONS *options) const
 {
-       assert(options);
+       assert(options != nullptr);
        DIFFOPTIONS tmpOptions = {0};
        m_options.GetAsDiffOptions(tmpOptions);
        *options = tmpOptions;
@@ -174,30 +153,18 @@ void CDiffWrapper::GetOptions(DIFFOPTIONS *options) const
  */
 void CDiffWrapper::SetOptions(const DIFFOPTIONS *options)
 {
-       assert(options);
+       assert(options != nullptr);
        m_options.SetFromDiffOptions(*options);
 }
 
-/**
- * @brief Set text tested to find the prediffer automatically.
- * Most probably a concatenated string of both filenames.
- */
-void CDiffWrapper::SetTextForAutomaticPrediff(const String &text)
-{
-       m_sToFindPrediffer = text;
-}
-void CDiffWrapper::SetPrediffer(const PrediffingInfo * prediffer /*=NULL*/)
+void CDiffWrapper::SetPrediffer(const PrediffingInfo * prediffer /*= nullptr*/)
 {
        // all flags are set correctly during the construction
        m_infoPrediffer.reset(new PrediffingInfo);
 
-       if (prediffer)
+       if (prediffer != nullptr)
                *m_infoPrediffer = *prediffer;
 }
-void CDiffWrapper::GetPrediffer(PrediffingInfo * prediffer) const
-{
-       *prediffer = *m_infoPrediffer;
-}
 
 /**
  * @brief Set options used for patch-file creation.
@@ -205,7 +172,7 @@ void CDiffWrapper::GetPrediffer(PrediffingInfo * prediffer) const
  */
 void CDiffWrapper::SetPatchOptions(const PATCHOPTIONS *options)
 {
-       assert(options);
+       assert(options != nullptr);
        m_options.m_contextLines = options->nContext;
 
        switch (options->outputStyle)
@@ -238,7 +205,7 @@ void CDiffWrapper::SetDetectMovedBlocks(bool bDetectMovedBlocks)
 {
        if (bDetectMovedBlocks)
        {
-               if (m_pMovedLines[0] == NULL)
+               if (m_pMovedLines[0] == nullptr)
                {
                        m_pMovedLines[0].reset(new MovedLines);
                        m_pMovedLines[1].reset(new MovedLines);
@@ -253,95 +220,74 @@ void CDiffWrapper::SetDetectMovedBlocks(bool bDetectMovedBlocks)
        }
 }
 
-/**
- * @brief Test for trivial only characters in string
- * @param [in] Start                           - Start position in string
- * @param [in] End                                     - One character pass the end position of the string
- * @param [in] filtercommentsset       - For future use to determine trivial bytes
- * @return Returns true if all characters are trivial
- */
-bool CDiffWrapper::IsTrivialBytes(const char* Start, const char* End,
-       const FilterCommentsSet& filtercommentsset) const
+static String convertToTString(const char* start, const char* end)
 {
-       std::string testdata(Start, End);
-       //@TODO: Need to replace the following trivial string with a user specified string
-       size_t pos = testdata.find_first_not_of(" \t\r\n");
-       return (pos == std::string::npos);
-}
-
-/**
- * @brief Test for a line of trivial data
- * @param [in] Line                                    - String to test for
- * @param [in] StartOfComment          - 
- * @param [in] EndOfComment                    - 
- * @param [in] InLineComment           - 
- * @param [in] filtercommentsset       - Comment marker set used to indicate comment blocks.
- * @return Returns true if entire line is trivial
- */
-bool CDiffWrapper::IsTrivialLine(const std::string &Line, 
-                                  const char * StartOfComment, 
-                                  const char * EndOfComment,   
-                                  const char * InLineComment,  
-                                  const FilterCommentsSet& filtercommentsset) const
-{
-       //Do easy test first
-       if ((!StartOfComment || !EndOfComment) && !InLineComment)
-               return false;//In no Start and End pair, and no single in-line set, then it's not trivial
-
-       if (StartOfComment == Line.c_str() &&
-               ((EndOfComment + filtercommentsset.EndMarker.size()) - StartOfComment) == Line.size())
-       {//If entire line is blocked by End and Start markers, then entire line is trivial
-               return true;
+       if (!ucr::CheckForInvalidUtf8(start, end - start))
+       {
+               return ucr::toTString(std::string(start, end));
        }
-
-       if (InLineComment && InLineComment < StartOfComment)
+       else
        {
-               if (InLineComment == Line.c_str())
-                       return true;//If line starts with InLineComment marker, then entire line is trivial
-
-               //Other wise, check if data before InLineComment marker is trivial
-               return IsTrivialBytes(Line.c_str(), InLineComment, filtercommentsset);
+               bool lossy = false;
+               String text;
+               ucr::maketstring(text, start, end - start, -1, &lossy);
+               return text;
        }
+}
 
-       //Done with easy test, so now do more complex test
-       if (StartOfComment && 
-               EndOfComment && 
-               StartOfComment < EndOfComment &&
-               IsTrivialBytes(Line.c_str(), StartOfComment, filtercommentsset) &&
-               IsTrivialBytes(EndOfComment + filtercommentsset.EndMarker.size(),
-                       Line.c_str()+Line.size(), filtercommentsset))
+static unsigned GetLastLineCookie(unsigned dwCookie, int startLine, int endLine, const char **linbuf, CrystalLineParser::TextDefinition* enuType)
+{
+       if (!enuType)
+               return dwCookie;
+       for (int i = startLine; i <= endLine; ++i)
        {
-               return true;
+               String text = convertToTString(linbuf[i], linbuf[i + 1]);
+               int nActualItems = 0;
+               std::vector<CrystalLineParser::TEXTBLOCK> blocks(text.length());
+               dwCookie = enuType->ParseLineX(dwCookie, text.c_str(), static_cast<int>(text.length()), blocks.data(), nActualItems);
        }
-
-       return false;
+       return dwCookie;
 }
 
-/**
- * @brief Find comment marker in string, excluding portions enclosed in quotation marks or apostrophes
- * @param [in] target                          - string to search
- * @param [in] marker                          - marker to search for
- * @return Returns position of marker, or NULL if none is present
- */
-static const char *FindCommentMarker(const char *target, const char *marker)
+static unsigned GetCommentsFilteredText(unsigned dwCookie, int startLine, int endLine, const char **linbuf, std::string& filtered, CrystalLineParser::TextDefinition* enuType)
 {
-       char prev = '\0';
-       char quote = '\0';
-       size_t marker_len = strlen(marker);
-       while (char c = *target)
-       {
-               if (quote == '\0' && strncmp(target, marker, marker_len) == 0)
-                       return target;
-               if ((prev != '\\') &&
-                       (c == '"' || c == '\'') &&
-                       (quote == '\0' || quote == c))
+       String filteredT;
+       for (int i = startLine; i <= endLine; ++i)
+       {
+               String text = convertToTString(linbuf[i], linbuf[i + 1]);
+               unsigned textlen = static_cast<unsigned>(text.size());
+               if (!enuType)
                {
-                       quote ^= c;
+                       filteredT += text;
+               }
+               else
+               {
+                       int nActualItems = 0;
+                       std::vector<CrystalLineParser::TEXTBLOCK> blocks(textlen);
+                       dwCookie = enuType->ParseLineX(dwCookie, text.c_str(), textlen, blocks.data(), nActualItems);
+
+                       if (nActualItems == 0)
+                       {
+                               filteredT += text;
+                       }
+                       else
+                       {
+                               for (int j = 0; j < nActualItems; ++j)
+                               {
+                                       CrystalLineParser::TEXTBLOCK& block = blocks[j];
+                                       if (block.m_nColorIndex != COLORINDEX_COMMENT)
+                                       {
+                                               unsigned blocklen = (j < nActualItems - 1) ? (blocks[j + 1].m_nCharPos - block.m_nCharPos) : textlen - block.m_nCharPos;
+                                               filteredT.append(text.c_str() + block.m_nCharPos, blocklen);
+                                       }
+                               }
+                       }
                }
-               prev = c;
-               ++target;
        }
-       return NULL;
+
+       filtered = ucr::toUTF8(filteredT);
+
+       return dwCookie;
 }
 
 /**
@@ -356,138 +302,13 @@ static void ReplaceSpaces(std::string & str, const char *rep)
        while ((pos = str.find_first_of(" \t", pos)) != std::string::npos)
        {
                std::string::size_type posend = str.find_first_not_of(" \t", pos);
-               if (posend != std::string::npos)
+               if (posend != String::npos)
                        str.replace(pos, posend - pos, rep);
                else
                        str.replace(pos, 1, rep);
                pos += replen;
        }
 }
-
-/**
-       @brief Performs post-filtering, by setting comment blocks to trivial
-       @param [in]  StartPos                   - First line number to read
-       @param [in]  EndPos                             - The line number PASS the last line number to read
-       @param [in]  QtyLinesInBlock            - Number of lines in diff block.  Not needed in backward direction.
-       @param [in]  Direction                  - This should be 1 or -1, to indicate which direction to read (backward or forward)
-       @param [in,out]  Op                             - This variable is set to trivial if block should be ignored.
-       @param [in]  FileNo                             - Should be 0 or 1, to indicate left or right file.
-       @param [in]  filtercommentsset  - Comment marker set used to indicate comment blocks.
-       @return         Always returns true in reverse direction.
-                               In forward direction, returns false if none trivial data is found within QtyLinesInBlock
-*/
-bool CDiffWrapper::PostFilter(int StartPos, int EndPos, int Direction,
-       int QtyLinesInBlock, OP_TYPE &Op, int FileNo,
-       FilterCommentsSet& filtercommentsset) const
-{
-       if (Op == OP_TRIVIAL) //If already set to trivial, then exit.
-               return true;
-       bool OpShouldBeTrivial = false;
-       int QtyTrivialLines = 0;
-       for(int i = StartPos + ((Direction == -1)?-1:0); i != EndPos;i += Direction)
-       {
-               if ((i - StartPos) == QtyLinesInBlock && 
-                       QtyLinesInBlock == QtyTrivialLines)
-               {
-                       OpShouldBeTrivial = true;
-                       break;
-               }
-               size_t len = files[FileNo].linbuf[i + 1] - files[FileNo].linbuf[i];
-               const char *LineStr = files[FileNo].linbuf[i];
-               std::string LineData(LineStr, linelen(LineStr, len));
-
-               const char * StartOfComment             = FindCommentMarker(LineData.c_str(), filtercommentsset.StartMarker.c_str());
-               const char * EndOfComment               = FindCommentMarker(LineData.c_str(), filtercommentsset.EndMarker.c_str());
-               const char * InLineComment              = FindCommentMarker(LineData.c_str(), filtercommentsset.InlineMarker.c_str());
-               //The following logic determines if the entire block is a comment block, and only marks it as trivial
-               //if all the changes are within a comment block.
-               if (Direction == -1)
-               {
-                       if (!StartOfComment && EndOfComment)
-                               break;
-                       
-                       if (StartOfComment && (!EndOfComment || EndOfComment < StartOfComment) && (!InLineComment || InLineComment > StartOfComment))
-                       {
-                               OpShouldBeTrivial = true;
-                               break;
-                       }
-               }
-               else if (Direction == 1)
-               {
-                       if (IsTrivialBytes(LineData.c_str(), LineData.c_str()+LineData.size(), filtercommentsset) || 
-                               IsTrivialLine(LineData, StartOfComment, EndOfComment, InLineComment, filtercommentsset))
-                       {
-                               ++QtyTrivialLines;
-                       }
-
-                       if (!EndOfComment && StartOfComment)
-                       {
-                               if (i == (StartPos + QtyTrivialLines) )
-                               {
-                                       if (StartOfComment == LineData.c_str())
-                                       {//If this is at the beginning of the first line, then lets continue
-                                               continue;
-                                       }
-                                       if (IsTrivialBytes(LineData.c_str(), StartOfComment, filtercommentsset))
-                                       {//If only trivial bytes before comment marker, then continue
-                                               continue;
-                                       }
-                                       break;
-                               }
-                               //If this is not the first line, then assume
-                               //previous lines are non-trivial, and return true.
-                               return false;
-                       }
-
-                       if (EndOfComment && 
-                               (!StartOfComment || StartOfComment > EndOfComment) && 
-                               (!InLineComment || InLineComment > EndOfComment) )
-                       {
-                               if (!IsTrivialBytes(EndOfComment+filtercommentsset.EndMarker.size(), LineData.c_str()+LineData.size(), filtercommentsset))
-                               {
-                                       return false;
-                               }
-
-                               if ((i - StartPos) >=  (QtyLinesInBlock-1))
-                               {
-                                       OpShouldBeTrivial = true;
-                                       break;
-                               }
-
-                               //Lets check if the remaining lines only contain trivial data
-                               bool AllRemainingLinesContainTrivialData = true;
-                               int TrivLinePos = i+1;
-                               for(; TrivLinePos != (StartPos + QtyLinesInBlock);++TrivLinePos)
-                               {
-                                       size_t len = files[FileNo].linbuf[TrivLinePos + 1] - files[FileNo].linbuf[TrivLinePos];
-                                       const char *LineStrTrvCk = files[FileNo].linbuf[TrivLinePos];
-                                       std::string LineDataTrvCk(LineStrTrvCk, linelen(LineStrTrvCk, len));
-                                       if (LineDataTrvCk.size() &&
-                                               !IsTrivialBytes(LineDataTrvCk.c_str(), LineDataTrvCk.c_str() + LineDataTrvCk.size(), filtercommentsset))
-                                       {
-                                               AllRemainingLinesContainTrivialData = false;
-                                               break;
-                                       }
-                               }
-                               if (AllRemainingLinesContainTrivialData)
-                               {
-                                       OpShouldBeTrivial = true;
-                                       break;
-                               }
-                               if (TrivLinePos != (StartPos + QtyLinesInBlock) )
-                               {
-                                       return PostFilter(TrivLinePos, EndPos, Direction, QtyLinesInBlock - (TrivLinePos - StartPos), Op, FileNo, filtercommentsset);
-                               }
-                       }
-               }
-       }
-       if (OpShouldBeTrivial)
-       {
-               Op = OP_TRIVIAL;
-       }
-       return true;
-}
-
 /**
 @brief The main entry for post filtering.  Performs post-filtering, by setting comment blocks to trivial
 @param [in]  LineNumberLeft            - First line number to read from left file
@@ -495,147 +316,70 @@ bool CDiffWrapper::PostFilter(int StartPos, int EndPos, int Direction,
 @param [in]  LineNumberRight           - First line number to read from right file
 @param [in]  QtyLinesRight             - Number of lines in the block for right file
 @param [in,out]  Op                            - This variable is set to trivial if block should be ignored.
-@param [in]  FileNameExt                       - The file name extension.  Needs to be lower case string ("cpp", "java", "c")
 */
-void CDiffWrapper::PostFilter(int LineNumberLeft, int QtyLinesLeft, int LineNumberRight,
-       int QtyLinesRight, OP_TYPE &Op, const String& FileNameExt) const
+void CDiffWrapper::PostFilter(PostFilterContext& ctxt, int LineNumberLeft, int QtyLinesLeft, int LineNumberRight,
+       int QtyLinesRight, OP_TYPE &Op, const file_data *file_data_ary) const
 {
-       if (Op == OP_TRIVIAL || !m_pFilterCommentsManager)
+       if (Op == OP_TRIVIAL)
                return;
-       
-       //First we need to get lowercase file name extension
-       FilterCommentsSet filtercommentsset = m_pFilterCommentsManager->GetSetForFileType(FileNameExt);
-       if (filtercommentsset.StartMarker.empty() && 
-               filtercommentsset.EndMarker.empty() &&
-               filtercommentsset.InlineMarker.empty())
+
+       std::string LineDataLeft, LineDataRight;
+
+       if (m_options.m_filterCommentsLines)
        {
-               return;
-       }
+               ctxt.dwCookieLeft = GetLastLineCookie(ctxt.dwCookieLeft,
+                       ctxt.nParsedLineEndLeft + 1, LineNumberLeft - 1, file_data_ary[0].linbuf + file_data_ary[0].linbuf_base, m_pFilterCommentsDef);
+               ctxt.dwCookieRight = GetLastLineCookie(ctxt.dwCookieRight,
+                       ctxt.nParsedLineEndRight + 1, LineNumberRight - 1, file_data_ary[1].linbuf + file_data_ary[1].linbuf_base, m_pFilterCommentsDef);
 
-       OP_TYPE LeftOp = OP_NONE;
-       OP_TYPE RightOp = OP_NONE;
+               ctxt.nParsedLineEndLeft = LineNumberLeft + QtyLinesLeft - 1;
+               ctxt.nParsedLineEndRight = LineNumberRight + QtyLinesRight - 1;;
 
-       if (QtyLinesRight == 0)
-       {       //Only check left side
-               if (PostFilter(LineNumberLeft, files[0].valid_lines, 1, QtyLinesLeft, LeftOp, 0, filtercommentsset))
-                       PostFilter(LineNumberLeft, -1, -1, QtyLinesLeft, LeftOp, 0, filtercommentsset);
-       }
-       else if (QtyLinesLeft == 0)
-       {       //Only check right side
-               if (PostFilter(LineNumberRight, files[1].valid_lines, 1, QtyLinesRight, RightOp, 1, filtercommentsset))
-                       PostFilter(LineNumberRight, -1, -1, QtyLinesRight, RightOp, 1, filtercommentsset);
+               ctxt.dwCookieLeft = GetCommentsFilteredText(ctxt.dwCookieLeft,
+                       LineNumberLeft, ctxt.nParsedLineEndLeft, file_data_ary[0].linbuf + file_data_ary[0].linbuf_base, LineDataLeft, m_pFilterCommentsDef);
+               ctxt.dwCookieRight = GetCommentsFilteredText(ctxt.dwCookieRight,
+                       LineNumberRight, ctxt.nParsedLineEndRight, file_data_ary[1].linbuf + file_data_ary[1].linbuf_base, LineDataRight, m_pFilterCommentsDef);
        }
        else
        {
-               if (PostFilter(LineNumberLeft, files[0].valid_lines, 1, QtyLinesLeft, LeftOp, 0, filtercommentsset))
-                       PostFilter(LineNumberLeft, -1, -1, QtyLinesLeft, LeftOp, 0, filtercommentsset);
-
-               if (PostFilter(LineNumberRight, files[1].valid_lines, 1, QtyLinesRight, RightOp, 1, filtercommentsset))
-                       PostFilter(LineNumberRight, -1, -1, QtyLinesRight, RightOp, 1, filtercommentsset);
+               LineDataLeft.assign(file_data_ary[0].linbuf[LineNumberLeft + file_data_ary[0].linbuf_base],
+                       file_data_ary[0].linbuf[LineNumberLeft + QtyLinesLeft + file_data_ary[0].linbuf_base]
+                       - file_data_ary[0].linbuf[LineNumberLeft + file_data_ary[0].linbuf_base]);
+               LineDataRight.assign(file_data_ary[1].linbuf[LineNumberRight + file_data_ary[1].linbuf_base],
+                       file_data_ary[1].linbuf[LineNumberRight + QtyLinesRight + file_data_ary[1].linbuf_base]
+                       - file_data_ary[1].linbuf[LineNumberRight + file_data_ary[1].linbuf_base]);
        }
 
-       std::list<std::string> LeftLines, RightLines;
-       for (int i = 0; (i < QtyLinesLeft) || (i < QtyLinesRight); i++)
+       if (m_pSubstitutionList)
        {
-               //Lets test  all lines if only a comment is different.
-               const char *    LineStrLeft = "";
-               const char *    EndLineLeft = LineStrLeft;
-               const char *    LineStrRight = "";
-               const char *    EndLineRight = LineStrRight;
-               if(i < QtyLinesLeft)
-               {
-                       LineStrLeft = files[0].linbuf[LineNumberLeft + i];
-                       EndLineLeft = files[0].linbuf[LineNumberLeft + i + 1];
-               }
-               if(i < QtyLinesRight)
-               {
-                       LineStrRight = files[1].linbuf[LineNumberRight + i];
-                       EndLineRight = files[1].linbuf[LineNumberRight + i + 1];
-               }
-                       
-               if (EndLineLeft && EndLineRight)
-               {       
-                       std::string LineDataLeft(LineStrLeft, EndLineLeft);
-                       std::string LineDataRight(LineStrRight, EndLineRight);
-
-                       if (!filtercommentsset.StartMarker.empty() && !filtercommentsset.EndMarker.empty())
-                       {
-                               const char * CommentStrLeftStart;
-                               const char * CommentStrLeftEnd;
-                               const char * CommentStrRightStart;
-                               const char * CommentStrRightEnd;
-
-                               bool bFirstLoop = true;
-                               do {
-                                       //Lets remove block comments, and see if lines are equal
-                                       CommentStrLeftStart = FindCommentMarker(LineDataLeft.c_str(), filtercommentsset.StartMarker.c_str());
-                                       CommentStrLeftEnd = FindCommentMarker(LineDataLeft.c_str(), filtercommentsset.EndMarker.c_str());
-                                       CommentStrRightStart = FindCommentMarker(LineDataRight.c_str(), filtercommentsset.StartMarker.c_str());
-                                       CommentStrRightEnd = FindCommentMarker(LineDataRight.c_str(), filtercommentsset.EndMarker.c_str());
-                                       
-                                       if (CommentStrLeftStart != NULL && CommentStrLeftEnd != NULL && CommentStrLeftStart < CommentStrLeftEnd)
-                                               LineDataLeft.erase(CommentStrLeftStart - LineDataLeft.c_str(), CommentStrLeftEnd + filtercommentsset.EndMarker.size() - CommentStrLeftStart);
-                                       else if (CommentStrLeftEnd != NULL)
-                                               LineDataLeft.erase(0, CommentStrLeftEnd + filtercommentsset.EndMarker.size() - LineDataLeft.c_str());
-                                       else if (CommentStrLeftStart != NULL)
-                                               LineDataLeft.erase(CommentStrLeftStart - LineDataLeft.c_str());
-                                       else if(LeftOp == OP_TRIVIAL && bFirstLoop)
-                                               LineDataLeft.erase(0);  //This line is all in block comments
-
-                                       if (CommentStrRightStart != NULL && CommentStrRightEnd != NULL && CommentStrRightStart < CommentStrRightEnd)
-                                               LineDataRight.erase(CommentStrRightStart - LineDataRight.c_str(), CommentStrRightEnd + filtercommentsset.EndMarker.size() - CommentStrRightStart);
-                                       else if (CommentStrRightEnd != NULL)
-                                               LineDataRight.erase(0, CommentStrRightEnd + filtercommentsset.EndMarker.size() - LineDataRight.c_str());
-                                       else if (CommentStrRightStart != NULL)
-                                               LineDataRight.erase(CommentStrRightStart - LineDataRight.c_str());
-                                       else if(RightOp == OP_TRIVIAL && bFirstLoop)
-                                               LineDataRight.erase(0);  //This line is all in block comments
-
-                                       bFirstLoop = false;
-
-                               } while (CommentStrLeftStart != NULL || CommentStrLeftEnd != NULL
-                                       || CommentStrRightStart != NULL || CommentStrRightEnd != NULL); //Loops until all blockcomments are lost
-                       }
-
-                       if (!filtercommentsset.InlineMarker.empty())
-                       {
-                               //Lets remove line comments
-                               const char * CommentStrLeft = FindCommentMarker(LineDataLeft.c_str(), filtercommentsset.InlineMarker.c_str());
-                               const char * CommentStrRight = FindCommentMarker(LineDataRight.c_str(), filtercommentsset.InlineMarker.c_str());
-
-                               if (CommentStrLeft != NULL)
-                                       LineDataLeft.erase(CommentStrLeft - LineDataLeft.c_str());
-                               if (CommentStrRight != NULL)
-                                       LineDataRight.erase(CommentStrRight - LineDataRight.c_str());
-                       }
-
-               if (m_options.m_ignoreWhitespace == WHITESPACE_IGNORE_ALL)
-                       {
-                               //Ignore character case
-                               ReplaceSpaces(LineDataLeft, "");
-                               ReplaceSpaces(LineDataRight, "");
-                       }
-                       else if (m_options.m_ignoreWhitespace == WHITESPACE_IGNORE_CHANGE)
-                       {
-                               //Ignore change in whitespace char count
-                               ReplaceSpaces(LineDataLeft, " ");
-                               ReplaceSpaces(LineDataRight, " ");
-                       }
-
-                       if (m_options.m_bIgnoreCase)
-                       {
-                               //ignore case
-                               std::transform(LineDataLeft.begin(),  LineDataLeft.end(),  LineDataLeft.begin(),  ::toupper);
-                               std::transform(LineDataRight.begin(), LineDataRight.end(), LineDataRight.begin(), ::toupper);
-                       }
+               LineDataLeft = m_pSubstitutionList->Subst(LineDataLeft);
+               LineDataRight = m_pSubstitutionList->Subst(LineDataRight);
+       }
 
-                       if (!LineDataLeft.empty())
-                               LeftLines.push_back(LineDataLeft);
-                       if (!LineDataRight.empty())
-                               RightLines.push_back(LineDataRight);
-               }
+       if (m_options.m_ignoreWhitespace == WHITESPACE_IGNORE_ALL)
+       {
+               //Ignore character case
+               ReplaceSpaces(LineDataLeft, "");
+               ReplaceSpaces(LineDataRight, "");
        }
-       if (LeftLines != RightLines)
+       else if (m_options.m_ignoreWhitespace == WHITESPACE_IGNORE_CHANGE)
+       {
+               //Ignore change in whitespace char count
+               ReplaceSpaces(LineDataLeft, " ");
+               ReplaceSpaces(LineDataRight, " ");
+       }
+
+       if (m_options.m_bIgnoreCase)
+       {
+               //ignore case
+               // std::transform(LineDataLeft.begin(),  LineDataLeft.end(),  LineDataLeft.begin(),  ::toupper);
+               for (std::string::iterator pb = LineDataLeft.begin(), pe = LineDataLeft.end(); pb != pe; ++pb) 
+                       *pb = static_cast<char>(::toupper(*pb));
+               // std::transform(LineDataRight.begin(), LineDataRight.end(), LineDataRight.begin(), ::toupper);
+               for (std::string::iterator pb = LineDataRight.begin(), pe = LineDataRight.end(); pb != pe; ++pb) 
+                       *pb = static_cast<char>(::toupper(*pb));
+       }
+       if (LineDataLeft != LineDataRight)
                return;
        //only difference is trival
        Op = OP_TRIVIAL;
@@ -649,45 +393,22 @@ void CDiffWrapper::PostFilter(int LineNumberLeft, int QtyLinesLeft, int LineNumb
  * @param [in] files Files to compare
  * @param [in] tempPaths Are given paths temporary (can be deleted)?.
  */
-void CDiffWrapper::SetPaths(const PathContext &files,
+void CDiffWrapper::SetPaths(const PathContext &tFiles,
                bool tempPaths)
 {
-       m_files = files;
+       m_files = tFiles;
        m_bPathsAreTemp = tempPaths;
 }
 
 /**
- * @brief Set source paths for original (NON-TEMP) diffing two files.
- * Sets full paths to two (NON-TEMP) files we are diffing.
- * @param [in] OriginalFile1 First file to compare "(NON-TEMP) file".
- * @param [in] OriginalFile2 Second file to compare "(NON-TEMP) file".
- */
-void CDiffWrapper::SetCompareFiles(const PathContext &originalFile)
-{
-       m_originalFile = originalFile;
-}
-
-/**
- * @brief Set alternative paths for compared files.
- * Sets alternative paths for diff'ed files. These alternative paths might not
- * be real paths. For example when creating a patch file from folder compare
- * we want to use relative paths.
- * @param [in] altPaths Alternative file paths.
- */
-void CDiffWrapper::SetAlternativePaths(const PathContext &altPaths)
-{
-       m_alternativePaths = altPaths;
-}
-
-/**
  * @brief Runs diff-engine.
  */
 bool CDiffWrapper::RunFileDiff()
 {
-       PathContext files = m_files;
+       PathContext aFiles = m_files;
        int file;
        for (file = 0; file < m_files.GetSize(); file++)
-               replace_char(&*files[file].begin(), '/', '\\');
+               aFiles[file] = paths::ToWindowsPath(aFiles[file]);
 
        bool bRet = true;
        String strFileTemp[3];
@@ -698,7 +419,7 @@ bool CDiffWrapper::RunFileDiff()
        if (m_bUseDiffList)
                m_nDiffs = m_pDiffList->GetSize();
 
-       for (file = 0; file < files.GetSize(); file++)
+       for (file = 0; file < aFiles.GetSize(); file++)
        {
                if (m_bPluginsEnabled)
                {
@@ -709,34 +430,34 @@ bool CDiffWrapper::RunFileDiff()
 
                        // this can only fail if the data can not be saved back (no more
                        // place on disk ???) What to do then ??
-                       if (!FileTransform_Prediffing(m_infoPrediffer.get(), strFileTemp[file], m_sToFindPrediffer, m_bPathsAreTemp))
+                       if (!FileTransform::Prediffing(m_infoPrediffer.get(), strFileTemp[file], m_sToFindPrediffer, m_bPathsAreTemp))
                        {
                                // display a message box
-                               String sError = string_format(
+                               String sError = strutils::format(
                                        _T("An error occurred while prediffing the file '%s' with the plugin '%s'. The prediffing is not applied any more."),
                                        strFileTemp[file].c_str(),
-                                       m_infoPrediffer->pluginName.c_str());
+                                       m_infoPrediffer->m_PluginName.c_str());
                                AppErrorMessageBox(sError);
                                // don't use any more this prediffer
-                               m_infoPrediffer->bToBeScanned = false;
-                               m_infoPrediffer->pluginName.erase();
+                               m_infoPrediffer->m_PluginOrPredifferMode = PLUGIN_MODE::PLUGIN_MANUAL;
+                               m_infoPrediffer->m_PluginName.erase();
                        }
 
                        // We use the same plugin for both files, so it must be defined before
                        // second file
-                       assert(m_infoPrediffer->bToBeScanned == false);
+                       assert(m_infoPrediffer->m_PluginOrPredifferMode == PLUGIN_MODE::PLUGIN_MANUAL);
                }
        }
 
-       struct change *script = NULL;
-       struct change *script10 = NULL;
-       struct change *script12 = NULL;
+       struct change *script = nullptr;
+       struct change *script10 = nullptr;
+       struct change *script12 = nullptr;
        DiffFileData diffdata, diffdata10, diffdata12;
        int bin_flag = 0, bin_flag10 = 0, bin_flag12 = 0;
 
-       if (files.GetSize() == 2)
+       if (aFiles.GetSize() == 2)
        {
-               diffdata.SetDisplayFilepaths(files[0], files[1]); // store true names for diff utils patch file
+               diffdata.SetDisplayFilepaths(aFiles[0], aFiles[1]); // store true names for diff utils patch file
                // This opens & fstats both files (if it succeeds)
                if (!diffdata.OpenFiles(strFileTemp[0], strFileTemp[1]))
                {
@@ -744,9 +465,9 @@ bool CDiffWrapper::RunFileDiff()
                }
 
                // Compare the files, if no error was found.
-               // Last param (bin_file) is NULL since we don't
+               // Last param (bin_file) is `nullptr` since we don't
                // (yet) need info about binary sides.
-               bRet = Diff2Files(&script, &diffdata, &bin_flag, NULL);
+               bRet = Diff2Files(&script, &diffdata, &bin_flag, nullptr);
 
                // We don't anymore create diff-files for every rescan.
                // User can create patch-file whenever one wants to.
@@ -755,36 +476,35 @@ bool CDiffWrapper::RunFileDiff()
                // what differences diff-engine sees!
 #ifdef _DEBUG
                // throw the diff into a temp file
-               String sTempPath = env_GetTempPath(); // get path to Temp folder
-               String path = paths_ConcatPath(sTempPath, _T("Diff.txt"));
+               String sTempPath = env::GetTemporaryPath(); // get path to Temp folder
+               String path = paths::ConcatPath(sTempPath, _T("Diff.txt"));
 
-               outfile = _tfopen(path.c_str(), _T("w+"));
-               if (outfile != NULL)
+               if (_tfopen_s(&outfile, path.c_str(), _T("w+")) == 0)
                {
                        print_normal_script(script);
                        fclose(outfile);
-                       outfile = NULL;
+                       outfile = nullptr;
                }
 #endif
        }
        else
        {
-               diffdata10.SetDisplayFilepaths(files[1], files[0]); // store true names for diff utils patch file
-               diffdata12.SetDisplayFilepaths(files[1], files[2]); // store true names for diff utils patch file
+               diffdata10.SetDisplayFilepaths(aFiles[1], aFiles[0]); // store true names for diff utils patch file
+               diffdata12.SetDisplayFilepaths(aFiles[1], aFiles[2]); // store true names for diff utils patch file
 
                if (!diffdata10.OpenFiles(strFileTemp[1], strFileTemp[0]))
                {
                        return false;
                }
 
-               bRet = Diff2Files(&script10, &diffdata10, &bin_flag10, NULL);
+               bRet = Diff2Files(&script10, &diffdata10, &bin_flag10, nullptr);
 
                if (!diffdata12.OpenFiles(strFileTemp[1], strFileTemp[2]))
                {
                        return false;
                }
 
-               bRet = Diff2Files(&script12, &diffdata12, &bin_flag12, NULL);
+               bRet = Diff2Files(&script12, &diffdata12, &bin_flag12, nullptr);
        }
 
        // First determine what happened during comparison
@@ -798,19 +518,19 @@ bool CDiffWrapper::RunFileDiff()
        file_data * inf10 = diffdata10.m_inf;
        file_data * inf12 = diffdata12.m_inf;
 
-       if (files.GetSize() == 2)
+       if (aFiles.GetSize() == 2)
        {
                if (bin_flag != 0)
                {
                        m_status.bBinaries = true;
                        if (bin_flag != -1)
-                               m_status.Identical = IDENTLEVEL_ALL;
+                               m_status.Identical = IDENTLEVEL::ALL;
                        else
-                               m_status.Identical = IDENTLEVEL_NONE;
+                               m_status.Identical = IDENTLEVEL::NONE;
                }
                else
                { // text files according to diffutils, so change script exists
-                       m_status.Identical = (script == 0) ? IDENTLEVEL_ALL : IDENTLEVEL_NONE;
+                       m_status.Identical = (script == 0) ? IDENTLEVEL::ALL : IDENTLEVEL::NONE;
                        m_status.bBinaries = false;
                }
                m_status.bMissingNL[0] = !!inf[0].missing_newline;
@@ -818,30 +538,30 @@ bool CDiffWrapper::RunFileDiff()
        }
        else
        {
-               m_status.Identical = IDENTLEVEL_NONE;
+               m_status.Identical = IDENTLEVEL::NONE;
                if (bin_flag10 != 0 || bin_flag12 != 0)
                {
                        m_status.bBinaries = true;
                        if (bin_flag10 != -1 && bin_flag12 != -1)
-                               m_status.Identical = IDENTLEVEL_ALL;
+                               m_status.Identical = IDENTLEVEL::ALL;
                        else if (bin_flag10 != -1)
-                               m_status.Identical = IDENTLEVEL_EXCEPTRIGHT;
+                               m_status.Identical = IDENTLEVEL::EXCEPTRIGHT;
                        else if (bin_flag12 != -1)
-                               m_status.Identical = IDENTLEVEL_EXCEPTLEFT;
+                               m_status.Identical = IDENTLEVEL::EXCEPTLEFT;
                        else
-                               m_status.Identical = IDENTLEVEL_EXCEPTMIDDLE;
+                               m_status.Identical = IDENTLEVEL::EXCEPTMIDDLE;
                }
                else
                { // text files according to diffutils, so change script exists
                        m_status.bBinaries = false;
-                       if (script10 == 0 && script12 == 0)
-                               m_status.Identical = IDENTLEVEL_ALL;
-                       else if (script10 == 0)
-                               m_status.Identical = IDENTLEVEL_EXCEPTRIGHT;
-                       else if (script12 == 0)
-                               m_status.Identical = IDENTLEVEL_EXCEPTLEFT;
+                       if (script10 == nullptr && script12 == nullptr)
+                               m_status.Identical = IDENTLEVEL::ALL;
+                       else if (script10 == nullptr)
+                               m_status.Identical = IDENTLEVEL::EXCEPTRIGHT;
+                       else if (script12 == nullptr)
+                               m_status.Identical = IDENTLEVEL::EXCEPTLEFT;
                        else
-                               m_status.Identical = IDENTLEVEL_EXCEPTMIDDLE;
+                               m_status.Identical = IDENTLEVEL::EXCEPTMIDDLE;
                }
                m_status.bMissingNL[0] = !!inf10[1].missing_newline;
                m_status.bMissingNL[1] = !!inf12[0].missing_newline;
@@ -850,7 +570,7 @@ bool CDiffWrapper::RunFileDiff()
 
 
        // Create patch file
-       if (!m_status.bBinaries && m_bCreatePatchFile && files.GetSize() == 2)
+       if (!m_status.bBinaries && m_bCreatePatchFile && aFiles.GetSize() == 2)
        {
                WritePatchFile(script, &inf[0]);
        }
@@ -859,7 +579,7 @@ bool CDiffWrapper::RunFileDiff()
        // This is done on every WinMerge's doc rescan!
        if (!m_status.bBinaries && m_bUseDiffList)
        {
-               if (files.GetSize() == 2)
+               if (aFiles.GetSize() == 2)
                        LoadWinMergeDiffsFromDiffUtilsScript(script, diffdata.m_inf);
                else
                        LoadWinMergeDiffsFromDiffUtilsScript3(
@@ -868,13 +588,16 @@ bool CDiffWrapper::RunFileDiff()
        }                       
 
        // cleanup the script
-       if (files.GetSize() == 2)
+       if (aFiles.GetSize() == 2)
                FreeDiffUtilsScript(script);
        else
-               FreeDiffUtilsScript3(script10, script12);
+       {
+               FreeDiffUtilsScript(script10);
+               FreeDiffUtilsScript(script12);
+       }
 
        // Done with diffutils filedata
-       if (files.GetSize() == 2)
+       if (aFiles.GetSize() == 2)
        {
                diffdata.Close();
        }
@@ -887,9 +610,9 @@ bool CDiffWrapper::RunFileDiff()
        if (m_bPluginsEnabled)
        {
                // Delete temp files transformation functions possibly created
-               for (file = 0; file < files.GetSize(); file++)
+               for (file = 0; file < aFiles.GetSize(); file++)
                {
-                       if (string_compare_nocase(files[file], strFileTemp[file]) != 0)
+                       if (strutils::compare_nocase(aFiles[file], strFileTemp[file]) != 0)
                        {
                                try
                                {
@@ -947,13 +670,13 @@ void CDiffWrapper::AddDiffRange(DiffList *pDiffList, DIFFRANGE &dr)
  * @param [in] leftBufferLines size of array pane left
  * @param [in] rightBufferLines size of array pane right
  * @param [in] left on whitch side we have to insert
- * @param [in] bIgnoreBlankLines, if true we allways add a new diff and make as trivial
+ * @param [in] bIgnoreBlankLines, if true we always add a new diff and mark as trivial
  */
 void CDiffWrapper::FixLastDiffRange(int nFiles, int bufferLines[], bool bMissingNL[], bool bIgnoreBlankLines)
 {
        DIFFRANGE dr;
        const int count = m_pDiffList->GetSize();
-       if (count > 0 && !bIgnoreBlankLines)
+       if (count > 0)
        {
                m_pDiffList->GetDiff(count - 1, dr);
 
@@ -1002,50 +725,53 @@ String CDiffWrapper::FormatSwitchString() const
        
        switch (m_options.m_outputStyle)
        {
-       case OUTPUT_CONTEXT:
-               switches = _T(" C");
+       case DIFF_OUTPUT_NORMAL:
+               switches = _T(" ");
                break;
-       case OUTPUT_UNIFIED:
-               switches = _T(" U");
+       case DIFF_OUTPUT_CONTEXT:
+               switches = (m_options.m_contextLines > 0) ? _T(" -C ") : _T(" -c");
                break;
-       case OUTPUT_ED:
+       case DIFF_OUTPUT_UNIFIED:
+               switches = (m_options.m_contextLines > 0) ? _T(" -U ") : _T(" -u");
+               break;
+#if 0
+       case DIFF_OUTPUT_ED:
                switches = _T(" e");
                break;
-       case OUTPUT_FORWARD_ED:
+       case DIFF_OUTPUT_FORWARD_ED:
                switches = _T(" f");
                break;
-       case OUTPUT_RCS:
+       case DIFF_OUTPUT_RCS:
                switches = _T(" n");
                break;
-       case OUTPUT_NORMAL:
-               switches = _T(" ");
-               break;
-       case OUTPUT_IFDEF:
+       case DIFF_OUTPUT_IFDEF:
                switches = _T(" D");
                break;
-       case OUTPUT_SDIFF:
+       case DIFF_OUTPUT_SDIFF:
                switches = _T(" y");
                break;
+#endif
        }
 
-       if (m_options.m_contextLines > 0)
+       if ((m_options.m_outputStyle == DIFF_OUTPUT_CONTEXT || m_options.m_outputStyle == DIFF_OUTPUT_UNIFIED) &&
+               m_options.m_contextLines > 0)
        {
                TCHAR tmpNum[5] = {0};
-               _itot(m_options.m_contextLines, tmpNum, 10);
+               _itot_s(m_options.m_contextLines, tmpNum, 10);
                switches += tmpNum;
        }
 
        if (ignore_all_space_flag > 0)
-               switches += _T("w");
+               switches += _T(" -w");
 
        if (ignore_blank_lines_flag > 0)
-               switches += _T("B");
+               switches += _T(" -B");
 
        if (ignore_case_flag > 0)
-               switches += _T("i");
+               switches += _T(" -i");
 
        if (ignore_space_change_flag > 0)
-               switches += _T("b");
+               switches += _T(" -b");
 
        return switches;
 }
@@ -1071,7 +797,7 @@ void CDiffWrapper::SetAppendFiles(bool bAppendFiles)
  * @param [in] diffData files to compare.
  * @param [out] bin_status used to return binary status from compare.
  * @param [out] bin_file Returns which file was binary file as bitmap.
-    So if first file is binary, first bit is set etc. Can be NULL if binary file
+    So if first file is binary, first bit is set etc. Can be `nullptr` if binary file
     info is not needed (faster compare since diffutils don't bother checking
     second file if first is binary).
  * @return true when compare succeeds, false if error happened during compare.
@@ -1085,14 +811,24 @@ bool CDiffWrapper::Diff2Files(struct change ** diffs, DiffFileData *diffData,
        SE_Handler seh;
        try
        {
-               // Diff files. depth is zero because we are not 6comparing dirs
-               *diffs = diff_2_files (diffData->m_inf, 0, bin_status,
-                               (m_pMovedLines[0] != NULL), bin_file);
+               if (m_options.m_diffAlgorithm != DIFF_ALGORITHM_DEFAULT)
+               {
+                       unsigned xdl_flags = make_xdl_flags(m_options);
+                       *diffs = diff_2_files_xdiff(diffData->m_inf, (m_pMovedLines[0] != nullptr), xdl_flags);
+                       files[0] = diffData->m_inf[0];
+                       files[1] = diffData->m_inf[1];
+               }
+               else
+               {
+                       // Diff files. depth is zero because we are not comparing dirs
+                       *diffs = diff_2_files(diffData->m_inf, 0, bin_status,
+                               (m_pMovedLines[0] != nullptr), bin_file);
+               }
                CopyDiffutilTextStats(diffData->m_inf, diffData);
        }
        catch (SE_Exception&)
        {
-               *diffs = NULL;
+               *diffs = nullptr;
                bRet = false;
        }
        return bRet;
@@ -1101,43 +837,18 @@ bool CDiffWrapper::Diff2Files(struct change ** diffs, DiffFileData *diffData,
 /**
  * @brief Free script (the diffutils linked list of differences)
  */
-static void
-FreeDiffUtilsScript(struct change * & script)
+void
+CDiffWrapper::FreeDiffUtilsScript(struct change * & script)
 {
-       if (!script) return;
-       struct change *e=0, *p=0;
+       if (script == nullptr) return;
+       struct change *e=nullptr, *p=nullptr;
        // cleanup the script
-       for (e = script; e; e = p)
+       for (e = script; e != nullptr; e = p)
        {
                p = e->link;
                free(e);
        }
-       script = 0;
-}
-
-/**
- * @brief Free script (the diffutils linked list of differences)
- */
-static void
-FreeDiffUtilsScript3(struct change * & script10, struct change * & script12)
-{
-       struct change *e=0, *p=0;
-       for (e = script10; e; e = p)
-       {
-               p = e->link;
-               free (e);
-       }
-       for (e = script12; e; e = p)
-       {
-               p = e->link;
-               free (e);
-       }
-}
-
-void
-CDiffWrapper::FreeDiffUtilsScript3(struct change * & script10, struct change * & script12)
-{
-       ::FreeDiffUtilsScript3(script10, script12);
+       script = nullptr;
 }
 
 /**
@@ -1150,24 +861,23 @@ CDiffWrapper::FreeDiffUtilsScript3(struct change * & script10, struct change * &
  * @param [in] FileNo File to match.
  * return true if any of the expressions matches.
  */
-bool CDiffWrapper::RegExpFilter(int StartPos, int EndPos, int FileNo) const
+bool CDiffWrapper::RegExpFilter(int StartPos, int EndPos, const file_data *pinf) const
 {
-       if (m_pFilterList == NULL)
+       if (m_pFilterList == nullptr)
        {       
                throw "CDiffWrapper::RegExpFilter() called when "
-                       "filterlist doesn't exist (=NULL)";
-               return false;
+                       "filterlist doesn't exist (=nullptr)";
        }
 
        bool linesMatch = true; // set to false when non-matching line is found.
        int line = StartPos;
 
-       while (line <= EndPos && linesMatch == true)
+       while (line <= EndPos && linesMatch)
        {
-               size_t len = files[FileNo].linbuf[line + 1] - files[FileNo].linbuf[line];
-               const char *string = files[FileNo].linbuf[line];
+               size_t len = pinf->linbuf[line + 1] - pinf->linbuf[line];
+               const char *string = pinf->linbuf[line];
                size_t stringlen = linelen(string, len);
-               if (!m_pFilterList->Match(std::string(string, stringlen), m_codepage))
+               if (!m_pFilterList->Match(std::string(string, stringlen)))
 
                {
                        linesMatch = false;
@@ -1181,27 +891,14 @@ bool CDiffWrapper::RegExpFilter(int StartPos, int EndPos, int FileNo) const
  * @brief Walk the diff utils change script, building the WinMerge list of diff blocks
  */
 void
-CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const file_data * inf)
+CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const file_data * file_data_ary)
 {
        //Logic needed for Ignore comment option
-       DIFFOPTIONS options;
-       GetOptions(&options);
-       String asLwrCaseExt;
-       if (options.bFilterCommentsLines)
-       {
-               String LowerCaseExt = m_originalFile.GetLeft();
-               String::size_type PosOfDot = LowerCaseExt.rfind('.');
-               if (PosOfDot != String::npos)
-               {
-                       LowerCaseExt.erase(0, PosOfDot + 1);
-                       std::transform(LowerCaseExt.begin(), LowerCaseExt.end(), LowerCaseExt.begin(), ::tolower);
-                       asLwrCaseExt = LowerCaseExt;
-               }
-       }
+       PostFilterContext ctxt;
 
        struct change *next = script;
        
-       while (next)
+       while (next != nullptr)
        {
                /* Find a set of changes that belong together.  */
                struct change *thisob = next;
@@ -1210,7 +907,7 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const
                /* Disconnect them from the rest of the changes,
                making them a hunk, and remember the rest for next iteration.  */
                next = end->link;
-               end->link = 0;
+               end->link = nullptr;
 #ifdef DEBUG
                debug_script(thisob);
 #endif
@@ -1220,7 +917,7 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const
                {                                       
                        /* Determine range of line numbers involved in each file.  */
                        int first0=0, last0=0, first1=0, last1=0, deletes=0, inserts=0;
-                       analyze_hunk (thisob, &first0, &last0, &first1, &last1, &deletes, &inserts, inf);
+                       analyze_hunk (thisob, &first0, &last0, &first1, &last1, &deletes, &inserts, file_data_ary);
                        if (deletes || inserts || thisob->trivial)
                        {
                                OP_TYPE op = OP_NONE;
@@ -1233,59 +930,57 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const
                                
                                /* Print the lines that the first file has.  */
                                int trans_a0=0, trans_b0=0, trans_a1=0, trans_b1=0;
-                               translate_range(&inf[0], first0, last0, &trans_a0, &trans_b0);
-                               translate_range(&inf[1], first1, last1, &trans_a1, &trans_b1);
+                               translate_range(&file_data_ary[0], first0, last0, &trans_a0, &trans_b0);
+                               translate_range(&file_data_ary[1], first1, last1, &trans_a1, &trans_b1);
 
                                // Store information about these blocks in moved line info
                                if (GetDetectMovedBlocks())
                                {
                                        if (thisob->match0>=0)
                                        {
-                                               assert(thisob->inserted);
+                                               assert(thisob->inserted > 0);
                                                for (int i=0; i<thisob->inserted; ++i)
                                                {
                                                        int line0 = i+thisob->match0 + (trans_a0-first0-1);
                                                        int line1 = i+thisob->line1 + (trans_a1-first1-1);
-                                                       GetMovedLines(1)->Add(MovedLines::SIDE_LEFT, line1, line0);
+                                                       GetMovedLines(1)->Add(MovedLines::SIDE::LEFT, line1, line0);
                                                }
                                        }
                                        if (thisob->match1>=0)
                                        {
-                                               assert(thisob->deleted);
+                                               assert(thisob->deleted > 0);
                                                for (int i=0; i<thisob->deleted; ++i)
                                                {
                                                        int line0 = i+thisob->line0 + (trans_a0-first0-1);
                                                        int line1 = i+thisob->match1 + (trans_a1-first1-1);
-                                                       GetMovedLines(0)->Add(MovedLines::SIDE_RIGHT, line0, line1);
+                                                       GetMovedLines(0)->Add(MovedLines::SIDE::RIGHT, line0, line1);
                                                }
                                        }
                                }
+                               int QtyLinesLeft = (trans_b0 - trans_a0) + 1; //Determine quantity of lines in this block for left side
+                               int QtyLinesRight = (trans_b1 - trans_a1) + 1;//Determine quantity of lines in this block for right side
 
-                               if (options.bFilterCommentsLines)
-                               {
-                                       int QtyLinesLeft = (trans_b0 - trans_a0) + 1; //Determine quantity of lines in this block for left side
-                                       int QtyLinesRight = (trans_b1 - trans_a1) + 1;//Determine quantity of lines in this block for right side
-                                       PostFilter(thisob->line0, QtyLinesLeft, thisob->line1, QtyLinesRight, op, asLwrCaseExt);
-                               }
+                               if (m_options.m_filterCommentsLines ||
+                                       (m_pSubstitutionList && m_pSubstitutionList->HasRegExps()))
+                                       PostFilter(ctxt, trans_a0 - 1, QtyLinesLeft, trans_a1 - 1, QtyLinesRight, op, file_data_ary);
 
-                               if (m_pFilterList && m_pFilterList->HasRegExps())
+                               if (m_pFilterList != nullptr && m_pFilterList->HasRegExps())
                                {
-                                        //Determine quantity of lines in this block for both sides
-                                       int QtyLinesLeft = (trans_b0 - trans_a0);
-                                       int QtyLinesRight = (trans_b1 - trans_a1);
-                                       
                                        // Match lines against regular expression filters
                                        // Our strategy is that every line in both sides must
                                        // match regexp before we mark difference as ignored.
                                        bool match2 = false;
-                                       bool match1 = RegExpFilter(thisob->line0, thisob->line0 + QtyLinesLeft, 0);
+                                       bool match1 = RegExpFilter(thisob->line0, thisob->line0 + QtyLinesLeft - 1, &file_data_ary[0]);
                                        if (match1)
-                                               match2 = RegExpFilter(thisob->line1, thisob->line1 + QtyLinesRight, 1);
+                                               match2 = RegExpFilter(thisob->line1, thisob->line1 + QtyLinesRight - 1, &file_data_ary[1]);
                                        if (match1 && match2)
                                                op = OP_TRIVIAL;
                                }
 
-                               AddDiffRange(m_pDiffList, trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, op);
+                               if (op == OP_TRIVIAL && m_options.m_bCompletelyBlankOutIgnoredDiffereneces)
+                                       op = OP_NONE;
+                               if (op != OP_NONE)
+                                       AddDiffRange(m_pDiffList, trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, op);
                        }
                }
                
@@ -1333,17 +1028,19 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
        const file_data * inf10, 
        const file_data * inf12)
 {
-       DiffList diff10, diff12, *pdiff;
+       DiffList diff10, diff12;
        diff10.Clear();
        diff12.Clear();
 
        for (int file = 0; file < 2; file++)
        {
-               struct change *next;
+               struct change *next = nullptr;
                int trans_a0, trans_b0, trans_a1, trans_b1;
                int first0, last0, first1, last1, deletes, inserts;
                OP_TYPE op;
-               const file_data *pinf;
+               const file_data *pinf = nullptr;
+               DiffList *pdiff = nullptr;
+               PostFilterContext ctxt;
 
                switch (file)
                {
@@ -1351,7 +1048,7 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
                case 1: next = script12; pdiff = &diff12; pinf = inf12; break;
                }
 
-               while (next)
+               while (next != nullptr)
                {
                        /* Find a set of changes that belong together.  */
                        struct change *thisob = next;
@@ -1360,7 +1057,7 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
                        /* Disconnect them from the rest of the changes,
                        making them a hunk, and remember the rest for next iteration.  */
                        next = end->link;
-                       end->link = 0;
+                       end->link = nullptr;
 #ifdef DEBUG
                        debug_script(thisob);
 #endif
@@ -1386,29 +1083,22 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
                                        // Store information about these blocks in moved line info
                                        if (GetDetectMovedBlocks())
                                        {
-                                               int index1 = -1;
-                                               int index2 = -1;
-                                               MovedLines::ML_SIDE side1;
-                                               MovedLines::ML_SIDE side2;
-                                               if (file == 0 /* diff10 */)
-                                               {
-                                                       index1 = 0;
-                                                       index2 = 1;
-                                                       side1 = MovedLines::SIDE_RIGHT;
-                                                       side2 = MovedLines::SIDE_LEFT;
-                                               }
-                                               else if (file == 1 /* diff12 */)
+                                               int index1 = 0;  // defaults for (file == 0 /* diff10 */)
+                                               int index2 = 1;
+                                               MovedLines::SIDE side1 = MovedLines::SIDE::RIGHT;
+                                               MovedLines::SIDE side2 = MovedLines::SIDE::LEFT;
+                                               if (file == 1 /* diff12 */)
                                                {
                                                        index1 = 2;
                                                        index2 = 1;
-                                                       side1 = MovedLines::SIDE_LEFT;
-                                                       side2 = MovedLines::SIDE_RIGHT;
+                                                       side1 = MovedLines::SIDE::LEFT;
+                                                       side2 = MovedLines::SIDE::RIGHT;
                                                }
                                                if (index1 != -1 && index2 != -1)
                                                {
                                                        if (thisob->match0>=0)
                                                        {
-                                                               assert(thisob->inserted);
+                                                               assert(thisob->inserted > 0);
                                                                for (int i=0; i<thisob->inserted; ++i)
                                                                {
                                                                        int line0 = i+thisob->match0 + (trans_a0-first0-1);
@@ -1418,7 +1108,7 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
                                                        }
                                                        if (thisob->match1>=0)
                                                        {
-                                                               assert(thisob->deleted);
+                                                               assert(thisob->deleted > 0);
                                                                for (int i=0; i<thisob->deleted; ++i)
                                                                {
                                                                        int line0 = i+thisob->line0 + (trans_a0-first0-1);
@@ -1429,6 +1119,26 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
                                                }
                                        }
 
+                                       int QtyLinesLeft = (trans_b0 - trans_a0) + 1; //Determine quantity of lines in this block for left side
+                                       int QtyLinesRight = (trans_b1 - trans_a1) + 1;//Determine quantity of lines in this block for right side
+
+                                       if (m_options.m_filterCommentsLines ||
+                                               (m_pSubstitutionList && m_pSubstitutionList->HasRegExps()))
+                                               PostFilter(ctxt, trans_a0 - 1, QtyLinesLeft, trans_a1 - 1, QtyLinesRight, op, pinf);
+
+                                       if (m_pFilterList != nullptr && m_pFilterList->HasRegExps())
+                                       {
+                                               // Match lines against regular expression filters
+                                               // Our strategy is that every line in both sides must
+                                               // match regexp before we mark difference as ignored.
+                                               bool match2 = false;
+                                               bool match1 = RegExpFilter(thisob->line0, thisob->line0 + QtyLinesLeft - 1, &pinf[0]);
+                                               if (match1)
+                                                       match2 = RegExpFilter(thisob->line1, thisob->line1 + QtyLinesRight - 1, &pinf[1]);
+                                               if (match1 && match2)
+                                                       op = OP_TRIVIAL;
+                                       }
+
                                        AddDiffRange(pdiff, trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, op);
                                }
                        }
@@ -1439,35 +1149,39 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
        }
 
        Make3wayDiff(m_pDiffList->GetDiffRangeInfoVector(), diff10.GetDiffRangeInfoVector(), diff12.GetDiffRangeInfoVector(), 
-               Comp02Functor(inf10, inf12), (m_pFilterList && m_pFilterList->HasRegExps()));
+               Comp02Functor(inf10, inf12), 
+               (m_pFilterList != nullptr && m_pFilterList->HasRegExps()) || m_options.m_bIgnoreBlankLines || m_options.m_filterCommentsLines);
 }
 
-void CDiffWrapper::WritePatchFileHeader(enum output_style output_style, bool bAppendFiles)
+void CDiffWrapper::WritePatchFileHeader(enum output_style tOutput_style, bool bAppendFiles)
 {
-       outfile = NULL;
+       outfile = nullptr;
        if (!m_sPatchFile.empty())
        {
                const TCHAR *mode = (bAppendFiles ? _T("a+") : _T("w+"));
-               outfile = _tfopen(m_sPatchFile.c_str(), mode);
+               if (_tfopen_s(&outfile, m_sPatchFile.c_str(), mode) != 0)
+                       outfile = nullptr;
        }
 
-       if (!outfile)
+       if (outfile == nullptr)
        {
                m_status.bPatchFileFailed = true;
                return;
        }
 
        // Output patchfile
-       switch (output_style)
+       switch (tOutput_style)
        {
+       case OUTPUT_NORMAL:
        case OUTPUT_CONTEXT:
        case OUTPUT_UNIFIED:
+#if 0
        case OUTPUT_ED:
        case OUTPUT_FORWARD_ED:
        case OUTPUT_RCS:
-       case OUTPUT_NORMAL:
        case OUTPUT_IFDEF:
        case OUTPUT_SDIFF:
+#endif
                break;
        case OUTPUT_HTML:
                print_html_header();
@@ -1475,34 +1189,37 @@ void CDiffWrapper::WritePatchFileHeader(enum output_style output_style, bool bAp
        }
        
        fclose(outfile);
-       outfile = NULL;
+       outfile = nullptr;
 }
 
-void CDiffWrapper::WritePatchFileTerminator(enum output_style output_style)
+void CDiffWrapper::WritePatchFileTerminator(enum output_style tOutput_style)
 {
-       outfile = NULL;
+       outfile = nullptr;
        if (!m_sPatchFile.empty())
        {
-               outfile = _tfopen(m_sPatchFile.c_str(), _T("a+"));
+               if (_tfopen_s(&outfile, m_sPatchFile.c_str(), _T("a+")) != 0)
+                       outfile = nullptr;
        }
 
-       if (!outfile)
+       if (outfile == nullptr)
        {
                m_status.bPatchFileFailed = true;
                return;
        }
 
        // Output patchfile
-       switch (output_style)
+       switch (tOutput_style)
        {
+       case OUTPUT_NORMAL:
        case OUTPUT_CONTEXT:
        case OUTPUT_UNIFIED:
+#if 0
        case OUTPUT_ED:
        case OUTPUT_FORWARD_ED:
        case OUTPUT_RCS:
-       case OUTPUT_NORMAL:
        case OUTPUT_IFDEF:
        case OUTPUT_SDIFF:
+#endif
                break;
        case OUTPUT_HTML:
                print_html_terminator();
@@ -1510,7 +1227,7 @@ void CDiffWrapper::WritePatchFileTerminator(enum output_style output_style)
        }
        
        fclose(outfile);
-       outfile = NULL;
+       outfile = nullptr;
 }
 
 /**
@@ -1522,9 +1239,8 @@ void CDiffWrapper::WritePatchFileTerminator(enum output_style output_style)
  */
 void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
 {
-       file_data inf_patch[2] = {0};
-       std::memcpy(&inf_patch, inf, sizeof(file_data) * 2);
-       
+       file_data inf_patch[2] = { inf[0], inf[1] };
+
        // Get paths, primarily use alternative paths, only if they are empty
        // use full filepaths
        String path1(m_alternativePaths[0]);
@@ -1533,10 +1249,19 @@ void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
                path1 = m_files[0];
        if (path2.empty())
                path2 = m_files[1];
-       replace_char(&*path1.begin(), '\\', '/');
-       replace_char(&*path2.begin(), '\\', '/');
-       inf_patch[0].name = strdup(ucr::toSystemCP(path1).c_str());
-       inf_patch[1].name = strdup(ucr::toSystemCP(path2).c_str());
+       path1 = paths::ToUnixPath(path1);
+       path2 = paths::ToUnixPath(path2);
+       if ((inf_patch[0].linbuf && ucr::CheckForInvalidUtf8(inf_patch[0].buffer, inf_patch[0].buffered_chars)) ||
+               (inf_patch[1].linbuf && ucr::CheckForInvalidUtf8(inf_patch[1].buffer, inf_patch[1].buffered_chars)))
+       {
+               inf_patch[0].name = _strdup(ucr::toThreadCP(path1).c_str());
+               inf_patch[1].name = _strdup(ucr::toThreadCP(path2).c_str());
+       }
+       else
+       {
+               inf_patch[0].name = _strdup(ucr::toUTF8(path1).c_str());
+               inf_patch[1].name = _strdup(ucr::toUTF8(path2).c_str());
+       }
 
        // If paths in m_s1File and m_s2File point to original files, then we can use
        // them to fix potentially meaningless stats from potentially temporary files,
@@ -1544,22 +1269,23 @@ void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
        // If not, then we can't help it, and hence assert that this won't happen.
        if (!m_bPathsAreTemp)
        {
-               _tstat(m_files[0].c_str(), &inf_patch[0].stat);
-               _tstat(m_files[1].c_str(), &inf_patch[1].stat);
+               mywstat(m_files[0].c_str(), &inf_patch[0].stat);
+               mywstat(m_files[1].c_str(), &inf_patch[1].stat);
        }
        else
        {
                assert(false);
        }
 
-       outfile = NULL;
+       outfile = nullptr;
        if (!m_sPatchFile.empty())
        {
                const TCHAR *mode = (m_bAppendFiles ? _T("a+") : _T("w+"));
-               outfile = _tfopen(m_sPatchFile.c_str(), mode);
+               if (_tfopen_s(&outfile, m_sPatchFile.c_str(), mode) != 0)
+                       outfile = nullptr;
        }
 
-       if (!outfile)
+       if (outfile == nullptr)
        {
                m_status.bPatchFileFailed = true;
                return;
@@ -1577,18 +1303,21 @@ void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
 
        if (strcmp(inf[0].name, "NUL") == 0)
        {
-               free((void *)inf[0].name);
-               inf[0].name = strdup("/dev/null");
+               free((void *)inf_patch[0].name);
+               inf_patch[0].name = _strdup("/dev/null");
        }
        if (strcmp(inf[1].name, "NUL") == 0)
        {
-               free((void *)inf[1].name);
-               inf[1].name = strdup("/dev/null");
+               free((void *)inf_patch[1].name);
+               inf_patch[1].name = _strdup("/dev/null");
        }
 
        // Output patchfile
        switch (output_style)
        {
+       case OUTPUT_NORMAL:
+               print_normal_script(script);
+               break;
        case OUTPUT_CONTEXT:
                print_context_header(inf_patch, 0);
                print_context_script(script, 0);
@@ -1597,6 +1326,7 @@ void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
                print_context_header(inf_patch, 1);
                print_context_script(script, 1);
                break;
+#if 0
        case OUTPUT_ED:
                print_ed_script(script);
                break;
@@ -1606,15 +1336,13 @@ void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
        case OUTPUT_RCS:
                print_rcs_script(script);
                break;
-       case OUTPUT_NORMAL:
-               print_normal_script(script);
-               break;
        case OUTPUT_IFDEF:
                print_ifdef_script(script);
                break;
        case OUTPUT_SDIFF:
                print_sdiff_script(script);
                break;
+#endif
        case OUTPUT_HTML:
                print_html_diff_header(inf_patch);
                print_html_script(script);
@@ -1622,7 +1350,7 @@ void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
        }
        
        fclose(outfile);
-       outfile = NULL;
+       outfile = nullptr;
 
        free((void *)inf_patch[0].name);
        free((void *)inf_patch[1].name);
@@ -1642,7 +1370,7 @@ void CDiffWrapper::SetFilterList(const String& filterStr)
        }
 
        // Adding new filter without previous filter
-       if (m_pFilterList == NULL)
+       if (m_pFilterList == nullptr)
        {
                m_pFilterList.reset(new FilterList);
        }
@@ -1657,6 +1385,32 @@ void CDiffWrapper::SetFilterList(const String& filterStr)
                m_pFilterList->AddRegExp(*it);
 }
 
+void CDiffWrapper::SetFilterList(const FilterList* pFilterList)
+{
+       if (!pFilterList)
+               m_pFilterList.reset();
+       else
+       {
+               m_pFilterList.reset(new FilterList());
+               *m_pFilterList = *pFilterList;
+       }
+}
+
+const SubstitutionList* CDiffWrapper::GetSubstitutionList() const
+{
+       return m_pSubstitutionList.get();
+}
+
+void CDiffWrapper::SetSubstitutionList(std::shared_ptr<SubstitutionList> pSubstitutionList)
+{
+       m_pSubstitutionList = pSubstitutionList;
+}
+
+void CDiffWrapper::SetFilterCommentsSourceDef(const String& ext)
+{
+       m_pFilterCommentsDef = CrystalLineParser::GetTextType(ext.c_str());
+}
+
 /**
  * @brief Copy text stat results from diffutils back into the FileTextStats structure
  */