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

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

Kernel/QProcess:

  • Fixed access violation (accessing a deleted instance) when QT_QPROCESS_DEBUG was defined.
  • Fixed: if a program to execute was a shell script (started via '%COMSPEC% /c'), it was not separated by a space from its first argument (if any).
  • Property svn:keywords set to Id
File size: 46.6 KB
Line 
1/****************************************************************************
2** $Id: qprocess_pm.cpp 159 2006-12-03 22:23: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 // check parameter validity (we can safely do it outside the lock
547 // because the GUI thread is the only that can modify pipeKeys)
548 if ( proc == pipeKeys.find( key ) ) {
549#if defined(QT_QPROCESS_DEBUG)
550 qDebug( "QProcessMonitor::WM_U_PIPE_RDATA: proc=%p (%s/%s) d=%p "
551 "key=%04hX",
552 proc, proc->name(), proc->className(), proc->d, key );
553#endif
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#if defined(QT_QPROCESS_DEBUG)
563 else {
564 qDebug( "QProcessMonitor::WM_U_PIPE_RDATA: proc=%p (invalid)",
565 proc );
566 }
567#endif
568 break;
569 }
570 case WM_U_PIPE_CLOSE: {
571 QProcess *proc = (QProcess *) PVOIDFROMMP( mp1 );
572 USHORT key = SHORT1FROMMP( mp2 );
573 // check parameter validity (we can safely do it outside the lock
574 // because the GUI thread is the only that can modify pipeKeys)
575 if ( proc == pipeKeys.find( key ) ) {
576#if defined(QT_QPROCESS_DEBUG)
577 qDebug( "QProcessMonitor::WM_U_PIPE_CLOSE: proc=%p (%s/%s) d=%p "
578 "key=%04hX",
579 proc, proc->name(), proc->className(), proc->d, key );
580#endif
581 QProcessPrivate *d = proc->d;
582 QProcessPrivate::Pipe *pipe = d->findPipe( key );
583 Q_ASSERT( pipe );
584 if ( pipe ) {
585 Q_ASSERT( pipe->closed );
586 // remove the single pipe from watching
587 removeProcess( d, pipe );
588 // close the pipe since no more necessary
589 // (pipe is not watched anymore, no need to lock access)
590 if ( pipe->pending == 0 )
591 d->closePipe( pipe );
592 }
593 }
594#if defined(QT_QPROCESS_DEBUG)
595 else {
596 qDebug( "QProcessMonitor::WM_U_PIPE_CLOSE: proc=%p (invalid)",
597 proc );
598 }
599#endif
600 break;
601 }
602 default:
603 break;
604 }
605
606 return FALSE;
607}
608
609static QProcessMonitor *processMonitor = NULL;
610
611void QProcessMonitor_cleanup()
612{
613#if defined(QT_QPROCESS_DEBUG)
614 qDebug( "QProcessMonitor_cleanup()" );
615#endif
616 delete processMonitor;
617 processMonitor = NULL;
618}
619
620/** Returns true if some data was successfully read */
621bool QProcessPrivate::readPipe( Pipe *pipe )
622{
623#if defined(QT_QPROCESS_DEBUG)
624 qDebug( "QProcessPrivate::readPipe(): this=%p key=%04hX", this, pipe->key );
625#endif
626
627 if ( pipe->pipe == HP_NULL )
628 return false;
629
630 // Pipe::pending and Pipe::closed need the lock
631 QMutex *lock = NULL;
632
633 // Notes:
634 // 1. If procesMonitor is NULL, it which means we're somewhere inside
635 // the QApplication destruction procedure.
636 // 2. If pipe->key us KEY_NULL, it means GUI thread has already processed
637 // the WM_U_PIPE_CLOSE message and we're free to access fields w/o a
638 // lock. pipe->key is assigned only on the GUI thread, so it's safe
639 // to check it here w/o the lock.
640
641 if ( processMonitor && pipe->key != KEY_NULL )
642 lock = &processMonitor->mlock();
643
644 QByteArray *ba = NULL;
645 bool close = FALSE;
646
647 {
648 QMutexLocker locker( lock );
649
650 if ( pipe->pending == 0 )
651 return false;
652
653 ba = new QByteArray( pipe->pending );
654
655 ULONG read = 0;
656 APIRET rc = DosRead( pipe->pipe, ba->data(), pipe->pending, &read );
657 if ( rc != NO_ERROR ) {
658 delete ba;
659#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
660 qSystemWarning( "Failed to read from the pipe!", rc );
661#endif
662 return false;
663 }
664
665 Q_ASSERT( read == pipe->pending );
666 if ( read < pipe->pending )
667 ba->resize( read );
668
669 pipe->pending -= read;
670
671 close = pipe->pending == 0 && pipe->closed;
672 }
673
674 if ( close ) {
675 // the client's end of pipe has been closed, so close our end as well
676 if ( processMonitor && pipe->key != KEY_NULL ) {
677 // WM_U_PIPE_CLOSE has been posted but not yet processed
678 processMonitor->removeProcess( this, pipe );
679 }
680 closePipe( pipe );
681 }
682
683 pipe->buf.append( ba );
684 return true;
685}
686
687/***********************************************************************
688 *
689 * QProcess
690 *
691 **********************************************************************/
692
693void QProcess::init()
694{
695 d = new QProcessPrivate( this );
696 exitStat = 0;
697 exitNormal = FALSE;
698
699#if defined(QT_QPROCESS_DEBUG)
700 qDebug( "QProcess::init(): d=%p", d );
701#endif
702}
703
704void QProcess::reset()
705{
706 // remove monitoring if the monitor is there (note that it's done before
707 // resetting QProcessPrivate which has fields protected by the
708 // QProcessMonitor lock)
709 if ( processMonitor )
710 processMonitor->removeProcess( d );
711
712 d->reset();
713 exitStat = 0;
714 exitNormal = FALSE;
715}
716
717QMembuf* QProcess::membufStdout()
718{
719 if( d->stdout.pipe != 0 )
720 d->readPipe( &d->stdout );
721 return &d->stdout.buf;
722}
723
724QMembuf* QProcess::membufStderr()
725{
726 if( d->stderr.pipe != 0 )
727 d->readPipe( &d->stderr );
728 return &d->stderr.buf;
729}
730
731QProcess::~QProcess()
732{
733#if defined(QT_QPROCESS_DEBUG)
734 qDebug( "~QProcess::QProcess(): d=%p", d );
735#endif
736
737 // remove monitoring if the monitor is there (note that it's done before
738 // resetting QProcessPrivate which has fields protected by the
739 // QProcessMonitor lock)
740 if ( processMonitor )
741 processMonitor->removeProcess( d );
742
743 delete d;
744}
745
746bool QProcess::start( QStringList *env )
747{
748#if defined(QT_QPROCESS_DEBUG)
749 qDebug( "QProcess::start()" );
750#endif
751
752 reset();
753
754 if ( _arguments.isEmpty() )
755 return FALSE;
756
757 // construct the arguments for DosExecPgm()
758
759 QCString appName, appArgs;
760 {
761 QString args;
762 for ( QStringList::ConstIterator it = _arguments.begin();
763 it != _arguments.end(); ++ it
764 ) {
765 if ( it == _arguments.begin() ) {
766 appName = (*it).local8Bit();
767 } else {
768 QString s = *it;
769 // quote the argument when it contains spaces
770 if ( s.contains( ' ' ) )
771 s = '"' + s + '"';
772 if ( args.isNull() )
773 args = s;
774 else
775 args += ' ' + s;
776 }
777 }
778 appArgs = args.local8Bit();
779
780#if defined(QT_QPROCESS_DEBUG)
781 qDebug( "QProcess::start(): app [%s], args [%s]",
782 appName.data(), appArgs.data() );
783#endif
784 }
785
786 QByteArray envlist;
787 if ( env != 0 ) {
788 uint pos = 0;
789 // inherit PATH if missing (for convenience)
790 // (note that BEGINLIBPATH and ENDLIBPATH, if any, are automatically
791 // inherited, while LIBPATH is always a global setting)
792 {
793 const char *path = getenv( "PATH" );
794 if ( env->grep( QRegExp( "^PATH=", FALSE ) ).empty() && path ) {
795 uint sz = strlen( path ) + 5 /* PATH= */ + 1;
796 envlist.resize( envlist.size() + sz );
797 sprintf( envlist.data() + pos, "PATH=%s", path );
798 pos += sz;
799 }
800 }
801 // inherit COMSPEC if missing (to let the child start .cmd and .bat)
802 {
803 const char *comspec = getenv( "COMSPEC" );
804 if ( env->grep( QRegExp( "^COMSPEC=", FALSE ) ).empty() && comspec ) {
805 uint sz = strlen( comspec ) + 8 /* COMSPEC= */ + 1;
806 envlist.resize( envlist.size() + sz );
807 sprintf( envlist.data() + pos, "COMSPEC=%s", comspec );
808 pos += sz;
809 }
810 }
811 // add the user environment
812 for ( QStringList::ConstIterator it = env->begin();
813 it != env->end(); ++ it
814 ) {
815 QCString var = (*it).local8Bit();
816 uint sz = var.length() + 1;
817 envlist.resize( envlist.size() + sz );
818 memcpy( envlist.data() + pos, var.data(), sz );
819 pos += sz;
820 }
821 // add the terminating 0
822 envlist.resize( envlist.size() + 1 );
823 envlist[ pos ++ ] = 0;
824 } else {
825 // copy the entire environment (to let all variables added using
826 // putenv() appear in the child process)
827 uint sz = 1;
828 for( const char * const *envp = environ; *envp != NULL; ++ envp )
829 sz += strlen( *envp ) + 1;
830 envlist.resize( sz );
831 uint pos = 0;
832 for( const char * const *envp = environ; *envp != NULL; ++ envp ) {
833 sz = strlen( *envp ) + 1;
834 memcpy( envlist.data() + pos, *envp, sz );
835 pos += sz;
836 }
837 // add the terminating 0
838 envlist[ pos ++ ] = 0;
839 }
840
841 // search for the app executable
842
843 const uint appNameLen = appName.length();
844 const uint appArgsLen = appArgs.length();
845
846 bool hasPath = appName[ strcspn( appName.data(), "/\\" ) ] != '\0';
847 bool hasSpaces = strchr( appName.data(), ' ' ) != NULL;
848
849 // list of executable file extensions, in order of CMD.EXE's precedence
850 // (the first runs directly, the rest requires CMD.EXE)
851 const char *exeExts[] = { ".exe", ".cmd", ".bat" };
852
853 // detect which exe extension do we have, if any
854 int ext = -1;
855 if ( appNameLen >= 4 ) {
856 for ( uint i = 0; ext < 0 && i < sizeof(exeExts) / sizeof(exeExts[0]); ++ i )
857 if ( stricmp( appName.data() + appNameLen - 4, exeExts[i] ) == 0 )
858 ext = i;
859 }
860
861 QByteArray buf;
862 QCString appNameFull;
863
864 APIRET rc = 0;
865 char pathBuf[ CCHMAXPATH ];
866
867 // run through all possible exe extension combinations (+ no extension case)
868 for ( uint i = 0; i <= sizeof(exeExts) / sizeof(exeExts[ 0 ]); ++ i ) {
869 if ( i == 0 ) {
870 // try the unmodified app name first if it contains a path spec
871 // or has one of predefined exe extensions
872 if ( hasPath || ext >= 0 ) {
873 if ( buf.size() < appNameLen + 1 )
874 buf.resize ( appNameLen + 1 );
875 strcpy( buf.data(), appName.data() );
876 } else
877 continue;
878 } else {
879 ext = i - 1;
880 const uint extLen = strlen( exeExts[ ext ] );
881 uint sz = appNameLen + extLen + 1;
882 if ( buf.size() < sz )
883 buf.resize( sz );
884 strcpy( buf.data(), appName.data() );
885 strcpy( buf.data() + appNameLen, exeExts[ ext ] );
886 }
887
888#if defined(QT_QPROCESS_DEBUG)
889 qDebug( "QProcess::start(): trying to find [%s]", buf.data() );
890#endif
891 if ( hasPath ) {
892 uint lastSep = strlen( buf.data() );
893 while ( lastSep-- && buf[ lastSep ] != '/' && buf[ lastSep ] != '\\' )
894 ;
895 buf[ lastSep ] = '\0';
896 rc = DosSearchPath( 0, buf.data(), buf.data() + lastSep + 1,
897 pathBuf, sizeof(pathBuf) );
898 } else {
899 rc = DosSearchPath( SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT |
900 SEARCH_CUR_DIRECTORY, "PATH",
901 buf.data(), pathBuf, sizeof(pathBuf) );
902 }
903
904 if ( rc == NO_ERROR ) {
905#if defined(QT_QPROCESS_DEBUG)
906 qDebug( "QProcess::start(): found [%s]", pathBuf );
907#endif
908 appNameFull = pathBuf;
909 break;
910 }
911
912 if ( rc != ERROR_FILE_NOT_FOUND && rc != ERROR_PATH_NOT_FOUND ) {
913#if defined(QT_QPROCESS_DEBUG)
914 qDebug( "QProcess::start(): found [%s], but got an error:", pathBuf );
915 qSystemWarning( "", rc );
916#endif
917 break;
918 }
919 }
920
921 if ( appNameFull.isNull() ) {
922#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
923#if defined(QT_DEBUG)
924 qWarning( "[%s]:", appName.data() );
925#endif
926 qSystemWarning( "Failed to find the executable!", rc );
927#endif
928 return FALSE;
929 }
930
931 // final preparation of arguments
932
933 if ( ext <= 0 ) {
934 // run directly (args = <appName>\0\<appArgs>\0\0)
935 uint sz = appNameLen + 1 + appArgsLen + 2;
936 if ( buf.size() < sz )
937 buf.resize( sz );
938 strcpy( buf.data(), appName.data() );
939 strcpy( buf.data() + appNameLen + 1, appArgs.data() );
940 buf[ sz - 1 ] = '\0';
941 } else {
942 // run via shell (args = <shell>\0"/c "<appName>" "<appArgs>\0\0)
943 appNameFull = getenv( "COMSPEC" );
944 if ( appNameFull.isNull() ) {
945 ULONG bootDrv = 0;
946 rc = DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
947 &bootDrv, sizeof(bootDrv) );
948 if ( rc == NO_ERROR && bootDrv >= 1 ) {
949 appNameFull = "?:\\OS2\\CMD.EXE";
950 appNameFull[ 0 ] = char( bootDrv ) + 'A' - 1;
951 }
952 }
953 if ( appNameFull.isNull() ) {
954#if defined(QT_QPROCESS_DEBUG)
955 qDebug( "QProcess::start(): OS/2 command line interpreter is not found!" );
956#endif
957 return FALSE;
958 }
959 const uint shellLen = strlen( appNameFull );
960 uint sz = shellLen + 1 + 3 + appNameLen + 1 + appArgsLen + 2;
961 if ( hasSpaces )
962 sz += 2; // for quotes
963 if ( !appArgsLen )
964 sz -= 1; // no need for space
965 if ( buf.size() < sz )
966 buf.resize( sz );
967 strcpy( buf.data(), appNameFull );
968 char *argsPtr = buf.data() + shellLen + 1;
969 sprintf( argsPtr, hasSpaces ? "/c \"%s\"" : "/c %s", appName.data() );
970 if ( appArgsLen ) {
971 strcat( argsPtr, " " );
972 strcat( argsPtr, appArgs.data() );
973 }
974 buf[ sz - 1 ] = '\0';
975 }
976
977#if defined(QT_QPROCESS_DEBUG)
978 qDebug( "QProcess::start(): will start [%s] as [%s] [%s]",
979 appNameFull.data(), buf.data(), buf.data() + strlen( buf.data() ) + 1 );
980#endif
981
982 // create STDIN/OUT/ERR redirection pipes
983
984 if ( comms & (Stdout | Stderr) ) {
985 // lazily create the process monitor
986 if ( !processMonitor ) {
987 processMonitor = new QProcessMonitor();
988 Q_ASSERT( processMonitor );
989 if ( !processMonitor )
990 return FALSE;
991 if ( !processMonitor->isOk() ) {
992 QProcessMonitor_cleanup();
993 return FALSE;
994 }
995 qAddPostRoutine( QProcessMonitor_cleanup );
996 }
997 }
998
999 HFILE tmpStdin = HF_NULL, tmpStdout = HF_NULL, tmpStderr = HF_NULL;
1000 HFILE realStdin = HF_STDIN, realStdout = HF_STDOUT, realStderr = HF_STDERR;
1001
1002 // we need the process identifier to guarantee pipe name unicity
1003 PPIB ppib = NULL;
1004 DosGetInfoBlocks( NULL, &ppib );
1005
1006 // use the custom Qt message handler to print errors/wranings
1007 // to the console when the real STDERR handle is redirected
1008 InstallQtMsgHandler();
1009
1010 if ( comms & Stdin ) {
1011 HFILE clientStdin = HF_NULL;
1012 do {
1013 // create a Stdin pipe
1014 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdin",
1015 ppib->pib_ulpid, this );
1016 rc = DosCreateNPipe( pathBuf, &d->pipeStdin,
1017 NP_ACCESS_OUTBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
1018 PIPE_SIZE_STDIN, 0, 0 );
1019 if ( rc != NO_ERROR )
1020 break;
1021 DosConnectNPipe( d->pipeStdin );
1022 // open a client end of the Stdout pipe
1023 ULONG action = 0;
1024 rc = DosOpen( pathBuf, &clientStdin, &action, 0, FILE_NORMAL, FILE_OPEN,
1025 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
1026 (PEAOP2) NULL);
1027 if ( rc != NO_ERROR )
1028 break;
1029 // save the real STDIN handle
1030 if ( (rc = DosDupHandle( HF_STDIN, &tmpStdin)) != NO_ERROR )
1031 break;
1032 // redirect the real STDIN handle to the client end of the pipe
1033 if ( (rc = DosDupHandle( clientStdin, &realStdin)) != NO_ERROR )
1034 break;
1035 } while( 0 );
1036 // close the client end anyway (no more necessary)
1037 if ( clientStdin != HF_NULL )
1038 DosClose ( clientStdin );
1039 // close all opened handles on error
1040 if ( rc != NO_ERROR ) {
1041#if defined(QT_QPROCESS_DEBUG)
1042 qDebug( "Failed to create a STDIN redirection (%s): SYS%04ld",
1043 pathBuf, rc );
1044#endif
1045 if ( tmpStdin != HF_NULL )
1046 DosClose ( tmpStdin );
1047 d->closeHandles();
1048 UninstallQtMsgHandler();
1049 return FALSE;
1050 }
1051 }
1052 if ( comms & Stdout ) {
1053 HFILE clientStdout = HF_NULL;
1054 do {
1055 // create a Stdout pipe
1056 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdout",
1057 ppib->pib_ulpid, this );
1058 rc = DosCreateNPipe( pathBuf, &d->stdout.pipe,
1059 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
1060 0, PIPE_SIZE_STDOUT, 0 );
1061 if ( rc != NO_ERROR )
1062 break;
1063 DosConnectNPipe( d->stdout.pipe );
1064 // open a client end of the Stdout pipe
1065 ULONG action = 0;
1066 rc = DosOpen( pathBuf, &clientStdout, &action, 0, FILE_NORMAL, FILE_OPEN,
1067 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
1068 (PEAOP2) NULL);
1069 if ( rc != NO_ERROR )
1070 break;
1071 // save the real STDOUT handle
1072 if ( (rc = DosDupHandle( HF_STDOUT, &tmpStdout )) != NO_ERROR )
1073 break;
1074 // redirect the real STDOUT handle to the client end of the pipe
1075 if ( (rc = DosDupHandle( clientStdout, &realStdout )) != NO_ERROR )
1076 break;
1077 } while( 0 );
1078 // close the client end anyway (no more necessary)
1079 if ( clientStdout != HF_NULL )
1080 DosClose ( clientStdout );
1081 // close all opened handles on error
1082 if ( rc != NO_ERROR ) {
1083#if defined(QT_QPROCESS_DEBUG)
1084 qDebug( "Failed to create a STDOUT redirection (%s): SYS%04ld",
1085 pathBuf, rc );
1086#endif
1087 if ( tmpStdin != HF_NULL ) {
1088 DosDupHandle( tmpStdin, &realStdin );
1089 DosClose( tmpStdin );
1090 }
1091 if ( tmpStdout != HF_NULL )
1092 DosClose ( tmpStdout );
1093 d->closeHandles();
1094 UninstallQtMsgHandler();
1095 return FALSE;
1096 }
1097 }
1098 if ( (comms & Stderr) && !(comms & DupStderr) ) {
1099 HFILE clientStderr = HF_NULL;
1100 do {
1101 // create a Stderr pipe
1102 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stderr",
1103 ppib->pib_ulpid, this );
1104 rc = DosCreateNPipe( pathBuf, &d->stderr.pipe,
1105 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
1106 0, PIPE_SIZE_STDERR, 0 );
1107 if ( rc != NO_ERROR )
1108 break;
1109 DosConnectNPipe( d->stderr.pipe );
1110 // open a client end of the Stderr pipe
1111 ULONG action = 0;
1112 rc = DosOpen( pathBuf, &clientStderr, &action, 0, FILE_NORMAL, FILE_OPEN,
1113 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
1114 (PEAOP2) NULL);
1115 if ( rc != NO_ERROR )
1116 break;
1117 // save the real STDERR handle
1118 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
1119 break;
1120 // redirect the real STDERR handle to the client end of the pipe
1121 if ( (rc = DosDupHandle( clientStderr, &realStderr )) != NO_ERROR )
1122 break;
1123 } while( 0 );
1124 // close the client end anyway (no more necessary)
1125 if ( clientStderr != HF_NULL )
1126 DosClose ( clientStderr );
1127 // close all opened handles on error
1128 if ( rc != NO_ERROR ) {
1129#if defined(QT_QPROCESS_DEBUG)
1130 qDebug( "Failed to create a STDERR redirection (%s): SYS%04ld",
1131 pathBuf, rc );
1132#endif
1133 if ( tmpStdin != HF_NULL ) {
1134 DosDupHandle( tmpStdin, &realStdin );
1135 DosClose( tmpStdin );
1136 }
1137 if ( tmpStdout != HF_NULL ) {
1138 DosDupHandle( tmpStdout, &realStdout );
1139 DosClose( tmpStdout );
1140 }
1141 if ( tmpStderr != HF_NULL )
1142 DosClose ( tmpStderr );
1143 d->closeHandles();
1144 UninstallQtMsgHandler();
1145 return FALSE;
1146 }
1147 }
1148 if ( comms & DupStderr ) {
1149 // leave d->stderr.pipe equal to HP_NULL
1150 do {
1151 // save the real STDERR handle
1152 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
1153 break;
1154 // redirect STDERR to STDOUT
1155 if ( (rc = DosDupHandle( HF_STDOUT, &realStderr )) != NO_ERROR )
1156 break;
1157 } while( 0 );
1158 // close all opened handles on error
1159 if ( rc != NO_ERROR ) {
1160#if defined(QT_QPROCESS_DEBUG)
1161 qDebug( "Failed to redirect STDERR to STDOUT: SYS%04ld",
1162 rc );
1163#endif
1164 if ( tmpStdin != HF_NULL ) {
1165 DosDupHandle( tmpStdin, &realStdin );
1166 DosClose( tmpStdin );
1167 }
1168 if ( tmpStdout != HF_NULL ) {
1169 DosDupHandle( tmpStdout, &realStdout );
1170 DosClose( tmpStdout );
1171 }
1172 if ( tmpStderr != HF_NULL )
1173 DosClose ( tmpStderr );
1174 d->closeHandles();
1175 UninstallQtMsgHandler();
1176 return FALSE;
1177 }
1178 }
1179
1180 // add this process to the monitor
1181 if ( !processMonitor->addProcess( this ) ) {
1182 if ( tmpStdin != HF_NULL ) {
1183 DosDupHandle( tmpStdin, &realStdin );
1184 DosClose( tmpStdin );
1185 }
1186 if ( tmpStdout != HF_NULL ) {
1187 DosDupHandle( tmpStdout, &realStdout );
1188 DosClose( tmpStdout );
1189 }
1190 if ( tmpStderr != HF_NULL ) {
1191 DosDupHandle( tmpStderr, &realStderr );
1192 DosClose ( tmpStderr );
1193 }
1194 d->closeHandles();
1195 UninstallQtMsgHandler();
1196 return FALSE;
1197 }
1198
1199 // set the working directory
1200 QString curDir = QDir::currentDirPath();
1201 QDir::setCurrent( workingDir.path() );
1202#if defined(QT_QPROCESS_DEBUG)
1203 qDebug( "QProcess::start(): curDir='%s', workingDir='%s'",
1204 curDir.local8Bit().data(),
1205 QDir::currentDirPath().local8Bit().data() );
1206#endif
1207
1208 // DosExecPgm()
1209
1210 RESULTCODES resc = { 0 };
1211
1212 rc = DosExecPgm( pathBuf, sizeof(pathBuf), EXEC_ASYNCRESULT,
1213 buf.data(), envlist.data(), &resc, appNameFull.data() );
1214
1215 // restore the current directory
1216 QDir::setCurrent( curDir );
1217
1218 // cancel STDIN/OUT/ERR redirections
1219 if ( tmpStdin != HF_NULL ) {
1220 DosDupHandle( tmpStdin, &realStdin );
1221 DosClose( tmpStdin );
1222 }
1223 if ( tmpStdout != HF_NULL ) {
1224 DosDupHandle( tmpStdout, &realStdout );
1225 DosClose( tmpStdout );
1226 }
1227 if ( tmpStderr != HF_NULL ) {
1228 DosDupHandle( tmpStderr, &realStderr );
1229 DosClose( tmpStderr );
1230 }
1231
1232 UninstallQtMsgHandler();
1233
1234 if ( rc != NO_ERROR ) {
1235#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
1236#if defined(QT_DEBUG)
1237 qWarning( "[%s] [%s]\n(%s):",
1238 buf.data(), buf.data() + strlen( buf.data() ) + 1,
1239 pathBuf );
1240#endif
1241 qSystemWarning( "Failed to start a new process!", rc );
1242#endif
1243 processMonitor->removeProcess( d );
1244 d->closeHandles();
1245 return FALSE;
1246 }
1247
1248 // memporize PID of the started process
1249 d->pid = resc.codeTerminate;
1250
1251 // timer is not necessary for ioRedirection (we use the monitor thread)
1252 if ( /* ioRedirection || */ notifyOnExit ) {
1253 d->lookup->start( POLL_TIMER );
1254 }
1255
1256 return TRUE;
1257}
1258
1259void QProcess::tryTerminate() const
1260{
1261 if ( d->pid == PID_NULL )
1262 return;
1263
1264 HSWITCH hswitch = WinQuerySwitchHandle( NULL, d->pid );
1265 if ( hswitch != NULLHANDLE ) {
1266 SWCNTRL swcntrl = { 0 };
1267 APIRET rc = WinQuerySwitchEntry( hswitch, &swcntrl );
1268 // WinQuerySwitchEntry will return a switch entry of the parent
1269 // process if the specfied one doesn't have a separate session
1270 // (running a plain CMD.EXE is an example); ignore this case.
1271 if ( rc == NO_ERROR && swcntrl.idProcess == d->pid )
1272 {
1273 // first, ensure that the Close action is enabled in the main frame
1274 // window (otherwise WM_SYSCOMMAND/SC_CLOSE will be ignored)
1275 HWND hwndSysMenu = WinWindowFromID( swcntrl.hwnd, FID_SYSMENU );
1276 if (hwndSysMenu) {
1277 WinPostMsg( hwndSysMenu, MM_SETITEMATTR,
1278 MPFROM2SHORT( SC_CLOSE, TRUE ),
1279 MPFROM2SHORT( MIA_DISABLED, 0 ) );
1280 }
1281 WinPostMsg( swcntrl.hwnd, WM_SYSCOMMAND,
1282 MPFROM2SHORT( SC_CLOSE, CMDSRC_OTHER ),
1283 MPFROMLONG( FALSE ) );
1284 }
1285 }
1286}
1287
1288void QProcess::kill() const
1289{
1290 if ( d->pid != PID_NULL )
1291 DosKillProcess( DKP_PROCESS, d->pid );
1292}
1293
1294bool QProcess::isRunning() const
1295{
1296 if ( d->pid == PID_NULL )
1297 return FALSE;
1298
1299 PID pidEnded = PID_NULL;
1300 RESULTCODES resc = { 0 };
1301 APIRET rc = DosWaitChild( DCWA_PROCESS, DCWW_NOWAIT, &resc,
1302 &pidEnded, d->pid );
1303
1304 if ( rc == ERROR_CHILD_NOT_COMPLETE )
1305 return TRUE;
1306
1307 QProcess *that = (QProcess *) this;
1308
1309 // There might be data to read, but WM_U_PIPE_RDATA messages won't be
1310 // converted to signals after removeProcess() is called below. Therefore,
1311 // emit signals ourselved if necessary.
1312
1313 if ( d->readPipe( &d->stdout ) )
1314 emit that->readyReadStdout();
1315 if ( d->readPipe( &d->stderr ) )
1316 emit that->readyReadStderr();
1317
1318 // compute the exit values
1319 if ( !d->exitValuesCalculated ) {
1320 that->exitNormal = resc.codeTerminate == TC_EXIT;
1321 that->exitStat = resc.codeResult;
1322 d->exitValuesCalculated = TRUE;
1323 }
1324
1325 processMonitor->removeProcess( d );
1326
1327 d->pid = PID_NULL;
1328 d->closeHandles();
1329 return FALSE;
1330}
1331
1332bool QProcess::canReadLineStdout() const
1333{
1334 if( d->stdout.pipe == HP_NULL )
1335 return d->stdout.buf.size() != 0;
1336
1337 QProcess *that = (QProcess *) this;
1338 return that->membufStdout()->scanNewline( 0 );
1339}
1340
1341bool QProcess::canReadLineStderr() const
1342{
1343 if( d->stderr.pipe == HP_NULL )
1344 return d->stderr.buf.size() != 0;
1345
1346 QProcess *that = (QProcess *) this;
1347 return that->membufStderr()->scanNewline( 0 );
1348}
1349
1350void QProcess::writeToStdin( const QByteArray& buf )
1351{
1352 d->stdinBuf.enqueue( new QByteArray(buf) );
1353 socketWrite( 0 );
1354}
1355
1356void QProcess::closeStdin( )
1357{
1358 if ( d->pipeStdin != HP_NULL ) {
1359 DosDisConnectNPipe( d->pipeStdin );
1360 DosClose( d->pipeStdin );
1361 d->pipeStdin = HP_NULL;
1362 }
1363}
1364
1365/* dummy, since MOC doesn't understand preprocessor defines */
1366void QProcess::socketRead( int fd )
1367{
1368}
1369
1370void QProcess::socketWrite( int )
1371{
1372 if ( d->pipeStdin == HP_NULL )
1373 return;
1374
1375 while ( !d->stdinBuf.isEmpty() && isRunning() ) {
1376 ULONG written = 0;
1377 APIRET rc = DosWrite( d->pipeStdin,
1378 d->stdinBuf.head()->data() + d->stdinBufRead,
1379 QMIN( PIPE_SIZE_STDIN,
1380 d->stdinBuf.head()->size() - d->stdinBufRead ),
1381 &written );
1382 if ( rc != NO_ERROR ) {
1383#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
1384 qSystemWarning( "Failed to write to the pipe!", rc );
1385#endif
1386 d->lookup->start( POLL_TIMER );
1387 return;
1388 }
1389
1390 d->stdinBufRead += written;
1391 if ( d->stdinBufRead == d->stdinBuf.head()->size() ) {
1392 d->stdinBufRead = 0;
1393 delete d->stdinBuf.dequeue();
1394 if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
1395 emit wroteToStdin();
1396 }
1397 }
1398}
1399
1400void QProcess::flushStdin()
1401{
1402 socketWrite( 0 );
1403}
1404
1405/*
1406 Use a timer for polling misc. stuff.
1407*/
1408void QProcess::timeout()
1409{
1410 // Disable the timer temporary since one of the slots that are connected to
1411 // the readyRead...(), etc. signals might trigger recursion if
1412 // processEvents() is called.
1413 d->lookup->stop();
1414
1415 // try to write pending data to stdin
1416 if ( !d->stdinBuf.isEmpty() )
1417 socketWrite( 0 );
1418
1419 if ( isRunning() ) {
1420 // enable timer again, if needed
1421 // timer is not necessary for ioRedirection (we use the monitor thread)
1422 if ( !d->stdinBuf.isEmpty() || /* ioRedirection || */ notifyOnExit )
1423 d->lookup->start( POLL_TIMER );
1424 } else if ( notifyOnExit ) {
1425 emit processExited();
1426 }
1427}
1428
1429/*
1430 Used by connectNotify() and disconnectNotify() to change the value of
1431 ioRedirection (and related behaviour)
1432*/
1433void QProcess::setIoRedirection( bool value )
1434{
1435 ioRedirection = value;
1436 // timer is not necessary for ioRedirection (we use the monitor thread)
1437#if 0
1438 if ( !ioRedirection && !notifyOnExit )
1439 d->lookup->stop();
1440 if ( ioRedirection ) {
1441 if ( isRunning() )
1442 d->lookup->start( POLL_TIMER );
1443 }
1444#endif
1445}
1446
1447/*
1448 Used by connectNotify() and disconnectNotify() to change the value of
1449 notifyOnExit (and related behaviour)
1450*/
1451void QProcess::setNotifyOnExit( bool value )
1452{
1453 notifyOnExit = value;
1454 // timer is not necessary for ioRedirection (we use the monitor thread)
1455 if ( /* !ioRedirection && */ !notifyOnExit )
1456 d->lookup->stop();
1457 if ( notifyOnExit ) {
1458 if ( isRunning() )
1459 d->lookup->start( POLL_TIMER );
1460 }
1461}
1462
1463/*
1464 Used by connectNotify() and disconnectNotify() to change the value of
1465 wroteToStdinConnected (and related behaviour)
1466*/
1467void QProcess::setWroteStdinConnected( bool value )
1468{
1469 wroteToStdinConnected = value;
1470}
1471
1472QProcess::PID QProcess::processIdentifier()
1473{
1474 return d->pid;
1475}
1476
1477#endif // QT_NO_PROCESS
Note: See TracBrowser for help on using the repository browser.