File indexing completed on 2024-05-12 04:51:20

0001 /*
0002     SPDX-FileCopyrightText: 2009 Nokia Corporation and /or its subsidiary(-ies).
0003     Contact: Qt Software Information (qt-info@nokia.com)
0004 
0005     This file is part of the QtCore module of the Qt Toolkit.
0006 
0007     $QT_BEGIN_LICENSE:LGPL$
0008     Commercial Usage
0009     Licensees holding valid Qt Commercial licenses may use this file in
0010     accordance with the Qt Commercial License Agreement provided with the
0011     Software or, alternatively, in accordance with the terms contained in
0012     a written agreement between you and Nokia.
0013 
0014     GNU Lesser General Public License Usage
0015     Alternatively, this file may be used under the terms of the GNU Lesser
0016     General Public License version 2.1 as published by the Free Software
0017     Foundation and appearing in the file LICENSE.LGPL included in the
0018     packaging of this file.  Please review the following information to
0019     ensure the GNU Lesser General Public License version 2.1 requirements
0020     will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
0021 
0022     In addition, as a special exception, Nokia gives you certain
0023     additional rights. These rights are described in the Nokia Qt LGPL
0024     Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
0025     package.
0026 
0027     GNU General Public License Usage
0028     Alternatively, this file may be used under the terms of the GNU
0029     General Public License version 3.0 as published by the Free Software
0030     Foundation and appearing in the file LICENSE.GPL included in the
0031     packaging of this file.  Please review the following information to
0032     ensure the GNU General Public License version 3.0 requirements will be
0033     met: https://www.gnu.org/licenses/gpl-3.0.html.
0034 
0035     If you are unsure which license is appropriate for your use, please
0036     contact the sales department at qt-sales@nokia.com.
0037     $QT_END_LICENSE$
0038 
0039 */
0040 
0041 #include "k3bqprocess.h"
0042 #include "k3bqprocess_p.h"
0043 #include "qwindowspipewriter_p.h"
0044 
0045 #include <QDateTime>
0046 #include <QDir>
0047 #include <QFileInfo>
0048 #include <QTimer>
0049 #include <QThread>
0050 #include <QMutex>
0051 #include <QWaitCondition>
0052 #include <private/qwineventnotifier_p.h>
0053 #include <private/qthread_p.h>
0054 #include <qdebug.h>
0055 
0056 #include "private/qfsfileengine.h" // for longFileName and win95FileName
0057 #include "private/qfsfileengine_p.h" // for longFileName and win95FileName
0058 
0059 
0060 #ifndef QT_NO_PROCESS
0061 
0062 QT_BEGIN_NAMESPACE
0063 
0064 //#define QPROCESS_DEBUG
0065 
0066 #define NOTIFYTIMEOUT 100
0067 
0068 static void qt_create_pipe(Q_PIPE *pipe, bool in)
0069 {
0070     // Open the pipes.  Make non-inheritable copies of input write and output
0071     // read handles to avoid non-closable handles (this is done by the
0072     // DuplicateHandle() call).
0073 
0074 #if !defined(Q_OS_WINCE)
0075     SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
0076 
0077     HANDLE tmpHandle;
0078     if (in) {                   // stdin
0079         if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024))
0080             return;
0081         if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
0082                              &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS))
0083             return;
0084     } else {                    // stdout or stderr
0085         if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024))
0086             return;
0087         if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
0088                              &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS))
0089             return;
0090     }
0091 
0092     CloseHandle(tmpHandle);
0093 #else
0094     Q_UNUSED(pipe);
0095     Q_UNUSED(in);
0096 #endif
0097 }
0098 
0099 /*
0100     Create the pipes to a K3bQProcessPrivate::Channel.
0101 
0102     This function must be called in order: stdin, stdout, stderr
0103 */
0104 bool K3bQProcessPrivate::createChannel(Channel &channel)
0105 {
0106     Q_Q(K3bQProcess);
0107 
0108     if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
0109         return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(),
0110                                &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
0111     }
0112 
0113     if (channel.type == Channel::Normal) {
0114         // we're piping this channel to our own process
0115         qt_create_pipe(channel.pipe, &channel == &stdinChannel);
0116 
0117         return true;
0118     } else if (channel.type == Channel::Redirect) {
0119         // we're redirecting the channel to/from a file
0120         SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
0121 
0122         if (&channel == &stdinChannel) {
0123             // try to open in read-only mode
0124             channel.pipe[1] = INVALID_Q_PIPE;
0125             QT_WA({
0126                 channel.pipe[0] =
0127                     CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
0128                                 GENERIC_READ,
0129                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
0130                                 &secAtt,
0131                                 OPEN_EXISTING,
0132                                 FILE_ATTRIBUTE_NORMAL,
0133                                 NULL);
0134             }, {
0135                 channel.pipe[0] =
0136                     CreateFileA(QFSFileEnginePrivate::win95Name(channel.file),
0137                                 GENERIC_READ,
0138                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
0139                                 &secAtt,
0140                                 OPEN_EXISTING,
0141                                 FILE_ATTRIBUTE_NORMAL,
0142                                 NULL);
0143             });
0144             if (channel.pipe[0] != INVALID_Q_PIPE)
0145                 return true;
0146 
0147             q->setErrorString(K3bQProcess::tr("Could not open input redirection for reading"));
0148         } else {
0149             // open in write mode
0150             channel.pipe[0] = INVALID_Q_PIPE;
0151             DWORD creation;
0152             if (channel.append)
0153                 creation = OPEN_ALWAYS;
0154             else
0155                 creation = CREATE_ALWAYS;
0156 
0157             QT_WA({
0158                 channel.pipe[1] =
0159                     CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
0160                                 GENERIC_WRITE,
0161                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
0162                                 &secAtt,
0163                                 creation,
0164                                 FILE_ATTRIBUTE_NORMAL,
0165                                 NULL);
0166             }, {
0167                 channel.pipe[1] =
0168                     CreateFileA(QFSFileEnginePrivate::win95Name(channel.file),
0169                                 GENERIC_WRITE,
0170                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
0171                                 &secAtt,
0172                                 creation,
0173                                 FILE_ATTRIBUTE_NORMAL,
0174                                 NULL);
0175             });
0176             if (channel.pipe[1] != INVALID_Q_PIPE) {
0177                 if (channel.append) {
0178                     SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
0179                 }
0180                 return true;
0181             }
0182 
0183             q->setErrorString(K3bQProcess::tr("Could not open output redirection for writing"));
0184         }
0185 
0186         // could not open file
0187         processError = QProcess::FailedToStart;
0188         emit q->error(processError);
0189         cleanup();
0190         return false;
0191     } else {
0192         Q_ASSERT_X(channel.process, "K3bQProcess::start", "Internal error");
0193 
0194         Channel *source;
0195         Channel *sink;
0196 
0197         if (channel.type == Channel::PipeSource) {
0198             // we are the source
0199             source = &channel;
0200             sink = &channel.process->stdinChannel;
0201 
0202             if (source->pipe[1] != INVALID_Q_PIPE) {
0203                 // already constructed by the sink
0204                 // make it inheritable
0205                 HANDLE tmpHandle = source->pipe[1];
0206                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
0207                                      GetCurrentProcess(), &source->pipe[1],
0208                                      0, TRUE, DUPLICATE_SAME_ACCESS))
0209                     return false;
0210 
0211                 CloseHandle(tmpHandle);
0212                 return true;
0213             }
0214 
0215             Q_ASSERT(source == &stdoutChannel);
0216             Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
0217 
0218             qt_create_pipe(source->pipe, /* in = */ false); // source is stdout
0219             sink->pipe[0] = source->pipe[0];
0220             source->pipe[0] = INVALID_Q_PIPE;
0221 
0222             return true;
0223         } else {
0224             // we are the sink;
0225             source = &channel.process->stdoutChannel;
0226             sink = &channel;
0227 
0228             if (sink->pipe[0] != INVALID_Q_PIPE) {
0229                 // already constructed by the source
0230                 // make it inheritable
0231                 HANDLE tmpHandle = sink->pipe[0];
0232                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
0233                                      GetCurrentProcess(), &sink->pipe[0],
0234                                      0, TRUE, DUPLICATE_SAME_ACCESS))
0235                     return false;
0236 
0237                 CloseHandle(tmpHandle);
0238                 return true;
0239             }
0240             Q_ASSERT(sink == &stdinChannel);
0241             Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
0242 
0243             qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin
0244             source->pipe[1] = sink->pipe[1];
0245             sink->pipe[1] = INVALID_Q_PIPE;
0246 
0247             return true;
0248         }
0249     }
0250 }
0251 
0252 void K3bQProcessPrivate::destroyPipe(Q_PIPE pipe[2])
0253 {
0254     if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) {
0255         delete pipeWriter;
0256         pipeWriter = 0;
0257     }
0258 
0259     if (pipe[0] != INVALID_Q_PIPE) {
0260         CloseHandle(pipe[0]);
0261         pipe[0] = INVALID_Q_PIPE;
0262     }
0263     if (pipe[1] != INVALID_Q_PIPE) {
0264         CloseHandle(pipe[1]);
0265         pipe[1] = INVALID_Q_PIPE;
0266     }
0267 }
0268 
0269 
0270 static QString qt_create_commandline(const QString &program, const QStringList &arguments)
0271 {
0272     QString args;
0273     if (!program.isEmpty()) {
0274         QString programName = program;
0275         if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1String(" ")))
0276             programName = QLatin1String("\"") + programName + QLatin1String("\"");
0277         programName.replace(QLatin1String("/"), QLatin1String("\\"));
0278 
0279         // add the prgram as the first arg ... it works better
0280         args = programName + QLatin1String(" ");
0281     }
0282 
0283     for (int i=0; i<arguments.size(); ++i) {
0284         QString tmp = arguments.at(i);
0285         // in the case of \" already being in the string the \ must also be escaped
0286         tmp.replace( QLatin1String("\\\""), QLatin1String("\\\\\"") );
0287         // escape a single " because the arguments will be parsed
0288         tmp.replace( QLatin1String("\""), QLatin1String("\\\"") );
0289         if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
0290             // The argument must not end with a \ since this would be interpreted
0291             // as escaping the quote -- rather put the \ behind the quote: e.g.
0292             // rather use "foo"\ than "foo\"
0293             QString endQuote(QLatin1String("\""));
0294             int i = tmp.length();
0295             while (i>0 && tmp.at(i-1) == QLatin1Char('\\')) {
0296                 --i;
0297                 endQuote += QLatin1String("\\");
0298             }
0299             args += QLatin1String(" \"") + tmp.left(i) + endQuote;
0300         } else {
0301             args += QLatin1Char(' ') + tmp;
0302         }
0303     }
0304     return args;
0305 }
0306 
0307 static QByteArray qt_create_environment(const QStringList &environment)
0308 {
0309     QByteArray envlist;
0310     if (!environment.isEmpty()) {
0311         QStringList envStrings = environment;
0312         int pos = 0;
0313         // add PATH if necessary (for DLL loading)
0314         if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) {
0315             QByteArray path = qgetenv("PATH");
0316             if (!path.isEmpty())
0317                 envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path)));
0318         }
0319         // add systemroot if needed
0320         if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) {
0321             QByteArray systemRoot = qgetenv("SystemRoot");
0322             if (!systemRoot.isEmpty())
0323                 envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot)));
0324         }
0325 #ifdef UNICODE
0326         if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) {
0327             for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) {
0328                 QString tmp = *it;
0329                 uint tmpSize = sizeof(TCHAR) * (tmp.length()+1);
0330                 envlist.resize(envlist.size() + tmpSize);
0331                 memcpy(envlist.data()+pos, tmp.utf16(), tmpSize);
0332                 pos += tmpSize;
0333             }
0334             // add the 2 terminating 0 (actually 4, just to be on the safe side)
0335             envlist.resize( envlist.size()+4 );
0336             envlist[pos++] = 0;
0337             envlist[pos++] = 0;
0338             envlist[pos++] = 0;
0339             envlist[pos++] = 0;
0340         } else
0341 #endif // UNICODE
0342         {
0343             for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) {
0344                 QByteArray tmp = (*it).toLocal8Bit();
0345                 uint tmpSize = tmp.length() + 1;
0346                 envlist.resize(envlist.size() + tmpSize);
0347                 memcpy(envlist.data()+pos, tmp.data(), tmpSize);
0348                 pos += tmpSize;
0349             }
0350             // add the terminating 0 (actually 2, just to be on the safe side)
0351             envlist.resize(envlist.size()+2);
0352             envlist[pos++] = 0;
0353             envlist[pos++] = 0;
0354         }
0355     }
0356     return envlist;
0357 }
0358 
0359 void K3bQProcessPrivate::startProcess()
0360 {
0361     Q_Q(K3bQProcess);
0362 
0363     bool success = false;
0364 
0365     if (pid) {
0366         CloseHandle(pid->hThread);
0367         CloseHandle(pid->hProcess);
0368         delete pid;
0369         pid = 0;
0370     }
0371     pid = new PROCESS_INFORMATION;
0372     memset(pid, 0, sizeof(PROCESS_INFORMATION));
0373 
0374     q->setProcessState(QProcess::Starting);
0375 
0376     if (!createChannel(stdinChannel) ||
0377         !createChannel(stdoutChannel) ||
0378         !createChannel(stderrChannel))
0379         return;
0380 
0381 #if defined(Q_OS_WINCE)
0382     QString args = qt_create_commandline(QString(), arguments);
0383 #else
0384     QString args = qt_create_commandline(program, arguments);
0385     QByteArray envlist = qt_create_environment(environment);
0386 #endif
0387 
0388 #if defined QPROCESS_DEBUG
0389     qDebug("Creating process");
0390     qDebug("   program : [%s]", program.toLatin1().constData());
0391     qDebug("   args : %s", args.toLatin1().constData());
0392     qDebug("   pass environment : %s", environment.isEmpty() ? "no" : "yes");
0393 #endif
0394 
0395     DWORD dwCreationFlags = 0;
0396     if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based))
0397         dwCreationFlags |= CREATE_NO_WINDOW;
0398 
0399 #ifdef UNICODE
0400     if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) {
0401 #if defined(Q_OS_WINCE)
0402         QString fullPathProgram = program;
0403         if (!QDir::isAbsolutePath(fullPathProgram))
0404             fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath();
0405         fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\"));
0406         success = CreateProcessW((WCHAR*)fullPathProgram.utf16(),
0407                                  (WCHAR*)args.utf16(),
0408                                  0, 0, false, 0, 0, 0, 0, pid);
0409 #else
0410         dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
0411         STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
0412                                      (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0413                                          (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0414                                          0, 0, 0,
0415                                          STARTF_USESTDHANDLES,
0416                                          0, 0, 0,
0417                                          stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
0418         };
0419         success = CreateProcessW(0, (WCHAR*)args.utf16(),
0420                                  0, 0, TRUE, dwCreationFlags,
0421                                  environment.isEmpty() ? 0 : envlist.data(),
0422                                  workingDirectory.isEmpty() ? 0
0423                                     : (WCHAR*)QDir::toNativeSeparators(workingDirectory).utf16(),
0424                                  &startupInfo, pid);
0425 #endif
0426     } else
0427 #endif // UNICODE
0428     {
0429 #ifndef Q_OS_WINCE
0430         STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0,
0431                                          (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0432                                          (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0433                                          0, 0, 0,
0434                                          STARTF_USESTDHANDLES,
0435                                          0, 0, 0,
0436                                          stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
0437         };
0438 
0439         success = CreateProcessA(0, args.toLocal8Bit().data(),
0440                                      0, 0, TRUE, dwCreationFlags, environment.isEmpty() ? 0 : envlist.data(),
0441                                      workingDirectory.isEmpty() ? 0
0442                                         : QDir::toNativeSeparators(workingDirectory).toLocal8Bit().data(),
0443                                      &startupInfo, pid);
0444 #endif // Q_OS_WINCE
0445     }
0446 #ifndef Q_OS_WINCE
0447     if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
0448         CloseHandle(stdinChannel.pipe[0]);
0449         stdinChannel.pipe[0] = INVALID_Q_PIPE;
0450     }
0451     if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
0452         CloseHandle(stdoutChannel.pipe[1]);
0453         stdoutChannel.pipe[1] = INVALID_Q_PIPE;
0454     }
0455     if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
0456         CloseHandle(stderrChannel.pipe[1]);
0457         stderrChannel.pipe[1] = INVALID_Q_PIPE;
0458     }
0459 #endif // Q_OS_WINCE
0460 
0461     if (!success) {
0462         cleanup();
0463         processError = QProcess::FailedToStart;
0464         q->setErrorString(K3bQProcess::tr("Process failed to start"));
0465         emit q->error(processError);
0466         q->setProcessState(QProcess::NotRunning);
0467         return;
0468     }
0469 
0470     q->setProcessState(QProcess::Running);
0471     // User can call kill()/terminate() from the stateChanged() slot
0472     // so check before proceeding 
0473     if (!pid) 
0474         return;
0475 
0476 //    if (threadData->eventDispatcher) {
0477         processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
0478         QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
0479         processFinishedNotifier->setEnabled(true);
0480         notifier = new QTimer(q);
0481         QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
0482         notifier->start(NOTIFYTIMEOUT);
0483 //    }
0484 
0485     // give the process a chance to start ...
0486     Sleep(SLEEPMIN*2);
0487     _q_startupNotification();
0488 }
0489 
0490 bool K3bQProcessPrivate::processStarted()
0491 {
0492     return processState == QProcess::Running;
0493 }
0494 
0495 qint64 K3bQProcessPrivate::bytesAvailableFromStdout() const
0496 {
0497     if (stdoutChannel.pipe[0] == INVALID_Q_PIPE)
0498         return 0;
0499 
0500     DWORD bytesAvail = 0;
0501 #if !defined(Q_OS_WINCE)
0502     PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
0503 #if defined QPROCESS_DEBUG
0504     qDebug("K3bQProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail);
0505 #endif
0506     if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
0507         QByteArray buf(bytesAvail, 0);
0508         DWORD bytesRead = 0;
0509         if (ReadFile(stdoutChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
0510             HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
0511             if (hStdout) {
0512                 DWORD bytesWritten = 0;
0513                 WriteFile(hStdout, buf.data(), bytesRead, &bytesWritten, 0);
0514             }
0515         }
0516         bytesAvail = 0;
0517     }
0518 #endif
0519     return bytesAvail;
0520 }
0521 
0522 qint64 K3bQProcessPrivate::bytesAvailableFromStderr() const
0523 {
0524     if (stderrChannel.pipe[0] == INVALID_Q_PIPE)
0525         return 0;
0526 
0527     DWORD bytesAvail = 0;
0528 #if !defined(Q_OS_WINCE)
0529     PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
0530 #if defined QPROCESS_DEBUG
0531     qDebug("K3bQProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail);
0532 #endif
0533     if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
0534         QByteArray buf(bytesAvail, 0);
0535         DWORD bytesRead = 0;
0536         if (ReadFile(stderrChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
0537             HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
0538             if (hStderr) {
0539                 DWORD bytesWritten = 0;
0540                 WriteFile(hStderr, buf.data(), bytesRead, &bytesWritten, 0);
0541             }
0542         }
0543         bytesAvail = 0;
0544     }
0545 #endif
0546     return bytesAvail;
0547 }
0548 
0549 qint64 K3bQProcessPrivate::readFromStdout(char *data, qint64 maxlen)
0550 {
0551     DWORD read = qMin(maxlen, bytesAvailableFromStdout());
0552     DWORD bytesRead = 0;
0553 
0554     if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0))
0555         return -1;
0556     return bytesRead;
0557 }
0558 
0559 qint64 K3bQProcessPrivate::readFromStderr(char *data, qint64 maxlen)
0560 {
0561     DWORD read = qMin(maxlen, bytesAvailableFromStderr());
0562     DWORD bytesRead = 0;
0563 
0564     if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0))
0565         return -1;
0566     return bytesRead;
0567 }
0568 
0569 
0570 static BOOL CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
0571 {
0572     DWORD currentProcId = 0;
0573     GetWindowThreadProcessId(hwnd, &currentProcId);
0574     if (currentProcId == (DWORD)procId)
0575         PostMessage(hwnd, WM_CLOSE, 0, 0);
0576 
0577     return TRUE;
0578 }
0579 
0580 void K3bQProcessPrivate::terminateProcess()
0581 {
0582     if (pid) {
0583         EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
0584         PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
0585     }
0586 }
0587 
0588 void K3bQProcessPrivate::killProcess()
0589 {
0590     if (pid)
0591         TerminateProcess(pid->hProcess, 0xf291);
0592 }
0593 
0594 bool K3bQProcessPrivate::waitForStarted(int)
0595 {
0596     Q_Q(K3bQProcess);
0597 
0598     if (processStarted())
0599         return true;
0600 
0601     if (processError == QProcess::FailedToStart)
0602         return false;
0603 
0604     processError = QProcess::Timedout;
0605     q->setErrorString(K3bQProcess::tr("Process operation timed out"));
0606     return false;
0607 }
0608 
0609 bool K3bQProcessPrivate::waitForReadyRead(int msecs)
0610 {
0611     Q_Q(K3bQProcess);
0612 
0613 #if defined(Q_OS_WINCE)
0614     processError = K3bQProcess::ReadError;
0615     q->setErrorString(K3bQProcess::tr("Error reading from process"));
0616     emit q->error(processError);
0617     return false;
0618 #endif
0619 
0620     QIncrementalSleepTimer timer(msecs);
0621 
0622     forever {
0623         if (!writeBuffer.isEmpty() && !_q_canWrite())
0624             return false;
0625         if (pipeWriter && pipeWriter->waitForWrite(0))
0626             timer.resetIncrements();
0627         bool readyReadEmitted = false;
0628         if (bytesAvailableFromStdout() != 0) {
0629             readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted;
0630             timer.resetIncrements();
0631         }
0632 
0633         if (bytesAvailableFromStderr() != 0) {
0634             readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted;
0635             timer.resetIncrements();
0636         }
0637 
0638         if (readyReadEmitted)
0639             return true;
0640 
0641         if (!pid)
0642             return false;
0643         if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
0644             // find the return value if there is noew data to read
0645             _q_processDied();
0646             return false;
0647         }
0648 
0649         Sleep(timer.nextSleepTime());
0650         if (timer.hasTimedOut())
0651             break;
0652     }
0653 
0654     processError = QProcess::Timedout;
0655     q->setErrorString(K3bQProcess::tr("Process operation timed out"));
0656     return false;
0657 }
0658 
0659 bool K3bQProcessPrivate::waitForBytesWritten(int msecs)
0660 {
0661     Q_Q(K3bQProcess);
0662 
0663 #if defined(Q_OS_WINCE)
0664     processError = K3bQProcess::ReadError;
0665     q->setErrorString(K3bQProcess::tr("Error reading from process"));
0666     emit q->error(processError);
0667     return false;
0668 #endif
0669 
0670     QIncrementalSleepTimer timer(msecs);
0671 
0672     forever {
0673         // Check if we have any data pending: the pipe writer has
0674         // bytes waiting to written, or it has written data since the
0675         // last time we called pipeWriter->waitForWrite().
0676         bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten());
0677 
0678         // If we don't have pending data, and our write buffer is
0679         // empty, we fail.
0680         if (!pendingDataInPipe && writeBuffer.isEmpty())
0681             return false;
0682 
0683         // If we don't have pending data and we do have data in our
0684         // write buffer, try to flush that data over to the pipe
0685         // writer.  Fail on error.
0686         if (!pendingDataInPipe) {
0687             if (!_q_canWrite())
0688                 return false;
0689         }
0690 
0691         // Wait for the pipe writer to acknowledge that it has
0692         // written. This will succeed if either the pipe writer has
0693         // already written the data, or if it manages to write data
0694         // within the given timeout. If the write buffer was non-empty
0695         // and the pipeWriter is now dead, that means _q_canWrite()
0696         // destroyed the writer after it successfully wrote the last
0697         // batch.
0698         if (!pipeWriter || pipeWriter->waitForWrite(0))
0699             return true;
0700 
0701         // If we wouldn't write anything, check if we can read stdout.
0702         if (bytesAvailableFromStdout() != 0) {
0703             _q_canReadStandardOutput();
0704             timer.resetIncrements();
0705         }
0706 
0707         // Check if we can read stderr.
0708         if (bytesAvailableFromStderr() != 0) {
0709             _q_canReadStandardError();
0710             timer.resetIncrements();
0711         }
0712 
0713         // Check if the process died while reading.
0714         if (!pid)
0715             return false;
0716 
0717         // Wait for the process to signal any change in its state,
0718         // such as incoming data, or if the process died.
0719         if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
0720             _q_processDied();
0721             return false;
0722         }
0723 
0724         // Only wait for as long as we've been asked.
0725         if (timer.hasTimedOut())
0726             break;
0727     }
0728 
0729     processError = QProcess::Timedout;
0730     q->setErrorString(K3bQProcess::tr("Process operation timed out"));
0731     return false;
0732 }
0733 
0734 
0735 bool K3bQProcessPrivate::waitForFinished(int msecs)
0736 {
0737     Q_Q(K3bQProcess);
0738 #if defined QPROCESS_DEBUG
0739     qDebug("K3bQProcessPrivate::waitForFinished(%d)", msecs);
0740 #endif
0741 
0742     QIncrementalSleepTimer timer(msecs);
0743 
0744     forever {
0745         if (!writeBuffer.isEmpty() && !_q_canWrite())
0746             return false;
0747         if (pipeWriter && pipeWriter->waitForWrite(0))
0748             timer.resetIncrements();
0749 
0750         if (bytesAvailableFromStdout() != 0) {
0751             _q_canReadStandardOutput();
0752             timer.resetIncrements();
0753         }
0754 
0755         if (bytesAvailableFromStderr() != 0) {
0756             _q_canReadStandardError();
0757             timer.resetIncrements();
0758         }
0759 
0760         if (!pid)
0761             return true;
0762 
0763         if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
0764             _q_processDied();
0765             return true;
0766         }
0767 
0768         if (timer.hasTimedOut())
0769             break;
0770     }
0771     processError = QProcess::Timedout;
0772     q->setErrorString(K3bQProcess::tr("Process operation timed out"));
0773     return false;
0774 }
0775 
0776 
0777 void K3bQProcessPrivate::findExitCode()
0778 {
0779     DWORD theExitCode;
0780     if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
0781         exitCode = theExitCode;
0782         //### for now we assume a crash if exit code is less than -1 or the magic number
0783         crashed = (exitCode == 0xf291 || (int)exitCode < 0);
0784     }
0785 }
0786 
0787 void K3bQProcessPrivate::flushPipeWriter()
0788 {
0789     if (pipeWriter && pipeWriter->bytesToWrite() > 0) {
0790         pipeWriter->waitForWrite(ULONG_MAX);
0791     }
0792 }
0793 
0794 qint64 K3bQProcessPrivate::pipeWriterBytesToWrite() const
0795 {
0796     return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
0797 }
0798 
0799 qint64 K3bQProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
0800 {
0801     Q_Q(K3bQProcess);
0802 
0803 #if defined(Q_OS_WINCE)
0804     processError = K3bQProcess::WriteError;
0805     q->setErrorString(K3bQProcess::tr("Error writing to process"));
0806     emit q->error(processError);
0807     return -1;
0808 #endif
0809 
0810     if (!pipeWriter) {
0811         pipeWriter = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
0812         pipeWriter->start();
0813     }
0814 
0815     return pipeWriter->write(data, maxlen);
0816 }
0817 
0818 bool K3bQProcessPrivate::waitForWrite(int msecs)
0819 {
0820     Q_Q(K3bQProcess);
0821 
0822     if (!pipeWriter || pipeWriter->waitForWrite(msecs))
0823         return true;
0824 
0825     processError = QProcess::Timedout;
0826     q->setErrorString(K3bQProcess::tr("Process operation timed out"));
0827     return false;
0828 }
0829 
0830 void K3bQProcessPrivate::_q_notified()
0831 {
0832     notifier->stop();
0833 
0834     if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0)))
0835         _q_canWrite();
0836 
0837     if (bytesAvailableFromStdout())
0838         _q_canReadStandardOutput();
0839 
0840     if (bytesAvailableFromStderr())
0841         _q_canReadStandardError();
0842 
0843     if (processState != QProcess::NotRunning)
0844         notifier->start(NOTIFYTIMEOUT);
0845 }
0846 
0847 bool K3bQProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
0848 {
0849 #if defined(Q_OS_WINCE)
0850     Q_UNUSED(workingDir);
0851     QString args = qt_create_commandline(QString(), arguments);
0852 #else
0853     QString args = qt_create_commandline(program, arguments);
0854 #endif
0855 
0856     bool success = false;
0857 
0858     PROCESS_INFORMATION pinfo;
0859 
0860 #ifdef UNICODE
0861     if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) {
0862 #if defined(Q_OS_WINCE)
0863         QString fullPathProgram = program;
0864         if (!QDir::isAbsolutePath(fullPathProgram))
0865             fullPathProgram.prepend(QDir::currentPath().append(QLatin1String("/")));
0866         fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\"));
0867         success = CreateProcessW((WCHAR*)fullPathProgram.utf16(),
0868                                  (WCHAR*)args.utf16(),
0869                                  0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo);
0870 #else
0871         STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
0872                                      (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0873                                      (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0874                                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0875                                    };
0876         success = CreateProcessW(0, (WCHAR*)args.utf16(),
0877                                  0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0, 
0878                                  workingDir.isEmpty() ? (const WCHAR *)0 : (const WCHAR *)workingDir.utf16(),
0879                                  &startupInfo, &pinfo);
0880 #endif
0881     } else
0882 #endif // UNICODE
0883     {
0884 #ifndef Q_OS_WINCE
0885        STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0,
0886                                      (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0887                                      (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0888                                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0889                                   };
0890        success = CreateProcessA(0, args.toLocal8Bit().data(),
0891                                 0, 0, FALSE, CREATE_NEW_CONSOLE, 0,
0892                                 workingDir.isEmpty() ? (LPCSTR)0 : workingDir.toLocal8Bit().constData(),
0893                                 &startupInfo, &pinfo);
0894 #endif // Q_OS_WINCE
0895     }
0896 
0897     if (success) {
0898         CloseHandle(pinfo.hThread);
0899         CloseHandle(pinfo.hProcess);
0900         if (pid)
0901             *pid = pinfo.dwProcessId;
0902     }
0903 
0904     return success;
0905 }
0906 
0907 QT_END_NAMESPACE
0908 
0909 #endif // QT_NO_PROCESS