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