| /* |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 MSVCRT_intptr_t msvcrt_spawn(int flags, const char* exe, char* cmdline, char* env) |
| { |
| STARTUPINFOA si; |
| PROCESS_INFORMATION pi; |
| |
| 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 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 (MSVCRT_intptr_t)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.@) |
| */ |
| MSVCRT_intptr_t CDECL _cwait(int *status, MSVCRT_intptr_t 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 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. |
| */ |
| MSVCRT_intptr_t CDECL _execl(const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| MSVCRT_intptr_t 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.@) |
| */ |
| MSVCRT_intptr_t CDECL _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. |
| */ |
| MSVCRT_intptr_t CDECL _execlp(const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| MSVCRT_intptr_t 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.@) |
| */ |
| MSVCRT_intptr_t CDECL _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. |
| */ |
| MSVCRT_intptr_t CDECL _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. |
| */ |
| MSVCRT_intptr_t CDECL MSVCRT__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. |
| */ |
| MSVCRT_intptr_t CDECL _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. |
| */ |
| MSVCRT_intptr_t CDECL _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. |
| */ |
| MSVCRT_intptr_t CDECL _spawnl(int flags, const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| MSVCRT_intptr_t 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.@) |
| */ |
| MSVCRT_intptr_t CDECL _spawnle(int flags, const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char *args, *envs = NULL; |
| const char * const *envp; |
| MSVCRT_intptr_t 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); |
| MSVCRT_free(envs); |
| return ret; |
| } |
| |
| |
| /********************************************************************* |
| * _spawnlp (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| MSVCRT_intptr_t CDECL _spawnlp(int flags, const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char * args; |
| MSVCRT_intptr_t 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.@) |
| */ |
| MSVCRT_intptr_t CDECL _spawnlpe(int flags, const char* name, const char* arg0, ...) |
| { |
| va_list ap; |
| char *args, *envs = NULL; |
| const char * const *envp; |
| MSVCRT_intptr_t 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); |
| MSVCRT_free(envs); |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _spawnve (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| MSVCRT_intptr_t CDECL _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); |
| char fullname[MAX_PATH]; |
| const char *p; |
| int len; |
| MSVCRT_intptr_t ret = -1; |
| |
| TRACE(":call (%s), params (%s), env (%s)\n",debugstr_a(name),debugstr_a(args), |
| envs?"Custom":"Null"); |
| |
| /* no check for NULL name. |
| native doesn't do it */ |
| |
| p = memchr(name, '\0', MAX_PATH); |
| if( !p ) |
| p = name + MAX_PATH - 1; |
| len = p - name; |
| |
| /* extra-long names are silently truncated. */ |
| memcpy(fullname, name, len); |
| |
| for( p--; p >= name; p-- ) |
| { |
| if( *p == '\\' || *p == '/' || *p == ':' || *p == '.' ) |
| break; |
| } |
| |
| /* if no extension is given, assume .exe */ |
| if( (p < name || *p != '.') && len <= MAX_PATH - 5 ) |
| { |
| FIXME("only trying .exe when no extension given\n"); |
| memcpy(fullname+len, ".exe", 4); |
| len += 4; |
| } |
| |
| fullname[len] = '\0'; |
| |
| if (args) |
| { |
| ret = msvcrt_spawn(flags, fullname, args, envs); |
| MSVCRT_free(args); |
| } |
| MSVCRT_free(envs); |
| |
| return ret; |
| } |
| |
| /********************************************************************* |
| * _spawnv (MSVCRT.@) |
| * |
| * Like on Windows, this function does not handle arguments with spaces |
| * or double-quotes. |
| */ |
| MSVCRT_intptr_t CDECL _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. |
| */ |
| MSVCRT_intptr_t CDECL _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. |
| */ |
| MSVCRT_intptr_t CDECL _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* CDECL MSVCRT__popen(const char* command, const char* mode) |
| { |
| static const char wcmd[] = "cmd", 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; |
| } |
| } |
| 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 = 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* CDECL 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 CDECL MSVCRT__pclose(MSVCRT_FILE* file) |
| { |
| return MSVCRT_fclose(file); |
| } |
| |
| /********************************************************************* |
| * system (MSVCRT.@) |
| */ |
| int CDECL 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.@) |
| */ |
| MSVCRT_intptr_t CDECL _loaddll(const char* dllname) |
| { |
| return (MSVCRT_intptr_t)LoadLibraryA(dllname); |
| } |
| |
| /********************************************************************* |
| * _unloaddll (MSVCRT.@) |
| */ |
| int CDECL _unloaddll(MSVCRT_intptr_t dll) |
| { |
| if (FreeLibrary((HMODULE)dll)) |
| return 0; |
| else |
| { |
| int err = GetLastError(); |
| msvcrt_set_errno(err); |
| return err; |
| } |
| } |
| |
| /********************************************************************* |
| * _getdllprocaddr (MSVCRT.@) |
| */ |
| void * CDECL _getdllprocaddr(MSVCRT_intptr_t 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 ); |
| } |