1 /////////////////////////////////////////////////////////////////////////////
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 2 of the License, or (at
6 // your option) any later version.
8 // This program is distributed in the hope that it will be useful, but
9 // WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 /////////////////////////////////////////////////////////////////////////////
18 * @file DiffWrapper.cpp
20 * @brief Code for DiffWrapper class
22 * @date Created: 2003-08-22
26 #include "DiffWrapper.h"
27 #include <sys/types.h>
38 #include <Poco/Format.h>
39 #include <Poco/Debugger.h>
40 #include <Poco/StringTokenizer.h>
41 #include <Poco/Exception.h>
42 #include "DiffContext.h"
43 #include "coretools.h"
45 #include "MovedLines.h"
46 #include "FilterList.h"
49 #include "FileTransform.h"
51 #include "CompareOptions.h"
52 #include "FileTextStats.h"
53 #include "FolderCmp.h"
54 #include "FilterCommentsManager.h"
55 #include "Environment.h"
56 #include "PatchHTML.h"
57 #include "UnicodeString.h"
60 #include "Exceptions.h"
65 using Poco::StringTokenizer;
66 using Poco::Exception;
70 static void CopyTextStats(const file_data * inf, FileTextStats * myTextStats);
71 static void CopyDiffutilTextStats(file_data *inf, DiffFileData * diffData);
74 * @brief Default constructor.
75 * Initializes members and creates new FilterCommentsManager.
77 CDiffWrapper::CDiffWrapper()
78 : m_pFilterCommentsManager(nullptr)
79 , m_bCreatePatchFile(false)
80 , m_bUseDiffList(false)
82 , m_bAppendFiles(false)
84 , m_codepage(GetACP())
85 , m_infoPrediffer(nullptr)
86 , m_pDiffList(nullptr)
87 , m_bPathsAreTemp(false)
88 , m_pFilterList(nullptr)
89 , m_bPluginsEnabled(false)
92 // character that ends a line. Currently this is always `\n'
99 CDiffWrapper::~CDiffWrapper()
104 * @brief Set plugins enabled/disabled.
105 * @param [in] enable if true plugins are enabled.
107 void CDiffWrapper::EnablePlugins(bool enable)
109 m_bPluginsEnabled = enable;
113 * @brief Enables/disables patch-file creation and sets filename.
114 * This function enables or disables patch file creation. When
115 * @p filename is empty, patch files are disabled.
116 * @param [in] filename Filename for patch file, or empty string.
118 void CDiffWrapper::SetCreatePatchFile(const String &filename)
120 if (filename.empty())
122 m_bCreatePatchFile = false;
123 m_sPatchFile.clear();
127 m_bCreatePatchFile = true;
128 m_sPatchFile = filename;
129 strutils::replace(m_sPatchFile, _T("/"), _T("\\"));
134 * @brief Enables/disabled DiffList creation ands sets DiffList.
135 * This function enables or disables DiffList creation. When
136 * @p diffList is NULL difflist is not created. When valid DiffList
137 * pointer is given, compare results are stored into it.
138 * @param [in] diffList Pointer to DiffList getting compare results.
140 void CDiffWrapper::SetCreateDiffList(DiffList *diffList)
142 if (diffList == NULL)
144 m_bUseDiffList = false;
149 m_bUseDiffList = true;
150 m_pDiffList = diffList;
155 * @brief Returns current set of options used by diff-engine.
156 * This function converts internally used diff-options to
157 * format used outside CDiffWrapper and returns them.
158 * @param [in,out] options Pointer to structure getting used options.
160 void CDiffWrapper::GetOptions(DIFFOPTIONS *options) const
162 assert(options != nullptr);
163 DIFFOPTIONS tmpOptions = {0};
164 m_options.GetAsDiffOptions(tmpOptions);
165 *options = tmpOptions;
169 * @brief Set options for Diff-engine.
170 * This function converts given options to format CDiffWrapper uses
171 * internally and stores them.
172 * @param [in] options Pointer to structure having new options.
174 void CDiffWrapper::SetOptions(const DIFFOPTIONS *options)
176 assert(options != nullptr);
177 m_options.SetFromDiffOptions(*options);
181 * @brief Set text tested to find the prediffer automatically.
182 * Most probably a concatenated string of both filenames.
184 void CDiffWrapper::SetTextForAutomaticPrediff(const String &text)
186 m_sToFindPrediffer = text;
188 void CDiffWrapper::SetPrediffer(const PrediffingInfo * prediffer /*=NULL*/)
190 // all flags are set correctly during the construction
191 m_infoPrediffer.reset(new PrediffingInfo);
194 *m_infoPrediffer = *prediffer;
196 void CDiffWrapper::GetPrediffer(PrediffingInfo * prediffer) const
198 *prediffer = *m_infoPrediffer;
202 * @brief Set options used for patch-file creation.
203 * @param [in] options Pointer to structure having new options.
205 void CDiffWrapper::SetPatchOptions(const PATCHOPTIONS *options)
207 assert(options != nullptr);
208 m_options.m_contextLines = options->nContext;
210 switch (options->outputStyle)
213 m_options.m_outputStyle = DIFF_OUTPUT_NORMAL;
216 m_options.m_outputStyle = DIFF_OUTPUT_CONTEXT;
219 m_options.m_outputStyle = DIFF_OUTPUT_UNIFIED;
222 m_options.m_outputStyle = DIFF_OUTPUT_HTML;
225 throw "Unknown output style!";
229 m_bAddCmdLine = options->bAddCommandline;
233 * @brief Enables/disables moved block detection.
234 * @param [in] bDetectMovedBlocks If true moved blocks are detected.
236 void CDiffWrapper::SetDetectMovedBlocks(bool bDetectMovedBlocks)
238 if (bDetectMovedBlocks)
240 if (m_pMovedLines[0] == NULL)
242 m_pMovedLines[0].reset(new MovedLines);
243 m_pMovedLines[1].reset(new MovedLines);
244 m_pMovedLines[2].reset(new MovedLines);
249 m_pMovedLines[0].reset();
250 m_pMovedLines[1].reset();
251 m_pMovedLines[2].reset();
256 * @brief Test for trivial only characters in string
257 * @param [in] Start - Start position in string
258 * @param [in] End - One character pass the end position of the string
259 * @param [in] filtercommentsset - For future use to determine trivial bytes
260 * @return Returns true if all characters are trivial
262 bool CDiffWrapper::IsTrivialBytes(const char* Start, const char* End,
263 const FilterCommentsSet& filtercommentsset) const
265 std::string testdata(Start, End);
266 //@TODO: Need to replace the following trivial string with a user specified string
267 size_t pos = testdata.find_first_not_of(" \t\r\n");
268 return (pos == std::string::npos);
272 * @brief Test for a line of trivial data
273 * @param [in] Line - String to test for
274 * @param [in] StartOfComment -
275 * @param [in] EndOfComment -
276 * @param [in] InLineComment -
277 * @param [in] filtercommentsset - Comment marker set used to indicate comment blocks.
278 * @return Returns true if entire line is trivial
280 bool CDiffWrapper::IsTrivialLine(const std::string &Line,
281 const char * StartOfComment,
282 const char * EndOfComment,
283 const char * InLineComment,
284 const FilterCommentsSet& filtercommentsset) const
287 if ((!StartOfComment || !EndOfComment) && !InLineComment)
288 return false;//In no Start and End pair, and no single in-line set, then it's not trivial
290 if (StartOfComment == Line.c_str() &&
291 static_cast<size_t>((EndOfComment + filtercommentsset.EndMarker.size()) - StartOfComment) == Line.size())
292 {//If entire line is blocked by End and Start markers, then entire line is trivial
296 if (InLineComment && InLineComment < StartOfComment)
298 if (InLineComment == Line.c_str())
299 return true;//If line starts with InLineComment marker, then entire line is trivial
301 //Other wise, check if data before InLineComment marker is trivial
302 return IsTrivialBytes(Line.c_str(), InLineComment, filtercommentsset);
305 //Done with easy test, so now do more complex test
306 if (StartOfComment &&
308 StartOfComment < EndOfComment &&
309 IsTrivialBytes(Line.c_str(), StartOfComment, filtercommentsset) &&
310 IsTrivialBytes(EndOfComment + filtercommentsset.EndMarker.size(),
311 Line.c_str()+Line.size(), filtercommentsset))
320 * @brief Find comment marker in string, excluding portions enclosed in quotation marks or apostrophes
321 * @param [in] target - string to search
322 * @param [in] marker - marker to search for
323 * @return Returns position of marker, or NULL if none is present
325 static const char *FindCommentMarker(const char *target, const char *marker)
329 size_t marker_len = strlen(marker);
330 while (char c = *target)
332 if (quote == '\0' && strncmp(target, marker, marker_len) == 0)
334 if ((prev != '\\') &&
335 (c == '"' || c == '\'') &&
336 (quote == '\0' || quote == c))
347 * @brief Replace spaces in a string
348 * @param [in] str - String to search
349 * @param [in] rep - String to replace
351 static void ReplaceSpaces(std::string & str, const char *rep)
353 std::string::size_type pos = 0;
354 size_t replen = strlen(rep);
355 while ((pos = str.find_first_of(" \t", pos)) != std::string::npos)
357 std::string::size_type posend = str.find_first_not_of(" \t", pos);
358 if (posend != std::string::npos)
359 str.replace(pos, posend - pos, rep);
361 str.replace(pos, 1, rep);
367 @brief Performs post-filtering, by setting comment blocks to trivial
368 @param [in] StartPos - First line number to read
369 @param [in] EndPos - The line number PASS the last line number to read
370 @param [in] QtyLinesInBlock - Number of lines in diff block. Not needed in backward direction.
371 @param [in] Direction - This should be 1 or -1, to indicate which direction to read (backward or forward)
372 @param [in,out] Op - This variable is set to trivial if block should be ignored.
373 @param [in] FileNo - Should be 0 or 1, to indicate left or right file.
374 @param [in] filtercommentsset - Comment marker set used to indicate comment blocks.
375 @return Always returns true in reverse direction.
376 In forward direction, returns false if none trivial data is found within QtyLinesInBlock
378 bool CDiffWrapper::PostFilter(int StartPos, int EndPos, int Direction,
379 int QtyLinesInBlock, OP_TYPE &Op, int FileNo,
380 FilterCommentsSet& filtercommentsset) const
382 if (Op == OP_TRIVIAL) //If already set to trivial, then exit.
384 bool OpShouldBeTrivial = false;
385 int QtyTrivialLines = 0;
386 for(int i = StartPos + ((Direction == -1)?-1:0); i != EndPos;i += Direction)
388 if ((i - StartPos) == QtyLinesInBlock &&
389 QtyLinesInBlock == QtyTrivialLines)
391 OpShouldBeTrivial = true;
394 size_t len = files[FileNo].linbuf[i + 1] - files[FileNo].linbuf[i];
395 const char *LineStr = files[FileNo].linbuf[i];
396 std::string LineData(LineStr, linelen(LineStr, len));
398 const char * StartOfComment = FindCommentMarker(LineData.c_str(), filtercommentsset.StartMarker.c_str());
399 const char * EndOfComment = FindCommentMarker(LineData.c_str(), filtercommentsset.EndMarker.c_str());
400 const char * InLineComment = FindCommentMarker(LineData.c_str(), filtercommentsset.InlineMarker.c_str());
401 //The following logic determines if the entire block is a comment block, and only marks it as trivial
402 //if all the changes are within a comment block.
405 if (!StartOfComment && EndOfComment)
408 if (StartOfComment && (!EndOfComment || EndOfComment < StartOfComment) && (!InLineComment || InLineComment > StartOfComment))
410 OpShouldBeTrivial = true;
414 else if (Direction == 1)
416 if (IsTrivialBytes(LineData.c_str(), LineData.c_str()+LineData.size(), filtercommentsset) ||
417 IsTrivialLine(LineData, StartOfComment, EndOfComment, InLineComment, filtercommentsset))
422 if (!EndOfComment && StartOfComment)
424 if (i == (StartPos + QtyTrivialLines) )
426 if (StartOfComment == LineData.c_str())
427 {//If this is at the beginning of the first line, then lets continue
430 if (IsTrivialBytes(LineData.c_str(), StartOfComment, filtercommentsset))
431 {//If only trivial bytes before comment marker, then continue
436 //If this is not the first line, then assume
437 //previous lines are non-trivial, and return true.
442 (!StartOfComment || StartOfComment > EndOfComment) &&
443 (!InLineComment || InLineComment > EndOfComment) )
445 if (!IsTrivialBytes(EndOfComment+filtercommentsset.EndMarker.size(), LineData.c_str()+LineData.size(), filtercommentsset))
450 if ((i - StartPos) >= (QtyLinesInBlock-1))
452 OpShouldBeTrivial = true;
456 //Lets check if the remaining lines only contain trivial data
457 bool AllRemainingLinesContainTrivialData = true;
458 int TrivLinePos = i+1;
459 for(; TrivLinePos != (StartPos + QtyLinesInBlock);++TrivLinePos)
461 size_t len1 = files[FileNo].linbuf[TrivLinePos + 1] - files[FileNo].linbuf[TrivLinePos];
462 const char *LineStrTrvCk = files[FileNo].linbuf[TrivLinePos];
463 std::string LineDataTrvCk(LineStrTrvCk, linelen(LineStrTrvCk, len1));
464 if (LineDataTrvCk.size() &&
465 !IsTrivialBytes(LineDataTrvCk.c_str(), LineDataTrvCk.c_str() + LineDataTrvCk.size(), filtercommentsset))
467 AllRemainingLinesContainTrivialData = false;
471 if (AllRemainingLinesContainTrivialData)
473 OpShouldBeTrivial = true;
476 if (TrivLinePos != (StartPos + QtyLinesInBlock) )
478 return PostFilter(TrivLinePos, EndPos, Direction, QtyLinesInBlock - (TrivLinePos - StartPos), Op, FileNo, filtercommentsset);
483 if (OpShouldBeTrivial)
491 @brief The main entry for post filtering. Performs post-filtering, by setting comment blocks to trivial
492 @param [in] LineNumberLeft - First line number to read from left file
493 @param [in] QtyLinesLeft - Number of lines in the block for left file
494 @param [in] LineNumberRight - First line number to read from right file
495 @param [in] QtyLinesRight - Number of lines in the block for right file
496 @param [in,out] Op - This variable is set to trivial if block should be ignored.
497 @param [in] FileNameExt - The file name extension. Needs to be lower case string ("cpp", "java", "c")
499 void CDiffWrapper::PostFilter(int LineNumberLeft, int QtyLinesLeft, int LineNumberRight,
500 int QtyLinesRight, OP_TYPE &Op, const String& FileNameExt) const
502 if (Op == OP_TRIVIAL || !m_pFilterCommentsManager)
505 //First we need to get lowercase file name extension
506 FilterCommentsSet filtercommentsset = m_pFilterCommentsManager->GetSetForFileType(FileNameExt);
507 if (filtercommentsset.StartMarker.empty() &&
508 filtercommentsset.EndMarker.empty() &&
509 filtercommentsset.InlineMarker.empty())
514 OP_TYPE LeftOp = OP_NONE;
515 OP_TYPE RightOp = OP_NONE;
517 if (QtyLinesRight == 0)
518 { //Only check left side
519 if (PostFilter(LineNumberLeft, files[0].valid_lines, 1, QtyLinesLeft, LeftOp, 0, filtercommentsset))
520 PostFilter(LineNumberLeft, -1, -1, QtyLinesLeft, LeftOp, 0, filtercommentsset);
522 else if (QtyLinesLeft == 0)
523 { //Only check right side
524 if (PostFilter(LineNumberRight, files[1].valid_lines, 1, QtyLinesRight, RightOp, 1, filtercommentsset))
525 PostFilter(LineNumberRight, -1, -1, QtyLinesRight, RightOp, 1, filtercommentsset);
529 if (PostFilter(LineNumberLeft, files[0].valid_lines, 1, QtyLinesLeft, LeftOp, 0, filtercommentsset))
530 PostFilter(LineNumberLeft, -1, -1, QtyLinesLeft, LeftOp, 0, filtercommentsset);
532 if (PostFilter(LineNumberRight, files[1].valid_lines, 1, QtyLinesRight, RightOp, 1, filtercommentsset))
533 PostFilter(LineNumberRight, -1, -1, QtyLinesRight, RightOp, 1, filtercommentsset);
536 std::list<std::string> LeftLines, RightLines;
537 for (int i = 0; (i < QtyLinesLeft) || (i < QtyLinesRight); i++)
539 //Lets test all lines if only a comment is different.
540 const char * LineStrLeft = "";
541 const char * EndLineLeft = LineStrLeft;
542 const char * LineStrRight = "";
543 const char * EndLineRight = LineStrRight;
546 LineStrLeft = files[0].linbuf[LineNumberLeft + i];
547 EndLineLeft = files[0].linbuf[LineNumberLeft + i + 1];
549 if(i < QtyLinesRight)
551 LineStrRight = files[1].linbuf[LineNumberRight + i];
552 EndLineRight = files[1].linbuf[LineNumberRight + i + 1];
555 if (EndLineLeft && EndLineRight)
557 std::string LineDataLeft(LineStrLeft, EndLineLeft);
558 std::string LineDataRight(LineStrRight, EndLineRight);
560 if (!filtercommentsset.StartMarker.empty() && !filtercommentsset.EndMarker.empty())
562 const char * CommentStrLeftStart;
563 const char * CommentStrLeftEnd;
564 const char * CommentStrRightStart;
565 const char * CommentStrRightEnd;
567 bool bFirstLoop = true;
569 //Lets remove block comments, and see if lines are equal
570 CommentStrLeftStart = FindCommentMarker(LineDataLeft.c_str(), filtercommentsset.StartMarker.c_str());
571 CommentStrLeftEnd = FindCommentMarker(LineDataLeft.c_str(), filtercommentsset.EndMarker.c_str());
572 CommentStrRightStart = FindCommentMarker(LineDataRight.c_str(), filtercommentsset.StartMarker.c_str());
573 CommentStrRightEnd = FindCommentMarker(LineDataRight.c_str(), filtercommentsset.EndMarker.c_str());
575 if (CommentStrLeftStart != NULL && CommentStrLeftEnd != NULL && CommentStrLeftStart < CommentStrLeftEnd)
576 LineDataLeft.erase(CommentStrLeftStart - LineDataLeft.c_str(), CommentStrLeftEnd + filtercommentsset.EndMarker.size() - CommentStrLeftStart);
577 else if (CommentStrLeftEnd != NULL)
578 LineDataLeft.erase(0, CommentStrLeftEnd + filtercommentsset.EndMarker.size() - LineDataLeft.c_str());
579 else if (CommentStrLeftStart != NULL)
580 LineDataLeft.erase(CommentStrLeftStart - LineDataLeft.c_str());
581 else if(LeftOp == OP_TRIVIAL && bFirstLoop)
582 LineDataLeft.erase(0); //This line is all in block comments
584 if (CommentStrRightStart != NULL && CommentStrRightEnd != NULL && CommentStrRightStart < CommentStrRightEnd)
585 LineDataRight.erase(CommentStrRightStart - LineDataRight.c_str(), CommentStrRightEnd + filtercommentsset.EndMarker.size() - CommentStrRightStart);
586 else if (CommentStrRightEnd != NULL)
587 LineDataRight.erase(0, CommentStrRightEnd + filtercommentsset.EndMarker.size() - LineDataRight.c_str());
588 else if (CommentStrRightStart != NULL)
589 LineDataRight.erase(CommentStrRightStart - LineDataRight.c_str());
590 else if(RightOp == OP_TRIVIAL && bFirstLoop)
591 LineDataRight.erase(0); //This line is all in block comments
595 } while (CommentStrLeftStart != NULL || CommentStrLeftEnd != NULL
596 || CommentStrRightStart != NULL || CommentStrRightEnd != NULL); //Loops until all blockcomments are lost
599 if (!filtercommentsset.InlineMarker.empty())
601 //Lets remove line comments
602 const char * CommentStrLeft = FindCommentMarker(LineDataLeft.c_str(), filtercommentsset.InlineMarker.c_str());
603 const char * CommentStrRight = FindCommentMarker(LineDataRight.c_str(), filtercommentsset.InlineMarker.c_str());
605 if (CommentStrLeft != NULL)
606 LineDataLeft.erase(CommentStrLeft - LineDataLeft.c_str());
607 if (CommentStrRight != NULL)
608 LineDataRight.erase(CommentStrRight - LineDataRight.c_str());
611 if (m_options.m_ignoreWhitespace == WHITESPACE_IGNORE_ALL)
613 //Ignore character case
614 ReplaceSpaces(LineDataLeft, "");
615 ReplaceSpaces(LineDataRight, "");
617 else if (m_options.m_ignoreWhitespace == WHITESPACE_IGNORE_CHANGE)
619 //Ignore change in whitespace char count
620 ReplaceSpaces(LineDataLeft, " ");
621 ReplaceSpaces(LineDataRight, " ");
624 if (m_options.m_bIgnoreCase)
627 // std::transform(LineDataLeft.begin(), LineDataLeft.end(), LineDataLeft.begin(), ::toupper);
628 for (std::basic_string<char>::iterator pb = LineDataLeft.begin(), pe = LineDataLeft.end(); pb != pe; ++pb)
629 *pb = static_cast<char>(::toupper(*pb));
630 // std::transform(LineDataRight.begin(), LineDataRight.end(), LineDataRight.begin(), ::toupper);
631 for (std::basic_string<char>::iterator pb = LineDataRight.begin(), pe = LineDataRight.end(); pb != pe; ++pb)
632 *pb = static_cast<char>(::toupper(*pb));
635 if (!LineDataLeft.empty())
636 LeftLines.push_back(LineDataLeft);
637 if (!LineDataRight.empty())
638 RightLines.push_back(LineDataRight);
641 if (LeftLines != RightLines)
643 //only difference is trival
648 * @brief Set source paths for diffing two files.
649 * Sets full paths to two files we are diffing. Paths can be actual user files
650 * or temporary copies of user files. Parameter @p tempPaths tells if paths
651 * are temporary paths that can be deleted.
652 * @param [in] files Files to compare
653 * @param [in] tempPaths Are given paths temporary (can be deleted)?.
655 void CDiffWrapper::SetPaths(const PathContext &tFiles,
659 m_bPathsAreTemp = tempPaths;
663 * @brief Set source paths for original (NON-TEMP) diffing two files.
664 * Sets full paths to two (NON-TEMP) files we are diffing.
665 * @param [in] OriginalFile1 First file to compare "(NON-TEMP) file".
666 * @param [in] OriginalFile2 Second file to compare "(NON-TEMP) file".
668 void CDiffWrapper::SetCompareFiles(const PathContext &originalFile)
670 m_originalFile = originalFile;
674 * @brief Set alternative paths for compared files.
675 * Sets alternative paths for diff'ed files. These alternative paths might not
676 * be real paths. For example when creating a patch file from folder compare
677 * we want to use relative paths.
678 * @param [in] altPaths Alternative file paths.
680 void CDiffWrapper::SetAlternativePaths(const PathContext &altPaths)
682 m_alternativePaths = altPaths;
686 * @brief Runs diff-engine.
688 bool CDiffWrapper::RunFileDiff()
690 PathContext aFiles = m_files;
692 for (file = 0; file < m_files.GetSize(); file++)
693 aFiles[file] = paths::ToWindowsPath(aFiles[file]);
696 String strFileTemp[3];
697 std::copy(m_files.begin(), m_files.end(), strFileTemp);
699 m_options.SetToDiffUtils();
702 m_nDiffs = m_pDiffList->GetSize();
704 for (file = 0; file < aFiles.GetSize(); file++)
706 if (m_bPluginsEnabled)
708 // Do the preprocessing now, overwrite the temp files
709 // NOTE: FileTransform_UCS2ToUTF8() may create new temp
710 // files and return new names, those created temp files
711 // are deleted in end of function.
713 // this can only fail if the data can not be saved back (no more
714 // place on disk ???) What to do then ??
715 if (!FileTransform::Prediffing(m_infoPrediffer.get(), strFileTemp[file], m_sToFindPrediffer, m_bPathsAreTemp))
717 // display a message box
718 String sError = strutils::format(
719 _T("An error occurred while prediffing the file '%s' with the plugin '%s'. The prediffing is not applied any more."),
720 strFileTemp[file].c_str(),
721 m_infoPrediffer->pluginName.c_str());
722 AppErrorMessageBox(sError);
723 // don't use any more this prediffer
724 m_infoPrediffer->bToBeScanned = false;
725 m_infoPrediffer->pluginName.erase();
728 // We use the same plugin for both files, so it must be defined before
730 assert(!m_infoPrediffer->bToBeScanned);
734 struct change *script = NULL;
735 struct change *script10 = NULL;
736 struct change *script12 = NULL;
737 DiffFileData diffdata, diffdata10, diffdata12;
738 int bin_flag = 0, bin_flag10 = 0, bin_flag12 = 0;
740 if (aFiles.GetSize() == 2)
742 diffdata.SetDisplayFilepaths(aFiles[0], aFiles[1]); // store true names for diff utils patch file
743 // This opens & fstats both files (if it succeeds)
744 if (!diffdata.OpenFiles(strFileTemp[0], strFileTemp[1]))
749 // Compare the files, if no error was found.
750 // Last param (bin_file) is NULL since we don't
751 // (yet) need info about binary sides.
752 bRet = Diff2Files(&script, &diffdata, &bin_flag, NULL);
754 // We don't anymore create diff-files for every rescan.
755 // User can create patch-file whenever one wants to.
756 // We don't need to waste time. But lets keep this as
757 // debugging aid. Sometimes it is very useful to see
758 // what differences diff-engine sees!
760 // throw the diff into a temp file
761 String sTempPath = env::GetTemporaryPath(); // get path to Temp folder
762 String path = paths::ConcatPath(sTempPath, _T("Diff.txt"));
764 if (_tfopen_s(&outfile, path.c_str(), _T("w+")) == 0)
766 print_normal_script(script);
774 diffdata10.SetDisplayFilepaths(aFiles[1], aFiles[0]); // store true names for diff utils patch file
775 diffdata12.SetDisplayFilepaths(aFiles[1], aFiles[2]); // store true names for diff utils patch file
777 if (!diffdata10.OpenFiles(strFileTemp[1], strFileTemp[0]))
782 bRet = Diff2Files(&script10, &diffdata10, &bin_flag10, NULL);
784 if (!diffdata12.OpenFiles(strFileTemp[1], strFileTemp[2]))
789 bRet = Diff2Files(&script12, &diffdata12, &bin_flag12, NULL);
792 // First determine what happened during comparison
793 // If there were errors or files were binaries, don't bother
794 // creating diff-lists or patches
796 // diff_2_files set bin_flag to -1 if different binary
797 // diff_2_files set bin_flag to +1 if same binary
799 file_data * inf = diffdata.m_inf;
800 file_data * inf10 = diffdata10.m_inf;
801 file_data * inf12 = diffdata12.m_inf;
803 if (aFiles.GetSize() == 2)
807 m_status.bBinaries = true;
809 m_status.Identical = IDENTLEVEL_ALL;
811 m_status.Identical = IDENTLEVEL_NONE;
814 { // text files according to diffutils, so change script exists
815 m_status.Identical = (script == 0) ? IDENTLEVEL_ALL : IDENTLEVEL_NONE;
816 m_status.bBinaries = false;
818 m_status.bMissingNL[0] = !!inf[0].missing_newline;
819 m_status.bMissingNL[1] = !!inf[1].missing_newline;
823 m_status.Identical = IDENTLEVEL_NONE;
824 if (bin_flag10 != 0 || bin_flag12 != 0)
826 m_status.bBinaries = true;
827 if (bin_flag10 != -1 && bin_flag12 != -1)
828 m_status.Identical = IDENTLEVEL_ALL;
829 else if (bin_flag10 != -1)
830 m_status.Identical = IDENTLEVEL_EXCEPTRIGHT;
831 else if (bin_flag12 != -1)
832 m_status.Identical = IDENTLEVEL_EXCEPTLEFT;
834 m_status.Identical = IDENTLEVEL_EXCEPTMIDDLE;
837 { // text files according to diffutils, so change script exists
838 m_status.bBinaries = false;
839 if (script10 == 0 && script12 == 0)
840 m_status.Identical = IDENTLEVEL_ALL;
841 else if (script10 == 0)
842 m_status.Identical = IDENTLEVEL_EXCEPTRIGHT;
843 else if (script12 == 0)
844 m_status.Identical = IDENTLEVEL_EXCEPTLEFT;
846 m_status.Identical = IDENTLEVEL_EXCEPTMIDDLE;
848 m_status.bMissingNL[0] = !!inf10[1].missing_newline;
849 m_status.bMissingNL[1] = !!inf12[0].missing_newline;
850 m_status.bMissingNL[2] = !!inf12[1].missing_newline;
855 if (!m_status.bBinaries && m_bCreatePatchFile && aFiles.GetSize() == 2)
857 WritePatchFile(script, &inf[0]);
860 // Go through diffs adding them to WinMerge's diff list
861 // This is done on every WinMerge's doc rescan!
862 if (!m_status.bBinaries && m_bUseDiffList)
864 if (aFiles.GetSize() == 2)
865 LoadWinMergeDiffsFromDiffUtilsScript(script, diffdata.m_inf);
867 LoadWinMergeDiffsFromDiffUtilsScript3(
869 diffdata10.m_inf, diffdata12.m_inf);
872 // cleanup the script
873 if (aFiles.GetSize() == 2)
874 FreeDiffUtilsScript(script);
877 FreeDiffUtilsScript(script10);
878 FreeDiffUtilsScript(script12);
881 // Done with diffutils filedata
882 if (aFiles.GetSize() == 2)
892 if (m_bPluginsEnabled)
894 // Delete temp files transformation functions possibly created
895 for (file = 0; file < aFiles.GetSize(); file++)
897 if (strutils::compare_nocase(aFiles[file], strFileTemp[file]) != 0)
901 TFile(strFileTemp[file]).remove();
905 LogErrorStringUTF8(e.displayText());
907 strFileTemp[file].erase();
915 * @brief Add diff to external diff-list
917 void CDiffWrapper::AddDiffRange(DiffList *pDiffList, unsigned begin0, unsigned end0, unsigned begin1, unsigned end1, OP_TYPE op)
922 dr.begin[0] = begin0;
924 dr.begin[1] = begin1;
929 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
930 pDiffList->AddDiff(dr);
932 catch (std::exception& e)
934 AppErrorMessageBox(ucr::toTString(e.what()));
938 void CDiffWrapper::AddDiffRange(DiffList *pDiffList, DIFFRANGE &dr)
942 pDiffList->AddDiff(dr);
944 catch (std::exception& e)
946 AppErrorMessageBox(ucr::toTString(e.what()));
951 * @brief Expand last DIFFRANGE of file by one line to contain last line after EOL.
952 * @param [in] leftBufferLines size of array pane left
953 * @param [in] rightBufferLines size of array pane right
954 * @param [in] left on whitch side we have to insert
955 * @param [in] bIgnoreBlankLines, if true we always add a new diff and mark as trivial
957 void CDiffWrapper::FixLastDiffRange(int nFiles, int bufferLines[], bool bMissingNL[], bool bIgnoreBlankLines)
960 const int count = m_pDiffList->GetSize();
961 if (count > 0 && !bIgnoreBlankLines)
963 m_pDiffList->GetDiff(count - 1, dr);
965 for (int file = 0; file < nFiles; file++)
967 if (!bMissingNL[file])
971 m_pDiffList->SetDiff(count - 1, dr);
975 // we have to create the DIFF
976 for (int file = 0; file < nFiles; file++)
978 dr.end[file] = bufferLines[file] - 1;
979 if (bMissingNL[file])
980 dr.begin[file] = dr.end[file];
982 dr.begin[file] = dr.end[file] + 1;
984 assert(dr.begin[0] == dr.begin[file]);
986 if (bIgnoreBlankLines)
989 AddDiffRange(m_pDiffList, dr);
994 * @brief Returns status-data from diff-engine last run
996 void CDiffWrapper::GetDiffStatus(DIFFSTATUS *status) const
998 std::memcpy(status, &m_status, sizeof(DIFFSTATUS));
1002 * @brief Formats command-line for diff-engine last run (like it was called from command-line)
1004 String CDiffWrapper::FormatSwitchString() const
1008 switch (m_options.m_outputStyle)
1010 case DIFF_OUTPUT_NORMAL:
1013 case DIFF_OUTPUT_CONTEXT:
1014 switches = _T(" C");
1016 case DIFF_OUTPUT_UNIFIED:
1017 switches = _T(" U");
1020 case DIFF_OUTPUT_ED:
1021 switches = _T(" e");
1023 case DIFF_OUTPUT_FORWARD_ED:
1024 switches = _T(" f");
1026 case DIFF_OUTPUT_RCS:
1027 switches = _T(" n");
1029 case DIFF_OUTPUT_IFDEF:
1030 switches = _T(" D");
1032 case DIFF_OUTPUT_SDIFF:
1033 switches = _T(" y");
1038 if (m_options.m_contextLines > 0)
1040 TCHAR tmpNum[5] = {0};
1041 _itot_s(m_options.m_contextLines, tmpNum, 10);
1045 if (ignore_all_space_flag > 0)
1046 switches += _T("w");
1048 if (ignore_blank_lines_flag > 0)
1049 switches += _T("B");
1051 if (ignore_case_flag > 0)
1052 switches += _T("i");
1054 if (ignore_space_change_flag > 0)
1055 switches += _T("b");
1061 * @brief Enables/disables patch-file appending.
1062 * If the file for patch already exists then the patch will be appended to
1064 * @param [in] bAppendFiles If true patch will be appended to existing file.
1066 void CDiffWrapper::SetAppendFiles(bool bAppendFiles)
1068 m_bAppendFiles = bAppendFiles;
1072 * @brief Compare two files using diffutils.
1074 * Compare two files (in DiffFileData param) using diffutils. Run diffutils
1075 * inside SEH so we can trap possible error and exceptions. If error or
1076 * execption is trapped, return compare failure.
1077 * @param [out] diffs Pointer to list of change structs where diffdata is stored.
1078 * @param [in] diffData files to compare.
1079 * @param [out] bin_status used to return binary status from compare.
1080 * @param [out] bin_file Returns which file was binary file as bitmap.
1081 So if first file is binary, first bit is set etc. Can be NULL if binary file
1082 info is not needed (faster compare since diffutils don't bother checking
1083 second file if first is binary).
1084 * @return true when compare succeeds, false if error happened during compare.
1085 * @note This function is used in file compare, not folder compare. Similar
1086 * folder compare function is in DiffFileData.cpp.
1088 bool CDiffWrapper::Diff2Files(struct change ** diffs, DiffFileData *diffData,
1089 int * bin_status, int * bin_file) const
1095 // Diff files. depth is zero because we are not comparing dirs
1096 *diffs = diff_2_files (diffData->m_inf, 0, bin_status,
1097 (m_pMovedLines[0] != NULL), bin_file);
1098 CopyDiffutilTextStats(diffData->m_inf, diffData);
1100 catch (SE_Exception&)
1109 * @brief Free script (the diffutils linked list of differences)
1112 CDiffWrapper::FreeDiffUtilsScript(struct change * & script)
1114 if (!script) return;
1115 struct change *e=0, *p=0;
1116 // cleanup the script
1117 for (e = script; e; e = p)
1126 * @brief Match regular expression list against given difference.
1127 * This function matches the regular expression list against the difference
1128 * (given as start line and end line). Matching the diff requires that all
1129 * lines in difference match.
1130 * @param [in] StartPos First line of the difference.
1131 * @param [in] endPos Last line of the difference.
1132 * @param [in] FileNo File to match.
1133 * return true if any of the expressions matches.
1135 bool CDiffWrapper::RegExpFilter(int StartPos, int EndPos, int FileNo) const
1137 if (m_pFilterList == NULL)
1139 throw "CDiffWrapper::RegExpFilter() called when "
1140 "filterlist doesn't exist (=NULL)";
1143 bool linesMatch = true; // set to false when non-matching line is found.
1144 int line = StartPos;
1146 while (line <= EndPos && linesMatch)
1148 size_t len = files[FileNo].linbuf[line + 1] - files[FileNo].linbuf[line];
1149 const char *string = files[FileNo].linbuf[line];
1150 size_t stringlen = linelen(string, len);
1151 if (!m_pFilterList->Match(std::string(string, stringlen), m_codepage))
1162 * @brief Walk the diff utils change script, building the WinMerge list of diff blocks
1165 CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const file_data * inf)
1167 //Logic needed for Ignore comment option
1168 DIFFOPTIONS options;
1169 GetOptions(&options);
1170 String asLwrCaseExt;
1171 if (options.bFilterCommentsLines)
1173 String LowerCaseExt = m_originalFile.GetLeft();
1174 String::size_type PosOfDot = LowerCaseExt.rfind('.');
1175 if (PosOfDot != String::npos)
1177 LowerCaseExt.erase(0, PosOfDot + 1);
1178 std::transform(LowerCaseExt.begin(), LowerCaseExt.end(), LowerCaseExt.begin(), ::towlower);
1179 asLwrCaseExt = LowerCaseExt;
1183 struct change *next = script;
1187 /* Find a set of changes that belong together. */
1188 struct change *thisob = next;
1189 struct change *end = find_change(next);
1191 /* Disconnect them from the rest of the changes,
1192 making them a hunk, and remember the rest for next iteration. */
1196 debug_script(thisob);
1199 /* Print thisob hunk. */
1200 //(*printfun) (thisob);
1202 /* Determine range of line numbers involved in each file. */
1203 int first0=0, last0=0, first1=0, last1=0, deletes=0, inserts=0;
1204 analyze_hunk (thisob, &first0, &last0, &first1, &last1, &deletes, &inserts, inf);
1205 if (deletes || inserts || thisob->trivial)
1207 OP_TYPE op = OP_NONE;
1208 if (deletes && inserts)
1210 else if (deletes || inserts)
1215 /* Print the lines that the first file has. */
1216 int trans_a0=0, trans_b0=0, trans_a1=0, trans_b1=0;
1217 translate_range(&inf[0], first0, last0, &trans_a0, &trans_b0);
1218 translate_range(&inf[1], first1, last1, &trans_a1, &trans_b1);
1220 // Store information about these blocks in moved line info
1221 if (GetDetectMovedBlocks())
1223 if (thisob->match0>=0)
1225 assert(thisob->inserted > 0);
1226 for (int i=0; i<thisob->inserted; ++i)
1228 int line0 = i+thisob->match0 + (trans_a0-first0-1);
1229 int line1 = i+thisob->line1 + (trans_a1-first1-1);
1230 GetMovedLines(1)->Add(MovedLines::SIDE_LEFT, line1, line0);
1233 if (thisob->match1>=0)
1235 assert(thisob->deleted > 0);
1236 for (int i=0; i<thisob->deleted; ++i)
1238 int line0 = i+thisob->line0 + (trans_a0-first0-1);
1239 int line1 = i+thisob->match1 + (trans_a1-first1-1);
1240 GetMovedLines(0)->Add(MovedLines::SIDE_RIGHT, line0, line1);
1245 if (options.bFilterCommentsLines)
1247 int QtyLinesLeft = (trans_b0 - trans_a0) + 1; //Determine quantity of lines in this block for left side
1248 int QtyLinesRight = (trans_b1 - trans_a1) + 1;//Determine quantity of lines in this block for right side
1249 PostFilter(thisob->line0, QtyLinesLeft, thisob->line1, QtyLinesRight, op, asLwrCaseExt);
1252 if (m_pFilterList && m_pFilterList->HasRegExps())
1254 //Determine quantity of lines in this block for both sides
1255 int QtyLinesLeft = (trans_b0 - trans_a0);
1256 int QtyLinesRight = (trans_b1 - trans_a1);
1258 // Match lines against regular expression filters
1259 // Our strategy is that every line in both sides must
1260 // match regexp before we mark difference as ignored.
1261 bool match2 = false;
1262 bool match1 = RegExpFilter(thisob->line0, thisob->line0 + QtyLinesLeft, 0);
1264 match2 = RegExpFilter(thisob->line1, thisob->line1 + QtyLinesRight, 1);
1265 if (match1 && match2)
1269 AddDiffRange(m_pDiffList, trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, op);
1273 /* Reconnect the script so it will all be freed properly. */
1278 struct Comp02Functor
1280 Comp02Functor(const file_data * inf10, const file_data * inf12) :
1281 inf10_(inf10), inf12_(inf12)
1284 bool operator()(const DiffRangeInfo &dr3)
1286 int line0 = dr3.begin[0];
1287 int line2 = dr3.begin[2];
1288 int line0end = dr3.end[0];
1289 int line2end = dr3.end[2];
1290 if (line0end - line0 != line2end - line2)
1292 const char **linbuf0 = inf10_[1].linbuf + inf10_[1].linbuf_base;
1293 const char **linbuf2 = inf12_[1].linbuf + inf12_[1].linbuf_base;
1294 for (int i = 0; i < line0end - line0 + 1; ++i)
1296 const size_t line0len = linbuf0[line0 + i + 1] - linbuf0[line0 + i];
1297 const size_t line2len = linbuf2[line2 + i + 1] - linbuf2[line2 + i];
1298 if (line_cmp(linbuf0[line0 + i], line0len, linbuf2[line2 + i], line2len) != 0)
1303 const file_data *inf10_;
1304 const file_data *inf12_;
1308 * @brief Walk the diff utils change script, building the WinMerge list of diff blocks
1311 CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript3(
1312 struct change * script10,
1313 struct change * script12,
1314 const file_data * inf10,
1315 const file_data * inf12)
1317 DiffList diff10, diff12;
1321 for (int file = 0; file < 2; file++)
1323 struct change *next = nullptr;
1324 int trans_a0, trans_b0, trans_a1, trans_b1;
1325 int first0, last0, first1, last1, deletes, inserts;
1327 const file_data *pinf = nullptr;
1328 DiffList *pdiff = nullptr;
1332 case 0: next = script10; pdiff = &diff10; pinf = inf10; break;
1333 case 1: next = script12; pdiff = &diff12; pinf = inf12; break;
1338 /* Find a set of changes that belong together. */
1339 struct change *thisob = next;
1340 struct change *end = find_change(next);
1342 /* Disconnect them from the rest of the changes,
1343 making them a hunk, and remember the rest for next iteration. */
1347 debug_script(thisob);
1350 /* Print thisob hunk. */
1351 //(*printfun) (thisob);
1353 /* Determine range of line numbers involved in each file. */
1354 analyze_hunk (thisob, &first0, &last0, &first1, &last1, &deletes, &inserts, pinf);
1355 if (deletes || inserts || thisob->trivial)
1357 if (deletes && inserts)
1359 else if (deletes || inserts)
1364 /* Print the lines that the first file has. */
1365 translate_range (&pinf[0], first0, last0, &trans_a0, &trans_b0);
1366 translate_range (&pinf[1], first1, last1, &trans_a1, &trans_b1);
1368 // Store information about these blocks in moved line info
1369 if (GetDetectMovedBlocks())
1371 int index1 = 0; // defaults for (file == 0 /* diff10 */)
1373 MovedLines::ML_SIDE side1 = MovedLines::SIDE_RIGHT;
1374 MovedLines::ML_SIDE side2 = MovedLines::SIDE_LEFT;
1375 if (file == 1 /* diff12 */)
1379 side1 = MovedLines::SIDE_LEFT;
1380 side2 = MovedLines::SIDE_RIGHT;
1382 if (index1 != -1 && index2 != -1)
1384 if (thisob->match0>=0)
1386 assert(thisob->inserted > 0);
1387 for (int i=0; i<thisob->inserted; ++i)
1389 int line0 = i+thisob->match0 + (trans_a0-first0-1);
1390 int line1 = i+thisob->line1 + (trans_a1-first1-1);
1391 GetMovedLines(index1)->Add(side1, line1, line0);
1394 if (thisob->match1>=0)
1396 assert(thisob->deleted > 0);
1397 for (int i=0; i<thisob->deleted; ++i)
1399 int line0 = i+thisob->line0 + (trans_a0-first0-1);
1400 int line1 = i+thisob->match1 + (trans_a1-first1-1);
1401 GetMovedLines(index2)->Add(side2, line0, line1);
1407 AddDiffRange(pdiff, trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, op);
1411 /* Reconnect the script so it will all be freed properly. */
1416 Make3wayDiff(m_pDiffList->GetDiffRangeInfoVector(), diff10.GetDiffRangeInfoVector(), diff12.GetDiffRangeInfoVector(),
1417 Comp02Functor(inf10, inf12), (m_pFilterList && m_pFilterList->HasRegExps()));
1420 void CDiffWrapper::WritePatchFileHeader(enum output_style tOutput_style, bool bAppendFiles)
1423 if (!m_sPatchFile.empty())
1425 const TCHAR *mode = (bAppendFiles ? _T("a+") : _T("w+"));
1426 if (_tfopen_s(&outfile, m_sPatchFile.c_str(), mode) != 0)
1430 if (outfile == nullptr)
1432 m_status.bPatchFileFailed = true;
1437 switch (tOutput_style)
1440 case OUTPUT_CONTEXT:
1441 case OUTPUT_UNIFIED:
1444 case OUTPUT_FORWARD_ED:
1451 print_html_header();
1459 void CDiffWrapper::WritePatchFileTerminator(enum output_style tOutput_style)
1462 if (!m_sPatchFile.empty())
1464 if (_tfopen_s(&outfile, m_sPatchFile.c_str(), _T("a+")) != 0)
1468 if (outfile == nullptr)
1470 m_status.bPatchFileFailed = true;
1475 switch (tOutput_style)
1478 case OUTPUT_CONTEXT:
1479 case OUTPUT_UNIFIED:
1482 case OUTPUT_FORWARD_ED:
1489 print_html_terminator();
1498 * @brief Write out a patch file.
1499 * Writes patch file using already computed diffutils script. Converts path
1500 * delimiters from \ to / since we want to keep compatibility with patch-tools.
1501 * @param [in] script list of changes.
1502 * @param [in] inf file_data table containing filenames
1504 void CDiffWrapper::WritePatchFile(struct change * script, file_data * inf)
1506 file_data inf_patch[2] = {0};
1507 std::memcpy(&inf_patch, inf, sizeof(file_data) * 2);
1509 // Get paths, primarily use alternative paths, only if they are empty
1510 // use full filepaths
1511 String path1(m_alternativePaths[0]);
1512 String path2(m_alternativePaths[1]);
1517 path1 = paths::ToUnixPath(path1);
1518 path2 = paths::ToUnixPath(path2);
1519 inf_patch[0].name = _strdup(ucr::toSystemCP(path1).c_str());
1520 inf_patch[1].name = _strdup(ucr::toSystemCP(path2).c_str());
1522 // If paths in m_s1File and m_s2File point to original files, then we can use
1523 // them to fix potentially meaningless stats from potentially temporary files,
1524 // resulting from whatever transforms may have taken place.
1525 // If not, then we can't help it, and hence assert that this won't happen.
1526 if (!m_bPathsAreTemp)
1528 mywstat(m_files[0].c_str(), &inf_patch[0].stat);
1529 mywstat(m_files[1].c_str(), &inf_patch[1].stat);
1537 if (!m_sPatchFile.empty())
1539 const TCHAR *mode = (m_bAppendFiles ? _T("a+") : _T("w+"));
1540 if (_tfopen_s(&outfile, m_sPatchFile.c_str(), mode) != 0)
1544 if (outfile == nullptr)
1546 m_status.bPatchFileFailed = true;
1550 // Print "command line"
1551 if (m_bAddCmdLine && output_style != OUTPUT_HTML)
1553 String switches = FormatSwitchString();
1554 _ftprintf(outfile, _T("diff%s %s %s\n"),
1556 path1 == _T("NUL") ? _T("/dev/null") : path1.c_str(),
1557 path2 == _T("NUL") ? _T("/dev/null") : path2.c_str());
1560 if (strcmp(inf[0].name, "NUL") == 0)
1562 free((void *)inf[0].name);
1563 inf[0].name = _strdup("/dev/null");
1565 if (strcmp(inf[1].name, "NUL") == 0)
1567 free((void *)inf[1].name);
1568 inf[1].name = _strdup("/dev/null");
1572 switch (output_style)
1575 print_normal_script(script);
1577 case OUTPUT_CONTEXT:
1578 print_context_header(inf_patch, 0);
1579 print_context_script(script, 0);
1581 case OUTPUT_UNIFIED:
1582 print_context_header(inf_patch, 1);
1583 print_context_script(script, 1);
1587 print_ed_script(script);
1589 case OUTPUT_FORWARD_ED:
1590 pr_forward_ed_script(script);
1593 print_rcs_script(script);
1596 print_ifdef_script(script);
1599 print_sdiff_script(script);
1603 print_html_diff_header(inf_patch);
1604 print_html_script(script);
1605 print_html_diff_terminator();
1611 free((void *)inf_patch[0].name);
1612 free((void *)inf_patch[1].name);
1616 * @brief Set line filters, given as one string.
1617 * @param [in] filterStr Filters.
1619 void CDiffWrapper::SetFilterList(const String& filterStr)
1621 // Remove filterlist if new filter is empty
1622 if (filterStr.empty())
1624 m_pFilterList.reset();
1628 // Adding new filter without previous filter
1629 if (m_pFilterList == NULL)
1631 m_pFilterList.reset(new FilterList);
1634 m_pFilterList->RemoveAllFilters();
1636 std::string regexp_str = ucr::toUTF8(filterStr);
1638 // Add every "line" of regexps to regexp list
1639 StringTokenizer tokens(regexp_str, "\r\n");
1640 for (StringTokenizer::Iterator it = tokens.begin(); it != tokens.end(); ++it)
1641 m_pFilterList->AddRegExp(*it);
1645 * @brief Copy text stat results from diffutils back into the FileTextStats structure
1647 void CopyTextStats(const file_data * inf, FileTextStats * myTextStats)
1649 myTextStats->ncrlfs = inf->count_crlfs;
1650 myTextStats->ncrs = inf->count_crs;
1651 myTextStats->nlfs = inf->count_lfs;
1652 myTextStats->nzeros = inf->count_zeros;
1656 * @brief Copy both left & right text stats results back into the DiffFileData text stats
1658 void CopyDiffutilTextStats(file_data *inf, DiffFileData * diffData)
1660 CopyTextStats(&inf[0], &diffData->m_textStats[0]);
1661 CopyTextStats(&inf[1], &diffData->m_textStats[1]);