OSDN Git Service

autoit.cpp - Macros >> User 1 ..... Variable >> User 2 (#749) (2)
[winmerge-jp/winmerge-jp.git] / Src / PatchTool.cpp
index 779da95..ddfa1a7 100644 (file)
@@ -1,26 +1,9 @@
-/////////////////////////////////////////////////////////////////////////////
-//    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
 
 /**
@@ -57,12 +40,12 @@ CPatchTool::~CPatchTool()
  */
 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);
 }
 
 /**
@@ -79,14 +62,14 @@ void CPatchTool::AddFiles(const String &file1, const String &file2)
 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);
 }
 
 /** 
@@ -97,8 +80,6 @@ void CPatchTool::AddFiles(const String &file1, const String &altPath1,
 int CPatchTool::CreatePatch()
 {
        DIFFSTATUS status;
-       bool bResult = true;
-       bool bDiffSuccess;
        int retVal = 0;
 
        CPatchDlg dlgPatch;
@@ -111,7 +92,9 @@ int CPatchTool::CreatePatch()
 
        if (ShowDialog(&dlgPatch))
        {
-               if (!paths_CreateIfNeeded(paths_GetPathOnly(dlgPatch.m_fileResult)))
+               bool bResult = true;
+
+               if (!paths::CreateIfNeeded(paths::GetPathOnly(dlgPatch.m_fileResult)))
                {
                        LangMessageBox(IDS_FOLDER_NOTEXIST, MB_OK | MB_ICONSTOP);
                        return 0;
@@ -120,24 +103,50 @@ int CPatchTool::CreatePatch()
                // Select patch create -mode
                m_diffWrapper.SetCreatePatchFile(dlgPatch.m_fileResult);
                m_diffWrapper.SetAppendFiles(dlgPatch.m_appendFile);
-               m_diffWrapper.SetPrediffer(NULL);
+               m_diffWrapper.SetPrediffer(nullptr);
 
                size_t fileCount = dlgPatch.GetItemCount();
 
+               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);
 
+               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.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)
@@ -148,22 +157,29 @@ int CPatchTool::CreatePatch()
                        }
                        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 = string_format_string1(_("Could not write to file %1."), 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;
                                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);
@@ -174,12 +190,17 @@ int CPatchTool::CreatePatch()
                }
        }
        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)
 {
@@ -198,7 +219,7 @@ bool CPatchTool::ShowDialog(CPatchDlg *pDlgPatch)
                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
@@ -210,7 +231,9 @@ bool CPatchTool::ShowDialog(CPatchDlg *pDlgPatch)
                // patch file EOLs correctly
                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
@@ -219,19 +242,157 @@ bool CPatchTool::ShowDialog(CPatchDlg *pDlgPatch)
        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;
 }