File indexing completed on 2024-04-28 17:01:42

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