From a87037af61000d263fc3b7bbc3585011bfbb4eba Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Thu, 6 Jun 2024 02:33:41 +0200 Subject: [PATCH] setenv: On native Windows, don't modify _environ directly. * m4/setenv.m4 (gl_PREREQ_SETENV): Check for _putenv. * lib/setenv.c: On native Windows, include and define SetEnvironmentVariable. (setenv) [HAVE_DECL__PUTENV]: New implementation for platforms with _putenv. * modules/setenv (Depends-on): Add malloc-posix. --- ChangeLog | 10 ++++++ lib/setenv.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++-- m4/setenv.m4 | 3 +- modules/setenv | 1 + 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e7e4ecfc1..16071b6961 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2024-06-05 Bruno Haible + + setenv: On native Windows, don't modify _environ directly. + * m4/setenv.m4 (gl_PREREQ_SETENV): Check for _putenv. + * lib/setenv.c: On native Windows, include and define + SetEnvironmentVariable. + (setenv) [HAVE_DECL__PUTENV]: New implementation for platforms with + _putenv. + * modules/setenv (Depends-on): Add malloc-posix. + 2024-06-05 Bruno Haible unsetenv: On native Windows, don't modify _environ directly. diff --git a/lib/setenv.c b/lib/setenv.c index 706a032a49..909182eb12 100644 --- a/lib/setenv.c +++ b/lib/setenv.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1992, 1995-2003, 2005-2023 Free Software Foundation, Inc. +/* Copyright (C) 1992, 1995-2003, 2005-2024 Free Software Foundation, Inc. This file is part of the GNU C Library. This file is free software: you can redistribute it and/or modify @@ -38,11 +38,23 @@ # include #endif +#if defined _WIN32 && ! defined __CYGWIN__ +# define WIN32_LEAN_AND_MEAN +# include +#endif + #if !_LIBC # include "malloca.h" #endif +#if defined _WIN32 && ! defined __CYGWIN__ +/* Don't assume that UNICODE is not defined. */ +# undef SetEnvironmentVariable +# define SetEnvironmentVariable SetEnvironmentVariableA +#endif + #if _LIBC || !HAVE_SETENV +#if !HAVE_DECL__PUTENV #if !_LIBC # define __environ environ @@ -343,6 +355,84 @@ weak_alias (__setenv, setenv) weak_alias (__clearenv, clearenv) #endif +#else /* HAVE_DECL__PUTENV */ +/* Native Windows */ + +int +setenv (const char *name, const char *value, int replace) +{ + if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) + { + errno = EINVAL; + return -1; + } + + /* The Microsoft documentation + + says: + "Don't change an environment entry directly: instead, + use _putenv or _wputenv to change it." + Note: Microsoft's _putenv updates not only the contents of _environ but + also the contents of _wenviron, so that both are in kept in sync. */ + const char *existing_value = getenv (name); + if (existing_value != NULL) + { + if (replace) + { + if (strcmp (existing_value, value) == 0) + /* No need to allocate memory. */ + return 0; + } + else + /* Keep the existing value. */ + return 0; + } + /* Allocate a new environment entry in the heap. */ + /* _putenv ("NAME=") unsets NAME, so if VALUE is the empty string, invoke + _putenv ("NAME= ") and fix up the result afterwards. */ + const char *value_ = (value[0] == '\0' ? " " : value); + size_t name_len = strlen (name); + size_t value_len = strlen (value_); + char *string = (char *) malloc (name_len + 1 + value_len + 1); + if (string == NULL) + return -1; + memcpy (string, name, name_len); + string[name_len] = '='; + memcpy (&string[name_len + 1], value_, value_len + 1); + /* Use _putenv. */ + if (_putenv (string) < 0) + return -1; + if (value[0] == '\0') + { + /* Fix up the result. */ + char *new_value = getenv (name); + if (new_value != NULL && new_value[0] == ' ' && new_value[1] == '\0') + new_value[0] = '\0'; +# if defined _WIN32 && ! defined __CYGWIN__ + /* _putenv propagated "NAME= " into the subprocess environment; + fix that by calling SetEnvironmentVariable directly. */ + /* Documentation: + */ + if (!SetEnvironmentVariable (name, "")) + { + switch (GetLastError ()) + { + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + errno = ENOMEM; + break; + default: + errno = EINVAL; + break; + } + return -1; + } +# endif + } + return 0; +} + +#endif /* HAVE_DECL__PUTENV */ #endif /* _LIBC || !HAVE_SETENV */ /* The rest of this file is called into use when replacing an existing @@ -360,7 +450,7 @@ int rpl_setenv (const char *name, const char *value, int replace) { int result; - if (!name || !*name || strchr (name, '=')) + if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) { errno = EINVAL; return -1; diff --git a/m4/setenv.m4 b/m4/setenv.m4 index c31a5837d6..a5c844171a 100644 --- a/m4/setenv.m4 +++ b/m4/setenv.m4 @@ -1,4 +1,4 @@ -# setenv.m4 serial 34 +# setenv.m4 serial 35 dnl Copyright (C) 2001-2004, 2006-2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -154,6 +154,7 @@ AC_DEFUN([gl_PREREQ_SETENV], AC_REQUIRE([gl_ENVIRON]) AC_CHECK_HEADERS_ONCE([unistd.h]) AC_CHECK_HEADERS([search.h]) + AC_CHECK_DECLS_ONCE([_putenv]) gl_CHECK_FUNCS_ANDROID([tsearch], [[#include ]]) ]) diff --git a/modules/setenv b/modules/setenv index a907d1e5d0..f7d7f6ea91 100644 --- a/modules/setenv +++ b/modules/setenv @@ -9,6 +9,7 @@ Depends-on: stdlib malloca [test $HAVE_SETENV = 0 || test $REPLACE_SETENV = 1] alloca-opt [test $HAVE_SETENV = 0 || test $REPLACE_SETENV = 1] +malloc-posix [test $HAVE_SETENV = 0 || test $REPLACE_SETENV = 1] unistd [test $HAVE_SETENV = 0 || test $REPLACE_SETENV = 1] environ [test $HAVE_SETENV = 0 || test $REPLACE_SETENV = 1] -- 2.39.5