|  | /* | 
|  | * msvcrt.dll spawn/exec functions | 
|  | * | 
|  | * Copyright 1996,1998 Marcus Meissner | 
|  | * Copyright 1996 Jukka Iivonen | 
|  | * Copyright 1997,2000 Uwe Bonnes | 
|  | * Copyright 2000 Jon Griffiths | 
|  | * | 
|  | * 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 "msvcrt.h" | 
|  | #include "ms_errno.h" | 
|  |  | 
|  | #include "msvcrt/process.h" | 
|  | #include "msvcrt/stdlib.h" | 
|  | #include "msvcrt/string.h" | 
|  |  | 
|  | 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) | 
|  | { | 
|  | SET_THREAD_VAR(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 = alist; | 
|  | long size; | 
|  | const char *arg; | 
|  | char* p; | 
|  | char *ret; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | SET_THREAD_VAR(errno, MSVCRT_ECHILD); | 
|  | SET_THREAD_VAR(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; | 
|  | } | 
|  | } | 
|  |  |