| /* | |
| * 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 "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); | |
| /* 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 > MSVCRT__P_DETACH) | |
| { | |
| *MSVCRT__errno() = MSVCRT_EINVAL; | |
| return -1; | |
| } | |
| memset(&si, 0, sizeof(si)); | |
| si.cb = sizeof(si); | |
| msvcrt_create_io_inherit_block(&si); | |
| if (!CreateProcessA(exe, cmdline, NULL, NULL, TRUE, | |
| flags == MSVCRT__P_DETACH ? DETACHED_PROCESS : 0, | |
| env, NULL, &si, &pi)) | |
| { | |
| msvcrt_set_errno(GetLastError()); | |
| MSVCRT_free(si.lpReserved2); | |
| return -1; | |
| } | |
| MSVCRT_free(si.lpReserved2); | |
| switch(flags) | |
| { | |
| case MSVCRT__P_WAIT: | |
| WaitForSingleObject(pi.hProcess, INFINITE); | |
| GetExitCodeProcess(pi.hProcess,&pi.dwProcessId); | |
| CloseHandle(pi.hProcess); | |
| CloseHandle(pi.hThread); | |
| return (int)pi.dwProcessId; | |
| case MSVCRT__P_DETACH: | |
| CloseHandle(pi.hProcess); | |
| pi.hProcess = 0; | |
| /* fall through */ | |
| case MSVCRT__P_NOWAIT: | |
| case MSVCRT__P_NOWAITO: | |
| CloseHandle(pi.hThread); | |
| return (int)pi.hProcess; | |
| case MSVCRT__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(p,*a,len); | |
| p += len; | |
| *p++ = delim; | |
| a++; | |
| } | |
| if (delim && p > ret) p[-1] = 0; | |
| else *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; | |
| #ifdef HAVE_VA_COPY | |
| va_copy(alist2,alist); | |
| #else | |
| # ifdef 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); | |
| if (delim && p > ret) p[-1] = 0; | |
| else *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, INFINITE)) | |
| { | |
| if (status) | |
| { | |
| DWORD stat; | |
| GetExitCodeProcess(hPid, &stat); | |
| *status = (int)stat; | |
| } | |
| return (int)pid; | |
| } | |
| doserrno = GetLastError(); | |
| if (doserrno == ERROR_INVALID_HANDLE) | |
| { | |
| *MSVCRT__errno() = MSVCRT_ECHILD; | |
| *MSVCRT___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(MSVCRT__P_OVERLAY, name, args, NULL); | |
| MSVCRT_free(args); | |
| return ret; | |
| } | |
| /********************************************************************* | |
| * _execle (MSVCRT.@) | |
| */ | |
| int _execle(const char* name, const char* arg0, ...) | |
| { | |
| FIXME("stub\n"); | |
| return -1; | |
| } | |
| /********************************************************************* | |
| * _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(MSVCRT__P_OVERLAY, fullname[0] ? fullname : name, args, NULL); | |
| MSVCRT_free(args); | |
| return ret; | |
| } | |
| /********************************************************************* | |
| * _execlpe (MSVCRT.@) | |
| */ | |
| int _execlpe(const char* name, const char* arg0, ...) | |
| { | |
| FIXME("stub\n"); | |
| return -1; | |
| } | |
| /********************************************************************* | |
| * _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(MSVCRT__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(MSVCRT__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(MSVCRT__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; | |
| } | |
| /********************************************************************* | |
| * _spawnle (MSVCRT.@) | |
| */ | |
| int _spawnle(int flags, const char* name, const char* arg0, ...) | |
| { | |
| va_list ap; | |
| char *args, *envs = NULL; | |
| const char * const *envp; | |
| int ret; | |
| va_start(ap, arg0); | |
| args = msvcrt_valisttos(arg0, ap, ' '); | |
| va_end(ap); | |
| va_start(ap, arg0); | |
| while (va_arg( ap, char * ) != NULL) /*nothing*/; | |
| envp = va_arg( ap, const char * const * ); | |
| if (envp) envs = msvcrt_argvtos(envp, 0); | |
| va_end(ap); | |
| ret = msvcrt_spawn(flags, name, args, envs); | |
| MSVCRT_free(args); | |
| if (envs) MSVCRT_free(envs); | |
| 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; | |
| } | |
| /********************************************************************* | |
| * _spawnlpe (MSVCRT.@) | |
| */ | |
| int _spawnlpe(int flags, const char* name, const char* arg0, ...) | |
| { | |
| va_list ap; | |
| char *args, *envs = NULL; | |
| const char * const *envp; | |
| int ret; | |
| char fullname[MAX_PATH]; | |
| _searchenv(name, "PATH", fullname); | |
| va_start(ap, arg0); | |
| args = msvcrt_valisttos(arg0, ap, ' '); | |
| va_end(ap); | |
| va_start(ap, arg0); | |
| while (va_arg( ap, char * ) != NULL) /*nothing*/; | |
| envp = va_arg( ap, const char * const * ); | |
| if (envp) envs = msvcrt_argvtos(envp, 0); | |
| va_end(ap); | |
| ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, envs); | |
| MSVCRT_free(args); | |
| if (envs) MSVCRT_free(envs); | |
| 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",debugstr_a(name),debugstr_a(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); | |
| } | |
| /********************************************************************* | |
| * _popen (MSVCRT.@) | |
| * FIXME: convert to _wpopen and call that from here instead? But it | |
| * would have to convert the command back to ANSI to call msvcrt_spawn, | |
| * less than ideal. | |
| */ | |
| MSVCRT_FILE* MSVCRT__popen(const char* command, const char* mode) | |
| { | |
| static const char wcmd[] = "wcmd", cmdFlag[] = " /C ", comSpec[] = "COMSPEC"; | |
| MSVCRT_FILE *ret; | |
| BOOL readPipe = TRUE; | |
| int textmode, fds[2], fdToDup, fdToOpen, fdStdHandle = -1, fdStdErr = -1; | |
| const char *p; | |
| char *cmdcopy; | |
| DWORD comSpecLen; | |
| TRACE("(command=%s, mode=%s)\n", debugstr_a(command), debugstr_a(mode)); | |
| if (!command || !mode) | |
| return NULL; | |
| textmode = *__p__fmode() & (MSVCRT__O_BINARY | MSVCRT__O_TEXT); | |
| for (p = mode; *p; p++) | |
| { | |
| switch (*p) | |
| { | |
| case 'W': | |
| case 'w': | |
| readPipe = FALSE; | |
| break; | |
| case 'B': | |
| case 'b': | |
| textmode |= MSVCRT__O_BINARY; | |
| textmode &= ~MSVCRT__O_TEXT; | |
| break; | |
| case 'T': | |
| case 't': | |
| textmode |= MSVCRT__O_TEXT; | |
| textmode &= ~MSVCRT__O_BINARY; | |
| break; | |
| } | |
| } | |
| textmode |= MSVCRT__O_NOINHERIT; | |
| if (_pipe(fds, 0, textmode) == -1) | |
| return NULL; | |
| fdToDup = readPipe ? 1 : 0; | |
| fdToOpen = readPipe ? 0 : 1; | |
| if ((fdStdHandle = _dup(fdToDup)) == -1) | |
| goto error; | |
| if (_dup2(fds[fdToDup], fdToDup) != 0) | |
| goto error; | |
| if (readPipe) | |
| { | |
| if ((fdStdErr = _dup(MSVCRT_STDERR_FILENO)) == -1) | |
| goto error; | |
| if (_dup2(fds[fdToDup], MSVCRT_STDERR_FILENO) != 0) | |
| goto error; | |
| } | |
| _close(fds[fdToDup]); | |
| comSpecLen = GetEnvironmentVariableA(comSpec, NULL, 0); | |
| if (!comSpecLen) | |
| comSpecLen = strlen(wcmd) + 1; | |
| cmdcopy = (char *)HeapAlloc(GetProcessHeap(), 0, comSpecLen + strlen(cmdFlag) | |
| + strlen(command)); | |
| if (!GetEnvironmentVariableA(comSpec, cmdcopy, comSpecLen)) | |
| strcpy(cmdcopy, wcmd); | |
| strcat(cmdcopy, cmdFlag); | |
| strcat(cmdcopy, command); | |
| if (msvcrt_spawn(MSVCRT__P_NOWAIT, NULL, cmdcopy, NULL) == -1) | |
| { | |
| _close(fds[fdToOpen]); | |
| ret = NULL; | |
| } | |
| else | |
| { | |
| ret = MSVCRT__fdopen(fds[fdToOpen], mode); | |
| if (!ret) | |
| _close(fds[fdToOpen]); | |
| } | |
| HeapFree(GetProcessHeap(), 0, cmdcopy); | |
| _dup2(fdStdHandle, fdToDup); | |
| _close(fdStdHandle); | |
| if (readPipe) | |
| { | |
| _dup2(fdStdErr, MSVCRT_STDERR_FILENO); | |
| _close(fdStdErr); | |
| } | |
| return ret; | |
| error: | |
| if (fdStdHandle != -1) _close(fdStdHandle); | |
| if (fdStdErr != -1) _close(fdStdErr); | |
| _close(fds[0]); | |
| _close(fds[1]); | |
| return NULL; | |
| } | |
| /********************************************************************* | |
| * _wpopen (MSVCRT.@) | |
| */ | |
| MSVCRT_FILE* MSVCRT__wpopen(const MSVCRT_wchar_t* command, const MSVCRT_wchar_t* mode) | |
| { | |
| FIXME("(command=%s, mode=%s): stub\n", debugstr_w(command), debugstr_w(mode)); | |
| return NULL; | |
| } | |
| /********************************************************************* | |
| * _pclose (MSVCRT.@) | |
| */ | |
| int MSVCRT__pclose(MSVCRT_FILE* file) | |
| { | |
| return MSVCRT_fclose(file); | |
| } | |
| /********************************************************************* | |
| * 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(MSVCRT__P_WAIT, NULL, cmdcopy, NULL); | |
| MSVCRT_free(cmdcopy); | |
| return res; | |
| } | |
| /********************************************************************* | |
| * _loaddll (MSVCRT.@) | |
| */ | |
| int _loaddll(const char* dllname) | |
| { | |
| return (int)LoadLibraryA(dllname); | |
| } | |
| /********************************************************************* | |
| * _unloaddll (MSVCRT.@) | |
| */ | |
| int _unloaddll(int dll) | |
| { | |
| if (FreeLibrary((HMODULE)dll)) | |
| return 0; | |
| else | |
| { | |
| int err = GetLastError(); | |
| msvcrt_set_errno(err); | |
| return err; | |
| } | |
| } | |
| /********************************************************************* | |
| * _getdllprocaddr (MSVCRT.@) | |
| */ | |
| void *_getdllprocaddr(int dll, const char *name, int ordinal) | |
| { | |
| if (name) | |
| { | |
| if (ordinal != -1) return NULL; | |
| return GetProcAddress( (HMODULE)dll, name ); | |
| } | |
| if (HIWORD(ordinal)) return NULL; | |
| return GetProcAddress( (HMODULE)dll, (LPCSTR)(ULONG_PTR)ordinal ); | |
| } |