From 4fe7291edcac2688f042a11ff5c23374c6c0375d Mon Sep 17 00:00:00 2001
From: Oswald Buddenhagen <oswald.buddenhagen@nokia.com>
Date: Tue, 6 Jul 2010 13:56:38 +0200
Subject: add setNativeArguments() and nativeArguments()

this function enables starting subprocesses which need command lines
which cannot be constructed via the portable list-based api. such cases
would be programs which need quoting rules which diverge from the msvc
runtime.

Reviewed-by: joerg
Task-number: QTBUG-7620 (and various others which boil down to that)
---
 src/corelib/io/qprocess.cpp                        | 44 ++++++++++++++++++
 src/corelib/io/qprocess.h                          |  5 +++
 src/corelib/io/qprocess_p.h                        |  3 ++
 src/corelib/io/qprocess_symbian.cpp                | 17 ++++---
 src/corelib/io/qprocess_win.cpp                    |  5 +++
 src/s60installs/bwins/QtCoreu.def                  |  2 +
 src/s60installs/eabi/QtCoreu.def                   |  2 +
 tests/auto/qprocess/testProcessSpacesArgs/main.cpp |  7 ++-
 tests/auto/qprocess/tst_qprocess.cpp               | 52 ++++++++++++++++++++++
 9 files changed, 129 insertions(+), 8 deletions(-)

diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 4c21a40..9bc4063 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -1377,6 +1377,50 @@ void QProcess::setStandardOutputProcess(QProcess *destination)
     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 arguments for the program.
+
+    On operating systems where the system API for passing command line
+    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
diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h
index f84b855..b07d742 100644
--- a/src/corelib/io/qprocess.h
+++ b/src/corelib/io/qprocess.h
@@ -148,6 +148,11 @@ public:
     void setStandardErrorFile(const QString &fileName, OpenMode mode = Truncate);
     void setStandardOutputProcess(QProcess *destination);
 
+#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
+    QString nativeArguments() const;
+    void setNativeArguments(const QString &arguments);
+#endif
+
     QString workingDirectory() const;
     void setWorkingDirectory(const QString &dir);
 
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index 60b7b5a..8b7e3ff 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -181,6 +181,9 @@ public:
 
     QString program;
     QStringList arguments;
+#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
+    QString nativeArguments;
+#endif
     QProcessEnvironment environment;
 
     QRingBuffer outputReadBuffer;
diff --git a/src/corelib/io/qprocess_symbian.cpp b/src/corelib/io/qprocess_symbian.cpp
index 92212fe..af657b2 100644
--- a/src/corelib/io/qprocess_symbian.cpp
+++ b/src/corelib/io/qprocess_symbian.cpp
@@ -219,7 +219,8 @@ static bool qt_rprocess_running(RProcess *proc)
     return false;
 }
 
-static void qt_create_symbian_commandline(const QStringList &arguments, QString &commandLine)
+static void qt_create_symbian_commandline(
+    const QStringList &arguments, const QString &nativeArguments, QString &commandLine)
 {
     for (int i = 0; i < arguments.size(); ++i) {
         QString tmp = arguments.at(i);
@@ -243,12 +244,14 @@ static void qt_create_symbian_commandline(const QStringList &arguments, QString
         }
     }
 
-    // Chop the extra trailing space if any arguments were appended
-    if (arguments.size())
+    if (!nativeArguments.isEmpty())
+        commandLine += nativeArguments;
+    else if (!commandLine.isEmpty()) // Chop the extra trailing space if any arguments were appended
         commandLine.chop(1);
 }
 
-static TInt qt_create_symbian_process(RProcess **proc, const QString &programName, const QStringList &arguments)
+static TInt qt_create_symbian_process(RProcess **proc,
+    const QString &programName, const QStringList &arguments, const QString &nativeArguments)
 {
     RProcess *newProc = NULL;
     newProc = new RProcess();
@@ -257,7 +260,7 @@ static TInt qt_create_symbian_process(RProcess **proc, const QString &programNam
         return KErrNoMemory;
 
     QString commandLine;
-    qt_create_symbian_commandline(arguments, commandLine);
+    qt_create_symbian_commandline(arguments, nativeArguments, commandLine);
 
     TPtrC program_ptr(reinterpret_cast<const TText*>(programName.constData()));
     TPtrC cmdline_ptr(reinterpret_cast<const TText*>(commandLine.constData()));
@@ -794,7 +797,7 @@ void QProcessPrivate::startProcess()
                          q, SLOT(_q_processDied()));
     }
 
