src/corelib/io/qprocess.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/io/qprocess.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,2256 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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 <qdatetime.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;
+}
+
+/*!
+    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->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->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();
+}
+
+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.
+
+    \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"));
+#if defined(QPROCESS_DEBUG) && !defined(Q_OS_WINCE)
+        qDebug("QProcessPrivate::canWrite(), failed to write (%s)", strerror(errno));
+#endif
+        emit q->error(processError);
+        return false;
+    }
+
+#if defined QPROCESS_DEBUG
+    qDebug("QProcessPrivate::canWrite(), wrote %d bytes to the process input", int(written));
+#endif
+
+    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 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) {
+        QTime 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) {
+        QTime 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 program \a program in a new process, if one is not 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 Arguments that contain spaces are not passed to the
+    process as separate arguments.
+
+    \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.
+
+    \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().
+
+    \note Terminating running processes from other processes will typically
+    cause a panic in Symbian due to platform security.
+
+    \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.
+
+    \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 by the calling
+    process.
+
+    On Windows, arguments that contain spaces are wrapped in quotes.
+*/
+int QProcess::execute(const QString &program, const QStringList &arguments)
+{
+    QProcess process;
+    process.setReadChannelMode(ForwardedChannels);
+    process.start(program, arguments);
+    process.waitForFinished(-1);
+    return process.exitCode();
+}
+
+/*!
+    \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);
+    process.waitForFinished(-1);
+    return process.exitCode();
+}
+
+/*!
+    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
+#ifdef Q_OS_MAC
+# include <crt_externs.h>
+# define environ (*_NSGetEnviron())
+#elif defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
+  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
+