putenv: avoid compilation warning on mingw
[gnulib.git] / lib / putenv.c
1 /* Copyright (C) 1991, 1994, 1997-1998, 2000, 2003-2013 Free Software
2    Foundation, Inc.
3
4    NOTE: The canonical source of this file is maintained with the GNU C
5    Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
6
7    This program is free software: you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by the
9    Free Software Foundation; either version 3 of the License, or any
10    later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20 #include <config.h>
21
22 /* Specification.  */
23 #include <stdlib.h>
24
25 #include <stddef.h>
26
27 /* Include errno.h *after* sys/types.h to work around header problems
28    on AIX 3.2.5.  */
29 #include <errno.h>
30 #ifndef __set_errno
31 # define __set_errno(ev) ((errno) = (ev))
32 #endif
33
34 #include <string.h>
35 #include <unistd.h>
36
37 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
38 # define WIN32_LEAN_AND_MEAN
39 # include <windows.h>
40 #endif
41
42 #if _LIBC
43 # if HAVE_GNU_LD
44 #  define environ __environ
45 # else
46 extern char **environ;
47 # endif
48 #endif
49
50 #if _LIBC
51 /* This lock protects against simultaneous modifications of 'environ'.  */
52 # include <bits/libc-lock.h>
53 __libc_lock_define_initialized (static, envlock)
54 # define LOCK   __libc_lock_lock (envlock)
55 # define UNLOCK __libc_lock_unlock (envlock)
56 #else
57 # define LOCK
58 # define UNLOCK
59 #endif
60
61 static int
62 _unsetenv (const char *name)
63 {
64   size_t len;
65 #if !HAVE__PUTENV
66   char **ep;
67 #endif
68
69   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
70     {
71       __set_errno (EINVAL);
72       return -1;
73     }
74
75   len = strlen (name);
76
77 #if HAVE__PUTENV
78   {
79     int putenv_result, putenv_errno;
80     char *name_ = malloc (len + 2);
81     memcpy (name_, name, len);
82     name_[len] = '=';
83     name_[len + 1] = 0;
84     putenv_result = _putenv (name_);
85     putenv_errno = errno;
86     free (name_);
87     __set_errno (putenv_errno);
88     return putenv_result;
89   }
90 #else
91
92   LOCK;
93
94   ep = environ;
95   while (*ep != NULL)
96     if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
97       {
98         /* Found it.  Remove this pointer by moving later ones back.  */
99         char **dp = ep;
100
101         do
102           dp[0] = dp[1];
103         while (*dp++);
104         /* Continue the loop in case NAME appears again.  */
105       }
106     else
107       ++ep;
108
109   UNLOCK;
110
111   return 0;
112 #endif
113 }
114
115
116 /* Put STRING, which is of the form "NAME=VALUE", in the environment.
117    If STRING contains no '=', then remove STRING from the environment.  */
118 int
119 putenv (char *string)
120 {
121   const char *name_end = strchr (string, '=');
122   char **ep;
123
124   if (name_end == NULL)
125     {
126       /* Remove the variable from the environment.  */
127       return _unsetenv (string);
128     }
129
130 #if HAVE__PUTENV
131   /* Rely on _putenv to allocate the new environment.  If other
132      parts of the application use _putenv, the !HAVE__PUTENV code
133      would fight over who owns the environ vector, causing a crash.  */
134   if (name_end[1])
135     return _putenv (string);
136   else
137     {
138       /* _putenv ("NAME=") unsets NAME, so invoke _putenv ("NAME= ")
139          to allocate the environ vector and then replace the new
140          entry with "NAME=".  */
141       int putenv_result, putenv_errno;
142       char *name_x = malloc (name_end - string + sizeof "= ");
143       if (!name_x)
144         return -1;
145       memcpy (name_x, string, name_end - string + 1);
146       name_x[name_end - string + 1] = ' ';
147       name_x[name_end - string + 2] = 0;
148       putenv_result = _putenv (name_x);
149       putenv_errno = errno;
150       for (ep = environ; *ep; ep++)
151         if (strcmp (*ep, name_x) == 0)
152           {
153             *ep = string;
154             break;
155           }
156 # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
157       if (putenv_result == 0)
158         {
159           /* _putenv propagated "NAME= " into the subprocess environment;
160              fix that by calling SetEnvironmentVariable directly.  */
161           name_x[name_end - string] = 0;
162           putenv_result = SetEnvironmentVariable (name_x, "") ? 0 : -1;
163           putenv_errno = ENOMEM; /* ENOMEM is the only way to fail.  */
164         }
165 # endif
166       free (name_x);
167       __set_errno (putenv_errno);
168       return putenv_result;
169     }
170 #else
171   for (ep = environ; *ep; ep++)
172     if (strncmp (*ep, string, name_end - string) == 0
173         && (*ep)[name_end - string] == '=')
174       break;
175
176   if (*ep)
177     *ep = string;
178   else
179     {
180       static char **last_environ = NULL;
181       size_t size = ep - environ;
182       char **new_environ = malloc ((size + 2) * sizeof *new_environ);
183       if (! new_environ)
184         return -1;
185       new_environ[0] = string;
186       memcpy (new_environ + 1, environ, (size + 1) * sizeof *new_environ);
187       free (last_environ);
188       last_environ = new_environ;
189       environ = new_environ;
190     }
191 #endif
192
193   return 0;
194 }