OSDN Git Service

Fix GitHub issue #252: Crash when generating patch for multiple files and a file...
[winmerge-jp/winmerge-jp.git] / Src / PatchTool.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    License (GPLv2+):
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.
7 //    
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.
12 //
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 /////////////////////////////////////////////////////////////////////////////
17 /** 
18  * @file  PatchTool.cpp
19  *
20  * @brief Code file routines
21  */
22
23 #include "StdAfx.h"
24 #include "PatchTool.h"
25 #include "UnicodeString.h"
26 #include "DiffWrapper.h"
27 #include "PathContext.h"
28 #include "PatchDlg.h"
29 #include "TFile.h"
30 #include "TempFile.h"
31 #include "paths.h"
32 #include "Merge.h"
33
34 #ifdef _DEBUG
35 #define new DEBUG_NEW
36 #endif
37
38 /**
39  * @brief Default constructor.
40  */
41 CPatchTool::CPatchTool() : m_bOpenToEditor(false)
42 {
43 }
44
45 /**
46  * @brief Default destructor.
47  */
48 CPatchTool::~CPatchTool()
49 {
50 }
51
52 /** 
53  * @brief Adds files to list for patching.
54  * @param [in] file1 First file to add.
55  * @param [in] file2 Second file to add.
56  */
57 void CPatchTool::AddFiles(const String &file1, const String &file2)
58 {
59         PATCHFILES tFiles;
60         tFiles.lfile = file1;
61         tFiles.rfile = file2;
62
63         // TODO: Read and add file's timestamps
64         m_fileList.push_back(tFiles);
65 }
66
67 /**
68  * @brief Add files with alternative paths.
69  * This function adds files with alternative paths. Alternative path is the
70  * one that is added to the patch file. So while @p file1 and @p file2 are
71  * paths in disk (can be temp file names), @p altPath1 and @p altPath2 are
72  * "visible " paths printed to the patch file.
73  * @param [in] file1 First path in disk.
74  * @param [in] altPath1 First path as printed to the patch file.
75  * @param [in] file2 Second path in disk.
76  * @param [in] altPath2 Second path as printed to the patch file.
77  */
78 void CPatchTool::AddFiles(const String &file1, const String &altPath1,
79                 const String &file2, const String &altPath2)
80 {
81         PATCHFILES tFiles;
82         tFiles.lfile = file1;
83         tFiles.rfile = file2;
84         tFiles.pathLeft = altPath1;
85         tFiles.pathRight = altPath2;
86
87         // TODO: Read and add file's timestamps
88         m_fileList.push_back(tFiles);
89 }
90
91 /** 
92  * @brief Create a patch from files given.
93  * @note Files can be given using AddFiles() or selecting using
94  * CPatchDlg.
95  */
96 int CPatchTool::CreatePatch()
97 {
98         DIFFSTATUS status;
99         int retVal = 0;
100
101         CPatchDlg dlgPatch;
102
103         // If files already inserted, add them to dialog
104     for(std::vector<PATCHFILES>::iterator iter = m_fileList.begin(); iter != m_fileList.end(); ++iter)
105     {
106         dlgPatch.AddItem(*iter);
107         }
108
109         if (ShowDialog(&dlgPatch))
110         {
111                 bool bResult = true;
112
113                 if (!paths::CreateIfNeeded(paths::GetPathOnly(dlgPatch.m_fileResult)))
114                 {
115                         LangMessageBox(IDS_FOLDER_NOTEXIST, MB_OK | MB_ICONSTOP);
116                         return 0;
117                 }
118
119                 // Select patch create -mode
120                 m_diffWrapper.SetCreatePatchFile(dlgPatch.m_fileResult);
121                 m_diffWrapper.SetAppendFiles(dlgPatch.m_appendFile);
122                 m_diffWrapper.SetPrediffer(nullptr);
123
124                 size_t fileCount = dlgPatch.GetItemCount();
125
126                 m_diffWrapper.WritePatchFileHeader(dlgPatch.m_outputStyle, dlgPatch.m_appendFile);
127                 m_diffWrapper.SetAppendFiles(true);
128
129                 TempFile emptyFile;
130                 emptyFile.Create();
131                 TFile file(emptyFile.GetPath());
132                 file.setLastModified(Poco::Timestamp::fromEpochTime(0));
133
134                 for (size_t index = 0; index < fileCount; index++)
135                 {
136                         const PATCHFILES& tFiles = dlgPatch.GetItemAt(index);
137                         String filename1 = tFiles.lfile.length() == 0 ? emptyFile.GetPath() : tFiles.lfile;
138                         String filename2 = tFiles.rfile.length() == 0 ? emptyFile.GetPath() : tFiles.rfile;
139                         
140                         // Set up DiffWrapper
141                         m_diffWrapper.SetPaths(PathContext(filename1, filename2), false);
142                         m_diffWrapper.SetAlternativePaths(PathContext(tFiles.pathLeft, tFiles.pathRight));
143                         m_diffWrapper.SetCompareFiles(PathContext(tFiles.lfile, tFiles.rfile));
144                         bool bDiffSuccess = m_diffWrapper.RunFileDiff();
145                         m_diffWrapper.GetDiffStatus(&status);
146
147                         if (!bDiffSuccess)
148                         {
149                                 LangMessageBox(IDS_FILEERROR, MB_ICONSTOP);
150                                 bResult = false;
151                                 break;
152                         }
153                         else if (status.bBinaries)
154                         {
155                                 LangMessageBox(IDS_CANNOT_CREATE_BINARYPATCH, MB_ICONSTOP);
156                                 bResult = false;
157                                 break;
158                         }
159                         else if (status.bPatchFileFailed)
160                         {
161                                 String errMsg = strutils::format_string1(_("Could not write to file %1."), dlgPatch.m_fileResult);
162                                 AfxMessageBox(errMsg.c_str(), MB_ICONSTOP);
163                                 bResult = false;
164                                 break;
165                         }
166                 }
167                 
168                 m_diffWrapper.WritePatchFileTerminator(dlgPatch.m_outputStyle);
169
170                 if (bResult && fileCount > 0)
171                 {
172                         LangMessageBox(IDS_DIFF_SUCCEEDED, MB_ICONINFORMATION|MB_DONT_DISPLAY_AGAIN,
173                                             IDS_DIFF_SUCCEEDED);
174                         
175                         m_sPatchFile = dlgPatch.m_fileResult;
176                         m_bOpenToEditor = dlgPatch.m_openToEditor;
177                         retVal = 1;
178                 }
179         }
180         dlgPatch.ClearItems();
181         if (retVal)
182         {
183                 if (m_bOpenToEditor)
184                         theApp.OpenFileToExternalEditor(m_sPatchFile);
185         }
186         return retVal;
187 }
188
189 /** 
190  * @brief Show patch options dialog and check options selected.
191  * @return `true` if user wants to create a patch (didn't cancel dialog).
192  */
193 bool CPatchTool::ShowDialog(CPatchDlg *pDlgPatch)
194 {
195         DIFFOPTIONS diffOptions = {0};
196         PATCHOPTIONS patchOptions;
197         bool bRetVal = true;
198
199         if (pDlgPatch->DoModal() == IDOK)
200         {
201                 // There must be one filepair
202                 if (pDlgPatch->GetItemCount() < 1)
203                         bRetVal = false;
204
205                 // These two are from dropdown list - can't be wrong
206                 patchOptions.outputStyle = pDlgPatch->m_outputStyle;
207                 patchOptions.nContext = pDlgPatch->m_contextLines;
208
209                 // Checkbox - can't be wrong
210                 patchOptions.bAddCommandline = pDlgPatch->m_includeCmdLine;
211                 m_diffWrapper.SetPatchOptions(&patchOptions);
212
213                 // These are from checkboxes and radiobuttons - can't be wrong
214                 diffOptions.nIgnoreWhitespace = pDlgPatch->m_whitespaceCompare;
215                 diffOptions.bIgnoreBlankLines = pDlgPatch->m_ignoreBlanks;
216                 m_diffWrapper.SetAppendFiles(pDlgPatch->m_appendFile);
217
218                 // Use this because non-sensitive setting can't write
219                 // patch file EOLs correctly
220                 diffOptions.bIgnoreEol = pDlgPatch->m_ignoreEOLDifference;
221                 
222                 diffOptions.bIgnoreCase = !pDlgPatch->m_caseSensitive;
223                 diffOptions.nDiffAlgorithm = pDlgPatch->m_diffAlgorithm;
224                 diffOptions.bIndentHeuristic = pDlgPatch->m_indentHeuristic;
225                 m_diffWrapper.SetOptions(&diffOptions);
226         }
227         else
228                 return false;
229
230         return bRetVal;
231 }