OSDN Git Service

crystaledit: Use GetProfile*()/WriteProfile*() to read and write the registry wheneve...
[winmerge-jp/winmerge-jp.git] / Src / DirScan.cpp
index 23f81bb..4f45623 100644 (file)
@@ -3,11 +3,11 @@
  *
  *  @brief Implementation of DirScan (q.v.) and helper functions
  */ 
-// ID line follows -- this is updated by SVN
-// $Id: DirScan.cpp 6909 2009-07-11 13:03:42Z kimmov $
 
+#include "pch.h"
 #include "DirScan.h"
 #include <cassert>
+#include <memory>
 #define POCO_NO_UNWINDOWS 1
 #include <Poco/Semaphore.h>
 #include <Poco/Notification.h>
 #include <Poco/AutoPtr.h>
 #include <Poco/Stopwatch.h>
 #include <Poco/Format.h>
-#include <boost/shared_ptr.hpp>
 #include "DiffThread.h"
 #include "UnicodeString.h"
 #include "DiffWrapper.h"
 #include "CompareStats.h"
 #include "FolderCmp.h"
 #include "FileFilterHelper.h"
-#include "codepage.h"
 #include "IAbortable.h"
-#include "FolderCmp.h"
 #include "DirItem.h"
 #include "DirTravel.h"
 #include "paths.h"
 #include "Plugins.h"
 #include "MergeApp.h"
+#include "OptionsDef.h"
+#include "OptionsMgr.h"
+#include "PathContext.h"
+#include "DebugNew.h"
 
-using Poco::UIntPtr;
 using Poco::NotificationQueue;
 using Poco::Notification;
 using Poco::AutoPtr;
@@ -47,15 +47,13 @@ using Poco::Environment;
 using Poco::Stopwatch;
 
 // Static functions (ie, functions only used locally)
