File indexing completed on 2024-05-05 04:38:43

0001 /*
0002     SPDX-FileCopyrightText: 2009 Andreas Pakulat <apaku@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "commandexecutor.h"
0008 
0009 #include "processlinemaker.h"
0010 
0011 #include <KProcess>
0012 #include <KShell>
0013 
0014 #include <QMap>
0015 #include <QStringList>
0016 #include <QString>
0017 
0018 namespace KDevelop {
0019 
0020 class CommandExecutorPrivate
0021 {
0022 public:
0023     explicit CommandExecutorPrivate(CommandExecutor* cmd)
0024         : m_exec(cmd)
0025         , m_useShell(false)
0026     {
0027     }
0028     CommandExecutor* m_exec;
0029     KProcess* m_process;
0030     ProcessLineMaker* m_lineMaker;
0031     QString m_command;
0032     QStringList m_args;
0033     QString m_workDir;
0034     QMap<QString, QString> m_env;
0035     bool m_useShell;
0036     void procError(QProcess::ProcessError error)
0037     {
0038         Q_UNUSED(error)
0039         m_lineMaker->flushBuffers();
0040         emit m_exec->failed(error);
0041     }
0042     void procFinished(int code, QProcess::ExitStatus status)
0043     {
0044         m_lineMaker->flushBuffers();
0045         if (status == QProcess::NormalExit)
0046             emit m_exec->completed(code);
0047     }
0048 };
0049 
0050 CommandExecutor::CommandExecutor(const QString& command, QObject* parent)
0051     : QObject(parent)
0052     , d_ptr(new CommandExecutorPrivate(this))
0053 {
0054     Q_D(CommandExecutor);
0055 
0056     d->m_process = new KProcess(this);
0057     d->m_process->setOutputChannelMode(KProcess::SeparateChannels);
0058     d->m_lineMaker = new ProcessLineMaker(d->m_process);
0059     d->m_command = command;
0060     connect(d->m_lineMaker, &ProcessLineMaker::receivedStdoutLines,
0061             this, &CommandExecutor::receivedStandardOutput);
0062     connect(d->m_lineMaker, &ProcessLineMaker::receivedStderrLines,
0063             this, &CommandExecutor::receivedStandardError);
0064     connect(d->m_process, &QProcess::errorOccurred,
0065             this, [this](QProcess::ProcessError error) {
0066             Q_D(CommandExecutor);
0067             d->procError(error);
0068         });
0069     connect(d->m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
0070             this, [this](int code, QProcess::ExitStatus status) {
0071             Q_D(CommandExecutor);
0072             d->procFinished(code, status);
0073         });
0074 }
0075 
0076 CommandExecutor::~CommandExecutor()
0077 {
0078     Q_D(CommandExecutor);
0079 
0080     delete d->m_process;
0081     delete d->m_lineMaker;
0082 }
0083 
0084 void CommandExecutor::setEnvironment(const QMap<QString, QString>& env)
0085 {
0086     Q_D(CommandExecutor);
0087 
0088     d->m_env = env;
0089 }
0090 
0091 void CommandExecutor::setEnvironment(const QStringList& env)
0092 {
0093     Q_D(CommandExecutor);
0094 
0095     QMap<QString, QString> envmap;
0096     for (const QString& var : env) {
0097         int sep = var.indexOf(QLatin1Char('='));
0098         envmap.insert(var.left(sep), var.mid(sep + 1));
0099     }
0100 
0101     d->m_env = envmap;
0102 }
0103 
0104 void CommandExecutor::setArguments(const QStringList& args)
0105 {
0106     Q_D(CommandExecutor);
0107 
0108     d->m_args = args;
0109 }
0110 
0111 void CommandExecutor::setWorkingDirectory(const QString& dir)
0112 {
0113     Q_D(CommandExecutor);
0114 
0115     d->m_workDir = dir;
0116 }
0117 
0118 bool CommandExecutor::useShell() const
0119 {
0120     Q_D(const CommandExecutor);
0121 
0122     return d->m_useShell;
0123 }
0124 
0125 void CommandExecutor::setUseShell(bool shell)
0126 {
0127     Q_D(CommandExecutor);
0128 
0129     d->m_useShell = shell;
0130 }
0131 
0132 void CommandExecutor::start()
0133 {
0134     Q_D(CommandExecutor);
0135 
0136     for (auto it = d->m_env.constBegin(), itEnd = d->m_env.constEnd(); it != itEnd; ++it) {
0137         d->m_process->setEnv(it.key(), it.value());
0138     }
0139 
0140     d->m_process->setWorkingDirectory(d->m_workDir);
0141     if (!d->m_useShell) {
0142         d->m_process->setProgram(d->m_command, d->m_args);
0143     } else {
0144         QStringList arguments;
0145         arguments.reserve(d->m_args.size());
0146         for (const QString& a : qAsConst(d->m_args)) {
0147             arguments << KShell::quoteArg(a);
0148         }
0149 
0150         d->m_process->setShellCommand(d->m_command + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')));
0151     }
0152 
0153     d->m_process->start();
0154 }
0155 
0156 void CommandExecutor::setCommand(const QString& command)
0157 {
0158     Q_D(CommandExecutor);
0159 
0160     d->m_command = command;
0161 }
0162 
0163 void CommandExecutor::kill()
0164 {
0165     Q_D(CommandExecutor);
0166 
0167     d->m_process->kill();
0168 }
0169 
0170 QString CommandExecutor::command() const
0171 {
0172     Q_D(const CommandExecutor);
0173 
0174     return d->m_command;
0175 }
0176 
0177 QStringList CommandExecutor::arguments() const
0178 {
0179     Q_D(const CommandExecutor);
0180 
0181     return d->m_args;
0182 }
0183 
0184 QString CommandExecutor::workingDirectory() const
0185 {
0186     Q_D(const CommandExecutor);
0187 
0188     return d->m_workDir;
0189 }
0190 
0191 }
0192 
0193 #include "moc_commandexecutor.cpp"