]> Savannah Git Hosting - gnulib.git/commitdiff
setenv: On native Windows, don't modify _environ directly.
authorBruno Haible <bruno@clisp.org>
Thu, 6 Jun 2024 00:33:41 +0000 (02:33 +0200)
committerBruno Haible <bruno@clisp.org>
Thu, 6 Jun 2024 23:31:25 +0000 (01:31 +0200)
* m4/setenv.m4 (gl_PREREQ_SETENV): Check for _putenv.
* lib/setenv.c: On native Windows, include <windows.h> and define
SetEnvironmentVariable.
(setenv) [HAVE_DECL__PUTENV]: New implementation for platforms with
_putenv.
* modules/setenv (Depends-on): Add malloc-posix.

ChangeLog
lib/setenv.c
m4/setenv.m4
modules/setenv

index 1e7e4ecfc1d94f85bc826f2305c73474c3ff4503..16071b6961b7ee293dbac01c3f1ac605bedf73f2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2024-06-05  Bruno Haible  <bruno@clisp.org>
+
+       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 <windows.h> 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  <bruno@clisp.org>
 
        unsetenv: On native Windows, don't modify _environ directly.
index 706a032a49132556f1ae33b52cc76af08cf5a3ed..909182eb12a525166e251aee76109a5e7083d18e 100644 (file)
@@ -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
 # include <unistd.h>
 #endif
 
+#if defined _WIN32 && ! defined __CYGWIN__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#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
+     <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-wputenv>
+     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:
+         <https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable>  */
+      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;
index c31a5837d6f55019d0ace01b8caa1a7ba273317a..a5c844171a500c2598d683f5f847ce77bc0c56f7 100644 (file)
@@ -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 <search.h>]])
 ])
 
index a907d1e5d0e1a7a54c8467ecf0e27fca9e929b7d..f7d7f6ea919ef5b919d1da61c4014afcbd6c5f7d 100644 (file)
@@ -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]