diff options
-rw-r--r-- | doc/src/snippets/qprocess-environment/main.cpp | 6 | ||||
-rw-r--r-- | src/corelib/io/qprocess.cpp | 242 | ||||
-rw-r--r-- | src/corelib/io/qprocess.h | 44 | ||||
-rw-r--r-- | src/corelib/io/qprocess_p.h | 17 | ||||
-rw-r--r-- | src/corelib/io/qprocess_unix.cpp | 26 | ||||
-rw-r--r-- | src/corelib/io/qprocess_win.cpp | 14 | ||||
-rw-r--r-- | tests/auto/qprocess/tst_qprocess.cpp | 37 |
7 files changed, 300 insertions, 86 deletions
diff --git a/doc/src/snippets/qprocess-environment/main.cpp b/doc/src/snippets/qprocess-environment/main.cpp index a143bf8..0fa0896 100644 --- a/doc/src/snippets/qprocess-environment/main.cpp +++ b/doc/src/snippets/qprocess-environment/main.cpp @@ -57,10 +57,10 @@ process.start("myapp"); { //! [1] QProcess process; -QHash<QString, QString> env = QProcess::systemEnvironmentHash(); +QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("TMPDIR", "C:\\MyApp\\temp"); // Add an environment variable -env["PATH"] += ";C:\\Bin"; -process.setEnvironment(env); +env.insert("PATH", env.value("Path") + ";C:\\Bin"); +process.setProcessEnvironment(env); process.start("myapp"); //! [1] } diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index ccc16b2..3d143e9 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -101,27 +101,169 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE -static QHash<QString, QString> environmentHashFromList(const QStringList &environment) +#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() { - QHash<QString, QString> result; - QStringList::ConstIterator it = environment.constBegin(), - end = environment.constEnd(); + 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) { - int equals = it->indexOf(QLatin1Char('=')); + 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; - QString value; - if (equals != -1) { - name.truncate(equals); -#ifdef Q_OS_WIN - name = name.toUpper(); -#endif - value = it->mid(equals + 1); - } - result.insert(name, value); + name.truncate(pos); + env.insert(name, value); } + return env; +} - return result; +QProcessEnvironment::QProcessEnvironment() + : d(0) +{ +} + +QProcessEnvironment::~QProcessEnvironment() +{ +} + +QProcessEnvironment::QProcessEnvironment(const QProcessEnvironment &other) + : d(other.d) +{ +} + +QProcessEnvironment &QProcessEnvironment::operator=(const QProcessEnvironment &other) +{ + d = other.d; + return *this; +} + +bool QProcessEnvironment::operator==(const QProcessEnvironment &other) const +{ + return d->hash == other.d->hash; +} + +bool QProcessEnvironment::isEmpty() const +{ + return d ? d->hash.isEmpty() : true; +} + +void QProcessEnvironment::clear() +{ + d->hash.clear(); +} + +bool QProcessEnvironment::contains(const QString &name) const +{ + return d ? d->hash.contains(prepareName(name)) : false; +} + +void QProcessEnvironment::insert(const QString &name, const QString &value) +{ + d->hash.insert(prepareName(name), prepareValue(value)); +} + +void QProcessEnvironment::remove(const QString &name) +{ + d->hash.remove(prepareName(name)); +} + +QString QProcessEnvironment::value(const QString &name, const QString &defaultValue) const +{ + if (!d) + return defaultValue; + + QProcessEnvironmentPrivate::Unit result = d->hash.value(prepareName(name), prepareValue(defaultValue)); + return valueToString(result); +} + +bool QProcessEnvironment::containsRaw(const QByteArray &name) const +{ + return d ? d->hash.contains(prepareName(name)) : false; +} + +void QProcessEnvironment::insertRaw(const QByteArray &name, const QByteArray &value) +{ + d->hash.insert(prepareName(name), prepareValue(value)); +} + +void QProcessEnvironment::removeRaw(const QByteArray &name) +{ + d->hash.remove(prepareName(name)); +} + +QByteArray QProcessEnvironment::valueRaw(const QByteArray &name, const QByteArray &defaultValue) const +{ + if (!d) + return defaultValue; + QProcessEnvironmentPrivate::Unit result = d->hash.value(prepareName(name), prepareValue(defaultValue)); + return valueToByteArray(result); +} + +QStringList QProcessEnvironment::toStringList() const +{ + return d ? d->toList() : QStringList(); } void QProcessPrivate::Channel::clear() @@ -446,7 +588,6 @@ QProcessPrivate::QProcessPrivate() sequenceNumber = 0; exitCode = 0; exitStatus = QProcess::NormalExit; - environment = 0; startupSocketNotifier = 0; deathNotifier = 0; notifier = 0; @@ -473,7 +614,6 @@ QProcessPrivate::QProcessPrivate() */ QProcessPrivate::~QProcessPrivate() { - delete environment; if (stdinChannel.process) stdinChannel.process->stdoutChannel.clear(); if (stdoutChannel.process) @@ -1215,6 +1355,7 @@ QProcess::ProcessState QProcess::state() const } /*! + \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. @@ -1223,14 +1364,15 @@ QProcess::ProcessState QProcess::state() const \snippet doc/src/snippets/qprocess-environment/main.cpp 0 - \sa environment(), systemEnvironment(), setEnvironmentHash() + \sa environment(), setProcessEnvironment(), systemEnvironment() */ void QProcess::setEnvironment(const QStringList &environment) { - setEnvironmentHash(environmentHashFromList(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 @@ -1239,67 +1381,50 @@ void QProcess::setEnvironment(const QStringList &environment) \note The environment settings are ignored on Windows CE, as there is no concept of an environment. - \sa environmentHash(), setEnvironment(), systemEnvironment() + \sa processEnvironment(), setEnvironment(), systemEnvironment() */ QStringList QProcess::environment() const { Q_D(const QProcess); - - QStringList result; - if (!d->environment) - return result; - - QHash<QString, QString>::ConstIterator it = d->environment->constBegin(), - end = d->environment->constEnd(); - for ( ; it != end; ++it) { - QString data = it.key(); - data.reserve(data.length() + it.value().length() + 1); - data.append(QLatin1Char('=')); - data.append(it.value()); - result << data; - } - return result; + return d->environment.toStringList(); } /*! \since 4.5 Sets the environment that QProcess will use when starting a process to the - \a environment hash map. + \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 - \sa environment(), systemEnvironmentHash(), setEnvironment() + Note how, on Windows, environment variable names are case-insensitive. + + \sa processEnvironment(), QProcessEnvironment::systemEnvironment(), setEnvironment() */ -void QProcess::setEnvironmentHash(const QHash<QString, QString> &environment) +void QProcess::setProcessEnvironment(const QProcessEnvironment &environment) { Q_D(QProcess); - if (!d->environment) - d->environment = new QHash<QString, QString>(environment); - else - *d->environment = environment; + d->environment = environment; } /*! \since 4.5 Returns the environment that QProcess will use when starting a - process, or an empty QHash if no environment has been set using - setEnvironment() or setEnvironmentHash(). If no environment has + 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 setEnvironmentHash(), setEnvironment(), systemEnvironmentHash() + \sa setProcessEnvironment(), setEnvironment(), QProcessEnvironment::isValid() */ -QHash<QString, QString> QProcess::environmentHash() const +QProcessEnvironment QProcess::processEnvironment() const { Q_D(const QProcess); - if (d->environment) - return *d->environment; - return QHash<QString, QString>(); + return d->environment; } /*! @@ -1898,7 +2023,7 @@ QT_END_INCLUDE_NAMESPACE \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 8 - \sa systemEnvironmentHash(), environment(), setEnvironment() + \sa QProcessEnvironment::systemEnvironment(), environment(), setEnvironment() */ QStringList QProcess::systemEnvironment() { @@ -1915,11 +2040,22 @@ QStringList QProcess::systemEnvironment() Returns the environment of the calling process as a QHash. - \sa systemEnvironment(), environmentHash(), setEnvironmentHash() + \sa QProcess::systemEnvironment() */ -QHash<QString, QString> QProcess::systemEnvironmentHash() +QProcessEnvironment QProcessEnvironment::systemEnvironment() { - return environmentHashFromList(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.insertRaw(name, value); + } + return env; } /*! diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index 096f625..116168b 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -44,6 +44,7 @@ #include <QtCore/qiodevice.h> #include <QtCore/qstringlist.h> +#include <QtCore/qshareddata.h> QT_BEGIN_HEADER @@ -53,8 +54,6 @@ QT_MODULE(Core) #ifndef QT_NO_PROCESS -template <class Key, class T> class QHash; - #if (!defined(Q_OS_WIN32) && !defined(Q_OS_WINCE)) || defined(qdoc) typedef qint64 Q_PID; #else @@ -64,6 +63,42 @@ QT_BEGIN_NAMESPACE #endif class QProcessPrivate; +class QProcessEnvironmentPrivate; + +class Q_CORE_EXPORT QProcessEnvironment +{ +public: + QProcessEnvironment(); + QProcessEnvironment(const QProcessEnvironment &other); + ~QProcessEnvironment(); + QProcessEnvironment &operator=(const QProcessEnvironment &other); + + bool operator==(const QProcessEnvironment &other) const; + inline bool operator!=(const QProcessEnvironment &other) const + { return !(*this == other); } + + bool isEmpty() const; + void clear(); + + bool contains(const QString &name) const; + void insert(const QString &name, const QString &value); + void remove(const QString &name); + QString value(const QString &name, const QString &defaultValue = QString()) const; + + bool containsRaw(const QByteArray &name) const; + void insertRaw(const QByteArray &name, const QByteArray &value); + void removeRaw(const QByteArray &name); + QByteArray valueRaw(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const; + + QStringList toStringList() const; + + static QProcessEnvironment systemEnvironment(); + +private: + friend class QProcessPrivate; + friend class QProcessEnvironmentPrivate; + QSharedDataPointer<QProcessEnvironmentPrivate> d; +}; class Q_CORE_EXPORT QProcess : public QIODevice { @@ -123,8 +158,8 @@ public: void setEnvironment(const QStringList &environment); QStringList environment() const; - void setEnvironmentHash(const QHash<QString, QString> &environment); - QHash<QString, QString> environmentHash() const; + void setProcessEnvironment(const QProcessEnvironment &environment); + QProcessEnvironment processEnvironment() const; QProcess::ProcessError error() const; QProcess::ProcessState state() const; @@ -160,7 +195,6 @@ public: static bool startDetached(const QString &program); static QStringList systemEnvironment(); - static QHash<QString, QString> systemEnvironmentHash(); public Q_SLOTS: void terminate(); diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index 5482871..3b04d88 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -55,6 +55,7 @@ #include "QtCore/qprocess.h" #include "QtCore/qstringlist.h" +#include "QtCore/qhash.h" #include "private/qringbuffer_p.h" #include "private/qiodevice_p.h" @@ -76,6 +77,20 @@ class QWindowsPipeWriter; class QWinEventNotifier; class QTimer; +class QProcessEnvironmentPrivate: public QSharedData +{ +public: +#ifdef Q_OS_WIN + typedef QString Unit; +#else + typedef QByteArray Unit; +#endif + QHash<Unit, Unit> hash; + + static QProcessEnvironment fromList(const QStringList &list); + QStringList toList() const; +}; + class QProcessPrivate : public QIODevicePrivate { public: @@ -161,7 +176,7 @@ public: QString program; QStringList arguments; - QHash<QString, QString> *environment; + QProcessEnvironment environment; QRingBuffer outputReadBuffer; QRingBuffer errorReadBuffer; diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index d28cdc4..dfeeb71 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -458,11 +458,11 @@ bool QProcessPrivate::createChannel(Channel &channel) } } -static char **_q_dupEnvironment(const QHash<QString, QString> *environment, int *envc) +static char **_q_dupEnvironment(const QHash<QByteArray, QByteArray> &environment, int *envc) { *envc = 0; - if (!environment) - return 0; // use the default environment + if (environment.isEmpty()) + return 0; // if LD_LIBRARY_PATH exists in the current environment, but // not in the environment list passed by the programmer, then @@ -474,17 +474,17 @@ static char **_q_dupEnvironment(const QHash<QString, QString> *environment, int #endif const QByteArray envLibraryPath = qgetenv(libraryPath); bool needToAddLibraryPath = !envLibraryPath.isEmpty() && - !environment->contains(QLatin1String(libraryPath)); + !environment.contains(libraryPath); - char **envp = new char *[environment->count() + 2]; - envp[environment->count()] = 0; - envp[environment->count() + 1] = 0; + char **envp = new char *[environment.count() + 2]; + envp[environment.count()] = 0; + envp[environment.count() + 1] = 0; - QHash<QString, QString>::ConstIterator it = environment->constBegin(); - const QHash<QString, QString>::ConstIterator end = environment->constEnd(); + QHash<QByteArray, QByteArray>::ConstIterator it = environment.constBegin(); + const QHash<QByteArray, QByteArray>::ConstIterator end = environment.constEnd(); for ( ; it != end; ++it) { - QByteArray key = it.key().toLocal8Bit(); - QByteArray value = it.value().toLocal8Bit(); + QByteArray key = it.key(); + QByteArray value = it.value(); key.reserve(key.length() + 1 + value.length()); key.append('='); key.append(value); @@ -590,7 +590,9 @@ void QProcessPrivate::startProcess() // Duplicate the environment. int envc = 0; - char **envp = _q_dupEnvironment(environment, &envc); + char **envp = 0; + if (environment.d.constData()) + envp = _q_dupEnvironment(environment.d.constData()->hash, &envc); // Encode the working directory if it's non-empty, otherwise just pass 0. const char *workingDirPtr = 0; diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index acb169f..50a4a00 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -278,17 +278,17 @@ static QString qt_create_commandline(const QString &program, const QStringList & return args; } -static QByteArray qt_create_environment(const QHash<QString, QString> *environment) +static QByteArray qt_create_environment(const QHash<QString, QString> &environment) { QByteArray envlist; - if (environment) { - QHash<QString, QString> copy = *environment; + if (!environment.isEmpty()) { + QHash<QString, QString> copy = environment; // add PATH if necessary (for DLL loading) if (!copy.contains(QLatin1String("PATH"))) { QByteArray path = qgetenv("PATH"); if (!path.isEmpty()) - copy.insert(QLatin1String("PATH"), QString::fromLocal8Bit(path)); + copy.insert(QLatin1String("PATH"), QString::fromLocal8Bit(path)); } // add systemroot if needed @@ -362,7 +362,9 @@ void QProcessPrivate::startProcess() QString args = qt_create_commandline(QString(), arguments); #else QString args = qt_create_commandline(program, arguments); - QByteArray envlist = qt_create_environment(environment); + QByteArray envlist; + if (environment.d.constData()) + envlist = qt_create_environment(environment.d.constData()->hash); #endif #if defined QPROCESS_DEBUG @@ -393,7 +395,7 @@ void QProcessPrivate::startProcess() }; success = CreateProcess(0, (wchar_t*)args.utf16(), 0, 0, TRUE, dwCreationFlags, - environment ? envlist.data() : 0, + environment.isEmpty() ? 0 : envlist.data(), workingDirectory.isEmpty() ? 0 : (wchar_t*)QDir::toNativeSeparators(workingDirectory).utf16(), &startupInfo, pid); diff --git a/tests/auto/qprocess/tst_qprocess.cpp b/tests/auto/qprocess/tst_qprocess.cpp index b57139b..d2af86a 100644 --- a/tests/auto/qprocess/tst_qprocess.cpp +++ b/tests/auto/qprocess/tst_qprocess.cpp @@ -130,6 +130,8 @@ private slots: void exitCodeTest(); void setEnvironment_data(); void setEnvironment(); + void setProcessEnvironment_data(); + void setProcessEnvironment(); void systemEnvironment(); void spaceInName(); void lockupsInStartDetached(); @@ -1697,16 +1699,39 @@ void tst_QProcess::setEnvironment() QCOMPARE(process.readAll(), value.toLocal8Bit()); } +#endif +} + +//----------------------------------------------------------------------------- +void tst_QProcess::setProcessEnvironment_data() +{ + setEnvironment_data(); +} + +void tst_QProcess::setProcessEnvironment() +{ +#if !defined (Q_OS_WINCE) + // there is no concept of system variables on Windows CE as there is no console + + // make sure our environment variables are correct + QVERIFY(qgetenv("tst_QProcess").isEmpty()); + QVERIFY(!qgetenv("PATH").isEmpty()); +#ifdef Q_OS_WIN + QVERIFY(!qgetenv("PROMPT").isEmpty()); +#endif + + QFETCH(QString, name); + QFETCH(QString, value); + QString executable = QDir::currentPath() + "/testProcessEnvironment/testProcessEnvironment"; - // use the hash variant now { QProcess process; - QHash<QString, QString> environment = QProcess::systemEnvironmentHash(); + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); if (value.isNull()) environment.remove(name); else environment.insert(name, value); - process.setEnvironmentHash(environment); + process.setProcessEnvironment(environment); process.start(executable, QStringList() << name); QVERIFY(process.waitForFinished()); @@ -1725,12 +1750,12 @@ void tst_QProcess::systemEnvironment() #if defined (Q_OS_WINCE) // there is no concept of system variables on Windows CE as there is no console QVERIFY(QProcess::systemEnvironment().isEmpty()); - QVERIFY(QProcess::systemEnvironmentHash().isEmpty()); + QVERIFY(QProcessEnvironment::systemEnvironment().isEmpty()); #else QVERIFY(!QProcess::systemEnvironment().isEmpty()); - QVERIFY(!QProcess::systemEnvironmentHash().isEmpty()); + QVERIFY(!QProcessEnvironment::systemEnvironment().isEmpty()); - QVERIFY(QProcess::systemEnvironmentHash().contains("PATH")); + QVERIFY(QProcessEnvironment::systemEnvironment().contains("PATH")); QVERIFY(!QProcess::systemEnvironment().filter(QRegExp("^PATH=", Qt::CaseInsensitive)).isEmpty()); #endif } |