packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmWin32ProcessExecution.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmWin32ProcessExecution.h"
13
14 #include "cmSystemTools.h"
15
16 #include <malloc.h>
17 #include <io.h>
18 #include <fcntl.h>
19 #include <sys/stat.h>
20 #include <windows.h>
21
22 #if defined(__BORLANDC__)
23 #  define STRICMP stricmp
24 #  define TO_INTPTR(x) ((long)(x))
25 #endif // Borland
26 #if defined(_MSC_VER) // Visual studio
27 #  if ( _MSC_VER >= 1300 )
28 #    include <stddef.h>
29 #    define TO_INTPTR(x) ((intptr_t)(x))
30 #  else // Visual Studio 6
31 #    define TO_INTPTR(x) ((long)(x))
32 #  endif // Visual studio .NET
33 #  define STRICMP _stricmp
34 #endif // Visual Studio
35 #if defined(__MINGW32__)
36 # include <stdint.h>
37 # define TO_INTPTR(x) ((intptr_t)(x))
38 # define STRICMP _stricmp
39 #endif // MinGW
40
41 #define POPEN_1 1
42 #define POPEN_2 2
43 #define POPEN_3 3
44 #define POPEN_4 4
45
46 #define cmMAX(x,y) (((x)<(y))?(y):(x))
47
48 void DisplayErrorMessage()
49 {
50   LPVOID lpMsgBuf;
51   FormatMessage(
52     FORMAT_MESSAGE_ALLOCATE_BUFFER |
53     FORMAT_MESSAGE_FROM_SYSTEM |
54     FORMAT_MESSAGE_IGNORE_INSERTS,
55     NULL,
56     GetLastError(),
57     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
58     (LPTSTR) &lpMsgBuf,
59     0,
60     NULL
61     );
62   // Process any inserts in lpMsgBuf.
63   // ...
64   // Display the string.
65   MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
66   // Free the buffer.
67   LocalFree( lpMsgBuf );
68 }
69
70 // Code from a Borland web site with the following explaination :
71 /* In this article, I will explain how to spawn a console application
72  * and redirect its standard input/output using anonymous pipes. An
73  * anonymous pipe is a pipe that goes only in one direction (read
74  * pipe, write pipe, etc.). Maybe you are asking, "why would I ever
75  * need to do this sort of thing?" One example would be a Windows
76  * telnet server, where you spawn a shell and listen on a port and
77  * send and receive data between the shell and the socket
78  * client. (Windows does not really have a built-in remote
79  * shell). First, we should talk about pipes. A pipe in Windows is
80  * simply a method of communication, often between process. The SDK
81  * defines a pipe as "a communication conduit with two ends;
82  a process
83  * with a handle to one end can communicate with a process having a
84  * handle to the other end." In our case, we are using "anonymous"
85  * pipes, one-way pipes that "transfer data between a parent process
86  * and a child process or between two child processes of the same
87  * parent process." It's easiest to imagine a pipe as its namesake. An
88  * actual pipe running between processes that can carry data. We are
89  * using anonymous pipes because the console app we are spawning is a
90  * child process. We use the CreatePipe function which will create an
91  * anonymous pipe and return a read handle and a write handle. We will
92  * create two pipes, on for stdin and one for stdout. We will then
93  * monitor the read end of the stdout pipe to check for display on our
94  * child process. Every time there is something availabe for reading,
95  * we will display it in our app. Consequently, we check for input in
96  * our app and send it off to the write end of the stdin pipe. */
97
98 inline bool IsWinNT()
99 //check if we're running NT
100 {
101   OSVERSIONINFO osv;
102   osv.dwOSVersionInfoSize = sizeof(osv);
103   GetVersionEx(&osv);
104   return (osv.dwPlatformId == VER_PLATFORM_WIN32_NT);
105 }
106
107 //---------------------------------------------------------------------------
108 bool cmWin32ProcessExecution::BorlandRunCommand(
109   const char* command, const char* dir,
110   std::string& output, int& retVal, bool verbose, int /* timeout */,
111   bool hideWindows)
112 {
113   //verbose = true;
114   //std::cerr << std::endl
115   //        << "WindowsRunCommand(" << command << ")" << std::endl
116   //        << std::flush;
117   const int BUFFER_SIZE = 4096;
118   char buf[BUFFER_SIZE];
119
120 //i/o buffer
121   STARTUPINFO si;
122   SECURITY_ATTRIBUTES sa;
123   SECURITY_DESCRIPTOR sd;
124
125 //security information for pipes
126   PROCESS_INFORMATION pi;
127   HANDLE newstdin,newstdout,read_stdout,write_stdin;
128
129 //pipe handles
130   if (IsWinNT())
131 //initialize security descriptor (Windows NT)
132     {
133     InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
134     SetSecurityDescriptorDacl(&sd, true, NULL, false);
135     sa.lpSecurityDescriptor = &sd;
136
137     }
138   else sa.lpSecurityDescriptor = NULL;
139   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
140   sa.bInheritHandle = true;
141
142 //allow inheritable handles
143   if (!CreatePipe(&newstdin,&write_stdin,&sa,0))
144 //create stdin pipe
145     {
146     return false;
147     }
148   if (!CreatePipe(&read_stdout,&newstdout,&sa,0))
149 //create stdout pipe
150     {
151     CloseHandle(newstdin);
152     CloseHandle(write_stdin);
153     return false;
154
155     }
156   GetStartupInfo(&si);
157
158 //set startupinfo for the spawned process
159   /* The dwFlags member tells CreateProcess how to make the
160    * process. STARTF_USESTDHANDLES validates the hStd*
161    * members. STARTF_USESHOWWINDOW validates the wShowWindow
162    * member. */
163
164   si.cb = sizeof(STARTUPINFO);
165   si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
166   si.hStdOutput = newstdout;
167   si.hStdError = newstdout;
168   si.wShowWindow = SW_SHOWDEFAULT;
169   if(hideWindows)
170     {
171     si.wShowWindow = SW_HIDE;
172     }
173
174 //set the new handles for the child process si.hStdInput = newstdin;
175   char* commandAndArgs = strcpy(new char[strlen(command)+1], command);
176   if (!CreateProcess(NULL,commandAndArgs,NULL,NULL,TRUE,
177                      0, // CREATE_NEW_CONSOLE,
178                      NULL,dir,&si,&pi))
179     {
180     std::cerr << "CreateProcess failed " << commandAndArgs << std::endl;
181     CloseHandle(newstdin);
182     CloseHandle(newstdout);
183     CloseHandle(read_stdout);
184     CloseHandle(write_stdin);
185     delete [] commandAndArgs;
186     return false;
187
188     }
189   delete [] commandAndArgs;
190   unsigned long exit=0;
191
192 //process exit code unsigned
193   unsigned long bread;
194
195 //bytes read unsigned
196   unsigned long avail;
197
198 //bytes available
199   memset(buf, 0, sizeof(buf));
200   for(;;)
201 //main program loop
202     {
203     Sleep(10);
204 //check to see if there is any data to read from stdout
205     //std::cout << "Peek for data..." << std::endl;
206     PeekNamedPipe(read_stdout,buf,1023,&bread,&avail,NULL);
207     if (bread != 0)
208       {
209       memset(buf, 0, sizeof(buf));
210       if (avail > 1023)
211         {
212         while (bread >= 1023)
213           {
214           //std::cout << "Read data..." << std::endl;
215           ReadFile(read_stdout,buf,1023,&bread,NULL);
216
217           //read the stdout pipe
218           memset(buf, 0, sizeof(buf));
219           output += buf;
220           if (verbose)
221             {
222             cmSystemTools::Stdout(buf);
223             }
224           }
225         }
226       else
227         {
228         ReadFile(read_stdout,buf,1023,&bread,NULL);
229         output += buf;
230         if(verbose)
231           {
232           cmSystemTools::Stdout(buf);
233           }
234
235         }
236
237       }
238
239     //std::cout << "Check for process..." << std::endl;
240     GetExitCodeProcess(pi.hProcess,&exit);
241
242 //while the process is running
243     if (exit != STILL_ACTIVE) break;
244
245     }
246   WaitForSingleObject(pi.hProcess, INFINITE);
247   GetExitCodeProcess(pi.hProcess,&exit);
248   CloseHandle(pi.hThread);
249   CloseHandle(pi.hProcess);
250   CloseHandle(newstdin);
251
252 //clean stuff up
253   CloseHandle(newstdout);
254   CloseHandle(read_stdout);
255   CloseHandle(write_stdin);
256   retVal = exit;
257   return true;
258
259 }
260
261 bool cmWin32ProcessExecution::StartProcess(
262   const char* cmd, const char* path, bool verbose)
263 {
264   this->Initialize();
265   this->Verbose = verbose;
266   return this->PrivateOpen(cmd, path, _O_RDONLY | _O_TEXT, POPEN_3);
267 }
268
269 bool cmWin32ProcessExecution::Wait(int timeout)
270 {
271   return this->PrivateClose(timeout);
272 }
273
274 static BOOL RealPopenCreateProcess(const char *cmdstring,
275                                    const char *path,
276                                    const char *szConsoleSpawn,
277                                    HANDLE hStdin,
278                                    HANDLE hStdout,
279                                    HANDLE hStderr,
280                                    HANDLE *hProcess,
281                                    bool hideWindows,
282                                    std::string& output)
283 {
284   PROCESS_INFORMATION piProcInfo;
285   STARTUPINFO siStartInfo;
286   char *s1=0,*s2=0;
287   const char *s3 = " /c ";
288   int i = GetEnvironmentVariable("COMSPEC",NULL,0);
289   if (i)
290     {
291     char *comshell;
292
293     s1 = (char *)malloc(i);
294     int x = GetEnvironmentVariable("COMSPEC", s1, i);
295     if (!x)
296       {
297       free(s1);
298       return x;
299       }
300
301     /* Explicitly check if we are using COMMAND.COM.  If we are
302      * then use the w9xpopen hack.
303      */
304     comshell = s1 + x;
305     while (comshell >= s1 && *comshell != '\\')
306       --comshell;
307     ++comshell;
308
309     if (GetVersion() < 0x80000000 &&
310         STRICMP(comshell, "command.com") != 0)
311       {
312       /* NT/2000 and not using command.com. */
313       x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1;
314       s2 = (char *)malloc(x);
315       ZeroMemory(s2, x);
316       //sprintf(s2, "%s%s%s", s1, s3, cmdstring);
317       sprintf(s2, "%s", cmdstring);
318       }
319     else
320       {
321       /*
322        * Oh gag, we're on Win9x or using COMMAND.COM. Use
323        * the workaround listed in KB: Q150956
324        */
325       char modulepath[_MAX_PATH];
326       struct stat statinfo;
327       GetModuleFileName(NULL, modulepath, sizeof(modulepath));
328       for (i = x = 0; modulepath[i]; i++)
329         if (modulepath[i] == '\\')
330           x = i+1;
331       modulepath[x] = '\0';
332       /* Create the full-name to w9xpopen, so we can test it exists */
333       strncat(modulepath,
334               szConsoleSpawn,
335               (sizeof(modulepath)/sizeof(modulepath[0]))
336               -strlen(modulepath));
337       if (stat(modulepath, &statinfo) != 0)
338         {
339           /* Eeek - file-not-found - possibly an embedding
340              situation - see if we can locate it in sys.prefix
341           */
342         strncpy(modulepath,
343                 ".",
344                 sizeof(modulepath)/sizeof(modulepath[0]));
345         if (modulepath[strlen(modulepath)-1] != '\\')
346           strcat(modulepath, "\\");
347         strncat(modulepath,
348                 szConsoleSpawn,
349                 (sizeof(modulepath)/sizeof(modulepath[0]))
350                 -strlen(modulepath));
351         /* No where else to look - raise an easily identifiable
352            error, rather than leaving Windows to report
353            "file not found" - as the user is probably blissfully
354            unaware this shim EXE is used, and it will confuse them.
355            (well, it confused me for a while ;-)
356         */
357         if (stat(modulepath, &statinfo) != 0)
358           {
359           std::cout
360             << "Can not locate '" << modulepath
361             << "' which is needed "
362             "for popen to work with your shell "
363             "or platform." << std::endl;
364           free(s1);
365           free(s2);
366           return FALSE;
367           }
368         }
369       x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1 +
370         (int)strlen(modulepath) +
371         (int)strlen(szConsoleSpawn) + 1;
372       if(s2)
373         {
374         free(s2);
375         }
376       s2 = (char *)malloc(x);
377       ZeroMemory(s2, x);
378       sprintf(
379         s2,
380         "%s %s%s%s",
381         modulepath,
382         s1,
383         s3,
384         cmdstring);
385       sprintf(
386         s2,
387         "%s %s",
388         modulepath,
389         cmdstring);
390       }
391     }
392
393   /* Could be an else here to try cmd.exe / command.com in the path
394      Now we'll just error out.. */
395   else
396     {
397     std::cout << "Cannot locate a COMSPEC environment variable to "
398               << "use as the shell" << std::endl;
399     free(s2);
400     free(s1);
401     return FALSE;
402     }
403
404   ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
405   siStartInfo.cb = sizeof(STARTUPINFO);
406   siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
407   siStartInfo.hStdInput = hStdin;
408   siStartInfo.hStdOutput = hStdout;
409   siStartInfo.hStdError = hStderr;
410   siStartInfo.wShowWindow = SW_SHOWDEFAULT;
411   if(hideWindows)
412     {
413     siStartInfo.wShowWindow = SW_HIDE;
414     }
415
416   //std::cout << "Create process: " << s2 << std::endl;
417   if (CreateProcess(NULL,
418                     s2,
419                     NULL,
420                     NULL,
421                     TRUE,
422                     0, //CREATE_NEW_CONSOLE,
423                     NULL,
424                     path,
425                     &siStartInfo,
426                     &piProcInfo) )
427     {
428     /* Close the handles now so anyone waiting is woken. */
429     CloseHandle(piProcInfo.hThread);
430     /* Return process handle */
431     *hProcess = piProcInfo.hProcess;
432     //std::cout << "Process created..." << std::endl;
433     free(s2);
434     free(s1);
435     return TRUE;
436     }
437
438   output += "CreateProcessError: ";
439   {
440   /* Format the error message.  */
441   char message[1024];
442   DWORD original = GetLastError();
443   DWORD length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
444                                FORMAT_MESSAGE_IGNORE_INSERTS, 0, original,
445                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
446                                message, 1023, 0);
447   if(length < 1)
448     {
449     /* FormatMessage failed.  Use a default message.  */
450     _snprintf(message, 1023,
451               "Process execution failed with error 0x%X.  "
452               "FormatMessage failed with error 0x%X",
453               original, GetLastError());
454     }
455   output += message;
456   }
457   output += "\n";
458   output += "for command: ";
459   output += s2;
460   if(path)
461     {
462     output += "\nin dir: ";
463     output += path;
464     }
465   output += "\n";
466   free(s2);
467   free(s1);
468   return FALSE;
469 }
470
471 /* The following code is based off of KB: Q190351 */
472
473 bool cmWin32ProcessExecution::PrivateOpen(const char *cmdstring,
474                                           const char* path,
475                                           int mode,
476                                           int n)
477 {
478   HANDLE hProcess;
479
480   SECURITY_ATTRIBUTES saAttr;
481   BOOL fSuccess;
482   int fd1, fd2, fd3;
483   this->hChildStdinRd = 0;
484   this->hChildStdinWr = 0;
485   this->hChildStdoutRd = 0;
486   this->hChildStdoutWr = 0;
487   this->hChildStderrRd = 0;
488   this->hChildStderrWr = 0;
489   this->hChildStdinWrDup = 0;
490   this->hChildStdoutRdDup = 0;
491   this->hChildStderrRdDup = 0;
492
493   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
494   saAttr.bInheritHandle = TRUE;
495   saAttr.lpSecurityDescriptor = NULL;
496
497   fd1 = 0;
498   fd2 = 0;
499   fd3 = 0;
500
501   if (!CreatePipe(&this->hChildStdinRd, &this->hChildStdinWr, &saAttr, 0))
502     {
503     this->Output += "CreatePipeError\n";
504     return false;
505     }
506
507   /* Create new output read handle and the input write handle. Set
508    * the inheritance properties to FALSE. Otherwise, the child inherits
509    * these handles; resulting in non-closeable handles to the pipes
510    * being created. */
511   fSuccess = DuplicateHandle(GetCurrentProcess(), this->hChildStdinWr,
512                              GetCurrentProcess(), &this->hChildStdinWrDup, 0,
513                              FALSE,
514                              DUPLICATE_SAME_ACCESS);
515   if (!fSuccess)
516     {
517     this->Output += "DuplicateHandleError\n";
518     return false;
519     }
520
521
522   /* Close the inheritable version of ChildStdin
523      that we're using. */
524   CloseHandle(hChildStdinWr);
525
526   if (!CreatePipe(&this->hChildStdoutRd, &this->hChildStdoutWr, &saAttr, 0))
527     {
528     this->Output += "CreatePipeError\n";
529     return false;
530     }
531
532   fSuccess = DuplicateHandle(GetCurrentProcess(), this->hChildStdoutRd,
533                              GetCurrentProcess(), &this->hChildStdoutRdDup, 0,
534                              FALSE, DUPLICATE_SAME_ACCESS);
535   if (!fSuccess)
536     {
537     this->Output += "DuplicateHandleError\n";
538     return false;
539     }
540
541   /* Close the inheritable version of ChildStdout
542      that we're using. */
543   CloseHandle(hChildStdoutRd);
544
545   if (n != POPEN_4)
546     {
547     if (!CreatePipe(&this->hChildStderrRd, &this->hChildStderrWr, &saAttr, 0))
548       {
549       this->Output += "CreatePipeError\n";
550       return false;
551       }
552    fSuccess = DuplicateHandle(GetCurrentProcess(),
553                               this->hChildStderrRd,
554                               GetCurrentProcess(),
555                               &this->hChildStderrRdDup, 0,
556                               FALSE, DUPLICATE_SAME_ACCESS);
557     if (!fSuccess)
558       {
559       this->Output += "DuplicateHandleError\n";
560       return false;
561       }
562     /* Close the inheritable version of ChildStdErr that we're using. */
563     CloseHandle(hChildStderrRd);
564
565     }
566
567   switch (n)
568     {
569     case POPEN_1:
570       switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY))
571         {
572         case _O_WRONLY | _O_TEXT:
573           /* Case for writing to child Stdin in text mode. */
574           fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
575           /* We don't care about these pipes anymore,
576              so close them. */
577           break;
578
579         case _O_RDONLY | _O_TEXT:
580           /* Case for reading from child Stdout in text mode. */
581           fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
582           /* We don't care about these pipes anymore,
583              so close them. */
584           break;
585
586         case _O_RDONLY | _O_BINARY:
587           /* Case for readinig from child Stdout in
588              binary mode. */
589           fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
590           /* We don't care about these pipes anymore,
591              so close them. */
592           break;
593
594         case _O_WRONLY | _O_BINARY:
595           /* Case for writing to child Stdin in binary mode. */
596           fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
597           /* We don't care about these pipes anymore,
598              so close them. */
599           break;
600         }
601       break;
602
603     case POPEN_2:
604     case POPEN_4:
605       //if ( 1 )
606         {
607         fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
608         fd2 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
609         break;
610         }
611
612     case POPEN_3:
613       //if ( 1)
614         {
615         fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
616         fd2 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
617         fd3 = _open_osfhandle(TO_INTPTR(this->hChildStderrRdDup), mode);
618         break;
619         }
620     }
621
622   if (n == POPEN_4)
623     {
624     if (!RealPopenCreateProcess(cmdstring,
625                                 path,
626                                 this->ConsoleSpawn.c_str(),
627                                 this->hChildStdinRd,
628                                 this->hChildStdoutWr,
629                                 this->hChildStdoutWr,
630                                 &hProcess, this->HideWindows,
631                                 this->Output))
632       {
633       if(fd1 >= 0)
634         {
635         close(fd1);
636         }
637       if(fd2 >= 0)
638         {
639         close(fd2);
640         }
641       if(fd3 >= 0)
642         {
643         close(fd3);
644         }
645       return 0;
646       }
647     }
648   else
649     {
650     if (!RealPopenCreateProcess(cmdstring,
651                                 path,
652                                 this->ConsoleSpawn.c_str(),
653                                 this->hChildStdinRd,
654                                 this->hChildStdoutWr,
655                                 this->hChildStderrWr,
656                                 &hProcess, this->HideWindows,
657                                 this->Output))
658       {
659       if(fd1 >= 0)
660         {
661         close(fd1);
662         }
663       if(fd2 >= 0)
664         {
665         close(fd2);
666         }
667       if(fd3 >= 0)
668         {
669         close(fd3);
670         }
671       return 0;
672       }
673     }
674
675   /* Child is launched. Close the parents copy of those pipe
676    * handles that only the child should have open.  You need to
677    * make sure that no handles to the write end of the output pipe
678    * are maintained in this process or else the pipe will not close
679    * when the child process exits and the ReadFile will hang. */
680   this->ProcessHandle = hProcess;
681   if ( fd1 >= 0 )
682     {
683     this->pStdIn = fd1;
684     }
685   if ( fd2 >= 0 )
686     {
687     this->pStdOut = fd2;
688     }
689   if ( fd3 >= 0 )
690     {
691     this->pStdErr = fd3;
692     }
693
694   return true;
695 }
696
697 bool cmWin32ProcessExecution::CloseHandles()
698 {
699   if(this->pStdErr != -1 )
700     {
701     // this will close this as well: this->hChildStderrRdDup
702     _close(this->pStdErr);
703     this->pStdErr = -1;
704     this->hChildStderrRdDup = 0;
705     }
706   if(this->pStdIn != -1 )
707     {
708     // this will close this as well: this->hChildStdinWrDup
709     _close(this->pStdIn);
710     this->pStdIn = -1;
711     this->hChildStdinWrDup = 0;
712     }
713   if(this->pStdOut != -1 )
714     {
715     // this will close this as well: this->hChildStdoutRdDup
716     _close(this->pStdOut);
717     this->pStdOut = -1;
718     this->hChildStdoutRdDup = 0;
719     }
720
721   bool ret = true;
722   if (this->hChildStdinRd && !CloseHandle(this->hChildStdinRd))
723     {
724     ret = false;
725     }
726   this->hChildStdinRd = 0;
727   // now close these two
728   if (this->hChildStdoutWr && !CloseHandle(this->hChildStdoutWr))
729     {
730     ret = false;
731     }
732   this->hChildStdoutWr = 0;
733   if (this->hChildStderrWr && !CloseHandle(this->hChildStderrWr))
734     {
735     ret = false;
736     }
737   this->hChildStderrWr = 0;
738   return ret;
739 }
740 cmWin32ProcessExecution::~cmWin32ProcessExecution()
741 {
742   this->CloseHandles();
743 }
744
745 bool cmWin32ProcessExecution::PrivateClose(int /* timeout */)
746 {
747   HANDLE hProcess = this->ProcessHandle;
748
749   int result = -1;
750   DWORD exit_code;
751
752   std::string output = "";
753   bool done = false;
754   while(!done)
755     {
756     Sleep(10);
757     bool have_some = false;
758     struct _stat fsout;
759     struct _stat fserr;
760     int rout = _fstat(this->pStdOut, &fsout);
761     int rerr = _fstat(this->pStdErr, &fserr);
762     if ( rout && rerr )
763       {
764       break;
765       }
766     if (fserr.st_size > 0)
767       {
768       char buffer[1024];
769       int len = read(this->pStdErr, buffer, 1023);
770       buffer[len] = 0;
771       if ( this->Verbose )
772         {
773         cmSystemTools::Stdout(buffer);
774         }
775       output += buffer;
776       have_some = true;
777       }
778     if (fsout.st_size > 0)
779       {
780       char buffer[1024];
781       int len = read(this->pStdOut, buffer, 1023);
782       buffer[len] = 0;
783       if ( this->Verbose )
784         {
785         cmSystemTools::Stdout(buffer);
786         }
787       output += buffer;
788       have_some = true;
789       }
790     unsigned long exitCode;
791     if ( ! have_some )
792       {
793       GetExitCodeProcess(hProcess,&exitCode);
794       if (exitCode != STILL_ACTIVE)
795         {
796         break;
797         }
798       }
799     }
800
801
802   if (WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED &&
803       GetExitCodeProcess(hProcess, &exit_code))
804     {
805     result = exit_code;
806     }
807   else
808     {
809     /* Indicate failure - this will cause the file object
810      * to raise an I/O error and translate the last Win32
811      * error code from errno.  We do have a problem with
812      * last errors that overlap the normal errno table,
813      * but that's a consistent problem with the file object.
814      */
815     if (result != EOF)
816       {
817       /* If the error wasn't from the fclose(), then
818        * set errno for the file object error handling.
819        */
820       errno = GetLastError();
821       }
822     result = -1;
823     }
824
825   /* Free up the native handle at this point */
826   CloseHandle(hProcess);
827   this->ExitValue = result;
828   this->Output += output;
829   bool ret = this->CloseHandles();
830   if ( result < 0 || !ret)
831     {
832     return false;
833     }
834   return true;
835 }
836
837 int cmWin32ProcessExecution::Windows9xHack(const char* command)
838 {
839   BOOL bRet;
840   STARTUPINFO si;
841   PROCESS_INFORMATION pi;
842   DWORD exit_code=0;
843
844   if (!command)
845     {
846     cmSystemTools::Error("Windows9xHack: Command not specified");
847     return 1;
848   }
849
850   /* Make child process use this app's standard files. */
851   ZeroMemory(&si, sizeof si);
852   si.cb = sizeof si;
853   si.dwFlags = STARTF_USESTDHANDLES;
854   si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
855   si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
856   si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
857
858
859   char * app = 0;
860   char* cmd = new char[ strlen(command) + 1 ];
861   strcpy(cmd, command);
862
863   bRet = CreateProcess(
864     app, cmd,
865     0, 0,
866     TRUE, 0,
867     0, 0,
868     &si, &pi
869     );
870   delete [] cmd;
871
872   if (bRet)
873     {
874     if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED)
875       {
876       GetExitCodeProcess(pi.hProcess, &exit_code);
877       }
878     CloseHandle(pi.hProcess);
879     CloseHandle(pi.hThread);
880     return exit_code;
881     }
882
883   return 1;
884 }