-void CompareDiffItem(DIFFITEM &di, CDiffContext * pCtxt);
-static void StoreDiffData(DIFFITEM &di, CDiffContext * pCtxt,
-               const FolderCmp * pCmpData);
-static DIFFITEM *AddToList(const String& sLeftDir, const String& sRightDir, const DirItem * lent, const DirItem * rent,
-       unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent);
-static DIFFITEM *AddToList(const String& sLeftDir, const String& sMiddleDir, const String& sRightDir, const DirItem * lent, const DirItem * ment, const DirItem * rent,
+static void CompareDiffItem(FolderCmp &fc, DIFFITEM &di);
+static DIFFITEM *AddToList(const String &sLeftDir, const String &sRightDir, const DirItem *lent, const DirItem *rent,
        unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent);
-static void UpdateDiffItem(DIFFITEM & di, bool & bExists, CDiffContext *pCtxt);
-static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, UIntPtr parentdiffpos);
+static DIFFITEM *AddToList(const String &sDir1, const String &sDir2, const String &sDir3, const DirItem *ent1, const DirItem *ent2, const DirItem *ent3,
+       unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent, int nItems = 3);
+static void UpdateDiffItem(DIFFITEM &di, bool &bExists, CDiffContext *pCtxt);
+static int CompareItems(NotificationQueue &queue, DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos);
 
 class WorkNotification: public Poco::Notification
 {
@@ -71,7 +69,7 @@ private:
 class WorkCompletedNotification: public Poco::Notification
 {
 public:
-       WorkCompletedNotification(DIFFITEM& di): m_di(di) {}
+       explicit WorkCompletedNotification(DIFFITEM& di): m_di(di) {}
        DIFFITEM& data() const { return m_di; }
 private:
        DIFFITEM& m_di;
@@ -80,22 +78,24 @@ private:
 class DiffWorker: public Runnable
 {
 public:
-       DiffWorker(NotificationQueue& queue, CDiffContext *pCtxt):
-         m_queue(queue), m_pCtxt(pCtxt) {}
+       DiffWorker(NotificationQueue& queue, CDiffContext *pCtxt, int id):
+         m_queue(queue), m_pCtxt(pCtxt), m_id(id) {}
 
        void run()
        {
+               FolderCmp fc(m_pCtxt);
                // keep the scripts alive during the Rescan
                // when we exit the thread, we delete this and release the scripts
                CAssureScriptsForThread scriptsForRescan;
 
                AutoPtr<Notification> pNf(m_queue.waitDequeueNotification());
-               while (pNf)
+               while (pNf.get() != nullptr)
                {
                        WorkNotification* pWorkNf = dynamic_cast<WorkNotification*>(pNf.get());
-                       if (pWorkNf) {
+                       if (pWorkNf != nullptr) {
+                               m_pCtxt->m_pCompareStats->BeginCompare(&pWorkNf->data(), m_id);
                                if (!m_pCtxt->ShouldAbort())
-                                       CompareDiffItem(pWorkNf->data(), m_pCtxt);
+                                       CompareDiffItem(fc, pWorkNf->data());
                                pWorkNf->queueResult().enqueueNotification(new WorkCompletedNotification(pWorkNf->data()));
                        }
                        pNf = m_queue.waitDequeueNotification();
@@ -105,15 +105,16 @@ public:
 private:
        NotificationQueue& m_queue;
        CDiffContext *m_pCtxt;
+       int m_id;
 };
 
-typedef boost::shared_ptr<DiffWorker> DiffWorkerPtr;
+typedef std::shared_ptr<DiffWorker> DiffWorkerPtr;
 
 /**
  * @brief Collect file- and folder-names to list.
  * This function walks given folders and adds found subfolders and files into
  * lists. There are two modes, determined by the @p depth:
- * - in non-recursive mode we walk only given folders, and add files.
+ * - in non-recursive mode we walk only given folders, and add files
  *   contained. Subfolders are added as folder items, not walked into.
  * - in recursive mode we walk all subfolders and add the files they
  *   contain into list.
@@ -126,7 +127,7 @@ typedef boost::shared_ptr<DiffWorker> DiffWorkerPtr;
  * @param [in] rightsubdir Right side subdirectory under root path
  * @param [in] bRightUniq Is right-side folder unique folder?
  * @param [in] myStruct Compare-related data, like context etc.
- * @param [in] casesensitive Is filename compare casesensitive?
+ * @param [in] casesensitive Is filename compare case sensitive?
  * @param [in] depth Levels of subdirectories to scan, -1 scans all
  * @param [in] parent Folder diff item to be scanned
  * @param [in] bUniques If true, walk into unique folders.
@@ -143,21 +144,20 @@ int DirScan_GetItems(const PathContext &paths, const String subdir[],
        String sDir[3];
        String subprefix[3];
 
-       int nIndex;
        std::copy(paths.begin(), paths.end(), sDir);
 
        if (!subdir[0].empty())
        {
-               for (nIndex = 0; nIndex < paths.GetSize(); nIndex++)
+               for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
                {
-                       sDir[nIndex] = paths_ConcatPath(sDir[nIndex], subdir[nIndex]);
+                       sDir[nIndex] = paths::ConcatPath(sDir[nIndex], subdir[nIndex]);
                        subprefix[nIndex] = subdir[nIndex] + backslash;
                }
        }
 
-       DirItemArray dirs[3], files[3];
-       for (nIndex = 0; nIndex < nDirs; nIndex++)
-               LoadAndSortFiles(sDir[nIndex], &dirs[nIndex], &files[nIndex], casesensitive);
+       DirItemArray dirs[3], aFiles[3];
+       for (int nIndex = 0; nIndex < nDirs; nIndex++)
+               LoadAndSortFiles(sDir[nIndex], &dirs[nIndex], &aFiles[nIndex], casesensitive);
 
        // Allow user to abort scanning
        if (pCtxt->ShouldAbort())
@@ -167,14 +167,16 @@ int DirScan_GetItems(const PathContext &paths, const String subdir[],
        // i points to current directory in left list (leftDirs)
        // j points to current directory in right list (rightDirs)
 
-       for (nIndex = 0; nIndex < nDirs; nIndex++)
-               if (dirs[nIndex].size() != 0 || files[nIndex].size() != 0) break;
-       if (nIndex == nDirs)
-               return 0;
+       {
+               int nIndex;
+               for (nIndex = 0; nIndex < nDirs; nIndex++)
+                       if (dirs[nIndex].size() != 0 || aFiles[nIndex].size() != 0) break;
+               if (nIndex == nDirs)
+                       return 0;
+       }
 
        DirItemArray::size_type i=0, j=0, k=0;
-       unsigned nDiffCode;
-       while (1)
+       while (true)
        {
                if (pCtxt->ShouldAbort())
                        return -1;
@@ -182,237 +184,144 @@ int DirScan_GetItems(const PathContext &paths, const String subdir[],
                if (i >= dirs[0].size() && j >= dirs[1].size() && (nDirs < 3 || k >= dirs[2].size()))
                        break;
 
+               unsigned nDiffCode = DIFFCODE::DIR;
                // Comparing directories leftDirs[i].name to rightDirs[j].name
-#ifdef _DEBUG
-TCHAR buf[1024];
-if (nDirs == 2)
-       wsprintf(buf, _T("%s %s\n"), (i < dirs[0].size()) ? dirs[0][i].filename.get().c_str() : _T(""), (j < dirs[1].size()) ? dirs[1][j].filename.get().c_str() : _T(""));
-else
-       wsprintf(buf, _T("%s %s %s\n"), (i < dirs[0].size()) ? dirs[0][i].filename.get().c_str() : _T(""), (j < dirs[1].size()) ?  dirs[1][j].filename.get().c_str() : _T(""), (k < dirs[2].size()) ? dirs[2][k].filename.get().c_str() : _T(""));
-OutputDebugString(buf);
-#endif
-
                if (i<dirs[0].size() && (j==dirs[1].size() || collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive)<0)
                        && (nDirs < 3 ||      (k==dirs[2].size() || collstr(dirs[0][i].filename, dirs[2][k].filename, casesensitive)<0) ))
                {
-                       nDiffCode = DIFFCODE::FIRST | DIFFCODE::DIR;
-                       if (!bUniques)
-                       {
-                               if (nDirs < 3)
-                                       AddToList(subdir[0], subdir[1], &dirs[0][i], 0, nDiffCode, myStruct, parent);
-                               else
-                                       AddToList(subdir[0], subdir[1], subdir[2], &dirs[0][i], 0, 0, nDiffCode, myStruct, parent);
-                               // Advance left pointer over left-only entry, and then retest with new pointers
-                               ++i;
-                               continue;
-                       }
+                       nDiffCode |= DIFFCODE::FIRST;
                }
                else if (j<dirs[1].size() && (i==dirs[0].size() || collstr(dirs[1][j].filename, dirs[0][i].filename, casesensitive)<0)
                        && (nDirs < 3 ||      (k==dirs[2].size() || collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive)<0) ))
                {
-                       nDiffCode = DIFFCODE::SECOND | DIFFCODE::DIR;
-                       if (!bUniques)
-                       {
-                               if (nDirs < 3)
-                                       AddToList(subdir[0], subdir[1], 0, &dirs[1][j], nDiffCode, myStruct, parent);
-                               else
-                                       AddToList(subdir[0], subdir[1], subdir[2], 0, &dirs[1][j], 0, nDiffCode, myStruct, parent);
-                               // Advance right pointer over right-only entry, and then retest with new pointers
-                               ++j;
-                               continue;
-                       }
+                       nDiffCode |= DIFFCODE::SECOND;
                }
                else if (nDirs < 3)
                {
-                       nDiffCode = DIFFCODE::BOTH | DIFFCODE::DIR;
+                       nDiffCode |= DIFFCODE::BOTH;
                }
                else
                {
                        if (k<dirs[2].size() && (i==dirs[0].size() || collstr(dirs[2][k].filename, dirs[0][i].filename, casesensitive)<0)
                                &&                     (j==dirs[1].size() || collstr(dirs[2][k].filename, dirs[1][j].filename, casesensitive)<0) )
                        {
-                               nDiffCode = DIFFCODE::THIRD | DIFFCODE::DIR;
-                               if (!bUniques)
-                               {
-                                       AddToList(subdir[0], subdir[1], subdir[2], 0, 0, &dirs[2][k], nDiffCode, myStruct, parent);
-                                       ++k;
-                                       // Advance right pointer over right-only entry, and then retest with new pointers
-                                       continue;
-
-                               }       
-
+                               nDiffCode |= DIFFCODE::THIRD;
                        }
                        else if ((i<dirs[0].size() && j<dirs[1].size() && collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive) == 0)
                                && (k==dirs[2].size() || collstr(dirs[2][k].filename, dirs[0][i].filename, casesensitive) != 0))
                        {
-                               nDiffCode = DIFFCODE::FIRST | DIFFCODE::SECOND | DIFFCODE::DIR;
-                               if (!bUniques)
-                               {
-                                       AddToList(subdir[0], subdir[1], subdir[2], &dirs[0][i], &dirs[1][j], 0, nDiffCode, myStruct, parent);
-                                       ++i;
-                                       ++j;
-                                       continue;       
-                               }
+                               nDiffCode |= DIFFCODE::FIRST | DIFFCODE::SECOND;
                        }
                        else if ((i<dirs[0].size() && k<dirs[2].size() && collstr(dirs[0][i].filename, dirs[2][k].filename, casesensitive) == 0)
                                && (j==dirs[1].size() || collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive) != 0))
                        {
-                               nDiffCode = DIFFCODE::FIRST | DIFFCODE::THIRD | DIFFCODE::DIR;
-                               if (!bUniques)
-                               {
-                                       AddToList(subdir[0], subdir[1], subdir[2], &dirs[0][i], 0, &dirs[2][k], nDiffCode, myStruct, parent);
-                                       ++i;
-                                       ++k;
-                                       continue;
-                               }
+                               nDiffCode |= DIFFCODE::FIRST | DIFFCODE::THIRD;
                        }
                        else if ((j<dirs[1].size() && k<dirs[2].size() && collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive) == 0)
                                && (i==dirs[0].size() || collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive) != 0))
                        {
-                               nDiffCode = DIFFCODE::SECOND | DIFFCODE::THIRD | DIFFCODE::DIR;
-                               if (!bUniques)
-                               {
-                                       AddToList(subdir[0], subdir[1], subdir[2], 0, &dirs[1][j], &dirs[2][k], nDiffCode, myStruct, parent);
-                                       ++j;
-                                       ++k;
-                                       continue;
-                               }
+                               nDiffCode |= DIFFCODE::SECOND | DIFFCODE::THIRD;
                        }
                        else
                        {
-                               nDiffCode = DIFFCODE::ALL | DIFFCODE::DIR;
+                               nDiffCode |= DIFFCODE::ALL;
                        }
                }
 
+               String leftnewsub;
+               String rightnewsub;
+               String middlenewsub;
+               if (nDirs < 3)
+               {
+                       leftnewsub  = (nDiffCode & DIFFCODE::FIRST)  ? subprefix[0] + dirs[0][i].filename.get() : subprefix[0] + dirs[1][j].filename.get();
+                       rightnewsub = (nDiffCode & DIFFCODE::SECOND) ? subprefix[1] + dirs[1][j].filename.get() : subprefix[1] + dirs[0][i].filename.get();
+
+                       // Test against filter so we don't include contents of filtered out directories
+                       // Also this is only place we can test for both-sides directories in recursive compare
+                       if ((pCtxt->m_piFilterGlobal!=nullptr && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, rightnewsub)) ||
+                               (pCtxt->m_bIgnoreReparsePoints && (
+                               (nDiffCode & DIFFCODE::FIRST) && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
+                                       (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
+                                       )
+                               )
+                               nDiffCode |= DIFFCODE::SKIPPED;
+               }
+               else
+               {
+                       leftnewsub   = subprefix[0];
+                       if (nDiffCode & DIFFCODE::FIRST)       leftnewsub += dirs[0][i].filename;
+                       else if (nDiffCode & DIFFCODE::SECOND) leftnewsub += dirs[1][j].filename;
+                       else if (nDiffCode & DIFFCODE::THIRD)  leftnewsub += dirs[2][k].filename;
+                       middlenewsub = subprefix[1];
+                       if (nDiffCode & DIFFCODE::SECOND)      middlenewsub += dirs[1][j].filename;
+                       else if (nDiffCode & DIFFCODE::FIRST)  middlenewsub += dirs[0][i].filename;
+                       else if (nDiffCode & DIFFCODE::THIRD)  middlenewsub += dirs[2][k].filename;
+                       rightnewsub  = subprefix[2];
+                       if (nDiffCode & DIFFCODE::THIRD)       rightnewsub += dirs[2][k].filename;
+                       else if (nDiffCode & DIFFCODE::FIRST)  rightnewsub += dirs[0][i].filename;
+                       else if (nDiffCode & DIFFCODE::SECOND) rightnewsub += dirs[1][j].filename;
+
+                       // Test against filter so we don't include contents of filtered out directories
+                       // Also this is only place we can test for both-sides directories in recursive compare
+                       if ((pCtxt->m_piFilterGlobal!=nullptr && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, middlenewsub, rightnewsub)) ||
+                               (pCtxt->m_bIgnoreReparsePoints && (
+                                 (nDiffCode & DIFFCODE::FIRST)  && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
+                                 (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
+                                 (nDiffCode & DIFFCODE::THIRD)  && (dirs[2][k].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
+                               )
+                          )
+                               nDiffCode |= DIFFCODE::SKIPPED;
+               }
+
                // add to list
                if (!depth)
                {
                        if (nDirs < 3)
                                AddToList(subdir[0], subdir[1], 
-                                       nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL
-                                       nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
+                                       (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr
+                                       (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
                                        nDiffCode, myStruct, parent);
                        else
                                AddToList(subdir[0], subdir[1], subdir[2], 
-                                       nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL,
-                                       nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
-                                       nDiffCode & DIFFCODE::THIRD  ? &dirs[2][k] : NULL,
+                                       (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr,
+                                       (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
+                                       (nDiffCode & DIFFCODE::THIRD ) ? &dirs[2][k] : nullptr,
                                        nDiffCode, myStruct, parent);
                }
                else
                {
                        // Recursive compare
-                       String leftnewsub;
-                       String rightnewsub;
-                       String middlenewsub;
-                       if (nDirs < 3)
-                       {
-                               leftnewsub  = (nDiffCode & DIFFCODE::FIRST)  ? subprefix[0] + dirs[0][i].filename.get() : subprefix[0] + dirs[1][j].filename.get();
-                               rightnewsub = (nDiffCode & DIFFCODE::SECOND) ? subprefix[1] + dirs[1][j].filename.get() : subprefix[1] + dirs[0][i].filename.get();
-                       }
-                       else
-                       {
-                               leftnewsub   = subprefix[0];
-                               if (nDiffCode & DIFFCODE::FIRST)       leftnewsub += dirs[0][i].filename;
-                               else if (nDiffCode & DIFFCODE::SECOND) leftnewsub += dirs[1][j].filename;
-                               else if (nDiffCode & DIFFCODE::THIRD)  leftnewsub += dirs[2][k].filename;
-                               middlenewsub = subprefix[1];
-                               if (nDiffCode & DIFFCODE::SECOND)      middlenewsub += dirs[1][j].filename;
-                               else if (nDiffCode & DIFFCODE::FIRST)  middlenewsub += dirs[0][i].filename;
-                               else if (nDiffCode & DIFFCODE::THIRD)  middlenewsub += dirs[2][k].filename;
-                               rightnewsub  = subprefix[2];
-                               if (nDiffCode & DIFFCODE::THIRD)       rightnewsub += dirs[2][k].filename;
-                               else if (nDiffCode & DIFFCODE::FIRST)  rightnewsub += dirs[0][i].filename;
-                               else if (nDiffCode & DIFFCODE::SECOND) rightnewsub += dirs[1][j].filename;
-                       }
                        if (nDirs < 3)
                        {
-                               // Test against filter so we don't include contents of filtered out directories
-                               // Also this is only place we can test for both-sides directories in recursive compare
-                               if ((pCtxt->m_piFilterGlobal && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, rightnewsub)) ||
-                                       (pCtxt->m_bIgnoreReparsePoints && (
-                                         (nDiffCode & DIFFCODE::FIRST)  && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
-                                         (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
-                                       )
-                                  )
-                               {
-                                       nDiffCode |= DIFFCODE::SKIPPED;
-                                       AddToList(subdir[0], subdir[1], 
-                                               nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL, 
-                                               nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
-                                               nDiffCode, myStruct, parent);
-                               }
-                               else
+                               DIFFITEM *me = AddToList(subdir[0], subdir[1], 
+                                       (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr, 
+                                       (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
+                                       nDiffCode, myStruct, parent);
+                               if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::BOTH || bUniques))
                                {
-                                       DIFFITEM *me = AddToList(subdir[0], subdir[1], 
-                                               nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL, 
-                                               nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
-                                               nDiffCode, myStruct, parent);
                                        // Scan recursively all subdirectories too, we are not adding folders
                                        String newsubdir[3] = {leftnewsub, rightnewsub};
                                        int result = DirScan_GetItems(paths, newsubdir, myStruct, casesensitive,
                                                        depth - 1, me, bUniques);
                                        if (result == -1)
                                                return -1;
-/*
-                                       if (result == 0)
-                                       {
-                                               if (!(nDiffCode & DIFFCODE::FIRST) || !(nDiffCode & DIFFCODE::SECOND))
-                                               {
-                                                       AddToList(subdir[0], subdir[1], 
-                                                               nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL, 
-                                                               nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
-                                                               nDiffCode, myStruct, parent);
-                                               }
-                                       }
-*/
                                }
                        }
                        else
                        {
-                               // Test against filter so we don't include contents of filtered out directories
-                               // Also this is only place we can test for both-sides directories in recursive compare
-                               if ((pCtxt->m_piFilterGlobal && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, middlenewsub, rightnewsub)) ||
-                                       (pCtxt->m_bIgnoreReparsePoints && (
-                                         (nDiffCode & DIFFCODE::FIRST)  && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
-                                         (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
-                                         (nDiffCode & DIFFCODE::THIRD)  && (dirs[2][k].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
-                                       )
-                                  )
-                               {
-                                       nDiffCode |= DIFFCODE::SKIPPED;
-                                       AddToList(subdir[0], subdir[1], subdir[2], 
-                                               nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL,
-                                               nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
-                                               nDiffCode & DIFFCODE::THIRD  ? &dirs[2][k] : NULL,
-                                               nDiffCode, myStruct, parent);
-                               }
-                               else
+                               DIFFITEM *me = AddToList(subdir[0], subdir[1], subdir[2], 
+                                       (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr,
+                                       (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
+                                       (nDiffCode & DIFFCODE::THIRD ) ? &dirs[2][k] : nullptr,
+                                       nDiffCode, myStruct, parent);
+                               if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::ALL || bUniques))
                                {
-                                       DIFFITEM *me = AddToList(subdir[0], subdir[1], subdir[2], 
-                                               nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL,
-                                               nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
-                                               nDiffCode & DIFFCODE::THIRD  ? &dirs[2][k] : NULL,
-                                               nDiffCode, myStruct, parent);
                                        // Scan recursively all subdirectories too, we are not adding folders
                                        String newsubdir[3] = {leftnewsub, middlenewsub, rightnewsub};
                                        int result = DirScan_GetItems(paths, newsubdir, myStruct, casesensitive,
                                                        depth - 1, me, bUniques);
                                        if (result == -1)
                                                return -1;
-/*
-                                       if (result == 0)
-                                       {
-                                               if (!(nDiffCode & DIFFCODE::FIRST) || !(nDiffCode & DIFFCODE::SECOND) || !(nDiffCode & DIFFCODE::THIRD))
-                                               {
-                                                       AddToList(subdir[0], subdir[1], subdir[2], 
-                                                               nDiffCode & DIFFCODE::FIRST  ? &dirs[0][i] : NULL,
-                                                               nDiffCode & DIFFCODE::SECOND ? &dirs[1][j] : NULL,
-                                                               nDiffCode & DIFFCODE::THIRD  ? &dirs[2][k] : NULL,
-                                                               nDiffCode, myStruct, parent);
-                                               }
-                                       }
-*/
                                }
                        }
                }
@@ -424,116 +333,107 @@ OutputDebugString(buf);
                        k++;
        }
        // Handle files
-       // i points to current file in left list (files[0])
-       // j points to current file in right list (files[1])
+       // i points to current file in left list (aFiles[0])
+       // j points to current file in right list (aFiles[1])
        i=0, j=0, k=0;
-       while (1)
+       while (true)
        {
                if (pCtxt->ShouldAbort())
                        return -1;
 
 
-               // Comparing file files[0][i].name to files[1][j].name
-#ifdef _DEBUG
-TCHAR buf[1024];
-if (nDirs == 2)
-       wsprintf(buf, _T("%s %s\n"), (i < files[0].size()) ? files[0][i].filename.get().c_str() : _T(""), (j < files[1].size()) ? files[1][j].filename.get().c_str() : _T(""));
-else
-       wsprintf(buf, _T("%s %s %s\n"), (i < files[0].size()) ? files[0][i].filename.get().c_str() : _T(""), (j < files[1].size()) ?  files[1][j].filename.get().c_str() : _T(""), 
-(k < files[2].size()) ? files[2][k].filename.get().c_str() : _T(""));
-OutputDebugString(buf);
-#endif
-               if (i<files[0].size() && (j==files[1].size() ||
-                               collstr(files[0][i].filename, files[1][j].filename, casesensitive) < 0)
+               // Comparing file aFiles[0][i].name to aFiles[1][j].name
+               if (i<aFiles[0].size() && (j==aFiles[1].size() ||
+                               collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) < 0)
                        && (nDirs < 3 || 
-                               (k==files[2].size() || collstr(files[0][i].filename, files[2][k].filename, casesensitive)<0) ))
+                               (k==aFiles[2].size() || collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive)<0) ))
                {
                        if (nDirs < 3)
                        {
                                const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], &files[0][i], 0, nDiffCode, myStruct, parent);
+                               AddToList(subdir[0], subdir[1], &aFiles[0][i], nullptr, nDiffCode, myStruct, parent);
                        }
                        else
                        {
                                const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], subdir[2], &files[0][i], 0, 0, nDiffCode, myStruct, parent);
