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

0001 /*
0002 SPDX-FileCopyrightText: 2001-2005,2009 Otto Bruggeman <bruggie@gmail.com>
0003 SPDX-FileCopyrightText: 2001-2003 John Firebaugh <jfirebaugh@kde.org>
0004 SPDX-FileCopyrightText: 2007-2008 Kevin Kofler <kevin.kofler@chello.at>
0005 
0006 SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "kompareprocess.h"
0010 
0011 #include <QDir>
0012 #include <QStringList>
0013 #include <QTextCodec>
0014 
0015 #include <KIO/Global>
0016 
0017 #include <komparediffdebug.h>
0018 #include "diffsettings.h"
0019 
0020 namespace {
0021 /// TODO: This should be replaced to QDir::relativeFilePath
0022 static QString constructRelativePath(const QString& from, const QString& to)
0023 {
0024     QUrl fromURL(from);
0025     QUrl toURL(to);
0026     QUrl root;
0027     int upLevels = 0;
0028 
0029     // Find a common root.
0030     root = fromURL;
0031     while (root.isValid() && !root.isParentOf(toURL)) {
0032         root = KIO::upUrl(root);
0033         ++upLevels;
0034     }
0035 
0036     if (!root.isValid()) return to;
0037 
0038     QString relative;
0039     for (; upLevels > 0; --upLevels) {
0040         relative += QStringLiteral("../");
0041     }
0042 
0043     relative += QString(to).remove(0, root.path().length());
0044     return relative;
0045 }
0046 }
0047 
0048 KompareProcess::KompareProcess(DiffSettings* diffSettings, Kompare::DiffMode diffMode, const QString& source, const QString& destination, const QString& dir, Kompare::Mode mode)
0049     : KProcess(),
0050       m_diffSettings(diffSettings),
0051       m_diffMode(diffMode),
0052       m_mode(mode),
0053       m_textDecoder(nullptr)
0054 {
0055     // connect the signal that indicates that the process has exited
0056     connect(this, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
0057             this, &KompareProcess::slotFinished);
0058 
0059     setEnv(QStringLiteral("LANG"), QStringLiteral("C"));
0060 
0061     // Write command and options
0062     if (m_diffMode == Kompare::Default)
0063     {
0064         writeDefaultCommandLine();
0065     }
0066     else
0067     {
0068         writeCommandLine();
0069     }
0070 
0071     if (!dir.isEmpty()) {
0072         setWorkingDirectory(dir);
0073     }
0074 
0075     // Write file names
0076     *this << QStringLiteral("--");
0077 
0078     //Add the option for diff to read from stdin(QIODevice::write), and save a pointer to the string
0079     if (m_mode == Kompare::ComparingStringFile)
0080     {
0081         *this << QStringLiteral("-");
0082         m_customString = source;
0083     }
0084     else
0085     {
0086         *this << constructRelativePath(dir, source);
0087     }
0088 
0089     if (m_mode == Kompare::ComparingFileString)
0090     {
0091         *this << QStringLiteral("-");
0092         m_customString = destination;
0093     }
0094     else
0095     {
0096         *this << constructRelativePath(dir, destination);
0097     }
0098 }
0099 
0100 void KompareProcess::writeDefaultCommandLine()
0101 {
0102     if (!m_diffSettings || m_diffSettings->m_diffProgram.isEmpty())
0103     {
0104         *this << QStringLiteral("diff") << QStringLiteral("-dr");
0105     }
0106     else
0107     {
0108         *this << m_diffSettings->m_diffProgram << QStringLiteral("-dr");
0109     }
0110 
0111     *this << QStringLiteral("-U") << QString::number(m_diffSettings->m_linesOfContext);
0112 }
0113 
0114 void KompareProcess::writeCommandLine()
0115 {
0116     // load the executable into the KProcess
0117     if (m_diffSettings->m_diffProgram.isEmpty())
0118     {
0119         qCDebug(LIBKOMPAREDIFF2) << "Using the first diff in the path...";
0120         *this << QStringLiteral("diff");
0121     }
0122     else
0123     {
0124         qCDebug(LIBKOMPAREDIFF2) << "Using a user specified diff, namely: " << m_diffSettings->m_diffProgram;
0125         *this << m_diffSettings->m_diffProgram;
0126     }
0127 
0128     switch (m_diffSettings->m_format) {
0129     case Kompare::Unified :
0130         *this << QStringLiteral("-U") << QString::number(m_diffSettings->m_linesOfContext);
0131         break;
0132     case Kompare::Context :
0133         *this << QStringLiteral("-C") << QString::number(m_diffSettings->m_linesOfContext);
0134         break;
0135     case Kompare::RCS :
0136         *this << QStringLiteral("-n");
0137         break;
0138     case Kompare::Ed :
0139         *this << QStringLiteral("-e");
0140         break;
0141     case Kompare::SideBySide:
0142         *this << QStringLiteral("-y");
0143         break;
0144     case Kompare::Normal :
0145     case Kompare::UnknownFormat :
0146     default:
0147         break;
0148     }
0149 
0150     if (m_diffSettings->m_largeFiles
0151 // default diff does not have -H on OpenBSD
0152 // so don't pass this option unless the user overrode the default program
0153 #if defined(__OpenBSD__)
0154             && !m_diffSettings->m_diffProgram.isEmpty()
0155 #endif
0156        )
0157     {
0158         *this << QStringLiteral("-H");
0159     }
0160 
0161     if (m_diffSettings->m_ignoreWhiteSpace)
0162     {
0163         *this << QStringLiteral("-b");
0164     }
0165 
0166     if (m_diffSettings->m_ignoreAllWhiteSpace)
0167     {
0168         *this << QStringLiteral("-w");
0169     }
0170 
0171     if (m_diffSettings->m_ignoreEmptyLines)
0172     {
0173         *this << QStringLiteral("-B");
0174     }
0175 
0176     if (m_diffSettings->m_ignoreChangesDueToTabExpansion)
0177     {
0178         *this << QStringLiteral("-E");
0179     }
0180 
0181     if (m_diffSettings->m_createSmallerDiff)
0182     {
0183         *this << QStringLiteral("-d");
0184     }
0185 
0186     if (m_diffSettings->m_ignoreChangesInCase)
0187     {
0188         *this << QStringLiteral("-i");
0189     }
0190 
0191     if (m_diffSettings->m_ignoreRegExp && !m_diffSettings->m_ignoreRegExpText.isEmpty())
0192     {
0193         *this << QStringLiteral("-I") << m_diffSettings->m_ignoreRegExpText;
0194     }
0195 
0196     if (m_diffSettings->m_showCFunctionChange)
0197     {
0198         *this << QStringLiteral("-p");
0199     }
0200 
0201     if (m_diffSettings->m_convertTabsToSpaces)
0202     {
0203         *this << QStringLiteral("-t");
0204     }
0205 
0206     if (m_diffSettings->m_recursive)
0207     {
0208         *this << QStringLiteral("-r");
0209     }
0210 
0211     if (m_diffSettings->m_newFiles)
0212     {
0213         *this << QStringLiteral("-N");
0214     }
0215 
0216 // This option is more trouble than it is worth... please do not ever enable it unless you want really weird crashes
0217 //  if ( m_diffSettings->m_allText )
0218 //  {
0219 //      *this << QStringLiteral("-a");
0220 //  }
0221 
0222     if (m_diffSettings->m_excludeFilePattern)
0223     {
0224         for (const QString &it :
0225              std::as_const(m_diffSettings->m_excludeFilePatternList)) {
0226           *this << QStringLiteral("-x") << it;
0227         }
0228     }
0229 
0230     if (m_diffSettings->m_excludeFilesFile && !m_diffSettings->m_excludeFilesFileURL.isEmpty())
0231     {
0232         *this << QStringLiteral("-X") << m_diffSettings->m_excludeFilesFileURL;
0233     }
0234 }
0235 
0236 KompareProcess::~KompareProcess()
0237 {
0238     delete m_textDecoder;
0239 }
0240 
0241 void KompareProcess::setEncoding(const QString& encoding)
0242 {
0243     if (!encoding.compare(QLatin1String("default"), Qt::CaseInsensitive))
0244     {
0245         m_codec = QTextCodec::codecForLocale();
0246         m_textDecoder = m_codec->makeDecoder();
0247     }
0248     else
0249     {
0250         m_codec = QTextCodec::codecForName(encoding.toUtf8());
0251         if (m_codec)
0252             m_textDecoder = m_codec->makeDecoder();
0253         else
0254         {
0255             qCDebug(LIBKOMPAREDIFF2) << "Using locale codec as backup...";
0256             m_codec = QTextCodec::codecForLocale();
0257             m_textDecoder = m_codec->makeDecoder();
0258         }
0259     }
0260 }
0261 
0262 void KompareProcess::start()
0263 {
0264 #ifndef NDEBUG
0265     QString cmdLine;
0266     QStringList program = KProcess::program();
0267     QStringList::ConstIterator it = program.constBegin();
0268     QStringList::ConstIterator end = program.constEnd();
0269     for (; it != end; ++it)
0270         cmdLine += QLatin1Char('\"') + (*it) + QLatin1String("\" ");
0271     qCDebug(LIBKOMPAREDIFF2) << cmdLine;
0272 #endif
0273     setOutputChannelMode(SeparateChannels);
0274     setNextOpenMode(QIODevice::ReadWrite);
0275     KProcess::start();
0276 
0277     //If we have a string to compare against input it now
0278     if ((m_mode == Kompare::ComparingStringFile) || (m_mode == Kompare::ComparingFileString))
0279         write(m_codec->fromUnicode(m_customString));
0280     closeWriteChannel();
0281 }
0282 
0283 void KompareProcess::slotFinished(int exitCode, QProcess::ExitStatus exitStatus)
0284 {
0285     // add all output to m_stdout/m_stderr
0286     if (m_textDecoder)
0287     {
0288         m_stdout = m_textDecoder->toUnicode(readAllStandardOutput());
0289         m_stderr = m_textDecoder->toUnicode(readAllStandardError());
0290     }
0291     else
0292         qCDebug(LIBKOMPAREDIFF2) << "KompareProcess::slotFinished : No decoder !!!";
0293 
0294     // exit code of 0: no differences
0295     //              1: some differences
0296     //              2: error but there may be differences !
0297     qCDebug(LIBKOMPAREDIFF2) << "Exited with exit code : " << exitCode;
0298     Q_EMIT diffHasFinished(exitStatus == NormalExit && exitCode != 0);
0299 }
0300 
0301 #include "moc_kompareprocess.cpp"