From: Eric Pouech epouech@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55835
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/data.c | 321 ++++++++++++++++++++++++++++-------- dlls/msvcrt/environ.c | 19 +-- dlls/msvcrt/msvcrt.h | 5 +- dlls/msvcrt/tests/environ.c | 11 -- 4 files changed, 257 insertions(+), 99 deletions(-)
diff --git a/dlls/msvcrt/data.c b/dlls/msvcrt/data.c index d1f514fd460..0bf22c76664 100644 --- a/dlls/msvcrt/data.c +++ b/dlls/msvcrt/data.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <stdio.h> #include <fcntl.h> #include <math.h> #include "msvcrt.h" @@ -52,6 +53,9 @@ wchar_t **MSVCRT___wargv = NULL; static wchar_t **wargv_expand; char *MSVCRT__acmdln = NULL; wchar_t *MSVCRT__wcmdln = NULL; +/* The entries in in MSVCRT__environ[] and in MSVCRT__wenviron[] must be + * kept in sync. + */ char **MSVCRT__environ = NULL; wchar_t **MSVCRT__wenviron = NULL; char **MSVCRT___initenv = NULL; @@ -60,80 +64,229 @@ int MSVCRT_app_type = 0; char* MSVCRT__pgmptr = NULL; WCHAR* MSVCRT__wpgmptr = NULL;
-/* Get a snapshot of the current environment - * and construct the __p__environ array - * - * The pointer returned from GetEnvironmentStrings may get invalid when - * some other module cause a reallocation of the env-variable block - * - * blk is an array of pointers to environment strings, ending with a NULL - * and after that the actual copy of the environment strings, ending in a \0 - */ -char ** msvcrt_SnapshotOfEnvironmentA(char **blk) +static char **env_snapshot( void ) { - char* environ_strings = GetEnvironmentStringsA(); - int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */ - char *ptr; + char *environ_strings = GetEnvironmentStringsA(); + int count = 1; /* keep space for the trailing NULL */ + size_t size = 1; + char *ptr; + char **blk;
- for (ptr = environ_strings; *ptr; ptr += strlen(ptr) + 1) - { - /* Don't count environment variables starting with '=' which are command shell specific */ - if (*ptr != '=') count++; - len += strlen(ptr) + 1; - } - if (blk) - blk = HeapReAlloc( GetProcessHeap(), 0, blk, count* sizeof(char*) + len ); - else - blk = HeapAlloc(GetProcessHeap(), 0, count* sizeof(char*) + len ); + for (ptr = environ_strings; *ptr; ptr += strlen( ptr ) + 1) + { + /* Don't count environment variables starting with '=' which are command shell specific */ + if (*ptr != '=') + { + count++; + size += strlen( ptr ) + 1; + } + } + if ((blk = HeapAlloc( GetProcessHeap(), 0, count * sizeof(char *) + size * sizeof(wchar_t) ))) + { + int i = 0; + char* ptr_blk = (char *)(blk + count); + size_t len; + for (ptr = environ_strings; *ptr; ptr += len + 1) + { + len = strlen( ptr ); + /* Skip special environment strings set by the command shell */ + if (*ptr != '=') + { + strcpy( ptr_blk, ptr ); + blk[i] = ptr_blk; + ptr_blk += len + 1; + i++; + } + } + *ptr_blk = '\0'; + blk[i] = NULL; + } + FreeEnvironmentStringsA( environ_strings ); + return blk; +}
- if (blk) +static char **env_cloneA( char** env ) +{ + int i; + char **blk; + + for (i = 0; env[i]; i++) {} + if ((blk = HeapAlloc( GetProcessHeap(), 0, (i + 1) * sizeof(char *) ))) { - if (count) - { - memcpy(&blk[count],environ_strings,len); - for (ptr = (char*) &blk[count]; *ptr; ptr += strlen(ptr) + 1) - { - /* Skip special environment strings set by the command shell */ - if (*ptr != '=') blk[i++] = ptr; - } - } - blk[i] = NULL; + for (i = 0; env[i]; i++) + blk[i] = strdup( env[i] ); + blk[i] = NULL; } - FreeEnvironmentStringsA(environ_strings); - return blk; + return blk; }
-wchar_t ** msvcrt_SnapshotOfEnvironmentW(wchar_t **wblk) +int msvcrt_update_environmentA( const char *name, const char *value ) { - wchar_t* wenviron_strings = GetEnvironmentStringsW(); - int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */ - wchar_t *wptr; + int i = 0; + size_t len = strlen( name ); + char *set; + char **new;
- for (wptr = wenviron_strings; *wptr; wptr += wcslen(wptr) + 1) - { - /* Don't count environment variables starting with '=' which are command shell specific */ - if (*wptr != '=') count++; - len += wcslen(wptr) + 1; - } - if (wblk) - wblk = HeapReAlloc( GetProcessHeap(), 0, wblk, count* sizeof(wchar_t*) + len * sizeof(wchar_t)); - else - wblk = HeapAlloc(GetProcessHeap(), 0, count* sizeof(wchar_t*) + len * sizeof(wchar_t)); - if (wblk) + if (MSVCRT__environ == MSVCRT___initenv) + MSVCRT__environ = env_cloneA(MSVCRT___initenv); + + if (value[0]) { - if (count) - { - memcpy(&wblk[count],wenviron_strings,len * sizeof(wchar_t)); - for (wptr = (wchar_t*)&wblk[count]; *wptr; wptr += wcslen(wptr) + 1) - { - /* Skip special environment strings set by the command shell */ - if (*wptr != '=') wblk[i++] = wptr; - } - } - wblk[i] = NULL; + size_t sz = strlen( name ) + 1 + strlen( value ) + 1; + set = malloc( sz ); + if (!set) return -1; + snprintf( set, sz, "%s=%s", name, value ); } - FreeEnvironmentStringsW(wenviron_strings); - return wblk; + else set = NULL; + + for (i = 0; MSVCRT__environ[i] != NULL; i++) + { + if (!memcmp( name, MSVCRT__environ[i], len ) && MSVCRT__environ[i][len] == '=') + { + free( MSVCRT__environ[i] ); + if (MSVCRT__wenviron) free( MSVCRT__wenviron[i] ); + if (set) + { + MSVCRT__environ[i] = set; + if (MSVCRT__wenviron) MSVCRT__wenviron[i] = msvcrt_wstrdupa( set ); + } + else + { + do + { + MSVCRT__environ[i] = MSVCRT__environ[i + 1]; + if (MSVCRT__wenviron) MSVCRT__wenviron[i] = MSVCRT__wenviron[i + 1]; + } while (MSVCRT__environ[++i] != NULL); + } + return 0; + } + } + if (!set) return 0; + new = HeapReAlloc( GetProcessHeap(), 0, MSVCRT__environ, (i + 2) * sizeof(char *) ); + if (!new) + { + free(set); + return -1; + } + if (MSVCRT__wenviron) + { + WCHAR** wnew = HeapReAlloc( GetProcessHeap(), 0, MSVCRT__wenviron, (i + 2) * sizeof(wchar_t *) ); + if (!wnew) + { + HeapFree( GetProcessHeap(), 0, new ); + free( set ); + return -1; + } + MSVCRT__wenviron = wnew; + } + MSVCRT__environ = new; + MSVCRT__environ[i] = set; + MSVCRT__environ[i + 1] = NULL; + if (MSVCRT__wenviron) + { + MSVCRT__wenviron[i] = msvcrt_wstrdupa( set ); + MSVCRT__wenviron[i + 1] = NULL; + } + return 0; +} + +wchar_t **msvcrt_clone_environmentA2W( char **env ) +{ + int count; + wchar_t **ret; + + for (count = 0; env[count] != NULL; count++) {} + if ((ret = HeapAlloc( GetProcessHeap(), 0, (count + 1) * sizeof(wchar_t *))) ) + { + for (count = 0; env[count] != NULL; count++) + { + ret[count] = msvcrt_wstrdupa( env[count] ); + } + ret[count] = NULL; + } + return ret; +} + +/* INTERNAL: Create an ascii string from a wide string */ +static char *msvcrt_astrdupw( const wchar_t *wstr ) +{ + const unsigned int len = WideCharToMultiByte( CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL ); + char *str = malloc( len * sizeof(char) ); + if (!str) + return NULL; + WideCharToMultiByte( CP_ACP, 0, wstr, -1, str, len, NULL, NULL ); + return str; +} + +int msvcrt_update_environmentW( const wchar_t *name, const wchar_t *value ) +{ + int i = 0; + size_t len = wcslen( name ); + wchar_t *set; + wchar_t **new; + char** anew; + + if (MSVCRT__environ == MSVCRT___initenv) + MSVCRT__environ = env_cloneA(MSVCRT___initenv); + if (!MSVCRT__wenviron) + { + /* yes that looks strange, but it's how native does it */ + if (!MSVCRT___winitenv) MSVCRT___winitenv = msvcrt_clone_environmentA2W( MSVCRT__environ ); + MSVCRT__wenviron = msvcrt_clone_environmentA2W( MSVCRT__environ ); + } + + if (value[0]) + { + size_t sz = wcslen( name ) + 1 + wcslen( value ) + 1; + set = malloc( sz * sizeof(wchar_t) ); + if (!set) return -1; + swprintf(set, sz, L"%s=%s", name, value); + } + else set = NULL; + + for (i = 0; MSVCRT__wenviron[i] != NULL; i++) + { + if (!memcmp(name, MSVCRT__wenviron[i], len * sizeof(wchar_t)) && MSVCRT__wenviron[i][len] == L'=') + { + free(MSVCRT__environ[i]); + free(MSVCRT__wenviron[i]); + if (set) + { + MSVCRT__environ[i] = msvcrt_astrdupw( set ); + MSVCRT__wenviron[i] = set; + } + else + { + do + { + MSVCRT__environ[i] = MSVCRT__environ[i + 1]; + MSVCRT__wenviron[i] = MSVCRT__wenviron[i + 1]; + } while (MSVCRT__environ[++i] != NULL); + } + return 0; + } + } + if (!set) return 0; + new = HeapReAlloc( GetProcessHeap(), 0, MSVCRT__wenviron, (i + 2) * sizeof(wchar_t *) ); + if (!new) + { + free( set ); + return -1; + } + anew = HeapReAlloc( GetProcessHeap(), 0, MSVCRT__environ, (i + 2) * sizeof(char *) ); + if (!anew) + { + HeapFree( GetProcessHeap(), 0, new ); + free(set); + return -1; + } + MSVCRT__environ = anew; + MSVCRT__wenviron = new; + MSVCRT__environ[i] = msvcrt_astrdupw( set ); + MSVCRT__wenviron[i] = set; + MSVCRT__environ[i + 1] = NULL; + MSVCRT__wenviron[i + 1] = NULL; + return 0; }
static char **build_argv( WCHAR **wargv ) @@ -438,9 +591,7 @@ void msvcrt_init_args(void) MSVCRT___unguarded_readlc_active = 0; MSVCRT__fmode = _O_TEXT;
- MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(NULL); - MSVCRT___initenv = msvcrt_SnapshotOfEnvironmentA(NULL); - MSVCRT___winitenv = msvcrt_SnapshotOfEnvironmentW(NULL); + MSVCRT___initenv = MSVCRT__environ = env_snapshot();
MSVCRT__pgmptr = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); if (MSVCRT__pgmptr) @@ -461,15 +612,35 @@ void msvcrt_init_args(void) } }
+static void env_freeA(char** env) +{ + int i; + for (i = 0; env[i] != NULL; i++) + free( env[i] ); + HeapFree( GetProcessHeap(), 0, env ); +} + +static void env_freeW(wchar_t** wenv) +{ + int i; + for (i = 0; wenv[i] != NULL; i++) + free( wenv[i] ); + HeapFree( GetProcessHeap(), 0, wenv ); +} + /* INTERNAL: free memory used by args */ void msvcrt_free_args(void) { /* FIXME: more things to free */ HeapFree(GetProcessHeap(), 0, MSVCRT___argv); - HeapFree(GetProcessHeap(), 0, MSVCRT___initenv); - HeapFree(GetProcessHeap(), 0, MSVCRT___winitenv); - HeapFree(GetProcessHeap(), 0, MSVCRT__environ); - HeapFree(GetProcessHeap(), 0, MSVCRT__wenviron); + + if (MSVCRT__environ != MSVCRT___initenv) + env_freeA( MSVCRT__environ ); + HeapFree( GetProcessHeap(), 0, MSVCRT___initenv ); + if (MSVCRT__wenviron && MSVCRT__wenviron != MSVCRT___winitenv) + env_freeW( MSVCRT__wenviron ); + HeapFree( GetProcessHeap(), 0, MSVCRT___winitenv ); + HeapFree(GetProcessHeap(), 0, MSVCRT__pgmptr); HeapFree(GetProcessHeap(), 0, MSVCRT__wpgmptr); HeapFree(GetProcessHeap(), 0, wargv_expand); @@ -565,10 +736,14 @@ int CDECL __wgetmainargs(int *argc, wchar_t** *wargv, wchar_t** *wenvp,
/* Initialize the _wenviron array if it's not already created. */ if (!MSVCRT__wenviron) - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(NULL); + { + /* yes that looks strange, but it's how native does it */ + if (!MSVCRT___winitenv) MSVCRT___winitenv = msvcrt_clone_environmentA2W( MSVCRT__environ ); + MSVCRT__wenviron = msvcrt_clone_environmentA2W( MSVCRT__environ ); + } *argc = MSVCRT___argc; *wargv = MSVCRT___wargv; - *wenvp = MSVCRT___winitenv; + *wenvp = MSVCRT__wenviron; if (new_mode) _set_new_mode( *new_mode ); return 0; @@ -602,7 +777,7 @@ int CDECL __getmainargs(int *argc, char** *argv, char** *envp,
*argc = MSVCRT___argc; *argv = MSVCRT___argv; - *envp = MSVCRT___initenv; + *envp = MSVCRT__environ;
if (new_mode) _set_new_mode( *new_mode ); diff --git a/dlls/msvcrt/environ.c b/dlls/msvcrt/environ.c index 2c2a3353582..94d67982e19 100644 --- a/dlls/msvcrt/environ.c +++ b/dlls/msvcrt/environ.c @@ -66,7 +66,7 @@ static wchar_t * wgetenv_helper(const wchar_t *name)
/* Initialize the _wenviron array if it's not already created. */ if (!MSVCRT__wenviron) - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(NULL); + MSVCRT__wenviron = msvcrt_clone_environmentA2W(MSVCRT__environ);
for (env = MSVCRT__wenviron; *env; env++) { @@ -127,11 +127,8 @@ int CDECL _putenv(const char *str) /* _putenv returns success on deletion of nonexistent variable, unlike [Rtl]SetEnvironmentVariable */ if ((ret == -1) && (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) ret = 0;
- MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(MSVCRT__environ); - /* Update the __p__wenviron array only when already initialized */ - if (MSVCRT__wenviron) - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(MSVCRT__wenviron); - + if (ret == 0) ret = msvcrt_update_environmentA(name, value); + finish: HeapFree(GetProcessHeap(), 0, name); return ret; @@ -172,8 +169,7 @@ int CDECL _wputenv(const wchar_t *str) /* _putenv returns success on deletion of nonexistent variable, unlike [Rtl]SetEnvironmentVariable */ if ((ret == -1) && (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) ret = 0;
- MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(MSVCRT__environ); - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(MSVCRT__wenviron); + if (ret == 0) ret = msvcrt_update_environmentW(name, value);
finish: HeapFree(GetProcessHeap(), 0, name); @@ -201,9 +197,7 @@ errno_t CDECL _putenv_s(const char *name, const char *value) ret = *_errno(); } } - - MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(MSVCRT__environ); - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(MSVCRT__wenviron); + if (ret == 0) ret = msvcrt_update_environmentA(name, value);
return ret; } @@ -230,8 +224,7 @@ errno_t CDECL _wputenv_s(const wchar_t *name, const wchar_t *value) } }
- MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(MSVCRT__environ); - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(MSVCRT__wenviron); + if (ret == 0) ret = msvcrt_update_environmentW(name, value);
return ret; } diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h index 7c2dd4835f5..d6ddf6812ab 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -206,8 +206,9 @@ void __cdecl _amsg_exit(int errnum); extern char **MSVCRT__environ; extern wchar_t **MSVCRT__wenviron;
-extern char ** msvcrt_SnapshotOfEnvironmentA(char **); -extern wchar_t ** msvcrt_SnapshotOfEnvironmentW(wchar_t **); +int msvcrt_update_environmentA(const char *name, const char *value); +int msvcrt_update_environmentW(const wchar_t *name, const wchar_t * value); +wchar_t **msvcrt_clone_environmentA2W(char**);
wchar_t *msvcrt_wstrdupa(const char *);
diff --git a/dlls/msvcrt/tests/environ.c b/dlls/msvcrt/tests/environ.c index 3288c5ab99c..e97ab8d0c3a 100644 --- a/dlls/msvcrt/tests/environ.c +++ b/dlls/msvcrt/tests/environ.c @@ -150,7 +150,6 @@ static void test__environ(BOOL first_time)
if (first_time) { - todo_wine ok( *retptr == *p_environ, "Expected _environ to be equal to initial env\n" ); prev_initenv = *retptr; @@ -172,7 +171,6 @@ static void test__environ(BOOL first_time)
ok( envp != NULL, "Expected initial environment block pointer to be non-NULL\n" ); - todo_wine ok( envp == *p_environ, "Expected initial environment to be equal to _environ\n"); winetest_pop_context(); @@ -221,11 +219,8 @@ static void test__wenviron(BOOL first_time) { wchar_t ***retptr = p__p___winitenv(); if (first_time) - { - todo_wine ok( *retptr == NULL, "Expected initial env to be NULL\n" ); - } } else skip( "__p___winitenv() is not available\n" ); @@ -257,7 +252,6 @@ static void test__wenviron(BOOL first_time)
ok( *p_wenviron != NULL, "Expected _wenviron to be non-NULL\n" ); ok( wenvp != NULL, "Expected initial environment block pointer to be non-NULL\n" ); - todo_wine ok( wenvp == *p_wenviron, "Expected initial environment to be _wenviron[]\n");
if (p__p___winitenv) @@ -271,7 +265,6 @@ static void test__wenviron(BOOL first_time) "Expected _wenviron to be different from __p___winitenv() %p %p\n", *retptr, *p_wenviron ); /* test that w-initial env is derived from current _environ[] and not from ansi initial env */ value = env_get_valueW( *retptr, L"cat" ); - todo_wine ok( value && !wcscmp( value, L"dog" ), "Expecting initial env to be derived from current env (got %ls)\n", value);
if (first_time) @@ -386,7 +379,6 @@ static void test_environment_manipulation(void) ok( _putenv( "__winetest_dog=bark" ) == 0, "Couldn't set env var\n" ); ok( !strcmp( (*p_environ)[count + 1], "__winetest_dog=bark" ), "Unexpected env var value\n" ); ok( getenv( "__winetest_dog" ) == strchr( (*p_environ)[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" ); - todo_wine ok( first == (*p_environ)[count], "Expected stability of _environ[count] pointer\n" ); second = (*p_environ)[count + 1]; ok( count + 2 == env_get_entry_countA( *p_environ ), "Unexpected count\n" ); @@ -394,13 +386,11 @@ static void test_environment_manipulation(void) ok( _putenv( "__winetest_cat=purr" ) == 0, "Couldn't set env var\n" ); ok( !strcmp( (*p_environ)[count], "__winetest_cat=purr" ), "Unexpected env var value\n" ); ok( getenv( "__winetest_cat" ) == strchr( (*p_environ)[count], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" ); - todo_wine ok( second == (*p_environ)[count + 1], "Expected stability of _environ[count] pointer\n" ); ok( !strcmp( (*p_environ)[count + 1], "__winetest_dog=bark" ), "Couldn't get env var value\n" ); ok( getenv( "__winetest_dog" ) == strchr( (*p_environ)[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" ); ok( count + 2 == env_get_entry_countA( *p_environ ), "Unexpected count\n" ); ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env vat\n" ); - todo_wine ok( second == (*p_environ)[count], "Expected _environ[count] to be second\n" ); ok( !strcmp( (*p_environ)[count], "__winetest_dog=bark" ), "Unexpected env var value\n" ); ok( count + 1 == env_get_entry_countA( *p_environ ), "Unexpected count\n" ); @@ -413,7 +403,6 @@ static void test_environment_manipulation(void) ret = SetEnvironmentVariableA( "__winetest_duck", "quack" ); ok( ret, "SetEnvironmentVariableA failed: %lu\n", GetLastError() ); ok( _putenv( "__winetest_elephant=trumpet" ) == 0, "Couldn't set env var\n" ); - todo_wine ok( getenv( "__winetest_duck" ) == NULL, "msvcrt env cache shouldn't have been updated\n" ); ok( _putenv( "__winetest_duck=" ) == 0, "Couldn't reset env var\n" ); ok( _putenv( "__winetest_elephant=" ) == 0, "Couldn't reset env var\n" );