Imnplementation of the _wsystem function. In addition to this, Unicode versions of the internal spawn function has been created (for use by _wsystem) and the system call uses _wsystem internally to allow for the use of the COMSPEC variable in execution. License: LGPL Changelog: * dlls/msvcrt/msvcrt.spec, dlls/msvcrt/process.c: Jaco Greeff <jaco@puxedo.org> - Implementation of the _wsystem call - system call uses _wsystem internally to allow for the use of COMSPEC in execution (previous FIXME)
diff -aurN msvcrt-C00/dlls/msvcrt/msvcrt.spec msvcrt-C01/dlls/msvcrt/msvcrt.spec --- msvcrt-C00/dlls/msvcrt/msvcrt.spec Thu Nov 7 17:19:32 2002 +++ msvcrt-C01/dlls/msvcrt/msvcrt.spec Thu Nov 7 16:56:42 2002 @@ -556,7 +556,7 @@ @ stub _wstati64 #(wstr ptr) @ stub _wstrdate #(wstr) @ stub _wstrtime #(wstr) -@ stub _wsystem #(wstr) +@ cdecl _wsystem(wstr) MSVCRT__wsystem @ cdecl _wtempnam(wstr wstr) _wtempnam @ stub _wtmpnam #(wstr) @ forward -noimport _wtoi NTDLL._wtoi diff -aurN msvcrt-C00/dlls/msvcrt/process.c msvcrt-C01/dlls/msvcrt/process.c --- msvcrt-C00/dlls/msvcrt/process.c Thu Nov 7 17:14:55 2002 +++ msvcrt-C01/dlls/msvcrt/process.c Thu Nov 7 17:21:11 2002 @@ -5,6 +5,7 @@ * Copyright 1996 Jukka Iivonen * Copyright 1997,2000 Uwe Bonnes * Copyright 2000 Jon Griffiths + * Copyright 2002 Jaco Greeff * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,66 +37,163 @@ #include "msvcrt/stdlib.h" #include "msvcrt/string.h" +#include "wine/unicode.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); +static const WCHAR wszCOMSPEC[] = {'C','O','M','S','P','E','C', 0}; + /* 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) +/********************************************************************* + * WideStrToASCII + * (internal, non-exported) + * + * Wrapper to allocate enough memory and convert a LPCWSTR to a normal + * LPSTR + */ +static inline CHAR *WideToASCIIStr(LPCWSTR lpwszIn, INT nIn) { - STARTUPINFOA si; - PROCESS_INFORMATION pi; + INT nLen; + CHAR *szOut; - if (sizeof(HANDLE) != sizeof(int)) - WARN("This call is unsuitable for your architecture\n"); + if (!lpwszIn || !nIn) + return NULL; - if ((unsigned)flags > _P_DETACH) - { - *MSVCRT__errno() = MSVCRT_EINVAL; - return -1; - } + nLen = WideCharToMultiByte(CP_ACP, 0, lpwszIn, nIn, NULL, 0, NULL, NULL); + if ((szOut = (CHAR *)MSVCRT_malloc((nLen+1)*sizeof(CHAR)))) + { + WideCharToMultiByte(CP_ACP, 0, lpwszIn, nIn, szOut, nLen+1, NULL, NULL); + szOut[nLen] = '\0'; + } - FIXME(":must dup/kill streams for child process\n"); + return szOut; +} - 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; - } +/********************************************************************* + * ASCIIToWideStr + * (internal, non-exported) + * + * Wrapper to allocate enough memory and convert a LPCSTR to a normal + * LPWSTR + */ +static inline WCHAR *ASCIIToWideStr(LPCSTR lpszIn, INT nIn) +{ + INT nLen; + WCHAR *szOut; - 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 */ + if (!lpszIn || !nIn) + return NULL; + + nLen = MultiByteToWideChar(CP_ACP, 0, lpszIn, nIn, NULL, 0); + if ((szOut = (WCHAR *)MSVCRT_malloc((nLen+1)*sizeof(WCHAR)))) + { + MultiByteToWideChar(CP_ACP, 0, lpszIn, nIn, szOut, nLen+1); + szOut[nLen] = 0; + } + + return szOut; } + +/********************************************************************* + * MSVCRT_spawnW + * (internal non-exported) + * + * Spawn a child process using WCHAR arguments + * (This is a direct "port" of the original MSVCRT_spawnA + * function to support Unicode strings) Internally + * MSVCRT_spawnA now call MSVCRT_spawnAW) + */ +static int MSVCRT_spawnW(int nFlags, const WCHAR* wszExe, WCHAR *wszCmdline, WCHAR *wszEnv) +{ + STARTUPINFOW siStartup; + PROCESS_INFORMATION piInfo; + + TRACE("(nFlags == %d, wszExe == %s, wszCmdLine == %s, wszEnv == %s)\n", + nFlags, debugstr_w(wszExe), debugstr_w(wszCmdline), debugstr_w(wszEnv)); + + /* FIXME: this is not a problem at present, but might become + * so in the future when we move to 64 bit and support Win64 + */ + if (sizeof(HANDLE) != sizeof(int)) + WARN("This call is unsuitable for your architecture\n"); + + if ((unsigned)nFlags > _P_DETACH) + { + *MSVCRT__errno() = MSVCRT_EINVAL; + return -1; + } + + FIXME(":must dup/kill streams for child process\n"); + + memset(&siStartup, 0, sizeof(siStartup)); + siStartup.cb = sizeof(siStartup); + + if (!CreateProcessW(wszExe, wszCmdline, NULL, NULL, TRUE, + nFlags == _P_DETACH ? DETACHED_PROCESS : 0, + wszEnv, NULL, &siStartup, &piInfo)) + { + MSVCRT__set_errno(GetLastError()); + return -1; + } + + switch (nFlags) + { + case _P_WAIT: + WaitForSingleObject(piInfo.hProcess, -1); /* wait forvever */ + GetExitCodeProcess(piInfo.hProcess, &piInfo.dwProcessId); + CloseHandle(piInfo.hProcess); + CloseHandle(piInfo.hThread); + return (int)piInfo.dwProcessId; + + case _P_DETACH: + CloseHandle(piInfo.hProcess); + piInfo.hProcess = 0; + /* fall through */ + case _P_NOWAIT: + case _P_NOWAITO: + CloseHandle(piInfo.hThread); + return (int)piInfo.hProcess; + + case _P_OVERLAY: + MSVCRT__exit(0); + } + + return -1; /* can't reach here */ +} + + +/********************************************************************* + * MSVCRT_spawnA + * (internal non-exported) + */ +static int MSVCRT_spawnA(int nFlags, const char* szExe, char *szCmdline, char *szEnv) +{ + int nRet = 0; + + WCHAR *wszExe = ASCIIToWideStr(szExe, strlen(szExe)); + WCHAR *wszCmdline = ASCIIToWideStr(szCmdline, strlen(szCmdline)); + WCHAR *wszEnv = ASCIIToWideStr(szEnv, strlen(szEnv)); + nRet = MSVCRT_spawnW(nFlags, wszExe, wszCmdline, wszEnv); + if (wszExe) + MSVCRT_free(wszExe); + if (wszCmdline) + MSVCRT_free(wszCmdline); + if (wszEnv) + MSVCRT_free(wszEnv); + + return nRet; +} + + /* INTERNAL: Convert argv list to a single 'delim'-separated string, with an * extra '\0' to terminate it */ @@ -242,7 +340,7 @@ args = msvcrt_valisttos(arg0, ap, ' '); va_end(ap); - ret = msvcrt_spawn(_P_OVERLAY, name, args, NULL); + ret = MSVCRT_spawnA(_P_OVERLAY, name, args, NULL); MSVCRT_free(args); return ret; @@ -267,7 +365,7 @@ args = msvcrt_valisttos(arg0, ap, ' '); va_end(ap); - ret = msvcrt_spawn(_P_OVERLAY, fullname[0] ? fullname : name, args, NULL); + ret = MSVCRT_spawnA(_P_OVERLAY, fullname[0] ? fullname : name, args, NULL); MSVCRT_free(args); return ret; @@ -337,7 +435,7 @@ args = msvcrt_valisttos(arg0, ap, ' '); va_end(ap); - ret = msvcrt_spawn(flags, name, args, NULL); + ret = MSVCRT_spawnA(flags, name, args, NULL); MSVCRT_free(args); return ret; @@ -362,7 +460,7 @@ args = msvcrt_valisttos(arg0, ap, ' '); va_end(ap); - ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, NULL); + ret = MSVCRT_spawnA(flags, fullname[0] ? fullname : name, args, NULL); MSVCRT_free(args); return ret; @@ -387,7 +485,7 @@ if (args) { - ret = msvcrt_spawn(flags, fullname, args, envs); + ret = MSVCRT_spawnA(flags, fullname, args, envs); MSVCRT_free(args); } if (envs) @@ -433,19 +531,58 @@ } /********************************************************************* - * system (MSVCRT.@) + * _wsystem (MSVCRT.@) */ -int MSVCRT_system(const char* cmd) +int MSVCRT__wsystem(const WCHAR *wszCmd) { - char* cmdcopy; - int res; + static const WCHAR wszExec[] = {' ','/','c',' ', 0}; + WCHAR wszComspec[1024+1]; + WCHAR *wszCmdcopy; + int res, nLen; + + /* Get the value of COMSPEC which should point to our + * default command interpreter. If the interpreter is + * not available, fail. + */ + if (!GetEnvironmentVariableW(wszCOMSPEC, wszComspec, 1024)) + { + TRACE("COMSPEC not available, _wsystem call fails\n"); + return -1; + } + + /* Allocate enough memory to hold the interpreter + command + * string and set it. When using the command interpreter in + * this way, it is executed as "comspec /c command" + */ + nLen = strlenW(wszComspec)+strlenW(wszExec)+strlenW(wszCmd); + if (!(wszCmdcopy = (WCHAR *)MSVCRT_malloc((nLen+1)*sizeof(WCHAR)))) + { + TRACE("Unable to allocate memory for _wsystem call\n"); + return -1; + } + wszCmdcopy[0] = 0; + strcatW(wszCmdcopy, wszComspec); + strcatW(wszCmdcopy, wszExec); + strcatW(wszCmdcopy, wszCmd); + res = MSVCRT_spawnW(_P_WAIT, NULL, wszCmdcopy, NULL); + MSVCRT_free(wszCmdcopy); - /* 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; +} + +/********************************************************************* + * system (MSVCRT.@) + */ +int MSVCRT_system(const char *szCmd) +{ + int nRet = 0; + + WCHAR *wszCmd = ASCIIToWideStr(szCmd, strlen(szCmd)); + nRet = MSVCRT__wsystem(wszCmd); + if (wszCmd) + MSVCRT_free(wszCmd); + + return nRet; } /*********************************************************************