4 * @brief Implementation of DirScan (q.v.) and helper functions
10 #define POCO_NO_UNWINDOWS 1
11 #include <Poco/Semaphore.h>
12 #include <Poco/Notification.h>
13 #include <Poco/NotificationQueue.h>
14 #include <Poco/Environment.h>
15 #include <Poco/ThreadPool.h>
16 #include <Poco/Thread.h>
17 #include <Poco/Runnable.h>
18 #include <Poco/Mutex.h>
19 #include <Poco/AutoPtr.h>
20 #include <Poco/Stopwatch.h>
21 #include <Poco/Format.h>
22 #include "DiffThread.h"
23 #include "UnicodeString.h"
24 #include "DiffWrapper.h"
25 #include "CompareStats.h"
26 #include "FolderCmp.h"
27 #include "FileFilterHelper.h"
28 #include "IAbortable.h"
29 #include "FolderCmp.h"
31 #include "DirTravel.h"
35 #include "OptionsDef.h"
36 #include "OptionsMgr.h"
38 using Poco::NotificationQueue;
39 using Poco::Notification;
42 using Poco::ThreadPool;
44 using Poco::Environment;
45 using Poco::Stopwatch;
47 // Static functions (ie, functions only used locally)
48 void CompareDiffItem(DIFFITEM &di, CDiffContext * pCtxt);
49 static void StoreDiffData(DIFFITEM &di, CDiffContext * pCtxt,
50 const FolderCmp * pCmpData);
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, uintptr_t parentdiffpos);
58 class WorkNotification: public Poco::Notification
61 WorkNotification(DIFFITEM& di, NotificationQueue& queueResult): m_di(di), m_queueResult(queueResult) {}
62 DIFFITEM& data() const { return m_di; }
63 NotificationQueue& queueResult() const { return m_queueResult; }
66 NotificationQueue& m_queueResult;
69 class WorkCompletedNotification: public Poco::Notification
72 explicit WorkCompletedNotification(DIFFITEM& di): m_di(di) {}
73 DIFFITEM& data() const { return m_di; }
78 class DiffWorker: public Runnable
81 DiffWorker(NotificationQueue& queue, CDiffContext *pCtxt, int id):
82 m_queue(queue), m_pCtxt(pCtxt), m_id(id) {}
86 // keep the scripts alive during the Rescan
87 // when we exit the thread, we delete this and release the scripts
88 CAssureScriptsForThread scriptsForRescan;
90 AutoPtr<Notification> pNf(m_queue.waitDequeueNotification());
93 WorkNotification* pWorkNf = dynamic_cast<WorkNotification*>(pNf.get());
95 m_pCtxt->m_pCompareStats->BeginCompare(&pWorkNf->data(), m_id);
96 if (!m_pCtxt->ShouldAbort())
97 CompareDiffItem(pWorkNf->data(), m_pCtxt);
98 pWorkNf->queueResult().enqueueNotification(new WorkCompletedNotification(pWorkNf->data()));
100 pNf = m_queue.waitDequeueNotification();
105 NotificationQueue& m_queue;
106 CDiffContext *m_pCtxt;
110 typedef std::shared_ptr<DiffWorker> DiffWorkerPtr;
113 * @brief Collect file- and folder-names to list.
114 * This function walks given folders and adds found subfolders and files into
115 * lists. There are two modes, determined by the @p depth:
116 * - in non-recursive mode we walk only given folders, and add files.
117 * contained. Subfolders are added as folder items, not walked into.
118 * - in recursive mode we walk all subfolders and add the files they
121 * Items are tested against file filters in this function.
123 * @param [in] paths Root paths of compare
124 * @param [in] leftsubdir Left side subdirectory under root path
125 * @param [in] bLeftUniq Is left-side folder unique folder?
126 * @param [in] rightsubdir Right side subdirectory under root path
127 * @param [in] bRightUniq Is right-side folder unique folder?
128 * @param [in] myStruct Compare-related data, like context etc.
129 * @param [in] casesensitive Is filename compare casesensitive?
130 * @param [in] depth Levels of subdirectories to scan, -1 scans all
131 * @param [in] parent Folder diff item to be scanned
132 * @param [in] bUniques If true, walk into unique folders.
133 * @return 1 normally, -1 if compare was aborted
135 int DirScan_GetItems(const PathContext &paths, const String subdir[],
136 DiffFuncStruct *myStruct,
137 bool casesensitive, int depth, DIFFITEM *parent,
140 static const TCHAR backslash[] = _T("\\");
141 int nDirs = paths.GetSize();
142 CDiffContext *pCtxt = myStruct->context;
147 std::copy(paths.begin(), paths.end(), sDir);
149 if (!subdir[0].empty())
151 for (nIndex = 0; nIndex < paths.GetSize(); nIndex++)
153 sDir[nIndex] = paths::ConcatPath(sDir[nIndex], subdir[nIndex]);
154 subprefix[nIndex] = subdir[nIndex] + backslash;
158 DirItemArray dirs[3], aFiles[3];
159 for (nIndex = 0; nIndex < nDirs; nIndex++)
160 LoadAndSortFiles(sDir[nIndex], &dirs[nIndex], &aFiles[nIndex], casesensitive);
162 // Allow user to abort scanning
163 if (pCtxt->ShouldAbort())
166 // Handle directories
167 // i points to current directory in left list (leftDirs)
168 // j points to current directory in right list (rightDirs)
170 for (nIndex = 0; nIndex < nDirs; nIndex++)
171 if (dirs[nIndex].size() != 0 || aFiles[nIndex].size() != 0) break;
175 DirItemArray::size_type i=0, j=0, k=0;
178 if (pCtxt->ShouldAbort())
181 if (i >= dirs[0].size() && j >= dirs[1].size() && (nDirs < 3 || k >= dirs[2].size()))
184 unsigned nDiffCode = DIFFCODE::DIR;
185 // Comparing directories leftDirs[i].name to rightDirs[j].name
186 if (i<dirs[0].size() && (j==dirs[1].size() || collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive)<0)
187 && (nDirs < 3 || (k==dirs[2].size() || collstr(dirs[0][i].filename, dirs[2][k].filename, casesensitive)<0) ))
189 nDiffCode |= DIFFCODE::FIRST;
191 else if (j<dirs[1].size() && (i==dirs[0].size() || collstr(dirs[1][j].filename, dirs[0][i].filename, casesensitive)<0)
192 && (nDirs < 3 || (k==dirs[2].size() || collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive)<0) ))
194 nDiffCode |= DIFFCODE::SECOND;
198 nDiffCode |= DIFFCODE::BOTH;
202 if (k<dirs[2].size() && (i==dirs[0].size() || collstr(dirs[2][k].filename, dirs[0][i].filename, casesensitive)<0)
203 && (j==dirs[1].size() || collstr(dirs[2][k].filename, dirs[1][j].filename, casesensitive)<0) )
205 nDiffCode |= DIFFCODE::THIRD;
207 else if ((i<dirs[0].size() && j<dirs[1].size() && collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive) == 0)
208 && (k==dirs[2].size() || collstr(dirs[2][k].filename, dirs[0][i].filename, casesensitive) != 0))
210 nDiffCode |= DIFFCODE::FIRST | DIFFCODE::SECOND;
212 else if ((i<dirs[0].size() && k<dirs[2].size() && collstr(dirs[0][i].filename, dirs[2][k].filename, casesensitive) == 0)
213 && (j==dirs[1].size() || collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive) != 0))
215 nDiffCode |= DIFFCODE::FIRST | DIFFCODE::THIRD;
217 else if ((j<dirs[1].size() && k<dirs[2].size() && collstr(dirs[1][j].filename, dirs[2][k].filename, casesensitive) == 0)
218 && (i==dirs[0].size() || collstr(dirs[0][i].filename, dirs[1][j].filename, casesensitive) != 0))
220 nDiffCode |= DIFFCODE::SECOND | DIFFCODE::THIRD;
224 nDiffCode |= DIFFCODE::ALL;
233 leftnewsub = (nDiffCode & DIFFCODE::FIRST) ? subprefix[0] + dirs[0][i].filename.get() : subprefix[0] + dirs[1][j].filename.get();
234 rightnewsub = (nDiffCode & DIFFCODE::SECOND) ? subprefix[1] + dirs[1][j].filename.get() : subprefix[1] + dirs[0][i].filename.get();
236 // Test against filter so we don't include contents of filtered out directories
237 // Also this is only place we can test for both-sides directories in recursive compare
238 if ((pCtxt->m_piFilterGlobal && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, rightnewsub)) ||
239 (pCtxt->m_bIgnoreReparsePoints && (
240 (nDiffCode & DIFFCODE::FIRST) && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
241 (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
244 nDiffCode |= DIFFCODE::SKIPPED;
248 leftnewsub = subprefix[0];
249 if (nDiffCode & DIFFCODE::FIRST) leftnewsub += dirs[0][i].filename;
250 else if (nDiffCode & DIFFCODE::SECOND) leftnewsub += dirs[1][j].filename;
251 else if (nDiffCode & DIFFCODE::THIRD) leftnewsub += dirs[2][k].filename;
252 middlenewsub = subprefix[1];
253 if (nDiffCode & DIFFCODE::SECOND) middlenewsub += dirs[1][j].filename;
254 else if (nDiffCode & DIFFCODE::FIRST) middlenewsub += dirs[0][i].filename;
255 else if (nDiffCode & DIFFCODE::THIRD) middlenewsub += dirs[2][k].filename;
256 rightnewsub = subprefix[2];
257 if (nDiffCode & DIFFCODE::THIRD) rightnewsub += dirs[2][k].filename;
258 else if (nDiffCode & DIFFCODE::FIRST) rightnewsub += dirs[0][i].filename;
259 else if (nDiffCode & DIFFCODE::SECOND) rightnewsub += dirs[1][j].filename;
261 // Test against filter so we don't include contents of filtered out directories
262 // Also this is only place we can test for both-sides directories in recursive compare
263 if ((pCtxt->m_piFilterGlobal && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, middlenewsub, rightnewsub)) ||
264 (pCtxt->m_bIgnoreReparsePoints && (
265 (nDiffCode & DIFFCODE::FIRST) && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
266 (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
267 (nDiffCode & DIFFCODE::THIRD) && (dirs[2][k].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT))
270 nDiffCode |= DIFFCODE::SKIPPED;
277 AddToList(subdir[0], subdir[1],
278 (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : NULL,
279 (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : NULL,
280 nDiffCode, myStruct, parent);
282 AddToList(subdir[0], subdir[1], subdir[2],
283 (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : NULL,
284 (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : NULL,
285 (nDiffCode & DIFFCODE::THIRD ) ? &dirs[2][k] : NULL,
286 nDiffCode, myStruct, parent);
293 DIFFITEM *me = AddToList(subdir[0], subdir[1],
294 (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : NULL,
295 (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : NULL,
296 nDiffCode, myStruct, parent);
297 if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::BOTH || bUniques))
299 // Scan recursively all subdirectories too, we are not adding folders
300 String newsubdir[3] = {leftnewsub, rightnewsub};
301 int result = DirScan_GetItems(paths, newsubdir, myStruct, casesensitive,
302 depth - 1, me, bUniques);
309 DIFFITEM *me = AddToList(subdir[0], subdir[1], subdir[2],
310 (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : NULL,
311 (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : NULL,
312 (nDiffCode & DIFFCODE::THIRD ) ? &dirs[2][k] : NULL,
313 nDiffCode, myStruct, parent);
314 if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::ALL || bUniques))
316 // Scan recursively all subdirectories too, we are not adding folders
317 String newsubdir[3] = {leftnewsub, middlenewsub, rightnewsub};
318 int result = DirScan_GetItems(paths, newsubdir, myStruct, casesensitive,
319 depth - 1, me, bUniques);
325 if (nDiffCode & DIFFCODE::FIRST)
327 if (nDiffCode & DIFFCODE::SECOND)
329 if (nDiffCode & DIFFCODE::THIRD)
333 // i points to current file in left list (aFiles[0])
334 // j points to current file in right list (aFiles[1])
338 if (pCtxt->ShouldAbort())
342 // Comparing file aFiles[0][i].name to aFiles[1][j].name
343 if (i<aFiles[0].size() && (j==aFiles[1].size() ||
344 collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) < 0)
346 (k==aFiles[2].size() || collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive)<0) ))
350 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::FILE;
351 AddToList(subdir[0], subdir[1], &aFiles[0][i], 0, nDiffCode, myStruct, parent);
355 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::FILE;
356 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], 0, 0, nDiffCode, myStruct, parent);
358 // Advance left pointer over left-only entry, and then retest with new pointers
362 if (j<aFiles[1].size() && (i==aFiles[0].size() ||
363 collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) > 0)
365 (k==aFiles[2].size() || collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive)<0) ))
367 const unsigned nDiffCode = DIFFCODE::SECOND | DIFFCODE::FILE;
369 AddToList(subdir[0], subdir[1], 0, &aFiles[1][j], nDiffCode, myStruct, parent);
371 AddToList(subdir[0], subdir[1], subdir[2], 0, &aFiles[1][j], 0, nDiffCode, myStruct, parent);
372 // Advance right pointer over right-only entry, and then retest with new pointers
378 if (k<aFiles[2].size() && (i==aFiles[0].size() ||
379 collstr(aFiles[2][k].filename, aFiles[0][i].filename, casesensitive)<0)
380 && (j==aFiles[1].size() || collstr(aFiles[2][k].filename, aFiles[1][j].filename, casesensitive)<0) )
382 const unsigned nDiffCode = DIFFCODE::THIRD | DIFFCODE::FILE;
383 AddToList(subdir[0], subdir[1], subdir[2], 0, 0, &aFiles[2][k], nDiffCode, myStruct, parent);
385 // Advance right pointer over right-only entry, and then retest with new pointers
389 if ((i<aFiles[0].size() && j<aFiles[1].size() && collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) == 0)
390 && (k==aFiles[2].size() || collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive) != 0))
392 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::SECOND | DIFFCODE::FILE;
393 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], &aFiles[1][j], 0, nDiffCode, myStruct, parent);
398 else if ((i<aFiles[0].size() && k<aFiles[2].size() && collstr(aFiles[0][i].filename, aFiles[2][k].filename, casesensitive) == 0)
399 && (j==aFiles[1].size() || collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive) != 0))
401 const unsigned nDiffCode = DIFFCODE::FIRST | DIFFCODE::THIRD | DIFFCODE::FILE;
402 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], 0, &aFiles[2][k], nDiffCode, myStruct, parent);
407 else if ((j<aFiles[1].size() && k<aFiles[2].size() && collstr(aFiles[1][j].filename, aFiles[2][k].filename, casesensitive) == 0)
408 && (i==aFiles[0].size() || collstr(aFiles[0][i].filename, aFiles[1][j].filename, casesensitive) != 0))
410 const unsigned nDiffCode = DIFFCODE::SECOND | DIFFCODE::THIRD | DIFFCODE::FILE;
411 AddToList(subdir[0], subdir[1], subdir[2], 0, &aFiles[1][j], &aFiles[2][k], nDiffCode, myStruct, parent);
417 if (i<aFiles[0].size())
421 assert(j<aFiles[1].size());
422 const unsigned nDiffCode = DIFFCODE::BOTH | DIFFCODE::FILE;
423 AddToList(subdir[0], subdir[1], &aFiles[0][i], &aFiles[1][j], nDiffCode, myStruct, parent);
430 assert(j<aFiles[1].size());
431 assert(k<aFiles[2].size());
432 const unsigned nDiffCode = DIFFCODE::ALL | DIFFCODE::FILE;
433 AddToList(subdir[0], subdir[1], subdir[2], &aFiles[0][i], &aFiles[1][j], &aFiles[2][k], nDiffCode, myStruct, parent);
446 * @brief Compare DiffItems in list and add results to compare context.
448 * @param myStruct [in] A structure containing compare-related data.
449 * @param parentdiffpos [in] Position of parent diff item
450 * @return >= 0 number of diff items, -1 if compare was aborted
452 int DirScan_CompareItems(DiffFuncStruct *myStruct, uintptr_t parentdiffpos)
454 const int compareMethod = myStruct->context->GetCompareMethod();
457 if (compareMethod == CMP_CONTENT || compareMethod == CMP_QUICK_CONTENT)
459 nworkers = GetOptionsMgr()->GetInt(OPT_CMP_COMPARE_THREADS);
462 nworkers += Environment::processorCount();
468 ThreadPool threadPool(nworkers, nworkers);
469 std::vector<DiffWorkerPtr> workers;
470 NotificationQueue queue;
471 myStruct->context->m_pCompareStats->SetCompareThreadCount(nworkers);
472 for (int i = 0; i < nworkers; ++i)
474 workers.push_back(DiffWorkerPtr(new DiffWorker(queue, myStruct->context, i)));
475 threadPool.start(*workers[i]);
478 int res = CompareItems(queue, myStruct, parentdiffpos);
482 threadPool.joinAll();
487 static int CompareItems(NotificationQueue& queue, DiffFuncStruct *myStruct, uintptr_t parentdiffpos)
489 NotificationQueue queueResult;
491 CDiffContext *pCtxt = myStruct->context;
495 myStruct->pSemaphore->wait();
497 uintptr_t pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
500 if (pCtxt->ShouldAbort())
503 if (stopwatch.elapsed() > 2000000)
505 int event = CDiffThread::EVENT_COMPARE_PROGRESSED;
506 myStruct->m_listeners.notify(myStruct, event);
509 myStruct->pSemaphore->wait();
510 uintptr_t curpos = pos;
511 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
512 bool existsalldirs = di.diffcode.existAll();
513 if (di.diffcode.isDirectory() && pCtxt->m_bRecursive)
515 di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
516 int ndiff = CompareItems(queue, myStruct, curpos);
520 di.diffcode.diffcode |= DIFFCODE::DIFF;
526 di.diffcode.diffcode |= DIFFCODE::SAME;
530 queue.enqueueUrgentNotification(new WorkNotification(di, queueResult));
532 queue.enqueueNotification(new WorkNotification(di, queueResult));
535 pCtxt->GetNextSiblingDiffRefPosition(pos);
540 AutoPtr<Notification> pNf(queueResult.waitDequeueNotification());
543 WorkCompletedNotification* pWorkCompletedNf = dynamic_cast<WorkCompletedNotification*>(pNf.get());
544 if (pWorkCompletedNf) {
545 DIFFITEM &di = pWorkCompletedNf->data();
546 if (di.diffcode.isResultDiff() ||
547 (!di.diffcode.existAll() && !di.diffcode.isResultFiltered()))
553 return pCtxt->ShouldAbort() ? -1 : res;
557 * @brief Compare DiffItems in context marked for rescan.
559 * @param myStruct [in,out] A structure containing compare-related data.
560 * @param parentdiffpos [in] Position of parent diff item
561 * @return >= 0 number of diff items, -1 if compare was aborted
563 static int CompareRequestedItems(DiffFuncStruct *myStruct, uintptr_t parentdiffpos)
565 CDiffContext *pCtxt = myStruct->context;
567 uintptr_t pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
571 if (pCtxt->ShouldAbort())
577 uintptr_t curpos = pos;
578 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
579 bool existsalldirs = di.diffcode.existAll();
580 if (di.diffcode.isDirectory())
582 if (pCtxt->m_bRecursive)
584 di.diffcode.diffcode &= ~(DIFFCODE::DIFF | DIFFCODE::SAME);
585 int ndiff = CompareRequestedItems(myStruct, curpos);
589 di.diffcode.diffcode |= DIFFCODE::DIFF;
595 di.diffcode.diffcode |= DIFFCODE::SAME;
601 if (di.diffcode.isScanNeeded())
602 CompareDiffItem(di, pCtxt);
604 if (di.diffcode.isResultDiff() ||
605 (!existsalldirs && !di.diffcode.isResultFiltered()))
611 int DirScan_CompareRequestedItems(DiffFuncStruct *myStruct, uintptr_t parentdiffpos)
613 CAssureScriptsForThread scriptsForRescan;
614 return CompareRequestedItems(myStruct, parentdiffpos);
617 static int markChildrenForRescan(CDiffContext *pCtxt, uintptr_t parentdiffpos)
620 uintptr_t pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
623 uintptr_t curpos = pos;
624 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
625 if (di.diffcode.isDirectory())
626 ncount += markChildrenForRescan(pCtxt, curpos);
629 di.diffcode.diffcode |= DIFFCODE::NEEDSCAN;
636 int DirScan_UpdateMarkedItems(DiffFuncStruct *myStruct, uintptr_t parentdiffpos)
638 CDiffContext *pCtxt = myStruct->context;
639 uintptr_t pos = pCtxt->GetFirstChildDiffPosition(parentdiffpos);
644 if (pCtxt->ShouldAbort())
646 uintptr_t curpos = pos;
647 DIFFITEM &di = pCtxt->GetNextSiblingDiffRefPosition(pos);
648 bool bItemsExist = true;
649 if (di.diffcode.isScanNeeded())
651 UpdateDiffItem(di, bItemsExist, pCtxt);
654 else if (!di.diffcode.isDirectory())
657 if (bItemsExist && di.diffcode.isDirectory() && pCtxt->m_bRecursive)
659 if (di.diffcode.isScanNeeded() && !di.diffcode.isResultFiltered())
662 di.diffcode.diffcode &= ~DIFFCODE::NEEDSCAN;
664 bool casesensitive = false;
665 int depth = myStruct->context->m_bRecursive ? -1 : 0;
667 PathContext paths = myStruct->context->GetNormalizedPaths();
668 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
669 subdir[i] = di.diffFileInfo[i].GetFile();
670 DirScan_GetItems(paths, subdir, myStruct,
671 casesensitive, depth, &di, myStruct->context->m_bWalkUniques);
672 ncount += markChildrenForRescan(myStruct->context, curpos);
676 ncount += DirScan_UpdateMarkedItems(myStruct, curpos);
683 * @brief Update diffitem file/dir infos.
685 * Re-tests dirs/files if sides still exists, and updates infos for
686 * existing sides. This assumes filenames, or paths are not changed.
687 * Since in normal situations (I can think of) they cannot change
688 * after first compare.
690 * @param [in,out] di DiffItem to update.
691 * @param [out] bExists Set to
692 * - true if one of items exists so diffitem is valid
693 * - false if items were deleted, so diffitem is not valid
694 * @param [in] pCtxt Compare context
696 static void UpdateDiffItem(DIFFITEM & di, bool & bExists, CDiffContext *pCtxt)
699 di.diffcode.setSideNone();
700 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
702 di.diffFileInfo[i].ClearPartial();
703 if (pCtxt->UpdateInfoFromDiskHalf(di, i))
705 di.diffcode.diffcode |= DIFFCODE::FIRST << i;
712 * @brief Compare two diffitems and add results to difflist in context.
714 * This function does the actual compare for previously gathered list of
715 * items. Basically we:
716 * - ignore items matching filefilters
717 * - add non-ignored directories (no compare for directory items)
721 * @param [in] di DiffItem to compare
722 * @param [in,out] pCtxt Compare context: contains difflist, encoding info etc.
723 * @todo For date compare, maybe we should use creation date if modification
726 void CompareDiffItem(DIFFITEM &di, CDiffContext * pCtxt)
728 int nDirs = pCtxt->GetCompareDirs();
729 // Clear rescan-request flag (not set by all codepaths)
730 di.diffcode.diffcode &= ~DIFFCODE::NEEDSCAN;
731 // Is it a directory?
732 if (di.diffcode.isDirectory())
734 // We don't actually 'compare' directories, just add non-ignored
735 // directories to list.
736 StoreDiffData(di, pCtxt, NULL);
740 // 1. Test against filters
741 if (!pCtxt->m_piFilterGlobal ||
742 (nDirs == 2 && pCtxt->m_piFilterGlobal->includeFile(di.diffFileInfo[0].filename, di.diffFileInfo[1].filename)) ||
743 (nDirs == 3 && pCtxt->m_piFilterGlobal->includeFile(di.diffFileInfo[0].filename, di.diffFileInfo[1].filename, di.diffFileInfo[2].filename))
746 di.diffcode.diffcode |= DIFFCODE::INCLUDED;
748 di.diffcode.diffcode |= folderCmp.prepAndCompareFiles(pCtxt, di);
749 StoreDiffData(di, pCtxt, &folderCmp);
753 di.diffcode.diffcode |= DIFFCODE::SKIPPED;
754 StoreDiffData(di, pCtxt, NULL);
760 * @brief Send one file or directory result back through the diff context.
761 * @param [in] di Data to store.
762 * @param [in] pCtxt Compare context.
763 * @param [in] pCmpData Folder compare data.
765 static void StoreDiffData(DIFFITEM &di, CDiffContext * pCtxt,
766 const FolderCmp * pCmpData)
770 di.nsdiffs = pCmpData->m_ndiffs;
771 di.nidiffs = pCmpData->m_ntrivialdiffs;
773 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
775 // Set text statistics
776 if (di.diffcode.exists(i))
778 di.diffFileInfo[i].m_textStats = pCmpData->m_diffFileData.m_textStats[i];
779 di.diffFileInfo[i].encoding = pCmpData->m_diffFileData.m_FileLocation[i].encoding;
784 pCtxt->m_pCompareStats->AddItem(di.diffcode.diffcode);
785 //pCtxt->AddDiff(di);
789 * @brief Add one compare item to list.
790 * @param [in] sLeftDir Left subdirectory.
791 * @param [in] sRightDir Right subdirectory.
792 * @param [in] lent Left item data to add.
793 * @param [in] rent Right item data to add.
794 * @param [in] pCtxt Compare context.
795 * @param [in] parent Parent of item to be added
797 static DIFFITEM *AddToList(const String& sLeftDir, const String& sRightDir,
798 const DirItem * lent, const DirItem * rent,
799 unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent)
801 return AddToList(sLeftDir, sRightDir, sLeftDir, lent, rent, 0, code, myStruct, parent, 2);
805 * @brief Add one compare item to list.
807 static DIFFITEM *AddToList(const String& sDir1, const String& sDir2, const String& sDir3,
808 const DirItem * ent1, const DirItem * ent2, const DirItem * ent3,
809 unsigned code, DiffFuncStruct *myStruct, DIFFITEM *parent, int nItems)
811 // We must store both paths - we cannot get paths later
812 // and we need unique item paths for example when items
813 // change to identical
814 DIFFITEM *di = myStruct->context->AddDiff(parent);
817 di->diffFileInfo[0].path = sDir1;
818 di->diffFileInfo[1].path = sDir2;
819 di->diffFileInfo[2].path = sDir3;
823 di->diffFileInfo[0].filename = ent1->filename;
824 di->diffFileInfo[0].mtime = ent1->mtime;
825 di->diffFileInfo[0].ctime = ent1->ctime;
826 di->diffFileInfo[0].size = ent1->size;
827 di->diffFileInfo[0].flags.attributes = ent1->flags.attributes;
831 // Don't break CDirView::DoCopyRightToLeft()
833 di->diffFileInfo[0].filename = ent3->filename;
835 di->diffFileInfo[0].filename = ent2->filename;
840 di->diffFileInfo[1].filename = ent2->filename;
841 di->diffFileInfo[1].mtime = ent2->mtime;
842 di->diffFileInfo[1].ctime = ent2->ctime;
843 di->diffFileInfo[1].size = ent2->size;
844 di->diffFileInfo[1].flags.attributes = ent2->flags.attributes;
848 // Don't break CDirView::DoCopyLeftToRight()
850 di->diffFileInfo[1].filename = ent1->filename;
852 di->diffFileInfo[1].filename = ent3->filename;
857 di->diffFileInfo[2].filename = ent3->filename;
858 di->diffFileInfo[2].mtime = ent3->mtime;
859 di->diffFileInfo[2].ctime = ent3->ctime;
860 di->diffFileInfo[2].size = ent3->size;
861 di->diffFileInfo[2].flags.attributes = ent3->flags.attributes;
865 // Don't break CDirView::DoCopyLeftToRight()
867 di->diffFileInfo[2].filename = ent1->filename;
869 di->diffFileInfo[2].filename = ent2->filename;
873 di->diffcode.diffcode = code;
875 di->diffcode.diffcode = code | DIFFCODE::THREEWAY;
877 myStruct->context->m_pCompareStats->IncreaseTotalItems();
878 myStruct->pSemaphore->set();