예제 #1
0
/**
 * @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;
}
예제 #2
0
/**
 * @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;
}