user32: Cache the text buffer length to avoid excessive calls to strlenW.
[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
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)
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 CDECL _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 CDECL _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 CDECL _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 CDECL _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 CDECL _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 CDECL _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 CDECL MSVCRT__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 CDECL _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 CDECL _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 CDECL _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 CDECL _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     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 CDECL _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 CDECL _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     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 CDECL _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   MSVCRT_free(envs);
486
487   return ret;
488 }
489
490 /*********************************************************************
491  *              _spawnv (MSVCRT.@)
492  *
493  * Like on Windows, this function does not handle arguments with spaces
494  * or double-quotes.
495  */
496 MSVCRT_intptr_t CDECL _spawnv(int flags, const char* name, const char* const* argv)
497 {
498   return _spawnve(flags, name, argv, NULL);
499 }
500
501 /*********************************************************************
502  *              _spawnvpe (MSVCRT.@)
503  *
504  * Like on Windows, this function does not handle arguments with spaces
505  * or double-quotes.
506  */
507 MSVCRT_intptr_t CDECL _spawnvpe(int flags, const char* name, const char* const* argv,
508                                 const char* const* envv)
509 {
510   char fullname[MAX_PATH];
511   _searchenv(name, "PATH", fullname);
512   return _spawnve(flags, fullname[0] ? fullname : name, argv, envv);
513 }
514
515 /*********************************************************************
516  *              _spawnvp (MSVCRT.@)
517  *
518  * Like on Windows, this function does not handle arguments with spaces
519  * or double-quotes.
520  */
521 MSVCRT_intptr_t CDECL _spawnvp(int flags, const char* name, const char* const* argv)
522 {
523   return _spawnvpe(flags, name, argv, NULL);
524 }
525
526 /*********************************************************************
527  *              _popen (MSVCRT.@)
528  * FIXME: convert to _wpopen and call that from here instead?  But it
529  * would have to convert the command back to ANSI to call msvcrt_spawn,
530  * less than ideal.
531  */
532 MSVCRT_FILE* CDECL MSVCRT__popen(const char* command, const char* mode)
533 {
534   static const char wcmd[] = "cmd", cmdFlag[] = " /C ", comSpec[] = "COMSPEC";
535   MSVCRT_FILE *ret;
536   BOOL readPipe = TRUE;
537   int textmode, fds[2], fdToDup, fdToOpen, fdStdHandle = -1, fdStdErr = -1;
538   const char *p;
539   char *cmdcopy;
540   DWORD comSpecLen;
541
542   TRACE("(command=%s, mode=%s)\n", debugstr_a(command), debugstr_a(mode));
543
544   if (!command || !mode)
545     return NULL;
546
547   textmode = *__p__fmode() & (MSVCRT__O_BINARY | MSVCRT__O_TEXT);
548   for (p = mode; *p; p++)
549   {
550     switch (*p)
551     {
552       case 'W':
553       case 'w':
554         readPipe = FALSE;
555         break;
556       case 'B':
557       case 'b':
558         textmode |= MSVCRT__O_BINARY;
559         textmode &= ~MSVCRT__O_TEXT;
560         break;
561       case 'T':
562       case 't':
563         textmode |= MSVCRT__O_TEXT;
564         textmode &= ~MSVCRT__O_BINARY;
565         break;
566     }
567   }
568   if (_pipe(fds, 0, textmode) == -1)
569     return NULL;
570
571   fdToDup = readPipe ? 1 : 0;
572   fdToOpen = readPipe ? 0 : 1;
573
574   if ((fdStdHandle = _dup(fdToDup)) == -1)
575     goto error;
576   if (_dup2(fds[fdToDup], fdToDup) != 0)
577     goto error;
578   if (readPipe)
579   {
580     if ((fdStdErr = _dup(MSVCRT_STDERR_FILENO)) == -1)
581       goto error;
582     if (_dup2(fds[fdToDup], MSVCRT_STDERR_FILENO) != 0)
583       goto error;
584   }
585
586   _close(fds[fdToDup]);
587
588   comSpecLen = GetEnvironmentVariableA(comSpec, NULL, 0);
589   if (!comSpecLen)
590     comSpecLen = strlen(wcmd) + 1;
591   cmdcopy = HeapAlloc(GetProcessHeap(), 0, comSpecLen + strlen(cmdFlag)
592    + strlen(command));
593   if (!GetEnvironmentVariableA(comSpec, cmdcopy, comSpecLen))
594     strcpy(cmdcopy, wcmd);
595   strcat(cmdcopy, cmdFlag);
596   strcat(cmdcopy, command);
597   if (msvcrt_spawn(MSVCRT__P_NOWAIT, NULL, cmdcopy, NULL) == -1)
598   {
599     _close(fds[fdToOpen]);
600     ret = NULL;
601   }
602   else
603   {
604     ret = MSVCRT__fdopen(fds[fdToOpen], mode);
605     if (!ret)
606       _close(fds[fdToOpen]);
607   }
608   HeapFree(GetProcessHeap(), 0, cmdcopy);
609   _dup2(fdStdHandle, fdToDup);
610   _close(fdStdHandle);
611   if (readPipe)
612   {
613     _dup2(fdStdErr, MSVCRT_STDERR_FILENO);
614     _close(fdStdErr);
615   }
616   return ret;
617
618 error:
619   if (fdStdHandle != -1) _close(fdStdHandle);
620   if (fdStdErr != -1)    _close(fdStdErr);
621   _close(fds[0]);
622   _close(fds[1]);
623   return NULL;
624 }
625
626 /*********************************************************************
627  *              _wpopen (MSVCRT.@)
628  */
629 MSVCRT_FILE* CDECL MSVCRT__wpopen(const MSVCRT_wchar_t* command, const MSVCRT_wchar_t* mode)
630 {
631   FIXME("(command=%s, mode=%s): stub\n", debugstr_w(command), debugstr_w(mode));
632   return NULL;
633 }
634
635 /*********************************************************************
636  *              _pclose (MSVCRT.@)
637  */
638 int CDECL MSVCRT__pclose(MSVCRT_FILE* file)
639 {
640   return MSVCRT_fclose(file);
641 }
642
643 /*********************************************************************
644  *              system (MSVCRT.@)
645  */
646 int CDECL MSVCRT_system(const char* cmd)
647 {
648     char* cmdcopy;
649     int res;
650
651     /* Make a writable copy for CreateProcess */
652     cmdcopy=_strdup(cmd);
653     /* FIXME: should probably launch cmd interpreter in COMSPEC */
654     res=msvcrt_spawn(MSVCRT__P_WAIT, NULL, cmdcopy, NULL);
655     MSVCRT_free(cmdcopy);
656     return res;
657 }
658
659 /*********************************************************************
660  *              _loaddll (MSVCRT.@)
661  */
662 MSVCRT_intptr_t CDECL _loaddll(const char* dllname)
663 {
664   return (MSVCRT_intptr_t)LoadLibraryA(dllname);
665 }
666
667 /*********************************************************************
668  *              _unloaddll (MSVCRT.@)
669  */
670 int CDECL _unloaddll(MSVCRT_intptr_t dll)
671 {
672   if (FreeLibrary((HMODULE)dll))
673     return 0;
674   else
675   {
676     int err = GetLastError();
677     msvcrt_set_errno(err);
678     return err;
679   }
680 }
681
682 /*********************************************************************
683  *              _getdllprocaddr (MSVCRT.@)
684  */
685 void * CDECL _getdllprocaddr(MSVCRT_intptr_t dll, const char *name, int ordinal)
686 {
687     if (name)
688     {
689         if (ordinal != -1) return NULL;
690         return GetProcAddress( (HMODULE)dll, name );
691     }
692     if (HIWORD(ordinal)) return NULL;
693     return GetProcAddress( (HMODULE)dll, (LPCSTR)(ULONG_PTR)ordinal );
694 }