+                               AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], nullptr, nullptr, nDiffCode, myStruct, parent);
                        }
                        // Advance left pointer over left-only entry, and then retest with new pointers
                        ++i;
                        continue;
                }
-               if (j<files[1].size() && (i==files[0].size() ||
-                               collstr(files[0][i].filename, files[1][j].filename, casesensitive) > 0)
+               if (j<aFiles[1].size() && (i==aFiles[0].size() ||
+                               collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) > 0)
                        && (nDirs < 3 ||
-                               (k==files[2].size() || collstr(files[1][j].filename, files[2][k].filename, casesensitive)<0) ))
+                               (k==aFiles[2].size() || collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive)<0) ))
                {
                        const unsigned nDiffCode = DIFFCODE::SECOND | DIFFCODE::FILE;
                        if (nDirs < 3)
-                               AddToList(subdir[0], subdir[1], 0, &files[1][j], nDiffCode, myStruct, parent);
+                               AddToList(subdir[0], subdir[1], nullptr, &aFiles[1][j], nDiffCode, myStruct, parent);
                        else
-                               AddToList(subdir[0], subdir[1], subdir[2], 0, &files[1][j], 0, nDiffCode, myStruct, parent);
+                               AddToList(subdir[0], subdir[1], subdir[2], nullptr, &aFiles[1][j], nullptr, nDiffCode, myStruct, parent);
                        // Advance right pointer over right-only entry, and then retest with new pointers
                        ++j;
                        continue;
                }
                if (nDirs == 3)
                {
-                       if (k<files[2].size() && (i==files[0].size() ||
-                                       collstr(files[2][k].filename, files[0][i].filename, casesensitive)<0)
-                               && (j==files[1].size() || collstr(files[2][k].filename, files[1][j].filename, casesensitive)<0) )
+                       if (k<aFiles[2].size() && (i==aFiles[0].size() ||
+                                       collstr(aFiles[2][k].filename, aFiles[0][i].filename, casesensitive)<0)
+                               && (j==aFiles[1].size() || collstr(aFiles[2][k].filename, aFiles[1][j].filename, casesensitive)<0) )
                        {
-                               int nDiffCode = DIFFCODE::THIRD | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], subdir[2], 0, 0, &files[2][k], nDiffCode, myStruct, parent);
+                               const unsigned nDiffCode = DIFFCODE::THIRD | DIFFCODE::FILE;
+                               AddToList(subdir[0], subdir[1], subdir[2], nullptr, nullptr, &aFiles[2][k], nDiffCode, myStruct, parent);
                                ++k;
                                // Advance right pointer over right-only entry, and then retest with new pointers
                                continue;
                        }
 
