File indexing completed on 2024-05-05 14:20:16
0001 /* 0002 SPDX-FileCopyrightText: 2002-2004,2009 Otto Bruggeman <bruggie@gmail.com> 0003 SPDX-FileCopyrightText: 2007,2010 Kevin Kofler <kevin.kofler@chello.at> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "parserbase.h" 0009 0010 #include <QObject> 0011 0012 #include <komparediffdebug.h> 0013 #include "diffmodellist.h" 0014 #include "diffmodel.h" 0015 #include "diffhunk.h" 0016 #include "difference.h" 0017 #include "komparemodellist.h" 0018 0019 using namespace Diff2; 0020 0021 // static 0022 QString ParserBase::unescapePath(QString path) 0023 { 0024 // If path contains spaces, it is enclosed in quotes 0025 if (path.startsWith(QLatin1Char('"')) && path.endsWith(QLatin1Char('"'))) 0026 path = path.mid(1, path.size() - 2); 0027 0028 // Unescape quotes 0029 path.replace(QLatin1String("\\\""), QLatin1String("\"")); 0030 0031 #ifndef Q_OS_WIN 0032 // Unescape backquotes 0033 path.replace(QLatin1String("\\\\"), QLatin1String("\\")); 0034 #endif 0035 0036 return path; 0037 } 0038 0039 // static 0040 QString ParserBase::escapePath(QString path) 0041 { 0042 #ifndef Q_OS_WIN 0043 // Escape backquotes 0044 path.replace(QLatin1String("\\"), QLatin1String("\\\\")); 0045 #endif 0046 0047 // Escape quotes 0048 path.replace(QLatin1String("\""), QLatin1String("\\\"")); 0049 0050 // Enclose in quotes if path contains space 0051 if (path.contains(QLatin1Char(' '))) 0052 path = QLatin1Char('"') + path + QLatin1Char('"'); 0053 0054 return path; 0055 } 0056 0057 ParserBase::ParserBase(const KompareModelList* list, const QStringList& diff) : 0058 m_diffLines(diff), 0059 m_currentModel(nullptr), 0060 m_models(nullptr), 0061 m_diffIterator(m_diffLines.begin()), 0062 m_singleFileDiff(false), 0063 m_malformed(false), 0064 m_list(list) 0065 { 0066 // qCDebug(LIBKOMPAREDIFF2) << diff; 0067 // qCDebug(LIBKOMPAREDIFF2) << m_diffLines; 0068 m_models = new DiffModelList(); 0069 0070 // used in contexthunkheader 0071 m_contextHunkHeader1.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\*{15} ?(.*)\\n"))); // capture is for function name 0072 m_contextHunkHeader2.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\*\\*\\* ([0-9]+),([0-9]+) \\*\\*\\*\\*.*\\n"))); 0073 // used in contexthunkbody 0074 m_contextHunkHeader3.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("--- ([0-9]+),([0-9]+) ----\\n"))); 0075 0076 m_contextHunkBodyRemoved.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("- (.*)\\n"))); 0077 m_contextHunkBodyAdded.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\+ (.*)\\n"))); 0078 m_contextHunkBodyChanged.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("! (.*)\\n"))); 0079 m_contextHunkBodyContext.setPattern(QRegularExpression::anchoredPattern(QStringLiteral(" (.*)\\n"))); 0080 m_contextHunkBodyLine.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[-\\+! ] (.*)\\n"))); 0081 0082 // This regexp sucks... i'll see what happens 0083 m_normalDiffHeader.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("diff (?:(?:-|--)[a-zA-Z0-9=\\\"]+ )*(?:|-- +)(.*) +(.*)\\n"))); 0084 0085 m_normalHunkHeaderAdded.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("([0-9]+)a([0-9]+)(|,[0-9]+)(.*)\\n"))); 0086 m_normalHunkHeaderRemoved.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("([0-9]+)(|,[0-9]+)d([0-9]+)(.*)\\n"))); 0087 m_normalHunkHeaderChanged.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("([0-9]+)(|,[0-9]+)c([0-9]+)(|,[0-9]+)(.*)\\n"))); 0088 0089 m_normalHunkBodyRemoved.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("< (.*)\\n"))); 0090 m_normalHunkBodyAdded.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("> (.*)\\n"))); 0091 m_normalHunkBodyDivider.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("---\\n"))); 0092 0093 m_unifiedDiffHeader1.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("--- ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n"))); 0094 m_unifiedDiffHeader2.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\+\\+\\+ ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n"))); 0095 m_unifiedHunkHeader.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("@@ -([0-9]+)(|,([0-9]+)) \\+([0-9]+)(|,([0-9]+)) @@(?: ?)(.*)\\n"))); 0096 } 0097 0098 ParserBase::~ParserBase() 0099 { 0100 if (m_models) 0101 m_models = nullptr; // do not delete this, i pass it around... 0102 } 0103 0104 enum Kompare::Format ParserBase::determineFormat() 0105 { 0106 // Write your own format detection routine damn it :) 0107 return Kompare::UnknownFormat; 0108 } 0109 0110 DiffModelList* ParserBase::parse(bool* malformed) 0111 { 0112 DiffModelList* result; 0113 switch (determineFormat()) 0114 { 0115 case Kompare::Context : 0116 result = parseContext(); 0117 break; 0118 case Kompare::Ed : 0119 result = parseEd(); 0120 break; 0121 case Kompare::Normal : 0122 result = parseNormal(); 0123 break; 0124 case Kompare::RCS : 0125 result = parseRCS(); 0126 break; 0127 case Kompare::Unified : 0128 result = parseUnified(); 0129 break; 0130 default: // Unknown and SideBySide for now 0131 result = nullptr; 0132 break; 0133 } 0134 0135 // *malformed is set to true if some hunks or parts of hunks were 0136 // probably missed due to a malformed diff 0137 if (malformed) 0138 *malformed = m_malformed; 0139 0140 return result; 0141 } 0142 0143 bool ParserBase::parseContextDiffHeader() 0144 { 0145 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseContextDiffHeader()"; 0146 bool result = false; 0147 0148 while (m_diffIterator != m_diffLines.end()) 0149 { 0150 const auto contextDiffHeader1Match = m_contextDiffHeader1.match(*(m_diffIterator)++); 0151 if (!contextDiffHeader1Match.hasMatch()) 0152 { 0153 continue; 0154 } 0155 // qCDebug(LIBKOMPAREDIFF2) << "Matched length Header1 = " << contextDiffHeader1Match.capturedLength(); 0156 // qCDebug(LIBKOMPAREDIFF2) << "Matched string Header1 = " << contextDiffHeader1Match.captured( 0 ); 0157 const auto contextDiffHeader2Match = m_contextDiffHeader2.match(*m_diffIterator); 0158 if (m_diffIterator != m_diffLines.end() && contextDiffHeader2Match.hasMatch()) 0159 { 0160 // qCDebug(LIBKOMPAREDIFF2) << "Matched length Header2 = " << contextDiffHeader2Match.capturedLength(); 0161 // qCDebug(LIBKOMPAREDIFF2) << "Matched string Header2 = " << contextDiffHeader2Match.captured( 0 ); 0162 0163 m_currentModel = new DiffModel(unescapePath(contextDiffHeader1Match.captured(1)), unescapePath(contextDiffHeader2Match.captured(1))); 0164 m_currentModel->setSourceTimestamp(contextDiffHeader1Match.captured(3)); 0165 m_currentModel->setSourceRevision(contextDiffHeader1Match.captured(5)); 0166 m_currentModel->setDestinationTimestamp(contextDiffHeader2Match.captured(3)); 0167 m_currentModel->setDestinationRevision(contextDiffHeader2Match.captured(5)); 0168 0169 ++m_diffIterator; 0170 result = true; 0171 0172 break; 0173 } 0174 else 0175 { 0176 // We're screwed, second line does not match or is not there... 0177 break; 0178 } 0179 // Do not inc the Iterator because the second line might be the first line of 0180 // the context header and the first hit was a fluke (impossible imo) 0181 // maybe we should return false here because the diff is broken ? 0182 } 0183 0184 return result; 0185 } 0186 0187 bool ParserBase::parseEdDiffHeader() 0188 { 0189 return false; 0190 } 0191 0192 bool ParserBase::parseNormalDiffHeader() 0193 { 0194 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseNormalDiffHeader()"; 0195 bool result = false; 0196 0197 while (m_diffIterator != m_diffLines.end()) 0198 { 0199 const auto normalDiffHeaderMatch = m_normalDiffHeader.match(*m_diffIterator); 0200 if (normalDiffHeaderMatch.hasMatch()) 0201 { 0202 // qCDebug(LIBKOMPAREDIFF2) << "Matched length Header = " << normalDiffHeaderMatch.capturedLength(); 0203 // qCDebug(LIBKOMPAREDIFF2) << "Matched string Header = " << normalDiffHeaderMatch.captured( 0 ); 0204 0205 m_currentModel = new DiffModel(); 0206 m_currentModel->setSourceFile(unescapePath(normalDiffHeaderMatch.captured(1))); 0207 m_currentModel->setDestinationFile(unescapePath(normalDiffHeaderMatch.captured(2))); 0208 0209 result = true; 0210 0211 ++m_diffIterator; 0212 break; 0213 } 0214 else 0215 { 0216 qCDebug(LIBKOMPAREDIFF2) << "No match for: " << (*m_diffIterator); 0217 } 0218 ++m_diffIterator; 0219 } 0220 0221 if (result == false) 0222 { 0223 // Set this to the first line again and hope it is a single file diff 0224 m_diffIterator = m_diffLines.begin(); 0225 m_currentModel = new DiffModel(); 0226 m_singleFileDiff = true; 0227 } 0228 0229 return result; 0230 } 0231 0232 bool ParserBase::parseRCSDiffHeader() 0233 { 0234 return false; 0235 } 0236 0237 bool ParserBase::parseUnifiedDiffHeader() 0238 { 0239 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseUnifiedDiffHeader()"; 0240 bool result = false; 0241 0242 while (m_diffIterator != m_diffLines.end()) // do not assume we start with the diffheader1 line 0243 { 0244 const auto unifiedDiffHeader1Match = m_unifiedDiffHeader1.match(*m_diffIterator); 0245 if (!unifiedDiffHeader1Match.hasMatch()) 0246 { 0247 ++m_diffIterator; 0248 continue; 0249 } 0250 // qCDebug(LIBKOMPAREDIFF2) << "Matched length Header1 = " << unifiedDiffHeader1Match.capturedLength(); 0251 // qCDebug(LIBKOMPAREDIFF2) << "Matched string Header1 = " << unifiedDiffHeader1Match.captured( 0 ); 0252 ++m_diffIterator; 0253 const auto unifiedDiffHeader2Match = m_unifiedDiffHeader2.match(*m_diffIterator); 0254 if (m_diffIterator != m_diffLines.end() && unifiedDiffHeader2Match.hasMatch()) 0255 { 0256 m_currentModel = new DiffModel(unescapePath(unifiedDiffHeader1Match.captured(1)), unescapePath(unifiedDiffHeader2Match.captured(1))); 0257 m_currentModel->setSourceTimestamp(unifiedDiffHeader1Match.captured(2)); 0258 m_currentModel->setSourceRevision(unifiedDiffHeader1Match.captured(4)); 0259 m_currentModel->setDestinationTimestamp(unifiedDiffHeader2Match.captured(2)); 0260 m_currentModel->setDestinationRevision(unifiedDiffHeader2Match.captured(4)); 0261 0262 ++m_diffIterator; 0263 result = true; 0264 0265 break; 0266 } 0267 else 0268 { 0269 // We're screwed, second line does not match or is not there... 0270 break; 0271 } 0272 } 0273 0274 return result; 0275 } 0276 0277 bool ParserBase::parseContextHunkHeader() 0278 { 0279 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseContextHunkHeader()"; 0280 0281 if (m_diffIterator == m_diffLines.end()) 0282 return false; 0283 0284 m_contextHunkHeader1Match = m_contextHunkHeader1.match(*(m_diffIterator)); 0285 if (!m_contextHunkHeader1Match.hasMatch()) 0286 return false; // big fat trouble, aborting... 0287 0288 ++m_diffIterator; 0289 0290 if (m_diffIterator == m_diffLines.end()) 0291 return false; 0292 0293 m_contextHunkHeader2Match = m_contextHunkHeader2.match(*(m_diffIterator)); 0294 if (!m_contextHunkHeader2Match.hasMatch()) 0295 return false; // big fat trouble, aborting... 0296 0297 ++m_diffIterator; 0298 0299 return true; 0300 } 0301 0302 bool ParserBase::parseEdHunkHeader() 0303 { 0304 return false; 0305 } 0306 0307 bool ParserBase::parseNormalHunkHeader() 0308 { 0309 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseNormalHunkHeader()"; 0310 if (m_diffIterator != m_diffLines.end()) 0311 { 0312 // qCDebug(LIBKOMPAREDIFF2) << "Header = " << *m_diffIterator; 0313 if (m_normalHunkHeaderAddedMatch = m_normalHunkHeaderAdded.match(*m_diffIterator); m_normalHunkHeaderAddedMatch.hasMatch()) 0314 { 0315 m_normalDiffType = Difference::Insert; 0316 } 0317 else if (m_normalHunkHeaderRemovedMatch = m_normalHunkHeaderRemoved.match(*m_diffIterator); m_normalHunkHeaderRemovedMatch.hasMatch()) 0318 { 0319 m_normalDiffType = Difference::Delete; 0320 } 0321 else if (m_normalHunkHeaderChangedMatch = m_normalHunkHeaderChanged.match(*m_diffIterator); m_normalHunkHeaderChangedMatch.hasMatch()) 0322 { 0323 m_normalDiffType = Difference::Change; 0324 } 0325 else 0326 return false; 0327 0328 ++m_diffIterator; 0329 return true; 0330 } 0331 0332 return false; 0333 } 0334 0335 bool ParserBase::parseRCSHunkHeader() 0336 { 0337 return false; 0338 } 0339 0340 bool ParserBase::parseUnifiedHunkHeader() 0341 { 0342 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseUnifiedHunkHeader()"; 0343 0344 if (m_diffIterator != m_diffLines.end()) 0345 { 0346 m_unifiedHunkHeaderMatch = m_unifiedHunkHeader.match(*m_diffIterator); 0347 if (m_unifiedHunkHeaderMatch.hasMatch()) { 0348 ++m_diffIterator; 0349 return true; 0350 } 0351 } 0352 // qCDebug(LIBKOMPAREDIFF2) << "This is not a unified hunk header : " << (*m_diffIterator); 0353 return false; 0354 } 0355 0356 bool ParserBase::parseContextHunkBody() 0357 { 0358 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseContextHunkBody()"; 0359 0360 // Storing the src part of the hunk for later use 0361 QStringList oldLines; 0362 for (; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.match(*m_diffIterator).hasMatch(); ++m_diffIterator) { 0363 // qCDebug(LIBKOMPAREDIFF2) << "Added old line: " << *m_diffIterator; 0364 oldLines.append(*m_diffIterator); 0365 } 0366 0367 const auto contextHunkHeader3Match = m_contextHunkHeader3.match(*m_diffIterator); 0368 if (!contextHunkHeader3Match.hasMatch()) 0369 return false; 0370 0371 ++m_diffIterator; 0372 0373 // Storing the dest part of the hunk for later use 0374 QStringList newLines; 0375 for (; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.match(*m_diffIterator).hasMatch(); ++m_diffIterator) { 0376 // qCDebug(LIBKOMPAREDIFF2) << "Added new line: " << *m_diffIterator; 0377 newLines.append(*m_diffIterator); 0378 } 0379 0380 QString function = m_contextHunkHeader1Match.captured(1); 0381 // qCDebug(LIBKOMPAREDIFF2) << "Captured function: " << function; 0382 int linenoA = m_contextHunkHeader2Match.captured(1).toInt(); 0383 // qCDebug(LIBKOMPAREDIFF2) << "Source line number: " << linenoA; 0384 int linenoB = contextHunkHeader3Match.captured(1).toInt(); 0385 // qCDebug(LIBKOMPAREDIFF2) << "Dest line number: " << linenoB; 0386 0387 DiffHunk* hunk = new DiffHunk(linenoA, linenoB, function); 0388 0389 m_currentModel->addHunk(hunk); 0390 0391 QStringList::Iterator oldIt = oldLines.begin(); 0392 QStringList::Iterator newIt = newLines.begin(); 0393 0394 Difference* diff; 0395 while (oldIt != oldLines.end() || newIt != newLines.end()) 0396 { 0397 if (oldIt != oldLines.end() && m_contextHunkBodyRemoved.match(*oldIt).hasMatch()) 0398 { 0399 // qCDebug(LIBKOMPAREDIFF2) << "Delete: "; 0400 diff = new Difference(linenoA, linenoB); 0401 diff->setType(Difference::Delete); 0402 m_currentModel->addDiff(diff); 0403 // qCDebug(LIBKOMPAREDIFF2) << "Difference added"; 0404 hunk->add(diff); 0405 for (; oldIt != oldLines.end(); ++oldIt) 0406 { 0407 const auto contextHunkBodyRemovedMatch = m_contextHunkBodyRemoved.match(*oldIt); 0408 if (!contextHunkBodyRemovedMatch.hasMatch()) { 0409 break; 0410 } 0411 // qCDebug(LIBKOMPAREDIFF2) << " " << contextHunkBodyRemovedMatch.captured( 1 ); 0412 diff->addSourceLine(contextHunkBodyRemovedMatch.captured(1)); 0413 ++linenoA; 0414 } 0415 } 0416 else if (newIt != newLines.end() && m_contextHunkBodyAdded.match(*newIt).hasMatch()) 0417 { 0418 // qCDebug(LIBKOMPAREDIFF2) << "Insert: "; 0419 diff = new Difference(linenoA, linenoB); 0420 diff->setType(Difference::Insert); 0421 m_currentModel->addDiff(diff); 0422 // qCDebug(LIBKOMPAREDIFF2) << "Difference added"; 0423 hunk->add(diff); 0424 for (; newIt != newLines.end(); ++newIt) 0425 { 0426 const auto contextHunkBodyAddedMatch = m_contextHunkBodyAdded.match(*newIt); 0427 if (!contextHunkBodyAddedMatch.hasMatch()) { 0428 break; 0429 } 0430 // qCDebug(LIBKOMPAREDIFF2) << " " << contextHunkBodyAddedMatch.captured( 1 ); 0431 diff->addDestinationLine(contextHunkBodyAddedMatch.captured(1)); 0432 ++linenoB; 0433 } 0434 } 0435 else if ((oldIt == oldLines.end() || m_contextHunkBodyContext.match(*oldIt).hasMatch()) && 0436 (newIt == newLines.end() || m_contextHunkBodyContext.match(*newIt).hasMatch())) 0437 { 0438 // qCDebug(LIBKOMPAREDIFF2) << "Unchanged: "; 0439 diff = new Difference(linenoA, linenoB); 0440 // Do not add this diff with addDiff to the model... no unchanged differences allowed in there... 0441 diff->setType(Difference::Unchanged); 0442 hunk->add(diff); 0443 while ((oldIt == oldLines.end() || m_contextHunkBodyContext.match(*oldIt).hasMatch()) && 0444 (newIt == newLines.end() || m_contextHunkBodyContext.match(*newIt).hasMatch()) && 0445 (oldIt != oldLines.end() || newIt != newLines.end())) 0446 { 0447 QString l; 0448 if (oldIt != oldLines.end()) 0449 { 0450 l = m_contextHunkBodyContext.match(*oldIt).captured(1); 0451 // qCDebug(LIBKOMPAREDIFF2) << "old: " << l; 0452 ++oldIt; 0453 } 0454 if (newIt != newLines.end()) 0455 { 0456 l = m_contextHunkBodyContext.match(*newIt).captured(1); 0457 // qCDebug(LIBKOMPAREDIFF2) << "new: " << l; 0458 ++newIt; 0459 } 0460 diff->addSourceLine(l); 0461 diff->addDestinationLine(l); 0462 ++linenoA; 0463 ++linenoB; 0464 } 0465 } 0466 else if ((oldIt != oldLines.end() && m_contextHunkBodyChanged.match(*oldIt).hasMatch()) || 0467 (newIt != newLines.end() && m_contextHunkBodyChanged.match(*newIt).hasMatch())) 0468 { 0469 // qCDebug(LIBKOMPAREDIFF2) << "Changed: "; 0470 diff = new Difference(linenoA, linenoB); 0471 diff->setType(Difference::Change); 0472 m_currentModel->addDiff(diff); 0473 // qCDebug(LIBKOMPAREDIFF2) << "Difference added"; 0474 hunk->add(diff); 0475 while (oldIt != oldLines.end()) 0476 { 0477 const auto contextHunkBodyChangedMatch = m_contextHunkBodyChanged.match(*oldIt); 0478 if (!contextHunkBodyChangedMatch.hasMatch()) { 0479 break; 0480 } 0481 // qCDebug(LIBKOMPAREDIFF2) << " " << contextHunkBodyChangedMatch.captured( 1 ); 0482 diff->addSourceLine(contextHunkBodyChangedMatch.captured(1)); 0483 ++linenoA; 0484 ++oldIt; 0485 } 0486 while (newIt != newLines.end()) 0487 { 0488 const auto contextHunkBodyChangedMatch = m_contextHunkBodyChanged.match(*newIt); 0489 if (!contextHunkBodyChangedMatch.hasMatch()) { 0490 break; 0491 } 0492 // qCDebug(LIBKOMPAREDIFF2) << " " << contextHunkBodyChangedMatch.captured( 1 ); 0493 diff->addDestinationLine(contextHunkBodyChangedMatch.captured(1)); 0494 ++linenoB; 0495 ++newIt; 0496 } 0497 } 0498 else 0499 return false; 0500 diff->determineInlineDifferences(); 0501 } 0502 0503 return true; 0504 } 0505 0506 bool ParserBase::parseEdHunkBody() 0507 { 0508 return false; 0509 } 0510 0511 bool ParserBase::parseNormalHunkBody() 0512 { 0513 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseNormalHunkBody"; 0514 0515 QString type; 0516 0517 int linenoA = 0, linenoB = 0; 0518 0519 if (m_normalDiffType == Difference::Insert) 0520 { 0521 linenoA = m_normalHunkHeaderAddedMatch.captured(1).toInt(); 0522 linenoB = m_normalHunkHeaderAddedMatch.captured(2).toInt(); 0523 } 0524 else if (m_normalDiffType == Difference::Delete) 0525 { 0526 linenoA = m_normalHunkHeaderRemovedMatch.captured(1).toInt(); 0527 linenoB = m_normalHunkHeaderRemovedMatch.captured(3).toInt(); 0528 } 0529 else if (m_normalDiffType == Difference::Change) 0530 { 0531 linenoA = m_normalHunkHeaderChangedMatch.captured(1).toInt(); 0532 linenoB = m_normalHunkHeaderChangedMatch.captured(3).toInt(); 0533 } 0534 0535 DiffHunk* hunk = new DiffHunk(linenoA, linenoB); 0536 m_currentModel->addHunk(hunk); 0537 Difference* diff = new Difference(linenoA, linenoB); 0538 hunk->add(diff); 0539 m_currentModel->addDiff(diff); 0540 0541 diff->setType(m_normalDiffType); 0542 0543 if (m_normalDiffType == Difference::Change || m_normalDiffType == Difference::Delete) 0544 for (; m_diffIterator != m_diffLines.end(); ++m_diffIterator) 0545 { 0546 const auto normalHunkBodyRemovedMatch = m_normalHunkBodyRemoved.match(*m_diffIterator); 0547 if (!normalHunkBodyRemovedMatch.hasMatch()) { 0548 break; 0549 } 0550 // qCDebug(LIBKOMPAREDIFF2) << "Line = " << *m_diffIterator; 0551 diff->addSourceLine(normalHunkBodyRemovedMatch.captured(1)); 0552 } 0553 0554 if (m_normalDiffType == Difference::Change) 0555 { 0556 if (m_diffIterator != m_diffLines.end() && m_normalHunkBodyDivider.match(*m_diffIterator).hasMatch()) 0557 { 0558 // qCDebug(LIBKOMPAREDIFF2) << "Line = " << *m_diffIterator; 0559 ++m_diffIterator; 0560 } 0561 else 0562 return false; 0563 } 0564 0565 if (m_normalDiffType == Difference::Insert || m_normalDiffType == Difference::Change) 0566 for (; m_diffIterator != m_diffLines.end(); ++m_diffIterator) 0567 { 0568 const auto normalHunkBodyAddedMatch = m_normalHunkBodyAdded.match(*m_diffIterator); 0569 if (!normalHunkBodyAddedMatch.hasMatch()) { 0570 break; 0571 } 0572 // qCDebug(LIBKOMPAREDIFF2) << "Line = " << *m_diffIterator; 0573 diff->addDestinationLine(normalHunkBodyAddedMatch.captured(1)); 0574 } 0575 0576 return true; 0577 } 0578 0579 bool ParserBase::parseRCSHunkBody() 0580 { 0581 return false; 0582 } 0583 0584 bool ParserBase::matchesUnifiedHunkLine(const QString& line) const 0585 { 0586 static const QChar context = QLatin1Char(' '); 0587 static const QChar added = QLatin1Char('+'); 0588 static const QChar removed = QLatin1Char('-'); 0589 0590 QChar first = line[0]; 0591 0592 return (first == context || first == added || first == removed); 0593 } 0594 0595 bool ParserBase::parseUnifiedHunkBody() 0596 { 0597 // qCDebug(LIBKOMPAREDIFF2) << "ParserBase::parseUnifiedHunkBody"; 0598 0599 int linenoA = 0, linenoB = 0; 0600 bool wasNum; 0601 0602 // Fetching the stuff we need from the hunkheader regexp that was parsed in parseUnifiedHunkHeader(); 0603 linenoA = m_unifiedHunkHeaderMatch.captured(1).toInt(); 0604 int lineCountA = 1, lineCountB = 1; // an omitted line count in the header implies a line count of 1 0605 if (!m_unifiedHunkHeaderMatch.captured(3).isEmpty()) 0606 { 0607 lineCountA = m_unifiedHunkHeaderMatch.captured(3).toInt(&wasNum); 0608 if (!wasNum) 0609 return false; 0610 0611 // If a hunk is an insertion or deletion with no context, the line number given 0612 // is the one before the hunk. this isn't what we want, so increment it to fix this. 0613 if (lineCountA == 0) 0614 ++linenoA; 0615 } 0616 linenoB = m_unifiedHunkHeaderMatch.captured(4).toInt(); 0617 if (!m_unifiedHunkHeaderMatch.captured(6).isEmpty()) { 0618 lineCountB = m_unifiedHunkHeaderMatch.captured(6).toInt(&wasNum); 0619 if (!wasNum) 0620 return false; 0621 0622 if (lineCountB == 0) // see above 0623 ++linenoB; 0624 } 0625 QString function = m_unifiedHunkHeaderMatch.captured(7); 0626 0627 DiffHunk* hunk = new DiffHunk(linenoA, linenoB, function); 0628 m_currentModel->addHunk(hunk); 0629 0630 const QStringList::ConstIterator m_diffLinesEnd = m_diffLines.end(); 0631 0632 const QString context = QStringLiteral(" "); 0633 const QString added = QStringLiteral("+"); 0634 const QString removed = QStringLiteral("-"); 0635 0636 while (m_diffIterator != m_diffLinesEnd && matchesUnifiedHunkLine(*m_diffIterator) && (lineCountA || lineCountB)) 0637 { 0638 Difference* diff = new Difference(linenoA, linenoB); 0639 hunk->add(diff); 0640 0641 if ((*m_diffIterator).startsWith(context)) 0642 { // context 0643 for (; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith(context) && (lineCountA || lineCountB); ++m_diffIterator) 0644 { 0645 diff->addSourceLine(QString(*m_diffIterator).remove(0, 1)); 0646 diff->addDestinationLine(QString(*m_diffIterator).remove(0, 1)); 0647 ++linenoA; 0648 ++linenoB; 0649 --lineCountA; 0650 --lineCountB; 0651 } 0652 } 0653 else 0654 { // This is a real difference, not context 0655 for (; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith(removed) && (lineCountA || lineCountB); ++m_diffIterator) 0656 { 0657 diff->addSourceLine(QString(*m_diffIterator).remove(0, 1)); 0658 ++linenoA; 0659 --lineCountA; 0660 } 0661 for (; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith(added) && (lineCountA || lineCountB); ++m_diffIterator) 0662 { 0663 diff->addDestinationLine(QString(*m_diffIterator).remove(0, 1)); 0664 ++linenoB; 0665 --lineCountB; 0666 } 0667 if (diff->sourceLineCount() == 0) 0668 { 0669 diff->setType(Difference::Insert); 0670 // qCDebug(LIBKOMPAREDIFF2) << "Insert difference"; 0671 } 0672 else if (diff->destinationLineCount() == 0) 0673 { 0674 diff->setType(Difference::Delete); 0675 // qCDebug(LIBKOMPAREDIFF2) << "Delete difference"; 0676 } 0677 else 0678 { 0679 diff->setType(Difference::Change); 0680 // qCDebug(LIBKOMPAREDIFF2) << "Change difference"; 0681 } 0682 diff->determineInlineDifferences(); 0683 m_currentModel->addDiff(diff); 0684 } 0685 } 0686 0687 return true; 0688 } 0689 0690 void ParserBase::checkHeader(const QRegularExpression& header) 0691 { 0692 if (m_diffIterator != m_diffLines.end() 0693 && !header.match(*m_diffIterator).hasMatch() 0694 && !m_diffIterator->startsWith(QLatin1String("Index: ")) /* SVN diff */ 0695 && !m_diffIterator->startsWith(QLatin1String("diff ")) /* concatenated diff */ 0696 && !m_diffIterator->startsWith(QLatin1String("-- ")) /* git format-patch */) 0697 m_malformed = true; 0698 } 0699 0700 DiffModelList* ParserBase::parseContext() 0701 { 0702 while (parseContextDiffHeader()) 0703 { 0704 while (parseContextHunkHeader()) 0705 parseContextHunkBody(); 0706 if (m_currentModel->differenceCount() > 0) 0707 m_models->append(m_currentModel); 0708 checkHeader(m_contextDiffHeader1); 0709 } 0710 0711 m_models->sort(); 0712 0713 if (m_models->count() > 0) 0714 { 0715 return m_models; 0716 } 0717 else 0718 { 0719 delete m_models; 0720 return nullptr; 0721 } 0722 } 0723 0724 DiffModelList* ParserBase::parseEd() 0725 { 0726 while (parseEdDiffHeader()) 0727 { 0728 while (parseEdHunkHeader()) 0729 parseEdHunkBody(); 0730 if (m_currentModel->differenceCount() > 0) 0731 m_models->append(m_currentModel); 0732 } 0733 0734 m_models->sort(); 0735 0736 if (m_models->count() > 0) 0737 { 0738 return m_models; 0739 } 0740 else 0741 { 0742 delete m_models; 0743 return nullptr; 0744 } 0745 } 0746 0747 DiffModelList* ParserBase::parseNormal() 0748 { 0749 while (parseNormalDiffHeader()) 0750 { 0751 while (parseNormalHunkHeader()) 0752 parseNormalHunkBody(); 0753 if (m_currentModel->differenceCount() > 0) 0754 m_models->append(m_currentModel); 0755 checkHeader(m_normalDiffHeader); 0756 } 0757 0758 if (m_singleFileDiff) 0759 { 0760 while (parseNormalHunkHeader()) 0761 parseNormalHunkBody(); 0762 if (m_currentModel->differenceCount() > 0) 0763 m_models->append(m_currentModel); 0764 if (m_diffIterator != m_diffLines.end()) 0765 m_malformed = true; 0766 } 0767 0768 m_models->sort(); 0769 0770 if (m_models->count() > 0) 0771 { 0772 return m_models; 0773 } 0774 else 0775 { 0776 delete m_models; 0777 return nullptr; 0778 } 0779 } 0780 0781 DiffModelList* ParserBase::parseRCS() 0782 { 0783 while (parseRCSDiffHeader()) 0784 { 0785 while (parseRCSHunkHeader()) 0786 parseRCSHunkBody(); 0787 if (m_currentModel->differenceCount() > 0) 0788 m_models->append(m_currentModel); 0789 } 0790 0791 m_models->sort(); 0792 0793 if (m_models->count() > 0) 0794 { 0795 return m_models; 0796 } 0797 else 0798 { 0799 delete m_models; 0800 return nullptr; 0801 } 0802 } 0803 0804 DiffModelList* ParserBase::parseUnified() 0805 { 0806 while (parseUnifiedDiffHeader()) 0807 { 0808 while (parseUnifiedHunkHeader()) 0809 parseUnifiedHunkBody(); 0810 // qCDebug(LIBKOMPAREDIFF2) << "New model ready to be analyzed..."; 0811 // qCDebug(LIBKOMPAREDIFF2) << " differenceCount() == " << m_currentModel->differenceCount(); 0812 if (m_currentModel->differenceCount() > 0) 0813 m_models->append(m_currentModel); 0814 checkHeader(m_unifiedDiffHeader1); 0815 } 0816 0817 m_models->sort(); 0818 0819 if (m_models->count() > 0) 0820 { 0821 return m_models; 0822 } 0823 else 0824 { 0825 delete m_models; 0826 return nullptr; 0827 } 0828 } 0829