-    TInt err = qt_create_symbian_process(&symbianProcess, program, arguments);
+    TInt err = qt_create_symbian_process(&symbianProcess, program, arguments, nativeArguments);
 
     if (err == KErrNone) {
         pid = symbianProcess->Id().Id();
@@ -1030,7 +1033,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
 
     RProcess *newProc = NULL;
 
-    TInt err = qt_create_symbian_process(&newProc, program, arguments);
+    TInt err = qt_create_symbian_process(&newProc, program, arguments, QString());
 
     if (err == KErrNone) {
         if (pid)
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index cb25a58..702349f 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -366,6 +366,11 @@ void QProcessPrivate::startProcess()
     if (environment.d.constData())
         envlist = qt_create_environment(environment.d.constData()->hash);
 #endif
+    if (!nativeArguments.isEmpty()) {
+        if (!args.isEmpty())
+             args += QLatin1Char(' ');
+        args += nativeArguments;
+    }
 
 #if defined QPROCESS_DEBUG
     qDebug("Creating process");
diff --git a/src/s60installs/bwins/QtCoreu.def b/src/s60installs/bwins/QtCoreu.def
index 101c6a8..94be1bb 100644
--- a/src/s60installs/bwins/QtCoreu.def
+++ b/src/s60installs/bwins/QtCoreu.def
@@ -4482,4 +4482,6 @@ EXPORTS
 	?textDirection@QLocale@@QBE?AW4LayoutDirection@Qt@@XZ @ 4481 NONAME ; enum Qt::LayoutDirection QLocale::textDirection(void) const
 	?peek@QIODevicePrivate@@UAE_JPAD_J@Z @ 4482 NONAME ; long long QIODevicePrivate::peek(char *, long long)
 	?peek@QIODevicePrivate@@UAE?AVQByteArray@@_J@Z @ 4483 NONAME ; class QByteArray QIODevicePrivate::peek(long long)
+	?nativeArguments@QProcess@@QBE?AVQString@@XZ @ 4484 NONAME ; class QString QProcess::nativeArguments(void) const
+	?setNativeArguments@QProcess@@QAEXABVQString@@@Z @ 4485 NONAME ; void QProcess::setNativeArguments(class QString const &)
 
diff --git a/src/s60installs/eabi/QtCoreu.def b/src/s60installs/eabi/QtCoreu.def
index 7b9e777..46c4885 100644
--- a/src/s60installs/eabi/QtCoreu.def
+++ b/src/s60installs/eabi/QtCoreu.def
@@ -3709,4 +3709,6 @@ EXPORTS
 	_ZNK7QString13isRightToLeftEv @ 3708 NONAME
 	_ZN16QIODevicePrivate4peekEPcx @ 3709 NONAME
 	_ZN16QIODevicePrivate4peekEx @ 3710 NONAME
+	_ZN8QProcess18setNativeArgumentsERK7QString @ 3711 NONAME
+	_ZNK8QProcess15nativeArgumentsEv @ 3712 NONAME
 
diff --git a/tests/auto/qprocess/testProcessSpacesArgs/main.cpp b/tests/auto/qprocess/testProcessSpacesArgs/main.cpp
index d842934..ae5d3ae 100644
--- a/tests/auto/qprocess/testProcessSpacesArgs/main.cpp
+++ b/tests/auto/qprocess/testProcessSpacesArgs/main.cpp
@@ -44,9 +44,14 @@
 
 int main(int argc, char ** argv)
 {
-#if defined(__SYMBIAN32__)
+#if defined(__SYMBIAN32__) || defined(WINCE) || defined(_WIN32_WCE)
+# if defined(__SYMBIAN32__)
     // Printing to stdout messes up the out.txt, so open a file and print there.
     FILE* file = fopen("c:\\logs\\qprocess_args_test.txt","w+");
+# else
+    // No pipes on this "OS"
+    FILE* file = fopen("\\temp\\qprocess_args_test.txt","w+");
+# endif
     for (int i = 0; i < argc; ++i) {
         if (i)
             fprintf(file, "|");
diff --git a/tests/auto/qprocess/tst_qprocess.cpp b/tests/auto/qprocess/tst_qprocess.cpp
index 8242d30..fd310f4 100644
--- a/tests/auto/qprocess/tst_qprocess.cpp
+++ b/tests/auto/qprocess/tst_qprocess.cpp
@@ -132,6 +132,9 @@ private slots:
     void waitForBytesWrittenInABytesWrittenSlot();
     void spaceArgsTest_data();
     void spaceArgsTest();
+#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
+    void nativeArguments();
+#endif
     void exitCodeTest();
     void setEnvironment_data();
     void setEnvironment();
@@ -1630,6 +1633,55 @@ void tst_QProcess::spaceArgsTest()
     process = 0;
 }
 
+#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::nativeArguments()
+{
+    QProcess proc;
+
+    // This doesn't actually need special quoting, so it is pointless to use
+    // native arguments here, but that's not the point of this test.
+    proc.setNativeArguments("hello kitty, \"*\"!");
+
+    proc.start(QString::fromLatin1("testProcessSpacesArgs/nospace"), QStringList());
+
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+    QVERIFY(proc.waitForStarted(5000));
+    QVERIFY(proc.waitForFinished(5000));
+#else
+    QVERIFY(proc.waitForStarted(10000));
+    QVERIFY(proc.waitForFinished(10000));
+#endif
+
+#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE)
+    // Symbian test outputs to a file, so check that
+# ifdef Q_OS_SYMBIAN
+    FILE* file = fopen("c:\\logs\\qprocess_args_test.txt","r");
+# else
+    FILE* file = fopen("\\temp\\qprocess_args_test.txt","r");
+# endif
+    char buf[256];
+    fgets(buf, 256, file);
+    fclose(file);
+    QStringList actual = QString::fromLatin1(buf).split("|");
+#else
+    QStringList actual = QString::fromLatin1(proc.readAll()).split("|");
+#endif
+    QVERIFY(!actual.isEmpty());
+    // not interested in the program name, it might be different.
+    actual.removeFirst();
+    QStringList expected;
+#if defined(Q_OS_WINCE)
+    expected << "hello" << "kitty," << "\"*\"!"; // Weird, weird ...
+#else
+    expected << "hello" << "kitty," << "*!";
+#endif
+    QCOMPARE(actual, expected);
+}
+
+#endif
+
 //-----------------------------------------------------------------------------
 void tst_QProcess::exitCodeTest()
 {
-- 
cgit v0.12