-                       if ((i<files[0].size() && j<files[1].size() && collstr(files[0][i].filename, files[1][j].filename, casesensitive) == 0)
-                           && (k==files[2].size() || collstr(files[0][i].filename, files[2][k].filename, casesensitive) != 0))
+                       if ((i<aFiles[0].size() && j<aFiles[1].size() && collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) == 0)
+                           && (k==aFiles[2].size() || collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive) != 0))
                        {
-                               int nDiffCode = DIFFCODE::FIRST | DIFFCODE::SECOND | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], subdir[2], &files[0][i], &files[1][j], 0, nDiffCode, myStruct, parent);
+                               const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::SECOND | DIFFCODE::FILE;
+                               AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], &aFiles[1][j], nullptr, nDiffCode, myStruct, parent);
                                ++i;
                                ++j;
                                continue;
                        }
-                       else if ((i<files[0].size() && k<files[2].size() && collstr(files[0][i].filename, files[2][k].filename, casesensitive) == 0)
-                           && (j==files[1].size() || collstr(files[1][j].filename, files[2][k].filename, casesensitive) != 0))
+                       else if ((i<aFiles[0].size() && k<aFiles[2].size() && collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive) == 0)
+                           && (j==aFiles[1].size() || collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive) != 0))
                        {
-                               int nDiffCode = DIFFCODE::FIRST | DIFFCODE::THIRD | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], subdir[2], &files[0][i], 0, &files[2][k], nDiffCode, myStruct, parent);
+                               const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::THIRD | DIFFCODE::FILE;
+                               AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], nullptr, &aFiles[2][k], nDiffCode, myStruct, parent);
                                ++i;
                                ++k;
                                continue;
                        }
