This serie: - adds a couple of msvcrt env tests (mainly env block inheritance) - introduces env tests for ucrtbase - reimplement most of the env related functions to mimic native behavior (basically when env is modified, native crt uses a per-variable entry allocation block (instead of having pointers inside a single chunk of memory). This fixes programs which keep a pointer on returned getenv() string and expect that pointer content is not modified when other variables in env are modified.
@piotr: from previous version - I dropped the synchronization of _environ and _wenviron hence requiring the duplication of most of the code in ansi/unicode versions - I also tested the changes with unicode crt startup code (you can find in [1] this MR extended with this test code but I don't think we want to push it upstream) - there are some init code changes because of ucrtbase tests and unicode crt startup tests
[1] https://gitlab.winehq.org/epo/wine/-/tree/mr-crt-env-full?ref_type=heads
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/tests/environ.c | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+)
diff --git a/dlls/msvcrt/tests/environ.c b/dlls/msvcrt/tests/environ.c index f14764ea5a0..c0a392f9f9d 100644 --- a/dlls/msvcrt/tests/environ.c +++ b/dlls/msvcrt/tests/environ.c @@ -393,12 +393,75 @@ static void test_environment_manipulation(void) ok( count == env_get_entry_countA( *p_environ ), "Unexpected modification of _environ[]\n" ); }
+static void test_child_env(char** argv) +{ + static char env[4096]; + STARTUPINFOA si = {sizeof(si)}; + PROCESS_INFORMATION pi; + char tmp[1024]; + BOOL ret; + char *ptr; + WCHAR *wptr; + + ptr = env; +#define PUSHA( x, y ) do { if (y) { strcpy( ptr, (x) ); ptr += strlen( x ); *ptr++ = '='; strcpy( ptr, (y) ); ptr += strlen( y ) + 1; } } while (0) +#define PUSHENVA( x ) PUSHA( ( x ), getenv( x )) + PUSHA( "__winetest_child_dog", "bark" ); + PUSHA( "__winetest_child_cat", "meow" ); + PUSHENVA( "WINETEST_COLOR" ); + *ptr = '\0'; +#undef PUSHA +#undef PUSHENVA + + snprintf( tmp, sizeof(tmp), "%s %s create", argv[0], argv[1] ); + ret = CreateProcessA( NULL, tmp, NULL, NULL, FALSE, 0, env, NULL, &si, &pi ); + ok( ret, "Couldn't create child process %s\n", tmp ); + winetest_wait_child_process( pi.hProcess ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread); + + wptr = (void *)env; +#define PUSHW( x, y ) do { if (y) { wcscpy( wptr, (x) ); wptr += wcslen( x ); *wptr++ = L'='; wcscpy( wptr, (y) ); wptr += wcslen( y ) + 1; } } while (0) +#define PUSHENVW( x ) PUSHW( ( x ), _wgetenv( x )) + PUSHW( L"__winetest_child_dog", L"bark" ); + PUSHW( L"__winetest_child_cat", L"meow" ); + PUSHW( L"__winetest_child_\u03b1", L"\u03b2" ); /* alpha = beta */ + PUSHENVW( L"WINETEST_COLOR" ); + *wptr = L'\0'; +#undef PUSHW +#undef PUSHENVW + + snprintf( tmp, sizeof(tmp), "%s %s create /unicode", argv[0], argv[1] ); + ret = CreateProcessA( NULL, tmp, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, env, NULL, &si, &pi ); + ok( ret, "Couldn't create child process %s\n", tmp ); + winetest_wait_child_process( pi.hProcess ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); +} + START_TEST(environ) { + char **argv; + int argc; + init();
+ argc = winetest_get_mainargs( &argv ); + if (argc >= 3 && !strcmp( argv[2], "create" )) + { + ok( getenv( "__winetest_child_dog" ) && !strcmp( getenv( "__winetest_child_dog" ), "bark" ), + "Couldn't find env var\n" ); + if (argc >= 4) /* with unicode */ + { + ok( _wgetenv( L"__winetest_child_\u03b1" ) && !wcscmp( _wgetenv(L"__winetest_child_\u03b1" ), L"\u03b2" ), + "Couldn't find alpha\n" ); + } + return; + } + test__environ(); test__wenviron(); test_environment_manipulation(); + test_child_env(argv); test_system(); }
From: Eric Pouech epouech@codeweavers.com
Mostly adapting msvcrt tests/environ.c.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/ucrtbase/tests/Makefile.in | 1 + dlls/ucrtbase/tests/environ.c | 321 ++++++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 dlls/ucrtbase/tests/environ.c
diff --git a/dlls/ucrtbase/tests/Makefile.in b/dlls/ucrtbase/tests/Makefile.in index 4df5684290c..cd01a3aacb9 100644 --- a/dlls/ucrtbase/tests/Makefile.in +++ b/dlls/ucrtbase/tests/Makefile.in @@ -4,6 +4,7 @@ EXTRADEFS = -fno-builtin
SOURCES = \ cpp.c \ + environ.c \ misc.c \ printf.c \ scanf.c \ diff --git a/dlls/ucrtbase/tests/environ.c b/dlls/ucrtbase/tests/environ.c new file mode 100644 index 00000000000..3f10048d5a8 --- /dev/null +++ b/dlls/ucrtbase/tests/environ.c @@ -0,0 +1,321 @@ +/* + * Unit tests for C library environment routines + * + * Copyright 2004 Mike Hearn mh@codeweavers.com + * + * 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 + */ + +#include "wine/test.h" +#include <errno.h> +#include <stdlib.h> +#include <process.h> +#include <winnls.h> + +static const char *a_very_long_env_string = + "LIBRARY_PATH=" + "C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/;" + "C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/;" + "/mingw/lib/gcc/mingw32/3.4.2/;" + "/usr/lib/gcc/mingw32/3.4.2/;" + "C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../../mingw32/lib/mingw32/3.4.2/;" + "C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../../mingw32/lib/;" + "/mingw/mingw32/lib/mingw32/3.4.2/;" + "/mingw/mingw32/lib/;" + "/mingw/lib/mingw32/3.4.2/;" + "/mingw/lib/;" + "C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../mingw32/3.4.2/;" + "C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../;" + "/mingw/lib/mingw32/3.4.2/;" + "/mingw/lib/;" + "/lib/mingw32/3.4.2/;" + "/lib/;" + "/usr/lib/mingw32/3.4.2/;" + "/usr/lib/"; + +static char ***(__cdecl *p__p__environ)(void); +static WCHAR ***(__cdecl *p__p__wenviron)(void); +static char** (__cdecl *p_get_initial_narrow_environment)(void); +static wchar_t** (__cdecl *p_get_initial_wide_environment)(void); +static errno_t (__cdecl *p_putenv_s)(const char*, const char*); +static errno_t (__cdecl *p_wputenv_s)(const wchar_t*, const wchar_t*); +static errno_t (__cdecl *p_getenv_s)(size_t*, char*, size_t, const char*); + +static char ***p_environ; +static WCHAR ***p_wenviron; + +static void __cdecl test_invalid_parameter_handler(const wchar_t *expression, + const wchar_t *function, const wchar_t *file, + unsigned line, uintptr_t arg) +{ +} + +static BOOL init(void) +{ + HMODULE hmod = GetModuleHandleA( "ucrtbase.dll" ); + + p__p__environ = (void *)GetProcAddress( hmod, "__p__environ" ); + p__p__wenviron = (void *)GetProcAddress( hmod, "__p__wenviron" ); + p_get_initial_narrow_environment = (void *)GetProcAddress( hmod, "_get_initial_narrow_environment" ); + p_get_initial_wide_environment = (void *)GetProcAddress( hmod, "_get_initial_wide_environment" ); + p_putenv_s = (void *)GetProcAddress( hmod, "_putenv_s" ); + p_wputenv_s = (void *)GetProcAddress( hmod, "_wputenv_s" ); + p_getenv_s = (void *)GetProcAddress( hmod, "getenv_s" ); + + ok(p__p__environ != NULL, "Unexecped NULL pointer to environ\n" ); + ok(p__p__wenviron != NULL, "Unexecped NULL pointer to environ\n" ); + if (!p__p__environ || !p__p__wenviron) + { + skip( "NULL pointers for environment\n" ); + return FALSE; + } + p_environ = p__p__environ(); + p_wenviron = p__p__wenviron(); + return TRUE; +} + +static unsigned env_get_entry_countA( char **env ) +{ + unsigned count; + + if (!env) return 0; + for (count = 0; env[count] != NULL; count++) {} + return count; +} + +static void test_initial_environ( void ) +{ + ok( p__p__environ() != NULL, "Unexpected NULL _environ[]\n" ); + ok( *p__p__environ() != NULL, "Unexpected empty _environ[]\n" ); + ok( p_get_initial_narrow_environment() != NULL, "Unexpected empty narrow initial environment\n" ); + todo_wine + ok( p_get_initial_narrow_environment() == *p__p__environ(), "Expecting _environ[] to match initial narrow environment\n" ); + + ok( p__p__wenviron() != NULL, "Unexpected NULL _wenviron[]\n" ); + ok( *p__p__wenviron() == NULL, "Unexpected non empty _wenviron[]\n" ); + ok( p_get_initial_wide_environment() != NULL, "Unexpected empty wide initial environment\n" ); + todo_wine + ok( p_get_initial_wide_environment() == *p__p__wenviron(), "Expecting _wenviron[] to match initial wide environment\n" ); +} + +static void test_environment_manipulation(void) +{ + char buf[256]; + errno_t ret; + size_t len; + unsigned count; + char* first; + char* second; + + ok( _putenv( "cat=" ) == 0, "_putenv failed on deletion of nonexistent environment variable\n" ); + ok( _putenv( "cat=dog" ) == 0, "failed setting cat=dog\n" ); + ok( strcmp( getenv( "cat" ), "dog" ) == 0, "getenv did not return 'dog'\n" ); + if (p_getenv_s) + { + ret = p_getenv_s( &len, buf, sizeof(buf), "cat" ); + ok( !ret, "getenv_s returned %d\n", ret ); + ok( len == 4, "getenv_s returned length is %Id\n", len); + ok( !strcmp(buf, "dog"), "getenv_s did not return 'dog'\n" ); + } + ok( _putenv("cat=") == 0, "failed deleting cat\n" ); + + ok( _putenv("=") == -1, "should not accept '=' as input\n" ); + ok( _putenv("=dog") == -1, "should not accept '=dog' as input\n" ); + ok( _putenv(a_very_long_env_string) == 0, "_putenv failed for long environment string\n" ); + + ok( getenv("nonexistent") == NULL, "getenv should fail with nonexistent var name\n" ); + + if (p_putenv_s) + { + ret = p_putenv_s( NULL, "dog" ); + ok( ret == EINVAL, "_putenv_s returned %d\n", ret ); + ret = p_putenv_s( "cat", NULL ); + ok( ret == EINVAL, "_putenv_s returned %d\n", ret ); + ret = p_putenv_s( "a=b", NULL ); + ok( ret == EINVAL, "_putenv_s returned %d\n", ret ); + ret = p_putenv_s( "cat", "a=b" ); + ok( !ret, "_putenv_s returned %d\n", ret ); + ret = p_putenv_s( "cat", "" ); + ok( !ret, "_putenv_s returned %d\n", ret ); + } + + if (p_wputenv_s) + { + ret = p_wputenv_s( NULL, L"dog" ); + ok( ret == EINVAL, "_wputenv_s returned %d\n", ret ); + ret = p_wputenv_s( L"cat", NULL ); + ok( ret == EINVAL, "_wputenv_s returned %d\n", ret ); + ret = p_wputenv_s( L"a=b", NULL ); + ok( ret == EINVAL, "_wputenv_s returned %d\n", ret ); + ret = p_wputenv_s( L"cat", L"a=b" ); + ok( !ret, "_wputenv_s returned %d\n", ret ); + ret = p_wputenv_s( L"cat", L"" ); + ok( !ret, "_wputenv_s returned %d\n", ret ); + } + + if (p_getenv_s) + { + buf[0] = 'x'; + len = 1; + errno = 0xdeadbeef; + ret = p_getenv_s( &len, buf, sizeof(buf), "nonexistent" ); + ok( !ret, "_getenv_s returned %d\n", ret ); + ok( !len, "getenv_s returned length is %Id\n", len ); + ok( !buf[0], "buf = %s\n", buf ); + ok( errno == 0xdeadbeef, "errno = %d\n", errno ); + + buf[0] = 'x'; + len = 1; + errno = 0xdeadbeef; + ret = p_getenv_s( &len, buf, sizeof(buf), NULL ); + ok( !ret, "_getenv_s returned %d\n", ret ); + ok( !len, "getenv_s returned length is %Id\n", len ); + ok( !buf[0], "buf = %s\n", buf ); + ok( errno == 0xdeadbeef, "errno = %d\n", errno ); + } + + /* test stability of _environ[] pointers */ + ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env var\n" ); + ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" ); + count = env_get_entry_countA( *p_environ ); + ok( _putenv( "__winetest_cat=mew") == 0, "Couldn't set env var\n" ); + ok( !strcmp( (*p_environ)[count], "__winetest_cat=mew"), "Unexpected env var value\n" ); + first = (*p_environ)[count]; + ok( getenv("__winetest_cat") == strchr( (*p_environ)[count], '=') + 1, "Expected getenv() to return pointer inside _environ[] entry\n" ); + 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" ); + + 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" ); + ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" ); + ok( count == env_get_entry_countA( *p_environ ), "Unexpected count\n" ); + + /* in putenv, only changed variable is updated (no other reload of kernel info is done) */ + ret = SetEnvironmentVariableA( "__winetest_cat", "meow" ); + ok( ret, "SetEnvironmentVariableA failed: %lu\n", GetLastError() ); + ok( _putenv( "__winetest_dog=bark" ) == 0, "Couldn't set env var\n" ); + todo_wine + ok( getenv( "__winetest_cat" ) == NULL, "msvcrt env cache shouldn't have been updated\n" ); + ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env var\n" ); + ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" ); + + /* test setting unicode bits */ + count = env_get_entry_countA( *p_environ ); + ret = WideCharToMultiByte( CP_ACP, 0, L"\u263a", -1, buf, ARRAY_SIZE(buf), 0, 0 ); + ok( ret, "WideCharToMultiByte failed: %lu\n", GetLastError() ); + ok( _wputenv( L"__winetest_cat=\u263a" ) == 0, "Couldn't set env var\n" ); + ok( _wgetenv( L"__winetest_cat" ) && !wcscmp( _wgetenv( L"__winetest_cat" ), L"\u263a" ), "Couldn't retrieve env var\n" ); + ok( getenv( "__winetest_cat" ) && !strcmp( getenv( "__winetest_cat" ), buf ), "Couldn't retrieve env var\n" ); + ok( _wputenv( L"__winetest_cat=" ) == 0, "Couldn't reset env var\n" ); + + ret = WideCharToMultiByte( CP_ACP, 0, L"__winetest_\u263a", -1, buf, ARRAY_SIZE(buf), 0, 0 ); + ok( ret, "WideCharToMultiByte failed: %lu\n", GetLastError() ); + ok( _wputenv( L"__winetest_\u263a=bark" ) == 0, "Couldn't set env var\n" ); + ok( _wgetenv( L"__winetest_\u263a" ) && !wcscmp( _wgetenv( L"__winetest_\u263a" ), L"bark"), "Couldn't retrieve env var\n" ); + ok( getenv( buf ) && !strcmp( getenv( buf ), "bark"), "Couldn't retrieve env var %s\n", wine_dbgstr_a(buf) ); + ok( _wputenv( L"__winetest_\u263a=" ) == 0, "Couldn't reset env var\n" ); + ok( count == env_get_entry_countA( *p_environ ), "Unexpected modification of _environ[]\n" ); +} + +static void test_child_env(char** argv) +{ + static char env[4096]; + STARTUPINFOA si = {sizeof(si)}; + PROCESS_INFORMATION pi; + char tmp[1024]; + BOOL ret; + char *ptr; + WCHAR *wptr; + + ptr = env; +#define PUSHA( x, y ) do { if (y) { strcpy( ptr, (x) ); ptr += strlen( x ); *ptr++ = '='; strcpy( ptr, (y) ); ptr += strlen( y ) + 1; } } while (0) +#define PUSHENVA( x ) PUSHA( ( x ), getenv( x )) + PUSHA( "__winetest_child_dog", "bark" ); + PUSHA( "__winetest_child_cat", "meow" ); + PUSHENVA( "WINETEST_COLOR" ); + *ptr = '\0'; +#undef PUSHA +#undef PUSHENVA + + snprintf( tmp, sizeof(tmp), "%s %s create", argv[0], argv[1] ); + ret = CreateProcessA( NULL, tmp, NULL, NULL, FALSE, 0, env, NULL, &si, &pi ); + ok( ret, "Couldn't create child process %s\n", tmp ); + winetest_wait_child_process( pi.hProcess ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread); + + wptr = (void *)env; +#define PUSHW( x, y ) do { if (y) { wcscpy( wptr, (x) ); wptr += wcslen( x ); *wptr++ = L'='; wcscpy( wptr, (y) ); wptr += wcslen( y ) + 1; } } while (0) +#define PUSHENVW( x ) PUSHW( ( x ), _wgetenv( x )) + PUSHW( L"__winetest_child_dog", L"bark" ); + PUSHW( L"__winetest_child_cat", L"meow" ); + PUSHW( L"__winetest_child_\u03b1", L"\u03b2" ); /* alpha = beta */ + PUSHENVW( L"WINETEST_COLOR" ); + *wptr = L'\0'; +#undef PUSHW +#undef PUSHENVW + + snprintf( tmp, sizeof(tmp), "%s %s create /unicode", argv[0], argv[1] ); + ret = CreateProcessA( NULL, tmp, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, env, NULL, &si, &pi ); + ok( ret, "Couldn't create child process %s\n", tmp ); + winetest_wait_child_process( pi.hProcess ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); +} + +START_TEST(environ) +{ + char **argv; + int argc; + + if (!init()) return; + + ok( _set_invalid_parameter_handler( test_invalid_parameter_handler ) == NULL, + "Invalid parameter handler was already set\n" ); + + argc = winetest_get_mainargs(&argv); + if (argc >= 3 && !strcmp(argv[2], "create")) + { + test_initial_environ(); + ok( getenv( "__winetest_child_dog" ) && !strcmp( getenv( "__winetest_child_dog" ), "bark" ), + "Couldn't find env var\n" ); + if (argc >= 4) /* with unicode */ + { + ok( _wgetenv( L"__winetest_child_\u03b1" ) && !wcscmp(_wgetenv(L"__winetest_child_\u03b1" ), L"\u03b2" ), + "Couldn't find alpha\n" ); + } + return; + } + + test_initial_environ(); + test_child_env(argv); + test_environment_manipulation(); +}
From: Eric Pouech epouech@codeweavers.com
Letting each entry have a dedicated allocation chunk.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55835
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/data.c | 342 +++++++++++++++++++++++++++++++++- dlls/msvcrt/environ.c | 47 +---- dlls/msvcrt/msvcrt.h | 5 +- dlls/msvcrt/tests/environ.c | 7 - dlls/ucrtbase/tests/environ.c | 7 +- 5 files changed, 345 insertions(+), 63 deletions(-)
diff --git a/dlls/msvcrt/data.c b/dlls/msvcrt/data.c index 8a8552a151e..14855fe5c94 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" @@ -69,11 +70,12 @@ WCHAR* MSVCRT__wpgmptr = NULL; * 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 ** msvcrt_SnapshotOfEnvironmentA(void) { char* environ_strings = GetEnvironmentStringsA(); int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */ char *ptr; + char **blk;
for (ptr = environ_strings; *ptr; ptr += strlen(ptr) + 1) { @@ -81,7 +83,7 @@ char ** msvcrt_SnapshotOfEnvironmentA(char **blk) if (*ptr != '=') count++; len += strlen(ptr) + 1; } - blk = realloc(blk, count * sizeof(char*) + len); + blk = malloc(count * sizeof(char*) + len);
if (blk) { @@ -100,11 +102,12 @@ char ** msvcrt_SnapshotOfEnvironmentA(char **blk) return blk; }
-wchar_t ** msvcrt_SnapshotOfEnvironmentW(wchar_t **wblk) +static wchar_t ** msvcrt_SnapshotOfEnvironmentW(void) { wchar_t* wenviron_strings = GetEnvironmentStringsW(); int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */ wchar_t *wptr; + wchar_t **wblk;
for (wptr = wenviron_strings; *wptr; wptr += wcslen(wptr) + 1) { @@ -112,7 +115,7 @@ wchar_t ** msvcrt_SnapshotOfEnvironmentW(wchar_t **wblk) if (*wptr != '=') count++; len += wcslen(wptr) + 1; } - wblk = realloc(wblk, count * sizeof(wchar_t*) + len * sizeof(wchar_t)); + wblk = malloc(count * sizeof(wchar_t*) + len * sizeof(wchar_t));
if (wblk) { @@ -131,6 +134,327 @@ wchar_t ** msvcrt_SnapshotOfEnvironmentW(wchar_t **wblk) return wblk; }
+static char **env_cloneA( char** env ) +{ + int i; + char **blk; + + for (i = 0; env[i]; i++) {} + if ((blk = malloc( (i + 1) * sizeof(char *) ))) + { + for (i = 0; env[i]; i++) + blk[i] = strdup( env[i] ); + blk[i] = NULL; + } + return blk; +} + +static wchar_t **env_cloneW( wchar_t** env ) +{ + int i; + wchar_t **blk; + + for (i = 0; env[i]; i++) {} + if ((blk = malloc( (i + 1) * sizeof(wchar_t *) ))) + { + for (i = 0; env[i]; i++) + blk[i] = wcsdup( env[i] ); + blk[i] = NULL; + } + return blk; +} + +/* The __initenv (resp __winitenv) are created with a single chunk + * of memory. + * At startup, _environ points to __initenv. When a modification + * (add/replace/delete) is requested, _environ is transformed so + * that each entry has a dedicated allocation chunk. + * _wenviron behaves similary, except that __winitenv and + * _wenviron are only created when a Unicode request is made. + * This functions: + * - ensures that _environ is ready for modification (transforms + * it with per entry allocation if not already done). + * - when with_unicode is TRUE: + + + creates __winitenv (if not created yet) + * + ensures that __wenviron is ready for modification (with + * per entry allocation) if not already done. + */ +int msvcrt_ensure_environ( BOOL with_unicode ) +{ + if (MSVCRT__environ == MSVCRT___initenv) + { + char **new = env_cloneA( MSVCRT___initenv ); + if (!new) return -1; + MSVCRT__environ = new; + } + if (with_unicode) + { + wchar_t **new; + /* Note that the __initenv and __winitenv are allocated at + * different point in time, so could have different variables and values. + */ + if (!MSVCRT___winitenv) + { + new = msvcrt_SnapshotOfEnvironmentW(); + if (!new) return -1; + MSVCRT__wenviron = MSVCRT___winitenv = new; + } + + if (MSVCRT__wenviron == MSVCRT___winitenv) + { + new = env_cloneW( MSVCRT___winitenv ); + if (!new) return -1; + MSVCRT__wenviron = new; + } + } + return 0; +} + +static int internal_find_envA( const char *name ) +{ + int i; + size_t len = strlen( name ); + + for (i = 0; MSVCRT__environ[i] != NULL; i++) + { + if (!memcmp( name, MSVCRT__environ[i], len ) && MSVCRT__environ[i][len] == '=') + return i; + } + return -1; +} + +static void internal_remove_envA( int indx ) +{ + free( MSVCRT__environ[indx] ); + do + { + MSVCRT__environ[indx] = MSVCRT__environ[indx + 1]; + } while (MSVCRT__environ[++indx] != NULL); +} + +static void internal_replace_envA( int indx, char *set ) +{ + free( MSVCRT__environ[indx] ); + MSVCRT__environ[indx] = set; +} + +static int internal_append_envA( char *set ) +{ + int i; + char **new; + + for (i = 0; MSVCRT__environ[i] != NULL; i++) {} + new = realloc( MSVCRT__environ, (i + 2) * sizeof(char *) ); + if (!new) return -1; + + MSVCRT__environ = new; + MSVCRT__environ[i] = set; + MSVCRT__environ[i + 1] = NULL; + return 0; +} + +static char *env_alloc_setA( const char *name, const char *value ) +{ + size_t sz = strlen( name ) + 1 + strlen( value ) + 1; + char *setA = malloc( sz * sizeof( char ) ); + if (setA) + snprintf( setA, sz, "%s=%s", name, value ); + return setA; +} + +static int internal_find_envW( const wchar_t *name ) +{ + int i; + size_t len = wcslen( name ); + + for (i = 0; MSVCRT__wenviron[i] != NULL; i++) + { + if (!memcmp( name, MSVCRT__wenviron[i], len * sizeof(wchar_t) ) && MSVCRT__wenviron[i][len] == L'=') + return i; + } + return -1; +} + +static void internal_remove_envW( int indx ) +{ + free( MSVCRT__wenviron[indx] ); + do + { + MSVCRT__wenviron[indx] = MSVCRT__wenviron[indx + 1]; + } while (MSVCRT__wenviron[++indx] != NULL); +} + +static void internal_replace_envW( int indx, wchar_t *set ) +{ + free( MSVCRT__wenviron[indx] ); + MSVCRT__wenviron[indx] = set; +} + +static int internal_append_envW( wchar_t *set ) +{ + int i; + wchar_t **new; + + for (i = 0; MSVCRT__wenviron[i] != NULL; i++) {} + new = realloc( MSVCRT__wenviron, (i + 2) * sizeof(wchar_t *) ); + if (!new) return -1; + + MSVCRT__wenviron = new; + MSVCRT__wenviron[i] = set; + MSVCRT__wenviron[i + 1] = NULL; + return 0; +} + +static wchar_t *env_alloc_setW( const wchar_t *name, const wchar_t *value ) +{ + size_t sz = wcslen( name ) + 1 + wcslen( value ) + 1; + wchar_t *setW = malloc( sz * sizeof(wchar_t) ); + if (setW) + swprintf( setW, sz, L"%s=%s", name, value ); + return setW; +} + +int msvcrt_update_environmentA( const char *aname, const char *avalue ) +{ + wchar_t *wname = NULL; + wchar_t *wvalue = NULL; + int indxA, indxW; + int ret = -1; + + if (msvcrt_ensure_environ( FALSE ) < 0) return -1; + if (MSVCRT__wenviron) /* only updates _wenviron[] when it exists */ + { + if (!(wname = msvcrt_wstrdupa( aname ))) return -1; + if (*avalue && !(wvalue = msvcrt_wstrdupa( avalue ))) + goto done; + } + /* putenv returns success on deletion of nonexistent variable, unlike [Rtl]SetEnvironmentVariable */ + if (!SetEnvironmentVariableA( aname, *avalue ? avalue : NULL ) && + GetLastError() != ERROR_ENVVAR_NOT_FOUND) + goto done; + + indxA = internal_find_envA( aname ); + indxW = (MSVCRT__wenviron) ? internal_find_envW( wname ) : -1; /* to get rid of compiler warning */ + if (!*avalue) + { + if (indxA >= 0) internal_remove_envA( indxA ); + if (MSVCRT__wenviron && indxW >= 0) internal_remove_envW( indxW ); + ret = 0; + } + else + { + char *setA = NULL; + wchar_t *setW = NULL; + + if (!(setA = env_alloc_setA( aname, avalue ))) goto done; + if (MSVCRT__wenviron != NULL && !(setW = env_alloc_setW( wname, wvalue ))) + { + free( setA ); + goto done; + } + if (indxA >= 0) + internal_replace_envA( indxA, setA ); + else if (internal_append_envA( setA ) < 0) + { + free( setA ); + free( setW ); + goto done; + } + if (MSVCRT__wenviron) + { + if (indxW >= 0) + internal_replace_envW( indxW, setW ); + else if (internal_append_envW( setW ) < 0) + { + /* FIXME we don't rollback changes to _environ */ + free( setA ); + free( setW ); + goto done; + } + } + ret = 0; + } + +done: + free( wname ); + free( wvalue ); + 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 *wname, const wchar_t *wvalue ) +{ + char *aname = NULL; + char *avalue = NULL; + int indxA, indxW; + int ret = -1; + + if (msvcrt_ensure_environ( TRUE ) < 0) return -1; + if (!(aname = msvcrt_astrdupw( wname ))) return -1; + if (*wvalue && !(avalue = msvcrt_astrdupw( wvalue ))) + goto done; + + /* putenv returns success on deletion of nonexistent variable, unlike [Rtl]SetEnvironmentVariable */ + if (!SetEnvironmentVariableW( wname, *wvalue ? wvalue : NULL ) && + GetLastError() != ERROR_ENVVAR_NOT_FOUND) + goto done; + + indxA = internal_find_envA( aname ); + indxW = internal_find_envW( wname ); + + if (!*wvalue) + { + if (indxA >= 0) internal_remove_envA( indxA ); + if (indxW >= 0) internal_remove_envW( indxW ); + ret = 0; + } + else + { + char *setA = NULL; + wchar_t *setW = NULL; + + if (!(setA = env_alloc_setA( aname, avalue ))) goto done; + if (!(setW = env_alloc_setW( wname, wvalue ))) + { + free( setA ); + goto done; + } + if (indxA >= 0) + internal_replace_envA( indxA, setA ); + else if (internal_append_envA( setA ) < 0) + { + free( setA ); + free( setW ); + goto done; + } + if (indxW >= 0) + internal_replace_envW( indxW, setW ); + else if (internal_append_envW( setW ) < 0) + { + /* FIXME we don't rollback changes to _environ */ + free( setA ); + free( setW ); + goto done; + } + ret = 0; + } + +done: + free( aname ); + free( avalue ); + return ret; +} + static char **build_argv( WCHAR **wargv ) { int argc; @@ -433,9 +757,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 = msvcrt_SnapshotOfEnvironmentA();
MSVCRT__pgmptr = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); if (MSVCRT__pgmptr) @@ -554,9 +876,9 @@ int CDECL __wgetmainargs(int *argc, wchar_t** *wargv, wchar_t** *wenvp, MSVCRT___wargv = initial_wargv; }
- /* Initialize the _wenviron array if it's not already created. */ - if (!MSVCRT__wenviron) - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(NULL); + if (!MSVCRT___winitenv) + MSVCRT___winitenv = MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(); + *argc = MSVCRT___argc; *wargv = MSVCRT___wargv; *wenvp = MSVCRT__wenviron; diff --git a/dlls/msvcrt/environ.c b/dlls/msvcrt/environ.c index 2c2a3353582..f8b2d028734 100644 --- a/dlls/msvcrt/environ.c +++ b/dlls/msvcrt/environ.c @@ -65,8 +65,7 @@ static wchar_t * wgetenv_helper(const wchar_t *name) len = wcslen(name);
/* Initialize the _wenviron array if it's not already created. */ - if (!MSVCRT__wenviron) - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(NULL); + if (msvcrt_ensure_environ(TRUE) == -1) return NULL;
for (env = MSVCRT__wenviron; *env; env++) { @@ -122,16 +121,8 @@ int CDECL _putenv(const char *str) *dst++ = *str++; *dst = '\0';
- ret = SetEnvironmentVariableA(name, value[0] ? value : NULL) ? 0 : -1; + ret = msvcrt_update_environmentA( name, value );
- /* _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); - finish: HeapFree(GetProcessHeap(), 0, name); return ret; @@ -167,13 +158,7 @@ int CDECL _wputenv(const wchar_t *str) *dst++ = *str++; *dst = 0;
- ret = SetEnvironmentVariableW(name, value[0] ? value : NULL) ? 0 : -1; - - /* _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); + ret = msvcrt_update_environmentW(name, value);
finish: HeapFree(GetProcessHeap(), 0, name); @@ -192,19 +177,12 @@ errno_t CDECL _putenv_s(const char *name, const char *value) if (!MSVCRT_CHECK_PMT(name != NULL)) return EINVAL; if (!MSVCRT_CHECK_PMT(value != NULL)) return EINVAL;
- if (!SetEnvironmentVariableA(name, value[0] ? value : NULL)) + if (msvcrt_update_environmentA(name, value) < 0) { - /* _putenv returns success on deletion of nonexistent variable */ - if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) - { - msvcrt_set_errno(GetLastError()); - ret = *_errno(); - } + msvcrt_set_errno(GetLastError()); + ret = *_errno(); }
- MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(MSVCRT__environ); - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(MSVCRT__wenviron); - return ret; }
@@ -220,19 +198,12 @@ errno_t CDECL _wputenv_s(const wchar_t *name, const wchar_t *value) if (!MSVCRT_CHECK_PMT(name != NULL)) return EINVAL; if (!MSVCRT_CHECK_PMT(value != NULL)) return EINVAL;
- if (!SetEnvironmentVariableW(name, value[0] ? value : NULL)) + if (msvcrt_update_environmentW(name, value) < 0) { - /* _putenv returns success on deletion of nonexistent variable */ - if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) - { - msvcrt_set_errno(GetLastError()); - ret = *_errno(); - } + msvcrt_set_errno(GetLastError()); + ret = *_errno(); }
- MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(MSVCRT__environ); - MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(MSVCRT__wenviron); - return ret; }
diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h index 7c2dd4835f5..457ee935e95 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 **); +extern int msvcrt_update_environmentA(const char *name, const char *value); +extern int msvcrt_update_environmentW(const wchar_t *name, const wchar_t * value); +extern int msvcrt_ensure_environ(BOOL);
wchar_t *msvcrt_wstrdupa(const char *);
diff --git a/dlls/msvcrt/tests/environ.c b/dlls/msvcrt/tests/environ.c index c0a392f9f9d..abcfd314ff0 100644 --- a/dlls/msvcrt/tests/environ.c +++ b/dlls/msvcrt/tests/environ.c @@ -139,7 +139,6 @@ static void test__environ(void) { initenv = *p__p___initenv();
- todo_wine ok( initenv == *p_environ, "Expected _environ to be equal to initial env\n" ); } @@ -195,7 +194,6 @@ static void test__wenviron(void) if (p__p___winitenv) { wchar_t ***retptr = p__p___winitenv(); - todo_wine ok( !*retptr, "Expected initial env to be NULL\n" ); } else @@ -235,7 +233,6 @@ static void test__wenviron(void) "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 ); } @@ -345,7 +342,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" ); @@ -353,13 +349,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" ); @@ -370,7 +364,6 @@ static void test_environment_manipulation(void) ret = SetEnvironmentVariableA( "__winetest_cat", "meow" ); ok( ret, "SetEnvironmentVariableA failed: %lu\n", GetLastError() ); ok( _putenv( "__winetest_dog=bark" ) == 0, "Couldn't set env var\n" ); - todo_wine ok( getenv( "__winetest_cat" ) == NULL, "msvcrt env cache shouldn't have been updated\n" ); ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env var\n" ); ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" ); diff --git a/dlls/ucrtbase/tests/environ.c b/dlls/ucrtbase/tests/environ.c index 3f10048d5a8..e8e9f139145 100644 --- a/dlls/ucrtbase/tests/environ.c +++ b/dlls/ucrtbase/tests/environ.c @@ -100,13 +100,12 @@ static void test_initial_environ( void ) ok( p__p__environ() != NULL, "Unexpected NULL _environ[]\n" ); ok( *p__p__environ() != NULL, "Unexpected empty _environ[]\n" ); ok( p_get_initial_narrow_environment() != NULL, "Unexpected empty narrow initial environment\n" ); - todo_wine ok( p_get_initial_narrow_environment() == *p__p__environ(), "Expecting _environ[] to match initial narrow environment\n" );
ok( p__p__wenviron() != NULL, "Unexpected NULL _wenviron[]\n" ); ok( *p__p__wenviron() == NULL, "Unexpected non empty _wenviron[]\n" ); - ok( p_get_initial_wide_environment() != NULL, "Unexpected empty wide initial environment\n" ); todo_wine + ok( p_get_initial_wide_environment() != NULL, "Unexpected empty wide initial environment\n" ); ok( p_get_initial_wide_environment() == *p__p__wenviron(), "Expecting _wenviron[] to match initial wide environment\n" ); }
@@ -197,7 +196,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" ); @@ -205,13 +203,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" ); @@ -222,7 +218,6 @@ static void test_environment_manipulation(void) ret = SetEnvironmentVariableA( "__winetest_cat", "meow" ); ok( ret, "SetEnvironmentVariableA failed: %lu\n", GetLastError() ); ok( _putenv( "__winetest_dog=bark" ) == 0, "Couldn't set env var\n" ); - todo_wine ok( getenv( "__winetest_cat" ) == NULL, "msvcrt env cache shouldn't have been updated\n" ); ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env var\n" ); ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" );
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/data.c | 35 +++++++++++++++++++++-------------- dlls/ucrtbase/tests/environ.c | 1 - 2 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/dlls/msvcrt/data.c b/dlls/msvcrt/data.c index 14855fe5c94..e12a504c39c 100644 --- a/dlls/msvcrt/data.c +++ b/dlls/msvcrt/data.c @@ -988,14 +988,6 @@ void CDECL __set_app_type(int app_type)
#if _MSVCR_VER>=140
-/********************************************************************* - * _get_initial_narrow_environment (UCRTBASE.@) - */ -char** CDECL _get_initial_narrow_environment(void) -{ - return MSVCRT___initenv; -} - /********************************************************************* * _configure_narrow_argv (UCRTBASE.@) */ @@ -1010,16 +1002,19 @@ int CDECL _configure_narrow_argv(int mode) */ int CDECL _initialize_narrow_environment(void) { - TRACE("\n"); - return 0; + TRACE("\n"); + if (!MSVCRT___initenv) + MSVCRT___initenv = MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(); + return 0; }
/********************************************************************* - * _get_initial_wide_environment (UCRTBASE.@) + * _get_initial_narrow_environment (UCRTBASE.@) */ -wchar_t** CDECL _get_initial_wide_environment(void) +char** CDECL _get_initial_narrow_environment(void) { - return MSVCRT___winitenv; + _initialize_narrow_environment(); + return MSVCRT___initenv; }
/********************************************************************* @@ -1036,10 +1031,22 @@ int CDECL _configure_wide_argv(int mode) */ int CDECL _initialize_wide_environment(void) { - WARN("stub\n"); + TRACE("\n"); + if (!MSVCRT___winitenv) + MSVCRT___winitenv = MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(); return 0; }
+/********************************************************************* + * _get_initial_wide_environment (UCRTBASE.@) + */ +wchar_t** CDECL _get_initial_wide_environment(void) +{ + TRACE("\n"); + _initialize_wide_environment(); + return MSVCRT___winitenv; +} + /********************************************************************* * _get_narrow_winmain_command_line (UCRTBASE.@) */ diff --git a/dlls/ucrtbase/tests/environ.c b/dlls/ucrtbase/tests/environ.c index e8e9f139145..b093ba92e9f 100644 --- a/dlls/ucrtbase/tests/environ.c +++ b/dlls/ucrtbase/tests/environ.c @@ -104,7 +104,6 @@ static void test_initial_environ( void )
ok( p__p__wenviron() != NULL, "Unexpected NULL _wenviron[]\n" ); ok( *p__p__wenviron() == NULL, "Unexpected non empty _wenviron[]\n" ); - todo_wine ok( p_get_initial_wide_environment() != NULL, "Unexpected empty wide initial environment\n" ); ok( p_get_initial_wide_environment() == *p__p__wenviron(), "Expecting _wenviron[] to match initial wide environment\n" ); }