glib-unix: add function to ensure an fd is sealed
[platform/upstream/glib.git] / glib / genviron.c
index 3c1b54c..0a83ac9 100644 (file)
@@ -12,9 +12,7 @@
  * 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
 
 #include "config.h"
 
+#include "genviron.h"
+
 #include <stdlib.h>
 #include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 #ifdef HAVE_CRT_EXTERNS_H
 #include <crt_externs.h> /* for _NSGetEnviron */
 #endif
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
 
+#include "glib-private.h"
 #include "gmem.h"
 #include "gmessages.h"
 #include "gstrfuncs.h"
+#include "gunicode.h"
+#include "gconvert.h"
+#include "gquark.h"
+
+/* Environ array functions {{{1 */
+static gint
+g_environ_find (gchar       **envp,
+                const gchar  *variable)
+{
+  gint len, i;
 
+  if (envp == NULL)
+    return -1;
+
+  len = strlen (variable);
+
+  for (i = 0; envp[i]; i++)
+    {
+      if (strncmp (envp[i], variable, len) == 0 &&
+          envp[i][len] == '=')
+        return i;
+    }
+
+  return -1;
+}
+
+/**
+ * g_environ_getenv:
+ * @envp: (allow-none) (array zero-terminated=1) (transfer none): an environment
+ *     list (eg, as returned from g_get_environ()), or %NULL
+ *     for an empty environment list
+ * @variable: the environment variable to get, in the GLib file name
+ *     encoding
+ *
+ * Returns the value of the environment variable @variable in the
+ * provided list @envp.
+ *
+ * Returns: the value of the environment variable, or %NULL if
+ *     the environment variable is not set in @envp. The returned
+ *     string is owned by @envp, and will be freed if @variable is
+ *     set or unset again.
+ *
+ * Since: 2.32
+ */
+const gchar *
+g_environ_getenv (gchar       **envp,
+                  const gchar  *variable)
+{
+  gint index;
+
+  g_return_val_if_fail (variable != NULL, NULL);
+
+  index = g_environ_find (envp, variable);
+  if (index != -1)
+    return envp[index] + strlen (variable) + 1;
+  else
+    return NULL;
+}
+
+/**
+ * g_environ_setenv:
+ * @envp: (allow-none) (array zero-terminated=1) (transfer full): an
+ *     environment list that can be freed using g_strfreev() (e.g., as
+ *     returned from g_get_environ()), or %NULL for an empty
+ *     environment list
+ * @variable: the environment variable to set, must not contain '='
+ * @value: the value for to set the variable to
+ * @overwrite: whether to change the variable if it already exists
+ *
+ * Sets the environment variable @variable in the provided list
+ * @envp to @value.
+ *
+ * Returns: (array zero-terminated=1) (transfer full): the
+ *     updated environment list. Free it using g_strfreev().
+ *
+ * Since: 2.32
+ */
+gchar **
+g_environ_setenv (gchar       **envp,
+                  const gchar  *variable,
+                  const gchar  *value,
+                  gboolean      overwrite)
+{
+  gint index;
+
+  g_return_val_if_fail (variable != NULL, NULL);
+  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+  g_return_val_if_fail (value != NULL, NULL);
+
+  index = g_environ_find (envp, variable);
+  if (index != -1)
+    {
+      if (overwrite)
+        {
+          g_free (envp[index]);
+          envp[index] = g_strdup_printf ("%s=%s", variable, value);
+        }
+    }
+  else
+    {
+      gint length;
+
+      length = envp ? g_strv_length (envp) : 0;
+      envp = g_renew (gchar *, envp, length + 2);
+      envp[length] = g_strdup_printf ("%s=%s", variable, value);
+      envp[length + 1] = NULL;
+    }
+
+  return envp;
+}
+
+static gchar **
+g_environ_unsetenv_internal (gchar        **envp,
+                             const gchar   *variable,
+                             gboolean       free_value)
+{
+  gint len;
+  gchar **e, **f;
+
+  len = strlen (variable);
+
+  /* Note that we remove *all* environment entries for
+   * the variable name, not just the first.
+   */
+  e = f = envp;
+  while (*e != NULL)
+    {
+      if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
+        {
+          *f = *e;
+          f++;
+        }
+      else
+        {
+          if (free_value)
+            g_free (*e);
+        }
+
+      e++;
+    }
+  *f = NULL;
+
+  return envp;
+}
+
+
+/**
+ * g_environ_unsetenv:
+ * @envp: (allow-none) (array zero-terminated=1) (transfer full): an environment
+ *     list that can be freed using g_strfreev() (e.g., as returned from g_get_environ()), 
+ *     or %NULL for an empty environment list
+ * @variable: the environment variable to remove, must not contain '='
+ *
+ * Removes the environment variable @variable from the provided
+ * environment @envp.
+ *
+ * Returns: (array zero-terminated=1) (transfer full): the
+ *     updated environment list. Free it using g_strfreev().
+ *
+ * Since: 2.32
+ */
+gchar **
+g_environ_unsetenv (gchar       **envp,
+                    const gchar  *variable)
+{
+  g_return_val_if_fail (variable != NULL, NULL);
+  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+
+  if (envp == NULL)
+    return NULL;
+
+  return g_environ_unsetenv_internal (envp, variable, TRUE);
+}
+
+/* UNIX implemention {{{1 */
+#ifndef G_OS_WIN32
 
 /**
  * g_getenv:
  * On Windows, in case the environment variable's value contains
  * references to other environment variables, they are expanded.
  *
- * Return value: the value of the environment variable, or %NULL if
+ * Returns: the value of the environment variable, or %NULL if
  *     the environment variable is not found. The returned string
  *     may be overwritten by the next call to g_getenv(), g_setenv()
  *     or g_unsetenv().
 const gchar *
 g_getenv (const gchar *variable)
 {
-#ifndef G_OS_WIN32
-
   g_return_val_if_fail (variable != NULL, NULL);
 
   return getenv (variable);
+}
 
-#else /* G_OS_WIN32 */
+/**
+ * g_setenv:
+ * @variable: the environment variable to set, must not contain '='.
+ * @value: the value for to set the variable to.
+ * @overwrite: whether to change the variable if it already exists.
+ *
+ * Sets an environment variable. Both the variable's name and value
+ * should be in the GLib file name encoding. On UNIX, this means that
+ * they can be arbitrary byte strings. On Windows, they should be in
+ * UTF-8.
+ *
+ * Note that on some systems, when variables are overwritten, the memory
+ * used for the previous variables and its value isn't reclaimed.
+ *
+ * You should be mindful of the fact that environment variable handling
+ * in UNIX is not thread-safe, and your program may crash if one thread
+ * calls g_setenv() while another thread is calling getenv(). (And note
+ * that many functions, such as gettext(), call getenv() internally.)
+ * This function is only safe to use at the very start of your program,
+ * before creating any other threads (or creating objects that create
+ * worker threads of their own).
+ *
+ * If you need to set up the environment for a child process, you can
+ * use g_get_environ() to get an environment array, modify that with
+ * g_environ_setenv() and g_environ_unsetenv(), and then pass that
+ * array directly to execvpe(), g_spawn_async(), or the like.
+ *
+ * Returns: %FALSE if the environment variable couldn't be set.
+ *
+ * Since: 2.4
+ */
+gboolean
+g_setenv (const gchar *variable,
+          const gchar *value,
+          gboolean     overwrite)
+{
+  gint result;
+#ifndef HAVE_SETENV
+  gchar *string;
+#endif
 
+  g_return_val_if_fail (variable != NULL, FALSE);
+  g_return_val_if_fail (strchr (variable, '=') == NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+#ifdef HAVE_SETENV
+  result = setenv (variable, value, overwrite);
+#else
+  if (!overwrite && getenv (variable) != NULL)
+    return TRUE;
+
+  /* This results in a leak when you overwrite existing
+   * settings. It would be fairly easy to fix this by keeping
+   * our own parallel array or hash table.
+   */
+  string = g_strconcat (variable, "=", value, NULL);
+  result = putenv (string);
+#endif
+  return result == 0;
+}
+
+#ifdef HAVE__NSGETENVIRON
+#define environ (*_NSGetEnviron())
+#else
+/* According to the Single Unix Specification, environ is not
+ * in any system header, although unistd.h often declares it.
+ */
+extern char **environ;
+#endif
+
+/**
+ * g_unsetenv:
+ * @variable: the environment variable to remove, must not contain '='
+ *
+ * Removes an environment variable from the environment.
+ *
+ * Note that on some systems, when variables are overwritten, the
+ * memory used for the previous variables and its value isn't reclaimed.
+ *
+ * You should be mindful of the fact that environment variable handling
+ * in UNIX is not thread-safe, and your program may crash if one thread
+ * calls g_unsetenv() while another thread is calling getenv(). (And note
+ * that many functions, such as gettext(), call getenv() internally.) This
+ * function is only safe to use at the very start of your program, before
+ * creating any other threads (or creating objects that create worker
+ * threads of their own).
+ * 
+ * If you need to set up the environment for a child process, you can
+ * use g_get_environ() to get an environment array, modify that with
+ * g_environ_setenv() and g_environ_unsetenv(), and then pass that
+ * array directly to execvpe(), g_spawn_async(), or the like.
+ *
+ * Since: 2.4
+ */
+void
+g_unsetenv (const gchar *variable)
+{
+  g_return_if_fail (variable != NULL);
+  g_return_if_fail (strchr (variable, '=') == NULL);
+
+#ifdef HAVE_UNSETENV
+  unsetenv (variable);
+#else /* !HAVE_UNSETENV */
+  /* Mess directly with the environ array.
+   * This seems to be the only portable way to do this.
+   */
+  g_environ_unsetenv_internal (environ, variable, FALSE);
+#endif /* !HAVE_UNSETENV */
+}
+
+/**
+ * g_listenv:
+ *
+ * Gets the names of all variables set in the environment.
+ *
+ * Programs that want to be portable to Windows should typically use
+ * this function and g_getenv() instead of using the environ array
+ * from the C library directly. On Windows, the strings in the environ
+ * array are in system codepage encoding, while in most of the typical
+ * use cases for environment variables in GLib-using programs you want
+ * the UTF-8 encoding that this function and g_getenv() provide.
+ *
+ * Returns: (array zero-terminated=1) (transfer full): a %NULL-terminated
+ *     list of strings which must be freed with g_strfreev().
+ *
+ * Since: 2.8
+ */
+gchar **
+g_listenv (void)
+{
+  gchar **result, *eq;
+  gint len, i, j;
+
+  len = g_strv_length (environ);
+  result = g_new0 (gchar *, len + 1);
+
+  j = 0;
+  for (i = 0; i < len; i++)
+    {
+      eq = strchr (environ[i], '=');
+      if (eq)
+        result[j++] = g_strndup (environ[i], eq - environ[i]);
+    }
+
+  result[j] = NULL;
+
+  return result;
+}
+
+/**
+ * g_get_environ:
+ *
+ * Gets the list of environment variables for the current process.
+ *
+ * The list is %NULL terminated and each item in the list is of the
+ * form 'NAME=VALUE'.
+ *
+ * This is equivalent to direct access to the 'environ' global variable,
+ * except portable.
+ *
+ * The return value is freshly allocated and it should be freed with
+ * g_strfreev() when it is no longer needed.
+ *
+ * Returns: (array zero-terminated=1) (transfer full): the list of
+ *     environment variables
+ *
+ * Since: 2.28
+ */
+gchar **
+g_get_environ (void)
+{
+  return g_strdupv (environ);
+}
+
+/* Win32 implementation {{{1 */
+#else   /* G_OS_WIN32 */
+
+const gchar *
+g_getenv (const gchar *variable)
+{
   GQuark quark;
   gchar *value;
   wchar_t dummy[2], *wname, *wvalue;
@@ -94,7 +447,11 @@ g_getenv (const gchar *variable)
   if (len == 0)
     {
       g_free (wname);
-      return NULL;
+      if (GetLastError () == ERROR_ENVVAR_NOT_FOUND)
+        return NULL;
+
+      quark = g_quark_from_static_string ("");
+      return g_quark_to_string (quark);
     }
   else if (len == 1)
     len = 2;
@@ -133,85 +490,24 @@ g_getenv (const gchar *variable)
   g_free (wname);
   g_free (wvalue);
 
-  quark = g_quark_from_string (value);
-  g_free (value);
-
-  return g_quark_to_string (quark);
-
-#endif /* G_OS_WIN32 */
-}
-
-/**
- * g_setenv:
- * @variable: the environment variable to set, must not contain '='.
- * @value: the value for to set the variable to.
- * @overwrite: whether to change the variable if it already exists.
- *
- * Sets an environment variable. Both the variable's name and value
- * should be in the GLib file name encoding. On UNIX, this means that
- * they can be arbitrary byte strings. On Windows, they should be in
- * UTF-8.
- *
- * Note that on some systems, when variables are overwritten, the memory
- * used for the previous variables and its value isn't reclaimed.
- *
- * <warning><para>
- * Environment variable handling in UNIX is not thread-safe, and your
- * program may crash if one thread calls g_setenv() while another
- * thread is calling getenv(). (And note that many functions, such as
- * gettext(), call getenv() internally.) This function is only safe to
- * use at the very start of your program, before creating any other
- * threads (or creating objects that create worker threads of their
- * own).
- * </para><para>
- * If you need to set up the environment for a child process, you can
- * use g_get_environ() to get an environment array, modify that with
- * g_environ_setenv() and g_environ_unsetenv(), and then pass that
- * array directly to execvpe(), g_spawn_async(), or the like.
- * </para></warning>
- *
- * Returns: %FALSE if the environment variable couldn't be set.
- *
- * Since: 2.4
- */
-gboolean
-g_setenv (const gchar *variable,
-          const gchar *value,
-          gboolean     overwrite)
-{
-#ifndef G_OS_WIN32
-
-  gint result;
-#ifndef HAVE_SETENV
-  gchar *string;
-#endif
-
-  g_return_val_if_fail (variable != NULL, FALSE);
-  g_return_val_if_fail (strchr (variable, '=') == NULL, FALSE);
-
-#ifdef HAVE_SETENV
-  result = setenv (variable, value, overwrite);
-#else
-  if (!overwrite && getenv (variable) != NULL)
-    return TRUE;
-
-  /* This results in a leak when you overwrite existing
-   * settings. It would be fairly easy to fix this by keeping
-   * our own parallel array or hash table.
-   */
-  string = g_strconcat (variable, "=", value, NULL);
-  result = putenv (string);
-#endif
-  return result == 0;
+  quark = g_quark_from_string (value);
+  g_free (value);
 
-#else /* G_OS_WIN32 */
+  return g_quark_to_string (quark);
+}
 
+gboolean
+g_setenv (const gchar *variable,
+          const gchar *value,
+          gboolean     overwrite)
+{
   gboolean retval;
   wchar_t *wname, *wvalue, *wassignment;
   gchar *tem;
 
   g_return_val_if_fail (variable != NULL, FALSE);
   g_return_val_if_fail (strchr (variable, '=') == NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
   g_return_val_if_fail (g_utf8_validate (variable, -1, NULL), FALSE);
   g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
 
@@ -247,68 +543,11 @@ g_setenv (const gchar *variable,
   g_free (wvalue);
 
   return retval;
-
-#endif /* G_OS_WIN32 */
 }
 
-#ifdef HAVE__NSGETENVIRON
-#define environ (*_NSGetEnviron())
-#elif !defined(G_OS_WIN32)
-
-/* According to the Single Unix Specification, environ is not
- * in any system header, although unistd.h often declares it.
- */
-extern char **environ;
-#endif
-
-/**
- * g_unsetenv:
- * @variable: the environment variable to remove, must not contain '='
- *
- * Removes an environment variable from the environment.
- *
- * Note that on some systems, when variables are overwritten, the
- * memory used for the previous variables and its value isn't reclaimed.
- *
- * <warning><para>
- * Environment variable handling in UNIX is not thread-safe, and your
- * program may crash if one thread calls g_unsetenv() while another
- * thread is calling getenv(). (And note that many functions, such as
- * gettext(), call getenv() internally.) This function is only safe
- * to use at the very start of your program, before creating any other
- * threads (or creating objects that create worker threads of their
- * own).
- * </para><para>
- * If you need to set up the environment for a child process, you can
- * use g_get_environ() to get an environment array, modify that with
- * g_environ_setenv() and g_environ_unsetenv(), and then pass that
- * array directly to execvpe(), g_spawn_async(), or the like.
- * </para></warning>
- *
- * Since: 2.4
- */
 void
 g_unsetenv (const gchar *variable)
 {
-#ifndef G_OS_WIN32
-
-#ifdef HAVE_UNSETENV
-  g_return_if_fail (variable != NULL);
-  g_return_if_fail (strchr (variable, '=') == NULL);
-
-  unsetenv (variable);
-#else /* !HAVE_UNSETENV */
-  g_return_if_fail (variable != NULL);
-  g_return_if_fail (strchr (variable, '=') == NULL);
-
-  /* Mess directly with the environ array.
-   * This seems to be the only portable way to do this.
-   */
-  g_environ_unsetenv (environ, variable);
-#endif /* !HAVE_UNSETENV */
-
-#else  /* G_OS_WIN32 */
-
   wchar_t *wname, *wassignment;
   gchar *tem;
 
@@ -327,49 +566,11 @@ g_unsetenv (const gchar *variable)
   SetEnvironmentVariableW (wname, NULL);
 
   g_free (wname);
-
-#endif /* G_OS_WIN32 */
 }
 
-/**
- * g_listenv:
- *
- * Gets the names of all variables set in the environment.
- *
- * Programs that want to be portable to Windows should typically use
- * this function and g_getenv() instead of using the environ array
- * from the C library directly. On Windows, the strings in the environ
- * array are in system codepage encoding, while in most of the typical
- * use cases for environment variables in GLib-using programs you want
- * the UTF-8 encoding that this function and g_getenv() provide.
- *
- * Returns: (array zero-terminated=1) (transfer full): a %NULL-terminated
- *     list of strings which must be freed with g_strfreev().
- *
- * Since: 2.8
- */
 gchar **
 g_listenv (void)
 {
-#ifndef G_OS_WIN32
-  gchar **result, *eq;
-  gint len, i, j;
-
-  len = g_strv_length (environ);
-  result = g_new0 (gchar *, len + 1);
-
-  j = 0;
-  for (i = 0; i < len; i++)
-    {
-      eq = strchr (environ[i], '=');
-      if (eq)
-        result[j++] = g_strndup (environ[i], eq - environ[i]);
-    }
-
-  result[j] = NULL;
-
-  return result;
-#else
   gchar **result, *eq;
   gint len = 0, j;
   wchar_t *p, *q;
@@ -408,203 +609,86 @@ g_listenv (void)
   FreeEnvironmentStringsW (p);
 
   return result;
-#endif
 }
 
-/**
- * g_get_environ:
- *
- * Gets the list of environment variables for the current process.
- *
- * The list is %NULL terminated and each item in the list is of the
- * form 'NAME=VALUE'.
- *
- * This is equivalent to direct access to the 'environ' global variable,
- * except portable.
- *
- * The return value is freshly allocated and it should be freed with
- * g_strfreev() when it is no longer needed.
- *
- * Returns: (array zero-terminated=1) (transfer full): the list of
- *     environment variables
- *
- * Since: 2.28
- */
 gchar **
 g_get_environ (void)
 {
-#ifndef G_OS_WIN32
-  return g_strdupv (environ);
-#else
   gunichar2 *strings;
   gchar **result;
   gint i, n;
 
   strings = GetEnvironmentStringsW ();
-  for (n = 0; strings[n]; n += wcslen (strings + n) + 1);
-  result = g_new (char *, n + 1);
-  for (i = 0; strings[i]; i += wcslen (strings + i) + 1)
-    result[i] = g_utf16_to_utf8 (strings + i, -1, NULL, NULL, NULL);
+  for (n = 0, i = 0; strings[n]; i++)
+    n += wcslen (strings + n) + 1;
+
+  result = g_new (char *, i + 1);
+  for (n = 0, i = 0; strings[n]; i++)
+    {
+      result[i] = g_utf16_to_utf8 (strings + n, -1, NULL, NULL, NULL);
+      n += wcslen (strings + n) + 1;
+    }
   FreeEnvironmentStringsW (strings);
   result[i] = NULL;
 
   return result;
-#endif
 }
 
-static gint
-g_environ_find (gchar       **envp,
-                const gchar  *variable)
-{
-  gint len, i;
-
-  len = strlen (variable);
-
-  for (i = 0; envp[i]; i++)
-    {
-      if (strncmp (envp[i], variable, len) == 0 &&
-          envp[i][len] == '=')
-        return i;
-    }
+/* Win32 binary compatibility versions {{{1 */
+#ifndef _WIN64
 
-  return -1;
-}
+#undef g_getenv
 
-/**
- * g_environ_getenv:
- * @envp: (array zero-terminated=1) (transfer none): an environment
- *     list (eg, as returned from g_get_environ())
- * @variable: the environment variable to get, in the GLib file name
- *     encoding
- *
- * Returns the value of the environment variable @variable in the
- * provided list @envp.
- *
- * The name and value are in the GLib file name encoding.
- * On UNIX, this means the actual bytes which might or might not
- * be in some consistent character set and encoding. On Windows,
- * it is in UTF-8. On Windows, in case the environment variable's
- * value contains references to other environment variables, they
- * are expanded.
- *
- * Return value: the value of the environment variable, or %NULL if
- *     the environment variable is not set in @envp. The returned
- *     string is owned by @envp, and will be freed if @variable is
- *     set or unset again.
- *
- * Since: 2.32
- */
 const gchar *
-g_environ_getenv (gchar       **envp,
-                  const gchar  *variable)
+g_getenv (const gchar *variable)
 {
-  gint index;
-
-  g_return_val_if_fail (envp != NULL, NULL);
-  g_return_val_if_fail (variable != NULL, NULL);
+  gchar *utf8_variable = g_locale_to_utf8 (variable, -1, NULL, NULL, NULL);
+  const gchar *utf8_value = g_getenv_utf8 (utf8_variable);
+  gchar *value;
+  GQuark quark;
 
-  index = g_environ_find (envp, variable);
-  if (index != -1)
-    return envp[index] + strlen (variable) + 1;
-  else
+  g_free (utf8_variable);
+  if (!utf8_value)
     return NULL;
-}
+  value = g_locale_from_utf8 (utf8_value, -1, NULL, NULL, NULL);
+  quark = g_quark_from_string (value);
+  g_free (value);
 
-/**
- * g_environ_setenv:
- * @envp: (array zero-terminated=1) (transfer full): an environment
- *     list (eg, as returned from g_get_environ())
- * @variable: the environment variable to set, must not contain '='
- * @value: the value for to set the variable to
- * @overwrite: whether to change the variable if it already exists
- *
- * Sets the environment variable @variable in the provided list
- * @envp to @value.
- *
- * Both the variable's name and value should be in the GLib
- * file name encoding. On UNIX, this means that they can be
- * arbitrary byte strings. On Windows, they should be in UTF-8.
- *
- * Return value: (array zero-terminated=1) (transfer full): the
- *     updated environment
- *
- * Since: 2.32
- */
-gchar **
-g_environ_setenv (gchar       **envp,
-                  const gchar  *variable,
-                  const gchar  *value,
-                  gboolean      overwrite)
-{
-  gint index;
+  return g_quark_to_string (quark);
+}
 
-  g_return_val_if_fail (envp != NULL, NULL);
-  g_return_val_if_fail (variable != NULL, NULL);
-  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+#undef g_setenv
 
-  index = g_environ_find (envp, variable);
-  if (index != -1)
-    {
-      if (overwrite)
-        {
-          g_free (envp[index]);
-          envp[index] = g_strdup_printf ("%s=%s", variable, value);
-        }
-    }
-  else
-    {
-      gint length;
+gboolean
+g_setenv (const gchar *variable,
+          const gchar *value,
+          gboolean     overwrite)
+{
+  gchar *utf8_variable = g_locale_to_utf8 (variable, -1, NULL, NULL, NULL);
+  gchar *utf8_value = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+  gboolean retval = g_setenv_utf8 (utf8_variable, utf8_value, overwrite);
 
-      length = g_strv_length (envp);
-      envp = g_renew (gchar *, envp, length + 2);
-      envp[length] = g_strdup_printf ("%s=%s", variable, value);
-      envp[length + 1] = NULL;
-    }
+  g_free (utf8_variable);
+  g_free (utf8_value);
 
-  return envp;
+  return retval;
 }
 
-/**
- * g_environ_unsetenv:
- * @envp: (array zero-terminated=1) (transfer full): an environment
- *     list (eg, as returned from g_get_environ())
- * @variable: the environment variable to remove, must not contain '='
- *
- * Removes the environment variable @variable from the provided
- * environment @envp.
- *
- * Return value: (array zero-terminated=1) (transfer full): the
- *     updated environment
- *
- * Since: 2.32
- */
-gchar **
-g_environ_unsetenv (gchar       **envp,
-                    const gchar  *variable)
+#undef g_unsetenv
+
+void
+g_unsetenv (const gchar *variable)
 {
-  gint len;
-  gchar **e, **f;
+  gchar *utf8_variable = g_locale_to_utf8 (variable, -1, NULL, NULL, NULL);
 
-  g_return_val_if_fail (envp != NULL, NULL);
-  g_return_val_if_fail (variable != NULL, NULL);
-  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+  g_unsetenv_utf8 (utf8_variable);
 
-  len = strlen (variable);
+  g_free (utf8_variable);
+}
 
-  /* Note that we remove *all* environment entries for
-   * the variable name, not just the first.
-   */
-  e = f = envp;
-  while (*e != NULL)
-    {
-      if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
-        {
-          *f = *e;
-          f++;
-        }
-      e++;
-    }
-  *f = NULL;
+#endif  /* _WIN64 */
 
-  return envp;
-}
+#endif  /* G_OS_WIN32 */
+
+/* Epilogue {{{1 */
+/* vim: set foldmethod=marker: */