1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
8 * @file DiffContext.cpp
10 * @brief Implementation of CDiffContext
14 #include "DiffContext.h"
15 #include <Poco/ScopedLock.h>
16 #include "CompareOptions.h"
17 #include "VersionInfo.h"
19 #include "codepage_detect.h"
20 #include "DiffItemList.h"
21 #include "IAbortable.h"
22 #include "DiffWrapper.h"
25 using Poco::FastMutex;
27 //////////////////////////////////////////////////////////////////////
28 // Construction/Destruction
29 //////////////////////////////////////////////////////////////////////
32 * @brief Construct CDiffContext.
34 * @param [in] pszLeft Initial left-side path.
35 * @param [in] pszRight Initial right-side path.
36 * @param [in] compareMethod Main compare method for this compare.
38 CDiffContext::CDiffContext(const PathContext & paths, int compareMethod)
39 : m_piFilterGlobal(nullptr)
40 , m_piPluginInfos(nullptr)
41 , m_nCompMethod(compareMethod)
42 , m_bIgnoreSmallTimeDiff(false)
43 , m_pCompareStats(nullptr)
44 , m_piAbortable(nullptr)
45 , m_bStopAfterFirstDiff(false)
46 , m_pFilterList(nullptr)
47 , m_pSubstitutionList(nullptr)
48 , m_pContentCompareOptions(nullptr)
49 , m_pQuickCompareOptions(nullptr)
51 , m_bPluginsEnabled(false)
53 , m_bWalkUniques(true)
54 , m_bIgnoreReparsePoints(false)
55 , m_bIgnoreCodepage(false)
56 , m_iGuessEncodingType(0)
57 , m_nQuickCompareLimit(0)
58 , m_nBinaryCompareLimit(0)
59 , m_bEnableImageCompare(false)
60 , m_pImgfileFilter(nullptr)
61 , m_dColorDistanceThreshold(0.0)
64 for (index = 0; index < paths.GetSize(); index++)
65 m_paths.SetPath(index, paths[index]);
71 CDiffContext::~CDiffContext() = default;
74 * @brief Update info in item in result list from disk.
75 * This function updates result list item's file information from actual
76 * file in the disk. This updates info like date, size and attributes.
77 * @param [in] diffpos DIFFITEM to update.
78 * @param [in] nIndex index to update
80 void CDiffContext::UpdateStatusFromDisk(DIFFITEM *diffpos, int nIndex)
82 DIFFITEM &di = GetDiffRefAt(diffpos);
83 di.diffFileInfo[nIndex].ClearPartial();
84 if (di.diffcode.exists(nIndex))
85 UpdateInfoFromDiskHalf(di, nIndex);
89 * @brief Update file information from disk for DIFFITEM.
90 * This function updates DIFFITEM's file information from actual file in
91 * the disk. This updates info like date, size and attributes.
92 * @param [in, out] di DIFFITEM to update.
93 * @param [in] nIndex index to update
94 * @return true if file exists
96 bool CDiffContext::UpdateInfoFromDiskHalf(DIFFITEM &di, int nIndex)
98 String filepath = paths::ConcatPath(paths::ConcatPath(m_paths[nIndex], di.diffFileInfo[nIndex].path), di.diffFileInfo[nIndex].filename);
99 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
100 if (!dfi.Update(filepath))
102 UpdateVersion(di, nIndex);
103 dfi.encoding = codepage_detect::Guess(filepath, m_iGuessEncodingType);
108 * @brief Determine if file is one to have a version information.
109 * This function determines if the given file has a version information
110 * attached into it in resource. This is done by comparing file extension to
111 * list of known filename extensions usually to have a version information.
112 * @param [in] ext Extension to check.
113 * @return true if extension has version info, false otherwise.
115 static bool CheckFileForVersion(const String& ext)
117 String lower_ext = strutils::makelower(ext);
118 if (lower_ext == _T(".exe") || lower_ext == _T(".dll") || lower_ext == _T(".sys") ||
119 lower_ext == _T(".drv") || lower_ext == _T(".ocx") || lower_ext == _T(".cpl") ||
120 lower_ext == _T(".scr") || lower_ext == _T(".lang"))
128 * @brief Load file version from disk.
129 * Update fileversion for given item and side from disk. Note that versions
130 * are read from only some filetypes. See CheckFileForVersion() function
131 * for list of files to check versions.
132 * @param [in,out] di DIFFITEM to update.
133 * @param [in] bLeft If true left-side file is updated, right-side otherwise.
135 void CDiffContext::UpdateVersion(DIFFITEM &di, int nIndex) const
137 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
138 // Check only binary files
139 dfi.version.SetFileVersionNone();
141 if (di.diffcode.isDirectory())
145 if (!di.diffcode.exists(nIndex))
147 String ext = paths::FindExtension(di.diffFileInfo[nIndex].filename);
148 if (!CheckFileForVersion(ext))
150 spath = di.getFilepath(nIndex, GetNormalizedPath(nIndex));
151 spath = paths::ConcatPath(spath, di.diffFileInfo[nIndex].filename);
153 // Get version info if it exists
154 CVersionInfo ver(spath.c_str());
157 if (ver.GetFixedFileVersion(verMS, verLS))
158 dfi.version.SetFileVersion(verMS, verLS);
162 * @brief Create compare-method specific compare options class.
163 * This function creates a compare options class that is specific for
164 * main compare method. Compare options class is initialized from
165 * given set of options.
166 * @param [in] compareMethod Selected compare method.
167 * @param [in] options Initial set of compare options.
168 * @return true if creation succeeds.
170 bool CDiffContext::CreateCompareOptions(int compareMethod, const DIFFOPTIONS & options)
172 m_pContentCompareOptions.reset();
173 m_pQuickCompareOptions.reset();
174 m_pOptions.reset(new DIFFOPTIONS);
175 *m_pOptions.get() = options;
177 m_nCompMethod = compareMethod;
178 if (GetCompareOptions(m_nCompMethod) == nullptr)
180 // For Date and Date+Size compare `nullptr` is ok since they don't have actual
182 if (m_nCompMethod == CMP_DATE || m_nCompMethod == CMP_DATE_SIZE ||
183 m_nCompMethod == CMP_SIZE)
194 * @brief Get compare-type specific compare options.
195 * This function returns per-compare method options. The compare options
196 * returned are converted from general options to match options for specific
197 * comapare type. Not all compare options in general set are available for
198 * some other compare type. And some options can have different values.
199 * @param [in] compareMethod Compare method used.
200 * @return Compare options class.
202 CompareOptions * CDiffContext::GetCompareOptions(int compareMethod)
204 FastMutex::ScopedLock lock(m_mutex);
205 CompareOptions *pCompareOptions = nullptr;
207 // Otherwise we have to create new options
208 switch (compareMethod)
211 if (m_pContentCompareOptions != nullptr)
212 return m_pContentCompareOptions.get();
213 m_pContentCompareOptions.reset(pCompareOptions = new DiffutilsOptions());
216 case CMP_QUICK_CONTENT:
217 if (m_pQuickCompareOptions != nullptr)
218 return m_pQuickCompareOptions.get();
219 m_pQuickCompareOptions.reset(pCompareOptions = new QuickCompareOptions());
224 if (pCompareOptions != nullptr)
225 pCompareOptions->SetFromDiffOptions(*m_pOptions);
227 return pCompareOptions;
230 /** @brief Forward call to retrieve plugin info (winds up in DirDoc) */
231 void CDiffContext::FetchPluginInfos(const String& filteredFilenames,
232 PackingInfo ** infoUnpacker, PrediffingInfo ** infoPrediffer)
234 if (!m_piPluginInfos)
236 m_piPluginInfos->FetchPluginInfos(filteredFilenames, infoUnpacker, infoPrediffer);
240 * @brief Check if user has requested aborting the compare.
241 * @return true if user has requested abort, false otherwise.
243 bool CDiffContext::ShouldAbort() const
245 return m_piAbortable!=nullptr && m_piAbortable->ShouldAbort();
249 * @brief Get actual compared paths from DIFFITEM.
250 * @param [in] pCtx Pointer to compare context.
251 * @param [in] di DiffItem from which the paths are created.
252 * @param [out] left Gets the left compare path.
253 * @param [out] right Gets the right compare path.
254 * @note If item is unique, same path is returned for both.
256 void CDiffContext::GetComparePaths(const DIFFITEM &di, PathContext & tFiles) const
258 int nDirs = GetCompareDirs();
260 tFiles.SetSize(nDirs);
262 for (int nIndex = 0; nIndex < nDirs; nIndex++)
264 if (di.diffcode.exists(nIndex))
266 tFiles.SetPath(nIndex,
267 paths::ConcatPath(GetPath(nIndex), di.diffFileInfo[nIndex].GetFile()), false);
271 tFiles.SetPath(nIndex, _T("NUL"), false);
276 String CDiffContext::GetFilteredFilenames(const DIFFITEM& di) const
279 GetComparePaths(di, paths);
280 return GetFilteredFilenames(paths);
283 void CDiffContext::CreateDuplicateValueMap()
285 if (!m_pPropertySystem)
287 const int nDirs = GetCompareDirs();
288 m_duplicateValues.clear();
289 m_duplicateValues.resize(m_pPropertySystem->GetCanonicalNames().size());
290 DIFFITEM *pos = GetFirstDiffPosition();
291 std::vector<int> currentGroupId(m_duplicateValues.size());
292 while (pos != nullptr)
294 const DIFFITEM& di = GetNextDiffPosition(pos);
295 for (int pane = 0; pane < nDirs; ++pane)
297 const PropertyValues* pValues = di.diffFileInfo[pane].m_pAdditionalProperties.get();
300 for (size_t j = 0; j < pValues->GetSize(); ++j)
302 if (pValues->IsHashValue(j) )
304 std::vector<uint8_t> value = pValues->GetHashValue(j);
307 auto it = m_duplicateValues[j].find(value);
308 if (it == m_duplicateValues[j].end())
310 DuplicateInfo info{};
312 info.nonpaired = !di.diffcode.existAll();
313 m_duplicateValues[j].insert_or_assign(value, info);
317 if (it->second.groupid == 0)
320 for (int k = 0; k < nDirs; ++k)
321 count += it->second.count[k];
325 it->second.groupid = currentGroupId[j];
328 ++it->second.count[pane];
329 if (!it->second.nonpaired && !di.diffcode.existAll())
330 it->second.nonpaired = true;