wined3d: Prevent some console spamming.
[wine] / dlls / msvcrt / process.c
1 /*
2  * msvcrt.dll spawn/exec functions
3  *
4  * Copyright 1996,1998 Marcus Meissner
5  * Copyright 1996 Jukka Iivonen
6  * Copyright 1997,2000 Uwe Bonnes
7  * Copyright 2000 Jon Griffiths
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  *
23  * FIXME:
24  * -File handles need some special handling. Sometimes children get
25  *  open file handles, sometimes not. The docs are confusing
26  * -No check for maximum path/argument/environment size is done
27  */
28 #include "config.h"
29
30 #include <stdarg.h>
31
32 #include "msvcrt.h"
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
37
38 /* INTERNAL: Spawn a child process */
39 static MSVCRT_intptr_t msvcrt_spawn(int flags, const char* exe, char* cmdline, char* env)
40 {
41   STARTUPINFOA si;
42   PROCESS_INFORMATION pi;
43
44   if ((unsigned)flags > MSVCRT__P_DETACH)
45   {
46     *MSVCRT__errno() = MSVCRT_EINVAL;
47     return -1;
48   }
49
50   memset(&si, 0, sizeof(si));
51   si.cb = sizeof(si);
52   msvcrt_create_io_inherit_block(&si.cbReserved2, &si.lpReserved2);
53   if (!CreateProcessA(exe, cmdline, NULL, NULL, TRUE,
54                      flags == MSVCRT__P_DETACH ? DETACHED_PROCESS : 0,
55                      env, NULL, &si, &pi))
56   {
57     msvcrt_set_errno(GetLastError());
58     MSVCRT_free(si.lpReserved2);
59     return -1;
60   }
61
62   MSVCRT_free(si.lpReserved2);
63   switch(flags)
64   {
65   case MSVCRT__P_WAIT:
66     WaitForSingleObject(pi.hProcess, INFINITE);
67     GetExitCodeProcess(pi.hProcess,&pi.dwProcessId);
68     CloseHandle(pi.hProcess);
69     CloseHandle(pi.hThread);
70     return pi.dwProcessId;
71   case MSVCRT__P_DETACH:
72     CloseHandle(pi.hProcess);
73     pi.hProcess = 0;
74     /* fall through */
75   case MSVCRT__P_NOWAIT:
76   case MSVCRT__P_NOWAITO:
77     CloseHandle(pi.hThread);
78     return (MSVCRT_intptr_t)pi.hProcess;
79   case  MSVCRT__P_OVERLAY:
80     MSVCRT__exit(0);
81   }
82   return -1; /* can't reach here */
83 }
84
85 static MSVCRT_intptr_t msvcrt_spawn_wide(int flags, const MSVCRT_wchar_t* exe, MSVCRT_wchar_t* cmdline, MSVCRT_wchar_t* env)
86 {
87   STARTUPINFOW si;
88   PROCESS_INFORMATION pi;
89
90   if ((unsigned)flags > MSVCRT__P_DETACH)
91   {
92     *MSVCRT__errno() = MSVCRT_EINVAL;
93     return -1;
94   }
95
96   memset(&si, 0, sizeof(si));
97   si.cb = sizeof(si);
98   msvcrt_create_io_inherit_block(&si.cbReserved2, &si.lpReserved2);
99   if (!CreateProcessW(exe, cmdline, NULL, NULL, TRUE,
100                      flags == MSVCRT__P_DETACH ? DETACHED_PROCESS : 0,
101                      env, NULL, &si, &pi))
102   {
103     msvcrt_set_errno(GetLastError());
104     MSVCRT_free(si.lpReserved2);
105     return -1;
106   }
107
108   MSVCRT_free(si.lpReserved2);
109   switch(flags)
110   {
111   case MSVCRT__P_WAIT:
112     WaitForSingleObject(pi.hProcess, INFINITE);
113     GetExitCodeProcess(pi.hProcess,&pi.dwProcessId);
114     CloseHandle(pi.hProcess);
115     CloseHandle(pi.hThread);
116     return pi.dwProcessId;
117   case MSVCRT__P_DETACH:
118     CloseHandle(pi.hProcess);
119     pi.hProcess = 0;
120     /* fall through */
121   case MSVCRT__P_NOWAIT:
122   case MSVCRT__P_NOWAITO:
123     CloseHandle(pi.hThread);
124     return (MSVCRT_intptr_t)pi.hProcess;
125   case  MSVCRT__P_OVERLAY:
126     MSVCRT__exit(0);
127   }
128   return -1; /* can't reach here */
129 }
130
131 /* INTERNAL: Convert argv list to a single 'delim'-separated string, with an
132  * extra '\0' to terminate it
133  */
134 static char* msvcrt_argvtos(const char* const* arg, char delim)
135 {
136   const char* const* a;
137   long size;
138   char* p;
139   char* ret;
140
141   if (!arg && !delim)
142   {
143       /* Return NULL for an empty environment list */
144       return NULL;
145   }
146
147   /* get length */
148   a = arg;
149   size = 0;
150   while (*a)
151   {
152     size += strlen(*a) + 1;
153     a++;
154   }
155
156   ret = MSVCRT_malloc(size + 1);
157   if (!ret)
158     return NULL;
159
160   /* fill string */
161   a = arg;
162   p = ret;
163   while (*a)
164   {
165     int len = strlen(*a);
166     memcpy(p,*a,len);
167     p += len;
168     *p++ = delim;
169     a++;
170   }
171   if (delim && p > ret) p[-1] = 0;
172   else *p = 0;
173   return ret;
174 }
175
176 static MSVCRT_wchar_t* msvcrt_argvtos_wide(const MSVCRT_wchar_t* const* arg, MSVCRT_wchar_t delim)
177 {
178   const MSVCRT_wchar_t* const* a;
179   long size;
180   MSVCRT_wchar_t* p;
181   MSVCRT_wchar_t* ret;
182
183   if (!arg && !delim)
184   {
185       /* Return NULL for an empty environment list */
186       return NULL;
187   }
188
189   /* get length */
190   a = arg;
191   size = 0;
192   while (*a)
193   {
194     size += strlenW(*a) + 1;
195     a++;
196   }
197
198   ret = MSVCRT_malloc((size + 1) * sizeof(MSVCRT_wchar_t));
199   if (!ret)
200     return NULL;
201
202   /* fill string */
203   a = arg;
204   p = ret;
205   while (*a)
206   {
207     int len = strlenW(*a);
208     memcpy(p,*a,len * sizeof(MSVCRT_wchar_t));
209     p += len;
210     *p++ = delim;
211     a++;
212   }
213   if (delim && p > ret) p[-1] = 0;
214   else *p = 0;
215   return ret;
216 }
217
218 /* INTERNAL: Convert va_list to a single 'delim'-separated string, with an
219  * extra '\0' to terminate it
220  */
221 static char* msvcrt_valisttos(const char* arg0, va_list alist, char delim)
222 {
223   va_list alist2;
224   long size;
225   const char *arg;
226   char* p;
227   char *ret;
228
229 #ifdef HAVE_VA_COPY
230   va_copy(alist2,alist);
231 #else
232 # ifdef HAVE___VA_COPY
233   __va_copy(alist2,alist);
234 # else
235   alist2 = alist;
236 # endif
237 #endif
238
239   if (!arg0)
240   {
241       /* Return NULL for an empty environment list */
242       return NULL;
243   }
244
245   /* get length */
246   arg = arg0;
247   size = 0;
248   do {
249       size += strlen(arg) + 1;
250       arg = va_arg(alist, char*);
251   } while (arg != NULL);
252
253   ret = MSVCRT_malloc(size + 1);
254   if (!ret)
255     return NULL;
256
257   /* fill string */
258   arg = arg0;
259   p = ret;
260   do {
261       int len = strlen(arg);
262       memcpy(p,arg,len);
263       p += len;
264       *p++ = delim;
265       arg = va_arg(alist2, char*);
266   } while (arg != NULL);
267   if (delim && p > ret) p[-1] = 0;
268   else *p = 0;
269   return ret;
270 }
271
272 /*********************************************************************
273  *              _cwait (MSVCRT.@)
274  */
275 MSVCRT_intptr_t CDECL _cwait(int *status, MSVCRT_intptr_t pid, int action)
276 {
277   HANDLE hPid = (HANDLE)pid;
278   int doserrno;
279
280   action = action; /* Remove warning */
281
282   if (!WaitForSingleObject(hPid, INFINITE))
283   {
284     if (status)
285     {
286       DWORD stat;
287       GetExitCodeProcess(hPid, &stat);
288       *status = (int)stat;
289     }
290     return pid;
291   }
292   doserrno = GetLastError();
293
294   if (doserrno == ERROR_INVALID_HANDLE)
295   {
296     *MSVCRT__errno() =  MSVCRT_ECHILD;
297     *MSVCRT___doserrno() = doserrno;
298   }
299   else
300     msvcrt_set_errno(doserrno);
301
302   return status ? *status = -1 : -1;
303 }
304
305 /*********************************************************************
306  *              _execl (MSVCRT.@)
307  *
308  * Like on Windows, this function does not handle arguments with spaces
309  * or double-quotes.
310  */
311 MSVCRT_intptr_t CDECL _execl(const char* name, const char* arg0, ...)
312 {
313   va_list ap;
314   char * args;
315   MSVCRT_intptr_t ret;
316
317   va_start(ap, arg0);
318   args = msvcrt_valisttos(arg0, ap, ' ');
319   va_end(ap);
320
321   ret = msvcrt_spawn(MSVCRT__P_OVERLAY, name, args, NULL);
322   MSVCRT_free(args);
323
324   return ret;
325 }
326
327 /*********************************************************************
328  *              _execle (MSVCRT.@)
329  */
330 MSVCRT_intptr_t CDECL _execle(const char* name, const char* arg0, ...)
331 {
332     FIXME("stub\n");
333     return -1;
334 }
335
336 /*********************************************************************
337  *              _execlp (MSVCRT.@)
338  *
339  * Like on Windows, this function does not handle arguments with spaces
340  * or double-quotes.
341  */
342 MSVCRT_intptr_t CDECL _execlp(const char* name, const char* arg0, ...)
343 {
344   va_list ap;
345   char * args;
346   MSVCRT_intptr_t ret;
347   char fullname[MAX_PATH];
348
349   _searchenv(name, "PATH", fullname);
350
351   va_start(ap, arg0);
352   args = msvcrt_valisttos(arg0, ap, ' ');
353   va_end(ap);
354
355   ret = msvcrt_spawn(MSVCRT__P_OVERLAY, fullname[0] ? fullname : name, args, NULL);
356   MSVCRT_free(args);
357
358   return ret;
359 }
360
361 /*********************************************************************
362  *              _execlpe (MSVCRT.@)
363  */
364 MSVCRT_intptr_t CDECL _execlpe(const char* name, const char* arg0, ...)
365 {
366     FIXME("stub\n");
367     return -1;
368 }
369
370 /*********************************************************************
371  *              _execv (MSVCRT.@)
372  *
373  * Like on Windows, this function does not handle arguments with spaces
374  * or double-quotes.
375  */
376 MSVCRT_intptr_t CDECL _execv(const char* name, char* const* argv)
377 {
378   return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, NULL);
379 }
380
381 /*********************************************************************
382  *              _execve (MSVCRT.@)
383  *
384  * Like on Windows, this function does not handle arguments with spaces
385  * or double-quotes.
386  */
387 MSVCRT_intptr_t CDECL MSVCRT__execve(const char* name, char* const* argv, const char* const* envv)
388 {
389   return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, envv);
390 }
391
392 /*********************************************************************
393  *              _execvpe (MSVCRT.@)
394  *
395  * Like on Windows, this function does not handle arguments with spaces
396  * or double-quotes.
397  */
398 MSVCRT_intptr_t CDECL _execvpe(const char* name, char* const* argv, const char* const* envv)
399 {
400   char fullname[MAX_PATH];
401
402   _searchenv(name, "PATH", fullname);
403   return _spawnve(MSVCRT__P_OVERLAY, fullname[0] ? fullname : name,
404                   (const char* const*) argv, envv);
405 }
406
407 /*********************************************************************
408  *              _execvp (MSVCRT.@)
409  *
410  * Like on Windows, this function does not handle arguments with spaces
411  * or double-quotes.
412  */
413 MSVCRT_intptr_t CDECL _execvp(const char* name, char* const* argv)
414 {
415   return _execvpe(name, argv, NULL);
416 }
417
418 /*********************************************************************
419  *              _spawnl (MSVCRT.@)
420  *
421  * Like on Windows, this function does not handle arguments with spaces
422  * or double-quotes.
423  */
424 MSVCRT_intptr_t CDECL _spawnl(int flags, const char* name, const char* arg0, ...)
425 {
426   va_list ap;
427   char * args;
428   MSVCRT_intptr_t ret;
429
430   va_start(ap, arg0);
431   args = msvcrt_valisttos(arg0, ap, ' ');
432   va_end(ap);
433
434   ret = msvcrt_spawn(flags, name, args, NULL);
435   MSVCRT_free(args);
436
437   return ret;
438 }
439
440 /*********************************************************************
441  *              _spawnle (MSVCRT.@)
442  */
443 MSVCRT_intptr_t CDECL _spawnle(int flags, const char* name, const char* arg0, ...)
444 {
445     va_list ap;
446     char *args, *envs = NULL;
447     const char * const *envp;
448     MSVCRT_intptr_t ret;
449
450     va_start(ap, arg0);
451     args = msvcrt_valisttos(arg0, ap, ' ');
452     va_end(ap);
453
454     va_start(ap, arg0);
455     while (va_arg( ap, char * ) != NULL) /*nothing*/;
456     envp = va_arg( ap, const char * const * );
457     if (envp) envs = msvcrt_argvtos(envp, 0);
458     va_end(ap);
459
460     ret = msvcrt_spawn(flags, name, args, envs);
461
462     MSVCRT_free(args);
463     MSVCRT_free(envs);
464     return ret;
465 }
466
467
468 /*********************************************************************
469  *              _spawnlp (MSVCRT.@)
470  *
471  * Like on Windows, this function does not handle arguments with spaces
472  * or double-quotes.
473  */
474 MSVCRT_intptr_t CDECL _spawnlp(int flags, const char* name, const char* arg0, ...)
475 {
476   va_list ap;
477   char * args;
478   MSVCRT_intptr_t ret;
479   char fullname[MAX_PATH];
480
481   _searchenv(name, "PATH", fullname);
482
483   va_start(ap, arg0);
484   args = msvcrt_valisttos(arg0, ap, ' ');
485   va_end(ap);
486
487   ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, NULL);
488   MSVCRT_free(args);
489
490   return ret;
491 }
492
493 /*********************************************************************
494  *              _spawnlpe (MSVCRT.@)
495  */
496 MSVCRT_intptr_t CDECL _spawnlpe(int flags, const char* name, const char* arg0, ...)
497 {
498     va_list ap;
499     char *args, *envs = NULL;
500     const char * const *envp;
501     MSVCRT_intptr_t ret;
502     char fullname[MAX_PATH];
503
504     _searchenv(name, "PATH", fullname);
505
506     va_start(ap, arg0);
507     args = msvcrt_valisttos(arg0, ap, ' ');
508     va_end(ap);
509
510     va_start(ap, arg0);
511     while (va_arg( ap, char * ) != NULL) /*nothing*/;
512     envp = va_arg( ap, const char * const * );
513     if (envp) envs = msvcrt_argvtos(envp, 0);
514     va_end(ap);
515
516     ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, envs);
517
518     MSVCRT_free(args);
519     MSVCRT_free(envs);
520     return ret;
521 }
522
523 /*********************************************************************
524  *              _spawnve (MSVCRT.@)
525  *
526  * Like on Windows, this function does not handle arguments with spaces
527  * or double-quotes.
528  */
529 MSVCRT_intptr_t CDECL _spawnve(int flags, const char* name, const char* const* argv,
530                                const char* const* envv)
531 {
532   char * args = msvcrt_argvtos(argv,' ');
533   char * envs = msvcrt_argvtos(envv,0);
534   char fullname[MAX_PATH];
535   const char *p;
536   int len;
537   MSVCRT_intptr_t ret = -1;
538
539   TRACE(":call (%s), params (%s), env (%s)\n",debugstr_a(name),debugstr_a(args),
540    envs?"Custom":"Null");
541
542   /* no check for NULL name.
543      native doesn't do it */
544
545   p = memchr(name, '\0', MAX_PATH);
546   if( !p )
547     p = name + MAX_PATH - 1;
548   len = p - name;
549
550   /* extra-long names are silently truncated. */
551   memcpy(fullname, name, len);
552
553   for( p--; p >= name; p-- )
554   {
555     if( *p == '\\' || *p == '/' || *p == ':' || *p == '.' )
556       break;
557   }
558
559   /* if no extension is given, assume .exe */
560   if( (p < name || *p != '.') && len <= MAX_PATH - 5 )
561   {
562     FIXME("only trying .exe when no extension given\n");
563     memcpy(fullname+len, ".exe", 4);
564     len += 4;
565   }
566
567   fullname[len] = '\0';
568
569   if (args)
570   {
571     ret = msvcrt_spawn(flags, fullname, args, envs);
572     MSVCRT_free(args);
573   }
574   MSVCRT_free(envs);
575
576   return ret;
577 }
578
579 /*********************************************************************
580  *      _wspawnve (MSVCRT.@)
581  *
582  * Unicode version of _spawnve
583  */
584 MSVCRT_intptr_t CDECL _wspawnve(int flags, const MSVCRT_wchar_t* name, const MSVCRT_wchar_t* const* argv,
585                                 const MSVCRT_wchar_t* const* envv)
586 {
587   MSVCRT_wchar_t * args = msvcrt_argvtos_wide(argv,' ');
588   MSVCRT_wchar_t * envs = msvcrt_argvtos_wide(envv,0);
589   MSVCRT_wchar_t fullname[MAX_PATH];
590   const MSVCRT_wchar_t *p;
591   int len;
592   MSVCRT_intptr_t ret = -1;
593
594   TRACE(":call (%s), params (%s), env (%s)\n",debugstr_w(name),debugstr_w(args),
595    envs?"Custom":"Null");
596
597   /* no check for NULL name.
598      native doesn't do it */
599
600   p = memchrW(name, '\0', MAX_PATH);
601   if( !p )
602     p = name + MAX_PATH - 1;
603   len = p - name;
604
605   /* extra-long names are silently truncated. */
606   memcpy(fullname, name, len * sizeof(MSVCRT_wchar_t));
607
608   for( p--; p >= name; p-- )
609   {
610     if( *p == '\\' || *p == '/' || *p == ':' || *p == '.' )
611       break;
612   }
613
614   /* if no extension is given, assume .exe */
615   if( (p < name || *p != '.') && len <= MAX_PATH - 5 )
616   {
617     static const MSVCRT_wchar_t dotexe[] = {'.','e','x','e'};
618
619     FIXME("only trying .exe when no extension given\n");
620     memcpy(fullname+len, dotexe, 4 * sizeof(MSVCRT_wchar_t));
621     len += 4;
622   }
623
624   fullname[len] = '\0';
625
626   if (args)
627   {
628     ret = msvcrt_spawn_wide(flags, fullname, args, envs);
629     MSVCRT_free(args);
630   }
631   MSVCRT_free(envs);
632
633   return ret;
634 }
635
636 /*********************************************************************
637  *              _spawnv (MSVCRT.@)
638  *
639  * Like on Windows, this function does not handle arguments with spaces
640  * or double-quotes.
641  */
642 MSVCRT_intptr_t CDECL _spawnv(int flags, const char* name, const char* const* argv)
643 {
644   return _spawnve(flags, name, argv, NULL);
645 }
646
647 /*********************************************************************
648  *      _wspawnv (MSVCRT.@)
649  *
650  * Unicode version of _spawnv
651  */
652 MSVCRT_intptr_t CDECL _wspawnv(int flags, const MSVCRT_wchar_t* name, const MSVCRT_wchar_t* const* argv)
653 {
654   return _wspawnve(flags, name, argv, NULL);
655 }
656
657 /*********************************************************************
658  *              _spawnvpe (MSVCRT.@)
659  *
660  * Like on Windows, this function does not handle arguments with spaces
661  * or double-quotes.
662  */
663 MSVCRT_intptr_t CDECL _spawnvpe(int flags, const char* name, const char* const* argv,
664                                 const char* const* envv)
665 {
666   char fullname[MAX_PATH];
667   _searchenv(name, "PATH", fullname);
668   return _spawnve(flags, fullname[0] ? fullname : name, argv, envv);
669 }
670
671 /*********************************************************************
672  *      _wspawnvpe (MSVCRT.@)
673  *
674  * Unicode version of _spawnvpe
675  */
676 MSVCRT_intptr_t CDECL _wspawnvpe(int flags, const MSVCRT_wchar_t* name, const MSVCRT_wchar_t* const* argv,
677                                  const MSVCRT_wchar_t* const* envv)
678 {
679   static const MSVCRT_wchar_t path[] = {'P','A','T','H',0};
680   MSVCRT_wchar_t fullname[MAX_PATH];
681
682   _wsearchenv(name, path, fullname);
683   return _wspawnve(flags, fullname[0] ? fullname : name, argv, envv);
684 }
685
686 /*********************************************************************
687  *              _spawnvp (MSVCRT.@)
688  *
689  * Like on Windows, this function does not handle arguments with spaces
690  * or double-quotes.
691  */
692 MSVCRT_intptr_t CDECL _spawnvp(int flags, const char* name, const char* const* argv)
693 {
694   return _spawnvpe(flags, name, argv, NULL);
695 }
696
697 /*********************************************************************
698  *      _wspawnvp (MSVCRT.@)
699  *
700  * Unicode version of _spawnvp
701  */
702 MSVCRT_intptr_t CDECL _wspawnvp(int flags, const MSVCRT_wchar_t* name, const MSVCRT_wchar_t* const* argv)
703 {
704   return _wspawnvpe(flags, name, argv, NULL);
705 }
706
707 /*********************************************************************
708  *              _popen (MSVCRT.@)
709  * FIXME: convert to _wpopen and call that from here instead?  But it
710  * would have to convert the command back to ANSI to call msvcrt_spawn,
711  * less than ideal.
712  */
713 MSVCRT_FILE* CDECL MSVCRT__popen(const char* command, const char* mode)
714 {
715   static const char wcmd[] = "cmd", cmdFlag[] = " /C ", comSpec[] = "COMSPEC";
716   MSVCRT_FILE *ret;
717   BOOL readPipe = TRUE;
718   int textmode, fds[2], fdToDup, fdToOpen, fdStdHandle = -1, fdStdErr = -1;
719   const char *p;
720   char *cmdcopy;
721   DWORD comSpecLen;
722
723   TRACE("(command=%s, mode=%s)\n", debugstr_a(command), debugstr_a(mode));
724
725   if (!command || !mode)
726     return NULL;
727
728   textmode = *__p__fmode() & (MSVCRT__O_BINARY | MSVCRT__O_TEXT);
729   for (p = mode; *p; p++)
730   {
731     switch (*p)
732     {
733       case 'W':
734       case 'w':
735         readPipe = FALSE;
736         break;
737       case 'B':
738       case 'b':
739         textmode |= MSVCRT__O_BINARY;
740         textmode &= ~MSVCRT__O_TEXT;
741         break;
742       case 'T':
743       case 't':
744         textmode |= MSVCRT__O_TEXT;
745         textmode &= ~MSVCRT__O_BINARY;
746         break;
747     }
748   }
749   if (MSVCRT__pipe(fds, 0, textmode) == -1)
750     return NULL;
751
752   fdToDup = readPipe ? 1 : 0;
753   fdToOpen = readPipe ? 0 : 1;
754
755   if ((fdStdHandle = MSVCRT__dup(fdToDup)) == -1)
756     goto error;
757   if (MSVCRT__dup2(fds[fdToDup], fdToDup) != 0)
758     goto error;
759   if (readPipe)
760   {
761     if ((fdStdErr = MSVCRT__dup(MSVCRT_STDERR_FILENO)) == -1)
762       goto error;
763     if (MSVCRT__dup2(fds[fdToDup], MSVCRT_STDERR_FILENO) != 0)
764       goto error;
765   }
766
767   MSVCRT__close(fds[fdToDup]);
768
769   comSpecLen = GetEnvironmentVariableA(comSpec, NULL, 0);
770   if (!comSpecLen)
771     comSpecLen = strlen(wcmd) + 1;
772   cmdcopy = HeapAlloc(GetProcessHeap(), 0, comSpecLen + strlen(cmdFlag)
773    + strlen(command));
774   if (!GetEnvironmentVariableA(comSpec, cmdcopy, comSpecLen))
775     strcpy(cmdcopy, wcmd);
776   strcat(cmdcopy, cmdFlag);
777   strcat(cmdcopy, command);
778   if (msvcrt_spawn(MSVCRT__P_NOWAIT, NULL, cmdcopy, NULL) == -1)
779   {
780     MSVCRT__close(fds[fdToOpen]);
781     ret = NULL;
782   }
783   else
784   {
785     ret = MSVCRT__fdopen(fds[fdToOpen], mode);
786     if (!ret)
787       MSVCRT__close(fds[fdToOpen]);
788   }
789   HeapFree(GetProcessHeap(), 0, cmdcopy);
790   MSVCRT__dup2(fdStdHandle, fdToDup);
791   MSVCRT__close(fdStdHandle);
792   if (readPipe)
793   {
794     MSVCRT__dup2(fdStdErr, MSVCRT_STDERR_FILENO);
795     MSVCRT__close(fdStdErr);
796   }
797   return ret;
798
799 error:
800   if (fdStdHandle != -1) MSVCRT__close(fdStdHandle);
801   if (fdStdErr != -1)    MSVCRT__close(fdStdErr);
802   MSVCRT__close(fds[0]);
803   MSVCRT__close(fds[1]);
804   return NULL;
805 }
806
807 /*********************************************************************
808  *              _wpopen (MSVCRT.@)
809  */
810 MSVCRT_FILE* CDECL MSVCRT__wpopen(const MSVCRT_wchar_t* command, const MSVCRT_wchar_t* mode)
811 {
812   FIXME("(command=%s, mode=%s): stub\n", debugstr_w(command), debugstr_w(mode));
813   return NULL;
814 }
815
816 /*********************************************************************
817  *              _pclose (MSVCRT.@)
818  */
819 int CDECL MSVCRT__pclose(MSVCRT_FILE* file)
820 {
821   return MSVCRT_fclose(file);
822 }
823
824 /*********************************************************************
825  *              system (MSVCRT.@)
826  */
827 int CDECL MSVCRT_system(const char* cmd)
828 {
829     char* cmdcopy;
830     int res;
831
832     /* Make a writable copy for CreateProcess */
833     cmdcopy=_strdup(cmd);
834     /* FIXME: should probably launch cmd interpreter in COMSPEC */
835     res=msvcrt_spawn(MSVCRT__P_WAIT, NULL, cmdcopy, NULL);
836     MSVCRT_free(cmdcopy);
837     return res;
838 }
839
840 /*********************************************************************
841  *              _loaddll (MSVCRT.@)
842  */
843 MSVCRT_intptr_t CDECL _loaddll(const char* dllname)
844 {
845   return (MSVCRT_intptr_t)LoadLibraryA(dllname);
846 }
847
848 /*********************************************************************
849  *              _unloaddll (MSVCRT.@)
850  */
851 int CDECL _unloaddll(MSVCRT_intptr_t dll)
852 {
853   if (FreeLibrary((HMODULE)dll))
854     return 0;
855   else
856   {
857     int err = GetLastError();
858     msvcrt_set_errno(err);
859     return err;
860   }
861 }
862
863 /*********************************************************************
864  *              _getdllprocaddr (MSVCRT.@)
865  */
866 void * CDECL _getdllprocaddr(MSVCRT_intptr_t dll, const char *name, int ordinal)
867 {
868     if (name)
869     {
870         if (ordinal != -1) return NULL;
871         return GetProcAddress( (HMODULE)dll, name );
872     }
873     if (HIWORD(ordinal)) return NULL;
874     return GetProcAddress( (HMODULE)dll, (LPCSTR)(ULONG_PTR)ordinal );
875 }