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 7723d014ca5..11f5bfeea9c 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 dc470225f3d..64e25fa9f22 100644 --- a/dlls/ucrtbase/tests/environ.c +++ b/dlls/ucrtbase/tests/environ.c @@ -132,13 +132,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" ); }
@@ -241,7 +240,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" ); @@ -249,13 +247,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" ); @@ -266,7 +262,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" );