-                       else if ((j<files[1].size() && k<files[2].size() && collstr(files[1][j].filename, files[2][k].filename, casesensitive) == 0)
-                           && (i==files[0].size() || collstr(files[0][i].filename, files[1][j].filename, casesensitive) != 0))
+                       else if ((j<aFiles[1].size() && k<aFiles[2].size() && collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive) == 0)
+                           && (i==aFiles[0].size() || collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) != 0))
                        {
-                               int nDiffCode = DIFFCODE::SECOND | DIFFCODE::THIRD | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], subdir[2], 0, &files[1][j], &files[2][k], nDiffCode, myStruct, parent);
+                               const unsigned nDiffCode = DIFFCODE::SECOND | DIFFCODE::THIRD | DIFFCODE::FILE;
+                               AddToList(subdir[0], subdir[1], subdir[2], nullptr, &aFiles[1][j], &aFiles[2][k], nDiffCode, myStruct, parent);
                                ++j;
                                ++k;
                                continue;
                        }
                }
-               if (i<files[0].size())
+               if (i<aFiles[0].size())
                {
                        if (nDirs < 3)
                        {
-                               assert(j<files[1].size());
-                               const int nDiffCode = DIFFCODE::BOTH | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], &files[0][i], &files[1][j], nDiffCode, myStruct, parent);
+                               assert(j<aFiles[1].size());
+                               const unsigned nDiffCode = DIFFCODE::BOTH | DIFFCODE::FILE;
+                               AddToList(subdir[0], subdir[1], &aFiles[0][i], &aFiles[1][j], nDiffCode, myStruct, parent);
                                ++i;
                                ++j;
                                continue;
                        }
                        else
                        {
-                               assert(j<files[1].size());
-                               assert(k<files[2].size());
-                               const int nDiffCode = DIFFCODE::ALL | DIFFCODE::FILE;
-                               AddToList(subdir[0], subdir[1], subdir[2], &files[0][i], &files[1][j], &files[2][k], nDiffCode, myStruct, parent);
+                               assert(j<aFiles[1].size());
+                               assert(k<aFiles[2].size());
+                               const unsigned nDiffCode = DIFFCODE::ALL | DIFFCODE::FILE;
+                               AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], &aFiles[1][j], &aFiles[2][k], nDiffCode, myStruct, parent);
                                ++i;
                                ++j;
                                ++k;
@@ -542,6 +442,25 @@ OutputDebugString(buf);
                }
                break;
        }
+
+       if (parent != nullptr)
+       {
+               for (int nIndex = 0; nIndex < nDirs; ++nIndex)
+                       if (parent->diffcode.exists(nIndex) && parent->diffFileInfo[nIndex].size == DirItem::FILE_SIZE_NONE)
+                               parent->diffFileInfo[nIndex].size = 0;
+       
+               DIFFITEM *dic = parent->GetFirstChild();
+               while (dic)
+               {
+                       for (int nIndex = 0; nIndex < nDirs; ++nIndex)
+                       {
+                               if (dic->diffFileInfo[nIndex].size != DirItem::FILE_SIZE_NONE)
+                                       parent->diffFileInfo[nIndex].size += dic->diffFileInfo[nIndex].size;
+                       }
+                       dic = dic->GetFwdSiblingLink();
+               }
+       }
+
        return 1;
 }
 
@@ -552,16 +471,29 @@ OutputDebugString(buf);
  * @param parentdiffpos [in] Position of parent diff item 
  * @return >= 0 number of diff items, -1 if compare was aborted
  */
-int DirScan_CompareItems(DiffFuncStruct *myStruct, UIntPtr parentdiffpos)
+int DirScan_CompareItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
 {
-       ThreadPool threadPool;
+       const int compareMethod = myStruct->context->GetCompareMethod();
+       int nworkers = 1;
+
+       if (compareMethod == CMP_CONTENT || compareMethod == CMP_QUICK_CONTENT)
+       {
+               nworkers = GetOptionsMgr()->GetInt(OPT_CMP_COMPARE_THREADS);
+               if (nworkers <= 0)
+               {
+                       nworkers += Environment::processorCount();
+                       if (nworkers <= 0)
+                               nworkers = 1;
+               }
+       }
+
+       ThreadPool threadPool(nworkers, nworkers);
        std::vector<DiffWorkerPtr> workers;
-       size_t nworkers = Environment::processorCount();
        NotificationQueue queue;
-
-       for (size_t i = 0; i < nworkers; ++i)
+       myStruct->context->m_pCompareStats->SetCompareThreadCount(nworkers);
+       for (int i = 0; i < nworkers; ++i)
        {
-               workers.push_back(DiffWorkerPtr(new DiffWorker(queue, myStruct->context)));
+               workers.push_back(DiffWorkerPtr(new DiffWorker(queue, myStruct->context, i)));
                threadPool.start(*workers[i]);
        }
 
@@ -574,18 +506,19 @@ int DirScan_CompareItems(DiffFuncStruct *myStruct, UIntPtr parentdiffpos)
        return res;
 }
 
