OSDN Git Service

Allow NUL and \\.\NUL in paths specified as command line arguments (#2056)
[winmerge-jp/winmerge-jp.git] / Src / DirScan.cpp
1 /**
2  *  @file DirScan.cpp
3  *
4  *  @brief Implementation of DirScan (q.v.) and helper functions
5  */ 
6
7 #include "pch.h"
8 #include "DirScan.h"
9 #include <cassert>
10 #include <memory>
11 #define POCO_NO_UNWINDOWS 1
12 #include <Poco/Semaphore.h>
13 #include <Poco/Notification.h>
14 #include <Poco/NotificationQueue.h>
15 #include <Poco/Environment.h>
16 #include <Poco/ThreadPool.h>
17 #include <Poco/Thread.h>
18 #include <Poco/Runnable.h>
19 #include <Poco/Mutex.h>
20 #include <Poco/AutoPtr.h>
21 #include <Poco/Stopwatch.h>
22 #include <Poco/Format.h>
23 #include "DiffThread.h"
24 #include "UnicodeString.h"
25 #include "DiffWrapper.h"
26 #include "CompareStats.h"
27 #include "FolderCmp.h"
28 #include "FileFilterHelper.h"
29 #include "IAbortable.h"
30 #include "DirItem.h"
31 #include "DirTravel.h"
32 #include "paths.h"
33 #include "Plugins.h"
34 #include "MergeAppCOMClass.h"
35 #include "MergeApp.h"
36 #include "OptionsDef.h"
37 #include "OptionsMgr.h"
38 #include "PathContext.h"
39 #include "DebugNew.h"
40
41 using Poco::NotificationQueue;
42 using Poco::Notification;
43 using Poco::AutoPtr;
44 using Poco::Thread;
45 using Poco::ThreadPool;
46 using Poco::Runnable;
47 using Poco::Environment;
48 using Poco::Stopwatch;
49
50 // Static functions (ie, functions only used locally)
51 static void CompareDiffItem(FolderCmp &fc, DIFFITEM &di);
52 static DIFFITEM *AddToList(const String &sLeftDir, const String &sRightDir, const DirItem *lent, const DirItem *rent,
53         unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent);
54 static DIFFITEM *AddToList(const String &sDir1, const String &sDir2, const String &sDir3, const DirItem *ent1, const DirItem *ent2, const DirItem *ent3,
55         unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent, int nItems = 3);
56 static void UpdateDiffItem(DIFFITEM &di, bool &bExists, CDiffContext *pCtxt);
57 static int CompareItems(NotificationQueue &queue, DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos);
58 static unsigned GetDirCompareFlags3Way(const DIFFITEM& di);
59
60 class WorkNotification: public Poco::Notification
61 {
62 public:
63         WorkNotification(DIFFITEM& di, NotificationQueue& queueResult): m_di(di), m_queueResult(queueResult) {}
64         DIFFITEM& data() const { return m_di; }
65         NotificationQueue& queueResult() const { return m_queueResult; }
66 private:
67         DIFFITEM& m_di;
68         NotificationQueue& m_queueResult;
69 };
70
71 class WorkCompletedNotification: public Poco::Notification
72 {
73 public:
74         explicit WorkCompletedNotification(DIFFITEM& di): m_di(di) {}
75         DIFFITEM& data() const { return m_di; }
76 private:
77         DIFFITEM& m_di;
78 };
79
80 class DiffWorker: public Runnable
81 {
82 public:
83         DiffWorker(NotificationQueue& queue, CDiffContext *pCtxt, int id):
84           m_queue(queue), m_pCtxt(pCtxt), m_id(id) {}
85
86         void run()
87         {
88                 FolderCmp fc(m_pCtxt);
89                 // keep the scripts alive during the Rescan
90                 // when we exit the thread, we delete this and release the scripts
91                 CAssureScriptsForThread scriptsForRescan(new MergeAppCOMClass());
92
93                 AutoPtr<Notification> pNf(m_queue.waitDequeueNotification());
94                 while (pNf.get() != nullptr)
95                 {
96                         WorkNotification* pWorkNf = dynamic_cast<WorkNotification*>(pNf.get());
97                         if (pWorkNf != nullptr) {
98                                 m_pCtxt->m_pCompareStats->BeginCompare(&pWorkNf->data(), m_id);
99                                 if (!m_pCtxt->ShouldAbort())
100                                         CompareDiffItem(fc, pWorkNf->data());
101                                 pWorkNf->queueResult().enqueueNotification(new WorkCompletedNotification(pWorkNf->data()));
102                         }
103                         if (m_pCtxt->m_pCompareStats->IsIdleCompareThread(m_id))
104                         {
105                                 m_pCtxt->m_pCompareStats->BeginCompare(nullptr, m_id);
106                                 while (!m_pCtxt->ShouldAbort() && m_pCtxt->m_pCompareStats->IsIdleCompareThread(m_id))
107                                         Poco::Thread::sleep(10);
108                         }
109
110                         pNf = m_queue.waitDequeueNotification();
111                 }
112         }
113
114 private:
115         NotificationQueue& m_queue;
116         CDiffContext *m_pCtxt;
117         int m_id;
118 };
119
120 typedef std::shared_ptr<DiffWorker> DiffWorkerPtr;
121
122 /**
123  * @brief Collect file- and folder-names to list.
124  * This function walks given folders and adds found subfolders and files into
125  * lists. There are two modes, determined by the @p depth:
126  * - in non-recursive mode we walk only given folders, and add files
127  *   contained. Subfolders are added as folder items, not walked into.
128  * - in recursive mode we walk all subfolders and add the files they
129  *   contain into list.
130  *
131  * Items are tested against file filters in this function.
132  * 
133  * @param [in] paths Root paths of compare
134  * @param [in] leftsubdir Left side subdirectory under root path
135  * @param [in] bLeftUniq Is left-side folder unique folder?
136  * @param [in] rightsubdir Right side subdirectory under root path
137  * @param [in] bRightUniq Is right-side folder unique folder?
138  * @param [in] myStruct Compare-related data, like context etc.
139  * @param [in] casesensitive Is filename compare case sensitive?
140  * @param [in] depth Levels of subdirectories to scan, -1 scans all
141  * @param [in] parent Folder diff item to be scanned
142  * @param [in] bUniques If true, walk into unique folders.
143  * @return 1 normally, -1 if compare was aborted
144  */
145 int DirScan_GetItems(const PathContext &paths, const String subdir[],
146                 DiffFuncStruct *myStruct,
147                 bool casesensitive, int depth, DIFFITEM *parent,
148                 bool bUniques)
149 {
150         static const tchar_t backslash[] = _T("\\");
151         int nDirs = paths.GetSize();
152         CDiffContext *pCtxt = myStruct->context;
153         String sDir[3];
154         String subprefix[3];
155
156         std::copy(paths.begin(), paths.end(), sDir);
157
158         if (!subdir[0].empty())
159         {
160                 for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
161                 {
162                         sDir[nIndex] = paths::ConcatPath(sDir[nIndex], subdir[nIndex]);
163                         subprefix[nIndex] = subdir[nIndex] + backslash;
164                 }
165         }
166
167         DirItemArray dirs[3], aFiles[3];
168         for (int nIndex = 0; nIndex < nDirs; nIndex++)
169                 LoadAndSortFiles(sDir[nIndex], &dirs[nIndex], &aFiles[nIndex], casesensitive);
170
171         // Allow user to abort scanning
172         if (pCtxt->ShouldAbort())
173                 return -1;
174
175         // Handle directories
176         // i points to current directory in left list (leftDirs)
177         // j points to current directory in right list (rightDirs)
178
179         {
180                 int nIndex;
181                 for (nIndex = 0; nIndex < nDirs; nIndex++)
182                         if (dirs[nIndex].size() != 0 || aFiles[nIndex].size() != 0) break;
183                 if (nIndex == nDirs)
184                         return 0;
185         }
186
187         DirItemArray::size_type i=0, j=0, k=0;
188         while (true)
189         {
190                 if (pCtxt->ShouldAbort())
191                         return -1;
192
193                 if (i >= dirs[0].size() && j >= dirs[1].size() && (nDirs < 3 || k >= dirs[2].size()))
194                         break;
195
196                 unsigned nDiffCode = DIFFCODE::DIR;
197                 // Comparing directories leftDirs[i].name to rightDirs[j].name
198                 if (i<dirs[0].size() && (j==dirs[1].size() || collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive)<0)
199                         && (nDirs < 3 ||      (k==dirs[2].size() || collstr(dirs[0][i].filename, dirs[2][k].filename, casesensitive)<0) ))
200                 {
201                         nDiffCode |= DIFFCODE::FIRST;
202                 }
203                 else if (j<dirs[1].size() && (i==dirs[0].size() || collstr(dirs[1][j].filename, dirs[0][i].filename, casesensitive)<0)
204                         && (nDirs < 3 ||      (k==dirs[2].size() || collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive)<0) ))
205                 {
206                         nDiffCode |= DIFFCODE::SECOND;
207                 }
208                 else if (nDirs < 3)
209                 {
210                         nDiffCode |= DIFFCODE::BOTH;
211                 }
212                 else
213                 {
214                         if (k<dirs[2].size() && (i==dirs[0].size() || collstr(dirs[2][k].filename, dirs[0][i].filename, casesensitive)<0)
215                                 &&                     (j==dirs[1].size() || collstr(dirs[2][k].filename, dirs[1][j].filename, casesensitive)<0) )
216                         {
217                                 nDiffCode |= DIFFCODE::THIRD;
218                         }
219                         else if ((i<dirs[0].size() && j<dirs[1].size() && collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive) == 0)
220                                 && (k==dirs[2].size() || collstr(dirs[2][k].filename, dirs[0][i].filename, casesensitive) != 0))
221                         {
222                                 nDiffCode |= DIFFCODE::FIRST | DIFFCODE::SECOND;
223                         }
224                         else if ((i<dirs[0].size() && k<dirs[2].size() && collstr(dirs[0][i].filename, dirs[2][k].filename, casesensitive) == 0)
225                                 && (j==dirs[1].size() || collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive) != 0))
226                         {
227                                 nDiffCode |= DIFFCODE::FIRST | DIFFCODE::THIRD;
228                         }
229                         else if ((j<dirs[1].size() && k<dirs[2].size() && collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive) == 0)
230                                 && (i==dirs[0].size() || collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive) != 0))
231                         {
232                                 nDiffCode |= DIFFCODE::SECOND | DIFFCODE::THIRD;
233                         }
234                         else
235                         {
236                                 nDiffCode |= DIFFCODE::ALL;
237                         }
238                 }
239
240                 String leftnewsub;
241                 String rightnewsub;
242                 String middlenewsub;
243                 if (nDirs < 3)
244                 {
245                         leftnewsub  = (nDiffCode & DIFFCODE::FIRST)  ? subprefix[0] + dirs[0][i].filename.get() : subprefix[0] + dirs[1][j].filename.get();
246                         rightnewsub = (nDiffCode & DIFFCODE::SECOND) ? subprefix[1] + dirs[1][j].filename.get() : subprefix[1] + dirs[0][i].filename.get();
247
248                         // Test against filter so we don't include contents of filtered out directories
249                         // Also this is only place we can test for both-sides directories in recursive compare
250                         if ((pCtxt->m_piFilterGlobal!=nullptr && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, rightnewsub)) ||
251                                 (pCtxt->m_bIgnoreReparsePoints && (
252                                 (nDiffCode & DIFFCODE::FIRST) && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
253                                         (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
254                                         )
255                                 )
256                                 nDiffCode |= DIFFCODE::SKIPPED;
257                 }
258                 else
259                 {
260                         leftnewsub   = subprefix[0];
261                         if (nDiffCode & DIFFCODE::FIRST)       leftnewsub += dirs[0][i].filename;
262                         else if (nDiffCode & DIFFCODE::SECOND) leftnewsub += dirs[1][j].filename;
263                         else if (nDiffCode & DIFFCODE::THIRD)  leftnewsub += dirs[2][k].filename;
264                         middlenewsub = subprefix[1];
265                         if (nDiffCode & DIFFCODE::SECOND)      middlenewsub += dirs[1][j].filename;
266                         else if (nDiffCode & DIFFCODE::FIRST)  middlenewsub += dirs[0][i].filename;
267                         else if (nDiffCode & DIFFCODE::THIRD)  middlenewsub += dirs[2][k].filename;
268                         rightnewsub  = subprefix[2];
269                         if (nDiffCode & DIFFCODE::THIRD)       rightnewsub += dirs[2][k].filename;
270                         else if (nDiffCode & DIFFCODE::FIRST)  rightnewsub += dirs[0][i].filename;
271                         else if (nDiffCode & DIFFCODE::SECOND) rightnewsub += dirs[1][j].filename;
272
273                         // Test against filter so we don't include contents of filtered out directories
274                         // Also this is only place we can test for both-sides directories in recursive compare
275                         if ((pCtxt->m_piFilterGlobal!=nullptr && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, middlenewsub, rightnewsub)) ||
276                                 (pCtxt->m_bIgnoreReparsePoints && (
277                                   (nDiffCode & DIFFCODE::FIRST)  && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
278                                   (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
279                                   (nDiffCode & DIFFCODE::THIRD)  && (dirs[2][k].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
280                                 )
281                            )
282                                 nDiffCode |= DIFFCODE::SKIPPED;
283                 }
284
285                 // add to list
286                 if (!depth)
287                 {
288                         if (nDirs < 3)
289                                 AddToList(subdir[0], subdir[1], 
290                                         (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr, 
291                                         (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
292                                         nDiffCode, myStruct, parent);
293                         else
294                                 AddToList(subdir[0], subdir[1], subdir[2], 
295                                         (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr,
296                                         (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
297                                         (nDiffCode & DIFFCODE::THIRD ) ? &dirs[2][k] : nullptr,
298                                         nDiffCode, myStruct, parent);
299                 }
300                 else
301                 {
302                         // Recursive compare
303                         assert(pCtxt->m_bRecursive);
304                         if (nDirs < 3)
305                         {
306                                 DIFFITEM *me = AddToList(subdir[0], subdir[1], 
307                                         (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr, 
308                                         (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
309                                         nDiffCode, myStruct, parent);
310                                 if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::BOTH || bUniques))
311                                 {
312                                         // Scan recursively all subdirectories too, we are not adding folders
313                                         String newsubdir[3] = {leftnewsub, rightnewsub};
314                                         int result = DirScan_GetItems(paths, newsubdir, myStruct, casesensitive,
315                                                         depth - 1, me, bUniques);
316                                         if (result == -1)
317                                                 return -1;
318                                 }
319                         }
320                         else
321                         {
322                                 DIFFITEM *me = AddToList(subdir[0], subdir[1], subdir[2], 
323                                         (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr,
324                                         (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr,
325                                         (nDiffCode & DIFFCODE::THIRD ) ? &dirs[2][k] : nullptr,
326                                         nDiffCode, myStruct, parent);
327                                 if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::ALL || bUniques))
328                                 {
329                                         // Scan recursively all subdirectories too, we are not adding folders
330                                         String newsubdir[3] = {leftnewsub, middlenewsub, rightnewsub};
331                                         int result = DirScan_GetItems(paths, newsubdir, myStruct, casesensitive,
332                                                         depth - 1, me, bUniques);
333                                         if (result == -1)
334                                                 return -1;
335                                 }
336                         }
337                 }
338                 if (nDiffCode & DIFFCODE::FIRST)
339                         i++;
340                 if (nDiffCode & DIFFCODE::SECOND)
341                         j++;
342                 if (nDiffCode & DIFFCODE::THIRD)
343                         k++;
344         }
345         // Handle files
346         // i points to current file in left list (aFiles[0])
347         // j points to current file in right list (aFiles[1])
348         i=0, j=0, k=0;
349         while (true)
350         {
351                 if (pCtxt->ShouldAbort())
352                         return -1;
353
354
355                 // Comparing file aFiles[0][i].name to aFiles[1][j].name
356                 if (i<aFiles[0].size() && (j==aFiles[1].size() ||
357                                 collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) < 0)
358                         && (nDirs < 3 || 
359                                 (k==aFiles[2].size() || collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive)<0) ))
360                 {
361                         if (nDirs < 3)
362                         {
363                                 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::FILE;
364                                 AddToList(subdir[0], subdir[1], &aFiles[0][i], nullptr, nDiffCode, myStruct, parent);
365                         }
366                         else
367                         {
368                                 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::FILE;
369                                 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], nullptr, nullptr, nDiffCode, myStruct, parent);
370                         }
371                         // Advance left pointer over left-only entry, and then retest with new pointers
372                         ++i;
373                         continue;
374                 }
375                 if (j<aFiles[1].size() && (i==aFiles[0].size() ||
376                                 collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) > 0)
377                         && (nDirs < 3 ||
378                                 (k==aFiles[2].size() || collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive)<0) ))
379                 {
380                         const unsigned nDiffCode = DIFFCODE::SECOND | DIFFCODE::FILE;
381                         if (nDirs < 3)
382                                 AddToList(subdir[0], subdir[1], nullptr, &aFiles[1][j], nDiffCode, myStruct, parent);
383                         else
384                                 AddToList(subdir[0], subdir[1], subdir[2], nullptr, &aFiles[1][j], nullptr, nDiffCode, myStruct, parent);
385                         // Advance right pointer over right-only entry, and then retest with new pointers
386                         ++j;
387                         continue;
388                 }
389                 if (nDirs == 3)
390                 {
391                         if (k<aFiles[2].size() && (i==aFiles[0].size() ||
392                                         collstr(aFiles[2][k].filename, aFiles[0][i].filename, casesensitive)<0)
393                                 && (j==aFiles[1].size() || collstr(aFiles[2][k].filename, aFiles[1][j].filename, casesensitive)<0) )
394                         {
395                                 const unsigned nDiffCode = DIFFCODE::THIRD | DIFFCODE::FILE;
396                                 AddToList(subdir[0], subdir[1], subdir[2], nullptr, nullptr, &aFiles[2][k], nDiffCode, myStruct, parent);
397                                 ++k;
398                                 // Advance right pointer over right-only entry, and then retest with new pointers
399                                 continue;
400                         }
401
402                         if ((i<aFiles[0].size() && j<aFiles[1].size() && collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) == 0)
403                             && (k==aFiles[2].size() || collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive) != 0))
404                         {
405                                 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::SECOND | DIFFCODE::FILE;
406                                 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], &aFiles[1][j], nullptr, nDiffCode, myStruct, parent);
407                                 ++i;
408                                 ++j;
409                                 continue;
410                         }
411                         else if ((i<aFiles[0].size() && k<aFiles[2].size() && collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive) == 0)
412                             && (j==aFiles[1].size() || collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive) != 0))
413                         {
414                                 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::THIRD | DIFFCODE::FILE;
415                                 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], nullptr, &aFiles[2][k], nDiffCode, myStruct, parent);
416                                 ++i;
417                                 ++k;
418                                 continue;
419                         }
420                         else if ((j<aFiles[1].size() && k<aFiles[2].size() && collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive) == 0)
421                             && (i==aFiles[0].size() || collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) != 0))
422                         {
423                                 const unsigned nDiffCode = DIFFCODE::SECOND | DIFFCODE::THIRD | DIFFCODE::FILE;
424                                 AddToList(subdir[0], subdir[1], subdir[2], nullptr, &aFiles[1][j], &aFiles[2][k], nDiffCode, myStruct, parent);
425                                 ++j;
426                                 ++k;
427                                 continue;
428                         }
429                 }
430                 if (i<aFiles[0].size())
431                 {
432                         if (nDirs < 3)
433                         {
434                                 assert(j<aFiles[1].size());
435                                 const unsigned nDiffCode = DIFFCODE::BOTH | DIFFCODE::FILE;
436                                 AddToList(subdir[0], subdir[1], &aFiles[0][i], &aFiles[1][j], nDiffCode, myStruct, parent);
437                                 ++i;
438                                 ++j;
439                                 continue;
440                         }
441                         else
442                         {
443                                 assert(j<aFiles[1].size());
444                                 assert(k<aFiles[2].size());
445                                 const unsigned nDiffCode = DIFFCODE::ALL | DIFFCODE::FILE;
446                                 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], &aFiles[1][j], &aFiles[2][k], nDiffCode, myStruct, parent);
447                                 ++i;
448                                 ++j;
449                                 ++k;
450                                 continue;
451                         }
452                 }
453                 break;
454         }
455
456         if (parent != nullptr)
457         {
458                 for (int nIndex = 0; nIndex < nDirs; ++nIndex)
459                         if (parent->diffcode.exists(nIndex) && parent->diffFileInfo[nIndex].size == DirItem::FILE_SIZE_NONE)
460                                 parent->diffFileInfo[nIndex].size = 0;
461         
462                 DIFFITEM *dic = parent->GetFirstChild();
463                 while (dic)
464                 {
465                         for (int nIndex = 0; nIndex < nDirs; ++nIndex)
466                         {
467                                 if (dic->diffFileInfo[nIndex].size != DirItem::FILE_SIZE_NONE)
468                                         parent->diffFileInfo[nIndex].size += dic->diffFileInfo[nIndex].size;
469                         }
470                         dic = dic->GetFwdSiblingLink();
471                 }
472         }
473
474         return 1;
475 }
476
477 /**
478  * @brief Compare DiffItems in list and add results to compare context.
479  *
480  * @param myStruct [in] A structure containing compare-related data.
481  * @param parentdiffpos [in] Position of parent diff item 
482  * @return >= 0 number of diff items, -1 if compare was aborted
483  */
484 int DirScan_CompareItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
485 {
486         const int compareMethod = myStruct->context->GetCompareMethod();
487         int nworkers = 1;
488
489         if (compareMethod == CMP_CONTENT || compareMethod == CMP_QUICK_CONTENT)
490         {
491                 nworkers = GetOptionsMgr()->GetInt(OPT_CMP_COMPARE_THREADS);
492                 if (nworkers <= 0)
493                         nworkers += Environment::processorCount();
494                 nworkers = std::clamp(nworkers, 1, static_cast<int>(Environment::processorCount()));
495         }
496
497         ThreadPool threadPool(nworkers, nworkers);
498         std::vector<DiffWorkerPtr> workers;
499         NotificationQueue queue;
500         myStruct->context->m_pCompareStats->SetCompareThreadCount(nworkers);
501         workers.reserve(nworkers);
502         for (int i = 0; i < nworkers; ++i)
503         {
504                 workers.emplace_back(std::make_shared<DiffWorker>(queue, myStruct->context, i));
505                 threadPool.start(*workers[i]);
506         }
507
508         int res = CompareItems(queue, myStruct, parentdiffpos);
509
510         myStruct->context->m_pCompareStats->SetIdleCompareThreadCount(0);
511         Thread::sleep(100);
512         queue.wakeUpAll();
513         threadPool.joinAll();
514
515         return res;
516 }
517
518 static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
519 {
520         NotificationQueue queueResult;
521         Stopwatch stopwatch;
522         CDiffContext *pCtxt = myStruct->context;
523         int res = 0;
524         int count = 0;
525         bool bCompareFailure = false;
526         if (parentdiffpos == nullptr)
527                 myStruct->pSemaphore->wait();
528         stopwatch.start();
529         int nDirs = pCtxt->GetCompareDirs();
530         DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
531         while (pos != nullptr)
532         {
533                 if (pCtxt->ShouldAbort())
534                         break;
535
536                 if (stopwatch.elapsed() > 2000000)
537                 {
538                         int event = CDiffThread::EVENT_COMPARE_PROGRESSED;
539                         myStruct->m_listeners.notify(myStruct, event);
540                         stopwatch.restart();
541                 }
542                 myStruct->pSemaphore->wait();
543                 DIFFITEM *curpos = pos;
544                 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
545                 bool existsalldirs = di.diffcode.existAll();
546                 if (di.diffcode.isDirectory() && pCtxt->m_bRecursive)
547                 {
548                         if ((di.diffcode.diffcode & DIFFCODE::CMPERR) != DIFFCODE::CMPERR)
549                         {       // Only clear DIFF|SAME flags if not CMPERR (eg. both flags together)
550                                 di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
551                         }
552                         int ndiff = CompareItems(queue, myStruct, curpos);
553                         // Propagate sub-directory status to this directory
554                         if (ndiff > 0)
555                         {       // There were differences in the sub-directories
556                                 if (existsalldirs || pCtxt->m_bWalkUniques)
557                                         di.diffcode.diffcode |= DIFFCODE::DIFF;
558                                 res += ndiff;
559                         }
560                         else 
561                         if (ndiff == 0)
562                         {       // Sub-directories were identical
563                                 if (existsalldirs)
564                                         di.diffcode.diffcode |= DIFFCODE::SAME;
565                                 else if (pCtxt->m_bWalkUniques && !di.diffcode.isResultFiltered())
566                                         di.diffcode.diffcode |= DIFFCODE::DIFF;
567                         }
568                         else
569                         if (ndiff == -1)
570                         {       // There were file IO-errors during sub-directory comparison.
571                                 di.diffcode.diffcode |= DIFFCODE::CMPERR;
572                                 bCompareFailure = true;
573                         }
574
575                         if (nDirs == 3 && (di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF && !di.diffcode.isResultFiltered())
576                         {
577                                 di.diffcode.diffcode &= ~DIFFCODE::COMPAREFLAGS3WAY;
578                                 di.diffcode.diffcode |= GetDirCompareFlags3Way(di);
579                         }
580                 }
581                 if (existsalldirs)
582                         queue.enqueueUrgentNotification(new WorkNotification(di, queueResult));
583                 else
584                         queue.enqueueNotification(new WorkNotification(di, queueResult));
585                 ++count;
586                 pos = curpos;
587                 pCtxt->GetNextSiblingDiffRefPosition(pos);
588         }
589
590         while (count > 0)
591         {
592                 AutoPtr<Notification> pNf(queueResult.waitDequeueNotification());
593                 if (pNf.get() == nullptr)
594                         break;
595                 WorkCompletedNotification* pWorkCompletedNf = dynamic_cast<WorkCompletedNotification*>(pNf.get());
596                 if (pWorkCompletedNf != nullptr) {
597                         DIFFITEM &di = pWorkCompletedNf->data();
598                         if (di.diffcode.isResultError()) { 
599                                 DIFFITEM *diParent = di.GetParentLink();
600                                 assert(diParent != nullptr);
601                                 if (diParent != nullptr)
602                                 {
603                                         diParent->diffcode.diffcode |= DIFFCODE::CMPERR;
604                                         bCompareFailure = true;
605                                 }
606                         }
607                                 
608                         if (di.diffcode.isResultDiff() ||
609                                 (!di.diffcode.existAll() && !di.diffcode.isResultFiltered()))
610                                 res++;
611                 }
612                 --count;
613         }
614
615         return bCompareFailure || pCtxt->ShouldAbort() ? -1 : res;
616 }
617
618 /**
619  * @brief Compare DiffItems in context marked for rescan.
620  *
621  * @param myStruct [in,out] A structure containing compare-related data.
622  * @param parentdiffpos [in] Position of parent diff item 
623  * @return >= 0 number of diff items, -1 if compare was aborted
624  */
625 static int CompareRequestedItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
626 {
627         CDiffContext *pCtxt = myStruct->context;
628         FolderCmp fc(pCtxt);
629         int res = 0;
630         bool bCompareFailure = false;
631         bool bCompareIndeterminate = false;
632         if (parentdiffpos == nullptr)
633                 myStruct->pSemaphore->wait();
634
635         // Since the collect thread deletes the DiffItems in the rescan by "Refresh selected",
636         // the compare thread process should not be executed until the collect thread process is completed 
637         // to avoid accessing  the deleted DiffItems.
638         assert(myStruct->nCollectThreadState == CDiffThread::THREAD_COMPLETED);
639
640         int nDirs = pCtxt->GetCompareDirs();
641         DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
642         while (pos != nullptr)
643         {
644                 if (pCtxt->ShouldAbort())
645                 {
646                         res = -1;
647                         break;
648                 }
649
650                 DIFFITEM *curpos = pos;
651                 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
652                 bool existsalldirs = di.diffcode.existAll();
653                 if (di.diffcode.isDirectory())
654                 {
655                         if (pCtxt->m_bRecursive)
656                         {
657                                 di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
658                                 int ndiff = CompareRequestedItems(myStruct, curpos);
659                                 if (ndiff > 0)
660                                 {
661                                         if (existsalldirs || pCtxt->m_bWalkUniques)
662                                                 di.diffcode.diffcode |= DIFFCODE::DIFF;
663                                         res += ndiff;
664                                 }
665                                 else 
666                                 if (ndiff == 0)
667                                 {
668                                         if (existsalldirs)
669                                                 di.diffcode.diffcode |= DIFFCODE::SAME;
670                                         else if (pCtxt->m_bWalkUniques && !di.diffcode.isResultFiltered())
671                                                 di.diffcode.diffcode |= DIFFCODE::DIFF;
672                                 } 
673                                 else
674                                 if (ndiff == -1)
675                                 {       // There were file IO-errors during sub-directory comparison.
676                                         di.diffcode.diffcode |= DIFFCODE::CMPERR;
677                                         bCompareFailure = true;
678                                 }
679                                 else
680                                 if (ndiff == -2)
681                                 {       // There were files that have not been compared
682                                         bCompareIndeterminate = true;
683                                 }
684
685                                 if (nDirs == 3 && (di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF && !di.diffcode.isResultFiltered())
686                                 {
687                                         di.diffcode.diffcode &= ~DIFFCODE::COMPAREFLAGS3WAY;
688                                         di.diffcode.diffcode |= GetDirCompareFlags3Way(di);
689                                 }
690                         }
691                 }
692                 else
693                 {
694                         if (di.diffcode.isScanNeeded())
695                         {
696                                 CompareDiffItem(fc, di);
697                                 if (di.diffcode.isResultError())
698                                 { 
699                                         DIFFITEM *diParent = di.GetParentLink();
700                                         assert(diParent != nullptr);
701                                         if (diParent != nullptr)
702                                         {
703                                                 diParent->diffcode.diffcode |= DIFFCODE::CMPERR;
704                                                 bCompareFailure = true;
705                                         }
706                                 }
707                         }
708                         else
709                         {
710                                 if (di.diffcode.isResultError())
711                                         bCompareFailure = true;
712                                 else if (di.diffcode.isResultNone() || di.diffcode.isResultAbort())
713                                         bCompareIndeterminate = true;
714                         }
715                 }
716                 if (di.diffcode.isResultDiff() ||
717                         (!existsalldirs && !di.diffcode.isResultFiltered()))
718                         res++;
719         }
720         return bCompareIndeterminate ? -2 : (bCompareFailure ? -1 : res);
721 }
722
723 int DirScan_CompareRequestedItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
724 {
725         return CompareRequestedItems(myStruct, parentdiffpos);
726 }
727
728 static int markChildrenForRescan(CDiffContext *pCtxt, DIFFITEM *parentdiffpos)
729 {
730         int ncount = 0;
731         DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
732         while (pos != nullptr)
733         {
734                 DIFFITEM *curpos = pos;
735                 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
736                 if (di.diffcode.isDirectory())
737                         ncount += markChildrenForRescan(pCtxt, curpos);
738                 else
739                 {
740                         di.diffcode.diffcode |= DIFFCODE::NEEDSCAN;
741                         ++ncount;
742                 }
743         }
744         return ncount;
745 }
746
747 int DirScan_UpdateMarkedItems(DiffFuncStruct *myStruct, DIFFITEM *parentdiffpos)
748 {
749         CDiffContext *pCtxt = myStruct->context;
750         DIFFITEM *pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
751         int ncount = 0;
752
753         while (pos != nullptr)
754         {
755                 if (pCtxt->ShouldAbort())
756                         break;
757                 DIFFITEM *curpos = pos;
758                 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
759                 if (di.diffcode.isScanNeeded())
760                 {
761                         bool bItemsExist = true;
762                         UpdateDiffItem(di, bItemsExist, pCtxt);
763                         if (!bItemsExist)
764                         { 
765                                 di.DelinkFromSiblings();        // delink from list of Siblings
766                                 delete &di;                                     // Also delete all Children items
767                                 continue;                                       // (... because `di` is now invalid)
768                         }
769                         if (!di.diffcode.isDirectory())
770                                 ++ncount;
771                 }
772                 if (di.diffcode.isDirectory() && pCtxt->m_bRecursive)
773                 {
774                         for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
775                                 if (di.diffcode.exists(i))
776                                         di.diffFileInfo[i].size = 0;
777                         if (di.diffcode.isScanNeeded() && !di.diffcode.isResultFiltered())
778                         {
779                                 di.RemoveChildren();
780                                 di.diffcode.diffcode &= ~DIFFCODE::NEEDSCAN;
781
782                                 bool casesensitive = false;
783                                 int depth = myStruct->context->m_bRecursive ? -1 : 0;
784                                 String subdir[3];
785                                 PathContext paths = myStruct->context->GetNormalizedPaths();
786                                 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
787                                         subdir[i] = di.diffFileInfo[i].GetFile();
788                                 DirScan_GetItems(paths, subdir, myStruct,
789                                         casesensitive, depth, &di, myStruct->context->m_bWalkUniques);
790                                 ncount += markChildrenForRescan(myStruct->context, curpos);
791                         }
792                         else
793                         {
794                                 ncount += DirScan_UpdateMarkedItems(myStruct, curpos);
795                         }
796                 }
797                 if (parentdiffpos != nullptr && pCtxt->m_bRecursive)
798                 {
799                         for (int nIndex = 0; nIndex < pCtxt->GetCompareDirs(); ++nIndex)
800                                 if (curpos->diffFileInfo[nIndex].size != DirItem::FILE_SIZE_NONE)
801                                         parentdiffpos->diffFileInfo[nIndex].size += curpos->diffFileInfo[nIndex].size;
802                 }
803         }
804         return ncount;
805 }
806 /**
807  * @brief Update diffitem file/dir infos.
808  *
809  * Re-tests dirs/files if sides still exists, and updates infos for
810  * existing sides. This assumes filenames, or paths are not changed.
811  * Since in normal situations (I can think of) they cannot change
812  * after first compare.
813  *
814  * @param [in,out] di DiffItem to update.
815  * @param [out] bExists Set to
816  *  - true if one of items exists so diffitem is valid
817  *  - false if items were deleted, so diffitem is not valid
818  * @param [in] pCtxt Compare context
819  */
820 static void UpdateDiffItem(DIFFITEM &di, bool & bExists, CDiffContext *pCtxt)
821 {
822         bExists = false;
823         di.diffcode.setSideNone();
824         for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
825         {
826                 di.diffFileInfo[i].ClearPartial();
827                 if (pCtxt->UpdateInfoFromDiskHalf(di, i))
828                 {
829                         bool bUpdated = false;
830                         if (di.diffFileInfo[i].IsDirectory() == di.diffcode.isDirectory())
831                         {
832                                 String filepath = paths::ConcatPath(pCtxt->GetPath(i), di.diffFileInfo[i].GetFile());
833                                 if (di.diffFileInfo[i].UpdateFileName(filepath)) {
834                                         di.diffcode.diffcode |= DIFFCODE::FIRST << i;
835                                         bExists = true;
836                                         bUpdated = true;
837                                 }
838                         }
839                         if (!bUpdated)
840                                 di.diffFileInfo[i].ClearPartial();
841                 }
842         }
843 }
844
845 /**
846  * @brief Compare two diffitems and add results to difflist in context.
847  *
848  * This function does the actual compare for previously gathered list of
849  * items. Basically we:
850  * - ignore items matching file filters
851  * - add non-ignored directories (no compare for directory items)
852  * - add unique files
853  * - compare files
854  *
855  * @param [in] di DiffItem to compare
856  * @param [in,out] pCtxt Compare context: contains difflist, encoding info etc.
857  * @todo For date compare, maybe we should use creation date if modification
858  * date is missing?
859  */
860 static void CompareDiffItem(FolderCmp &fc, DIFFITEM &di)
861 {
862         CDiffContext *const pCtxt = fc.m_pCtxt;
863         int nDirs = pCtxt->GetCompareDirs();
864         // Clear rescan-request flag (not set by all codepaths)
865         di.diffcode.diffcode &= ~DIFFCODE::NEEDSCAN;
866         // Is it a directory?
867         if (di.diffcode.isDirectory())
868         {
869                 // We don't actually 'compare' directories, just add non-ignored
870                 // directories to list.
871         }
872         else
873         {
874                 // 1. Test against filters
875                 if (pCtxt->m_piFilterGlobal==nullptr ||
876                         (nDirs == 2 && pCtxt->m_piFilterGlobal->includeFile(
877                                 paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename), 
878                                 paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[1].filename)
879                         )) ||
880                         (nDirs == 3 && pCtxt->m_piFilterGlobal->includeFile(
881                                 paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename),
882                                 paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[1].filename),
883                                 paths::ConcatPath(di.diffFileInfo[2].path, di.diffFileInfo[2].filename)
884                         )))
885                 {
886                         di.diffcode.diffcode |= DIFFCODE::INCLUDED;
887                         di.diffcode.diffcode |= fc.prepAndCompareFiles(di);
888                         di.nsdiffs = fc.m_ndiffs;
889                         di.nidiffs = fc.m_ntrivialdiffs;
890
891                         for (int i = 0; i < nDirs; ++i)
892                         {
893                                 // Set text statistics
894                                 if (di.diffcode.exists(i))
895                                 {
896                                         di.diffFileInfo[i].m_textStats = fc.m_diffFileData.m_textStats[i];
897                                         di.diffFileInfo[i].encoding = fc.m_diffFileData.m_FileLocation[i].encoding;
898                                 }
899                         }
900                 }
901                 else
902                 {
903                         di.diffcode.diffcode |= DIFFCODE::SKIPPED;
904                 }
905         }
906         pCtxt->m_pCompareStats->AddItem(di.diffcode.diffcode);
907 }
908
909 /**
910  * @brief Add one compare item to list.
911  * @param [in] sLeftDir Left subdirectory.
912  * @param [in] sRightDir Right subdirectory.
913  * @param [in] lent Left item data to add.
914  * @param [in] rent Right item data to add.
915  * @param [in] pCtxt Compare context.
916  * @param [in] parent Parent of item to be added
917  */
918 static DIFFITEM *AddToList(const String& sLeftDir, const String& sRightDir,
919         const DirItem * lent, const DirItem * rent,
920         unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent)
921 {
922         return AddToList(sLeftDir, sRightDir, sLeftDir, lent, rent, nullptr, code, myStruct, parent, 2);
923 }
924
925 /**
926  * @brief Add one compare item to list.
927  */
928 static DIFFITEM *AddToList(const String& sDir1, const String& sDir2, const String& sDir3,
929         const DirItem *ent1, const DirItem *ent2, const DirItem *ent3,
930         unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent, int nItems /*= 3*/)
931 {
932         // We must store both paths - we cannot get paths later
933         // and we need unique item paths for example when items
934         // change to identical
935         DIFFITEM *di = myStruct->context->AddNewDiff(parent);
936
937         di->diffFileInfo[0].path = sDir1;
938         di->diffFileInfo[1].path = sDir2;
939         di->diffFileInfo[2].path = sDir3;
940
941         if (ent1 != nullptr)
942         {
943                 di->diffFileInfo[0].filename = ent1->filename;
944                 di->diffFileInfo[0].mtime = ent1->mtime;
945                 di->diffFileInfo[0].ctime = ent1->ctime;
946                 di->diffFileInfo[0].size = ent1->size;
947                 di->diffFileInfo[0].flags.attributes = ent1->flags.attributes;
948         }
949         else
950         {
951                 // Don't break CDirView::DoCopyRightToLeft()
952                 if (ent3 != nullptr)
953                         di->diffFileInfo[0].filename = ent3->filename;
954                 else if (ent2 != nullptr)
955                         di->diffFileInfo[0].filename = ent2->filename;
956         }
957
958         if (ent2 != nullptr)
959         {
960                 di->diffFileInfo[1].filename = ent2->filename;
961                 di->diffFileInfo[1].mtime = ent2->mtime;
962                 di->diffFileInfo[1].ctime = ent2->ctime;
963                 di->diffFileInfo[1].size = ent2->size;
964                 di->diffFileInfo[1].flags.attributes = ent2->flags.attributes;
965         }
966         else
967         {
968                 // Don't break CDirView::DoCopyLeftToRight()
969                 if (ent1 != nullptr)
970                         di->diffFileInfo[1].filename = ent1->filename;
971                 else if (ent3 != nullptr)
972                         di->diffFileInfo[1].filename = ent3->filename;
973         }
974
975         if (ent3 != nullptr)
976         {
977                 di->diffFileInfo[2].filename = ent3->filename;
978                 di->diffFileInfo[2].mtime = ent3->mtime;
979                 di->diffFileInfo[2].ctime = ent3->ctime;
980                 di->diffFileInfo[2].size = ent3->size;
981                 di->diffFileInfo[2].flags.attributes = ent3->flags.attributes;
982         }
983         else
984         {
985                 // Don't break CDirView::DoCopyLeftToRight()
986                 if (ent1 != nullptr)
987                         di->diffFileInfo[2].filename = ent1->filename;
988                 else if (ent2 != nullptr)
989                         di->diffFileInfo[2].filename = ent2->filename;
990         }
991
992         if (nItems == 2)
993                 di->diffcode.diffcode = code;
994         else
995                 di->diffcode.diffcode = code | DIFFCODE::THREEWAY;
996
997         if (!myStruct->bMarkedRescan && myStruct->m_fncCollect)
998         {
999                 myStruct->context->m_pCompareStats->IncreaseTotalItems();
1000                 myStruct->pSemaphore->set();
1001         }
1002         return di;
1003 }
1004
1005 /**
1006  * @brief Get the 3-way compare flag of the specified directory item.
1007  * @param [in] di Directory Item to get the 3-way compare flag.
1008  * @return 3-way compare flag of the specified directory item.
1009  */
1010 static unsigned GetDirCompareFlags3Way(const DIFFITEM& di)
1011 {
1012         assert(di.diffcode.isDirectory());
1013         assert((di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF && !di.diffcode.isResultFiltered());
1014
1015         bool bSet = false;
1016         unsigned code = DIFFCODE::DIFFALL;
1017
1018         if (di.diffcode.isSideFirstOnly() || di.diffcode.isMissingFirstOnly())
1019         {
1020                 code = DIFFCODE::DIFF1STONLY;
1021                 bSet = true;
1022         }
1023         else if (di.diffcode.isSideSecondOnly() || di.diffcode.isMissingSecondOnly())
1024         {
1025                 code = DIFFCODE::DIFF2NDONLY;
1026                 bSet = true;
1027         }
1028         else if (di.diffcode.isSideThirdOnly() || di.diffcode.isMissingThirdOnly())
1029         {
1030                 code = DIFFCODE::DIFF3RDONLY;
1031                 bSet = true;
1032         }
1033
1034         if (di.HasChildren())
1035         {
1036                 for (DIFFITEM* pdi = di.GetFirstChild(); pdi != nullptr; pdi = pdi->GetFwdSiblingLink())
1037                 {
1038                         if ((pdi->diffcode.diffcode & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF && !pdi->diffcode.isResultFiltered())
1039                         {
1040                                 if (!bSet)
1041                                 {
1042                                         code = (pdi->diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY);
1043                                         bSet = true;
1044                                 }
1045                                 else
1046                                 {
1047                                         switch (code)
1048                                         {
1049                                         case DIFFCODE::DIFF1STONLY:
1050                                                 if ((pdi->diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) != DIFFCODE::DIFF1STONLY)
1051                                                         code = DIFFCODE::DIFFALL;
1052                                                 break;
1053                                         case DIFFCODE::DIFF2NDONLY:
1054                                                 if ((pdi->diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) != DIFFCODE::DIFF2NDONLY)
1055                                                         code = DIFFCODE::DIFFALL;
1056                                                 break;
1057                                         case DIFFCODE::DIFF3RDONLY:
1058                                                 if ((pdi->diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) != DIFFCODE::DIFF3RDONLY)
1059                                                         code = DIFFCODE::DIFFALL;
1060                                                 break;
1061                                         default:
1062                                                 code = DIFFCODE::DIFFALL;
1063                                         }
1064                                 }
1065                         }
1066                 }
1067         }
1068
1069         return code;
1070 }