OSDN Git Service

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