Web lists-archives.com

Re: [Mingw-users] setenv()




-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 17/11/16 16:56, Anton Shepelev wrote:
> Keith Marshall to Anton Shepelev:
> 
>>> As I can tell, the variable buf is a local one, whereas 
>>> according to the putenv man entry, "The string pointed to by
>>> string becomes part of the environment, so altering the string
>>> changes the environment."
>>> 
>>> Does the Microsoft implementation copy the string passed?
>> 
>> Yes.  Given MS-DOS' organization of the environment as a
>> contiguous sequence of NUL separated name=value C strings,
>> terminated by a single zero-length C string, that would have been
>> a practical necessity.  Of course, there's no guarantee that
>> Windows' has adopted  that MS-DOS environment organizational
>> legacy, but the printf() statements, which I added to the sample
>> implementation, confirm that the pointer returned by getenv()
>> bears no relationship to the original address of the string
>> passed to putenv();
> 
> Thanks -- that is interesing.  The result would be the same if 
> getenv() returned a copy of the string as a safety measure.

True, but it doesn't work that way -- getenv() returns a pointer to
the value, immediately following the appropriate "name=" string within
the environment itself.  This is readily demonstrated, by adaptation
of your example program (with attached setenv() implementation):

  #define _POSIX_C_SOURCE 200809L

  #include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>

  int main( int argc, char** argv )
  {
    char **p;
    const char *envval;
    errno = 0; setenv ("test", "1", 1);
    printf (
        "setenv (\"test\", \"1\", 1) returned errno = %d\n", errno
      );
    errno = 0; setenv ("test2", "2", 1);
    printf (
        "setenv (\"test2\", \"2\", 1) returned errno = %d\n", errno
      );
    if( (envval = getenv ("test")) != NULL )
      printf (
          "envval 'test' retrieved at %1$#08x; value = %1$s\n", envval
        );
    if( (envval = getenv ("test2")) != NULL )
      printf (
          "envval 'test2' retrieved at %1$#08x; value = %1$s\n", envval
        );
    for( p = _environ; *p; p++ )
      printf( "%1$#08x: %1$s\n", *p );
    errno = 0; unsetenv ("test");
    printf ("unsetenv (\"test\") returned errno = %d\n", errno);
    if( (envval = getenv ("test")) != NULL )
      printf (
          "envval 'test' retrieved at %1$#08x; value = %1$s\n", envval
        );
    else
      printf ("envval 'test' was successfully deleted\n");
    return 0;
  }

which yields output (on WinXP VM):

  setenv ("test", "1", 1) returned errno = 0
  setenv ("test2", "2", 1) returned errno = 0
  envval 'test' retrieved at 0x3d2445; value = 1
  envval 'test2' retrieved at 0x3d24de; value = 2
  0x3d2be0: !::=::\
  0x3d2bf0: ALLUSERSPROFILE=C:\Documents and Settings\All Users
  ...
  0x3d32b8: WINDIR=C:\WINDOWS
  0x3d32d8: _=e:/a
  0x3d2440: test=1
  0x3d24d8: test2=2
  unsetenv ("test") returned errno = 0
  envval 'test' was successfully deleted

Note that the address retrieved by getenv("test") is exactly 5 bytes
beyond the corresponding "test=1" string in the environment, as it is
enumerated within the _environ pointer array, (so refers to the part
of that string immediately following "test=").  Also note that the
separation of "test=1" and "test2=2" is much more than it would be,
if WinXP had adopted the MS-DOS environment organization, so that
assumption obviously wasn't valid, (although curiously, running the
same program under wine suggests that wine has adopted exactly that
organization).

Further note that the setenv("test2", "2", 1) call will overwrite the
stack space used by setenv("test", "1", 1), and the "test=1" string,
in the environment has not been affected in any way.

- -- 
Regards,
Keith.

Public key available from keys.gnupg.net
Key fingerprint: C19E C018 1547 DE50 E1D4 8F53 C0AD 36C6 347E 5A3F
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.20 (GNU/Linux)

