oleaut32: Fix differences between the size returned in sizing the
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
35 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
36
37 /* INTERNAL: Spawn a child process */
38 static MSVCRT_intptr_t msvcrt_spawn(int flags, const char* exe, char* cmdline, char* env)
39 {
40   STARTUPINFOA si;
41   PROCESS_INFORMATION pi;
42
43   if ((unsigned)flags > MSVCRT__P_DETACH)
44   {
45     *MSVCRT__errno() = MSVCRT_EINVAL;
46     return -1;
47   }
48
49   memset(&si, 0, sizeof(si));
50   si.cb = sizeof(si);
51   msvcrt_create_io_inherit_block(&si);
52   if (!CreateProcessA(exe, cmdline, NULL, NULL, TRUE,
53                      flags == MSVCRT__P_DETACH ? DETACHED_PROCESS : 0,
54                      env, NULL, &si, &pi))
55   {
56     msvcrt_set_errno(GetLastError());
57     MSVCRT_free(si.lpReserved2);
58     return -1;
59   }
60
61   MSVCRT_free(si.lpReserved2);
62   switch(flags)
63   {
64   case MSVCRT__P_WAIT:
65     WaitForSingleObject(pi.hProcess, INFINITE);
66     GetExitCodeProcess(pi.hProcess,&pi.dwProcessId);
67     CloseHandle(pi.hProcess);
68     CloseHandle(pi.hThread);
69     return pi.dwProcessId;
70   case MSVCRT__P_DETACH:
71     CloseHandle(pi.hProcess);
72     pi.hProcess = 0;
73     /* fall through */
74   case MSVCRT__P_NOWAIT:
75   case MSVCRT__P_NOWAITO:
76     CloseHandle(pi.hThread);
77     return (MSVCRT_intptr_t)pi.hProcess;
78   case  MSVCRT__P_OVERLAY:
79     MSVCRT__exit(0);
80   }
81   return -1; /* can't reach here */
82 }
83
84 /* INTERNAL: Convert argv list to a single 'delim'-separated string, with an
85  * extra '\0' to terminate it
86  */
87 static char* msvcrt_argvtos(const char* const* arg, char delim)
88 {
89   const char* const* a;
90   long size;
91   char* p;
92   char* ret;
93
94   if (!arg && !delim)
95   {
96       /* Return NULL for an empty environment list */
97       return NULL;
98   }
99
100   /* get length */
101   a = arg;
102   size = 0;
103   while (*a)
104   {
105     size += strlen(*a) + 1;
106     a++;
107   }
108
109   ret = (char*)MSVCRT_malloc(size + 1);
110   if (!ret)
111     return NULL;
112
113   /* fill string */
114   a = arg;
115   p = ret;
116   while (*a)
117   {
118     int len = strlen(*a);
119     memcpy(p,*a,len);
120     p += len;
121     *p++ = delim;
122     a++;
123   }
124   if (delim && p > ret) p[-1] = 0;
125   else *p = 0;
126   return ret;
127 }
128
129 /* INTERNAL: Convert va_list to a single 'delim'-separated string, with an
130  * extra '\0' to terminate it
131  */
132 static char* msvcrt_valisttos(const char* arg0, va_list alist, char delim)
133 {
134   va_list alist2;
135   long size;
136   const char *arg;
137   char* p;
138   char *ret;
139
140 #ifdef HAVE_VA_COPY
141   va_copy(alist2,alist);
142 #else
143 # ifdef HAVE___VA_COPY
144   __va_copy(alist2,alist);
145 # else
146   alist2 = alist;
147 # endif
148 #endif
149
150   if (!arg0 && !delim)
151   {
152       /* Return NULL for an empty environment list */
153       return NULL;
154   }
155
156   /* get length */
157   arg = arg0;
158   size = 0;
159   do {
160       size += strlen(arg) + 1;
161       arg = va_arg(alist, char*);
162   } while (arg != NULL);
163
164   ret = (char*)MSVCRT_malloc(size + 1);
165   if (!ret)
166     return NULL;
167
168   /* fill string */
169   arg = arg0;
170   p = ret;
171   do {
172       int len = strlen(arg);
173       memcpy(p,arg,len);
174       p += len;
175       *p++ = delim;
176       arg = va_arg(alist2, char*);
177   } while (arg != NULL);
178   if (delim && p > ret) p[-1] = 0;
179   else *p = 0;
180   return ret;
181 }
182
183 /*********************************************************************
184  *              _cwait (MSVCRT.@)
185  */
186 MSVCRT_intptr_t _cwait(int *status, MSVCRT_intptr_t pid, int action)
187 {
188   HANDLE hPid = (HANDLE)pid;
189   int doserrno;
190
191   action = action; /* Remove warning */
192
193   if (!WaitForSingleObject(hPid, INFINITE))
194   {
195     if (status)
196     {
197       DWORD stat;
198       GetExitCodeProcess(hPid, &stat);
199       *status = (int)stat;
200     }
201     return pid;
202   }
203   doserrno = GetLastError();
204
205   if (doserrno == ERROR_INVALID_HANDLE)
206   {
207     *MSVCRT__errno() =  MSVCRT_ECHILD;
208     *MSVCRT___doserrno() = doserrno;
209   }
210   else
211     msvcrt_set_errno(doserrno);
212
213   return status ? *status = -1 : -1;
214 }
215
216 /*********************************************************************
217  *              _execl (MSVCRT.@)
218  *
219  * Like on Windows, this function does not handle arguments with spaces
220  * or double-quotes.
221  */
222 MSVCRT_intptr_t _execl(const char* name, const char* arg0, ...)
223 {
224   va_list ap;
225   char * args;
226   MSVCRT_intptr_t ret;
227
228   va_start(ap, arg0);
229   args = msvcrt_valisttos(arg0, ap, ' ');
230   va_end(ap);
231
232   ret = msvcrt_spawn(MSVCRT__P_OVERLAY, name, args, NULL);
233   MSVCRT_free(args);
234
235   return ret;
236 }
237
238 /*********************************************************************
239  *              _execle (MSVCRT.@)
240  */
241 MSVCRT_intptr_t _execle(const char* name, const char* arg0, ...)
242 {
243     FIXME("stub\n");
244     return -1;
245 }
246
247 /*********************************************************************
248  *              _execlp (MSVCRT.@)
249  *
250  * Like on Windows, this function does not handle arguments with spaces
251  * or double-quotes.
252  */
253 MSVCRT_intptr_t _execlp(const char* name, const char* arg0, ...)
254 {
255   va_list ap;
256   char * args;
257   MSVCRT_intptr_t ret;
258   char fullname[MAX_PATH];
259
260   _searchenv(name, "PATH", fullname);
261
262   va_start(ap, arg0);
263   args = msvcrt_valisttos(arg0, ap, ' ');
264   va_end(ap);
265
266   ret = msvcrt_spawn(MSVCRT__P_OVERLAY, fullname[0] ? fullname : name, args, NULL);
267   MSVCRT_free(args);
268
269   return ret;
270 }
271
272 /*********************************************************************
273  *              _execlpe (MSVCRT.@)
274  */
275 MSVCRT_intptr_t _execlpe(const char* name, const char* arg0, ...)
276 {
277     FIXME("stub\n");
278     return -1;
279 }
280
281 /*********************************************************************
282  *              _execv (MSVCRT.@)
283  *
284  * Like on Windows, this function does not handle arguments with spaces
285  * or double-quotes.
286  */
287 MSVCRT_intptr_t _execv(const char* name, char* const* argv)
288 {
289   return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, NULL);
290 }
291
292 /*********************************************************************
293  *              _execve (MSVCRT.@)
294  *
295  * Like on Windows, this function does not handle arguments with spaces
296  * or double-quotes.
297  */
298 MSVCRT_intptr_t _execve(const char* name, char* const* argv, const char* const* envv)
299 {
300   return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, envv);
301 }
302
303 /*********************************************************************
304  *              _execvpe (MSVCRT.@)
305  *
306  * Like on Windows, this function does not handle arguments with spaces
307  * or double-quotes.
308  */
309 MSVCRT_intptr_t _execvpe(const char* name, char* const* argv, const char* const* envv)
310 {
311   char fullname[MAX_PATH];
312
313   _searchenv(name, "PATH", fullname);
314   return _spawnve(MSVCRT__P_OVERLAY, fullname[0] ? fullname : name,
315                   (const char* const*) argv, envv);
316 }
317
318 /*********************************************************************
319  *              _execvp (MSVCRT.@)
320  *
321  * Like on Windows, this function does not handle arguments with spaces
322  * or double-quotes.
323  */
324 MSVCRT_intptr_t _execvp(const char* name, char* const* argv)
325 {
326   return _execvpe(name, argv, NULL);
327 }
328
329 /*********************************************************************
330  *              _spawnl (MSVCRT.@)
331  *
332  * Like on Windows, this function does not handle arguments with spaces
333  * or double-quotes.
334  */
335 MSVCRT_intptr_t _spawnl(int flags, const char* name, const char* arg0, ...)
336 {
337   va_list ap;
338   char * args;
339   MSVCRT_intptr_t ret;
340
341   va_start(ap, arg0);
342   args = msvcrt_valisttos(arg0, ap, ' ');
343   va_end(ap);
344
345   ret = msvcrt_spawn(flags, name, args, NULL);
346   MSVCRT_free(args);
347
348   return ret;
349 }
350
351 /*********************************************************************
352  *              _spawnle (MSVCRT.@)
353  */
354 MSVCRT_intptr_t _spawnle(int flags, const char* name, const char* arg0, ...)
355 {
356     va_list ap;
357     char *args, *envs = NULL;
358     const char * const *envp;
359     MSVCRT_intptr_t ret;
360
361     va_start(ap, arg0);
362     args = msvcrt_valisttos(arg0, ap, ' ');
363     va_end(ap);
364
365     va_start(ap, arg0);
366     while (va_arg( ap, char * ) != NULL) /*nothing*/;
367     envp = va_arg( ap, const char * const * );
368     if (envp) envs = msvcrt_argvtos(envp, 0);
369     va_end(ap);
370
371     ret = msvcrt_spawn(flags, name, args, envs);
372
373     MSVCRT_free(args);
374     if (envs) MSVCRT_free(envs);
375     return ret;
376 }
377
378
379 /*********************************************************************
380  *              _spawnlp (MSVCRT.@)
381  *
382  * Like on Windows, this function does not handle arguments with spaces
383  * or double-quotes.
384  */
385 MSVCRT_intptr_t _spawnlp(int flags, const char* name, const char* arg0, ...)
386 {
387   va_list ap;
388   char * args;
389   MSVCRT_intptr_t ret;
390   char fullname[MAX_PATH];
391
392   _searchenv(name, "PATH", fullname);
393
394   va_start(ap, arg0);
395   args = msvcrt_valisttos(arg0, ap, ' ');
396   va_end(ap);
397
398   ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, NULL);
399   MSVCRT_free(args);
400
401   return ret;
402 }
403
404 /*********************************************************************
405  *              _spawnlpe (MSVCRT.@)
406  */
407 MSVCRT_intptr_t _spawnlpe(int flags, const char* name, const char* arg0, ...)
408 {
409     va_list ap;
410     char *args, *envs = NULL;
411     const char * const *envp;
412     MSVCRT_intptr_t ret;
413     char fullname[MAX_PATH];
414
415     _searchenv(name, "PATH", fullname);
416
417     va_start(ap, arg0);
418     args = msvcrt_valisttos(arg0, ap, ' ');
419     va_end(ap);
420
421     va_start(ap, arg0);
422     while (va_arg( ap, char * ) != NULL) /*nothing*/;
423     envp = va_arg( ap, const char * const * );
424     if (envp) envs = msvcrt_argvtos(envp, 0);
425     va_end(ap);
426
427     ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, envs);
428
429     MSVCRT_free(args);
430     if (envs) MSVCRT_free(envs);
431     return ret;
432 }
433
434 /*********************************************************************
435  *              _spawnve (MSVCRT.@)
436  *
437  * Like on Windows, this function does not handle arguments with spaces
438  * or double-quotes.
439  */
440 MSVCRT_intptr_t _spawnve(int flags, const char* name, const char* const* argv,
441                             const char* const* envv)
442 {
443   char * args = msvcrt_argvtos(argv,' ');
444   char * envs = msvcrt_argvtos(envv,0);
445   char fullname[MAX_PATH];
446   const char *p;
447   int len;
448   MSVCRT_intptr_t ret = -1;
449
450   TRACE(":call (%s), params (%s), env (%s)\n",debugstr_a(name),debugstr_a(args),
451    envs?"Custom":"Null");
452
453   /* no check for NULL name.
454      native doesn't do it */
455
456   p = memchr(name, '\0', MAX_PATH);
457   if( !p )
458     p = name + MAX_PATH - 1;
459   len = p - name;
460
461   /* extra-long names are silently truncated. */
462   memcpy(fullname, name, len);
463
464   for( p--; p >= name; p-- )
465   {
466     if( *p == '\\' || *p == '/' || *p == ':' || *p == '.' )
467       break;
468   }
469
470   /* if no extension is given, assume .exe */
471   if( (p < name || *p != '.') && len <= MAX_PATH - 5 )
472   {
473     FIXME("only trying .exe when no extension given\n");
474     memcpy(fullname+len, ".exe", 4);
475     len += 4;
476   }
477
478   fullname[len] = '\0';
479
480   if (args)
481   {
482     ret = msvcrt_spawn(flags, fullname, args, envs);
483     MSVCRT_free(args);
484   }
485   if (envs)
486     MSVCRT_free(envs);
487
488   return ret;
489 }
490
491 /*********************************************************************
492  *              _spawnv (MSVCRT.@)
493  *
494  * Like on Windows, this function does not handle arguments with spaces
495  * or double-quotes.
496  */
497 MSVCRT_intptr_t _spawnv(int flags, const char* name, const char* const* argv)
498 {
499   return _spawnve(flags, name, argv, NULL);
500 }
501
502 /*********************************************************************
503  *              _spawnvpe (MSVCRT.@)
504  *
505  * Like on Windows, this function does not handle arguments with spaces
506  * or double-quotes.
507  */
508 MSVCRT_intptr_t _spawnvpe(int flags, const char* name, const char* const* argv,
509                             const char* const* envv)
510 {
511   char fullname[MAX_PATH];
512   _searchenv(name, "PATH", fullname);
513   return _spawnve(flags, fullname[0] ? fullname : name, argv, envv);
514 }
515
516 /*********************************************************************
517  *              _spawnvp (MSVCRT.@)
518  *
519  * Like on Windows, this function does not handle arguments with spaces
520  * or double-quotes.
521  */
522 MSVCRT_intptr_t _spawnvp(int flags, const char* name, const char* const* argv)
523 {
524   return _spawnvpe(flags, name, argv, NULL);
525 }
526
527 /*********************************************************************
528  *              _popen (MSVCRT.@)
529  * FIXME: convert to _wpopen and call that from here instead?  But it
530  * would have to convert the command back to ANSI to call msvcrt_spawn,
531  * less than ideal.
532  */
533 MSVCRT_FILE* MSVCRT__popen(const char* command, const char* mode)
534 {
535   static const char wcmd[] = "wcmd", cmdFlag[] = " /C ", comSpec[] = "COMSPEC";
536   MSVCRT_FILE *ret;
537   BOOL readPipe = TRUE;
538   int textmode, fds[2], fdToDup, fdToOpen, fdStdHandle = -1, fdStdErr = -1;
539   const char *p;
540   char *cmdcopy;
541   DWORD comSpecLen;
542
543   TRACE("(command=%s, mode=%s)\n", debugstr_a(command), debugstr_a(mode));
544
545   if (!command || !mode)
546     return NULL;
547
548   textmode = *__p__fmode() & (MSVCRT__O_BINARY | MSVCRT__O_TEXT);
549   for (p = mode; *p; p++)
550   {
551     switch (*p)
552     {
553       case 'W':
554       case 'w':
555         readPipe = FALSE;
556         break;
557       case 'B':
558       case 'b':
559         textmode |= MSVCRT__O_BINARY;
560         textmode &= ~MSVCRT__O_TEXT;
561         break;
562       case 'T':
563       case 't':
564         textmode |= MSVCRT__O_TEXT;
565         textmode &= ~MSVCRT__O_BINARY;
566         break;
567     }
568   }
569   if (_pipe(fds, 0, textmode) == -1)
570     return NULL;
571
572   fdToDup = readPipe ? 1 : 0;
573   fdToOpen = readPipe ? 0 : 1;
574
575   if ((fdStdHandle = _dup(fdToDup)) == -1)
576     goto error;
577   if (_dup2(fds[fdToDup], fdToDup) != 0)
578     goto error;
579   if (readPipe)
580   {
581     if ((fdStdErr = _dup(MSVCRT_STDERR_FILENO)) == -1)
582       goto error;
583     if (_dup2(fds[fdToDup], MSVCRT_STDERR_FILENO) != 0)
584       goto error;
585   }
586
587   _close(fds[fdToDup]);
588
589   comSpecLen = GetEnvironmentVariableA(comSpec, NULL, 0);
590   if (!comSpecLen)
591     comSpecLen = strlen(wcmd) + 1;
592   cmdcopy = HeapAlloc(GetProcessHeap(), 0, comSpecLen + strlen(cmdFlag)
593    + strlen(command));
594   if (!GetEnvironmentVariableA(comSpec, cmdcopy, comSpecLen))
595     strcpy(cmdcopy, wcmd);
596   strcat(cmdcopy, cmdFlag);
597   strcat(cmdcopy, command);
598   if (msvcrt_spawn(MSVCRT__P_NOWAIT, NULL, cmdcopy, NULL) == -1)
599   {
600     _close(fds[fdToOpen]);
601     ret = NULL;
602   }
603   else
604   {
605     ret = MSVCRT__fdopen(fds[fdToOpen], mode);
606     if (!ret)
607       _close(fds[fdToOpen]);
608   }
609   HeapFree(GetProcessHeap(), 0, cmdcopy);
610   _dup2(fdStdHandle, fdToDup);
611   _close(fdStdHandle);
612   if (readPipe)
613   {
614     _dup2(fdStdErr, MSVCRT_STDERR_FILENO);
615     _close(fdStdErr);
616   }
617   return ret;
618
619 error:
620   if (fdStdHandle != -1) _close(fdStdHandle);
621   if (fdStdErr != -1)    _close(fdStdErr);
622   _close(fds[0]);
623   _close(fds[1]);
624   return NULL;
625 }
626
627 /*********************************************************************
628  *              _wpopen (MSVCRT.@)
629  */
630 MSVCRT_FILE* MSVCRT__wpopen(const MSVCRT_wchar_t* command, const MSVCRT_wchar_t* mode)
631 {
632   FIXME("(command=%s, mode=%s): stub\n", debugstr_w(command), debugstr_w(mode));
633   return NULL;
634 }
635
636 /*********************************************************************
637  *              _pclose (MSVCRT.@)
638  */
639 int MSVCRT__pclose(MSVCRT_FILE* file)
640 {
641   return MSVCRT_fclose(file);
642 }
643
644 /*********************************************************************
645  *              system (MSVCRT.@)
646  */
647 int MSVCRT_system(const char* cmd)
648 {
649     char* cmdcopy;
650     int res;
651
652     /* Make a writable copy for CreateProcess */
653     cmdcopy=_strdup(cmd);
654     /* FIXME: should probably launch cmd interpreter in COMSPEC */
655     res=msvcrt_spawn(MSVCRT__P_WAIT, NULL, cmdcopy, NULL);
656     MSVCRT_free(cmdcopy);
657     return res;
658 }
659
660 /*********************************************************************
661  *              _loaddll (MSVCRT.@)
662  */
663 MSVCRT_intptr_t _loaddll(const char* dllname)
664 {
665   return (MSVCRT_intptr_t)LoadLibraryA(dllname);
666 }
667
668 /*********************************************************************
669  *              _unloaddll (MSVCRT.@)
670  */
671 int _unloaddll(MSVCRT_intptr_t dll)
672 {
673   if (FreeLibrary((HMODULE)dll))
674     return 0;
675   else
676   {
677     int err = GetLastError();
678     msvcrt_set_errno(err);
679     return err;
680   }
681 }
682
683 /*********************************************************************
684  *              _getdllprocaddr (MSVCRT.@)
685  */
686 void *_getdllprocaddr(MSVCRT_intptr_t dll, const char *name, int ordinal)
687 {
688     if (name)
689     {
690         if (ordinal != -1) return NULL;
691         return GetProcAddress( (HMODULE)dll, name );
692     }
693     if (HIWORD(ordinal)) return NULL;
694     return GetProcAddress( (HMODULE)dll, (LPCSTR)(ULONG_PTR)ordinal );
695 }