File indexing completed on 2025-01-19 04:23:27

0001 /*
0002  * This file is a part of QTerminal - http://gitorious.org/qterminal
0003  *
0004  * This file was un-linked from KDE and modified
0005  * by Maxim Bourmistrov <maxim@unixconn.com>
0006  *
0007  */
0008 
0009 /*
0010     This file is part of the KDE libraries
0011 
0012     Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>
0013 
0014     This library is free software; you can redistribute it and/or
0015     modify it under the terms of the GNU Library General Public
0016     License as published by the Free Software Foundation; either
0017     version 2 of the License, or (at your option) any later version.
0018 
0019     This library is distributed in the hope that it will be useful,
0020     but WITHOUT ANY WARRANTY; without even the implied warranty of
0021     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0022     Library General Public License for more details.
0023 
0024     You should have received a copy of the GNU Library General Public License
0025     along with this library; see the file COPYING.LIB.  If not, write to
0026     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0027     Boston, MA 02110-1301, USA.
0028 */
0029 
0030 #include "kprocess.h"
0031 
0032 #include <qfile.h>
0033 
0034 #ifdef Q_OS_WIN
0035 # include <windows.h>
0036 #else
0037 # include <unistd.h>
0038 # include <errno.h>
0039 #endif
0040 
0041 #ifndef Q_OS_WIN
0042 # define STD_OUTPUT_HANDLE 1
0043 # define STD_ERROR_HANDLE 2
0044 #endif
0045 
0046 #ifdef _WIN32_WCE
0047 #include <stdio.h>
0048 #endif
0049 
0050 void KProcessPrivate::writeAll(const QByteArray &buf, int fd)
0051 {
0052 #ifdef Q_OS_WIN
0053 #ifndef _WIN32_WCE
0054     HANDLE h = GetStdHandle(fd);
0055     if (h) {
0056         DWORD wr;
0057         WriteFile(h, buf.data(), buf.size(), &wr, 0);
0058     }
0059 #else
0060     fwrite(buf.data(), 1, buf.size(), (FILE*)fd);
0061 #endif
0062 #else
0063     int off = 0;
0064     do {
0065         int ret = ::write(fd, buf.data() + off, buf.size() - off);
0066         if (ret < 0) {
0067             if (errno != EINTR)
0068                 return;
0069         } else {
0070             off += ret;
0071         }
0072     } while (off < buf.size());
0073 #endif
0074 }
0075 
0076 void KProcessPrivate::forwardStd(KProcess::ProcessChannel good, int fd)
0077 {
0078     Q_Q(KProcess);
0079 
0080     QProcess::ProcessChannel oc = q->readChannel();
0081     q->setReadChannel(good);
0082     writeAll(q->readAll(), fd);
0083     q->setReadChannel(oc);
0084 }
0085 
0086 void KProcessPrivate::_k_forwardStdout()
0087 {
0088 #ifndef _WIN32_WCE
0089     forwardStd(KProcess::StandardOutput, STD_OUTPUT_HANDLE);
0090 #else
0091     forwardStd(KProcess::StandardOutput, (int)stdout);
0092 #endif
0093 }
0094 
0095 void KProcessPrivate::_k_forwardStderr()
0096 {
0097 #ifndef _WIN32_WCE
0098     forwardStd(KProcess::StandardError, STD_ERROR_HANDLE);
0099 #else
0100     forwardStd(KProcess::StandardError, (int)stderr);
0101 #endif
0102 }
0103 
0104 /////////////////////////////
0105 // public member functions //
0106 /////////////////////////////
0107 
0108 KProcess::KProcess(QObject *parent) :
0109     QProcess(parent),
0110     d_ptr(new KProcessPrivate)
0111 {
0112     d_ptr->q_ptr = this;
0113     setOutputChannelMode(ForwardedChannels);
0114 }
0115 
0116 KProcess::KProcess(KProcessPrivate *d, QObject *parent) :
0117     QProcess(parent),
0118     d_ptr(d)
0119 {
0120     d_ptr->q_ptr = this;
0121     setOutputChannelMode(ForwardedChannels);
0122 }
0123 
0124 KProcess::~KProcess()
0125 {
0126     delete d_ptr;
0127 }
0128 
0129 void KProcess::setOutputChannelMode(OutputChannelMode mode)
0130 {
0131     Q_D(KProcess);
0132 
0133     d->outputChannelMode = mode;
0134     disconnect(this, SIGNAL(readyReadStandardOutput()));
0135     disconnect(this, SIGNAL(readyReadStandardError()));
0136     switch (mode) {
0137     case OnlyStdoutChannel:
0138         connect(this, SIGNAL(readyReadStandardError()), SLOT(_k_forwardStderr()));
0139         break;
0140     case OnlyStderrChannel:
0141         connect(this, SIGNAL(readyReadStandardOutput()), SLOT(_k_forwardStdout()));
0142         break;
0143     default:
0144         QProcess::setProcessChannelMode((ProcessChannelMode)mode);
0145         return;
0146     }
0147     QProcess::setProcessChannelMode(QProcess::SeparateChannels);
0148 }
0149 
0150 KProcess::OutputChannelMode KProcess::outputChannelMode() const
0151 {
0152     Q_D(const KProcess);
0153 
0154     return d->outputChannelMode;
0155 }
0156 
0157 void KProcess::setNextOpenMode(QIODevice::OpenMode mode)
0158 {
0159     Q_D(KProcess);
0160 
0161     d->openMode = mode;
0162 }
0163 
0164 #define DUMMYENV "_KPROCESS_DUMMY_="
0165 
0166 void KProcess::clearEnvironment()
0167 {
0168     setEnvironment(QStringList() << QString::fromLatin1(DUMMYENV));
0169 }
0170 
0171 void KProcess::setEnv(const QString &name, const QString &value, bool overwrite)
0172 {
0173     QStringList env = environment();
0174     if (env.isEmpty()) {
0175         env = systemEnvironment();
0176         env.removeAll(QString::fromLatin1(DUMMYENV));
0177     }
0178     QString fname(name);
0179     fname.append(QLatin1Char('='));
0180     for (QStringList::Iterator it = env.begin(); it != env.end(); ++it)
0181         if ((*it).startsWith(fname)) {
0182             if (overwrite) {
0183                 *it = fname.append(value);
0184                 setEnvironment(env);
0185             }
0186             return;
0187         }
0188     env.append(fname.append(value));
0189     setEnvironment(env);
0190 }
0191 
0192 void KProcess::unsetEnv(const QString &name)
0193 {
0194     QStringList env = environment();
0195     if (env.isEmpty()) {
0196         env = systemEnvironment();
0197         env.removeAll(QString::fromLatin1(DUMMYENV));
0198     }
0199     QString fname(name);
0200     fname.append(QLatin1Char('='));
0201     for (QStringList::Iterator it = env.begin(); it != env.end(); ++it)
0202         if ((*it).startsWith(fname)) {
0203             env.erase(it);
0204             if (env.isEmpty())
0205                 env.append(QString::fromLatin1(DUMMYENV));
0206             setEnvironment(env);
0207             return;
0208         }
0209 }
0210 
0211 void KProcess::setProgram(const QString &exe, const QStringList &args)
0212 {
0213     Q_D(KProcess);
0214 
0215     d->prog = exe;
0216     d->args = args;
0217 #ifdef Q_OS_WIN
0218     setNativeArguments(QString());
0219 #endif
0220 }
0221 
0222 void KProcess::setProgram(const QStringList &argv)
0223 {
0224     Q_D(KProcess);
0225 
0226     Q_ASSERT( !argv.isEmpty() );
0227     d->args = argv;
0228     d->prog = d->args.takeFirst();
0229 #ifdef Q_OS_WIN
0230     setNativeArguments(QString());
0231 #endif
0232 }
0233 
0234 KProcess &KProcess::operator<<(const QString &arg)
0235 {
0236     Q_D(KProcess);
0237 
0238     if (d->prog.isEmpty())
0239         d->prog = arg;
0240     else
0241         d->args << arg;
0242     return *this;
0243 }
0244 
0245 KProcess &KProcess::operator<<(const QStringList &args)
0246 {
0247     Q_D(KProcess);
0248 
0249     if (d->prog.isEmpty())
0250         setProgram(args);
0251     else
0252         d->args << args;
0253     return *this;
0254 }
0255 
0256 void KProcess::clearProgram()
0257 {
0258     Q_D(KProcess);
0259 
0260     d->prog.clear();
0261     d->args.clear();
0262 #ifdef Q_OS_WIN
0263     setNativeArguments(QString());
0264 #endif
0265 }
0266 
0267 #if 0
0268 void KProcess::setShellCommand(const QString &cmd)
0269 {
0270     Q_D(KProcess);
0271 
0272     KShell::Errors err;
0273     d->args = KShell::splitArgs(
0274             cmd, KShell::AbortOnMeta | KShell::TildeExpand, &err);
0275     if (err == KShell::NoError && !d->args.isEmpty()) {
0276         d->prog = KStandardDirs::findExe(d->args[0]);
0277         if (!d->prog.isEmpty()) {
0278             d->args.removeFirst();
0279 #ifdef Q_OS_WIN
0280             setNativeArguments(QString());
0281 #endif
0282             return;
0283         }
0284     }
0285 
0286     d->args.clear();
0287 
0288 #ifdef Q_OS_UNIX
0289 // #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh
0290 # if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__GNU__)
0291     // If /bin/sh is a symlink, we can be pretty sure that it points to a
0292     // POSIX shell - the original bourne shell is about the only non-POSIX
0293     // shell still in use and it is always installed natively as /bin/sh.
0294     d->prog = QFile::symLinkTarget(QString::fromLatin1("/bin/sh"));
0295     if (d->prog.isEmpty()) {
0296         // Try some known POSIX shells.
0297         d->prog = KStandardDirs::findExe(QString::fromLatin1("ksh"));
0298         if (d->prog.isEmpty()) {
0299             d->prog = KStandardDirs::findExe(QString::fromLatin1("ash"));
0300             if (d->prog.isEmpty()) {
0301                 d->prog = KStandardDirs::findExe(QString::fromLatin1("bash"));
0302                 if (d->prog.isEmpty()) {
0303                     d->prog = KStandardDirs::findExe(QString::fromLatin1("zsh"));
0304                     if (d->prog.isEmpty())
0305                         // We're pretty much screwed, to be honest ...
0306                         d->prog = QString::fromLatin1("/bin/sh");
0307                 }
0308             }
0309         }
0310     }
0311 # else
0312     d->prog = QString::fromLatin1("/bin/sh");
0313 # endif
0314 
0315     d->args << QString::fromLatin1("-c") << cmd;
0316 #else // Q_OS_UNIX
0317     // KMacroExpander::expandMacrosShellQuote(), KShell::quoteArg() and
0318     // KShell::joinArgs() may generate these for security reasons.
0319     setEnv(PERCENT_VARIABLE, QLatin1String("%"));
0320 
0321 #ifndef _WIN32_WCE
0322     WCHAR sysdir[MAX_PATH + 1];
0323     UINT size = GetSystemDirectoryW(sysdir, MAX_PATH + 1);
0324     d->prog = QString::fromUtf16((const ushort *) sysdir, size);
0325     d->prog += QLatin1String("\\cmd.exe");
0326     setNativeArguments(QLatin1String("/V:OFF /S /C \"") + cmd + QLatin1Char('"'));
0327 #else
0328     d->prog = QLatin1String("\\windows\\cmd.exe");
0329     setNativeArguments(QLatin1String("/S /C \"") + cmd + QLatin1Char('"'));
0330 #endif
0331 #endif
0332 }
0333 #endif
0334 QStringList KProcess::program() const
0335 {
0336     Q_D(const KProcess);
0337 
0338     QStringList argv = d->args;
0339     argv.prepend(d->prog);
0340     return argv;
0341 }
0342 
0343 void KProcess::start()
0344 {
0345     Q_D(KProcess);
0346 
0347     QProcess::start(d->prog, d->args, d->openMode);
0348 }
0349 
0350 int KProcess::execute(int msecs)
0351 {
0352     start();
0353     if (!waitForFinished(msecs)) {
0354         kill();
0355         waitForFinished(-1);
0356         return -2;
0357     }
0358     return (exitStatus() == QProcess::NormalExit) ? exitCode() : -1;
0359 }
0360 
0361 // static
0362 int KProcess::execute(const QString &exe, const QStringList &args, int msecs)
0363 {
0364     KProcess p;
0365     p.setProgram(exe, args);
0366     return p.execute(msecs);
0367 }
0368 
0369 // static
0370 int KProcess::execute(const QStringList &argv, int msecs)
0371 {
0372     KProcess p;
0373     p.setProgram(argv);
0374     return p.execute(msecs);
0375 }
0376 
0377 int KProcess::startDetached()
0378 {
0379     Q_D(KProcess);
0380 
0381     qint64 pid;
0382     if (!QProcess::startDetached(d->prog, d->args, workingDirectory(), &pid))
0383         return 0;
0384     return (int) pid;
0385 }
0386 
0387 // static
0388 int KProcess::startDetached(const QString &exe, const QStringList &args)
0389 {
0390     qint64 pid;
0391     if (!QProcess::startDetached(exe, args, QString(), &pid))
0392         return 0;
0393     return (int) pid;
0394 }
0395 
0396 // static
0397 int KProcess::startDetached(const QStringList &argv)
0398 {
0399     QStringList args = argv;
0400     QString prog = args.takeFirst();
0401     return startDetached(prog, args);
0402 }
0403 
0404 int KProcess::pid() const
0405 {
0406 #ifdef Q_OS_UNIX
0407     return (int) QProcess::processId();
0408 #else
0409     return QProcess::pid() ? QProcess::pid()->dwProcessId : 0;
0410 #endif
0411 }
0412