source: trunk/src/kernel/qprocess_pm.cpp@ 138

Last change on this file since 138 was 134, checked in by dmik, 19 years ago

Kernel: QProcess: Significantly improved the speed of reading data from the child process using readyReadStd[out|err]() signals (it's now approximately 380 times faster on my system for a 28 MB file).

  • Property svn:keywords set to Id
File size: 46.2 KB
Line 
1/****************************************************************************
2** $Id: qprocess_pm.cpp 134 2006-10-08 22:14:18Z dmik $
3**
4** Implementation of QProcess class for OS/2
5**
6** Copyright (C) 1992-2001 Trolltech AS. All rights reserved.
7** Copyright (C) 2005 netlabs.org. OS/2 Version.
8**
9** This file is part of the kernel module of the Qt GUI Toolkit.
10**
11** This file may be distributed under the terms of the Q Public License
12** as defined by Trolltech AS of Norway and appearing in the file
13** LICENSE.QPL included in the packaging of this file.
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
21** licenses may use this file in accordance with the Qt Commercial License
22** Agreement provided with the Software.
23**
24** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26**
27** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
28** information about Qt Commercial License Agreements.
29** See http://www.trolltech.com/qpl/ for QPL licensing information.
30** See http://www.trolltech.com/gpl/ for GPL licensing information.
31**
32** Contact info@trolltech.com if any conditions of this licensing are
33** not clear to you.
34**
35**********************************************************************/
36
37#include "qplatformdefs.h"
38#include "qprocess.h"
39
40#ifndef QT_NO_PROCESS
41
42#include "qapplication.h"
43#include "qptrqueue.h"
44#include "qtimer.h"
45#include "qregexp.h"
46#include "qdir.h"
47#include "qthread.h"
48#include "qintdict.h"
49#include "qmutex.h"
50#include "private/qinternal_p.h"
51#include "qt_os2.h"
52
53#include <string.h>
54
55//#define QT_QPROCESS_DEBUG
56
57#define HF_STDIN HFILE( 0 )
58#define HF_STDOUT HFILE( 1 )
59#define HF_STDERR HFILE( 2 )
60#define HF_NULL HFILE( ~0 )
61
62#define HP_NULL HPIPE( ~0 )
63#define KEY_NULL USHORT( ~0 )
64
65#define PID_NULL PID( ~0 )
66
67enum
68{
69 PIPE_SIZE_STDIN = 65520, // max
70 PIPE_SIZE_STDOUT = 65520, // max
71 PIPE_SIZE_STDERR = 4096,
72
73 POLL_TIMER = 100,
74
75 // new pipe data notification
76 WM_U_PIPE_RDATA = WM_USER + 0,
77 WM_U_PIPE_CLOSE = WM_USER + 1,
78};
79
80#if defined(QT_QPROCESS_DEBUG)
81#include <stdarg.h>
82static HFILE StdErrHandle = HF_NULL;
83QtMsgHandler OldMsgHandler = NULL;
84static void StdErrMsgHandler( QtMsgType type, const char *msg )
85{
86 if ( OldMsgHandler == NULL ) {
87 size_t len = strlen( msg );
88 ULONG written = 0;
89 DosWrite( StdErrHandle, msg, len, &written );
90 const char *EOL = "\n\r";
91 DosWrite( StdErrHandle, EOL, 2, &written );
92 } else {
93 OldMsgHandler( type, msg );
94 }
95}
96#define InstallQtMsgHandler() \
97 do { \
98 DosDupHandle( HF_STDERR, &StdErrHandle ); \
99 qInstallMsgHandler( StdErrMsgHandler ); \
100 } while (0)
101#define UninstallQtMsgHandler() \
102 do { \
103 qInstallMsgHandler( OldMsgHandler ); \
104 DosClose( StdErrHandle ); \
105 } while (0)
106#else
107#define InstallQtMsgHandler() do {} while (0)
108#define UninstallQtMsgHandler() do {} while (0)
109#endif
110
111/***********************************************************************
112 *
113 * QProcessPrivate
114 *
115 **********************************************************************/
116class QProcessPrivate
117{
118public:
119 struct Pipe
120 {
121 Pipe () : pipe( HP_NULL ), key( KEY_NULL ), pending( 0 )
122 , closed( false ) {}
123 HPIPE pipe;
124 USHORT key;
125 QMembuf buf;
126 // note: when QProcess is watched by QProcessMonitor, the below fields
127 // must be accessed from under the QProcessMonitor lock
128 uint pending;
129 bool closed : 1;
130 };
131
132 QProcessPrivate( QProcess *proc )
133 {
134 stdinBufRead = 0;
135 pipeStdin = HP_NULL;
136 pid = PID_NULL;
137 exitValuesCalculated = FALSE;
138
139 lookup = new QTimer( proc );
140 qApp->connect( lookup, SIGNAL(timeout()), proc, SLOT(timeout()) );
141 }
142
143 ~QProcessPrivate()
144 {
145 reset();
146 }
147
148 void reset()
149 {
150 while ( !stdinBuf.isEmpty() ) {
151 delete stdinBuf.dequeue();
152 }
153 stdinBufRead = 0;
154 closeHandles();
155 stdout.buf.clear();
156 stderr.buf.clear();
157 pid = PID_NULL;
158 exitValuesCalculated = FALSE;
159 }
160
161 Pipe *findPipe( USHORT key )
162 {
163 if ( stdout.key == key ) return &stdout;
164 if ( stderr.key == key ) return &stderr;
165 return NULL;
166 }
167
168 void closePipe( Pipe *pipe )
169 {
170 if ( pipe->pipe != HP_NULL ) {
171 Q_ASSERT( pipe->key == KEY_NULL );
172 DosDisConnectNPipe( pipe->pipe );
173 DosClose( pipe->pipe );
174 pipe->pipe = HP_NULL;
175 pipe->pending = 0;
176 pipe->closed = FALSE;
177 }
178 }
179
180 bool readPipe( Pipe *pipe );
181
182 void closeHandles()
183 {
184 if( pipeStdin != HP_NULL ) {
185 DosDisConnectNPipe( pipeStdin );
186 DosClose( pipeStdin );
187 pipeStdin = HP_NULL;
188 }
189 closePipe( &stdout );
190 closePipe( &stderr );
191 }
192
193 QPtrQueue <QByteArray> stdinBuf;
194 uint stdinBufRead;
195
196 HPIPE pipeStdin;
197
198 Pipe stdout;
199 Pipe stderr;
200
201 PID pid;
202
203 QTimer *lookup;
204
205 bool exitValuesCalculated : 1;
206};
207
208class QProcessMonitor : public QPMObjectWindow
209{
210public:
211 class Thread : public QThread
212 {
213 public:
214 Thread( QProcessMonitor *m ) : mon( m ) {}
215 void run() { mon->monitor(); }
216 QProcessMonitor *mon;
217 };
218
219 QProcessMonitor();
220 ~QProcessMonitor();
221
222 QMutex &mlock() { return lock; }
223
224 bool isOk() const { return pipeSem != 0 && thread != NULL; }
225
226 bool addProcess( QProcess *proc );
227 void removeProcess( QProcessPrivate *d,
228 QProcessPrivate::Pipe *pipe = NULL );
229
230 void monitor();
231 MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
232
233private:
234
235 struct PipeStates
236 {
237 PipeStates() {
238 size = 4;
239 arr = new PIPESEMSTATE[ size ];
240 }
241 ~PipeStates() {
242 delete[] arr;
243 }
244 void ensure( size_t sz ) {
245 // best size for sz pipes is sz * 2 (to be able to store both
246 // NPSS_RDATA & NPSS_CLOSE for every pipe) + one for NPSS_EOI
247 size_t newSize = sz * 2 + 1;
248 newSize = ((newSize + 5 - 1) / 5) * 5;
249 if ( newSize != size ) {
250 delete[] arr;
251 size = newSize;
252 arr = new PIPESEMSTATE[ size ];
253 }
254 }
255 size_t dataSize() { return size * sizeof(PIPESEMSTATE); }
256 PIPESEMSTATE *data() { return arr; }
257 PIPESEMSTATE &operator[]( size_t i ) {
258 Q_ASSERT( i < size );
259 return arr[i];
260 }
261 private:
262 PIPESEMSTATE *arr;
263 size_t size;
264 };
265
266 bool addPipe( QProcess *proc, QProcessPrivate::Pipe *pipe );
267 void removePipe( QProcessPrivate::Pipe *pipe );
268
269 HEV pipeSem;
270 Thread *thread;
271 bool threadRunning;
272 QIntDict<QProcess> pipeKeys;
273 PipeStates pipeStates;
274 QMutex lock;
275};
276
277QProcessMonitor::QProcessMonitor()
278 : pipeSem( 0 ), thread( NULL ), threadRunning( FALSE )
279{
280 APIRET rc = DosCreateEventSem( NULL, &pipeSem, DC_SEM_SHARED, 0 );
281 if ( rc != NO_ERROR ) {
282#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
283 qSystemWarning( "Failed to create a semaphore!", rc );
284#endif
285 return;
286 }
287
288 thread = new Thread( this );
289 Q_ASSERT( thread != NULL );
290}
291
292QProcessMonitor::~QProcessMonitor()
293{
294 if ( thread ) {
295 lock.lock();
296 if ( threadRunning ) {
297 threadRunning = FALSE;
298 DosPostEventSem( pipeSem );
299 }
300 lock.unlock();
301 thread->wait();
302 delete thread;
303 }
304
305 if ( pipeSem )
306 DosCloseEventSem( pipeSem );
307}
308
309bool QProcessMonitor::addPipe( QProcess *proc, QProcessPrivate::Pipe *pipe )
310{
311 // Note: we use HPIPE as pipe keys in DosSetNPipeSem. However, HPIPE
312 // is 32-bit (ulong), while usKey in PIPESEMSTATE is 16-bit (USHORT)
313 // We unreasonably assume that the system will never assign HPIPE >= 0xFFFF,
314 // and just cast HPIPE to USHORT. There is an assertion checking this
315 // condition, so this method should simply return FALSE once our assumption
316 // breaks.
317
318 Q_ASSERT( pipe->pipe < HPIPE( KEY_NULL ) );
319 if ( pipe->pipe >= HPIPE( KEY_NULL ) )
320 return FALSE;
321
322 Q_ASSERT( pipe->pipe != HP_NULL && pipe->key == KEY_NULL );
323
324 pipe->key = USHORT( pipe->pipe );
325 APIRET rc = DosSetNPipeSem( pipe->pipe, (HSEM) pipeSem, pipe->key );
326 if ( rc != NO_ERROR ) {
327#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
328 qSystemWarning( "Failed to set the pipe semaphore!", rc );
329#endif
330 return FALSE;
331 }
332
333 pipeKeys.insert( pipe->key, proc );
334 return TRUE;
335}
336
337void QProcessMonitor::removePipe( QProcessPrivate::Pipe *pipe )
338{
339 Q_ASSERT( pipe->pipe != HP_NULL && pipe->key != KEY_NULL );
340
341 /// @todo (r=dmik) is this really necessary to detach pipeSem?
342 DosSetNPipeSem( pipe->pipe, 0, 0 );
343 bool ok = pipeKeys.remove( pipe->key );
344 pipe->key = KEY_NULL;
345 Q_ASSERT( ok );
346 Q_UNUSED( ok );
347}
348
349bool QProcessMonitor::addProcess( QProcess *proc )
350{
351#if defined(QT_QPROCESS_DEBUG)
352 qDebug( "QProcessMonitor::addProcess(): proc=%p d=%p", proc, proc->d );
353#endif
354
355 QProcessPrivate *d = proc->d;
356
357 // check if we need to monitor this process before entering the lock
358 if ( d->stdout.pipe == HP_NULL && d->stderr.pipe == HP_NULL )
359 return TRUE;
360
361 uint newPipes = 0;
362
363 QMutexLocker locker( &lock );
364
365 if ( d->stdout.pipe != HP_NULL ) {
366 if ( !addPipe( proc, &d->stdout ) )
367 return FALSE;
368 ++ newPipes;
369 }
370
371 if ( d->stderr.pipe != HP_NULL ) {
372 if ( !addPipe( proc, &d->stderr ) ) {
373 removePipe( &d->stderr );
374 return FALSE;
375 }
376 ++ newPipes;
377 }
378
379 Q_ASSERT( newPipes > 0 );
380
381 pipeStates.ensure( pipeKeys.count() );
382
383 // start the monitor thread if necessary
384 if ( pipeKeys.count() == newPipes ) {
385 threadRunning = TRUE;
386 thread->start();
387 }
388
389#if defined(QT_QPROCESS_DEBUG)
390 qDebug( "QProcessMonitor::addProcess(): added %d pipes", newPipes );
391#endif
392 return TRUE;
393}
394
395void QProcessMonitor::removeProcess( QProcessPrivate *d,
396 QProcessPrivate::Pipe *pipe /* = NULL */)
397{
398#if defined(QT_QPROCESS_DEBUG)
399 qDebug( "QProcessMonitor::removeProcess(): d=%p pipe=%p key=%04hX",
400 d, pipe, pipe ? pipe->key : KEY_NULL );
401#endif
402
403 // check if we monitor this process before entering the lock
404 if ( d->stdout.pipe == HP_NULL && d->stderr.pipe == HP_NULL )
405 return;
406
407 lock.lock();
408
409 if ( pipeKeys.count() == 0 ) {
410 // Nothing to do. This is an outdated general removeProcess (d, NULL)
411 // call from the QProcess destructor or from isRunning(). Just return.
412 lock.unlock();
413 return;
414 }
415
416 if ( pipe ) {
417 removePipe( pipe );
418 } else {
419 if ( d->stdout.pipe != HP_NULL && d->stdout.key != KEY_NULL )
420 removePipe( &d->stdout );
421 if ( d->stderr.pipe != HP_NULL && d->stderr.key != KEY_NULL )
422 removePipe( &d->stderr );
423 }
424
425 pipeStates.ensure( pipeKeys.count() );
426
427 // stop the monitor thread if no more necessary
428 if ( pipeKeys.count() == 0 ) {
429 Q_ASSERT( threadRunning );
430 if ( threadRunning ) {
431 threadRunning = FALSE;
432 DosPostEventSem( pipeSem );
433 }
434 lock.unlock();
435 thread->wait();
436 } else {
437 lock.unlock();
438 }
439}
440
441/** Monitor thread function */
442void QProcessMonitor::monitor()
443{
444#if defined(QT_QPROCESS_DEBUG)
445 qDebug( "QProcessMonitor::monitor() ENTER" );
446#endif
447
448 lock.lock();
449 while( 1 ) {
450 lock.unlock();
451 APIRET rc = DosWaitEventSem( pipeSem, SEM_INDEFINITE_WAIT );
452 lock.lock();
453 if ( rc != NO_ERROR ) {
454#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
455 qSystemWarning( "Failed to wait on event semaphore!", rc );
456#endif
457 break;
458 }
459
460 ULONG posts = 0;
461 DosResetEventSem( pipeSem, &posts );
462#if defined(QT_QPROCESS_DEBUG)
463 qDebug( "QProcessMonitor::monitor(): got semaphore (posts=%ld)", posts );
464#endif
465
466 if ( !threadRunning )
467 break;
468
469 rc = DosQueryNPipeSemState( (HSEM) pipeSem, pipeStates.data(),
470 pipeStates.dataSize() );
471 if ( rc != NO_ERROR ) {
472#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
473 qSystemWarning( "Failed to query pipe semaphore state!", rc );
474#endif
475 continue;
476 }
477
478 // In the returned information array, CLOSE and READ records for the
479 // same pipe key may be mixed. We need CLOSE messages to be posted after
480 // READ messages, so we do two passes.
481
482 int pass = 0;
483 for ( int i = 0; pass < 2; ++ i ) {
484 if ( pipeStates[i].fStatus == NPSS_EOI ) {
485 ++ pass;
486 i = -1;
487 continue;
488 }
489 if ( pass == 0 && pipeStates[i].fStatus != NPSS_RDATA )
490 continue;
491 if ( pass == 1 && pipeStates[i].fStatus != NPSS_CLOSE )
492 continue;
493
494#if defined(QT_QPROCESS_DEBUG)
495 qDebug( " %d/%d: fStatus=%u fFlag=%02X usKey=%04hX usAvail=%hu",
496 pass, i, (uint) pipeStates[i].fStatus,
497 (uint) pipeStates[i].fFlag, pipeStates[i].usKey,
498 pipeStates[i].usAvail );
499#endif
500 QProcess *proc = pipeKeys.find( pipeStates[i].usKey );
501 Q_ASSERT( proc );
502 if ( !proc )
503 continue;
504#if defined(QT_QPROCESS_DEBUG)
505 qDebug( " proc=%p (%s/%s) d=%p",
506 proc, proc->name(), proc->className(), proc->d );
507#endif
508 QProcessPrivate::Pipe *pipe = proc->d->findPipe( pipeStates[i].usKey );
509 Q_ASSERT( pipe );
510 if ( !pipe )
511 continue;
512
513 if ( pipeStates[i].fStatus == NPSS_RDATA ) {
514 bool wasPending = pipe->pending > 0;
515 pipe->pending = pipeStates[i].usAvail;
516 // inform the GUI thread that there is new data
517 if ( !wasPending )
518 WinPostMsg( hwnd(), WM_U_PIPE_RDATA, MPFROMP( proc ),
519 MPFROMSHORT( pipe->key ) );
520 } else if ( pipeStates[i].fStatus == NPSS_CLOSE ) {
521 // there may be repeated CLOSE records for the same pipe
522 // in subsequent DosQueryNPipeSemState() calls
523 if ( pipe->closed == FALSE ) {
524 pipe->closed = TRUE;
525 // inform the GUI thread that the client's pipe end
526 // was closed
527 WinPostMsg( hwnd(), WM_U_PIPE_CLOSE, MPFROMP( proc ),
528 MPFROMSHORT( pipe->key ) );
529 }
530 }
531 }
532 }
533 lock.unlock();
534
535#if defined(QT_QPROCESS_DEBUG)
536 qDebug( "QProcessMonitor::monitor() LEAVE" );
537#endif
538}
539
540MRESULT QProcessMonitor::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
541{
542 switch ( msg ) {
543 case WM_U_PIPE_RDATA: {
544 QProcess *proc = (QProcess *) PVOIDFROMMP( mp1 );
545 USHORT key = SHORT1FROMMP( mp2 );
546#if defined(QT_QPROCESS_DEBUG)
547 qDebug( "QProcessMonitor::WM_U_PIPE_RDATA: proc=%p (%s/%s) d=%p "
548 "key=%04hX",
549 proc, proc->name(), proc->className(), proc->d, key );
550#endif
551 // check parameter validity (we can safely do it outside the lock
552 // because the GUI thread is the only that can modify pipeKeys)
553 if ( proc == pipeKeys.find( key ) ) {
554 QProcessPrivate *d = proc->d;
555 if ( d->stdout.key == key ) {
556 emit proc->readyReadStdout();
557 } else {
558 Q_ASSERT( d->stderr.key == key );
559 emit proc->readyReadStderr();
560 }
561 }
562 break;
563 }
564 case WM_U_PIPE_CLOSE: {
565 QProcess *proc = (QProcess *) PVOIDFROMMP( mp1 );
566 USHORT key = SHORT1FROMMP( mp2 );
567#if defined(QT_QPROCESS_DEBUG)
568 qDebug( "QProcessMonitor::WM_U_PIPE_CLOSE: proc=%p (%s/%s) d=%p "
569 "key=%04hX",
570 proc, proc->name(), proc->className(), proc->d, key );
571#endif
572 // check parameter validity (we can safely do it outside the lock
573 // because the GUI thread is the only that can modify pipeKeys)
574 if ( proc == pipeKeys.find( key ) ) {
575 QProcessPrivate *d = proc->d;
576 QProcessPrivate::Pipe *pipe = d->findPipe( key );
577 Q_ASSERT( pipe );
578 if ( pipe ) {
579 Q_ASSERT( pipe->closed );
580 // remove the single pipe from watching
581 removeProcess( d, pipe );
582 // close the pipe since no more necessary
583 // (pipe is not watched anymore, no need to lock access)
584 if ( pipe->pending == 0 )
585 d->closePipe( pipe );
586 }
587 }
588 break;
589 }
590 default:
591 break;
592 }
593
594 return FALSE;
595}
596
597static QProcessMonitor *processMonitor = NULL;
598
599void QProcessMonitor_cleanup()
600{
601#if defined(QT_QPROCESS_DEBUG)
602 qDebug( "QProcessMonitor_cleanup()" );
603#endif
604 delete processMonitor;
605 processMonitor = NULL;
606}
607
608/** Returns true if some data was successfully read */
609bool QProcessPrivate::readPipe( Pipe *pipe )
610{
611#if defined(QT_QPROCESS_DEBUG)
612 qDebug( "QProcessPrivate::readPipe(): this=%p key=%04hX", this, pipe->key );
613#endif
614
615 if ( pipe->pipe == HP_NULL )
616 return false;
617
618 // Pipe::pending and Pipe::closed need the lock
619 QMutex *lock = NULL;
620
621 // Notes:
622 // 1. If procesMonitor is NULL, it which means we're somewhere inside
623 // the QApplication destruction procedure.
624 // 2. If pipe->key us KEY_NULL, it means GUI thread has already processed
625 // the WM_U_PIPE_CLOSE message and we're free to access fields w/o a
626 // lock. pipe->key is assigned only on the GUI thread, so it's safe
627 // to check it here w/o the lock.
628
629 if ( processMonitor && pipe->key != KEY_NULL )
630 lock = &processMonitor->mlock();
631
632 QByteArray *ba = NULL;
633 bool close = FALSE;
634
635 {
636 QMutexLocker locker( lock );
637
638 if ( pipe->pending == 0 )
639 return false;
640
641 ba = new QByteArray( pipe->pending );
642
643 ULONG read = 0;
644 APIRET rc = DosRead( pipe->pipe, ba->data(), pipe->pending, &read );
645 if ( rc != NO_ERROR ) {
646 delete ba;
647#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
648 qSystemWarning( "Failed to read from the pipe!", rc );
649#endif
650 return false;
651 }
652
653 Q_ASSERT( read == pipe->pending );
654 if ( read < pipe->pending )
655 ba->resize( read );
656
657 pipe->pending -= read;
658
659 close = pipe->pending == 0 && pipe->closed;
660 }
661
662 if ( close ) {
663 // the client's end of pipe has been closed, so close our end as well
664 if ( processMonitor && pipe->key != KEY_NULL ) {
665 // WM_U_PIPE_CLOSE has been posted but not yet processed
666 processMonitor->removeProcess( this, pipe );
667 }
668 closePipe( pipe );
669 }
670
671 pipe->buf.append( ba );
672 return true;
673}
674
675/***********************************************************************
676 *
677 * QProcess
678 *
679 **********************************************************************/
680
681void QProcess::init()
682{
683 d = new QProcessPrivate( this );
684 exitStat = 0;
685 exitNormal = FALSE;
686
687#if defined(QT_QPROCESS_DEBUG)
688 qDebug( "~QProcess::init(): d=%p", d );
689#endif
690}
691
692void QProcess::reset()
693{
694 // remove monitoring if the monitor is there (note that it's done before
695 // resetting QProcessPrivate which has fields protected by the
696 // QProcessMonitor lock)
697 if ( processMonitor )
698 processMonitor->removeProcess( d );
699
700 d->reset();
701 exitStat = 0;
702 exitNormal = FALSE;
703}
704
705QMembuf* QProcess::membufStdout()
706{
707 if( d->stdout.pipe != 0 )
708 d->readPipe( &d->stdout );
709 return &d->stdout.buf;
710}
711
712QMembuf* QProcess::membufStderr()
713{
714 if( d->stderr.pipe != 0 )
715 d->readPipe( &d->stderr );
716 return &d->stderr.buf;
717}
718
719QProcess::~QProcess()
720{
721#if defined(QT_QPROCESS_DEBUG)
722 qDebug( "~QProcess::QProcess(): d=%p", d );
723#endif
724
725 // remove monitoring if the monitor is there (note that it's done before
726 // resetting QProcessPrivate which has fields protected by the
727 // QProcessMonitor lock)
728 if ( processMonitor )
729 processMonitor->removeProcess( d );
730
731 delete d;
732}
733
734bool QProcess::start( QStringList *env )
735{
736#if defined(QT_QPROCESS_DEBUG)
737 qDebug( "QProcess::start()" );
738#endif
739
740 reset();
741
742 if ( _arguments.isEmpty() )
743 return FALSE;
744
745 // construct the arguments for DosExecPgm()
746
747 QCString appName, appArgs;
748 {
749 QString args;
750 for ( QStringList::ConstIterator it = _arguments.begin();
751 it != _arguments.end(); ++ it
752 ) {
753 if ( it == _arguments.begin() ) {
754 appName = (*it).local8Bit();
755 } else {
756 QString s = *it;
757 // quote the argument when it contains spaces
758 if ( s.contains( ' ' ) )
759 s = '"' + s + '"';
760 if ( args.isNull() )
761 args = s;
762 else
763 args += ' ' + s;
764 }
765 }
766 appArgs = args.local8Bit();
767
768#if defined(QT_QPROCESS_DEBUG)
769 qDebug( "QProcess::start(): app [%s], args [%s]",
770 appName.data(), appArgs.data() );
771#endif
772 }
773
774 QByteArray envlist;
775 if ( env != 0 ) {
776 uint pos = 0;
777 // inherit PATH if missing (for convenience)
778 // (note that BEGINLIBPATH and ENDLIBPATH, if any, are automatically
779 // inherited, while LIBPATH is always a global setting)
780 {
781 const char *path = getenv( "PATH" );
782 if ( env->grep( QRegExp( "^PATH=", FALSE ) ).empty() && path ) {
783 uint sz = strlen( path ) + 5 /* PATH= */ + 1;
784 envlist.resize( envlist.size() + sz );
785 sprintf( envlist.data() + pos, "PATH=%s", path );
786 pos += sz;
787 }
788 }
789 // inherit COMSPEC if missing (to let the child start .cmd and .bat)
790 {
791 const char *comspec = getenv( "COMSPEC" );
792 if ( env->grep( QRegExp( "^COMSPEC=", FALSE ) ).empty() && comspec ) {
793 uint sz = strlen( comspec ) + 8 /* COMSPEC= */ + 1;
794 envlist.resize( envlist.size() + sz );
795 sprintf( envlist.data() + pos, "COMSPEC=%s", comspec );
796 pos += sz;
797 }
798 }
799 // add the user environment
800 for ( QStringList::ConstIterator it = env->begin();
801 it != env->end(); ++ it
802 ) {
803 QCString var = (*it).local8Bit();
804 uint sz = var.length() + 1;
805 envlist.resize( envlist.size() + sz );
806 memcpy( envlist.data() + pos, var.data(), sz );
807 pos += sz;
808 }
809 // add the terminating 0
810 envlist.resize( envlist.size() + 1 );
811 envlist[ pos ++ ] = 0;
812 } else {
813 // copy the entire environment (to let all variables added using
814 // putenv() appear in the child process)
815 uint sz = 1;
816 for( const char * const *envp = environ; *envp != NULL; ++ envp )
817 sz += strlen( *envp ) + 1;
818 envlist.resize( sz );
819 uint pos = 0;
820 for( const char * const *envp = environ; *envp != NULL; ++ envp ) {
821 sz = strlen( *envp ) + 1;
822 memcpy( envlist.data() + pos, *envp, sz );
823 pos += sz;
824 }
825 // add the terminating 0
826 envlist[ pos ++ ] = 0;
827 }
828
829 // search for the app executable
830
831 const uint appNameLen = appName.length();
832 const uint appArgsLen = appArgs.length();
833
834 bool hasPath = appName[ strcspn( appName.data(), "/\\" ) ] != '\0';
835 bool hasSpaces = strchr( appName.data(), ' ' ) != NULL;
836
837 // list of executable file extensions, in order of CMD.EXE's precedence
838 // (the first runs directly, the rest requires CMD.EXE)
839 const char *exeExts[] = { ".exe", ".cmd", ".bat" };
840
841 // detect which exe extension do we have, if any
842 int ext = -1;
843 if ( appNameLen >= 4 ) {
844 for ( uint i = 0; ext < 0 && i < sizeof(exeExts) / sizeof(exeExts[0]); ++ i )
845 if ( stricmp( appName.data() + appNameLen - 4, exeExts[i] ) == 0 )
846 ext = i;
847 }
848
849 QByteArray buf;
850 QCString appNameFull;
851
852 APIRET rc = 0;
853 char pathBuf[ CCHMAXPATH ];
854
855 // run through all possible exe extension combinations (+ no extension case)
856 for ( uint i = 0; i <= sizeof(exeExts) / sizeof(exeExts[ 0 ]); ++ i ) {
857 if ( i == 0 ) {
858 // try the unmodified app name first if it contains a path spec
859 // or has one of predefined exe extensions
860 if ( hasPath || ext >= 0 ) {
861 if ( buf.size() < appNameLen + 1 )
862 buf.resize ( appNameLen + 1 );
863 strcpy( buf.data(), appName.data() );
864 } else
865 continue;
866 } else {
867 ext = i - 1;
868 const uint extLen = strlen( exeExts[ ext ] );
869 uint sz = appNameLen + extLen + 1;
870 if ( buf.size() < sz )
871 buf.resize( sz );
872 strcpy( buf.data(), appName.data() );
873 strcpy( buf.data() + appNameLen, exeExts[ ext ] );
874 }
875
876#if defined(QT_QPROCESS_DEBUG)
877 qDebug( "QProcess::start(): trying to find [%s]", buf.data() );
878#endif
879 if ( hasPath ) {
880 uint lastSep = strlen( buf.data() );
881 while ( lastSep-- && buf[ lastSep ] != '/' && buf[ lastSep ] != '\\' )
882 ;
883 buf[ lastSep ] = '\0';
884 rc = DosSearchPath( 0, buf.data(), buf.data() + lastSep + 1,
885 pathBuf, sizeof(pathBuf) );
886 } else {
887 rc = DosSearchPath( SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT |
888 SEARCH_CUR_DIRECTORY, "PATH",
889 buf.data(), pathBuf, sizeof(pathBuf) );
890 }
891
892 if ( rc == NO_ERROR ) {
893#if defined(QT_QPROCESS_DEBUG)
894 qDebug( "QProcess::start(): found [%s]", pathBuf );
895#endif
896 appNameFull = pathBuf;
897 break;
898 }
899
900 if ( rc != ERROR_FILE_NOT_FOUND && rc != ERROR_PATH_NOT_FOUND ) {
901#if defined(QT_QPROCESS_DEBUG)
902 qDebug( "QProcess::start(): found [%s], but got an error:", pathBuf );
903 qSystemWarning( "", rc );
904#endif
905 break;
906 }
907 }
908
909 if ( appNameFull.isNull() ) {
910#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
911#if defined(QT_DEBUG)
912 qWarning( "[%s]:", appName.data() );
913#endif
914 qSystemWarning( "Failed to find the executable!", rc );
915#endif
916 return FALSE;
917 }
918
919 // final preparation of arguments
920
921 if ( ext <= 0 ) {
922 // run directly (args = <appName>\0\<appArgs>\0\0)
923 uint sz = appNameLen + 1 + appArgsLen + 2;
924 if ( buf.size() < sz )
925 buf.resize( sz );
926 strcpy( buf.data(), appName.data() );
927 strcpy( buf.data() + appNameLen + 1, appArgs.data() );
928 buf[ sz - 1 ] = '\0';
929 } else {
930 // run via shell (args = <shell>\0"/c "<appName>" "<appArgs>\0\0)
931 appNameFull = getenv( "COMSPEC" );
932 if ( appNameFull.isNull() ) {
933 ULONG bootDrv = 0;
934 rc = DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
935 &bootDrv, sizeof(bootDrv) );
936 if ( rc == NO_ERROR && bootDrv >= 1 ) {
937 appNameFull = "?:\\OS2\\CMD.EXE";
938 appNameFull[ 0 ] = char( bootDrv ) + 'A' - 1;
939 }
940 }
941 if ( appNameFull.isNull() ) {
942#if defined(QT_QPROCESS_DEBUG)
943 qDebug( "QProcess::start(): OS/2 command line interpreter is not found!" );
944#endif
945 return FALSE;
946 }
947 const uint shellLen = strlen( appNameFull );
948 uint sz = shellLen + 1 + 3 + appNameLen + 1 + appArgsLen + 2;
949 if ( hasSpaces )
950 sz += 2; // for quotes
951 if ( !appArgsLen )
952 sz -= 1; // no need for space
953 if ( buf.size() < sz )
954 buf.resize( sz );
955 strcpy( buf.data(), appNameFull );
956 sprintf( buf.data() + shellLen + 1, hasSpaces ? "/c \"%s\"" : "/c %s",
957 appName.data() );
958 if ( appArgsLen )
959 strcat( buf.data() + shellLen + 1, appArgs.data() );
960 buf[ sz - 1 ] = '\0';
961 }
962
963#if defined(QT_QPROCESS_DEBUG)
964 qDebug( "QProcess::start(): will start [%s] as [%s] [%s]",
965 appNameFull.data(), buf.data(), buf.data() + strlen( buf.data() ) + 1 );
966#endif
967
968 // create STDIN/OUT/ERR redirection pipes
969
970 if ( comms & (Stdout | Stderr) ) {
971 // lazily create the process monitor
972 if ( !processMonitor ) {
973 processMonitor = new QProcessMonitor();
974 Q_ASSERT( processMonitor );
975 if ( !processMonitor )
976 return FALSE;
977 if ( !processMonitor->isOk() ) {
978 QProcessMonitor_cleanup();
979 return FALSE;
980 }
981 qAddPostRoutine( QProcessMonitor_cleanup );
982 }
983 }
984
985 HFILE tmpStdin = HF_NULL, tmpStdout = HF_NULL, tmpStderr = HF_NULL;
986 HFILE realStdin = HF_STDIN, realStdout = HF_STDOUT, realStderr = HF_STDERR;
987
988 // we need the process identifier to guarantee pipe name unicity
989 PPIB ppib = NULL;
990 DosGetInfoBlocks( NULL, &ppib );
991
992 // use the custom Qt message handler to print errors/wranings
993 // to the console when the real STDERR handle is redirected
994 InstallQtMsgHandler();
995
996 if ( comms & Stdin ) {
997 HFILE clientStdin = HF_NULL;
998 do {
999 // create a Stdin pipe
1000 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdin",
1001 ppib->pib_ulpid, this );
1002 rc = DosCreateNPipe( pathBuf, &d->pipeStdin,
1003 NP_ACCESS_OUTBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
1004 PIPE_SIZE_STDIN, 0, 0 );
1005 if ( rc != NO_ERROR )
1006 break;
1007 DosConnectNPipe( d->pipeStdin );
1008 // open a client end of the Stdout pipe
1009 ULONG action = 0;
1010 rc = DosOpen( pathBuf, &clientStdin, &action, 0, FILE_NORMAL, FILE_OPEN,
1011 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
1012 (PEAOP2) NULL);
1013 if ( rc != NO_ERROR )
1014 break;
1015 // save the real STDIN handle
1016 if ( (rc = DosDupHandle( HF_STDIN, &tmpStdin)) != NO_ERROR )
1017 break;
1018 // redirect the real STDIN handle to the client end of the pipe
1019 if ( (rc = DosDupHandle( clientStdin, &realStdin)) != NO_ERROR )
1020 break;
1021 } while( 0 );
1022 // close the client end anyway (no more necessary)
1023 if ( clientStdin != HF_NULL )
1024 DosClose ( clientStdin );
1025 // close all opened handles on error
1026 if ( rc != NO_ERROR ) {
1027#if defined(QT_QPROCESS_DEBUG)
1028 qDebug( "Failed to create a STDIN redirection (%s): SYS%04ld",
1029 pathBuf, rc );
1030#endif
1031 if ( tmpStdin != HF_NULL )
1032 DosClose ( tmpStdin );
1033 d->closeHandles();
1034 UninstallQtMsgHandler();
1035 return FALSE;
1036 }
1037 }
1038 if ( comms & Stdout ) {
1039 HFILE clientStdout = HF_NULL;
1040 do {
1041 // create a Stdout pipe
1042 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdout",
1043 ppib->pib_ulpid, this );
1044 rc = DosCreateNPipe( pathBuf, &d->stdout.pipe,
1045 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
1046 0, PIPE_SIZE_STDOUT, 0 );
1047 if ( rc != NO_ERROR )
1048 break;
1049 DosConnectNPipe( d->stdout.pipe );
1050 // open a client end of the Stdout pipe
1051 ULONG action = 0;
1052 rc = DosOpen( pathBuf, &clientStdout, &action, 0, FILE_NORMAL, FILE_OPEN,
1053 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
1054 (PEAOP2) NULL);
1055 if ( rc != NO_ERROR )
1056 break;
1057 // save the real STDOUT handle
1058 if ( (rc = DosDupHandle( HF_STDOUT, &tmpStdout )) != NO_ERROR )
1059 break;
1060 // redirect the real STDOUT handle to the client end of the pipe
1061 if ( (rc = DosDupHandle( clientStdout, &realStdout )) != NO_ERROR )
1062 break;
1063 } while( 0 );
1064 // close the client end anyway (no more necessary)
1065 if ( clientStdout != HF_NULL )
1066 DosClose ( clientStdout );
1067 // close all opened handles on error
1068 if ( rc != NO_ERROR ) {
1069#if defined(QT_QPROCESS_DEBUG)
1070 qDebug( "Failed to create a STDOUT redirection (%s): SYS%04ld",
1071 pathBuf, rc );
1072#endif
1073 if ( tmpStdin != HF_NULL ) {
1074 DosDupHandle( tmpStdin, &realStdin );
1075 DosClose( tmpStdin );
1076 }
1077 if ( tmpStdout != HF_NULL )
1078 DosClose ( tmpStdout );
1079 d->closeHandles();
1080 UninstallQtMsgHandler();
1081 return FALSE;
1082 }
1083 }
1084 if ( (comms & Stderr) && !(comms & DupStderr) ) {
1085 HFILE clientStderr = HF_NULL;
1086 do {
1087 // create a Stderr pipe
1088 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stderr",
1089 ppib->pib_ulpid, this );
1090 rc = DosCreateNPipe( pathBuf, &d->stderr.pipe,
1091 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
1092 0, PIPE_SIZE_STDERR, 0 );
1093 if ( rc != NO_ERROR )
1094 break;
1095 DosConnectNPipe( d->stderr.pipe );
1096 // open a client end of the Stderr pipe
1097 ULONG action = 0;
1098 rc = DosOpen( pathBuf, &clientStderr, &action, 0, FILE_NORMAL, FILE_OPEN,
1099 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
1100 (PEAOP2) NULL);
1101 if ( rc != NO_ERROR )
1102 break;
1103 // save the real STDERR handle
1104 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
1105 break;
1106 // redirect the real STDERR handle to the client end of the pipe
1107 if ( (rc = DosDupHandle( clientStderr, &realStderr )) != NO_ERROR )
1108 break;
1109 } while( 0 );
1110 // close the client end anyway (no more necessary)
1111 if ( clientStderr != HF_NULL )
1112 DosClose ( clientStderr );
1113 // close all opened handles on error
1114 if ( rc != NO_ERROR ) {
1115#if defined(QT_QPROCESS_DEBUG)
1116 qDebug( "Failed to create a STDERR redirection (%s): SYS%04ld",
1117 pathBuf, rc );
1118#endif
1119 if ( tmpStdin != HF_NULL ) {
1120 DosDupHandle( tmpStdin, &realStdin );
1121 DosClose( tmpStdin );
1122 }
1123 if ( tmpStdout != HF_NULL ) {
1124 DosDupHandle( tmpStdout, &realStdout );
1125 DosClose( tmpStdout );
1126 }
1127 if ( tmpStderr != HF_NULL )
1128 DosClose ( tmpStderr );
1129 d->closeHandles();
1130 UninstallQtMsgHandler();
1131 return FALSE;
1132 }
1133 }
1134 if ( comms & DupStderr ) {
1135 // leave d->stderr.pipe equal to HP_NULL
1136 do {
1137 // save the real STDERR handle
1138 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
1139 break;
1140 // redirect STDERR to STDOUT
1141 if ( (rc = DosDupHandle( HF_STDOUT, &realStderr )) != NO_ERROR )
1142 break;
1143 } while( 0 );
1144 // close all opened handles on error
1145 if ( rc != NO_ERROR ) {
1146#if defined(QT_QPROCESS_DEBUG)
1147 qDebug( "Failed to redirect STDERR to STDOUT: SYS%04ld",
1148 rc );
1149#endif
1150 if ( tmpStdin != HF_NULL ) {
1151 DosDupHandle( tmpStdin, &realStdin );
1152 DosClose( tmpStdin );
1153 }
1154 if ( tmpStdout != HF_NULL ) {
1155 DosDupHandle( tmpStdout, &realStdout );
1156 DosClose( tmpStdout );
1157 }
1158 if ( tmpStderr != HF_NULL )
1159 DosClose ( tmpStderr );
1160 d->closeHandles();
1161 UninstallQtMsgHandler();
1162 return FALSE;
1163 }
1164 }
1165
1166 // add this process to the monitor
1167 if ( !processMonitor->addProcess( this ) ) {
1168 if ( tmpStdin != HF_NULL ) {
1169 DosDupHandle( tmpStdin, &realStdin );
1170 DosClose( tmpStdin );
1171 }
1172 if ( tmpStdout != HF_NULL ) {
1173 DosDupHandle( tmpStdout, &realStdout );
1174 DosClose( tmpStdout );
1175 }
1176 if ( tmpStderr != HF_NULL ) {
1177 DosDupHandle( tmpStderr, &realStderr );
1178 DosClose ( tmpStderr );
1179 }
1180 d->closeHandles();
1181 UninstallQtMsgHandler();
1182 return FALSE;
1183 }
1184
1185 // set the working directory
1186 QString curDir = QDir::currentDirPath();
1187 QDir::setCurrent( workingDir.path() );
1188#if defined(QT_QPROCESS_DEBUG)
1189 qDebug( "QProcess::start(): curDir='%s', workingDir='%s'",
1190 curDir.local8Bit().data(),
1191 QDir::currentDirPath().local8Bit().data() );
1192#endif
1193
1194 // DosExecPgm()
1195
1196 RESULTCODES resc = { 0 };
1197
1198 rc = DosExecPgm( pathBuf, sizeof(pathBuf), EXEC_ASYNCRESULT,
1199 buf.data(), envlist.data(), &resc, appNameFull.data() );
1200
1201 // restore the current directory
1202 QDir::setCurrent( curDir );
1203
1204 // cancel STDIN/OUT/ERR redirections
1205 if ( tmpStdin != HF_NULL ) {
1206 DosDupHandle( tmpStdin, &realStdin );
1207 DosClose( tmpStdin );
1208 }
1209 if ( tmpStdout != HF_NULL ) {
1210 DosDupHandle( tmpStdout, &realStdout );
1211 DosClose( tmpStdout );
1212 }
1213 if ( tmpStderr != HF_NULL ) {
1214 DosDupHandle( tmpStderr, &realStderr );
1215 DosClose( tmpStderr );
1216 }
1217
1218 UninstallQtMsgHandler();
1219
1220 if ( rc != NO_ERROR ) {
1221#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
1222#if defined(QT_DEBUG)
1223 qWarning( "[%s] [%s]\n(%s):",
1224 buf.data(), buf.data() + strlen( buf.data() ) + 1,
1225 pathBuf );
1226#endif
1227 qSystemWarning( "Failed to start a new process!", rc );
1228#endif
1229 processMonitor->removeProcess( d );
1230 d->closeHandles();
1231 return FALSE;
1232 }
1233
1234 // memporize PID of the started process
1235 d->pid = resc.codeTerminate;
1236
1237 // timer is not necessary for ioRedirection (we use the monitor thread)
1238 if ( /* ioRedirection || */ notifyOnExit ) {
1239 d->lookup->start( POLL_TIMER );
1240 }
1241
1242 return TRUE;
1243}
1244
1245void QProcess::tryTerminate() const
1246{
1247 if ( d->pid == PID_NULL )
1248 return;
1249
1250 HSWITCH hswitch = WinQuerySwitchHandle( NULL, d->pid );
1251 if ( hswitch != NULLHANDLE ) {
1252 SWCNTRL swcntrl = { 0 };
1253 APIRET rc = WinQuerySwitchEntry( hswitch, &swcntrl );
1254 // WinQuerySwitchEntry will return a switch entry of the parent
1255 // process if the specfied one doesn't have a separate session
1256 // (running a plain CMD.EXE is an example); ignore this case.
1257 if ( rc == NO_ERROR && swcntrl.idProcess == d->pid )
1258 {
1259 // first, ensure that the Close action is enabled in the main frame
1260 // window (otherwise WM_SYSCOMMAND/SC_CLOSE will be ignored)
1261 HWND hwndSysMenu = WinWindowFromID( swcntrl.hwnd, FID_SYSMENU );
1262 if (hwndSysMenu) {
1263 WinPostMsg( hwndSysMenu, MM_SETITEMATTR,
1264 MPFROM2SHORT( SC_CLOSE, TRUE ),
1265 MPFROM2SHORT( MIA_DISABLED, 0 ) );
1266 }
1267 WinPostMsg( swcntrl.hwnd, WM_SYSCOMMAND,
1268 MPFROM2SHORT( SC_CLOSE, CMDSRC_OTHER ),
1269 MPFROMLONG( FALSE ) );
1270 }
1271 }
1272}
1273
1274void QProcess::kill() const
1275{
1276 if ( d->pid != PID_NULL )
1277 DosKillProcess( DKP_PROCESS, d->pid );
1278}
1279
1280bool QProcess::isRunning() const
1281{
1282 if ( d->pid == PID_NULL )
1283 return FALSE;
1284
1285 PID pidEnded = PID_NULL;
1286 RESULTCODES resc = { 0 };
1287 APIRET rc = DosWaitChild( DCWA_PROCESS, DCWW_NOWAIT, &resc,
1288 &pidEnded, d->pid );
1289
1290 if ( rc == ERROR_CHILD_NOT_COMPLETE )
1291 return TRUE;
1292
1293 QProcess *that = (QProcess *) this;
1294
1295 // There might be data to read, but WM_U_PIPE_RDATA messages won't be
1296 // converted to signals after removeProcess() is called below. Therefore,
1297 // emit signals ourselved if necessary.
1298
1299 if ( d->readPipe( &d->stdout ) )
1300 emit that->readyReadStdout();
1301 if ( d->readPipe( &d->stderr ) )
1302 emit that->readyReadStderr();
1303
1304 // compute the exit values
1305 if ( !d->exitValuesCalculated ) {
1306 that->exitNormal = resc.codeTerminate == TC_EXIT;
1307 that->exitStat = resc.codeResult;
1308 d->exitValuesCalculated = TRUE;
1309 }
1310
1311 processMonitor->removeProcess( d );
1312
1313 d->pid = PID_NULL;
1314 d->closeHandles();
1315 return FALSE;
1316}
1317
1318bool QProcess::canReadLineStdout() const
1319{
1320 if( d->stdout.pipe == HP_NULL )
1321 return d->stdout.buf.size() != 0;
1322
1323 QProcess *that = (QProcess *) this;
1324 return that->membufStdout()->scanNewline( 0 );
1325}
1326
1327bool QProcess::canReadLineStderr() const
1328{
1329 if( d->stderr.pipe == HP_NULL )
1330 return d->stderr.buf.size() != 0;
1331
1332 QProcess *that = (QProcess *) this;
1333 return that->membufStderr()->scanNewline( 0 );
1334}
1335
1336void QProcess::writeToStdin( const QByteArray& buf )
1337{
1338 d->stdinBuf.enqueue( new QByteArray(buf) );
1339 socketWrite( 0 );
1340}
1341
1342void QProcess::closeStdin( )
1343{
1344 if ( d->pipeStdin != HP_NULL ) {
1345 DosDisConnectNPipe( d->pipeStdin );
1346 DosClose( d->pipeStdin );
1347 d->pipeStdin = HP_NULL;
1348 }
1349}
1350
1351/* dummy, since MOC doesn't understand preprocessor defines */
1352void QProcess::socketRead( int fd )
1353{
1354}
1355
1356void QProcess::socketWrite( int )
1357{
1358 if ( d->pipeStdin == HP_NULL )
1359 return;
1360
1361 while ( !d->stdinBuf.isEmpty() && isRunning() ) {
1362 ULONG written = 0;
1363 APIRET rc = DosWrite( d->pipeStdin,
1364 d->stdinBuf.head()->data() + d->stdinBufRead,
1365 QMIN( PIPE_SIZE_STDIN,
1366 d->stdinBuf.head()->size() - d->stdinBufRead ),
1367 &written );
1368 if ( rc != NO_ERROR ) {
1369#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
1370 qSystemWarning( "Failed to write to the pipe!", rc );
1371#endif
1372 d->lookup->start( POLL_TIMER );
1373 return;
1374 }
1375
1376 d->stdinBufRead += written;
1377 if ( d->stdinBufRead == d->stdinBuf.head()->size() ) {
1378 d->stdinBufRead = 0;
1379 delete d->stdinBuf.dequeue();
1380 if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
1381 emit wroteToStdin();
1382 }
1383 }
1384}
1385
1386void QProcess::flushStdin()
1387{
1388 socketWrite( 0 );
1389}
1390
1391/*
1392 Use a timer for polling misc. stuff.
1393*/
1394void QProcess::timeout()
1395{
1396 // Disable the timer temporary since one of the slots that are connected to
1397 // the readyRead...(), etc. signals might trigger recursion if
1398 // processEvents() is called.
1399 d->lookup->stop();
1400
1401 // try to write pending data to stdin
1402 if ( !d->stdinBuf.isEmpty() )
1403 socketWrite( 0 );
1404
1405 if ( isRunning() ) {
1406 // enable timer again, if needed
1407 // timer is not necessary for ioRedirection (we use the monitor thread)
1408 if ( !d->stdinBuf.isEmpty() || /* ioRedirection || */ notifyOnExit )
1409 d->lookup->start( POLL_TIMER );
1410 } else if ( notifyOnExit ) {
1411 emit processExited();
1412 }
1413}
1414
1415/*
1416 Used by connectNotify() and disconnectNotify() to change the value of
1417 ioRedirection (and related behaviour)
1418*/
1419void QProcess::setIoRedirection( bool value )
1420{
1421 ioRedirection = value;
1422 // timer is not necessary for ioRedirection (we use the monitor thread)
1423#if 0
1424 if ( !ioRedirection && !notifyOnExit )
1425 d->lookup->stop();
1426 if ( ioRedirection ) {
1427 if ( isRunning() )
1428 d->lookup->start( POLL_TIMER );
1429 }
1430#endif
1431}
1432
1433/*
1434 Used by connectNotify() and disconnectNotify() to change the value of
1435 notifyOnExit (and related behaviour)
1436*/
1437void QProcess::setNotifyOnExit( bool value )
1438{
1439 notifyOnExit = value;
1440 // timer is not necessary for ioRedirection (we use the monitor thread)
1441 if ( /* !ioRedirection && */ !notifyOnExit )
1442 d->lookup->stop();
1443 if ( notifyOnExit ) {
1444 if ( isRunning() )
1445 d->lookup->start( POLL_TIMER );
1446 }
1447}
1448
1449/*
1450 Used by connectNotify() and disconnectNotify() to change the value of
1451 wroteToStdinConnected (and related behaviour)
1452*/
1453void QProcess::setWroteStdinConnected( bool value )
1454{
1455 wroteToStdinConnected = value;
1456}
1457
1458QProcess::PID QProcess::processIdentifier()
1459{
1460 return d->pid;
1461}
1462
1463#endif // QT_NO_PROCESS
Note: See TracBrowser for help on using the repository browser.