File indexing completed on 2024-04-21 05:44:09

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