| /* |
| * msvcrt.dll spawn/exec functions |
| * |
| * Copyright 1996,1998 Marcus Meissner |
| * Copyright 1996 Jukka Iivonen |
| * Copyright 1997,2000 Uwe Bonnes |
| * Copyright 2000 Jon Griffiths |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| * FIXME: |
| * -File handles need some special handling. Sometimes children get |
| * open file handles, sometimes not. The docs are confusing |
| * -No check for maximum path/argument/environment size is done |
| */ |
| #include "config.h" |
| |
| #include <stdarg.h> |
| |
| #include "msvcrt.h" |
| #include "msvcrt/errno.h" |
| |
| #include "msvcrt/process.h" |
| #include "msvcrt/stdlib.h" |
| #include "msvcrt/string.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); |
| |
| /* FIXME: Check file extensions for app to run */ |
| static const unsigned int EXE = 'e' << 16 | 'x' << 8 | 'e'; |
| static const unsigned int BAT = 'b' << 16 | 'a' << 8 | 't'; |
| static const unsigned int CMD = 'c' << 16 | 'm' << 8 | 'd'; |
| static const unsigned int COM = 'c' << 16 | 'o' << 8 | 'm'; |
| |
| /* INTERNAL: Spawn a child process */ |
| static int msvcrt_spawn(int flags, const char* exe, char* cmdline, char* env) |
| { |
| STARTUPINFOA si; |
| PROCESS_INFORMATION pi; |
| |
| if (sizeof(HANDLE) != sizeof(int)) |
| WARN("This call is unsuitable for your architecture\n"); |
| |
| if ((unsigned)flags > _P_DETACH) |
| { |
| *MSVCRT__errno() = MSVCRT_EINVAL; |
| return -1; |
| } |
| |
| FIXME(":must dup/kill streams for child process\n"); |
| |
| memset(&si, 0, sizeof(si)); |
| si.cb = sizeof(si); |
| |
| if (!CreateProcessA(exe, cmdline, NULL, NULL, TRUE, |
| flags == _P_DETACH ? DETACHED_PROCESS : 0, |
| env, NULL, &si, &pi)) |
| { |
| MSVCRT__set_errno(GetLastError()); |
| return -1; |
| } |
| |
| switch(flags) |
| { |
| case _P_WAIT: |
| WaitForSingleObject(pi.hProcess,-1); /* wait forvever */ |
| GetExitCodeProcess(pi.hProcess,&pi.dwProcessId); |
| CloseHandle(pi.hProcess); |
| CloseHandle(pi.hThread); |
| return (int)pi.dwProcessId; |
| case _P_DETACH: |
| CloseHandle(pi.hProcess); |
| pi.hProcess = 0; |
| /* fall through */ |
| case _P_NOWAIT: |
| case _P_NOWAITO: |
| CloseHandle(pi.hThread); |
| return (int)pi.hProcess; |
| case _P_OVERLAY: |
| MSVCRT__exit(0); |
| } |
| return -1; /* can't reach here */ |
| } |
| |
| /* INTERNAL: Convert argv list to a single 'delim'-separated string, with an |
| * extra '\0' to terminate it |
| */ |
| static char* msvcrt_argvtos(const char* const* arg, char delim) |
| { |
| const char* const* a; |
| long size; |
| char* p; |
| char* ret; |
| |
| if (!arg && !delim) |
| { |
| /* Return NULL for an empty environment list */ |
| return NULL; |
| } |
| |
| /* get length */ |
| a = arg; |
| size = 0; |
| while (*a) |
| { |
| size += strlen(*a) + 1; |
| a++; |
| } |
| |
| ret = (char*)MSVCRT_malloc(size + 1); |
| if (!ret) |
| return NULL; |
| |
| /* fill string */ |
| a = arg; |
| p = ret; |
| while (*a) |
| { |
| int len = strlen(*a); |
| memcpy(ret+size,*a,len); |
| p += len; |
| *p++ = delim; |
| a++; |
| } |
| *p='\0'; |
| return ret; |
| } |
| |
| /* INTERNAL: Convert va_list to a single 'delim'-separated string, with an |
| * extra '\0' to terminate it |
| */ |
| static char* msvcrt_valisttos(const char* arg0, va_list alist, char delim) |
| { |
| va_list alist2; |
| long size; |
| const char *arg; |
| char* p; |
| char *ret; |
| |
| #if HAVE_VA_COPY |
| va_copy(alist2,alist); |
| #else |
| # if HAVE___VA_COPY |
| __va_copy(alist2,alist); |
| # else |
| alist2 = alist; |
| # endif |
| #endif |
| |
| if (!arg0 && !delim) |
| { |
| /* Return NULL for an empty environment list */ |
| return NULL; |
| } |
| |
| /* get length */ |
| arg = arg0; |
| size = 0; |
| do { |
| size += strlen(arg) + 1; |
| arg = va_arg(alist, char*); |
| } while (arg != NULL); |
| |
| ret = (char*)MSVCRT_malloc(size + 1); |
| if (!ret) |
| return NULL; |
| |
| /* fill string */ |
| arg = arg0; |
| p = ret; |
| do { |
| int len = strlen(arg); |
| memcpy(p,arg,len); |
| p += len; |
| *p++ = delim; |
| arg = va_arg(alist2, char*); |
| } while (arg != NULL); |
| *p = '\0'; |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _cwait (MSVCRT.@) |
| */ |
| int _cwait(int *status, int pid, int action) |
| { |
| HANDLE hPid = (HANDLE)pid; |
| int doserrno; |
| |
| action = action; /* Remove warning */ |
| |
| if (!WaitForSingleObject(hPid, -1)) /* wait forever */ |
| { |
| if (status) |
| { |
| DWORD stat; |
| GetExitCodeProcess(hPid, &stat); |
| *status = (int)stat; |
| } |
| return (int)pid; |
| } |
| doserrno = GetLastError(); |
| |
| if (doserrno == ERROR_INVALID_HANDLE) |
| { |
| *MSVCRT__errno() = MSVCRT_ECHILD; |
| *__doserrno() = doserrno; |
| } |
| else |
| MSVCRT__set_errno(doserrno); |
| |
| return status ? *status = -1 : -1; |
| } |
| |
| /********************************************************************* |
| * _execl (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _execl(const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| int ret; |
| |
| va_start(ap, arg0); |
| args = msvcrt_valisttos(arg0, ap, ' '); |
| va_end(ap); |
| |
| ret = msvcrt_spawn(_P_OVERLAY, name, args, NULL); |
| MSVCRT_free(args); |
| |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _execlp (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _execlp(const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| int ret; |
| char fullname[MAX_PATH]; |
| |
| _searchenv(name, "PATH", fullname); |
| |
| va_start(ap, arg0); |
| args = msvcrt_valisttos(arg0, ap, ' '); |
| va_end(ap); |
| |
| ret = msvcrt_spawn(_P_OVERLAY, fullname[0] ? fullname : name, args, NULL); |
| MSVCRT_free(args); |
| |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _execv (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _execv(const char* name, char* const* argv) |
| { |
| return _spawnve(_P_OVERLAY, name, (const char* const*) argv, NULL); |
| } |
| |
| /********************************************************************* |
| * _execve (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _execve(const char* name, char* const* argv, const char* const* envv) |
| { |
| return _spawnve(_P_OVERLAY, name, (const char* const*) argv, envv); |
| } |
| |
| /********************************************************************* |
| * _execvpe (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _execvpe(const char* name, char* const* argv, const char* const* envv) |
| { |
| char fullname[MAX_PATH]; |
| |
| _searchenv(name, "PATH", fullname); |
| return _spawnve(_P_OVERLAY, fullname[0] ? fullname : name, |
| (const char* const*) argv, envv); |
| } |
| |
| /********************************************************************* |
| * _execvp (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _execvp(const char* name, char* const* argv) |
| { |
| return _execvpe(name, argv, NULL); |
| } |
| |
| /********************************************************************* |
| * _spawnl (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _spawnl(int flags, const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| int ret; |
| |
| va_start(ap, arg0); |
| args = msvcrt_valisttos(arg0, ap, ' '); |
| va_end(ap); |
| |
| ret = msvcrt_spawn(flags, name, args, NULL); |
| MSVCRT_free(args); |
| |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _spawnlp (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _spawnlp(int flags, const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| int ret; |
| char fullname[MAX_PATH]; |
| |
| _searchenv(name, "PATH", fullname); |
| |
| va_start(ap, arg0); |
| args = msvcrt_valisttos(arg0, ap, ' '); |
| va_end(ap); |
| |
| ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, NULL); |
| MSVCRT_free(args); |
| |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _spawnve (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _spawnve(int flags, const char* name, const char* const* argv, |
| const char* const* envv) |
| { |
| char * args = msvcrt_argvtos(argv,' '); |
| char * envs = msvcrt_argvtos(envv,0); |
| const char *fullname = name; |
| int ret = -1; |
| |
| FIXME(":not translating name %s to locate program\n",fullname); |
| TRACE(":call (%s), params (%s), env (%s)\n",name,args,envs?"Custom":"Null"); |
| |
| if (args) |
| { |
| ret = msvcrt_spawn(flags, fullname, args, envs); |
| MSVCRT_free(args); |
| } |
| if (envs) |
| MSVCRT_free(envs); |
| |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _spawnv (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _spawnv(int flags, const char* name, const char* const* argv) |
| { |
| return _spawnve(flags, name, argv, NULL); |
| } |
| |
| /********************************************************************* |
| * _spawnvpe (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _spawnvpe(int flags, const char* name, const char* const* argv, |
| const char* const* envv) |
| { |
| char fullname[MAX_PATH]; |
| _searchenv(name, "PATH", fullname); |
| return _spawnve(flags, fullname[0] ? fullname : name, argv, envv); |
| } |
| |
| /********************************************************************* |
| * _spawnvp (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| int _spawnvp(int flags, const char* name, const char* const* argv) |
| { |
| return _spawnvpe(flags, name, argv, NULL); |
| } |
| |
| /********************************************************************* |
| * system (MSVCRT.@) |
| */ |
| int MSVCRT_system(const char* cmd) |
| { |
| char* cmdcopy; |
| int res; |
| |
| /* Make a writable copy for CreateProcess */ |
| cmdcopy=_strdup(cmd); |
| /* FIXME: should probably launch cmd interpreter in COMSPEC */ |
| res=msvcrt_spawn(_P_WAIT, NULL, cmdcopy, NULL); |
| MSVCRT_free(cmdcopy); |
| return res; |
| } |
| |
| /********************************************************************* |
| * _loaddll (MSVCRT.@) |
| */ |
| int _loaddll(const char* dllname) |
| { |
| return LoadLibraryA(dllname); |
| } |
| |
| /********************************************************************* |
| * _unloaddll (MSVCRT.@) |
| */ |
| int _unloaddll(int dll) |
| { |
| if (FreeLibrary((HANDLE)dll)) |
| return 0; |
| else |
| { |
| int err = GetLastError(); |
| MSVCRT__set_errno(err); |
| return err; |
| } |
| } |