4 * @brief Implementation file for DiffUtils class.
6 // ID line follows -- this is updated by SVN
7 // $Id: DiffUtils.cpp 6932 2009-07-26 14:04:31Z kimmov $
12 #include "CompareOptions.h"
13 #include "FilterList.h"
14 #include "DiffContext.h"
16 #include "DiffUtils.h"
17 #include "coretools.h"
19 #include "DiffWrapper.h"
20 #include "FilterCommentsManager.h"
22 namespace CompareEngines
24 static void CopyTextStats(const file_data * inf, FileTextStats * myTextStats);
27 * @brief Default constructor.
29 DiffUtils::DiffUtils()
33 , m_FilterCommentsManager(new ::FilterCommentsManager)
34 , m_pDiffWrapper(new ::CDiffWrapper)
39 * @brief Default destructor.
41 DiffUtils::~DiffUtils()
47 * @brief Set compare options from general compare options.
48 * @param [in ]options General compare options.
49 * @return true if succeeded, false otherwise.
51 bool DiffUtils::SetCompareOptions(const CompareOptions & options)
53 m_pOptions.reset(new DiffutilsOptions((DiffutilsOptions&)options));
54 if (m_pOptions.get() == NULL)
57 m_pOptions->SetToDiffUtils();
62 * @brief Clear current filters list.
63 * Don't delete the list as it points to external list.
65 void DiffUtils::ClearFilterList()
71 * @brief Set line filters list to use.
72 * @param [in] list List of line filters.
74 void DiffUtils::SetFilterList(FilterList * list)
80 * @brief Set filedata.
81 * @param [in] items Count of filedata items to set.
82 * @param [in] data File data.
84 void DiffUtils::SetFileData(int items, file_data *data)
86 // We support only two files currently!
92 * @brief Compare two files (as earlier specified).
93 * @return DIFFCODE as a result of compare.
95 int DiffUtils::diffutils_compare_files()
98 int bin_file = 0; // bitmap for binary files
100 // Do the actual comparison (generating a change script)
101 struct change *script = NULL;
102 bool success = Diff2Files(&script, 0, &bin_flag, false, &bin_file);
105 return DIFFCODE::FILE | DIFFCODE::TEXT | DIFFCODE::CMPERR;
107 UINT code = DIFFCODE::FILE | DIFFCODE::TEXT | DIFFCODE::SAME;
109 // make sure to start counting diffs at 0
110 // (usually it is -1 at this point, for unknown)
116 struct change *next = script;
117 struct change *thisob = 0, *end = 0;
120 String LowerCaseExt = CA2T(m_inf[0].name);
121 int PosOfDot = LowerCaseExt.rfind('.');
124 LowerCaseExt.erase(0, PosOfDot + 1);
125 CharLower(&*LowerCaseExt.begin());
126 asLwrCaseExt = LowerCaseExt;
131 /* Find a set of changes that belong together. */
133 end = find_change(next);
135 /* Disconnect them from the rest of the changes,
136 making them a hunk, and remember the rest for next iteration. */
140 debug_script(thisob);
144 /* Determine range of line numbers involved in each file. */
145 int first0 = 0, last0 = 0, first1 = 0, last1 = 0, deletes = 0, inserts = 0;
146 analyze_hunk (thisob, &first0, &last0, &first1, &last1, &deletes, &inserts, m_inf);
147 if (deletes || inserts || thisob->trivial)
149 /* Print the lines that the first file has. */
150 int trans_a0 = 0, trans_b0 = 0, trans_a1 = 0, trans_b1 = 0;
151 translate_range(&m_inf[0], first0, last0, &trans_a0, &trans_b0);
152 translate_range(&m_inf[1], first1, last1, &trans_a1, &trans_b1);
154 //Determine quantity of lines in this block for both sides
155 int QtyLinesLeft = (trans_b0 - trans_a0);
156 int QtyLinesRight = (trans_b1 - trans_a1);
158 if(m_pOptions->m_filterCommentsLines)
160 OP_TYPE op = OP_NONE;
161 if (!deletes && !inserts)
166 DIFFOPTIONS options = {0};
167 options.nIgnoreWhitespace = m_pOptions->m_ignoreWhitespace;
168 options.bIgnoreBlankLines = m_pOptions->m_bIgnoreBlankLines;
169 options.bFilterCommentsLines = m_pOptions->m_filterCommentsLines;
170 options.bIgnoreCase = m_pOptions->m_bIgnoreCase;
171 options.bIgnoreEol = m_pOptions->m_bIgnoreEOLDifference;
172 m_pDiffWrapper->SetOptions(&options);
173 m_pDiffWrapper->PostFilter(thisob->line0, QtyLinesLeft+1, thisob->line1, QtyLinesRight+1, op, *m_FilterCommentsManager, asLwrCaseExt.c_str());
180 // Match lines against regular expression filters
181 // Our strategy is that every line in both sides must
182 // match regexp before we mark difference as ignored.
183 if(m_pFilterList && m_pFilterList->HasRegExps())
186 bool match1 = RegExpFilter(thisob->line0, thisob->line0 + QtyLinesLeft, 0);
188 match2 = RegExpFilter(thisob->line1, thisob->line1 + QtyLinesRight, 1);
189 if (match1 && match2)
194 /* Reconnect the script so it will all be freed properly. */
201 // Free change script (which we don't want)
204 struct change *p, *e;
205 for (e = script; e; e = p)
215 code = code & ~DIFFCODE::SAME | DIFFCODE::DIFF;
218 // diff_2_files set bin_flag to -1 if different binary
219 // diff_2_files set bin_flag to +1 if same binary
223 // Clear text-flag, set binary flag
224 // We don't know diff counts for binary files
225 code = code & ~DIFFCODE::TEXT;
229 code |= DIFFCODE::BINSIDE1;
232 code |= DIFFCODE::BINSIDE2;
234 case BINFILE_SIDE1 | BINFILE_SIDE2:
235 code |= DIFFCODE::BIN;
238 _RPTF1(_CRT_ERROR, "Invalid bin_file value: %d", bin_file);
241 m_ndiffs = CDiffContext::DIFFS_UNKNOWN;
246 // Clear same-flag, set diff-flag
247 code = code & ~DIFFCODE::SAME | DIFFCODE::DIFF;
254 * @brief Match regular expression list against given difference.
255 * This function matches the regular expression list against the difference
256 * (given as start line and end line). Matching the diff requires that all
257 * lines in difference match.
258 * @param [in] StartPos First line of the difference.
259 * @param [in] endPos Last line of the difference.
260 * @param [in] FileNo File to match.
261 * return true if any of the expressions matches.
263 bool DiffUtils::RegExpFilter(int StartPos, int EndPos, int FileNo)
265 if (m_pFilterList == NULL)
267 _RPTF0(_CRT_ERROR, "DiffUtils::RegExpFilter() called when "
268 "filterlist doesn't exist (=NULL)");
272 bool linesMatch = true; // set to false when non-matching line is found.
275 while (line <= EndPos && linesMatch == true)
277 size_t len = files[FileNo].linbuf[line + 1] - files[FileNo].linbuf[line];
278 const char *string = files[FileNo].linbuf[line];
279 size_t stringlen = linelen(string, len);
280 if (!m_pFilterList->Match(std::string(string, stringlen), m_codepage))
290 * @brief Compare two files using diffutils.
292 * Compare two files (in DiffFileData param) using diffutils. Run diffutils
293 * inside SEH so we can trap possible error and exceptions. If error or
294 * execption is trapped, return compare failure.
295 * @param [out] diffs Pointer to list of change structs where diffdata is stored.
296 * @param [in] depth Depth in folder compare (we use 0).
297 * @param [out] bin_status used to return binary status from compare.
298 * @param [in] bMovedBlocks If TRUE moved blocks are analyzed.
299 * @param [out] bin_file Returns which file was binary file as bitmap.
300 So if first file is binary, first bit is set etc. Can be NULL if binary file
301 info is not needed (faster compare since diffutils don't bother checking
302 second file if first is binary).
303 * @return TRUE when compare succeeds, FALSE if error happened during compare.
305 bool DiffUtils::Diff2Files(struct change ** diffs, int depth,
306 int * bin_status, bool bMovedBlocks, int * bin_file)
311 *diffs = diff_2_files(m_inf, depth, bin_status, bMovedBlocks, bin_file);
313 __except(EXCEPTION_EXECUTE_HANDLER)
322 * @brief Copy text stat results from diffutils back into the FileTextStats structure
324 static void CopyTextStats(const file_data * inf, FileTextStats * myTextStats)
326 myTextStats->ncrlfs = inf->count_crlfs;
327 myTextStats->ncrs = inf->count_crs;
328 myTextStats->nlfs = inf->count_lfs;
332 * @brief Return diff counts for last compare.
333 * @param [out] diffs Count of real differences.
334 * @param [out] trivialDiffs Count of ignored differences.
336 void DiffUtils::GetDiffCounts(int & diffs, int & trivialDiffs)
339 trivialDiffs = m_ntrivialdiffs;
343 * @brief Return text statistics for last compare.
344 * @param [in] side For which file to return statistics.
345 * @param [out] stats Stats as asked.
347 void DiffUtils::GetTextStats(int side, FileTextStats *stats)
349 CopyTextStats(&m_inf[side], stats);
352 } // namespace CompareEngines