-static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, UIntPtr parentdiffpos)
+static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
 {
        NotificationQueue queueResult;
        Stopwatch stopwatch;
        CDiffContext *pCtxt = myStruct->context;
        int res = 0;
        int count = 0;
-       if (!parentdiffpos)
+       bool bCompareFailure = false;
+       if (parentdiffpos == nullptr)
                myStruct->pSemaphore->wait();
        stopwatch.start();
-       UIntPtr pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
-       while (pos)
+       DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
+       while (pos != nullptr)
        {
                if (pCtxt->ShouldAbort())
                        break;
@@ -597,26 +530,40 @@ static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, UInt
                        stopwatch.restart();
                }
                myStruct->pSemaphore->wait();
-               UIntPtr curpos = pos;
+               DIFFITEM *curpos = pos;
                DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
-               bool existsalldirs = ((pCtxt->GetCompareDirs() == 2 && di.diffcode.isSideBoth()) || (pCtxt->GetCompareDirs() == 3 && di.diffcode.isSideAll()));
+               bool existsalldirs = di.diffcode.existAll();
                if (di.diffcode.isDirectory() && pCtxt->m_bRecursive)
                {
-                       di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
+                       if ((di.diffcode.diffcode & DIFFCODE::CMPERR) != DIFFCODE::CMPERR)
+                       {       // Only clear DIFF|SAME flags if not CMPERR (eg. both flags together)
+                               di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
+                       }
                        int ndiff = CompareItems(queue, myStruct, curpos);
+                       // Propogate sub-directory status to this directory
                        if (ndiff > 0)
-                       {
+                       {       // There were differences in the sub-directories
                                if (existsalldirs)
                                        di.diffcode.diffcode |= DIFFCODE::DIFF;
                                res += ndiff;
                        }
-                       else if (ndiff == 0)
-                       {
+                       else 
+                       if (ndiff == 0)
+                       {       // Sub-directories were identical
                                if (existsalldirs)
                                        di.diffcode.diffcode |= DIFFCODE::SAME;
                        }
+                       else
+                       if (ndiff == -1)
+                       {       // There were file IO-errors during sub-directory comparison.
+                               di.diffcode.diffcode |= DIFFCODE::CMPERR;
+                               bCompareFailure = true;
+                       }
                }
-               queue.enqueueNotification(new WorkNotification(di, queueResult));
+               if (existsalldirs)
+                       queue.enqueueUrgentNotification(new WorkNotification(di, queueResult));
+               else
+                       queue.enqueueNotification(new WorkNotification(di, queueResult));
                ++count;
                pos = curpos;
                pCtxt->GetNextSiblingDiffRefPosition(pos);
@@ -625,20 +572,29 @@ static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, UInt
        while (count > 0)
        {
                AutoPtr<Notification> pNf(queueResult.waitDequeueNotification());
-               if (!pNf)
+               if (pNf.get() == nullptr)
                        break;
                WorkCompletedNotification* pWorkCompletedNf = dynamic_cast<WorkCompletedNotification*>(pNf.get());
-               if (pWorkCompletedNf) {
+               if (pWorkCompletedNf != nullptr) {
                        DIFFITEM &di = pWorkCompletedNf->data();
-                       bool existsalldirs = ((pCtxt->GetCompareDirs() == 2 && di.diffcode.isSideBoth()) || (pCtxt->GetCompareDirs() == 3 && di.diffcode.isSideAll()));
+                       if (di.diffcode.isResultError()) { 
+                               DIFFITEM *diParent = di.GetParentLink();
+                               assert(diParent != nullptr);
+                               if (diParent != nullptr)
+                               {
+                                       diParent->diffcode.diffcode |= DIFFCODE::CMPERR;
+                                       bCompareFailure = true;
+                               }
+                       }
+                               
                        if (di.diffcode.isResultDiff() ||
-                               (!existsalldirs && !di.diffcode.isResultFiltered()))
+                               (!di.diffcode.existAll() && !di.diffcode.isResultFiltered()))
                                res++;
                }
                --count;
        }
 
-       return pCtxt->ShouldAbort() ? -1 : res;
+       return bCompareFailure || pCtxt->ShouldAbort() ? -1 : res;
 }
 
 /**
@@ -648,13 +604,16 @@ static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, UInt
  * @param parentdiffpos [in] Position of parent diff item 
  * @return >= 0 number of diff items, -1 if compare was aborted
  */
-int DirScan_CompareRequestedItems(DiffFuncStruct *myStruct, UIntPtr parentdiffpos)
+static int CompareRequestedItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
 {
        CDiffContext *pCtxt = myStruct->context;
+       FolderCmp fc(pCtxt);
        int res = 0;
-       UIntPtr pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
-       
-       while (pos != NULL)
+       bool bCompareFailure = false;
+       if (parentdiffpos == nullptr)
+               myStruct->pSemaphore->wait();
+       DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
+       while (pos != nullptr)
        {
                if (pCtxt->ShouldAbort())
                {
@@ -662,44 +621,142 @@ int DirScan_CompareRequestedItems(DiffFuncStruct *myStruct, UIntPtr parentdiffpo
                        break;
                }
 
-               UIntPtr curpos = pos;
+               DIFFITEM *curpos = pos;
                DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
-               bool existsalldirs = ((pCtxt->GetCompareDirs() == 2 && di.diffcode.isSideBoth()) || (pCtxt->GetCompareDirs() == 3 && di.diffcode.isSideAll()));
-               if (di.diffcode.isDirectory() && pCtxt->m_bRecursive)
+               bool existsalldirs = di.diffcode.existAll();
+               if (di.diffcode.isDirectory())
                {
-                       di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
-                       int ndiff = DirScan_CompareRequestedItems(myStruct, curpos);
-                       if (ndiff > 0)
+                       if (pCtxt->m_bRecursive)
                        {
-                               if (existsalldirs)
-                                       di.diffcode.diffcode |= DIFFCODE::DIFF;
-                               res += ndiff;
+                               di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
+                               int ndiff = CompareRequestedItems(myStruct, curpos);
+                               if (ndiff > 0)
+                               {
+                                       if (existsalldirs)
+                                               di.diffcode.diffcode |= DIFFCODE::DIFF;
+                                       res += ndiff;
+                               }
+                               else 
+                               if (ndiff == 0)
+                               {
+                                       if (existsalldirs)
+                                               di.diffcode.diffcode |= DIFFCODE::SAME;
+                               } 
+                               else
+                               if (ndiff == -1)
+                               {       // There were file IO-errors during sub-directory comparison.
+                                       di.diffcode.diffcode |= DIFFCODE::CMPERR;
+                                       bCompareFailure = true;
+                               }
                        }
-                       else if (ndiff == 0)
-                       {
-                               if (existsalldirs)
-                                       di.diffcode.diffcode |= DIFFCODE::SAME;
-                       }               
                }
                else
                {
                        if (di.diffcode.isScanNeeded())
                        {
-                               bool bItemsExist = true;
-                               UpdateDiffItem(di, bItemsExist, pCtxt);
-                               if (bItemsExist)
-                               {
-                                       CompareDiffItem(di, pCtxt);
-                               }
+                               CompareDiffItem(fc, di);
+                               if (di.diffcode.isResultError()) { 
+                                       DIFFITEM *diParent = di.GetParentLink();
+                                       assert(diParent != nullptr);
+                                       if (diParent != nullptr)
+                                       {
+                                               diParent->diffcode.diffcode |= DIFFCODE::CMPERR;
+                                               bCompareFailure = true;
+                                       }
+                       }
+                               
                        }
                }
                if (di.diffcode.isResultDiff() ||
                        (!existsalldirs && !di.diffcode.isResultFiltered()))
                        res++;
        }
-       return res;
+       return bCompareFailure ? -1 : res;;
 }
 
+int DirScan_CompareRequestedItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
+{
+       return CompareRequestedItems(myStruct, parentdiffpos);
+}
+
+static int markChildrenForRescan(CDiffContext *pCtxt, DIFFITEM *parentdiffpos)
+{
+       int ncount = 0;
+       DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
+       while (pos != nullptr)
+       {
+               DIFFITEM *curpos = pos;
+               DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
+               if (di.diffcode.isDirectory())
+                       ncount += markChildrenForRescan(pCtxt, curpos);
+               else
+               {
+                       di.diffcode.diffcode |= DIFFCODE::NEEDSCAN;
+                       ++ncount;
+               }
+       }
+       return ncount;
+}
+
+int DirScan_UpdateMarkedItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
+{
+       CDiffContext *pCtxt = myStruct->context;
+       DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
+       int ncount = 0;
+
+       while (pos != nullptr)
+       {
+               if (pCtxt->ShouldAbort())
+                       break;
+               DIFFITEM *curpos = pos;
+               DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
+               if (di.diffcode.isScanNeeded())
+               {
+                       bool bItemsExist = true;
+                       UpdateDiffItem(di, bItemsExist, pCtxt);
+                       if (!bItemsExist)
+                       { 
+                               di.DelinkFromSiblings();        // delink from list of Siblings
+                               delete &di;                                     // Also delete all Children items
+                               continue;                                       // (... because `di` is now invalid)
+                       }
+                       if (!di.diffcode.isDirectory())
+                               ++ncount;
+               }
+               if (di.diffcode.isDirectory() && pCtxt->m_bRecursive)
+               {
+                       for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
+                               if (di.diffcode.exists(i))
+                                       di.diffFileInfo[i].size = 0;
+                       if (di.diffcode.isScanNeeded() && !di.diffcode.isResultFiltered())
+                       {
+                               di.RemoveChildren();
+                               di.diffcode.diffcode &= ~DIFFCODE::NEEDSCAN;
+
+                               bool casesensitive = false;
+                               int depth = myStruct->context->m_bRecursive ? -1 : 0;
+                               String subdir[3];
+                               PathContext paths = myStruct->context->GetNormalizedPaths();
+                               for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
+                                       subdir[i] = di.diffFileInfo[i].GetFile();
+                               DirScan_GetItems(paths, subdir, myStruct,
+                                       casesensitive, depth, &di, myStruct->context->m_bWalkUniques);
+                               ncount += markChildrenForRescan(myStruct->context, curpos);
+                       }
+                       else
+                       {
+                               ncount += DirScan_UpdateMarkedItems(myStruct, curpos);
+                       }
+               }
+               if (parentdiffpos != nullptr && pCtxt->m_bRecursive)
+               {
+                       for (int nIndex = 0; nIndex < pCtxt->GetCompareDirs(); ++nIndex)
+                               if (curpos->diffFileInfo[nIndex].size != DirItem::FILE_SIZE_NONE)
+                                       parentdiffpos->diffFileInfo[nIndex].size += curpos->diffFileInfo[nIndex].size;
+               }
+       }
+       return ncount;
+}
 /**
  * @brief Update diffitem file/dir infos.
  *
@@ -714,28 +771,18 @@ int DirScan_CompareRequestedItems(DiffFuncStruct *myStruct, UIntPtr parentdiffpo
  *  - false if items were deleted, so diffitem is not valid
  * @param [in] pCtxt Compare context
  */
-void UpdateDiffItem(DIFFITEM & di, bool & bExists, CDiffContext *pCtxt)
+static void UpdateDiffItem(DIFFITEM &di, bool & bExists, CDiffContext *pCtxt)
 {
-       // Clear side-info and file-infos
-       di.diffFileInfo[0].ClearPartial();
-       di.diffFileInfo[1].ClearPartial();
-       di.diffcode.setSideBoth(); // FIXME: DIRTY HACK for UpdateInfoFromDiskHalf
-       bool bLeftExists = pCtxt->UpdateInfoFromDiskHalf(di, 0);
-       bool bRightExists = pCtxt->UpdateInfoFromDiskHalf(di, 1);
-       bExists = bLeftExists || bRightExists;
-       if (bLeftExists)
-       {
-               if (bRightExists)
-                       di.diffcode.setSideBoth();
-               else
-                       di.diffcode.setSideFirstOnly();
-       }
-       else
+       bExists = false;
+       di.diffcode.setSideNone();
+       for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
        {
-               if (bRightExists)
-                       di.diffcode.setSideSecondOnly();
-               else
-                       di.diffcode.setSideNone();
+               di.diffFileInfo[i].ClearPartial();
+               if (pCtxt->UpdateInfoFromDiskHalf(di, i))
+               {
+                       di.diffcode.diffcode |= DIFFCODE::FIRST << i;
+                       bExists = true;
+               }
        }
 }
 
@@ -744,9 +791,9 @@ void UpdateDiffItem(DIFFITEM & di, bool & bExists, CDiffContext *pCtxt)
  *
  * This function does the actual compare for previously gathered list of
  * items. Basically we:
- * - ignore items matching filefilters
+ * - ignore items matching file filters
  * - add non-ignored directories (no compare for directory items)
- * - add  unique files
+ * - add unique files
  * - compare files
  *
  * @param [in] di DiffItem to compare
@@ -754,107 +801,47 @@ void UpdateDiffItem(DIFFITEM & di, bool & bExists, CDiffContext *pCtxt)
  * @todo For date compare, maybe we should use creation date if modification
  * date is missing?
  */
-void CompareDiffItem(DIFFITEM &di, CDiffContext * pCtxt)
+static void CompareDiffItem(FolderCmp &fc, DIFFITEM &di)
 {
+       CDiffContext *const pCtxt = fc.m_pCtxt;
        int nDirs = pCtxt->GetCompareDirs();
        // Clear rescan-request flag (not set by all codepaths)
        di.diffcode.diffcode &= ~DIFFCODE::NEEDSCAN;
        // Is it a directory?
        if (di.diffcode.isDirectory())
        {
-               // 1. Test against filters
-               if (!pCtxt->m_piFilterGlobal ||
-                       (nDirs == 2 && pCtxt->m_piFilterGlobal->includeDir(di.diffFileInfo[0].filename, di.diffFileInfo[1].filename)) ||
-                       (nDirs == 3 && pCtxt->m_piFilterGlobal->includeDir(di.diffFileInfo[0].filename, di.diffFileInfo[1].filename, di.diffFileInfo[2].filename))
-                       )
-                       di.diffcode.diffcode |= DIFFCODE::INCLUDED;
-               else
-                       di.diffcode.diffcode |= DIFFCODE::SKIPPED;
                // We don't actually 'compare' directories, just add non-ignored
                // directories to list.
-               StoreDiffData(di, pCtxt, NULL);
        }
        else
        {
                // 1. Test against filters
-               if (!pCtxt->m_piFilterGlobal ||
+               if (pCtxt->m_piFilterGlobal==nullptr ||
                        (nDirs == 2 && pCtxt->m_piFilterGlobal->includeFile(di.diffFileInfo[0].filename, di.diffFileInfo[1].filename)) ||
                        (nDirs == 3 && pCtxt->m_piFilterGlobal->includeFile(di.diffFileInfo[0].filename, di.diffFileInfo[1].filename, di.diffFileInfo[2].filename))
                        )
                {
                        di.diffcode.diffcode |= DIFFCODE::INCLUDED;
-                       // 2. Add unique files
-                       // We must compare unique files to itself to detect encoding
-                       if (di.diffcode.isSideFirstOnly() || di.diffcode.isSideSecondOnly() || (nDirs > 2 && di.diffcode.isSideThirdOnly()))
+                       di.diffcode.diffcode |= fc.prepAndCompareFiles(di);
+                       di.nsdiffs = fc.m_ndiffs;
+                       di.nidiffs = fc.m_ntrivialdiffs;
+
+                       for (int i = 0; i < nDirs; ++i)
                        {
-                               int nCurrentCompMethod = pCtxt->m_nCurrentCompMethod.get();
-                               if (nCurrentCompMethod != CMP_DATE &&
-                                       nCurrentCompMethod != CMP_DATE_SIZE &&
-                                       nCurrentCompMethod != CMP_SIZE)
+                               // Set text statistics
+                               if (di.diffcode.exists(i))
                                {
-                                       FolderCmp folderCmp;
-                                       unsigned diffCode = folderCmp.prepAndCompareFiles(pCtxt, di);
-                                       
-                                       // Add possible binary flag for unique items
-                                       if (diffCode & DIFFCODE::BIN)
-                                               di.diffcode.diffcode |= DIFFCODE::BIN;
-                                       StoreDiffData(di, pCtxt, &folderCmp);
-                               }
-                               else
-                               {
-                                       StoreDiffData(di, pCtxt, NULL);
+                                       di.diffFileInfo[i].m_textStats = fc.m_diffFileData.m_textStats[i];
+                                       di.diffFileInfo[i].encoding = fc.m_diffFileData.m_FileLocation[i].encoding;
                                }
                        }
-                       // 3. Compare two files
-                       else
-                       {
-                               // Really compare
-                               FolderCmp folderCmp;
-                               di.diffcode.diffcode |= folderCmp.prepAndCompareFiles(pCtxt, di);
-                               StoreDiffData(di, pCtxt, &folderCmp);
-                       }
                }
                else
                {
                        di.diffcode.diffcode |= DIFFCODE::SKIPPED;
-                       StoreDiffData(di, pCtxt, NULL);
                }
        }
-}
-
-/**
- * @brief Send one file or directory result back through the diff context.
- * @param [in] di Data to store.
- * @param [in] pCtxt Compare context.
- * @param [in] pCmpData Folder compare data.
- */
-static void StoreDiffData(DIFFITEM &di, CDiffContext * pCtxt,
-               const FolderCmp * pCmpData)
-{
-       if (pCmpData)
-       {
-               // Set text statistics
-               if (di.diffcode.isSideLeftOnlyOrBoth())
-                       di.diffFileInfo[0].m_textStats = pCmpData->m_diffFileData.m_textStats[0];
-               if (di.diffcode.isSideRightOnlyOrBoth())
-                       di.diffFileInfo[1].m_textStats = pCmpData->m_diffFileData.m_textStats[1];
-
-               di.nsdiffs = pCmpData->m_ndiffs;
-               di.nidiffs = pCmpData->m_ntrivialdiffs;
-
-               if (!di.diffcode.isSideFirstOnly())
-               {
-                       di.diffFileInfo[1].encoding = pCmpData->m_diffFileData.m_FileLocation[1].encoding;
-               }
-               
-               if (!di.diffcode.isSideSecondOnly())
-               {
-                       di.diffFileInfo[0].encoding = pCmpData->m_diffFileData.m_FileLocation[0].encoding;
-               }
-       }
-
        pCtxt->m_pCompareStats->AddItem(di.diffcode.diffcode);
-       //pCtxt->AddDiff(di);
 }
 
 /**
@@ -870,80 +857,85 @@ static DIFFITEM *AddToList(const String& sLeftDir, const String& sRightDir,
        const DirItem * lent, const DirItem * rent,
        unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent)
 {
-       return AddToList(sLeftDir, sRightDir, sLeftDir, lent, rent, 0, code, myStruct, parent);
+       return AddToList(sLeftDir, sRightDir, sLeftDir, lent, rent, nullptr, code, myStruct, parent, 2);
 }
 
 /**
  * @brief Add one compare item to list.
  */
-static DIFFITEM *AddToList(const String& sLeftDir, const String& sMiddleDir, const String& sRightDir,
-       const DirItem * lent, const DirItem * ment, const DirItem * rent,
-       unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent)
+static DIFFITEM *AddToList(const String& sDir1, const String& sDir2, const String& sDir3,
+       const DirItem *ent1, const DirItem *ent2, const DirItem *ent3,
+       unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent, int nItems /*= 3*/)
 {
        // We must store both paths - we cannot get paths later
        // and we need unique item paths for example when items
        // change to identical
-       DIFFITEM *di = myStruct->context->AddDiff(parent);
+       DIFFITEM *di = myStruct->context->AddNewDiff(parent);
 
-       di->parent = parent;
-       di->diffFileInfo[0].path = sLeftDir;
-       di->diffFileInfo[1].path = sMiddleDir;
-       di->diffFileInfo[2].path = sRightDir;
+       di->diffFileInfo[0].path = sDir1;
+       di->diffFileInfo[1].path = sDir2;
+       di->diffFileInfo[2].path = sDir3;
 
-       if (lent)
+       if (ent1 != nullptr)
        {
-               di->diffFileInfo[0].filename = lent->filename;
-               di->diffFileInfo[0].mtime = lent->mtime;
-               di->diffFileInfo[0].ctime = lent->ctime;
-               di->diffFileInfo[0].size = lent->size;
-               di->diffFileInfo[0].flags.attributes = lent->flags.attributes;
+               di->diffFileInfo[0].filename = ent1->filename;
+               di->diffFileInfo[0].mtime = ent1->mtime;
+               di->diffFileInfo[0].ctime = ent1->ctime;
+               di->diffFileInfo[0].size = ent1->size;
+               di->diffFileInfo[0].flags.attributes = ent1->flags.attributes;
        }
        else
        {
                // Don't break CDirView::DoCopyRightToLeft()
-               if (rent)
-                       di->diffFileInfo[0].filename = rent->filename;
-               else
-                       di->diffFileInfo[0].filename = ment->filename;
+               if (ent3 != nullptr)
+                       di->diffFileInfo[0].filename = ent3->filename;
+               else if (ent2 != nullptr)
+                       di->diffFileInfo[0].filename = ent2->filename;
        }
 
-       if (ment)
+       if (ent2 != nullptr)
        {
-               di->diffFileInfo[1].filename = ment->filename;
-               di->diffFileInfo[1].mtime = ment->mtime;
-               di->diffFileInfo[1].ctime = ment->ctime;
-               di->diffFileInfo[1].size = ment->size;
-               di->diffFileInfo[1].flags.attributes = ment->flags.attributes;
+               di->diffFileInfo[1].filename = ent2->filename;
+               di->diffFileInfo[1].mtime = ent2->mtime;
+               di->diffFileInfo[1].ctime = ent2->ctime;
+               di->diffFileInfo[1].size = ent2->size;
+               di->diffFileInfo[1].flags.attributes = ent2->flags.attributes;
        }
        else
        {
                // Don't break CDirView::DoCopyLeftToRight()
-               if (lent)
-                       di->diffFileInfo[1].filename = lent->filename;
-               else
-                       di->diffFileInfo[1].filename = rent->filename;
+               if (ent1 != nullptr)
+                       di->diffFileInfo[1].filename = ent1->filename;
+               else if (ent3 != nullptr)
+                       di->diffFileInfo[1].filename = ent3->filename;
        }
 
-       if (rent)
+       if (ent3 != nullptr)
        {
-               di->diffFileInfo[2].filename = rent->filename;
-               di->diffFileInfo[2].mtime = rent->mtime;
-               di->diffFileInfo[2].ctime = rent->ctime;
-               di->diffFileInfo[2].size = rent->size;
-               di->diffFileInfo[2].flags.attributes = rent->flags.attributes;
+               di->diffFileInfo[2].filename = ent3->filename;
+               di->diffFileInfo[2].mtime = ent3->mtime;
+               di->diffFileInfo[2].ctime = ent3->ctime;
+               di->diffFileInfo[2].size = ent3->size;
+               di->diffFileInfo[2].flags.attributes = ent3->flags.attributes;
        }
        else
        {
                // Don't break CDirView::DoCopyLeftToRight()
-               if (lent)
-                       di->diffFileInfo[2].filename = lent->filename;
-               else
-                       di->diffFileInfo[2].filename = ment->filename;
+               if (ent1 != nullptr)
+                       di->diffFileInfo[2].filename = ent1->filename;
+               else if (ent2 != nullptr)
+                       di->diffFileInfo[2].filename = ent2->filename;
        }
 
-       di->diffcode = code;
+       if (nItems == 2)
+               di->diffcode.diffcode = code;
+       else
+               di->diffcode.diffcode = code | DIFFCODE::THREEWAY;
 
-       myStruct->context->m_pCompareStats->IncreaseTotalItems();
-       myStruct->pSemaphore->set();
+       if (myStruct->m_fncCollect)
+       {
+               myStruct->context->m_pCompareStats->IncreaseTotalItems();
+               myStruct->pSemaphore->set();
+       }
        return di;
 }