-/////////////////////////////////////////////////////////////////////////////
-// License (GPLv2+):
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or (at
-// your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-/////////////////////////////////////////////////////////////////////////////
+// SPDX-License-Identifier: GPL-2.0-or-later
/**
- * @file files.cpp
+ * @file PatchTool.cpp
*
* @brief Code file routines
*/
-// ID line follows -- this is updated by SVN
-// $Id: PatchTool.cpp 6858 2009-06-25 07:48:26Z kimmov $
#include "StdAfx.h"
#include "PatchTool.h"
#include "PathContext.h"
#include "PatchDlg.h"
#include "paths.h"
+#include "Merge.h"
+#include "DirTravel.h"
#ifdef _DEBUG
#define new DEBUG_NEW
-#undef THIS_FILE
-static char THIS_FILE[] = __FILE__;
#endif
/**
* @brief Default constructor.
*/
-CPatchTool::CPatchTool() : m_bOpenToEditor(FALSE)
+CPatchTool::CPatchTool() : m_bOpenToEditor(false)
{
}
*/
void CPatchTool::AddFiles(const String &file1, const String &file2)
{
- PATCHFILES files;
- files.lfile = file1;
- files.rfile = file2;
+ PATCHFILES tFiles;
+ tFiles.lfile = file1;
+ tFiles.rfile = file2;
// TODO: Read and add file's timestamps
- m_fileList.push_back(files);
+ m_fileList.push_back(tFiles);
}
/**
void CPatchTool::AddFiles(const String &file1, const String &altPath1,
const String &file2, const String &altPath2)
{
- PATCHFILES files;
- files.lfile = file1;
- files.rfile = file2;
- files.pathLeft = altPath1;
- files.pathRight = altPath2;
+ PATCHFILES tFiles;
+ tFiles.lfile = file1;
+ tFiles.rfile = file2;
+ tFiles.pathLeft = altPath1;
+ tFiles.pathRight = altPath2;
// TODO: Read and add file's timestamps
- m_fileList.push_back(files);
+ m_fileList.push_back(tFiles);
}
/**
int CPatchTool::CreatePatch()
{
DIFFSTATUS status;
- BOOL bResult = TRUE;
- BOOL bDiffSuccess;
int retVal = 0;
CPatchDlg dlgPatch;
if (ShowDialog(&dlgPatch))
{
- String path;
- paths_SplitFilename((const TCHAR *)dlgPatch.m_fileResult, &path, NULL, NULL);
- if (!paths_CreateIfNeeded(path))
+ bool bResult = true;
+
+ if (!paths::CreateIfNeeded(paths::GetPathOnly(dlgPatch.m_fileResult)))
{
LangMessageBox(IDS_FOLDER_NOTEXIST, MB_OK | MB_ICONSTOP);
return 0;
}
// Select patch create -mode
- m_diffWrapper.SetCreatePatchFile((LPCTSTR)dlgPatch.m_fileResult);
- m_diffWrapper.SetAppendFiles(!!dlgPatch.m_appendFile);
- m_diffWrapper.SetPrediffer(NULL);
+ m_diffWrapper.SetCreatePatchFile(dlgPatch.m_fileResult);
+ m_diffWrapper.SetAppendFiles(dlgPatch.m_appendFile);
+ m_diffWrapper.SetPrediffer(nullptr);
- int fileCount = dlgPatch.GetItemCount();
+ size_t fileCount = dlgPatch.GetItemCount();
- m_diffWrapper.WritePatchFileHeader(dlgPatch.m_outputStyle, !!dlgPatch.m_appendFile);
- m_diffWrapper.SetAppendFiles(TRUE);
+ std::vector<PATCHFILES> fileList;
+ for (size_t index = 0; index < fileCount; index++)
+ {
+ const PATCHFILES& tFiles = dlgPatch.GetItemAt(index);
+ if (paths::DoesPathExist(tFiles.lfile) == paths::IS_EXISTING_DIR && paths::DoesPathExist(tFiles.rfile) == paths::IS_EXISTING_DIR)
+ {
+ // Walk given folders recursively and adds found files into patch list
+ String lfile = tFiles.lfile;
+ String rfile = tFiles.rfile;
+ paths::normalize(lfile);
+ paths::normalize(rfile);
+ PathContext paths((tFiles.pathLeft.empty() ? _T("") : paths::GetParentPath(lfile)),
+ (tFiles.pathRight.empty() ? _T("") : paths::GetParentPath(rfile)));
+ String subdir[2] = { (tFiles.pathLeft.empty() ? lfile : tFiles.pathLeft),
+ (tFiles.pathRight.empty() ? rfile : tFiles.pathRight) };
+ GetItemsForPatchList(paths, subdir, &fileList);
+ }
+ else {
+ fileList.push_back(tFiles);
+ }
+ }
+ fileCount = fileList.size();
+
+ m_diffWrapper.WritePatchFileHeader(dlgPatch.m_outputStyle, dlgPatch.m_appendFile);
+ m_diffWrapper.SetAppendFiles(true);
- for (int index = 0; index < fileCount; index++)
+ bool bShowedBinaryMessage = false;
+ int writeFileCount = 0;
+
+ for (size_t index = 0; index < fileCount; index++)
{
- const PATCHFILES& files = dlgPatch.GetItemAt(index);
- String filename1 = files.lfile.length() == 0 ? _T("NUL") : files.lfile;
- String filename2 = files.rfile.length() == 0 ? _T("NUL") : files.rfile;
+ const PATCHFILES& tFiles = fileList[index];
+ String filename1 = tFiles.lfile.length() == 0 ? _T("NUL") : tFiles.lfile;
+ String filename2 = tFiles.rfile.length() == 0 ? _T("NUL") : tFiles.rfile;
// Set up DiffWrapper
- m_diffWrapper.SetPaths(PathContext(filename1, filename2), FALSE);
- m_diffWrapper.SetAlternativePaths(PathContext(files.pathLeft, files.pathRight));
- m_diffWrapper.SetCompareFiles(PathContext(files.lfile, files.rfile));
- bDiffSuccess = m_diffWrapper.RunFileDiff();
+ m_diffWrapper.SetPaths(PathContext(filename1, filename2), false);
+ m_diffWrapper.SetAlternativePaths(PathContext(tFiles.pathLeft, tFiles.pathRight));
+ m_diffWrapper.SetCompareFiles(PathContext(tFiles.lfile, tFiles.rfile));
+ bool bDiffSuccess = m_diffWrapper.RunFileDiff();
m_diffWrapper.GetDiffStatus(&status);
if (!bDiffSuccess)
{
LangMessageBox(IDS_FILEERROR, MB_ICONSTOP);
- bResult = FALSE;
+ bResult = false;
break;
}
else if (status.bBinaries)
{
- LangMessageBox(IDS_CANNOT_CREATE_BINARYPATCH, MB_ICONSTOP);
- bResult = FALSE;
- break;
+ if (!bShowedBinaryMessage)
+ {
+ LangMessageBox(IDS_CANNOT_CREATE_BINARYPATCH, MB_ICONWARNING);
+ bShowedBinaryMessage = true;
+ }
}
else if (status.bPatchFileFailed)
{
- String errMsg = LangFormatString1(IDS_FILEWRITE_ERROR, dlgPatch.m_fileResult);
+ String errMsg = strutils::format_string1(_("Could not write to file %1."), dlgPatch.m_fileResult);
AfxMessageBox(errMsg.c_str(), MB_ICONSTOP);
- bResult = FALSE;
+ bResult = false;
break;
}
+ else
+ {
+ writeFileCount++;
+ }
+
}
m_diffWrapper.WritePatchFileTerminator(dlgPatch.m_outputStyle);
- if (bResult && fileCount > 0)
+ if (bResult && writeFileCount > 0)
{
LangMessageBox(IDS_DIFF_SUCCEEDED, MB_ICONINFORMATION|MB_DONT_DISPLAY_AGAIN,
IDS_DIFF_SUCCEEDED);
- m_sPatchFile = (LPCTSTR)dlgPatch.m_fileResult;
+ m_sPatchFile = dlgPatch.m_fileResult;
m_bOpenToEditor = dlgPatch.m_openToEditor;
retVal = 1;
}
}
dlgPatch.ClearItems();
+ if (retVal)
+ {
+ if (m_bOpenToEditor)
+ theApp.OpenFileToExternalEditor(m_sPatchFile);
+ }
return retVal;
}
/**
* @brief Show patch options dialog and check options selected.
- * @return TRUE if user wants to create a patch (didn't cancel dialog).
+ * @return `true` if user wants to create a patch (didn't cancel dialog).
*/
-BOOL CPatchTool::ShowDialog(CPatchDlg *pDlgPatch)
+bool CPatchTool::ShowDialog(CPatchDlg *pDlgPatch)
{
DIFFOPTIONS diffOptions = {0};
PATCHOPTIONS patchOptions;
- BOOL bRetVal = TRUE;
+ bool bRetVal = true;
if (pDlgPatch->DoModal() == IDOK)
{
// There must be one filepair
if (pDlgPatch->GetItemCount() < 1)
- bRetVal = FALSE;
+ bRetVal = false;
// These two are from dropdown list - can't be wrong
patchOptions.outputStyle = pDlgPatch->m_outputStyle;
patchOptions.nContext = pDlgPatch->m_contextLines;
// Checkbox - can't be wrong
- patchOptions.bAddCommandline = !!pDlgPatch->m_includeCmdLine;
+ patchOptions.bAddCommandline = pDlgPatch->m_includeCmdLine;
m_diffWrapper.SetPatchOptions(&patchOptions);
// These are from checkboxes and radiobuttons - can't be wrong
diffOptions.nIgnoreWhitespace = pDlgPatch->m_whitespaceCompare;
- diffOptions.bIgnoreBlankLines = !!pDlgPatch->m_ignoreBlanks;
- m_diffWrapper.SetAppendFiles(!!pDlgPatch->m_appendFile);
+ diffOptions.bIgnoreBlankLines = pDlgPatch->m_ignoreBlanks;
+ m_diffWrapper.SetAppendFiles(pDlgPatch->m_appendFile);
// Use this because non-sensitive setting can't write
// patch file EOLs correctly
- diffOptions.bIgnoreEol = !!pDlgPatch->m_ignoreEOLDifference;
+ diffOptions.bIgnoreEol = pDlgPatch->m_ignoreEOLDifference;
- diffOptions.bIgnoreCase = pDlgPatch->m_caseSensitive == FALSE;
+ diffOptions.bIgnoreCase = pDlgPatch->m_ignoreCase;
+ diffOptions.nDiffAlgorithm = pDlgPatch->m_diffAlgorithm;
+ diffOptions.bIndentHeuristic = pDlgPatch->m_indentHeuristic;
m_diffWrapper.SetOptions(&diffOptions);
}
else
- return FALSE;
+ return false;
return bRetVal;
}
-/**
- * @brief Returns filename and path for patch-file
+/**
+ * @brief Add one compare item to patch list.
+ * @param [in] sDir1 Left subdirectory.
+ * @param [in] sDir2 Right subdirectory.
+ * @param [in] ent1 Left item data to add.
+ * @param [in] ent2 Right item data to add.
+ * @param [out] fileList Patch files list.
*/
-String CPatchTool::GetPatchFile() const
+void CPatchTool::AddFilesToList(const String& sDir1, const String& sDir2, const DirItem* ent1, const DirItem* ent2, std::vector<PATCHFILES>* fileList)
{
- return m_sPatchFile;
+ if ((ent1 == nullptr && ent2 == nullptr) || fileList == nullptr)
+ return;
+
+ static const TCHAR backslash[] = _T("\\");
+
+ PATCHFILES tFiles;
+
+ if (ent1 != nullptr)
+ {
+ tFiles.lfile = ent1->path.get() + backslash + ent1->filename.get();
+
+ String pathLeft = _T("");
+ if (!sDir1.empty())
+ pathLeft = sDir1 + backslash;
+ pathLeft += ent1->filename.get();
+ tFiles.pathLeft = pathLeft;
+ }
+
+ if (ent2 != nullptr)
+ {
+ tFiles.rfile = ent2->path.get() + backslash + ent2->filename.get();
+
+ String pathRight = _T("");
+ if (!sDir2.empty())
+ pathRight = sDir2 + backslash;
+ pathRight += ent2->filename.get();
+
+ tFiles.pathRight = pathRight;
+ }
+
+ fileList->push_back(tFiles);
}
-/**
- * @brief Returns TRUE if user wants to open patch file
- * to external editor (specified in WinMerge options).
+/**
+ * @brief This function walks given folders and adds found files into patch list.
+ * We walk all subfolders and add the files they contain into list.
+ *
+ * @param [in] paths Root paths of compare
+ * @param [in] subdir Subdirectories under root path
+ * @param [out] fileList Patch files list
+ * @return 1 normally, 0 if no directories and files exist.
+ * @remark This function was written based on DirScan_GetItems() in DirScan.cpp
*/
-BOOL CPatchTool::GetOpenToEditor() const
+int CPatchTool::GetItemsForPatchList(const PathContext& paths, const String subdir[], std::vector<PATCHFILES>* fileList)
{
- return m_bOpenToEditor;
+ static const TCHAR backslash[] = _T("\\");
+ int nDirs = paths.GetSize();
+
+ String sDir[2];
+ String subprefix[2];
+
+ std::copy(paths.begin(), paths.end(), sDir);
+
+ if (!subdir[0].empty())
+ {
+ for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
+ {
+ sDir[nIndex] = paths::ConcatPath(sDir[nIndex], subdir[nIndex]);
+ subprefix[nIndex] = subdir[nIndex] + backslash;
+ }
+ }
+
+ DirItemArray dirs[2], aFiles[2];
+ for (int nIndex = 0; nIndex < nDirs; nIndex++)
+ LoadAndSortFiles(sDir[nIndex], &dirs[nIndex], &aFiles[nIndex], false);
+
+ {
+ int nIndex;
+ for (nIndex = 0; nIndex < nDirs; nIndex++)
+ if (dirs[nIndex].size() != 0 || aFiles[nIndex].size() != 0) break;
+ if (nIndex == nDirs)
+ return 0;
+ }
+
+ // Handle directories
+ // i points to current directory in left list (dirs[0])
+ // j points to current directory in right list (dirs[1])
+ DirItemArray::size_type i = 0, j = 0;
+ while (true)
+ {
+ if (i >= dirs[0].size() && j >= dirs[1].size())
+ break;
+
+ unsigned nDiffCode = DIFFCODE::DIR;
+ // Comparing directories leftDirs[i].name to rightDirs[j].name
+ if (i < dirs[0].size() && (j == dirs[1].size() || collstr(dirs[0][i].filename, dirs[1][j].filename, false) < 0))
+ {
+ nDiffCode |= DIFFCODE::FIRST;
+ }
+ else if (j < dirs[1].size() && (i == dirs[0].size() || collstr(dirs[1][j].filename, dirs[0][i].filename, false) < 0))
+ {
+ nDiffCode |= DIFFCODE::SECOND;
+ }
+ else
+ {
+ nDiffCode |= DIFFCODE::BOTH;
+ }
+
+ String leftnewsub = (nDiffCode & DIFFCODE::FIRST) ? subprefix[0] + dirs[0][i].filename.get() : subprefix[0] + dirs[1][j].filename.get();
+ String rightnewsub = (nDiffCode & DIFFCODE::SECOND) ? subprefix[1] + dirs[1][j].filename.get() : subprefix[1] + dirs[0][i].filename.get();
+
+ // Scan recursively all subdirectories too
+ String newsubdir[2] = { leftnewsub, rightnewsub };
+ GetItemsForPatchList(paths, newsubdir, fileList);
+
+ if (nDiffCode & DIFFCODE::FIRST)
+ i++;
+ if (nDiffCode & DIFFCODE::SECOND)
+ j++;
+ }
+
+ // Handle files
+ // i points to current file in left list (aFiles[0])
+ // j points to current file in right list (aFiles[1])
+ i = 0, j = 0;
+ while (true)
+ {
+ // Comparing file aFiles[0][i].name to aFiles[1][j].name
+ if (i < aFiles[0].size() && (j == aFiles[1].size() || collstr(aFiles[0][i].filename, aFiles[1][j].filename, false) < 0))
+ {
+ AddFilesToList(subdir[0], subdir[1], &aFiles[0][i], nullptr, fileList);
+ ++i;
+ continue;
+ }
+ if (j < aFiles[1].size() && (i == aFiles[0].size() || collstr(aFiles[0][i].filename, aFiles[1][j].filename, false) > 0))
+ {
+ AddFilesToList(subdir[0], subdir[1], nullptr, &aFiles[1][j], fileList);
+ ++j;
+ continue;
+ }
+ if (i < aFiles[0].size())
+ {
+ assert(j < aFiles[1].size());
+
+ AddFilesToList(subdir[0], subdir[1], &aFiles[0][i], &aFiles[1][j], fileList);
+ ++i;
+ ++j;
+ continue;
+ }
+ break;
+ }
+
+ return 1;
}