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 }