diff options
Diffstat (limited to 'src/corelib/io/qprocess.cpp')
-rw-r--r-- | src/corelib/io/qprocess.cpp | 2371 |
1 files changed, 2371 insertions, 0 deletions
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp new file mode 100644 index 0000000000..a45225f3f7 --- /dev/null +++ b/src/corelib/io/qprocess.cpp @@ -0,0 +1,2371 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QPROCESS_DEBUG + +#if defined QPROCESS_DEBUG +#include <qdebug.h> +#include <qstring.h> +#include <ctype.h> +#if !defined(Q_OS_WINCE) +#include <errno.h> +#endif + +QT_BEGIN_NAMESPACE +/* + Returns a human readable representation of the first \a len + characters in \a data. +*/ +static QByteArray qt_prettyDebug(const char *data, int len, int maxSize) +{ + if (!data) return "(null)"; + QByteArray out; + for (int i = 0; i < len && i < maxSize; ++i) { + char c = data[i]; + if (isprint(c)) { + out += c; + } else switch (c) { + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: + char buf[5]; + qsnprintf(buf, sizeof(buf), "\\%3o", c); + buf[4] = '\0'; + out += QByteArray(buf); + } + } + + if (len < maxSize) + out += "..."; + + return out; +} + +QT_END_NAMESPACE + +#endif + +#include "qprocess.h" +#include "qprocess_p.h" + +#include <qbytearray.h> +#include <qelapsedtimer.h> +#include <qcoreapplication.h> +#include <qsocketnotifier.h> +#include <qtimer.h> + +#ifdef Q_WS_WIN +#include <private/qwineventnotifier_p.h> +#endif + +#ifdef Q_OS_SYMBIAN +#include <e32std.h> +#endif + +#ifndef QT_NO_PROCESS + +QT_BEGIN_NAMESPACE + +/*! + \class QProcessEnvironment + + \brief The QProcessEnvironment class holds the environment variables that + can be passed to a program. + + \ingroup io + \ingroup misc + \mainclass + \reentrant + \since 4.6 + + A process's environment is composed of a set of key=value pairs known as + environment variables. The QProcessEnvironment class wraps that concept + and allows easy manipulation of those variables. It's meant to be used + along with QProcess, to set the environment for child processes. It + cannot be used to change the current process's environment. + + The environment of the calling process can be obtained using + QProcessEnvironment::systemEnvironment(). + + On Unix systems, the variable names are case-sensitive. For that reason, + this class will not touch the names of the variables. Note as well that + Unix environment allows both variable names and contents to contain arbitrary + binary data (except for the NUL character), but this is not supported by + QProcessEnvironment. This class only supports names and values that are + encodable by the current locale settings (see QTextCodec::codecForLocale). + + On Windows, the variable names are case-insensitive. Therefore, + QProcessEnvironment will always uppercase the names and do case-insensitive + comparisons. + + On Windows CE, the concept of environment does not exist. This class will + keep the values set for compatibility with other platforms, but the values + set will have no effect on the processes being created. + + \sa QProcess, QProcess::systemEnvironment(), QProcess::setProcessEnvironment() +*/ +#ifdef Q_OS_WIN +static inline QProcessEnvironmentPrivate::Unit prepareName(const QString &name) +{ return name.toUpper(); } +static inline QProcessEnvironmentPrivate::Unit prepareName(const QByteArray &name) +{ return QString::fromLocal8Bit(name).toUpper(); } +static inline QString nameToString(const QProcessEnvironmentPrivate::Unit &name) +{ return name; } +static inline QProcessEnvironmentPrivate::Unit prepareValue(const QString &value) +{ return value; } +static inline QProcessEnvironmentPrivate::Unit prepareValue(const QByteArray &value) +{ return QString::fromLocal8Bit(value); } +static inline QString valueToString(const QProcessEnvironmentPrivate::Unit &value) +{ return value; } +static inline QByteArray valueToByteArray(const QProcessEnvironmentPrivate::Unit &value) +{ return value.toLocal8Bit(); } +#else +static inline QProcessEnvironmentPrivate::Unit prepareName(const QByteArray &name) +{ return name; } +static inline QProcessEnvironmentPrivate::Unit prepareName(const QString &name) +{ return name.toLocal8Bit(); } +static inline QString nameToString(const QProcessEnvironmentPrivate::Unit &name) +{ return QString::fromLocal8Bit(name); } +static inline QProcessEnvironmentPrivate::Unit prepareValue(const QByteArray &value) +{ return value; } +static inline QProcessEnvironmentPrivate::Unit prepareValue(const QString &value) +{ return value.toLocal8Bit(); } +static inline QString valueToString(const QProcessEnvironmentPrivate::Unit &value) +{ return QString::fromLocal8Bit(value); } +static inline QByteArray valueToByteArray(const QProcessEnvironmentPrivate::Unit &value) +{ return value; } +#endif + +template<> void QSharedDataPointer<QProcessEnvironmentPrivate>::detach() +{ + if (d && d->ref == 1) + return; + QProcessEnvironmentPrivate *x = (d ? new QProcessEnvironmentPrivate(*d) + : new QProcessEnvironmentPrivate); + x->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = x; +} + +QStringList QProcessEnvironmentPrivate::toList() const +{ + QStringList result; + QHash<Unit, Unit>::ConstIterator it = hash.constBegin(), + end = hash.constEnd(); + for ( ; it != end; ++it) { + QString data = nameToString(it.key()); + QString value = valueToString(it.value()); + data.reserve(data.length() + value.length() + 1); + data.append(QLatin1Char('=')); + data.append(value); + result << data; + } + return result; +} + +QProcessEnvironment QProcessEnvironmentPrivate::fromList(const QStringList &list) +{ + QProcessEnvironment env; + QStringList::ConstIterator it = list.constBegin(), + end = list.constEnd(); + for ( ; it != end; ++it) { + int pos = it->indexOf(QLatin1Char('=')); + if (pos < 1) + continue; + + QString value = it->mid(pos + 1); + QString name = *it; + name.truncate(pos); + env.insert(name, value); + } + return env; +} + +QStringList QProcessEnvironmentPrivate::keys() const +{ + QStringList result; + QHash<Unit, Unit>::ConstIterator it = hash.constBegin(), + end = hash.constEnd(); + for ( ; it != end; ++it) + result << nameToString(it.key()); + return result; +} + +void QProcessEnvironmentPrivate::insert(const Hash &h) +{ + QHash<Unit, Unit>::ConstIterator it = h.constBegin(), + end = h.constEnd(); + for ( ; it != end; ++it) + hash.insert(it.key(), it.value()); +} + +/*! + Creates a new QProcessEnvironment object. This constructor creates an + empty environment. If set on a QProcess, this will cause the current + environment variables to be removed. +*/ +QProcessEnvironment::QProcessEnvironment() + : d(0) +{ +} + +/*! + Frees the resources associated with this QProcessEnvironment object. +*/ +QProcessEnvironment::~QProcessEnvironment() +{ +} + +/*! + Creates a QProcessEnvironment object that is a copy of \a other. +*/ +QProcessEnvironment::QProcessEnvironment(const QProcessEnvironment &other) + : d(other.d) +{ +} + +/*! + Copies the contents of the \a other QProcessEnvironment object into this + one. +*/ +QProcessEnvironment &QProcessEnvironment::operator=(const QProcessEnvironment &other) +{ + d = other.d; + return *this; +} + +/*! + \fn bool QProcessEnvironment::operator !=(const QProcessEnvironment &other) const + + Returns true if this and the \a other QProcessEnvironment objects are different. + + \sa operator==() +*/ + +/*! + Returns true if this and the \a other QProcessEnvironment objects are equal. + + Two QProcessEnvironment objects are considered equal if they have the same + set of key=value pairs. The comparison of keys is done case-sensitive on + platforms where the environment is case-sensitive. + + \sa operator!=(), contains() +*/ +bool QProcessEnvironment::operator==(const QProcessEnvironment &other) const +{ + return d == other.d || (d && other.d && d->hash == other.d->hash); +} + +/*! + Returns true if this QProcessEnvironment object is empty: that is + there are no key=value pairs set. + + \sa clear(), systemEnvironment(), insert() +*/ +bool QProcessEnvironment::isEmpty() const +{ + return d ? d->hash.isEmpty() : true; +} + +/*! + Removes all key=value pairs from this QProcessEnvironment object, making + it empty. + + \sa isEmpty(), systemEnvironment() +*/ +void QProcessEnvironment::clear() +{ + if (d) + d->hash.clear(); +} + +/*! + Returns true if the environment variable of name \a name is found in + this QProcessEnvironment object. + + On Windows, variable names are case-insensitive, so the key is converted + to uppercase before searching. On other systems, names are case-sensitive + so no trasformation is applied. + + \sa insert(), value() +*/ +bool QProcessEnvironment::contains(const QString &name) const +{ + return d ? d->hash.contains(prepareName(name)) : false; +} + +/*! + Inserts the environment variable of name \a name and contents \a value + into this QProcessEnvironment object. If that variable already existed, + it is replaced by the new value. + + On Windows, variable names are case-insensitive, so this function always + uppercases the variable name before inserting. On other systems, names + are case-sensitive, so no transformation is applied. + + On most systems, inserting a variable with no contents will have the + same effect for applications as if the variable had not been set at all. + However, to guarantee that there are no incompatibilities, to remove a + variable, please use the remove() function. + + \sa contains(), remove(), value() +*/ +void QProcessEnvironment::insert(const QString &name, const QString &value) +{ + // d detaches from null + d->hash.insert(prepareName(name), prepareValue(value)); +} + +/*! + Removes the environment variable identified by \a name from this + QProcessEnvironment object. If that variable did not exist before, + nothing happens. + + On Windows, variable names are case-insensitive, so the key is converted + to uppercase before searching. On other systems, names are case-sensitive + so no trasformation is applied. + + \sa contains(), insert(), value() +*/ +void QProcessEnvironment::remove(const QString &name) +{ + if (d) + d->hash.remove(prepareName(name)); +} + +/*! + Searches this QProcessEnvironment object for a variable identified by + \a name and returns its value. If the variable is not found in this object, + then \a defaultValue is returned instead. + + On Windows, variable names are case-insensitive, so the key is converted + to uppercase before searching. On other systems, names are case-sensitive + so no trasformation is applied. + + \sa contains(), insert(), remove() +*/ +QString QProcessEnvironment::value(const QString &name, const QString &defaultValue) const +{ + if (!d) + return defaultValue; + + QProcessEnvironmentPrivate::Hash::ConstIterator it = d->hash.constFind(prepareName(name)); + if (it == d->hash.constEnd()) + return defaultValue; + + return valueToString(it.value()); +} + +/*! + Converts this QProcessEnvironment object into a list of strings, one for + each environment variable that is set. The environment variable's name + and its value are separated by an equal character ('='). + + The QStringList contents returned by this function are suitable for use + with the QProcess::setEnvironment function. However, it is recommended + to use QProcess::setProcessEnvironment instead since that will avoid + unnecessary copying of the data. + + \sa systemEnvironment(), QProcess::systemEnvironment(), QProcess::environment(), + QProcess::setEnvironment() +*/ +QStringList QProcessEnvironment::toStringList() const +{ + return d ? d->toList() : QStringList(); +} + +/*! + \since 4.8 + + Returns a list containing all the variable names in this QProcessEnvironment + object. +*/ +QStringList QProcessEnvironment::keys() const +{ + return d ? d->keys() : QStringList(); +} + +/*! + \overload + \since 4.8 + + Inserts the contents of \a e in this QProcessEnvironment object. Variables in + this object that also exist in \a e will be overwritten. +*/ +void QProcessEnvironment::insert(const QProcessEnvironment &e) +{ + if (!e.d) + return; + + // d detaches from null + d->insert(e.d->hash); +} + +void QProcessPrivate::Channel::clear() +{ + switch (type) { + case PipeSource: + Q_ASSERT(process); + process->stdinChannel.type = Normal; + process->stdinChannel.process = 0; + break; + case PipeSink: + Q_ASSERT(process); + process->stdoutChannel.type = Normal; + process->stdoutChannel.process = 0; + break; + } + + type = Normal; + file.clear(); + process = 0; +} + +/*! \fn bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) + +\internal + */ + +/*! + \class QProcess + + \brief The QProcess class is used to start external programs and + to communicate with them. + + \ingroup io + + \reentrant + + \section1 Running a Process + + To start a process, pass the name and command line arguments of + the program you want to run as arguments to start(). Arguments + are supplied as individual strings in a QStringList. + + For example, the following code snippet runs the analog clock + example in the Motif style on X11 platforms by passing strings + containing "-style" and "motif" as two items in the list of + arguments: + + \snippet doc/src/snippets/qprocess/qprocess-simpleexecution.cpp 0 + \dots + \snippet doc/src/snippets/qprocess/qprocess-simpleexecution.cpp 1 + \snippet doc/src/snippets/qprocess/qprocess-simpleexecution.cpp 2 + + QProcess then enters the \l Starting state, and when the program + has started, QProcess enters the \l Running state and emits + started(). + + QProcess allows you to treat a process as a sequential I/O + device. You can write to and read from the process just as you + would access a network connection using QTcpSocket. You can then + write to the process's standard input by calling write(), and + read the standard output by calling read(), readLine(), and + getChar(). Because it inherits QIODevice, QProcess can also be + used as an input source for QXmlReader, or for generating data to + be uploaded using QFtp. + + \note On Windows CE and Symbian, reading and writing to a process + is not supported. + + When the process exits, QProcess reenters the \l NotRunning state + (the initial state), and emits finished(). + + The finished() signal provides the exit code and exit status of + the process as arguments, and you can also call exitCode() to + obtain the exit code of the last process that finished, and + exitStatus() to obtain its exit status. If an error occurs at + any point in time, QProcess will emit the error() signal. You + can also call error() to find the type of error that occurred + last, and state() to find the current process state. + + \section1 Communicating via Channels + + Processes have two predefined output channels: The standard + output channel (\c stdout) supplies regular console output, and + the standard error channel (\c stderr) usually supplies the + errors that are printed by the process. These channels represent + two separate streams of data. You can toggle between them by + calling setReadChannel(). QProcess emits readyRead() when data is + available on the current read channel. It also emits + readyReadStandardOutput() when new standard output data is + available, and when new standard error data is available, + readyReadStandardError() is emitted. Instead of calling read(), + readLine(), or getChar(), you can explicitly read all data from + either of the two channels by calling readAllStandardOutput() or + readAllStandardError(). + + The terminology for the channels can be misleading. Be aware that + the process's output channels correspond to QProcess's + \e read channels, whereas the process's input channels correspond + to QProcess's \e write channels. This is because what we read + using QProcess is the process's output, and what we write becomes + the process's input. + + QProcess can merge the two output channels, so that standard + output and standard error data from the running process both use + the standard output channel. Call setProcessChannelMode() with + MergedChannels before starting the process to activative + this feature. You also have the option of forwarding the output of + the running process to the calling, main process, by passing + ForwardedChannels as the argument. + + Certain processes need special environment settings in order to + operate. You can set environment variables for your process by + calling setEnvironment(). To set a working directory, call + setWorkingDirectory(). By default, processes are run in the + current working directory of the calling process. + + \note On Symbian, setting environment or working directory + is not supported. The working directory will always be the private + directory of the running process. + + \section1 Synchronous Process API + + QProcess provides a set of functions which allow it to be used + without an event loop, by suspending the calling thread until + certain signals are emitted: + + \list + \o waitForStarted() blocks until the process has started. + + \o waitForReadyRead() blocks until new data is + available for reading on the current read channel. + + \o waitForBytesWritten() blocks until one payload of + data has been written to the process. + + \o waitForFinished() blocks until the process has finished. + \endlist + + Calling these functions from the main thread (the thread that + calls QApplication::exec()) may cause your user interface to + freeze. + + The following example runs \c gzip to compress the string "Qt + rocks!", without an event loop: + + \snippet doc/src/snippets/process/process.cpp 0 + + \section1 Notes for Windows Users + + Some Windows commands (for example, \c dir) are not provided by + separate applications, but by the command interpreter itself. + If you attempt to use QProcess to execute these commands directly, + it won't work. One possible solution is to execute the command + interpreter itself (\c{cmd.exe} on some Windows systems), and ask + the interpreter to execute the desired command. + + \section1 Symbian Platform Security Requirements + + On Symbian, processes which use the functions kill() or terminate() + must have the \c PowerMgmt platform security capability. If the client + process lacks this capability, these functions will fail. + + Platform security capabilities are added via the + \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY} + qmake variable. + + \sa QBuffer, QFile, QTcpSocket +*/ + +/*! + \enum QProcess::ProcessChannel + + This enum describes the process channels used by the running process. + Pass one of these values to setReadChannel() to set the + current read channel of QProcess. + + \value StandardOutput The standard output (stdout) of the running + process. + + \value StandardError The standard error (stderr) of the running + process. + + \sa setReadChannel() +*/ + +/*! + \enum QProcess::ProcessChannelMode + + This enum describes the process channel modes of QProcess. Pass + one of these values to setProcessChannelMode() to set the + current read channel mode. + + \value SeparateChannels QProcess manages the output of the + running process, keeping standard output and standard error data + in separate internal buffers. You can select the QProcess's + current read channel by calling setReadChannel(). This is the + default channel mode of QProcess. + + \value MergedChannels QProcess merges the output of the running + process into the standard output channel (\c stdout). The + standard error channel (\c stderr) will not receive any data. The + standard output and standard error data of the running process + are interleaved. + + \value ForwardedChannels QProcess forwards the output of the + running process onto the main process. Anything the child process + writes to its standard output and standard error will be written + to the standard output and standard error of the main process. + + \sa setProcessChannelMode() +*/ + +/*! + \enum QProcess::ProcessError + + This enum describes the different types of errors that are + reported by QProcess. + + \value FailedToStart The process failed to start. Either the + invoked program is missing, or you may have insufficient + permissions to invoke the program. + + \value Crashed The process crashed some time after starting + successfully. + + \value Timedout The last waitFor...() function timed out. The + state of QProcess is unchanged, and you can try calling + waitFor...() again. + + \value WriteError An error occurred when attempting to write to the + process. For example, the process may not be running, or it may + have closed its input channel. + + \value ReadError An error occurred when attempting to read from + the process. For example, the process may not be running. + + \value UnknownError An unknown error occurred. This is the default + return value of error(). + + \sa error() +*/ + +/*! + \enum QProcess::ProcessState + + This enum describes the different states of QProcess. + + \value NotRunning The process is not running. + + \value Starting The process is starting, but the program has not + yet been invoked. + + \value Running The process is running and is ready for reading and + writing. + + \sa state() +*/ + +/*! + \enum QProcess::ExitStatus + + This enum describes the different exit statuses of QProcess. + + \value NormalExit The process exited normally. + + \value CrashExit The process crashed. + + \sa exitStatus() +*/ + +/*! + \fn void QProcess::error(QProcess::ProcessError error) + + This signal is emitted when an error occurs with the process. The + specified \a error describes the type of error that occurred. +*/ + +/*! + \fn void QProcess::started() + + This signal is emitted by QProcess when the process has started, + and state() returns \l Running. +*/ + +/*! + \fn void QProcess::stateChanged(QProcess::ProcessState newState) + + This signal is emitted whenever the state of QProcess changes. The + \a newState argument is the state QProcess changed to. +*/ + +/*! + \fn void QProcess::finished(int exitCode) + \obsolete + \overload + + Use finished(int exitCode, QProcess::ExitStatus status) instead. +*/ + +/*! + \fn void QProcess::finished(int exitCode, QProcess::ExitStatus exitStatus) + + This signal is emitted when the process finishes. \a exitCode is the exit + code of the process, and \a exitStatus is the exit status. After the + process has finished, the buffers in QProcess are still intact. You can + still read any data that the process may have written before it finished. + + \sa exitStatus() +*/ + +/*! + \fn void QProcess::readyReadStandardOutput() + + This signal is emitted when the process has made new data + available through its standard output channel (\c stdout). It is + emitted regardless of the current \l{readChannel()}{read channel}. + + \sa readAllStandardOutput(), readChannel() +*/ + +/*! + \fn void QProcess::readyReadStandardError() + + This signal is emitted when the process has made new data + available through its standard error channel (\c stderr). It is + emitted regardless of the current \l{readChannel()}{read + channel}. + + \sa readAllStandardError(), readChannel() +*/ + +/*! \internal +*/ +QProcessPrivate::QProcessPrivate() +{ + processChannel = QProcess::StandardOutput; + processChannelMode = QProcess::SeparateChannels; + processError = QProcess::UnknownError; + processState = QProcess::NotRunning; + pid = 0; + sequenceNumber = 0; + exitCode = 0; + exitStatus = QProcess::NormalExit; + startupSocketNotifier = 0; + deathNotifier = 0; + notifier = 0; + pipeWriter = 0; + childStartedPipe[0] = INVALID_Q_PIPE; + childStartedPipe[1] = INVALID_Q_PIPE; + deathPipe[0] = INVALID_Q_PIPE; + deathPipe[1] = INVALID_Q_PIPE; + exitCode = 0; + crashed = false; + dying = false; + emittedReadyRead = false; + emittedBytesWritten = false; +#ifdef Q_WS_WIN + pipeWriter = 0; + processFinishedNotifier = 0; +#endif // Q_WS_WIN +#ifdef Q_OS_UNIX + serial = 0; +#endif +#ifdef Q_OS_SYMBIAN + symbianProcess = NULL; + processLaunched = false; +#endif +} + +/*! \internal +*/ +QProcessPrivate::~QProcessPrivate() +{ + if (stdinChannel.process) + stdinChannel.process->stdoutChannel.clear(); + if (stdoutChannel.process) + stdoutChannel.process->stdinChannel.clear(); +} + +/*! \internal +*/ +void QProcessPrivate::cleanup() +{ + q_func()->setProcessState(QProcess::NotRunning); +#ifdef Q_OS_WIN + if (pid) { + CloseHandle(pid->hThread); + CloseHandle(pid->hProcess); + delete pid; + pid = 0; + } + if (processFinishedNotifier) { + processFinishedNotifier->setEnabled(false); + qDeleteInEventHandler(processFinishedNotifier); + processFinishedNotifier = 0; + } + +#endif + pid = 0; + sequenceNumber = 0; + dying = false; + + if (stdoutChannel.notifier) { + stdoutChannel.notifier->setEnabled(false); + qDeleteInEventHandler(stdoutChannel.notifier); + stdoutChannel.notifier = 0; + } + if (stderrChannel.notifier) { + stderrChannel.notifier->setEnabled(false); + qDeleteInEventHandler(stderrChannel.notifier); + stderrChannel.notifier = 0; + } + if (stdinChannel.notifier) { + stdinChannel.notifier->setEnabled(false); + qDeleteInEventHandler(stdinChannel.notifier); + stdinChannel.notifier = 0; + } + if (startupSocketNotifier) { + startupSocketNotifier->setEnabled(false); + qDeleteInEventHandler(startupSocketNotifier); + startupSocketNotifier = 0; + } + if (deathNotifier) { + deathNotifier->setEnabled(false); + qDeleteInEventHandler(deathNotifier); + deathNotifier = 0; + } + if (notifier) { + qDeleteInEventHandler(notifier); + notifier = 0; + } + destroyPipe(stdoutChannel.pipe); + destroyPipe(stderrChannel.pipe); + destroyPipe(stdinChannel.pipe); + destroyPipe(childStartedPipe); + destroyPipe(deathPipe); +#ifdef Q_OS_UNIX + serial = 0; +#endif +#ifdef Q_OS_SYMBIAN + if (symbianProcess) { + symbianProcess->Close(); + delete symbianProcess; + symbianProcess = NULL; + } +#endif +} + +/*! \internal +*/ +bool QProcessPrivate::_q_canReadStandardOutput() +{ + Q_Q(QProcess); + qint64 available = bytesAvailableFromStdout(); + if (available == 0) { + if (stdoutChannel.notifier) + stdoutChannel.notifier->setEnabled(false); + destroyPipe(stdoutChannel.pipe); +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::canReadStandardOutput(), 0 bytes available"); +#endif + return false; + } + + char *ptr = outputReadBuffer.reserve(available); + qint64 readBytes = readFromStdout(ptr, available); + if (readBytes == -1) { + processError = QProcess::ReadError; + q->setErrorString(QProcess::tr("Error reading from process")); + emit q->error(processError); +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::canReadStandardOutput(), failed to read from the process"); +#endif + return false; + } +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::canReadStandardOutput(), read %d bytes from the process' output", + int(readBytes)); +#endif + + if (stdoutChannel.closed) { + outputReadBuffer.chop(readBytes); + return false; + } + + outputReadBuffer.chop(available - readBytes); + + bool didRead = false; + if (readBytes == 0) { + if (stdoutChannel.notifier) + stdoutChannel.notifier->setEnabled(false); + } else if (processChannel == QProcess::StandardOutput) { + didRead = true; + if (!emittedReadyRead) { + emittedReadyRead = true; + emit q->readyRead(); + emittedReadyRead = false; + } + } + emit q->readyReadStandardOutput(); + return didRead; +} + +/*! \internal +*/ +bool QProcessPrivate::_q_canReadStandardError() +{ + Q_Q(QProcess); + qint64 available = bytesAvailableFromStderr(); + if (available == 0) { + if (stderrChannel.notifier) + stderrChannel.notifier->setEnabled(false); + destroyPipe(stderrChannel.pipe); + return false; + } + + char *ptr = errorReadBuffer.reserve(available); + qint64 readBytes = readFromStderr(ptr, available); + if (readBytes == -1) { + processError = QProcess::ReadError; + q->setErrorString(QProcess::tr("Error reading from process")); + emit q->error(processError); + return false; + } + if (stderrChannel.closed) { + errorReadBuffer.chop(readBytes); + return false; + } + + errorReadBuffer.chop(available - readBytes); + + bool didRead = false; + if (readBytes == 0) { + if (stderrChannel.notifier) + stderrChannel.notifier->setEnabled(false); + } else if (processChannel == QProcess::StandardError) { + didRead = true; + if (!emittedReadyRead) { + emittedReadyRead = true; + emit q->readyRead(); + emittedReadyRead = false; + } + } + emit q->readyReadStandardError(); + return didRead; +} + +/*! \internal +*/ +bool QProcessPrivate::_q_canWrite() +{ + Q_Q(QProcess); + if (stdinChannel.notifier) + stdinChannel.notifier->setEnabled(false); + + if (writeBuffer.isEmpty()) { +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer)."); +#endif + return false; + } + + qint64 written = writeToStdin(writeBuffer.readPointer(), + writeBuffer.nextDataBlockSize()); + if (written < 0) { + destroyPipe(stdinChannel.pipe); + processError = QProcess::WriteError; + q->setErrorString(QProcess::tr("Error writing to process")); + emit q->error(processError); + return false; + } + +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::canWrite(), wrote %d bytes to the process input", int(written)); +#endif + + if (written != 0) { + writeBuffer.free(written); + if (!emittedBytesWritten) { + emittedBytesWritten = true; + emit q->bytesWritten(written); + emittedBytesWritten = false; + } + } + if (stdinChannel.notifier && !writeBuffer.isEmpty()) + stdinChannel.notifier->setEnabled(true); + if (writeBuffer.isEmpty() && stdinChannel.closed) + closeWriteChannel(); + return true; +} + +/*! \internal +*/ +bool QProcessPrivate::_q_processDied() +{ + Q_Q(QProcess); +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::_q_processDied()"); +#endif +#ifdef Q_OS_UNIX + if (!waitForDeadChild()) + return false; +#endif +#ifdef Q_OS_WIN + if (processFinishedNotifier) + processFinishedNotifier->setEnabled(false); +#endif + + // the process may have died before it got a chance to report that it was + // either running or stopped, so we will call _q_startupNotification() and + // give it a chance to emit started() or error(FailedToStart). + if (processState == QProcess::Starting) { + if (!_q_startupNotification()) + return true; + } + + if (dying) { + // at this point we know the process is dead. prevent + // reentering this slot recursively by calling waitForFinished() + // or opening a dialog inside slots connected to the readyRead + // signals emitted below. + return true; + } + dying = true; + + // in case there is data in the pipe line and this slot by chance + // got called before the read notifications, call these two slots + // so the data is made available before the process dies. + _q_canReadStandardOutput(); + _q_canReadStandardError(); + + findExitCode(); + + if (crashed) { + exitStatus = QProcess::CrashExit; + processError = QProcess::Crashed; + q->setErrorString(QProcess::tr("Process crashed")); + emit q->error(processError); + } + + bool wasRunning = (processState == QProcess::Running); + + cleanup(); + + if (wasRunning) { + // we received EOF now: + emit q->readChannelFinished(); + // in the future: + //emit q->standardOutputClosed(); + //emit q->standardErrorClosed(); + + emit q->finished(exitCode); + emit q->finished(exitCode, exitStatus); + } +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::_q_processDied() process is dead"); +#endif + return true; +} + +/*! \internal +*/ +bool QProcessPrivate::_q_startupNotification() +{ + Q_Q(QProcess); +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::startupNotification()"); +#endif + + if (startupSocketNotifier) + startupSocketNotifier->setEnabled(false); + if (processStarted()) { + q->setProcessState(QProcess::Running); + emit q->started(); + return true; + } + + q->setProcessState(QProcess::NotRunning); + processError = QProcess::FailedToStart; + emit q->error(processError); +#ifdef Q_OS_UNIX + // make sure the process manager removes this entry + waitForDeadChild(); + findExitCode(); +#endif + cleanup(); + return false; +} + +/*! \internal +*/ +void QProcessPrivate::closeWriteChannel() +{ +#if defined QPROCESS_DEBUG + qDebug("QProcessPrivate::closeWriteChannel()"); +#endif + if (stdinChannel.notifier) { + extern void qDeleteInEventHandler(QObject *o); + stdinChannel.notifier->setEnabled(false); + if (stdinChannel.notifier) { + qDeleteInEventHandler(stdinChannel.notifier); + stdinChannel.notifier = 0; + } + } +#ifdef Q_OS_WIN + // ### Find a better fix, feeding the process little by little + // instead. + flushPipeWriter(); +#endif + destroyPipe(stdinChannel.pipe); +} + +/*! + Constructs a QProcess object with the given \a parent. +*/ +QProcess::QProcess(QObject *parent) + : QIODevice(*new QProcessPrivate, parent) +{ +#if defined QPROCESS_DEBUG + qDebug("QProcess::QProcess(%p)", parent); +#endif +} + +/*! + Destructs the QProcess object, i.e., killing the process. + + Note that this function will not return until the process is + terminated. +*/ +QProcess::~QProcess() +{ + Q_D(QProcess); + if (d->processState != NotRunning) { + qWarning("QProcess: Destroyed while process is still running."); + kill(); + waitForFinished(); + } +#ifdef Q_OS_UNIX + // make sure the process manager removes this entry + d->findExitCode(); +#endif + d->cleanup(); +} + +/*! + \obsolete + Returns the read channel mode of the QProcess. This function is + equivalent to processChannelMode() + + \sa processChannelMode() +*/ +QProcess::ProcessChannelMode QProcess::readChannelMode() const +{ + return processChannelMode(); +} + +/*! + \obsolete + + Use setProcessChannelMode(\a mode) instead. + + \sa setProcessChannelMode() +*/ +void QProcess::setReadChannelMode(ProcessChannelMode mode) +{ + setProcessChannelMode(mode); +} + +/*! + \since 4.2 + + Returns the channel mode of the QProcess standard output and + standard error channels. + + \sa setProcessChannelMode(), ProcessChannelMode, setReadChannel() +*/ +QProcess::ProcessChannelMode QProcess::processChannelMode() const +{ + Q_D(const QProcess); + return d->processChannelMode; +} + +/*! + \since 4.2 + + Sets the channel mode of the QProcess standard output and standard + error channels to the \a mode specified. + This mode will be used the next time start() is called. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 0 + + \sa processChannelMode(), ProcessChannelMode, setReadChannel() +*/ +void QProcess::setProcessChannelMode(ProcessChannelMode mode) +{ + Q_D(QProcess); + d->processChannelMode = mode; +} + +/*! + Returns the current read channel of the QProcess. + + \sa setReadChannel() +*/ +QProcess::ProcessChannel QProcess::readChannel() const +{ + Q_D(const QProcess); + return d->processChannel; +} + +/*! + Sets the current read channel of the QProcess to the given \a + channel. The current input channel is used by the functions + read(), readAll(), readLine(), and getChar(). It also determines + which channel triggers QProcess to emit readyRead(). + + \sa readChannel() +*/ +void QProcess::setReadChannel(ProcessChannel channel) +{ + Q_D(QProcess); + if (d->processChannel != channel) { + QByteArray buf = d->buffer.readAll(); + if (d->processChannel == QProcess::StandardOutput) { + for (int i = buf.size() - 1; i >= 0; --i) + d->outputReadBuffer.ungetChar(buf.at(i)); + } else { + for (int i = buf.size() - 1; i >= 0; --i) + d->errorReadBuffer.ungetChar(buf.at(i)); + } + } + d->processChannel = channel; +} + +/*! + Closes the read channel \a channel. After calling this function, + QProcess will no longer receive data on the channel. Any data that + has already been received is still available for reading. + + Call this function to save memory, if you are not interested in + the output of the process. + + \sa closeWriteChannel(), setReadChannel() +*/ +void QProcess::closeReadChannel(ProcessChannel channel) +{ + Q_D(QProcess); + + if (channel == StandardOutput) + d->stdoutChannel.closed = true; + else + d->stderrChannel.closed = true; +} + +/*! + Schedules the write channel of QProcess to be closed. The channel + will close once all data has been written to the process. After + calling this function, any attempts to write to the process will + fail. + + Closing the write channel is necessary for programs that read + input data until the channel has been closed. For example, the + program "more" is used to display text data in a console on both + Unix and Windows. But it will not display the text data until + QProcess's write channel has been closed. Example: + + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 1 + + The write channel is implicitly opened when start() is called. + + \sa closeReadChannel() +*/ +void QProcess::closeWriteChannel() +{ + Q_D(QProcess); + d->stdinChannel.closed = true; // closing + if (d->writeBuffer.isEmpty()) + d->closeWriteChannel(); +} + +/*! + \since 4.2 + + Redirects the process' standard input to the file indicated by \a + fileName. When an input redirection is in place, the QProcess + object will be in read-only mode (calling write() will result in + error). + + If the file \a fileName does not exist at the moment start() is + called or is not readable, starting the process will fail. + + Calling setStandardInputFile() after the process has started has no + effect. + + \sa setStandardOutputFile(), setStandardErrorFile(), + setStandardOutputProcess() +*/ +void QProcess::setStandardInputFile(const QString &fileName) +{ + Q_D(QProcess); + d->stdinChannel = fileName; +} + +/*! + \since 4.2 + + Redirects the process' standard output to the file \a + fileName. When the redirection is in place, the standard output + read channel is closed: reading from it using read() will always + fail, as will readAllStandardOutput(). + + If the file \a fileName doesn't exist at the moment start() is + called, it will be created. If it cannot be created, the starting + will fail. + + If the file exists and \a mode is QIODevice::Truncate, the file + will be truncated. Otherwise (if \a mode is QIODevice::Append), + the file will be appended to. + + Calling setStandardOutputFile() after the process has started has + no effect. + + \sa setStandardInputFile(), setStandardErrorFile(), + setStandardOutputProcess() +*/ +void QProcess::setStandardOutputFile(const QString &fileName, OpenMode mode) +{ + Q_ASSERT(mode == Append || mode == Truncate); + Q_D(QProcess); + + d->stdoutChannel = fileName; + d->stdoutChannel.append = mode == Append; +} + +/*! + \since 4.2 + + Redirects the process' standard error to the file \a + fileName. When the redirection is in place, the standard error + read channel is closed: reading from it using read() will always + fail, as will readAllStandardError(). The file will be appended to + if \a mode is Append, otherwise, it will be truncated. + + See setStandardOutputFile() for more information on how the file + is opened. + + Note: if setProcessChannelMode() was called with an argument of + QProcess::MergedChannels, this function has no effect. + + \sa setStandardInputFile(), setStandardOutputFile(), + setStandardOutputProcess() +*/ +void QProcess::setStandardErrorFile(const QString &fileName, OpenMode mode) +{ + Q_ASSERT(mode == Append || mode == Truncate); + Q_D(QProcess); + + d->stderrChannel = fileName; + d->stderrChannel.append = mode == Append; +} + +/*! + \since 4.2 + + Pipes the standard output stream of this process to the \a + destination process' standard input. + + The following shell command: + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 2 + + Can be accomplished with QProcesses with the following code: + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 3 +*/ +void QProcess::setStandardOutputProcess(QProcess *destination) +{ + QProcessPrivate *dfrom = d_func(); + QProcessPrivate *dto = destination->d_func(); + dfrom->stdoutChannel.pipeTo(dto); + dto->stdinChannel.pipeFrom(dfrom); +} + +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + +/*! + \since 4.7 + + Returns the additional native command line arguments for the program. + + \note This function is available only on the Windows and Symbian + platforms. + + \sa setNativeArguments() +*/ +QString QProcess::nativeArguments() const +{ + Q_D(const QProcess); + return d->nativeArguments; +} + +/*! + \since 4.7 + \overload + + Sets additional native command line \a arguments for the program. + + On operating systems where the system API for passing command line + \a arguments to a subprocess natively uses a single string, one can + conceive command lines which cannot be passed via QProcess's portable + list-based API. In such cases this function must be used to set a + string which is \e appended to the string composed from the usual + argument list, with a delimiting space. + + \note This function is available only on the Windows and Symbian + platforms. + + \sa nativeArguments() +*/ +void QProcess::setNativeArguments(const QString &arguments) +{ + Q_D(QProcess); + d->nativeArguments = arguments; +} + +#endif + +/*! + If QProcess has been assigned a working directory, this function returns + the working directory that the QProcess will enter before the program has + started. Otherwise, (i.e., no directory has been assigned,) an empty + string is returned, and QProcess will use the application's current + working directory instead. + + \sa setWorkingDirectory() +*/ +QString QProcess::workingDirectory() const +{ + Q_D(const QProcess); + return d->workingDirectory; +} + +/*! + Sets the working directory to \a dir. QProcess will start the + process in this directory. The default behavior is to start the + process in the working directory of the calling process. + + \note The working directory setting is ignored on Symbian; + the private directory of the process is considered its working + directory. + + \sa workingDirectory(), start() +*/ +void QProcess::setWorkingDirectory(const QString &dir) +{ + Q_D(QProcess); + d->workingDirectory = dir; +} + +/*! + Returns the native process identifier for the running process, if + available. If no process is currently running, 0 is returned. +*/ +Q_PID QProcess::pid() const +{ + Q_D(const QProcess); + return d->pid; +} + +/*! \reimp + + This function operates on the current read channel. + + \sa readChannel(), setReadChannel() +*/ +bool QProcess::canReadLine() const +{ + Q_D(const QProcess); + const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError) + ? &d->errorReadBuffer + : &d->outputReadBuffer; + return readBuffer->canReadLine() || QIODevice::canReadLine(); +} + +/*! + Closes all communication with the process and kills it. After calling this + function, QProcess will no longer emit readyRead(), and data can no + longer be read or written. +*/ +void QProcess::close() +{ + emit aboutToClose(); + while (waitForBytesWritten(-1)) + ; + kill(); + waitForFinished(-1); + QIODevice::close(); +} + +/*! \reimp + + Returns true if the process is not running, and no more data is available + for reading; otherwise returns false. +*/ +bool QProcess::atEnd() const +{ + Q_D(const QProcess); + const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError) + ? &d->errorReadBuffer + : &d->outputReadBuffer; + return QIODevice::atEnd() && (!isOpen() || readBuffer->isEmpty()); +} + +/*! \reimp +*/ +bool QProcess::isSequential() const +{ + return true; +} + +/*! \reimp +*/ +qint64 QProcess::bytesAvailable() const +{ + Q_D(const QProcess); + const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError) + ? &d->errorReadBuffer + : &d->outputReadBuffer; +#if defined QPROCESS_DEBUG + qDebug("QProcess::bytesAvailable() == %i (%s)", readBuffer->size(), + (d->processChannel == QProcess::StandardError) ? "stderr" : "stdout"); +#endif + return readBuffer->size() + QIODevice::bytesAvailable(); +} + +/*! \reimp +*/ +qint64 QProcess::bytesToWrite() const +{ + Q_D(const QProcess); + qint64 size = d->writeBuffer.size(); +#ifdef Q_OS_WIN + size += d->pipeWriterBytesToWrite(); +#endif + return size; +} + +/*! + Returns the type of error that occurred last. + + \sa state() +*/ +QProcess::ProcessError QProcess::error() const +{ + Q_D(const QProcess); + return d->processError; +} + +/*! + Returns the current state of the process. + + \sa stateChanged(), error() +*/ +QProcess::ProcessState QProcess::state() const +{ + Q_D(const QProcess); + return d->processState; +} + +/*! + \deprecated + Sets the environment that QProcess will use when starting a process to the + \a environment specified which consists of a list of key=value pairs. + + For example, the following code adds the \c{C:\\BIN} directory to the list of + executable paths (\c{PATHS}) on Windows: + + \snippet doc/src/snippets/qprocess-environment/main.cpp 0 + + \note This function is less efficient than the setProcessEnvironment() + function. + + \sa environment(), setProcessEnvironment(), systemEnvironment() +*/ +void QProcess::setEnvironment(const QStringList &environment) +{ + setProcessEnvironment(QProcessEnvironmentPrivate::fromList(environment)); +} + +/*! + \deprecated + Returns the environment that QProcess will use when starting a + process, or an empty QStringList if no environment has been set + using setEnvironment() or setEnvironmentHash(). If no environment + has been set, the environment of the calling process will be used. + + \note The environment settings are ignored on Windows CE and Symbian, + as there is no concept of an environment. + + \sa processEnvironment(), setEnvironment(), systemEnvironment() +*/ +QStringList QProcess::environment() const +{ + Q_D(const QProcess); + return d->environment.toStringList(); +} + +/*! + \since 4.6 + Sets the environment that QProcess will use when starting a process to the + \a environment object. + + For example, the following code adds the \c{C:\\BIN} directory to the list of + executable paths (\c{PATHS}) on Windows and sets \c{TMPDIR}: + + \snippet doc/src/snippets/qprocess-environment/main.cpp 1 + + Note how, on Windows, environment variable names are case-insensitive. + + \sa processEnvironment(), QProcessEnvironment::systemEnvironment(), setEnvironment() +*/ +void QProcess::setProcessEnvironment(const QProcessEnvironment &environment) +{ + Q_D(QProcess); + d->environment = environment; +} + +/*! + \since 4.6 + Returns the environment that QProcess will use when starting a + process, or an empty object if no environment has been set using + setEnvironment() or setProcessEnvironment(). If no environment has + been set, the environment of the calling process will be used. + + \note The environment settings are ignored on Windows CE, + as there is no concept of an environment. + + \sa setProcessEnvironment(), setEnvironment(), QProcessEnvironment::isEmpty() +*/ +QProcessEnvironment QProcess::processEnvironment() const +{ + Q_D(const QProcess); + return d->environment; +} + +/*! + Blocks until the process has started and the started() signal has + been emitted, or until \a msecs milliseconds have passed. + + Returns true if the process was started successfully; otherwise + returns false (if the operation timed out or if an error + occurred). + + This function can operate without an event loop. It is + useful when writing non-GUI applications and when performing + I/O operations in a non-GUI thread. + + \warning Calling this function from the main (GUI) thread + might cause your user interface to freeze. + + If msecs is -1, this function will not time out. + + \sa started(), waitForReadyRead(), waitForBytesWritten(), waitForFinished() +*/ +bool QProcess::waitForStarted(int msecs) +{ + Q_D(QProcess); + if (d->processState == QProcess::Starting) { + if (!d->waitForStarted(msecs)) + return false; + setProcessState(QProcess::Running); + emit started(); + } + return d->processState == QProcess::Running; +} + +/*! \reimp +*/ +bool QProcess::waitForReadyRead(int msecs) +{ + Q_D(QProcess); + + if (d->processState == QProcess::NotRunning) + return false; + if (d->processChannel == QProcess::StandardOutput && d->stdoutChannel.closed) + return false; + if (d->processChannel == QProcess::StandardError && d->stderrChannel.closed) + return false; + return d->waitForReadyRead(msecs); +} + +/*! \reimp +*/ +bool QProcess::waitForBytesWritten(int msecs) +{ + Q_D(QProcess); + if (d->processState == QProcess::NotRunning) + return false; + if (d->processState == QProcess::Starting) { + QElapsedTimer stopWatch; + stopWatch.start(); + bool started = waitForStarted(msecs); + if (!started) + return false; + if (msecs != -1) + msecs -= stopWatch.elapsed(); + } + + return d->waitForBytesWritten(msecs); +} + +/*! + Blocks until the process has finished and the finished() signal + has been emitted, or until \a msecs milliseconds have passed. + + Returns true if the process finished; otherwise returns false (if + the operation timed out, if an error occurred, or if this QProcess + is already finished). + + This function can operate without an event loop. It is + useful when writing non-GUI applications and when performing + I/O operations in a non-GUI thread. + + \warning Calling this function from the main (GUI) thread + might cause your user interface to freeze. + + If msecs is -1, this function will not time out. + + \sa finished(), waitForStarted(), waitForReadyRead(), waitForBytesWritten() +*/ +bool QProcess::waitForFinished(int msecs) +{ + Q_D(QProcess); + if (d->processState == QProcess::NotRunning) + return false; + if (d->processState == QProcess::Starting) { + QElapsedTimer stopWatch; + stopWatch.start(); + bool started = waitForStarted(msecs); + if (!started) + return false; + if (msecs != -1) + msecs -= stopWatch.elapsed(); + } + + return d->waitForFinished(msecs); +} + +/*! + Sets the current state of the QProcess to the \a state specified. + + \sa state() +*/ +void QProcess::setProcessState(ProcessState state) +{ + Q_D(QProcess); + if (d->processState == state) + return; + d->processState = state; + emit stateChanged(state); +} + +/*! + This function is called in the child process context just before the + program is executed on Unix or Mac OS X (i.e., after \e fork(), but before + \e execve()). Reimplement this function to do last minute initialization + of the child process. Example: + + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 4 + + You cannot exit the process (by calling exit(), for instance) from + this function. If you need to stop the program before it starts + execution, your workaround is to emit finished() and then call + exit(). + + \warning This function is called by QProcess on Unix and Mac OS X + only. On Windows, it is not called. +*/ +void QProcess::setupChildProcess() +{ +} + +/*! \reimp +*/ +qint64 QProcess::readData(char *data, qint64 maxlen) +{ + Q_D(QProcess); + QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError) + ? &d->errorReadBuffer + : &d->outputReadBuffer; + + if (maxlen == 1 && !readBuffer->isEmpty()) { + int c = readBuffer->getChar(); + if (c == -1) { +#if defined QPROCESS_DEBUG + qDebug("QProcess::readData(%p \"%s\", %d) == -1", + data, qt_prettyDebug(data, 1, maxlen).constData(), 1); +#endif + return -1; + } + *data = (char) c; +#if defined QPROCESS_DEBUG + qDebug("QProcess::readData(%p \"%s\", %d) == 1", + data, qt_prettyDebug(data, 1, maxlen).constData(), 1); +#endif + return 1; + } + + qint64 bytesToRead = qint64(qMin(readBuffer->size(), (int)maxlen)); + qint64 readSoFar = 0; + while (readSoFar < bytesToRead) { + const char *ptr = readBuffer->readPointer(); + int bytesToReadFromThisBlock = qMin<qint64>(bytesToRead - readSoFar, + readBuffer->nextDataBlockSize()); + memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock); + readSoFar += bytesToReadFromThisBlock; + readBuffer->free(bytesToReadFromThisBlock); + } + +#if defined QPROCESS_DEBUG + qDebug("QProcess::readData(%p \"%s\", %lld) == %lld", + data, qt_prettyDebug(data, readSoFar, 16).constData(), maxlen, readSoFar); +#endif + if (!readSoFar && d->processState == QProcess::NotRunning) + return -1; // EOF + return readSoFar; +} + +/*! \reimp +*/ +qint64 QProcess::writeData(const char *data, qint64 len) +{ + Q_D(QProcess); + +#if defined(Q_OS_WINCE) + Q_UNUSED(data); + Q_UNUSED(len); + d->processError = QProcess::WriteError; + setErrorString(tr("Error writing to process")); + emit error(d->processError); + return -1; +#endif + + if (d->stdinChannel.closed) { +#if defined QPROCESS_DEBUG + qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)", + data, qt_prettyDebug(data, len, 16).constData(), len); +#endif + return 0; + } + + if (len == 1) { + d->writeBuffer.putChar(*data); + if (d->stdinChannel.notifier) + d->stdinChannel.notifier->setEnabled(true); +#if defined QPROCESS_DEBUG + qDebug("QProcess::writeData(%p \"%s\", %lld) == 1 (written to buffer)", + data, qt_prettyDebug(data, len, 16).constData(), len); +#endif + return 1; + } + + char *dest = d->writeBuffer.reserve(len); + memcpy(dest, data, len); + if (d->stdinChannel.notifier) + d->stdinChannel.notifier->setEnabled(true); +#if defined QPROCESS_DEBUG + qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)", + data, qt_prettyDebug(data, len, 16).constData(), len, len); +#endif + return len; +} + +/*! + Regardless of the current read channel, this function returns all + data available from the standard output of the process as a + QByteArray. + + \sa readyReadStandardOutput(), readAllStandardError(), readChannel(), setReadChannel() +*/ +QByteArray QProcess::readAllStandardOutput() +{ + ProcessChannel tmp = readChannel(); + setReadChannel(StandardOutput); + QByteArray data = readAll(); + setReadChannel(tmp); + return data; +} + +/*! + Regardless of the current read channel, this function returns all + data available from the standard error of the process as a + QByteArray. + + \sa readyReadStandardError(), readAllStandardOutput(), readChannel(), setReadChannel() +*/ +QByteArray QProcess::readAllStandardError() +{ + ProcessChannel tmp = readChannel(); + setReadChannel(StandardError); + QByteArray data = readAll(); + setReadChannel(tmp); + return data; +} + +/*! + Starts the given \a program in a new process, if none is already + running, passing the command line arguments in \a arguments. The OpenMode + is set to \a mode. + + The QProcess object will immediately enter the Starting state. If the + process starts successfully, QProcess will emit started(); otherwise, + error() will be emitted. If the QProcess object is already running a + process, a warning may be printed at the console, and the existing + process will continue running. + + \note Processes are started asynchronously, which means the started() + and error() signals may be delayed. Call waitForStarted() to make + sure the process has started (or has failed to start) and those signals + have been emitted. + + \note No further splitting of the arguments is performed. + + \bold{Windows:} Arguments that contain spaces are wrapped in quotes. + + \sa pid(), started(), waitForStarted() +*/ +void QProcess::start(const QString &program, const QStringList &arguments, OpenMode mode) +{ + Q_D(QProcess); + if (d->processState != NotRunning) { + qWarning("QProcess::start: Process is already running"); + return; + } + +#if defined QPROCESS_DEBUG + qDebug() << "QProcess::start(" << program << ',' << arguments << ',' << mode << ')'; +#endif + + d->outputReadBuffer.clear(); + d->errorReadBuffer.clear(); + + if (d->stdinChannel.type != QProcessPrivate::Channel::Normal) + mode &= ~WriteOnly; // not open for writing + if (d->stdoutChannel.type != QProcessPrivate::Channel::Normal && + (d->stderrChannel.type != QProcessPrivate::Channel::Normal || + d->processChannelMode == MergedChannels)) + mode &= ~ReadOnly; // not open for reading + if (mode == 0) + mode = Unbuffered; + QIODevice::open(mode); + + d->stdinChannel.closed = false; + d->stdoutChannel.closed = false; + d->stderrChannel.closed = false; + + d->program = program; + d->arguments = arguments; + + d->exitCode = 0; + d->exitStatus = NormalExit; + d->processError = QProcess::UnknownError; + d->errorString.clear(); + d->startProcess(); +} + + +static QStringList parseCombinedArgString(const QString &program) +{ + QStringList args; + QString tmp; + int quoteCount = 0; + bool inQuote = false; + + // handle quoting. tokens can be surrounded by double quotes + // "hello world". three consecutive double quotes represent + // the quote character itself. + for (int i = 0; i < program.size(); ++i) { + if (program.at(i) == QLatin1Char('"')) { + ++quoteCount; + if (quoteCount == 3) { + // third consecutive quote + quoteCount = 0; + tmp += program.at(i); + } + continue; + } + if (quoteCount) { + if (quoteCount == 1) + inQuote = !inQuote; + quoteCount = 0; + } + if (!inQuote && program.at(i).isSpace()) { + if (!tmp.isEmpty()) { + args += tmp; + tmp.clear(); + } + } else { + tmp += program.at(i); + } + } + if (!tmp.isEmpty()) + args += tmp; + + return args; +} + +/*! + \overload + + Starts the program \a program in a new process, if one is not already + running. \a program is a single string of text containing both the + program name and its arguments. The arguments are separated by one or + more spaces. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 5 + + The \a program string can also contain quotes, to ensure that arguments + containing spaces are correctly supplied to the new process. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 6 + + If the QProcess object is already running a process, a warning may be + printed at the console, and the existing process will continue running. + + Note that, on Windows, quotes need to be both escaped and quoted. + For example, the above code would be specified in the following + way to ensure that \c{"My Documents"} is used as the argument to + the \c dir executable: + + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 7 + + The OpenMode is set to \a mode. +*/ +void QProcess::start(const QString &program, OpenMode mode) +{ + QStringList args = parseCombinedArgString(program); + if (args.isEmpty()) { + Q_D(QProcess); + d->processError = QProcess::FailedToStart; + setErrorString(tr("No program defined")); + emit error(d->processError); + return; + } + + QString prog = args.first(); + args.removeFirst(); + + start(prog, args, mode); +} + +/*! + Attempts to terminate the process. + + The process may not exit as a result of calling this function (it is given + the chance to prompt the user for any unsaved files, etc). + + On Windows, terminate() posts a WM_CLOSE message to all toplevel windows + of the process and then to the main thread of the process itself. On Unix + and Mac OS X the SIGTERM signal is sent. + + Console applications on Windows that do not run an event loop, or whose + event loop does not handle the WM_CLOSE message, can only be terminated by + calling kill(). + + On Symbian, this function requires platform security capability + \c PowerMgmt. If absent, the process will panic with KERN-EXEC 46. + + \note Terminating running processes from other processes will typically + cause a panic in Symbian due to platform security. + + \sa {Symbian Platform Security Requirements} + \sa kill() +*/ +void QProcess::terminate() +{ + Q_D(QProcess); + d->terminateProcess(); +} + +/*! + Kills the current process, causing it to exit immediately. + + On Windows, kill() uses TerminateProcess, and on Unix and Mac OS X, the + SIGKILL signal is sent to the process. + + On Symbian, this function requires platform security capability + \c PowerMgmt. If absent, the process will panic with KERN-EXEC 46. + + \note Killing running processes from other processes will typically + cause a panic in Symbian due to platform security. + + \sa {Symbian Platform Security Requirements} + \sa terminate() +*/ +void QProcess::kill() +{ + Q_D(QProcess); + d->killProcess(); +} + +/*! + Returns the exit code of the last process that finished. +*/ +int QProcess::exitCode() const +{ + Q_D(const QProcess); + return d->exitCode; +} + +/*! + \since 4.1 + + Returns the exit status of the last process that finished. + + On Windows, if the process was terminated with TerminateProcess() + from another application this function will still return NormalExit + unless the exit code is less than 0. +*/ +QProcess::ExitStatus QProcess::exitStatus() const +{ + Q_D(const QProcess); + return d->exitStatus; +} + +/*! + Starts the program \a program with the arguments \a arguments in a + new process, waits for it to finish, and then returns the exit + code of the process. Any data the new process writes to the + console is forwarded to the calling process. + + The environment and working directory are inherited from the calling + process. + + On Windows, arguments that contain spaces are wrapped in quotes. + + If the process cannot be started, -2 is returned. If the process + crashes, -1 is returned. Otherwise, the process' exit code is + returned. +*/ +int QProcess::execute(const QString &program, const QStringList &arguments) +{ + QProcess process; + process.setReadChannelMode(ForwardedChannels); + process.start(program, arguments); + if (!process.waitForFinished(-1)) + return -2; + return process.exitStatus() == QProcess::NormalExit ? process.exitCode() : -1; +} + +/*! + \overload + + Starts the program \a program in a new process. \a program is a + single string of text containing both the program name and its + arguments. The arguments are separated by one or more spaces. +*/ +int QProcess::execute(const QString &program) +{ + QProcess process; + process.setReadChannelMode(ForwardedChannels); + process.start(program); + if (!process.waitForFinished(-1)) + return -2; + return process.exitStatus() == QProcess::NormalExit ? process.exitCode() : -1; +} + +/*! + Starts the program \a program with the arguments \a arguments in a + new process, and detaches from it. Returns true on success; + otherwise returns false. If the calling process exits, the + detached process will continue to live. + + Note that arguments that contain spaces are not passed to the + process as separate arguments. + + \bold{Unix:} The started process will run in its own session and act + like a daemon. + + \bold{Windows:} Arguments that contain spaces are wrapped in quotes. + The started process will run as a regular standalone process. + + The process will be started in the directory \a workingDirectory. + + If the function is successful then *\a pid is set to the process + identifier of the started process. +*/ +bool QProcess::startDetached(const QString &program, + const QStringList &arguments, + const QString &workingDirectory, + qint64 *pid) +{ + return QProcessPrivate::startDetached(program, + arguments, + workingDirectory, + pid); +} + +/*! + Starts the program \a program with the given \a arguments in a + new process, and detaches from it. Returns true on success; + otherwise returns false. If the calling process exits, the + detached process will continue to live. + + \note Arguments that contain spaces are not passed to the + process as separate arguments. + + \bold{Unix:} The started process will run in its own session and act + like a daemon. + + \bold{Windows:} Arguments that contain spaces are wrapped in quotes. + The started process will run as a regular standalone process. +*/ +bool QProcess::startDetached(const QString &program, + const QStringList &arguments) +{ + return QProcessPrivate::startDetached(program, arguments); +} + +/*! + \overload + + Starts the program \a program in a new process. \a program is a + single string of text containing both the program name and its + arguments. The arguments are separated by one or more spaces. + + The \a program string can also contain quotes, to ensure that arguments + containing spaces are correctly supplied to the new process. +*/ +bool QProcess::startDetached(const QString &program) +{ + QStringList args = parseCombinedArgString(program); + if (args.isEmpty()) + return false; + + QString prog = args.first(); + args.removeFirst(); + + return QProcessPrivate::startDetached(prog, args); +} + +QT_BEGIN_INCLUDE_NAMESPACE +#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES) +# include <crt_externs.h> +# define environ (*_NSGetEnviron()) +#elif defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) || (defined(Q_OS_MAC) && defined(QT_NO_CORESERVICES)) + static char *qt_empty_environ[] = { 0 }; +#define environ qt_empty_environ +#elif !defined(Q_OS_WIN) + extern char **environ; +#endif +QT_END_INCLUDE_NAMESPACE + +/*! + \since 4.1 + + Returns the environment of the calling process as a list of + key=value pairs. Example: + + \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 8 + + This function does not cache the system environment. Therefore, it's + possible to obtain an updated version of the environment if low-level C + library functions like \tt setenv ot \tt putenv have been called. + + However, note that repeated calls to this function will recreate the + list of environment variables, which is a non-trivial operation. + + \note For new code, it is recommended to use QProcessEvironment::systemEnvironment() + + \sa QProcessEnvironment::systemEnvironment(), environment(), setEnvironment() +*/ +QStringList QProcess::systemEnvironment() +{ + QStringList tmp; + char *entry = 0; + int count = 0; + while ((entry = environ[count++])) + tmp << QString::fromLocal8Bit(entry); + return tmp; +} + +/*! + \since 4.6 + + \brief The systemEnvironment function returns the environment of + the calling process. + + It is returned as a QProcessEnvironment. This function does not + cache the system environment. Therefore, it's possible to obtain + an updated version of the environment if low-level C library + functions like \tt setenv ot \tt putenv have been called. + + However, note that repeated calls to this function will recreate the + QProcessEnvironment object, which is a non-trivial operation. + + \sa QProcess::systemEnvironment() +*/ +QProcessEnvironment QProcessEnvironment::systemEnvironment() +{ + QProcessEnvironment env; + const char *entry; + for (int count = 0; (entry = environ[count]); ++count) { + const char *equal = strchr(entry, '='); + if (!equal) + continue; + + QByteArray name(entry, equal - entry); + QByteArray value(equal + 1); + env.insert(QString::fromLocal8Bit(name), QString::fromLocal8Bit(value)); + } + return env; +} + +/*! + \typedef Q_PID + \relates QProcess + + Typedef for the identifiers used to represent processes on the underlying + platform. On Unix and Symbian, this corresponds to \l qint64; on Windows, it + corresponds to \c{_PROCESS_INFORMATION*}. + + \sa QProcess::pid() +*/ + +QT_END_NAMESPACE + +#include "moc_qprocess.cpp" + +#endif // QT_NO_PROCESS + |