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("||||||| ");
32 * @brief Check if the file is a conflict file.
33 * This function checks if the conflict file marker is found from given file.
34 * This is faster than trying to parse a file that is not conflict file.
35 * @param [in] conflictFileName Full path to file to check.
36 * @return true if given file is a conflict file, false otherwise.
38 bool IsConflictFile(const String& conflictFileName)
40 UniMemFile conflictFile;
41 bool startFound = false;
44 bool success = conflictFile.OpenReadOnly(conflictFileName);
48 // Search for a conflict marker
49 bool linesToRead = true;
50 while (linesToRead && !startFound)
55 linesToRead = conflictFile.ReadString(line, eol, &lossy);
57 std::string::size_type pos;
58 pos = line.find(MineBegin);
68 * @brief Parse a conflict file to separate files.
69 * This function parses a conflict file to two different files which can be
70 * opened into WinMerge's file compare.
71 * @param [in] conflictFileName Full path to conflict file.
72 * @param [in] workingCopyFileName Full path for user's modified file in
73 * working copy/working folder.
74 * @param [in] newRevisionFileName Full path for revision control file.
75 * @param [in] iGuessEncodingType Try to guess codepage (not just unicode encoding)
76 * @param [out] bNestedConflicts returned as true if nested conflicts found.
77 * @return true if conflict file was successfully parsed, false otherwise.
79 bool ParseConflictFile(const String& conflictFileName,
80 const String& workingCopyFileName, const String& newRevisionFileName, const String& baseRevisionFileName,
81 int iGuessEncodingType, bool &bNestedConflicts, bool &b3way)
83 UniMemFile conflictFile;
84 UniStdioFile workingCopy;
85 UniStdioFile newRevision;
86 UniStdioFile baseRevision;
88 std::string::size_type pos;
90 int iNestingLevel = 0;
92 String revision = _T("none");
93 bNestedConflicts = false;
97 bool success = conflictFile.OpenReadOnly(conflictFileName);
99 // Create output files
100 bool success2 = workingCopy.Open(workingCopyFileName, _T("wb"));
101 bool success3 = newRevision.Open(newRevisionFileName, _T("wb"));
102 bool success4 = baseRevision.Open(baseRevisionFileName, _T("wb"));
104 // detect codepage of conflict file
105 FileTextEncoding encoding = codepage_detect::Guess(conflictFileName, iGuessEncodingType);
107 conflictFile.SetUnicoding(encoding.m_unicoding);
108 conflictFile.SetBom(encoding.m_bom);
109 conflictFile.SetCodepage(encoding.m_codepage);
110 workingCopy.SetUnicoding(encoding.m_unicoding);
111 workingCopy.SetBom(encoding.m_bom);
112 workingCopy.SetCodepage(encoding.m_codepage);
113 newRevision.SetUnicoding(encoding.m_unicoding);
114 newRevision.SetBom(encoding.m_bom);
115 newRevision.SetCodepage(encoding.m_codepage);
116 baseRevision.SetUnicoding(encoding.m_unicoding);
117 baseRevision.SetBom(encoding.m_bom);
118 baseRevision.SetCodepage(encoding.m_codepage);
121 bool linesToRead = true;
126 linesToRead = conflictFile.ReadString(line, eol, &lossy);
131 // search beginning of conflict section
132 pos = line.find(MineBegin);
135 // working copy section starts
141 // we're in the common section, so write to both files
142 newRevision.WriteString(line);
143 newRevision.WriteString(eol);
145 baseRevision.WriteString(line);
146 baseRevision.WriteString(eol);
148 workingCopy.WriteString(line);
149 workingCopy.WriteString(eol);
153 // in working copy section
155 // search beginning of conflict section
156 pos = line.find(MineBegin);
159 // nested conflict section starts
161 workingCopy.WriteString(line);
162 workingCopy.WriteString(eol);
166 pos = line.find(BaseBegin);
167 if (pos != std::string::npos)
169 line = line.substr(0, pos);
172 baseRevision.WriteString(line);
173 baseRevision.WriteString(eol);
176 // base revision section
182 pos = line.find(Separator);
183 if ((pos != std::string::npos) && (pos == (line.length() - 7)))
185 line = line.substr(0, pos);
188 workingCopy.WriteString(line);
189 workingCopy.WriteString(eol);
192 // new revision section
197 workingCopy.WriteString(line);
198 workingCopy.WriteString(eol);
204 // in new revision section
206 // search beginning of nested conflict section
207 pos = line.find(MineBegin);
210 // nested conflict section starts
212 newRevision.WriteString(line);
213 newRevision.WriteString(eol);
217 pos = line.find(TheirsEnd);
218 if (pos != std::string::npos)
220 revision = line.substr(pos + 8);
221 line = line.substr(0, pos);
224 newRevision.WriteString(line);
225 newRevision.WriteString(eol);
233 newRevision.WriteString(line);
234 newRevision.WriteString(eol);
240 // in nested section in working copy section
242 // search beginning of nested conflict section
243 bNestedConflicts = true;
244 pos = line.find(MineBegin);
251 pos = line.find(TheirsEnd);
252 if (pos != std::string::npos)
254 if (iNestingLevel == 0)
264 workingCopy.WriteString(line);
265 workingCopy.WriteString(eol);
268 // in nested section in new revision section
270 // search beginning of nested conflict section
271 pos = line.find(MineBegin);
278 pos = line.find(TheirsEnd);
279 if (pos != std::string::npos)
281 if (iNestingLevel == 0)
291 newRevision.WriteString(line);
292 newRevision.WriteString(eol);
295 // in base revision section
297 pos = line.find(Separator);
298 if ((pos != std::string::npos) && (pos == (line.length() - 7)))
300 line = line.substr(0, pos);
303 baseRevision.WriteString(line);
304 baseRevision.WriteString(eol);
307 // new revision section
312 baseRevision.WriteString(line);
313 baseRevision.WriteString(eol);
318 } while (linesToRead);
321 baseRevision.Close();
324 conflictFile.Close();