/** * @brief Runs diff-engine. */ BOOL CDiffWrapper::RunFileDiff() { String filepath1(m_s1File); String filepath2(m_s2File); replace_char(&*filepath1.begin(), '/', '\\'); replace_char(&*filepath2.begin(), '/', '\\'); BOOL bRet = TRUE; String strFile1Temp(filepath1); String strFile2Temp(filepath2); m_options.SetToDiffUtils(); if (m_bUseDiffList) m_nDiffs = m_pDiffList->GetSize(); if (m_bPluginsEnabled) { // Do the preprocessing now, overwrite the temp files // NOTE: FileTransform_UCS2ToUTF8() may create new temp // files and return new names, those created temp files // are deleted in end of function. if (m_infoPrediffer->bToBeScanned) { // this can only fail if the data can not be saved back (no more // place on disk ???) What to do then ?? FileTransform_Prediffing(strFile1Temp, m_sToFindPrediffer.c_str(), m_infoPrediffer, m_bPathsAreTemp); } else { // This can fail if the prediffer has a problem if (FileTransform_Prediffing(strFile1Temp, *m_infoPrediffer, m_bPathsAreTemp) == FALSE) { // display a message box CString sError; LangFormatString2(sError, IDS_PREDIFFER_ERROR, strFile1Temp.c_str(), m_infoPrediffer->pluginName.c_str()); AfxMessageBox(sError, MB_OK | MB_ICONSTOP); // don't use any more this prediffer m_infoPrediffer->bToBeScanned = FALSE; m_infoPrediffer->pluginName.erase(); } } // We use the same plugin for both files, so it must be defined before // second file ASSERT(m_infoPrediffer->bToBeScanned == FALSE); if (FileTransform_Prediffing(strFile2Temp, *m_infoPrediffer, m_bPathsAreTemp) == FALSE) { // display a message box CString sError; LangFormatString2(sError, IDS_PREDIFFER_ERROR, strFile2Temp.c_str(), m_infoPrediffer->pluginName.c_str()); AfxMessageBox(sError, MB_OK | MB_ICONSTOP); // don't use any more this prediffer m_infoPrediffer->bToBeScanned = FALSE; m_infoPrediffer->pluginName.erase(); } } // Comparing UTF-8 files seems to require this conversion? // I'm still very confused about why, as what the functions // document doing is UCS2 to UTF-8 conversion, nothing else. // Something is wrong here. - Kimmo FileTransform_UCS2ToUTF8(strFile1Temp, m_bPathsAreTemp); FileTransform_UCS2ToUTF8(strFile2Temp, m_bPathsAreTemp); DiffFileData diffdata; diffdata.SetDisplayFilepaths(filepath1.c_str(), filepath2.c_str()); // store true names for diff utils patch file // This opens & fstats both files (if it succeeds) if (!diffdata.OpenFiles(strFile1Temp.c_str(), strFile2Temp.c_str())) { return FALSE; } // Compare the files, if no error was found. // Last param (bin_file) is NULL since we don't // (yet) need info about binary sides. int bin_flag = 0; struct change *script = NULL; bRet = Diff2Files(&script, &diffdata, &bin_flag, NULL); // We don't anymore create diff-files for every rescan. // User can create patch-file whenever one wants to. // We don't need to waste time. But lets keep this as // debugging aid. Sometimes it is very useful to see // what differences diff-engine sees! #ifdef _DEBUG // throw the diff into a temp file String sTempPath = env_GetTempPath(); // get path to Temp folder String path = paths_ConcatPath(sTempPath, _T("Diff.txt")); outfile = _tfopen(path.c_str(), _T("w+")); if (outfile != NULL) { print_normal_script(script); fclose(outfile); outfile = NULL; } #endif // First determine what happened during comparison // If there were errors or files were binaries, don't bother // creating diff-lists or patches // diff_2_files set bin_flag to -1 if different binary // diff_2_files set bin_flag to +1 if same binary if (bin_flag != 0) { m_status.bBinaries = TRUE; if (bin_flag == -1) m_status.bIdentical = FALSE; else m_status.bIdentical = TRUE; } else { // text files according to diffutils, so change script exists m_status.bIdentical = (script == 0); m_status.bBinaries = FALSE; } file_data * inf = diffdata.m_inf; m_status.bLeftMissingNL = inf[0].missing_newline; m_status.bRightMissingNL = inf[1].missing_newline; // Create patch file if (!m_status.bBinaries && m_bCreatePatchFile) { WritePatchFile(script, &inf[0]); } // Go through diffs adding them to WinMerge's diff list // This is done on every WinMerge's doc rescan! if (!m_status.bBinaries && m_bUseDiffList) { LoadWinMergeDiffsFromDiffUtilsScript(script, diffdata.m_inf); } FreeDiffUtilsScript(script); // Done with diffutils filedata diffdata.Close(); if (m_bPluginsEnabled) { // Delete temp files transformation functions possibly created if (lstrcmpi(filepath1.c_str(), strFile1Temp.c_str()) != 0) { if (!::DeleteFile(strFile1Temp.c_str())) { LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"), strFile1Temp.c_str(), GetSysError(GetLastError()).c_str())); } strFile1Temp.erase(); } if (lstrcmpi(filepath2.c_str(), strFile2Temp.c_str()) != 0) { if (!::DeleteFile(strFile2Temp.c_str())) { LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"), strFile2Temp.c_str(), GetSysError(GetLastError()).c_str())); } strFile2Temp.erase(); } } return bRet; }
/** * @brief Compare two files (as earlier specified). * @return DIFFCODE as a result of compare. */ int DiffUtils::diffutils_compare_files() { int bin_flag = 0; int bin_file = 0; // bitmap for binary files // Do the actual comparison (generating a change script) struct change *script = NULL; bool success = Diff2Files(&script, 0, &bin_flag, false, &bin_file); if (!success) { return DIFFCODE::FILE | DIFFCODE::TEXT | DIFFCODE::CMPERR; } UINT code = DIFFCODE::FILE | DIFFCODE::TEXT | DIFFCODE::SAME; // make sure to start counting diffs at 0 // (usually it is -1 at this point, for unknown) m_ndiffs = 0; m_ntrivialdiffs = 0; if (script) { struct change *next = script; struct change *thisob = 0, *end = 0; String asLwrCaseExt; String LowerCaseExt = CA2T(m_inf[0].name); int PosOfDot = LowerCaseExt.rfind('.'); if (PosOfDot != -1) { LowerCaseExt.erase(0, PosOfDot + 1); CharLower(&*LowerCaseExt.begin()); asLwrCaseExt = LowerCaseExt; } while (next) { /* Find a set of changes that belong together. */ thisob = next; end = find_change(next); /* Disconnect them from the rest of the changes, making them a hunk, and remember the rest for next iteration. */ next = end->link; end->link = 0; #ifdef _DEBUG debug_script(thisob); #endif { /* Determine range of line numbers involved in each file. */ int first0 = 0, last0 = 0, first1 = 0, last1 = 0, deletes = 0, inserts = 0; analyze_hunk(thisob, &first0, &last0, &first1, &last1, &deletes, &inserts); if (deletes || inserts || thisob->trivial) { /* Print the lines that the first file has. */ int trans_a0 = 0, trans_b0 = 0, trans_a1 = 0, trans_b1 = 0; translate_range(&m_inf[0], first0, last0, &trans_a0, &trans_b0); translate_range(&m_inf[1], first1, last1, &trans_a1, &trans_b1); //Determine quantity of lines in this block for both sides int QtyLinesLeft = (trans_b0 - trans_a0); int QtyLinesRight = (trans_b1 - trans_a1); if(m_pOptions->m_filterCommentsLines || m_pOptions->m_bIgnoreBlankLines || m_pOptions->m_bIgnoreCase) { OP_TYPE op = OP_NONE; if (!deletes && !inserts) op = OP_TRIVIAL; else op = OP_DIFF; DIFFOPTIONS options = {0}; options.nIgnoreWhitespace = m_pOptions->m_ignoreWhitespace; options.bIgnoreBlankLines = m_pOptions->m_bIgnoreBlankLines; options.bFilterCommentsLines = m_pOptions->m_filterCommentsLines; options.bIgnoreCase = m_pOptions->m_bIgnoreCase; options.bIgnoreEol = m_pOptions->m_bIgnoreEOLDifference; m_pDiffWrapper->SetOptions(&options); m_pDiffWrapper->PostFilter(thisob->line0, QtyLinesLeft+1, thisob->line1, QtyLinesRight+1, op, *m_FilterCommentsManager, asLwrCaseExt.c_str()); if(op == OP_TRIVIAL) { thisob->trivial = 1; } } // Match lines against regular expression filters // Our strategy is that every line in both sides must // match regexp before we mark difference as ignored. if(m_pFilterList && m_pFilterList->HasRegExps()) { bool match2 = false; bool match1 = RegExpFilter(thisob->line0, thisob->line0 + QtyLinesLeft, 0); if (match1) match2 = RegExpFilter(thisob->line1, thisob->line1 + QtyLinesRight, 1); if (match1 && match2) thisob->trivial = 1; } } /* Reconnect the script so it will all be freed properly. */ end->link = next; } } } // Free change script (which we don't want) if (script != NULL) { struct change *p, *e; for (e = script; e; e = p) { if (!e->trivial) ++m_ndiffs; else ++m_ntrivialdiffs; p = e->link; free(e); } if (m_ndiffs > 0) code = code & ~DIFFCODE::SAME | DIFFCODE::DIFF; } // diff_2_files set bin_flag to -1 if different binary // diff_2_files set bin_flag to +1 if same binary if (bin_flag != 0) { // Clear text-flag, set binary flag // We don't know diff counts for binary files code = code & ~DIFFCODE::TEXT; switch (bin_file) { case BINFILE_SIDE1: code |= DIFFCODE::BINSIDE1; break; case BINFILE_SIDE2: code |= DIFFCODE::BINSIDE2; break; case BINFILE_SIDE1 | BINFILE_SIDE2: code |= DIFFCODE::BIN; break; default: _RPTF1(_CRT_ERROR, "Invalid bin_file value: %d", bin_file); break; } m_ndiffs = CDiffContext::DIFFS_UNKNOWN; } if (bin_flag < 0) { // Clear same-flag, set diff-flag code = code & ~DIFFCODE::SAME | DIFFCODE::DIFF; } return code; }