1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * @file ConflictFileParser.cpp
5 * @brief Implementation for conflict file parser.
8 // Conflict file parsing method modified from original code got from:
9 // TortoiseCVS - a Windows shell extension for easy version control
10 // Copyright (C) 2000 - Francis Irving
11 // <francis@flourish.org> - January 2001
14 #include "ConflictFileParser.h"
15 #include "UnicodeString.h"
17 #include "FileTextEncoding.h"
18 #include "codepage_detect.h"
21 // Note: keep these strings in "wrong" order so we can resolve this file :)
22 /** @brief String separating Mine and Theirs blocks. */
23 static const TCHAR Separator[] = _T("=======");
24 /** @brief String ending Theirs block (and conflict). */
25 static const TCHAR TheirsEnd[] = _T(">>>>>>> ");
26 /** @brief String starting Mine block (and conflict). */
27 static const TCHAR MineBegin[] = _T("<<<<<<< ");
28 /** @brief String starting Base block (and conflict). */
29 static const TCHAR BaseBegin[] = _T("||||||| ");
31 namespace ConflictFileParser
35 * @brief Check if the file is a conflict file.
36 * This function checks if the conflict file marker is found from given file.
37 * This is faster than trying to parse a file that is not conflict file.
38 * @param [in] conflictFileName Full path to file to check.
39 * @return true if given file is a conflict file, false otherwise.
41 bool IsConflictFile(const String& conflictFileName)
43 UniMemFile conflictFile;
44 bool startFound = false;
47 bool success = conflictFile.OpenReadOnly(conflictFileName);
51 // Search for a conflict marker
52 bool linesToRead = true;
53 while (linesToRead && !startFound)
58 linesToRead = conflictFile.ReadString(line, eol, &lossy);
60 std::string::size_type pos;
61 pos = line.find(MineBegin);
71 * @brief Parse a conflict file to separate files.
72 * This function parses a conflict file to two different files which can be
73 * opened into WinMerge's file compare.
74 * @param [in] conflictFileName Full path to conflict file.
75 * @param [in] workingCopyFileName Full path for user's modified file in
76 * working copy/working folder.
77 * @param [in] newRevisionFileName Full path for revision control file.
78 * @param [in] iGuessEncodingType Try to guess codepage (not just unicode encoding)
79 * @param [out] bNestedConflicts returned as true if nested conflicts found.
80 * @return true if conflict file was successfully parsed, false otherwise.
82 bool ParseConflictFile(const String& conflictFileName,
83 const String& workingCopyFileName, const String& newRevisionFileName, const String& baseRevisionFileName,
84 int iGuessEncodingType, bool &bNestedConflicts, bool &b3way)
86 UniMemFile conflictFile;
87 UniStdioFile workingCopy;
88 UniStdioFile newRevision;
89 UniStdioFile baseRevision;
91 std::string::size_type pos;
93 int iNestingLevel = 0;
95 String revision = _T("none");
96 bNestedConflicts = false;
100 bool success = conflictFile.OpenReadOnly(conflictFileName);
102 // Create output files
103 bool success2 = workingCopy.Open(workingCopyFileName, _T("wb"));
104 bool success3 = newRevision.Open(newRevisionFileName, _T("wb"));
105 bool success4 = baseRevision.Open(baseRevisionFileName, _T("wb"));
107 // detect codepage of conflict file
108 FileTextEncoding encoding = codepage_detect::Guess(conflictFileName, iGuessEncodingType);
110 conflictFile.SetUnicoding(encoding.m_unicoding);
111 conflictFile.SetBom(encoding.m_bom);
112 conflictFile.SetCodepage(encoding.m_codepage);
113 workingCopy.SetUnicoding(encoding.m_unicoding);
114 workingCopy.SetBom(encoding.m_bom);
115 workingCopy.SetCodepage(encoding.m_codepage);
116 newRevision.SetUnicoding(encoding.m_unicoding);
117 newRevision.SetBom(encoding.m_bom);
118 newRevision.SetCodepage(encoding.m_codepage);
119 baseRevision.SetUnicoding(encoding.m_unicoding);
120 baseRevision.SetBom(encoding.m_bom);
121 baseRevision.SetCodepage(encoding.m_codepage);
124 bool linesToRead = true;
129 linesToRead = conflictFile.ReadString(line, eol, &lossy);
134 // search beginning of conflict section
135 pos = line.find(MineBegin);
138 // working copy section starts
144 // we're in the common section, so write to both files
145 newRevision.WriteString(line);
146 newRevision.WriteString(eol);
148 baseRevision.WriteString(line);
149 baseRevision.WriteString(eol);
151 workingCopy.WriteString(line);
152 workingCopy.WriteString(eol);
156 // in working copy section
158 // search beginning of conflict section
159 pos = line.find(MineBegin);
162 // nested conflict section starts
164 workingCopy.WriteString(line);
165 workingCopy.WriteString(eol);
169 pos = line.find(BaseBegin);
170 if (pos != std::string::npos)
172 line = line.substr(0, pos);
175 baseRevision.WriteString(line);
176 baseRevision.WriteString(eol);
179 // base revision section
185 pos = line.find(Separator);
186 if ((pos != std::string::npos) && (pos == (line.length() - 7)))
188 line = line.substr(0, pos);
191 workingCopy.WriteString(line);
192 workingCopy.WriteString(eol);
195 // new revision section
200 workingCopy.WriteString(line);
201 workingCopy.WriteString(eol);
207 // in new revision section
209 // search beginning of nested conflict section
210 pos = line.find(MineBegin);
213 // nested conflict section starts
215 newRevision.WriteString(line);
216 newRevision.WriteString(eol);
220 pos = line.find(TheirsEnd);
221 if (pos != std::string::npos)
223 revision = line.substr(pos + 8);
224 line = line.substr(0, pos);
227 newRevision.WriteString(line);
228 newRevision.WriteString(eol);
236 newRevision.WriteString(line);
237 newRevision.WriteString(eol);
243 // in nested section in working copy section
245 // search beginning of nested conflict section
246 bNestedConflicts = true;
247 pos = line.find(MineBegin);
254 pos = line.find(TheirsEnd);
255 if (pos != std::string::npos)
257 if (iNestingLevel == 0)
267 workingCopy.WriteString(line);
268 workingCopy.WriteString(eol);
271 // in nested section in new revision section
273 // search beginning of nested conflict section
274 pos = line.find(MineBegin);
281 pos = line.find(TheirsEnd);
282 if (pos != std::string::npos)
284 if (iNestingLevel == 0)
294 newRevision.WriteString(line);
295 newRevision.WriteString(eol);
298 // in base revision section
300 pos = line.find(Separator);
301 if ((pos != std::string::npos) && (pos == (line.length() - 7)))
303 line = line.substr(0, pos);
306 baseRevision.WriteString(line);
307 baseRevision.WriteString(eol);
310 // new revision section
315 baseRevision.WriteString(line);
316 baseRevision.WriteString(eol);
321 } while (linesToRead);
324 baseRevision.Close();
327 conflictFile.Close();