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

Last change on this file since 41 was 41, checked in by dmik, 20 years ago

Implemented the OS/2 version of the QProcess class.

File size: 28.3 KB
Line 
1/****************************************************************************
2** $Id$
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. Further OS/2 Development.
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 "private/qinternal_p.h"
47#include "qt_os2.h"
48
49#include <string.h>
50
51//#define QT_QPROCESS_DEBUG
52
53const HFILE HF_STDIN = 0;
54const HFILE HF_STDOUT = 1;
55const HFILE HF_STDERR = 2;
56const HFILE HF_ANY = 0xFFFFFFFF;
57
58const PID PID_NULL = ~1;
59
60const ULONG PIPE_SIZE = 1024;
61
62// just fake constants, for convenience
63const int FD_PIPE_STDOUT = 1;
64const int FD_PIPE_STDERR = 2;
65
66#if defined(QT_QPROCESS_DEBUG)
67#include <stdarg.h>
68#endif
69
70/***********************************************************************
71 *
72 * QProcessPrivate
73 *
74 **********************************************************************/
75class QProcessPrivate
76{
77public:
78 QProcessPrivate( QProcess *proc )
79 {
80 stdinBufRead = 0;
81 pipeStdin = HF_ANY;
82 pipeStdout = HF_ANY;
83 pipeStderr = HF_ANY;
84 exitValuesCalculated = FALSE;
85
86 lookup = new QTimer( proc );
87 qApp->connect( lookup, SIGNAL(timeout()),
88 proc, SLOT(timeout()) );
89
90 pid = PID_NULL;
91 }
92
93 ~QProcessPrivate()
94 {
95 reset();
96 }
97
98 void reset()
99 {
100 while ( !stdinBuf.isEmpty() ) {
101 delete stdinBuf.dequeue();
102 }
103 closeHandles();
104 stdinBufRead = 0;
105 pipeStdin = HF_ANY;
106 pipeStdout = HF_ANY;
107 pipeStderr = HF_ANY;
108 exitValuesCalculated = FALSE;
109
110 pid = PID_NULL;
111 }
112
113 void closeHandles()
114 {
115 if( pipeStdin != HF_ANY ) {
116 DosDisConnectNPipe( pipeStdin );
117 DosClose( pipeStdin );
118 pipeStdin = HF_ANY;
119 }
120 if( pipeStdout != HF_ANY ) {
121 // check if STDERR was redirected to STDOUT
122 if ( pipeStderr == pipeStdout )
123 pipeStderr = HF_ANY;
124 DosDisConnectNPipe( pipeStdout );
125 DosClose( pipeStdout );
126 pipeStdout = HF_ANY;
127 }
128 if( pipeStderr != HF_ANY ) {
129 DosDisConnectNPipe( pipeStderr );
130 DosClose( pipeStderr );
131 pipeStderr = HF_ANY;
132 }
133 }
134
135 QMembuf bufStdout;
136 QMembuf bufStderr;
137
138 QPtrQueue <QByteArray> stdinBuf;
139
140 HPIPE pipeStdin;
141 HPIPE pipeStdout;
142 HPIPE pipeStderr;
143
144 PID pid;
145
146 QTimer *lookup;
147
148 uint stdinBufRead;
149
150 bool exitValuesCalculated;
151};
152
153
154/***********************************************************************
155 *
156 * QProcess
157 *
158 **********************************************************************/
159
160void QProcess::init()
161{
162 d = new QProcessPrivate( this );
163 exitStat = 0;
164 exitNormal = FALSE;
165}
166
167void QProcess::reset()
168{
169 d->reset();
170 exitStat = 0;
171 exitNormal = FALSE;
172 d->bufStdout.clear();
173 d->bufStderr.clear();
174}
175
176QMembuf* QProcess::membufStdout()
177{
178 if( d->pipeStdout != 0 )
179 socketRead( FD_PIPE_STDOUT );
180 return &d->bufStdout;
181}
182
183QMembuf* QProcess::membufStderr()
184{
185 if( d->pipeStderr != 0 )
186 socketRead( FD_PIPE_STDERR );
187 return &d->bufStderr;
188}
189
190QProcess::~QProcess()
191{
192 delete d;
193}
194
195#if defined(QT_QPROCESS_DEBUG)
196static void DosPrintf( HFILE fd, const char *fmt, ... )
197{
198 static char buf[ 1024 ];
199
200 va_list args;
201 va_start( args, fmt );
202 vsnprintf( buf, sizeof(buf), fmt, args );
203 va_end ( args );
204
205 ULONG written = 0;
206 DosWrite( fd, buf, strlen( buf ), &written );
207}
208#endif
209
210bool QProcess::start( QStringList *env )
211{
212#if defined(QT_QPROCESS_DEBUG)
213 qDebug( "QProcess::start()" );
214#endif
215 reset();
216
217 if ( _arguments.isEmpty() )
218 return FALSE;
219
220 // construct the arguments for DosExecPgm()
221
222 QCString appName, appArgs;
223 {
224 QString args;
225 for ( QStringList::ConstIterator it = _arguments.begin();
226 it != _arguments.end(); ++ it
227 ) {
228 if ( it == _arguments.begin() ) {
229 appName = (*it).local8Bit();
230 } else {
231 QString s = *it;
232 // quote the argument when it contains spaces
233 if ( s.contains( ' ' ) )
234 s = '"' + s + '"';
235 if ( args.isNull() )
236 args = s;
237 else
238 args += ' ' + s;
239 }
240 }
241 appArgs = args.local8Bit();
242
243#if defined(QT_QPROCESS_DEBUG)
244 qDebug( "QProcess::start(): app [%s], args [%s]",
245 appName.data(), appArgs.data() );
246#endif
247 }
248
249 QByteArray envlist;
250 if ( env != 0 ) {
251 uint pos = 0;
252 // inherit PATH if missing (for convenience)
253 // (note that BEGINLIBPATH and ENDLIBPATH, if any, are automatically
254 // inherited, while LIBPATH is always a global setting)
255 {
256 const char *path = getenv( "PATH" );
257 if ( env->grep( QRegExp( "^PATH=", FALSE ) ).empty() && path ) {
258 uint sz = strlen( path ) + 5 /* PATH= */ + 1;
259 envlist.resize( envlist.size() + sz );
260 sprintf( envlist.data() + pos, "PATH=%s", path );
261 pos += sz;
262 }
263 }
264 // inherit COMSPEC if missing (to let the child start .cmd and .bat)
265 {
266 const char *comspec = getenv( "COMSPEC" );
267 if ( env->grep( QRegExp( "^COMSPEC=", FALSE ) ).empty() && comspec ) {
268 uint sz = strlen( comspec ) + 8 /* COMSPEC= */ + 1;
269 envlist.resize( envlist.size() + sz );
270 sprintf( envlist.data() + pos, "COMSPEC=%s", comspec );
271 pos += sz;
272 }
273 }
274 // add the user environment
275 for ( QStringList::ConstIterator it = env->begin();
276 it != env->end(); ++ it
277 ) {
278 QCString var = (*it).local8Bit();
279 uint sz = var.length() + 1;
280 envlist.resize( envlist.size() + sz );
281 memcpy( envlist.data() + pos, var.data(), sz );
282 pos += sz;
283 }
284 // add the terminating 0
285 envlist.resize( envlist.size() + 1 );
286 envlist[ pos ++ ] = 0;
287 }
288
289 // search for the app executable
290
291 const uint appNameLen = appName.length();
292 const uint appArgsLen = appArgs.length();
293
294 bool hasPath = appName[ strcspn( appName.data(), "/\\" ) ] != '\0';
295 bool hasSpaces = strchr( appName.data(), ' ' ) != NULL;
296
297 // list of executable file extensions, in order of CMD.EXE's precedence
298 // (the first runs directly, the rest requires CMD.EXE)
299 const char *exeExts[] = { ".exe", ".cmd", ".bat" };
300
301 // detect which exe extension do we have, if any
302 int ext = -1;
303 if ( appNameLen >= 4 ) {
304 for ( uint i = 0; ext < 0 && i < sizeof(exeExts) / sizeof(exeExts[0]); ++ i )
305 if ( stricmp( appName.data() + appNameLen - 4, exeExts[i] ) == 0 )
306 ext = i;
307 }
308
309 QByteArray buf;
310 QCString appNameFull;
311
312 APIRET rc = 0;
313 char pathBuf[ CCHMAXPATH ];
314
315 // run through all possible exe extension combinations (+ no extension case)
316 for ( uint i = 0; i <= sizeof(exeExts) / sizeof(exeExts[ 0 ]); ++ i ) {
317 if ( i == 0 ) {
318 // try the unmodified app name first if it contains a path spec
319 // or has one of predefined exe extensions
320 if ( hasPath || ext >= 0 ) {
321 if ( buf.size() < appNameLen + 1 )
322 buf.resize ( appNameLen + 1 );
323 strcpy( buf.data(), appName.data() );
324 } else
325 continue;
326 } else {
327 ext = i - 1;
328 const uint extLen = strlen( exeExts[ ext ] );
329 uint sz = appNameLen + extLen + 1;
330 if ( buf.size() < sz )
331 buf.resize( sz );
332 strcpy( buf.data(), appName.data() );
333 strcpy( buf.data() + appNameLen, exeExts[ ext ] );
334 }
335
336#if defined(QT_QPROCESS_DEBUG)
337 qDebug( "QProcess::start(): trying to find [%s]", buf.data() );
338#endif
339 if ( hasPath ) {
340 uint lastSep = strlen( buf.data() );
341 while ( lastSep-- && buf[ lastSep ] != '/' && buf[ lastSep ] != '\\' )
342 ;
343 buf[ lastSep ] = '\0';
344 rc = DosSearchPath( 0, buf.data(), buf.data() + lastSep + 1,
345 pathBuf, sizeof(pathBuf) );
346 } else {
347 rc = DosSearchPath( SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT |
348 SEARCH_CUR_DIRECTORY, "PATH",
349 buf.data(), pathBuf, sizeof(pathBuf) );
350 }
351
352 if ( rc == NO_ERROR ) {
353#if defined(QT_QPROCESS_DEBUG)
354 qDebug( "QProcess::start(): found [%s]", pathBuf );
355#endif
356 appNameFull = pathBuf;
357 break;
358 }
359
360 if ( rc != ERROR_FILE_NOT_FOUND && rc != ERROR_PATH_NOT_FOUND ) {
361#if defined(QT_QPROCESS_DEBUG)
362 qDebug( "QProcess::start(): found [%s], but got an error:", pathBuf );
363 qSystemWarning( "", rc );
364#endif
365 break;
366 }
367 }
368
369 if ( appNameFull.isNull() ) {
370#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
371#if defined(QT_DEBUG)
372 qWarning( "[%s]:", appName.data() );
373#endif
374 qSystemWarning( "Failed to find the executable!", rc );
375#endif
376 return FALSE;
377 }
378
379 // final preparation of arguments
380
381 if ( ext <= 0 ) {
382 // run directly (args = <appName>\0\<appArgs>\0\0)
383 uint sz = appNameLen + 1 + appArgsLen + 2;
384 if ( buf.size() < sz )
385 buf.resize( sz );
386 strcpy( buf.data(), appName.data() );
387 strcpy( buf.data() + appNameLen + 1, appArgs.data() );
388 buf[ sz - 1 ] = '\0';
389 } else {
390 // run via shell (args = <shell>\0"/c "<appName>" "<appArgs>\0\0)
391 appNameFull = getenv( "COMSPEC" );
392 if ( appNameFull.isNull() ) {
393 ULONG bootDrv = 0;
394 rc = DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
395 &bootDrv, sizeof(bootDrv) );
396 if ( rc == NO_ERROR && bootDrv >= 1 ) {
397 appNameFull = "?:\\OS2\\CMD.EXE";
398 appNameFull[ 0 ] = char( bootDrv ) + 'A' - 1;
399 }
400 }
401 if ( appNameFull.isNull() ) {
402#if defined(QT_QPROCESS_DEBUG)
403 qDebug( "QProcess::start(): OS/2 command line interpreter is not found!" );
404#endif
405 return FALSE;
406 }
407 const uint shellLen = strlen( appNameFull );
408 uint sz = shellLen + 1 + 3 + appNameLen + 1 + appArgsLen + 2;
409 if ( hasSpaces )
410 sz += 2; // for quotes
411 if ( !appArgsLen )
412 sz -= 1; // no need for space
413 if ( buf.size() < sz )
414 buf.resize( sz );
415 strcpy( buf.data(), appNameFull );
416 sprintf( buf.data() + shellLen + 1, hasSpaces ? "/c \"%s\"" : "/c %s",
417 appName.data() );
418 if ( appArgsLen )
419 strcat( buf.data() + shellLen + 1, appArgs.data() );
420 buf[ sz - 1 ] = '\0';
421 }
422
423#if defined(QT_QPROCESS_DEBUG)
424 qDebug( "QProcess::start(): will start [%s] as [%s] [%s]",
425 appNameFull.data(), buf.data(), buf.data() + strlen( buf.data() ) + 1 );
426#endif
427
428 // create STDIN/OUT/ERR redirection pipes
429
430 HFILE tmpStdin = HF_ANY, tmpStdout = HF_ANY, tmpStderr = HF_ANY;
431 HFILE realStdin = HF_STDIN, realStdout = HF_STDOUT, realStderr = HF_STDERR;
432
433 // we need the process identifier to guarantee pipe name unicity
434 PPIB ppib = NULL;
435 DosGetInfoBlocks( NULL, &ppib );
436
437 if ( comms & Stdin ) {
438 HFILE clientStdin = HF_ANY;
439 do {
440 // create a Stdin pipe
441 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdin",
442 ppib->pib_ulpid, this );
443 rc = DosCreateNPipe( pathBuf, &d->pipeStdin,
444 NP_ACCESS_OUTBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
445 PIPE_SIZE, PIPE_SIZE, 0 );
446 if ( rc != NO_ERROR )
447 break;
448 DosConnectNPipe( d->pipeStdin );
449 // open a client end of the Stdout pipe
450 ULONG action = 0;
451 rc = DosOpen( pathBuf, &clientStdin, &action, 0, FILE_NORMAL, FILE_OPEN,
452 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
453 (PEAOP2) NULL);
454 if ( rc != NO_ERROR )
455 break;
456 // save the real STDIN handle
457 if ( (rc = DosDupHandle( HF_STDIN, &tmpStdin)) != NO_ERROR )
458 break;
459 // redirect the real STDIN handle to the client end of the pipe
460 if ( (rc = DosDupHandle( clientStdin, &realStdin)) != NO_ERROR )
461 break;
462 } while( 0 );
463 // close the client end anyway (no more necessary)
464 if ( clientStdin != HF_ANY )
465 DosClose ( clientStdin );
466 // close all opened handles on error
467 if ( rc != NO_ERROR ) {
468#if defined(QT_QPROCESS_DEBUG)
469 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
470 "Failed to create a STDIN redirection (%s): SYS%04ld\n\r",
471 pathBuf, rc );
472#endif
473 if ( tmpStdin != HF_ANY )
474 DosClose ( tmpStdin );
475 d->closeHandles();
476 return FALSE;
477 }
478 }
479 if ( comms & Stdout ) {
480 HFILE clientStdout = HF_ANY;
481 do {
482 // create a Stdout pipe
483 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdout",
484 ppib->pib_ulpid, this );
485 rc = DosCreateNPipe( pathBuf, &d->pipeStdout,
486 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
487 PIPE_SIZE, PIPE_SIZE, 0 );
488 if ( rc != NO_ERROR )
489 break;
490 DosConnectNPipe( d->pipeStdout );
491 // open a client end of the Stdout pipe
492 ULONG action = 0;
493 rc = DosOpen( pathBuf, &clientStdout, &action, 0, FILE_NORMAL, FILE_OPEN,
494 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
495 (PEAOP2) NULL);
496 if ( rc != NO_ERROR )
497 break;
498 // save the real STDOUT handle
499 if ( (rc = DosDupHandle( HF_STDOUT, &tmpStdout )) != NO_ERROR )
500 break;
501 // redirect the real STDOUT handle to the client end of the pipe
502 if ( (rc = DosDupHandle( clientStdout, &realStdout )) != NO_ERROR )
503 break;
504 } while( 0 );
505 // close the client end anyway (no more necessary)
506 if ( clientStdout != HF_ANY )
507 DosClose ( clientStdout );
508 // close all opened handles on error
509 if ( rc != NO_ERROR ) {
510#if defined(QT_QPROCESS_DEBUG)
511 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
512 "Failed to create a STDOUT redirection (%s): SYS%04ld\n\r",
513 pathBuf, rc );
514#endif
515 if ( tmpStdin != HF_ANY ) {
516 DosDupHandle( tmpStdin, &realStdin );
517 DosClose( tmpStdin );
518 }
519 if ( tmpStdout != HF_ANY )
520 DosClose ( tmpStdout );
521 d->closeHandles();
522 return FALSE;
523 }
524 }
525 if ( (comms & Stderr) && !(comms & DupStderr) ) {
526 HFILE clientStderr = HF_ANY;
527 do {
528 // create a Stderr pipe
529 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stderr",
530 ppib->pib_ulpid, this );
531 rc = DosCreateNPipe( pathBuf, &d->pipeStderr,
532 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
533 PIPE_SIZE, PIPE_SIZE, 0 );
534 if ( rc != NO_ERROR )
535 break;
536 DosConnectNPipe( d->pipeStderr );
537 // open a client end of the Stderr pipe
538 ULONG action = 0;
539 rc = DosOpen( pathBuf, &clientStderr, &action, 0, FILE_NORMAL, FILE_OPEN,
540 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
541 (PEAOP2) NULL);
542 if ( rc != NO_ERROR )
543 break;
544 // save the real STDERR handle
545 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
546 break;
547 // redirect the real STDERR handle to the client end of the pipe
548 if ( (rc = DosDupHandle( clientStderr, &realStderr )) != NO_ERROR )
549 break;
550 } while( 0 );
551 // close the client end anyway (no more necessary)
552 if ( clientStderr != HF_ANY )
553 DosClose ( clientStderr );
554 // close all opened handles on error
555 if ( rc != NO_ERROR ) {
556#if defined(QT_QPROCESS_DEBUG)
557 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
558 "Failed to create a STDERR redirection (%s): SYS%04ld\n\r",
559 pathBuf, rc );
560#endif
561 if ( tmpStdin != HF_ANY ) {
562 DosDupHandle( tmpStdin, &realStdin );
563 DosClose( tmpStdin );
564 }
565 if ( tmpStdout != HF_ANY ) {
566 DosDupHandle( tmpStdout, &realStdout );
567 DosClose( tmpStdout );
568 }
569 if ( tmpStderr != HF_ANY )
570 DosClose ( tmpStderr );
571 d->closeHandles();
572 return FALSE;
573 }
574 }
575 if ( comms & DupStderr ) {
576 d->pipeStderr = d->pipeStdout;
577 do {
578 // save the real STDERR handle
579 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
580 break;
581 // redirect STDERR to STDOUT
582 if ( (rc = DosDupHandle( HF_STDOUT, &realStderr )) != NO_ERROR )
583 break;
584 } while( 0 );
585 // close all opened handles on error
586 if ( rc != NO_ERROR ) {
587#if defined(QT_QPROCESS_DEBUG)
588 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
589 "Failed to redirect STDERR to STDOUT: SYS%04ld\n\r",
590 rc );
591#endif
592 if ( tmpStdin != HF_ANY ) {
593 DosDupHandle( tmpStdin, &realStdin );
594 DosClose( tmpStdin );
595 }
596 if ( tmpStdout != HF_ANY ) {
597 DosDupHandle( tmpStdout, &realStdout );
598 DosClose( tmpStdout );
599 }
600 if ( tmpStderr != HF_ANY )
601 DosClose ( tmpStderr );
602 d->closeHandles();
603 return FALSE;
604 }
605 }
606
607 // DosExecPgm()
608
609 RESULTCODES resc = { 0 };
610
611 rc = DosExecPgm( pathBuf, sizeof(pathBuf), EXEC_ASYNCRESULT,
612 buf.data(), envlist.data(), &resc, appNameFull.data() );
613
614 // cancel STDIN/OUT/ERR redirections
615 if ( tmpStdin != HF_ANY ) {
616 DosDupHandle( tmpStdin, &realStdin );
617 DosClose( tmpStdin );
618 }
619 if ( tmpStdout != HF_ANY ) {
620 DosDupHandle( tmpStdout, &realStdout );
621 DosClose( tmpStdout );
622 }
623 if ( tmpStderr != HF_ANY ) {
624 DosDupHandle( tmpStderr, &realStderr );
625 DosClose( tmpStderr );
626 }
627
628 if ( rc != NO_ERROR ) {
629#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
630#if defined(QT_DEBUG)
631 qWarning( "[%s] [%s]\n(%s):",
632 buf.data(), buf.data() + strlen( buf.data() ) + 1,
633 pathBuf );
634#endif
635 qSystemWarning( "Failed to start a new process!", rc );
636#endif
637 d->closeHandles();
638 return FALSE;
639 }
640
641 // memporize PID of the started process
642 d->pid = resc.codeTerminate;
643
644 if ( ioRedirection || notifyOnExit ) {
645 d->lookup->start( 100 );
646 }
647
648 return TRUE;
649}
650
651void QProcess::tryTerminate() const
652{
653 if ( d->pid == PID_NULL )
654 return;
655
656 HSWITCH hswitch = WinQuerySwitchHandle( NULL, d->pid );
657 if ( hswitch != NULLHANDLE ) {
658 SWCNTRL swcntrl = { 0 };
659 APIRET rc = WinQuerySwitchEntry( hswitch, &swcntrl );
660 // WinQuerySwitchEntry will return a switch entry of the parent
661 // process if the specfied one doesn't have a separate session
662 // (running a plain CMD.EXE is an example); ignore this case.
663 if ( rc == NO_ERROR && swcntrl.idProcess == d->pid )
664 {
665 // first, ensure that the Close action is enabled in the main frame
666 // window (otherwise WM_SYSCOMMAND/SC_CLOSE will be ignored)
667 HWND hwndSysMenu = WinWindowFromID( swcntrl.hwnd, FID_SYSMENU );
668 if (hwndSysMenu) {
669 WinPostMsg( hwndSysMenu, MM_SETITEMATTR,
670 MPFROM2SHORT( SC_CLOSE, TRUE ),
671 MPFROM2SHORT( MIA_DISABLED, 0 ) );
672 }
673 WinPostMsg( swcntrl.hwnd, WM_SYSCOMMAND,
674 MPFROM2SHORT( SC_CLOSE, CMDSRC_OTHER ),
675 MPFROMLONG( FALSE ) );
676 }
677 }
678}
679
680void QProcess::kill() const
681{
682 if ( d->pid != PID_NULL )
683 DosKillProcess( DKP_PROCESS, d->pid );
684}
685
686bool QProcess::isRunning() const
687{
688 if ( d->pid == PID_NULL )
689 return FALSE;
690
691 PID pidEnded = PID_NULL;
692 RESULTCODES resc = { 0 };
693 APIRET rc = DosWaitChild( DCWA_PROCESS, DCWW_NOWAIT, &resc,
694 &pidEnded, d->pid );
695
696 if ( rc == ERROR_CHILD_NOT_COMPLETE )
697 return TRUE;
698
699 // there might be data to read
700 QProcess *that = (QProcess *) this;
701 that->socketRead( FD_PIPE_STDOUT ); // try stdout
702 that->socketRead( FD_PIPE_STDERR ); // try stderr
703
704 // compute the exit values
705 if ( !d->exitValuesCalculated ) {
706 that->exitNormal = resc.codeTerminate == TC_EXIT;
707 that->exitStat = resc.codeResult;
708 d->exitValuesCalculated = TRUE;
709 }
710
711 d->pid = PID_NULL;
712 d->closeHandles();
713 return FALSE;
714}
715
716bool QProcess::canReadLineStdout() const
717{
718 if( d->pipeStdout == HF_ANY )
719 return d->bufStdout.size() != 0;
720
721 QProcess *that = (QProcess *) this;
722 return that->membufStdout()->scanNewline( 0 );
723}
724
725bool QProcess::canReadLineStderr() const
726{
727 if( d->pipeStderr == HF_ANY )
728 return d->bufStderr.size() != 0;
729
730 QProcess *that = (QProcess *) this;
731 return that->membufStderr()->scanNewline( 0 );
732}
733
734void QProcess::writeToStdin( const QByteArray& buf )
735{
736 d->stdinBuf.enqueue( new QByteArray(buf) );
737 socketWrite( 0 );
738}
739
740void QProcess::closeStdin( )
741{
742 if ( d->pipeStdin != HF_ANY ) {
743 DosDisConnectNPipe( d->pipeStdin );
744 DosClose( d->pipeStdin );
745 d->pipeStdin = HF_ANY;
746 }
747}
748
749void QProcess::socketRead( int fd )
750{
751 Q_ASSERT( fd == FD_PIPE_STDOUT || fd == FD_PIPE_STDERR );
752
753 HPIPE pipe = HF_ANY;
754 switch( fd ) {
755 case FD_PIPE_STDOUT: pipe = d->pipeStdout; break;
756 case FD_PIPE_STDERR: pipe = d->pipeStderr; break;
757 }
758
759 if ( pipe == HF_ANY )
760 return;
761
762 // get the number of bytes that are waiting to be read
763 ULONG read = 0;
764 AVAILDATA avail = { 0 };
765 ULONG status = 0;
766 APIRET rc = DosPeekNPipe( pipe, NULL, 0, &read, &avail, &status );
767 if ( rc != NO_ERROR ) {
768#if defined(QT_QPROCESS_DEBUG)
769 qSystemWarning( "Failed to peek from the pipe!", rc );
770#endif
771 return;
772 }
773
774 if ( avail.cbpipe > 0 ) {
775 QMembuf *buffer;
776 if ( fd == FD_PIPE_STDOUT )
777 buffer = &d->bufStdout;
778 else
779 buffer = &d->bufStderr;
780
781 QByteArray *ba = new QByteArray( avail.cbpipe );
782
783 rc = DosRead( pipe, ba->data(), avail.cbpipe, &read );
784 if ( rc != NO_ERROR ) {
785 delete ba;
786#if defined(QT_QPROCESS_DEBUG)
787 qSystemWarning( "Failed to read from the pipe!", rc );
788#endif
789 return;
790 }
791
792 if ( read < avail.cbpipe )
793 ba->resize( avail.cbpipe );
794
795 buffer->append( ba );
796 if ( fd == FD_PIPE_STDOUT )
797 emit readyReadStdout();
798 else
799 emit readyReadStderr();
800 }
801}
802
803void QProcess::socketWrite( int )
804{
805 if ( d->pipeStdin == HF_ANY )
806 return;
807
808 while ( !d->stdinBuf.isEmpty() && isRunning() ) {
809 ULONG written = 0;
810 APIRET rc = DosWrite( d->pipeStdin,
811 d->stdinBuf.head()->data() + d->stdinBufRead,
812 QMIN( PIPE_SIZE, d->stdinBuf.head()->size() - d->stdinBufRead ),
813 &written );
814 if ( rc != NO_ERROR ) {
815#if defined(QT_QPROCESS_DEBUG)
816 qSystemWarning( "Failed to write to the pipe!", rc );
817#endif
818 d->lookup->start( 100 );
819 return;
820 }
821
822 d->stdinBufRead += written;
823 if ( d->stdinBufRead == d->stdinBuf.head()->size() ) {
824 d->stdinBufRead = 0;
825 delete d->stdinBuf.dequeue();
826 if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
827 emit wroteToStdin();
828 }
829 }
830}
831
832void QProcess::flushStdin()
833{
834 socketWrite( 0 );
835}
836
837/*
838 Use a timer for polling misc. stuff.
839*/
840void QProcess::timeout()
841{
842 // Disable the timer temporary since one of the slots that are connected to
843 // the readyRead...(), etc. signals might trigger recursion if
844 // processEvents() is called.
845 d->lookup->stop();
846
847 // try to write pending data to stdin
848 if ( !d->stdinBuf.isEmpty() )
849 socketWrite( 0 );
850
851 if ( ioRedirection ) {
852 socketRead( FD_PIPE_STDOUT ); // try stdout
853 socketRead( FD_PIPE_STDERR ); // try stderr
854 }
855
856 if ( isRunning() ) {
857 // enable timer again, if needed
858 if ( !d->stdinBuf.isEmpty() || ioRedirection || notifyOnExit )
859 d->lookup->start( 100 );
860 } else if ( notifyOnExit ) {
861 emit processExited();
862 }
863}
864
865/*
866 Used by connectNotify() and disconnectNotify() to change the value of
867 ioRedirection (and related behaviour)
868*/
869void QProcess::setIoRedirection( bool value )
870{
871 ioRedirection = value;
872 if ( !ioRedirection && !notifyOnExit )
873 d->lookup->stop();
874 if ( ioRedirection ) {
875 if ( isRunning() )
876 d->lookup->start( 100 );
877 }
878}
879
880/*
881 Used by connectNotify() and disconnectNotify() to change the value of
882 notifyOnExit (and related behaviour)
883*/
884void QProcess::setNotifyOnExit( bool value )
885{
886 notifyOnExit = value;
887 if ( !ioRedirection && !notifyOnExit )
888 d->lookup->stop();
889 if ( notifyOnExit ) {
890 if ( isRunning() )
891 d->lookup->start( 100 );
892 }
893}
894
895/*
896 Used by connectNotify() and disconnectNotify() to change the value of
897 wroteToStdinConnected (and related behaviour)
898*/
899void QProcess::setWroteStdinConnected( bool value )
900{
901 wroteToStdinConnected = value;
902}
903
904QProcess::PID QProcess::processIdentifier()
905{
906 return d->pid;
907}
908
909#endif // QT_NO_PROCESS
Note: See TracBrowser for help on using the repository browser.