iQIcBAEBAgAGBQJYLfz3AAoJEMCtNsY0flo/blcQAJaeLy1WILvb6lD8uKwkt7hJ
eq5yZCuJxM63MvT+fsH2AYwaTpYnybSEJifJtINOaRffPcz3+m3N408aBu5aI+7g
VBPKJmlniUIAhPucN1gszVoKmz6bkZd9xOQ0RmWsh9wwmQ9KuTuNbQp+xy8opiMB
PAF7vT2UwsvxBK6DTblw1M25dXGdKOPPGWyFoklu4NmdiIYJNrS7zTfolD00PLSM
3NBU8LO8H1pCpyOWgVZ06ZexI1Yykxi2uESF9aOyWcNfjWO2Lm3SvK9wopjlesmX
PWM1q+aFUtEn/O8tNJtIpvFMKR5rvvQPVoW/gliVlC55tOqDznkOLWbfbsgHlllj
v0KuUAe2Q/wY8kL+N5yJhO9VshCgzdXbOTNNXv7Q5zxZ2H13tf6nwitAHc/31o0L
MBr3q9GXCyNOe3mYAVtJks23rXYLveWrMVU9yR8L4i7hcmk+9ASZVE9LpVy370/P
VuitW0ohGU871RrvJOcCq7fOlaKwGo5/nuZLHImDVm3+QPJIaIIccqeLPrT0trr0
nzU18LlIqglVPq8xCM4E8p+nYc98bAxSKy0mORnONiuE3ksTD451sv+aqEy5M4LZ
a8CD35zgcg67FK8dGq/ehlSQdAaytCQWjY9BJFOkSSBWfYpth0U3W6IPIWiYPScE
tdIjG2KWoyy9iHL/SAR+
=QXIP
-----END PGP SIGNATURE-----
# HG changeset patch
# Parent b23b2c744128f97808fa3f7462723a70895a8a1f
diff --git a/mingwrt/Makefile.in b/mingwrt/Makefile.in
--- a/mingwrt/Makefile.in
+++ b/mingwrt/Makefile.in
@@ -489,11 +489,11 @@ libmingwex.a: $(addsuffix fmt.$(OBJEXT),
 
 # Some additional miscellaneous functions, in libmingwex.a
 #
 libmingwex.a: $(addsuffix .$(OBJEXT), mingw-aligned-malloc mingw-fseek)
 libmingwex.a: $(addsuffix .$(OBJEXT), glob getopt basename dirname nsleep)
-libmingwex.a: $(addsuffix .$(OBJEXT), mkstemp mkdtemp cryptnam)
+libmingwex.a: $(addsuffix .$(OBJEXT), mkstemp mkdtemp cryptnam setenv)
 
 libmingwex.a: $(addsuffix .$(OBJEXT), tdelete tfind tsearch twalk)
 
 libmingwex.a: $(addsuffix .$(OBJEXT), getdelim gettimeofday)
 libmingwex.a: $(addsuffix .$(OBJEXT), dirent wdirent dlfcn)
diff --git a/mingwrt/include/stdlib.h b/mingwrt/include/stdlib.h
--- a/mingwrt/include/stdlib.h
+++ b/mingwrt/include/stdlib.h
@@ -822,10 +822,28 @@ unsigned __int64 _strtoui64_l(const char
 __CRT_ALIAS __JMPSTUB__(( FUNCTION = mkdtemp ))
 __cdecl __MINGW_NOTHROW  char *mkdtemp (char *__dirname_template)
 { return __mingw_mkdtemp( __dirname_template ); }
 
 #endif	/* _POSIX_C_SOURCE >= 200809L (for mkdtemp()) */
+
+#if _POSIX_C_SOURCE >= 200112L
+/* setenv() and unsetenv() are also available, from POSIX.1-2001 onwards.
+ */
+__cdecl __MINGW_NOTHROW  int setenv( const char *, const char *, int );
+__cdecl __MINGW_NOTHROW  int unsetenv( const char * );
+
+__cdecl __MINGW_NOTHROW  int __mingw_setenv( const char *, const char *, int );
+
+__CRT_ALIAS __JMPSTUB__(( FUNCTION = setenv ))
+__cdecl __MINGW_NOTHROW  int setenv( const char *__n, const char *__v, int __f )
+{ return __mingw_setenv( __n, __v, __f ); }
+
+__CRT_ALIAS __LIBIMPL__(( FUNCTION = unsetenv ))
+__cdecl __MINGW_NOTHROW  int unsetenv( const char *__name )
+{ return __mingw_setenv( __name, NULL, 1 ); }
+
+#endif	/* _POSIX_C_SOURCE >= 200112L (for setenv()) */
 #endif	/* _STDLIB_H */
 
 _END_C_DECLS
 
 #endif	/* ! RC_INVOKED */
diff --git a/mingwrt/mingwex/setenv.c b/mingwrt/mingwex/setenv.c
new file mode 100644
--- /dev/null
+++ b/mingwrt/mingwex/setenv.c
@@ -0,0 +1,94 @@
+/*
+ * setenv.c
+ *
+ * Implementation of POSIX standard IEEE 1003.1-2008 setenv() function;
+ * may also be invoked inline, as "retval = setenv( varname, NULL, 1 )",
+ * to implement the complementary unsetenv() function.
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@xxxxxxxxxxxxxxxxxxxxx>
+ * Copyright (C) 2016, MinGW.org Project
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+int __mingw_setenv( const char *var, const char *value, int overwrite )
+{
+  /* Core implementation for both setenv() and unsetenv() functions;
+   * at the outset, assume that the requested operation may fail.
+   */
+  int retval = -1;
+
+  /* The specified "var" name MUST be non-NULL, not a zero-length
+   * string, and must not include any '=' character.
+   */
+  if( var && *var && (strchr( var, '=' ) == NULL) )
+  {
+    /* A properly named variable may be added to, removed from,
+     * or modified within the environment, ONLY if "overwrite"
+     * mode is enabled, OR if the named variable does not yet
+     * exist...
+     */ 
+    if( overwrite || getenv( var ) == NULL )
+    {
+      /* ... in which cases, we convert the specified name and
+       * value into the appropriate form for use with putenv(),
+       * (noting that we accept a NULL "value" as equivalent to
+       * a zero-length string, which renders putenv() as the
+       * equivalent of unsetenv()).
+       */
+      const char *fmt = "%s=%s";
+      const char *val = value ? value : "";
+      char buf[1 + snprintf( NULL, 0, fmt, var, val )];
+      snprintf( buf, sizeof( buf ), fmt, var, val );
+      if( (retval = putenv( buf )) != 0 )
+	/*
+	 * If putenv() returns non-zero, indicating failure, the
+	 * most probable explanation is that there wasn't enough
+	 * free memory; ensure that errno is set accordingly.
+	 */
+        errno = ENOMEM;
+    }
+    else
+      /* The named variable already exists, and overwrite mode
+       * was not enabled; there is nothing to be done.
+       */
+      retval = 0;
+  }
+  else
+    /* The specified environment variable name was invalid.
+     */
+    errno = EINVAL;
+
+  /* Succeed or fail, "retval" has now been set to indicate the
+   * appropriate status for return.
+   */
+  return retval;
+}
+
+/* $RCSfile$: end of file */
------------------------------------------------------------------------------
_______________________________________________
MinGW-users mailing list
MinGW-users@xxxxxxxxxxxxxxxxxxxxx

This list observes the Etiquette found at 
http://www.mingw.org/Mailing_Lists.
We ask that you be polite and do the same.  Disregard for the list etiquette may cause your account to be moderated.

_______________________________________________
You may change your MinGW Account Options or unsubscribe at:
https://lists.sourceforge.net/lists/listinfo/mingw-users
Also: mailto:mingw-users-request@xxxxxxxxxxxxxxxxxxxxx?subject=unsubscribe